Index: projects/cambria/ObsoleteFiles.inc =================================================================== --- projects/cambria/ObsoleteFiles.inc (revision 186459) +++ projects/cambria/ObsoleteFiles.inc (revision 186460) @@ -1,4609 +1,4611 @@ # # $FreeBSD$ # # This file lists old files (OLD_FILES), libraries (OLD_LIBS) and # directories (OLD_DIRS) which should get removed at an update. Recently # removed entries first (with the date as a comment). Dynamic libraries are # special cased (OLD_LIBS). Static libraries or the generic links to # the dynamic libraries (lib*.so) should (if you don't know why to make an # exception, make this a "must") be viewed as normal files (OLD_FILES). # # In case of a complete directory hierarchy the sorting is in depth first # order. # # The file is partitioned: OLD_FILES first, then OLD_LIBS and OLD_DIRS last. # +# 20081223: ipprotosw.h removed +OLD_FILES+=usr/include/netinet/ipprotosw.h # 20081123: vfs_mountedon.9 removed OLD_FILES+=usr/share/man/man9/vfs_mountedon.9.gz # 20081023: FREE.9 and MALLOC.9 removed OLD_FILES+=usr/share/man/man9/FREE.9.gz OLD_FILES+=usr/share/man/man9/MALLOC.9.gz # 20080928: removal of inaccurate device_ids(9) manual page OLD_FILES+=usr/share/man/man9/device_ids.9.gz OLD_FILES+=usr/share/man/man9/major.9.gz OLD_FILES+=usr/share/man/man9/minor.9.gz OLD_FILES+=usr/share/man/man9/umajor.9.gz OLD_FILES+=usr/share/man/man9/uminor.9.gz # 20080917: removal of manpage for axed kernel primitive suser(9) OLD_FILES+=usr/share/man/man9/suser.9.gz OLD_FILES+=usr/share/man/man9/suser_cred.9.gz # 20080913: pax removed from rescue OLD_FILES+=rescue/pax # 20080823: removal of unneeded pt_chown, to implement grantpt(3) OLD_FILES+=usr/libexec/pt_chown # 20080822: ntp 4.2.4p5 import OLD_FILES+=usr/share/doc/ntp/driver23.html OLD_FILES+=usr/share/doc/ntp/driver24.html # 20080821: several man pages moved from man4.i386 to man4 .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/share/man/man4/i386/acpi_aiboost.4.gz OLD_FILES+=usr/share/man/man4/i386/acpi_asus.4.gz OLD_FILES+=usr/share/man/man4/i386/acpi_fujitsu.4.gz OLD_FILES+=usr/share/man/man4/i386/acpi_ibm.4.gz OLD_FILES+=usr/share/man/man4/i386/acpi_panasonic.4.gz OLD_FILES+=usr/share/man/man4/i386/acpi_sony.4.gz OLD_FILES+=usr/share/man/man4/i386/acpi_toshiba.4.gz OLD_FILES+=usr/share/man/man4/i386/ichwd.4.gz OLD_FILES+=usr/share/man/man4/i386/if_ndis.4.gz OLD_FILES+=usr/share/man/man4/i386/io.4.gz OLD_FILES+=usr/share/man/man4/i386/linux.4.gz OLD_FILES+=usr/share/man/man4/i386/ndis.4.gz .endif # 20080820: MPSAFE TTY layer integrated OLD_FILES+=usr/include/sys/linedisc.h OLD_FILES+=usr/share/man/man3/posix_openpt.3.gz # 20080725: sgtty.h removed OLD_FILES+=usr/include/sgtty.h # 20080719: sade(8) removed on all but amd64, i386 and sparc64 .if ${TARGET_ARCH} != "amd64" && ${TARGET_ARCH} != "i386" && \ ${TARGET_ARCH} != "sparc64" OLD_FILES+=usr/sbin/sade OLD_FILES+=usr/share/man/man8/sade.8.gz .endif # 20080706: bsdlabel(8) removed on powerpc .if ${TARGET_ARCH} == "powerpc" OLD_FILES+=sbin/bsdlabel OLD_FILES+=usr/share/man/man8/bsdlabel.8.gz .endif # 20080704: sbsh(4) removed OLD_FILES+=usr/share/man/man4/if_sbsh.4.gz OLD_FILES+=usr/share/man/man4/sbsh.4.gz # 20080704: cnw(4) removed OLD_FILES+=usr/share/man/man4/if_cnw.4.gz OLD_FILES+=usr/share/man/man4/cnw.4.gz # 20080704: oltr(4) removed .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/share/man/man4/i386/if_oltr.4.gz OLD_FILES+=usr/share/man/man4/i386/oltr.4.gz .endif # 20080704: arl(4) removed .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/sbin/arlcontrol OLD_FILES+=usr/share/man/man4/i386/arl.4.gz OLD_FILES+=usr/share/man/man8/arlcontrol.8.gz .endif # 20080703: sunlabel only for sparc64 .if ${TARGET_ARCH} != "sparc64" OLD_FILES+=sbin/sunlabel OLD_FILES+=usr/share/man/man8/sunlabel.8.gz .endif # 20080703: bsdlabel & fdisk removed on ia64 .if ${TARGET_ARCH} == "ia64" OLD_FILES+=sbin/bsdlabel OLD_FILES+=usr/share/man/man8/bsdlabel.8.gz OLD_FILES+=usr/share/man/man8/disklabel.8.gz OLD_FILES+=sbin/fdisk OLD_FILES+=usr/share/man/man8/fdisk.8.gz .endif # 20080701: wpa_supplicant.conf moved to share/examples/etc/ OLD_FILES+=usr/share/examples/wpa_supplicant/wpa_supplicant.conf OLD_DIRS+=usr/share/examples/wpa_supplicant # 20080614: pecoff image activator removed .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/include/machine/pecoff_machdep.h .endif # 20080614: sgtty removed OLD_FILES+=usr/include/sys/ttychars.h OLD_FILES+=usr/include/sys/ttydev.h OLD_FILES+=usr/share/man/man3/gtty.3.gz OLD_FILES+=usr/share/man/man3/stty.3.gz # 20080609: gpt(8) removed OLD_FILES+=sbin/gpt OLD_FILES+=usr/share/man/man8/gpt.8.gz # 20080525: I4B removed OLD_FILES+=etc/isdn/answer OLD_FILES+=etc/isdn/isdntel OLD_FILES+=etc/isdn/record OLD_FILES+=etc/isdn/tell OLD_FILES+=etc/isdn/tell-record OLD_FILES+=etc/isdn/unknown_incoming OLD_FILES+=etc/isdn/holidays.D OLD_FILES+=etc/isdn/isdnd.rates.A OLD_FILES+=etc/isdn/isdnd.rates.D OLD_FILES+=etc/isdn/isdnd.rates.F OLD_FILES+=etc/isdn/isdnd.rates.L OLD_FILES+=etc/isdn/isdnd.rates.UK.BT OLD_FILES+=etc/isdn/isdnd.rc.sample OLD_FILES+=etc/isdn/isdntel.alias.sample OLD_DIRS+=etc/isdn OLD_FILES+=etc/rc.d/isdnd OLD_FILES+=usr/include/i4b/i4b_cause.h OLD_FILES+=usr/include/i4b/i4b_debug.h OLD_FILES+=usr/include/i4b/i4b_ioctl.h OLD_FILES+=usr/include/i4b/i4b_rbch_ioctl.h OLD_FILES+=usr/include/i4b/i4b_tel_ioctl.h OLD_FILES+=usr/include/i4b/i4b_trace.h OLD_DIRS+=usr/include/i4b OLD_FILES+=usr/sbin/dtmfdecode OLD_FILES+=usr/sbin/g711conv OLD_FILES+=usr/sbin/isdnd OLD_FILES+=usr/sbin/isdndebug OLD_FILES+=usr/sbin/isdndecode OLD_FILES+=usr/sbin/isdnmonitor OLD_FILES+=usr/sbin/isdnphone OLD_FILES+=usr/sbin/isdntel OLD_FILES+=usr/sbin/isdntelctl OLD_FILES+=usr/sbin/isdntrace OLD_FILES+=usr/share/isdn/0.al OLD_FILES+=usr/share/isdn/1.al OLD_FILES+=usr/share/isdn/2.al OLD_FILES+=usr/share/isdn/3.al OLD_FILES+=usr/share/isdn/4.al OLD_FILES+=usr/share/isdn/5.al OLD_FILES+=usr/share/isdn/6.al OLD_FILES+=usr/share/isdn/7.al OLD_FILES+=usr/share/isdn/8.al OLD_FILES+=usr/share/isdn/9.al OLD_FILES+=usr/share/isdn/beep.al OLD_FILES+=usr/share/isdn/msg.al OLD_DIRS+=usr/share/isdn OLD_FILES+=usr/share/man/man1/dtmfdecode.1.gz OLD_FILES+=usr/share/man/man1/g711conv.1.gz OLD_FILES+=usr/share/man/man4/i4b.4.gz OLD_FILES+=usr/share/man/man4/i4bcapi.4.gz OLD_FILES+=usr/share/man/man4/i4bctl.4.gz OLD_FILES+=usr/share/man/man4/i4bing.4.gz OLD_FILES+=usr/share/man/man4/i4bipr.4.gz OLD_FILES+=usr/share/man/man4/i4bisppp.4.gz OLD_FILES+=usr/share/man/man4/i4bq921.4.gz OLD_FILES+=usr/share/man/man4/i4bq931.4.gz OLD_FILES+=usr/share/man/man4/i4brbch.4.gz OLD_FILES+=usr/share/man/man4/i4btel.4.gz OLD_FILES+=usr/share/man/man4/i4btrc.4.gz OLD_FILES+=usr/share/man/man4/iavc.4.gz OLD_FILES+=usr/share/man/man4/isic.4.gz OLD_FILES+=usr/share/man/man4/ifpi.4.gz OLD_FILES+=usr/share/man/man4/ifpi2.4.gz OLD_FILES+=usr/share/man/man4/ifpnp.4.gz OLD_FILES+=usr/share/man/man4/ihfc.4.gz OLD_FILES+=usr/share/man/man4/itjc.4.gz OLD_FILES+=usr/share/man/man4/iwic.4.gz OLD_FILES+=usr/share/man/man5/isdnd.rc.5.gz OLD_FILES+=usr/share/man/man5/isdnd.rates.5.gz OLD_FILES+=usr/share/man/man5/isdnd.acct.5.gz OLD_FILES+=usr/share/man/man8/isdnd.8.gz OLD_FILES+=usr/share/man/man8/isdndebug.8.gz OLD_FILES+=usr/share/man/man8/isdndecode.8.gz OLD_FILES+=usr/share/man/man8/isdnmonitor.8.gz OLD_FILES+=usr/share/man/man8/isdnphone.8.gz OLD_FILES+=usr/share/man/man8/isdntel.8.gz OLD_FILES+=usr/share/man/man8/isdntelctl.8.gz OLD_FILES+=usr/share/man/man8/isdntrace.8.gz OLD_FILES+=usr/share/examples/isdn/contrib/README OLD_FILES+=usr/share/examples/isdn/contrib/anleitung.ppp OLD_FILES+=usr/share/examples/isdn/contrib/answer.c OLD_FILES+=usr/share/examples/isdn/contrib/answer.sh OLD_FILES+=usr/share/examples/isdn/contrib/convert.sh OLD_FILES+=usr/share/examples/isdn/contrib/hplay.c OLD_FILES+=usr/share/examples/isdn/contrib/i4b-ppp-newbie.txt OLD_FILES+=usr/share/examples/isdn/contrib/isdnctl OLD_FILES+=usr/share/examples/isdn/contrib/isdnd_acct OLD_FILES+=usr/share/examples/isdn/contrib/isdnd_acct.pl OLD_FILES+=usr/share/examples/isdn/contrib/isdntelmux.c OLD_FILES+=usr/share/examples/isdn/contrib/mrtg-isp0.sh OLD_FILES+=usr/share/examples/isdn/i4brunppp/Makefile OLD_FILES+=usr/share/examples/isdn/i4brunppp/README OLD_FILES+=usr/share/examples/isdn/i4brunppp/i4brunppp-isdnd.rc OLD_FILES+=usr/share/examples/isdn/i4brunppp/i4brunppp.8 OLD_FILES+=usr/share/examples/isdn/i4brunppp/i4brunppp.c OLD_FILES+=usr/share/examples/isdn/v21/Makefile OLD_FILES+=usr/share/examples/isdn/v21/README OLD_FILES+=usr/share/examples/isdn/v21/v21modem.c OLD_FILES+=usr/share/examples/isdn/FAQ OLD_FILES+=usr/share/examples/isdn/KERNEL OLD_FILES+=usr/share/examples/isdn/Overview OLD_FILES+=usr/share/examples/isdn/README OLD_FILES+=usr/share/examples/isdn/ROADMAP OLD_FILES+=usr/share/examples/isdn/ReleaseNotes OLD_FILES+=usr/share/examples/isdn/Resources OLD_FILES+=usr/share/examples/isdn/SupportedCards OLD_FILES+=usr/share/examples/isdn/ThankYou OLD_DIRS+=usr/share/examples/isdn/contrib OLD_DIRS+=usr/share/examples/isdn/i4brunppp OLD_DIRS+=usr/share/examples/isdn/v21 OLD_DIRS+=usr/share/examples/isdn OLD_FILES+=usr/share/examples/ppp/isdnd.rc OLD_FILES+=usr/share/examples/ppp/ppp.conf.isdn # 20080525: ng_atmpif removed OLD_FILES+=usr/include/netgraph/atm/ng_atmpif.h OLD_FILES+=usr/share/man/man4/ng_atmpif.4.gz # 20080522: pmap_addr_hint removed OLD_FILES+=usr/share/man/man9/pmap_addr_hint.9.gz # 20080517: ipsec_osdep.h removed OLD_FILES+=usr/include/netipsec/ipsec_osdep.h # 20080507: heimdal 1.1 import OLD_LIBS+=usr/lib/libasn1.so.9 OLD_LIBS+=usr/lib/libgssapi.so.9 OLD_LIBS+=usr/lib/libgssapi_krb5.so.9 OLD_LIBS+=usr/lib/libhdb.so.9 OLD_LIBS+=usr/lib/libkadm5clnt.so.9 OLD_LIBS+=usr/lib/libkadm5srv.so.9 OLD_LIBS+=usr/lib/libkafs5.so.9 OLD_LIBS+=usr/lib/libkrb5.so.9 OLD_LIBS+=usr/lib/libroken.so.9 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libgssapi.so.9 .endif # 20080420: Symbol card support dropped OLD_FILES+=usr/include/dev/wi/spectrum24t_cf.h # 20080420: awi removal OLD_FILES+=usr/share/man/man4/awi.4.gz OLD_FILES+=usr/share/man/man4/if_awi.4.gz # 20080331: pkg_sign has been removed OLD_FILES+=usr/sbin/pkg_check OLD_FILES+=usr/sbin/pkg_sign OLD_FILES+=usr/share/man/man1/pkg_check.1.gz OLD_FILES+=usr/share/man/man1/pkg_sign.1.gz # 20080325: tzdata2008b import OLD_FILES+=usr/share/zoneinfo/Asia/Calcutta OLD_FILES+=usr/share/zoneinfo/Asia/Saigon # 20080314: stack_print(9) mlink fixed OLD_FILES+=usr/share/man/man9/stack_printf.9.gz # 20080312: libkse removal OLD_FILES+=usr/include/sys/kse.h OLD_FILES+=usr/lib/libkse.so OLD_LIBS+=usr/lib/libkse.so.3 OLD_FILES+=usr/share/man/man2/kse.2.gz OLD_FILES+=usr/share/man/man2/kse_create.2.gz OLD_FILES+=usr/share/man/man2/kse_exit.2.gz OLD_FILES+=usr/share/man/man2/kse_release.2.gz OLD_FILES+=usr/share/man/man2/kse_switchin.2.gz OLD_FILES+=usr/share/man/man2/kse_thr_interrupt.2.gz OLD_FILES+=usr/share/man/man2/kse_wakeup.2.gz .if ${TARGET_ARCH} == "amd64" OLD_FILES+=usr/lib32/libkse.so OLD_LIBS+=usr/lib32/libkse.so.3 .endif # 20080220: geom_lvm rename to geom_linux_lvm OLD_FILES+=usr/share/man/man4/geom_lvm.4.gz # 20080126: oldcard.4 removal OLD_FILES+=usr/share/man/man4/card.4.gz OLD_FILES+=usr/share/man/man4/oldcard.4.gz # 20080122: Removed from the tree OLD_FILES+=usr/share/man/man9/BUF_REFCNT.9.gz # 20080108: Moved to section 2 OLD_FILES+=usr/share/man/man3/shm_open.3.gz OLD_FILES+=usr/share/man/man3/shm_unlink.3.gz # 20071207: Merged with fortunes-o.real OLD_FILES+=usr/share/games/fortune/fortunes2-o OLD_FILES+=usr/share/games/fortune/fortunes2-o.dat # 20071201: Removal of XRPU driver OLD_FILES+=usr/include/sys/xrpuio.h # 20071129: Disabled static versions of libkse by default OLD_FILES+=usr/lib/libkse.a OLD_FILES+=usr/lib/libkse_p.a OLD_FILES+=usr/lib/libkse_pic.a .if ${TARGET_ARCH} == "amd64" OLD_FILES+=usr/lib32/libkse.a OLD_FILES+=usr/lib32/libkse_p.a OLD_FILES+=usr/lib32/libkse_pic.a .endif # 20071129: Removed a Solaris compatibility header OLD_FILES+=usr/include/sys/_elf_solaris.h # 20071125: Renamed to pmc_get_msr() OLD_FILES+=usr/share/man/man3/pmc_x86_get_msr.3.gz # 20071108: Removed very crunch OLDCARD support file OLD_FILES+=etc/defaults/pccard.conf # 20071104: Removed bsdlabel, fdisk and gpt from rescue on ia64. .if ${TARGET_ARCH} == "ia64" OLD_FILES+=rescue/bsdlabel OLD_FILES+=rescue/fdisk OLD_FILES+=rescue/gpt .endif # 20071026: kthread(9)/kproc(9) API changes OLD_FILES+=usr/share/man/man9/kthread_create.9.gz # 20071025: rc.d/nfslocking superceeded by rc.d/lockd and rc.d/statd OLD_FILES+=etc/rc.d/nfslocking # 20070930: rename of cached to nscd OLD_FILES+=etc/cached.conf OLD_FILES+=etc/rc.d/cached OLD_FILES+=usr/sbin/cached OLD_FILES+=usr/share/man/man5/cached.conf.5.gz OLD_FILES+=usr/share/man/man8/cached.8.gz # 20070807: removal of PowerPC specific header file. .if ${TARGET_ARCH} == "powerpc" OLD_FILES+=usr/include/machine/interruptvar.h .endif # 20070801: fast_ipsec.4 gone OLD_FILES+=usr/share/man/man4/fast_ipsec.4.gz # 20070715: netatm temporarily disconnected (removed 20080525) OLD_FILES+=rescue/atm OLD_FILES+=rescue/fore_dnld OLD_FILES+=rescue/ilmid OLD_FILES+=sbin/atm OLD_FILES+=sbin/fore_dnld OLD_FILES+=sbin/ilmid OLD_FILES+=usr/include/libatm.h OLD_FILES+=usr/include/netatm/atm.h OLD_FILES+=usr/include/netatm/atm_cm.h OLD_FILES+=usr/include/netatm/atm_if.h OLD_FILES+=usr/include/netatm/atm_ioctl.h OLD_FILES+=usr/include/netatm/atm_pcb.h OLD_FILES+=usr/include/netatm/atm_sap.h OLD_FILES+=usr/include/netatm/atm_sigmgr.h OLD_FILES+=usr/include/netatm/atm_stack.h OLD_FILES+=usr/include/netatm/atm_sys.h OLD_FILES+=usr/include/netatm/atm_var.h OLD_FILES+=usr/include/netatm/atm_vc.h OLD_FILES+=usr/include/netatm/ipatm/ipatm.h OLD_FILES+=usr/include/netatm/ipatm/ipatm_serv.h OLD_FILES+=usr/include/netatm/ipatm/ipatm_var.h OLD_FILES+=usr/include/netatm/port.h OLD_FILES+=usr/include/netatm/queue.h OLD_FILES+=usr/include/netatm/sigpvc/sigpvc_var.h OLD_FILES+=usr/include/netatm/spans/spans_cls.h OLD_FILES+=usr/include/netatm/spans/spans_kxdr.h OLD_FILES+=usr/include/netatm/spans/spans_var.h OLD_FILES+=usr/include/netatm/uni/sscf_uni.h OLD_FILES+=usr/include/netatm/uni/sscf_uni_var.h OLD_FILES+=usr/include/netatm/uni/sscop.h OLD_FILES+=usr/include/netatm/uni/sscop_misc.h OLD_FILES+=usr/include/netatm/uni/sscop_pdu.h OLD_FILES+=usr/include/netatm/uni/sscop_var.h OLD_FILES+=usr/include/netatm/uni/uni.h OLD_FILES+=usr/include/netatm/uni/uniip_var.h OLD_FILES+=usr/include/netatm/uni/unisig.h OLD_FILES+=usr/include/netatm/uni/unisig_decode.h OLD_FILES+=usr/include/netatm/uni/unisig_mbuf.h OLD_FILES+=usr/include/netatm/uni/unisig_msg.h OLD_FILES+=usr/include/netatm/uni/unisig_print.h OLD_FILES+=usr/include/netatm/uni/unisig_var.h OLD_FILES+=usr/lib/libatm.a OLD_FILES+=usr/lib/libatm_p.a OLD_FILES+=usr/sbin/atmarpd OLD_FILES+=usr/sbin/scspd OLD_FILES+=usr/share/man/en.ISO8859-1/man8/atm.8.gz OLD_FILES+=usr/share/man/en.ISO8859-1/man8/atmarpd.8.gz OLD_FILES+=usr/share/man/en.ISO8859-1/man8/fore_dnld.8.gz OLD_FILES+=usr/share/man/en.ISO8859-1/man8/ilmid.8.gz OLD_FILES+=usr/share/man/en.ISO8859-1/man8/scspd.8.gz OLD_FILES+=usr/share/man/man8/atm.8.gz OLD_FILES+=usr/share/man/man8/atmarpd.8.gz OLD_FILES+=usr/share/man/man8/fore_dnld.8.gz OLD_FILES+=usr/share/man/man8/ilmid.8.gz OLD_FILES+=usr/share/man/man8/scspd.8.gz OLD_FILES+=usr/share/examples/atm/NOTES OLD_FILES+=usr/share/examples/atm/README OLD_FILES+=usr/share/examples/atm/Startup OLD_FILES+=usr/share/examples/atm/atm-config.sh OLD_FILES+=usr/share/examples/atm/atm-sockets.txt OLD_FILES+=usr/share/examples/atm/cpcs-design.txt OLD_FILES+=usr/share/examples/atm/fore-microcode.txt OLD_FILES+=usr/share/examples/atm/sscf-design.txt OLD_FILES+=usr/share/examples/atm/sscop-design.txt OLD_LIBS+=lib/libatm.so.5 OLD_LIBS+=usr/lib/libatm.so OLD_DIRS+=usr/include/netatm/sigpvc OLD_DIRS+=usr/include/netatm/spans OLD_DIRS+=usr/include/netatm/ipatm OLD_DIRS+=usr/include/netatm/uni OLD_DIRS+=usr/include/netatm OLD_DIRS+=usr/share/examples/atm .if ${TARGET_ARCH} == "amd64" OLD_FILES+=usr/lib32/libatm.a OLD_FILES+=usr/lib32/libatm.so OLD_LIBS+=usr/lib32/libatm.so.5 OLD_FILES+=usr/lib32/libatm_p.a .endif # 20070705: I4B headers repo-copied to include/i4b/ .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/include/machine/i4b_cause.h OLD_FILES+=usr/include/machine/i4b_debug.h OLD_FILES+=usr/include/machine/i4b_ioctl.h OLD_FILES+=usr/include/machine/i4b_rbch_ioctl.h OLD_FILES+=usr/include/machine/i4b_tel_ioctl.h OLD_FILES+=usr/include/machine/i4b_trace.h .endif # 20070704: I4B 'modules' temporary disconnected (removed 20080525) .if ${TARGET_ARCH} == "i386" OLD_FILES+=usr/share/man/man4/i4bing.4.gz OLD_FILES+=usr/share/man/man4/i4bipr.4.gz OLD_FILES+=usr/share/man/man4/i4bisppp.4.gz .endif # 20070703: pf 4.1 import OLD_FILES+=usr/libexec/ftp-proxy # 20070701: KAME IPSec removal OLD_FILES+=usr/include/netinet6/ah.h OLD_FILES+=usr/include/netinet6/ah6.h OLD_FILES+=usr/include/netinet6/ah_aesxcbcmac.h OLD_FILES+=usr/include/netinet6/esp.h OLD_FILES+=usr/include/netinet6/esp6.h OLD_FILES+=usr/include/netinet6/esp_aesctr.h OLD_FILES+=usr/include/netinet6/esp_camellia.h OLD_FILES+=usr/include/netinet6/esp_rijndael.h OLD_FILES+=usr/include/netinet6/ipsec.h OLD_FILES+=usr/include/netinet6/ipsec6.h OLD_FILES+=usr/include/netinet6/ipcomp.h OLD_FILES+=usr/include/netinet6/ipcomp6.h OLD_FILES+=usr/include/netkey/key.h OLD_FILES+=usr/include/netkey/key_debug.h OLD_FILES+=usr/include/netkey/key_var.h OLD_FILES+=usr/include/netkey/keydb.h OLD_FILES+=usr/include/netkey/keysock.h OLD_DIRS+=usr/include/netkey # 20070701: remove wicontrol OLD_FILES+=usr/sbin/wicontrol OLD_FILES+=usr/share/man/man8/wicontrol.8.gz # 20070625: umapfs removal OLD_FILES+=rescue/mount_umapfs OLD_FILES+=sbin/mount_umapfs OLD_FILES+=usr/include/fs/umapfs/umap.h OLD_FILES+=usr/share/man/man8/mount_umapfs.8.gz OLD_DIRS+=usr/include/fs/umapfs # 20070618: Removal of the PROTO.localhost* files OLD_FILES+=etc/namedb/PROTO.localhost-v6.rev OLD_FILES+=etc/namedb/PROTO.localhost.rev OLD_FILES+=etc/namedb/make-localhost # 20070618: shared library version bump OLD_LIBS+=lib/libalias.so.5 OLD_LIBS+=lib/libbsnmp.so.3 OLD_LIBS+=lib/libncurses.so.6 OLD_LIBS+=lib/libncursesw.so.6 OLD_LIBS+=lib/libreadline.so.6 OLD_LIBS+=usr/lib/libdialog.so.5 OLD_LIBS+=usr/lib/libgnuregex.so.3 OLD_LIBS+=usr/lib/libhistory.so.6 OLD_LIBS+=usr/lib/libpam.so.3 OLD_LIBS+=usr/lib/libssh.so.3 OLD_LIBS+=usr/lib/pam_chroot.so.3 OLD_LIBS+=usr/lib/pam_deny.so.3 OLD_LIBS+=usr/lib/pam_echo.so.3 OLD_LIBS+=usr/lib/pam_exec.so.3 OLD_LIBS+=usr/lib/pam_ftpusers.so.3 OLD_LIBS+=usr/lib/pam_group.so.3 OLD_LIBS+=usr/lib/pam_guest.so.3 OLD_LIBS+=usr/lib/pam_krb5.so.3 OLD_LIBS+=usr/lib/pam_ksu.so.3 OLD_LIBS+=usr/lib/pam_lastlog.so.3 OLD_LIBS+=usr/lib/pam_login_access.so.3 OLD_LIBS+=usr/lib/pam_nologin.so.3 OLD_LIBS+=usr/lib/pam_opie.so.3 OLD_LIBS+=usr/lib/pam_opieaccess.so.3 OLD_LIBS+=usr/lib/pam_passwdqc.so.3 OLD_LIBS+=usr/lib/pam_permit.so.3 OLD_LIBS+=usr/lib/pam_radius.so.3 OLD_LIBS+=usr/lib/pam_rhosts.so.3 OLD_LIBS+=usr/lib/pam_rootok.so.3 OLD_LIBS+=usr/lib/pam_securetty.so.3 OLD_LIBS+=usr/lib/pam_self.so.3 OLD_LIBS+=usr/lib/pam_ssh.so.3 OLD_LIBS+=usr/lib/pam_tacplus.so.3 OLD_LIBS+=usr/lib/pam_unix.so.3 OLD_LIBS+=usr/lib/snmp_atm.so.4 OLD_LIBS+=usr/lib/snmp_bridge.so.4 OLD_LIBS+=usr/lib/snmp_hostres.so.4 OLD_LIBS+=usr/lib/snmp_mibII.so.4 OLD_LIBS+=usr/lib/snmp_netgraph.so.4 OLD_LIBS+=usr/lib/snmp_pf.so.4 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libalias.so.5 OLD_LIBS+=usr/lib32/libbsnmp.so.3 OLD_LIBS+=usr/lib32/libdialog.so.5 OLD_LIBS+=usr/lib32/libgnuregex.so.3 OLD_LIBS+=usr/lib32/libhistory.so.6 OLD_LIBS+=usr/lib32/libncurses.so.6 OLD_LIBS+=usr/lib32/libncursesw.so.6 OLD_LIBS+=usr/lib32/libpam.so.3 OLD_LIBS+=usr/lib32/libreadline.so.6 OLD_LIBS+=usr/lib32/libssh.so.3 OLD_LIBS+=usr/lib32/pam_chroot.so.3 OLD_LIBS+=usr/lib32/pam_deny.so.3 OLD_LIBS+=usr/lib32/pam_echo.so.3 OLD_LIBS+=usr/lib32/pam_exec.so.3 OLD_LIBS+=usr/lib32/pam_ftpusers.so.3 OLD_LIBS+=usr/lib32/pam_group.so.3 OLD_LIBS+=usr/lib32/pam_guest.so.3 OLD_LIBS+=usr/lib32/pam_krb5.so.3 OLD_LIBS+=usr/lib32/pam_ksu.so.3 OLD_LIBS+=usr/lib32/pam_lastlog.so.3 OLD_LIBS+=usr/lib32/pam_login_access.so.3 OLD_LIBS+=usr/lib32/pam_nologin.so.3 OLD_LIBS+=usr/lib32/pam_opie.so.3 OLD_LIBS+=usr/lib32/pam_opieaccess.so.3 OLD_LIBS+=usr/lib32/pam_passwdqc.so.3 OLD_LIBS+=usr/lib32/pam_permit.so.3 OLD_LIBS+=usr/lib32/pam_radius.so.3 OLD_LIBS+=usr/lib32/pam_rhosts.so.3 OLD_LIBS+=usr/lib32/pam_rootok.so.3 OLD_LIBS+=usr/lib32/pam_securetty.so.3 OLD_LIBS+=usr/lib32/pam_self.so.3 OLD_LIBS+=usr/lib32/pam_ssh.so.3 OLD_LIBS+=usr/lib32/pam_tacplus.so.3 OLD_LIBS+=usr/lib32/pam_unix.so.3 .endif # 20070613: IPX over IP tunnel removal OLD_FILES+=usr/include/netipx/ipx_ip.h # 20070605: sched_core removal OLD_FILES+=usr/share/man/man4/sched_core.4.gz # 20070603: BIND 9.4.1 import OLD_LIBS+=usr/lib/liblwres.so.10 # 20070521: shared library version bump OLD_LIBS+=lib/libatm.so.4 OLD_LIBS+=lib/libbegemot.so.2 OLD_LIBS+=lib/libbsdxml.so.2 OLD_LIBS+=lib/libcam.so.3 OLD_LIBS+=lib/libcrypt.so.3 OLD_LIBS+=lib/libdevstat.so.5 OLD_LIBS+=lib/libedit.so.5 OLD_LIBS+=lib/libgeom.so.3 OLD_LIBS+=lib/libipsec.so.2 OLD_LIBS+=lib/libipx.so.3 OLD_LIBS+=lib/libkiconv.so.2 OLD_LIBS+=lib/libkse.so.2 OLD_LIBS+=lib/libkvm.so.3 OLD_LIBS+=lib/libm.so.4 OLD_LIBS+=lib/libmd.so.3 OLD_LIBS+=lib/libpcap.so.4 OLD_LIBS+=lib/libpthread.so.2 OLD_LIBS+=lib/libsbuf.so.3 OLD_LIBS+=lib/libthr.so.2 OLD_LIBS+=lib/libufs.so.3 OLD_LIBS+=lib/libutil.so.6 OLD_LIBS+=lib/libz.so.3 OLD_LIBS+=usr/lib/libbluetooth.so.2 OLD_LIBS+=usr/lib/libbsm.so.1 OLD_LIBS+=usr/lib/libbz2.so.2 OLD_LIBS+=usr/lib/libcalendar.so.3 OLD_LIBS+=usr/lib/libcom_err.so.3 OLD_LIBS+=usr/lib/libdevinfo.so.3 OLD_LIBS+=usr/lib/libfetch.so.4 OLD_LIBS+=usr/lib/libform.so.3 OLD_LIBS+=usr/lib/libformw.so.3 OLD_LIBS+=usr/lib/libftpio.so.6 OLD_LIBS+=usr/lib/libgpib.so.1 OLD_LIBS+=usr/lib/libkse.so.2 OLD_LIBS+=usr/lib/libmagic.so.2 OLD_LIBS+=usr/lib/libmemstat.so.1 OLD_LIBS+=usr/lib/libmenu.so.3 OLD_LIBS+=usr/lib/libmenuw.so.3 OLD_LIBS+=usr/lib/libmilter.so.3 OLD_LIBS+=usr/lib/libmp.so.5 OLD_LIBS+=usr/lib/libncp.so.2 OLD_LIBS+=usr/lib/libnetgraph.so.2 OLD_LIBS+=usr/lib/libngatm.so.2 OLD_LIBS+=usr/lib/libopie.so.4 OLD_LIBS+=usr/lib/libpanel.so.3 OLD_LIBS+=usr/lib/libpanelw.so.3 OLD_LIBS+=usr/lib/libpmc.so.3 OLD_LIBS+=usr/lib/libradius.so.2 OLD_LIBS+=usr/lib/librpcsvc.so.3 OLD_LIBS+=usr/lib/libsdp.so.2 OLD_LIBS+=usr/lib/libsmb.so.2 OLD_LIBS+=usr/lib/libstdc++.so.5 OLD_LIBS+=usr/lib/libtacplus.so.2 OLD_LIBS+=usr/lib/libthr.so.2 OLD_LIBS+=usr/lib/libthread_db.so.2 OLD_LIBS+=usr/lib/libugidfw.so.2 OLD_LIBS+=usr/lib/libusbhid.so.2 OLD_LIBS+=usr/lib/libvgl.so.4 OLD_LIBS+=usr/lib/libwrap.so.4 OLD_LIBS+=usr/lib/libypclnt.so.2 OLD_LIBS+=usr/lib/snmp_atm.so.3 OLD_LIBS+=usr/lib/snmp_bridge.so.3 OLD_LIBS+=usr/lib/snmp_hostres.so.3 OLD_LIBS+=usr/lib/snmp_mibII.so.3 OLD_LIBS+=usr/lib/snmp_netgraph.so.3 OLD_LIBS+=usr/lib/snmp_pf.so.3 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libatm.so.4 OLD_LIBS+=usr/lib32/libbegemot.so.2 OLD_LIBS+=usr/lib32/libbluetooth.so.2 OLD_LIBS+=usr/lib32/libbsdxml.so.2 OLD_LIBS+=usr/lib32/libbsm.so.1 OLD_LIBS+=usr/lib32/libbz2.so.2 OLD_LIBS+=usr/lib32/libcalendar.so.3 OLD_LIBS+=usr/lib32/libcam.so.3 OLD_LIBS+=usr/lib32/libcom_err.so.3 OLD_LIBS+=usr/lib32/libcrypt.so.3 OLD_LIBS+=usr/lib32/libdevinfo.so.3 OLD_LIBS+=usr/lib32/libdevstat.so.5 OLD_LIBS+=usr/lib32/libedit.so.5 OLD_LIBS+=usr/lib32/libfetch.so.4 OLD_LIBS+=usr/lib32/libform.so.3 OLD_LIBS+=usr/lib32/libformw.so.3 OLD_LIBS+=usr/lib32/libftpio.so.6 OLD_LIBS+=usr/lib32/libgeom.so.3 OLD_LIBS+=usr/lib32/libgpib.so.1 OLD_LIBS+=usr/lib32/libipsec.so.2 OLD_LIBS+=usr/lib32/libipx.so.3 OLD_LIBS+=usr/lib32/libkiconv.so.2 OLD_LIBS+=usr/lib32/libkse.so.2 OLD_LIBS+=usr/lib32/libkvm.so.3 OLD_LIBS+=usr/lib32/libm.so.4 OLD_LIBS+=usr/lib32/libmagic.so.2 OLD_LIBS+=usr/lib32/libmd.so.3 OLD_LIBS+=usr/lib32/libmemstat.so.1 OLD_LIBS+=usr/lib32/libmenu.so.3 OLD_LIBS+=usr/lib32/libmenuw.so.3 OLD_LIBS+=usr/lib32/libmilter.so.3 OLD_LIBS+=usr/lib32/libmp.so.5 OLD_LIBS+=usr/lib32/libncp.so.2 OLD_LIBS+=usr/lib32/libnetgraph.so.2 OLD_LIBS+=usr/lib32/libngatm.so.2 OLD_LIBS+=usr/lib32/libopie.so.4 OLD_LIBS+=usr/lib32/libpanel.so.3 OLD_LIBS+=usr/lib32/libpanelw.so.3 OLD_LIBS+=usr/lib32/libpcap.so.4 OLD_LIBS+=usr/lib32/libpmc.so.3 OLD_LIBS+=usr/lib32/libpthread.so.2 OLD_LIBS+=usr/lib32/libradius.so.2 OLD_LIBS+=usr/lib32/librpcsvc.so.3 OLD_LIBS+=usr/lib32/libsbuf.so.3 OLD_LIBS+=usr/lib32/libsdp.so.2 OLD_LIBS+=usr/lib32/libsmb.so.2 OLD_LIBS+=usr/lib32/libstdc++.so.5 OLD_LIBS+=usr/lib32/libtacplus.so.2 OLD_LIBS+=usr/lib32/libthr.so.2 OLD_LIBS+=usr/lib32/libthread_db.so.2 OLD_LIBS+=usr/lib32/libufs.so.3 OLD_LIBS+=usr/lib32/libugidfw.so.2 OLD_LIBS+=usr/lib32/libusbhid.so.2 OLD_LIBS+=usr/lib32/libutil.so.6 OLD_LIBS+=usr/lib32/libvgl.so.4 OLD_LIBS+=usr/lib32/libwrap.so.4 OLD_LIBS+=usr/lib32/libypclnt.so.2 OLD_LIBS+=usr/lib32/libz.so.3 .endif # 20070519: GCC 4.2 OLD_FILES+=usr/bin/f77 OLD_FILES+=usr/bin/protoize OLD_FILES+=usr/include/g2c.h OLD_FILES+=usr/libexec/f771 OLD_FILES+=usr/share/info/g77.info.gz OLD_FILES+=usr/share/man/man1/f77.1.gz OLD_FILES+=usr/include/c++/3.4/algorithm OLD_FILES+=usr/include/c++/3.4/backward/algo.h OLD_FILES+=usr/include/c++/3.4/backward/algobase.h OLD_FILES+=usr/include/c++/3.4/backward/alloc.h OLD_FILES+=usr/include/c++/3.4/backward/backward_warning.h OLD_FILES+=usr/include/c++/3.4/backward/bvector.h OLD_FILES+=usr/include/c++/3.4/backward/complex.h OLD_FILES+=usr/include/c++/3.4/backward/defalloc.h OLD_FILES+=usr/include/c++/3.4/backward/deque.h OLD_FILES+=usr/include/c++/3.4/backward/fstream.h OLD_FILES+=usr/include/c++/3.4/backward/function.h OLD_FILES+=usr/include/c++/3.4/backward/hash_map.h OLD_FILES+=usr/include/c++/3.4/backward/hash_set.h OLD_FILES+=usr/include/c++/3.4/backward/hashtable.h OLD_FILES+=usr/include/c++/3.4/backward/heap.h OLD_FILES+=usr/include/c++/3.4/backward/iomanip.h OLD_FILES+=usr/include/c++/3.4/backward/iostream.h OLD_FILES+=usr/include/c++/3.4/backward/istream.h OLD_FILES+=usr/include/c++/3.4/backward/iterator.h OLD_FILES+=usr/include/c++/3.4/backward/list.h OLD_FILES+=usr/include/c++/3.4/backward/map.h OLD_FILES+=usr/include/c++/3.4/backward/multimap.h OLD_FILES+=usr/include/c++/3.4/backward/multiset.h OLD_FILES+=usr/include/c++/3.4/backward/new.h OLD_FILES+=usr/include/c++/3.4/backward/ostream.h OLD_FILES+=usr/include/c++/3.4/backward/pair.h OLD_FILES+=usr/include/c++/3.4/backward/queue.h OLD_FILES+=usr/include/c++/3.4/backward/rope.h OLD_FILES+=usr/include/c++/3.4/backward/set.h OLD_FILES+=usr/include/c++/3.4/backward/slist.h OLD_FILES+=usr/include/c++/3.4/backward/stack.h OLD_FILES+=usr/include/c++/3.4/backward/stream.h OLD_FILES+=usr/include/c++/3.4/backward/streambuf.h OLD_FILES+=usr/include/c++/3.4/backward/strstream OLD_FILES+=usr/include/c++/3.4/backward/tempbuf.h OLD_FILES+=usr/include/c++/3.4/backward/tree.h OLD_FILES+=usr/include/c++/3.4/backward/vector.h OLD_FILES+=usr/include/c++/3.4/bits/allocator.h OLD_FILES+=usr/include/c++/3.4/bits/atomic_word.h OLD_FILES+=usr/include/c++/3.4/bits/atomicity.h OLD_FILES+=usr/include/c++/3.4/bits/basic_file.h OLD_FILES+=usr/include/c++/3.4/bits/basic_ios.h OLD_FILES+=usr/include/c++/3.4/bits/basic_ios.tcc OLD_FILES+=usr/include/c++/3.4/bits/basic_string.h OLD_FILES+=usr/include/c++/3.4/bits/basic_string.tcc OLD_FILES+=usr/include/c++/3.4/bits/boost_concept_check.h OLD_FILES+=usr/include/c++/3.4/bits/c++allocator.h OLD_FILES+=usr/include/c++/3.4/bits/c++config.h OLD_FILES+=usr/include/c++/3.4/bits/c++io.h OLD_FILES+=usr/include/c++/3.4/bits/c++locale.h OLD_FILES+=usr/include/c++/3.4/bits/c++locale_internal.h OLD_FILES+=usr/include/c++/3.4/bits/char_traits.h OLD_FILES+=usr/include/c++/3.4/bits/cmath.tcc OLD_FILES+=usr/include/c++/3.4/bits/codecvt.h OLD_FILES+=usr/include/c++/3.4/bits/codecvt_specializations.h OLD_FILES+=usr/include/c++/3.4/bits/concept_check.h OLD_FILES+=usr/include/c++/3.4/bits/concurrence.h OLD_FILES+=usr/include/c++/3.4/bits/cpp_type_traits.h OLD_FILES+=usr/include/c++/3.4/bits/ctype_base.h OLD_FILES+=usr/include/c++/3.4/bits/ctype_inline.h OLD_FILES+=usr/include/c++/3.4/bits/ctype_noninline.h OLD_FILES+=usr/include/c++/3.4/bits/deque.tcc OLD_FILES+=usr/include/c++/3.4/bits/fstream.tcc OLD_FILES+=usr/include/c++/3.4/bits/functexcept.h OLD_FILES+=usr/include/c++/3.4/bits/gslice.h OLD_FILES+=usr/include/c++/3.4/bits/gslice_array.h OLD_FILES+=usr/include/c++/3.4/bits/gthr-default.h OLD_FILES+=usr/include/c++/3.4/bits/gthr-posix.h OLD_FILES+=usr/include/c++/3.4/bits/gthr-single.h OLD_FILES+=usr/include/c++/3.4/bits/gthr.h OLD_FILES+=usr/include/c++/3.4/bits/indirect_array.h OLD_FILES+=usr/include/c++/3.4/bits/ios_base.h OLD_FILES+=usr/include/c++/3.4/bits/istream.tcc OLD_FILES+=usr/include/c++/3.4/bits/list.tcc OLD_FILES+=usr/include/c++/3.4/bits/locale_classes.h OLD_FILES+=usr/include/c++/3.4/bits/locale_facets.h OLD_FILES+=usr/include/c++/3.4/bits/locale_facets.tcc OLD_FILES+=usr/include/c++/3.4/bits/localefwd.h OLD_FILES+=usr/include/c++/3.4/bits/mask_array.h OLD_FILES+=usr/include/c++/3.4/bits/messages_members.h OLD_FILES+=usr/include/c++/3.4/bits/os_defines.h OLD_FILES+=usr/include/c++/3.4/bits/ostream.tcc OLD_FILES+=usr/include/c++/3.4/bits/postypes.h OLD_FILES+=usr/include/c++/3.4/bits/slice_array.h OLD_FILES+=usr/include/c++/3.4/bits/sstream.tcc OLD_FILES+=usr/include/c++/3.4/bits/stl_algo.h OLD_FILES+=usr/include/c++/3.4/bits/stl_algobase.h OLD_FILES+=usr/include/c++/3.4/bits/stl_bvector.h OLD_FILES+=usr/include/c++/3.4/bits/stl_construct.h OLD_FILES+=usr/include/c++/3.4/bits/stl_deque.h OLD_FILES+=usr/include/c++/3.4/bits/stl_function.h OLD_FILES+=usr/include/c++/3.4/bits/stl_heap.h OLD_FILES+=usr/include/c++/3.4/bits/stl_iterator.h OLD_FILES+=usr/include/c++/3.4/bits/stl_iterator_base_funcs.h OLD_FILES+=usr/include/c++/3.4/bits/stl_iterator_base_types.h OLD_FILES+=usr/include/c++/3.4/bits/stl_list.h OLD_FILES+=usr/include/c++/3.4/bits/stl_map.h OLD_FILES+=usr/include/c++/3.4/bits/stl_multimap.h OLD_FILES+=usr/include/c++/3.4/bits/stl_multiset.h OLD_FILES+=usr/include/c++/3.4/bits/stl_numeric.h OLD_FILES+=usr/include/c++/3.4/bits/stl_pair.h OLD_FILES+=usr/include/c++/3.4/bits/stl_queue.h OLD_FILES+=usr/include/c++/3.4/bits/stl_raw_storage_iter.h OLD_FILES+=usr/include/c++/3.4/bits/stl_relops.h OLD_FILES+=usr/include/c++/3.4/bits/stl_set.h OLD_FILES+=usr/include/c++/3.4/bits/stl_stack.h OLD_FILES+=usr/include/c++/3.4/bits/stl_tempbuf.h OLD_FILES+=usr/include/c++/3.4/bits/stl_threads.h OLD_FILES+=usr/include/c++/3.4/bits/stl_tree.h OLD_FILES+=usr/include/c++/3.4/bits/stl_uninitialized.h OLD_FILES+=usr/include/c++/3.4/bits/stl_vector.h OLD_FILES+=usr/include/c++/3.4/bits/stream_iterator.h OLD_FILES+=usr/include/c++/3.4/bits/streambuf.tcc OLD_FILES+=usr/include/c++/3.4/bits/streambuf_iterator.h OLD_FILES+=usr/include/c++/3.4/bits/stringfwd.h OLD_FILES+=usr/include/c++/3.4/bits/time_members.h OLD_FILES+=usr/include/c++/3.4/bits/type_traits.h OLD_FILES+=usr/include/c++/3.4/bits/valarray_after.h OLD_FILES+=usr/include/c++/3.4/bits/valarray_array.h OLD_FILES+=usr/include/c++/3.4/bits/valarray_array.tcc OLD_FILES+=usr/include/c++/3.4/bits/valarray_before.h OLD_FILES+=usr/include/c++/3.4/bits/vector.tcc OLD_FILES+=usr/include/c++/3.4/bitset OLD_FILES+=usr/include/c++/3.4/cassert OLD_FILES+=usr/include/c++/3.4/cctype OLD_FILES+=usr/include/c++/3.4/cerrno OLD_FILES+=usr/include/c++/3.4/cfloat OLD_FILES+=usr/include/c++/3.4/ciso646 OLD_FILES+=usr/include/c++/3.4/climits OLD_FILES+=usr/include/c++/3.4/clocale OLD_FILES+=usr/include/c++/3.4/cmath OLD_FILES+=usr/include/c++/3.4/complex OLD_FILES+=usr/include/c++/3.4/csetjmp OLD_FILES+=usr/include/c++/3.4/csignal OLD_FILES+=usr/include/c++/3.4/cstdarg OLD_FILES+=usr/include/c++/3.4/cstddef OLD_FILES+=usr/include/c++/3.4/cstdio OLD_FILES+=usr/include/c++/3.4/cstdlib OLD_FILES+=usr/include/c++/3.4/cstring OLD_FILES+=usr/include/c++/3.4/ctime OLD_FILES+=usr/include/c++/3.4/cwchar OLD_FILES+=usr/include/c++/3.4/cwctype OLD_FILES+=usr/include/c++/3.4/cxxabi.h OLD_FILES+=usr/include/c++/3.4/debug/bitset OLD_FILES+=usr/include/c++/3.4/debug/debug.h OLD_FILES+=usr/include/c++/3.4/debug/deque OLD_FILES+=usr/include/c++/3.4/debug/formatter.h OLD_FILES+=usr/include/c++/3.4/debug/hash_map OLD_FILES+=usr/include/c++/3.4/debug/hash_map.h OLD_FILES+=usr/include/c++/3.4/debug/hash_multimap.h OLD_FILES+=usr/include/c++/3.4/debug/hash_multiset.h OLD_FILES+=usr/include/c++/3.4/debug/hash_set OLD_FILES+=usr/include/c++/3.4/debug/hash_set.h OLD_FILES+=usr/include/c++/3.4/debug/list OLD_FILES+=usr/include/c++/3.4/debug/map OLD_FILES+=usr/include/c++/3.4/debug/map.h OLD_FILES+=usr/include/c++/3.4/debug/multimap.h OLD_FILES+=usr/include/c++/3.4/debug/multiset.h OLD_FILES+=usr/include/c++/3.4/debug/safe_base.h OLD_FILES+=usr/include/c++/3.4/debug/safe_iterator.h OLD_FILES+=usr/include/c++/3.4/debug/safe_iterator.tcc OLD_FILES+=usr/include/c++/3.4/debug/safe_sequence.h OLD_FILES+=usr/include/c++/3.4/debug/set OLD_FILES+=usr/include/c++/3.4/debug/set.h OLD_FILES+=usr/include/c++/3.4/debug/string OLD_FILES+=usr/include/c++/3.4/debug/vector OLD_FILES+=usr/include/c++/3.4/deque OLD_FILES+=usr/include/c++/3.4/exception OLD_FILES+=usr/include/c++/3.4/exception_defines.h OLD_FILES+=usr/include/c++/3.4/ext/algorithm OLD_FILES+=usr/include/c++/3.4/ext/bitmap_allocator.h OLD_FILES+=usr/include/c++/3.4/ext/debug_allocator.h OLD_FILES+=usr/include/c++/3.4/ext/enc_filebuf.h OLD_FILES+=usr/include/c++/3.4/ext/functional OLD_FILES+=usr/include/c++/3.4/ext/hash_fun.h OLD_FILES+=usr/include/c++/3.4/ext/hash_map OLD_FILES+=usr/include/c++/3.4/ext/hash_set OLD_FILES+=usr/include/c++/3.4/ext/hashtable.h OLD_FILES+=usr/include/c++/3.4/ext/iterator OLD_FILES+=usr/include/c++/3.4/ext/malloc_allocator.h OLD_FILES+=usr/include/c++/3.4/ext/memory OLD_FILES+=usr/include/c++/3.4/ext/mt_allocator.h OLD_FILES+=usr/include/c++/3.4/ext/new_allocator.h OLD_FILES+=usr/include/c++/3.4/ext/numeric OLD_FILES+=usr/include/c++/3.4/ext/pod_char_traits.h OLD_FILES+=usr/include/c++/3.4/ext/pool_allocator.h OLD_FILES+=usr/include/c++/3.4/ext/rb_tree OLD_FILES+=usr/include/c++/3.4/ext/rope OLD_FILES+=usr/include/c++/3.4/ext/ropeimpl.h OLD_FILES+=usr/include/c++/3.4/ext/slist OLD_FILES+=usr/include/c++/3.4/ext/stdio_filebuf.h OLD_FILES+=usr/include/c++/3.4/ext/stdio_sync_filebuf.h OLD_FILES+=usr/include/c++/3.4/fstream OLD_FILES+=usr/include/c++/3.4/functional OLD_FILES+=usr/include/c++/3.4/iomanip OLD_FILES+=usr/include/c++/3.4/ios OLD_FILES+=usr/include/c++/3.4/iosfwd OLD_FILES+=usr/include/c++/3.4/iostream OLD_FILES+=usr/include/c++/3.4/istream OLD_FILES+=usr/include/c++/3.4/iterator OLD_FILES+=usr/include/c++/3.4/limits OLD_FILES+=usr/include/c++/3.4/list OLD_FILES+=usr/include/c++/3.4/locale OLD_FILES+=usr/include/c++/3.4/map OLD_FILES+=usr/include/c++/3.4/memory OLD_FILES+=usr/include/c++/3.4/new OLD_FILES+=usr/include/c++/3.4/numeric OLD_FILES+=usr/include/c++/3.4/ostream OLD_FILES+=usr/include/c++/3.4/queue OLD_FILES+=usr/include/c++/3.4/set OLD_FILES+=usr/include/c++/3.4/sstream OLD_FILES+=usr/include/c++/3.4/stack OLD_FILES+=usr/include/c++/3.4/stdexcept OLD_FILES+=usr/include/c++/3.4/streambuf OLD_FILES+=usr/include/c++/3.4/string OLD_FILES+=usr/include/c++/3.4/typeinfo OLD_FILES+=usr/include/c++/3.4/utility OLD_FILES+=usr/include/c++/3.4/valarray OLD_FILES+=usr/include/c++/3.4/vector OLD_DIRS+=usr/include/c++/3.4/backward OLD_DIRS+=usr/include/c++/3.4/bits OLD_DIRS+=usr/include/c++/3.4/debug OLD_DIRS+=usr/include/c++/3.4/ext OLD_DIRS+=usr/include/c++/3.4 # 20070510: zpool/zfs moved to /sbin OLD_FILES+=usr/sbin/zfs OLD_FILES+=usr/sbin/zpool # 20070423: rc.bluetooth (examples) removed OLD_FILES+=usr/share/examples/netgraph/bluetooth/rc.bluetooth # 20070421: worm.4 removed OLD_FILES+=usr/share/man/man4/worm.4.gz # 20070417: trunk(4) renamed to lagg(4) OLD_FILES+=usr/include/net/if_trunk.h # 20070409: uuidgen moved to /bin/ OLD_FILES+=usr/bin/uuidgen # 20070328: bzip2 1.0.4 OLD_FILES+=usr/share/info/bzip2.info.gz # 20070303: libarchive 2.0 OLD_LIBS+=usr/lib/libarchive.so.3 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libarchive.so.3 .endif # 20070301: remove addr2ascii and ascii2addr OLD_FILES+=usr/share/man/man3/addr2ascii.3.gz OLD_FILES+=usr/share/man/man3/ascii2addr.3.gz # 20070225: vm_page_unmanage() removed OLD_FILES+=usr/share/man/man9/vm_page_unmanage.9.gz # 20070216: VFS_VPTOFH(9) -> VOP_VPTOFH(9) OLD_FILES+=usr/share/man/man9/VFS_VPTOFH.9.gz # 20070212: kame.4 removed OLD_FILES+=usr/share/man/man4/kame.4.gz # 20070201: remove libmytinfo link OLD_FILES+=usr/lib/libmytinfo.a OLD_FILES+=usr/lib/libmytinfo.so OLD_FILES+=usr/lib/libmytinfo_p.a OLD_FILES+=usr/lib/libmytinfow.a OLD_FILES+=usr/lib/libmytinfow.so OLD_FILES+=usr/lib/libmytinfow_p.a .if ${TARGET_ARCH} == "amd64" OLD_FILES+=usr/lib32/libmytinfo.a OLD_FILES+=usr/lib32/libmytinfo.so OLD_FILES+=usr/lib32/libmytinfo_p.a OLD_FILES+=usr/lib32/libmytinfow.a OLD_FILES+=usr/lib32/libmytinfow.so OLD_FILES+=usr/lib32/libmytinfow_p.a .endif # 20070128: remove vnconfig OLD_FILES+=usr/sbin/vnconfig # 20070127: remove bpf_compat.h OLD_FILES+=usr/include/net/bpf_compat.h # 20070125: objformat bites the dust OLD_FILES+=usr/bin/objformat OLD_FILES+=usr/share/man/man1/objformat.1.gz OLD_FILES+=usr/include/objformat.h OLD_FILES+=usr/share/man/man3/getobjformat.3.gz # 20061201: remove symlink to *.so.4 libalias modules OLD_FILES+=usr/lib/libalias_cuseeme.so OLD_FILES+=usr/lib/libalias_dummy.so OLD_FILES+=usr/lib/libalias_ftp.so OLD_FILES+=usr/lib/libalias_irc.so OLD_FILES+=usr/lib/libalias_nbt.so OLD_FILES+=usr/lib/libalias_pptp.so OLD_FILES+=usr/lib/libalias_skinny.so OLD_FILES+=usr/lib/libalias_smedia.so # 20061201: remove old *.so.4 libalias modules OLD_FILES+=lib/libalias_cuseeme.so.4 OLD_FILES+=lib/libalias_dummy.so.4 OLD_FILES+=lib/libalias_ftp.so.4 OLD_FILES+=lib/libalias_irc.so.4 OLD_FILES+=lib/libalias_nbt.so.4 OLD_FILES+=lib/libalias_pptp.so.4 OLD_FILES+=lib/libalias_skinny.so.4 OLD_FILES+=lib/libalias_smedia.so.4 # 20061126: remove old man page OLD_FILES+=usr/share/man/man3/archive_read_set_bytes_per_block.3.gz # 20061125: remove old man page OLD_FILES+=usr/share/man/man9/devsw.9.gz # 20061122: remove obsolete mount programs OLD_FILES+=sbin/mount_devfs OLD_FILES+=sbin/mount_ext2fs OLD_FILES+=sbin/mount_fdescfs OLD_FILES+=sbin/mount_linprocfs OLD_FILES+=sbin/mount_procfs OLD_FILES+=sbin/mount_std OLD_FILES+=rescue/mount_devfs OLD_FILES+=rescue/mount_ext2fs OLD_FILES+=rescue/mount_fdescfs OLD_FILES+=rescue/mount_linprocfs OLD_FILES+=rescue/mount_procfs OLD_FILES+=rescue/mount_std OLD_FILES+=usr/share/man/man8/mount_devfs.8.gz OLD_FILES+=usr/share/man/man8/mount_ext2fs.8.gz OLD_FILES+=usr/share/man/man8/mount_fdescfs.8.gz OLD_FILES+=usr/share/man/man8/mount_linprocfs.8.gz OLD_FILES+=usr/share/man/man8/mount_procfs.8.gz OLD_FILES+=usr/share/man/man8/mount_std.8.gz # 20061116: uhidev.4 removed OLD_FILES+=usr/share/man/man4/uhidev.4.gz # 20061106: archive_write_prepare.3 removed OLD_FILES+=usr/share/man/man3/archive_write_prepare.3.gz .if ${TARGET_ARCH} == "ia64" # 20061104: skiload.help removed OLD_FILES+=boot/skiload.help .endif # 20061018: pccardc removed OLD_FILES+=usr/sbin/pccardc usr/share/man/man8/pccardc.8.gz # 20060930: demangle.h from contrib/libstdc++/include/ext/ OLD_FILES+=usr/include/c++/3.4/ext/demangle.h # 20060929: mrouted removed OLD_FILES+=usr/sbin/map-mbone OLD_FILES+=usr/sbin/mrinfo OLD_FILES+=usr/sbin/mrouted OLD_FILES+=usr/sbin/mtrace OLD_FILES+=usr/share/man/man8/map-mbone.8.gz OLD_FILES+=usr/share/man/man8/mrinfo.8.gz OLD_FILES+=usr/share/man/man8/mrouted.8.gz OLD_FILES+=usr/share/man/man8/mtrace.8.gz # 20060924: tcpslice removed OLD_FILES+=usr/sbin/tcpslice OLD_FILES+=usr/share/man/man1/tcpslice.1.gz # 20060829: kvmdb cleanup script removed OLD_FILES+=etc/periodic/weekly/120.clean-kvmdb # 20060822: ramdisk{,-own} have been replaced by mdconfig{,2} OLD_FILES+=etc/rc.d/ramdisk OLD_FILES+=etc/rc.d/ramdisk-own # 20060729: OpenSSL 0.9.7e -> 0.9.8b upgrade OLD_FILES+=usr/include/openssl/eng_int.h OLD_FILES+=usr/include/openssl/hw_4758_cca_err.h OLD_FILES+=usr/include/openssl/hw_aep_err.h OLD_FILES+=usr/include/openssl/hw_atalla_err.h OLD_FILES+=usr/include/openssl/hw_cswift_err.h OLD_FILES+=usr/include/openssl/hw_ncipher_err.h OLD_FILES+=usr/include/openssl/hw_nuron_err.h OLD_FILES+=usr/include/openssl/hw_sureware_err.h OLD_FILES+=usr/include/openssl/hw_ubsec_err.h # 20060713: mount_linsysfs(8) never existed in 7.x OLD_FILES+=sbin/mount_linsysfs OLD_FILES+=usr/share/man/man8/mount_linsysfs.8.gz # 20060704: KAME compat file net_osdep.h removed OLD_FILES+=usr/include/net/net_osdep.h # 20060605: man page links removed by OpenBSM 1.0 alpha 6 import OLD_FILES+=usr/share/man/man3/au_to_socket.3.gz OLD_FILES+=usr/share/man/man3/au_to_socket_ex_128.3.gz OLD_FILES+=usr/share/man/man3/au_to_socket_ex_32.3.gz # 20060517: pcvt removed OLD_FILES+=usr/share/pcvt/README.FIRST OLD_FILES+=usr/share/pcvt/Etc/xmodmap-german OLD_FILES+=usr/share/pcvt/Etc/pcvt.sh OLD_FILES+=usr/share/pcvt/Etc/pcvt.el OLD_FILES+=usr/share/pcvt/Etc/Terminfo OLD_FILES+=usr/share/pcvt/Etc/Termcap OLD_DIRS+=usr/share/pcvt/Etc OLD_FILES+=usr/share/pcvt/Doc/NotesAndHints OLD_FILES+=usr/share/pcvt/Doc/Keyboard.VT OLD_FILES+=usr/share/pcvt/Doc/Keyboard.HP OLD_FILES+=usr/share/pcvt/Doc/EscapeSequences OLD_FILES+=usr/share/pcvt/Doc/Charsets OLD_FILES+=usr/share/pcvt/Doc/CharGen OLD_FILES+=usr/share/pcvt/Doc/Bibliography OLD_FILES+=usr/share/pcvt/Doc/Acknowledgements OLD_DIRS+=usr/share/pcvt/Doc OLD_DIRS+=usr/share/pcvt OLD_FILES+=usr/share/misc/pcvtfonts/vt220l.816 OLD_FILES+=usr/share/misc/pcvtfonts/vt220l.814 OLD_FILES+=usr/share/misc/pcvtfonts/vt220l.810 OLD_FILES+=usr/share/misc/pcvtfonts/vt220l.808 OLD_FILES+=usr/share/misc/pcvtfonts/vt220h.816 OLD_FILES+=usr/share/misc/pcvtfonts/vt220h.814 OLD_FILES+=usr/share/misc/pcvtfonts/vt220h.810 OLD_FILES+=usr/share/misc/pcvtfonts/vt220h.808 OLD_DIRS+=usr/share/misc/pcvtfonts OLD_FILES+=usr/share/misc/keycap.pcvt OLD_FILES+=usr/share/man/man8/ispcvt.8.gz OLD_FILES+=usr/share/man/man5/keycap.5.gz OLD_FILES+=usr/share/man/man4/vt.4.gz OLD_FILES+=usr/share/man/man4/pcvt.4.gz OLD_FILES+=usr/share/man/man3/kgetstr.3.gz OLD_FILES+=usr/share/man/man3/kgetnum.3.gz OLD_FILES+=usr/share/man/man3/kgetflag.3.gz OLD_FILES+=usr/share/man/man3/kgetent.3.gz OLD_FILES+=usr/share/man/man3/keycap.3.gz OLD_FILES+=usr/share/man/man1/vt220keys.1.gz OLD_FILES+=usr/share/man/man1/scon.1.gz OLD_FILES+=usr/share/man/man1/loadfont.1.gz OLD_FILES+=usr/share/man/man1/kcon.1.gz OLD_FILES+=usr/share/man/man1/fontedit.1.gz OLD_FILES+=usr/share/man/man1/cursor.1.gz OLD_FILES+=usr/sbin/vt220keys OLD_FILES+=usr/sbin/scon OLD_FILES+=usr/sbin/loadfont OLD_FILES+=usr/sbin/kcon OLD_FILES+=usr/sbin/ispcvt OLD_FILES+=usr/sbin/fontedit OLD_FILES+=usr/sbin/cursor OLD_FILES+=usr/lib/libkeycap_p.a OLD_FILES+=usr/lib/libkeycap.a OLD_FILES+=usr/include/machine/pcvt_ioctl.h # 20060514: lnc(4) replaced by le(4) OLD_FILES+=usr/share/man/man4/i386/lnc.4.gz # 20060512: remove ip6fw OLD_FILES+=etc/periodic/security/600.ip6fwdenied OLD_FILES+=etc/periodic/security/650.ip6fwlimit OLD_FILES+=sbin/ip6fw OLD_FILES+=usr/include/netinet6/ip6_fw.h OLD_FILES+=usr/share/man/man8/ip6fw.8.gz # 20060424: sab(4) removed OLD_FILES+=usr/share/man/man4/sab.4.gz # 20060328: remove redundant rc.d script OLD_FILES+=etc/rc.d/ike # 20060127: revert libdisk to static-only OLD_FILES+=usr/lib/libdisk.so # 20060115: sys/pccard includes cleanup OLD_FILES+=usr/include/pccard/driver.h OLD_FILES+=usr/include/pccard/i82365.h OLD_FILES+=usr/include/pccard/meciareg.h OLD_FILES+=usr/include/pccard/pccard_nbk.h OLD_FILES+=usr/include/pccard/pcic_pci.h OLD_FILES+=usr/include/pccard/pcicvar.h OLD_FILES+=usr/include/pccard/slot.h # 20051215: rescue/nextboot.sh renamed to rescue/nextboot OLD_FILES+=rescue/nextboot.sh # 20051214: usbd(8) removed OLD_FILES+=etc/rc.d/usbd OLD_FILES+=etc/usbd.conf OLD_FILES+=usr/sbin/usbd OLD_FILES+=usr/share/man/man8/usbd.8.gz # 20051029: rc.d/ppp-user renamed to rc.d/ppp for convenience OLD_FILES+=etc/rc.d/ppp-user # 20051012: setkey(8) moved to /sbin/ OLD_FILES+=usr/sbin/setkey # 20050930: pccardd(8) removed OLD_FILES+=usr/sbin/pccardd OLD_FILES+=usr/share/man/man5/pccard.conf.5.gz OLD_FILES+=usr/share/man/man8/pccardd.8.gz # 20050927: bridge(4) replaced by if_bridge(4) OLD_FILES+=usr/include/net/bridge.h # 20050831: not implemented OLD_FILES+=usr/share/man/man3/getino.3.gz OLD_FILES+=usr/share/man/man3/putino.3.gz # 20050825: T/TCP retired several months ago OLD_FILES+=usr/share/man/man4/ttcp.4.gz # 20050805 tn3270 retired long ago OLD_FILES+=usr/share/misc/map3270 # 20050801: too old to be interesting here OLD_FILES+=usr/share/doc/papers/px.ps.gz # 20050721: moved to ports OLD_FILES+=usr/sbin/vttest OLD_FILES+=usr/share/man/man1/vttest.1.gz # 20050617: wpa man pages moved to section 8 OLD_FILES+=usr/share/man/man1/hostapd.1.gz OLD_FILES+=usr/share/man/man1/hostapd_cli.1.gz OLD_FILES+=usr/share/man/man1/wpa_cli.1.gz OLD_FILES+=usr/share/man/man1/wpa_supplicant.1.gz # 20050610: rexecd (insecure by design) OLD_FILES+=etc/pam.d/rexecd OLD_FILES+=usr/share/man/man8/rexecd.8.gz OLD_FILES+=usr/libexec/rexecd # 20050606: OpenBSD dhclient replaces ISC one OLD_FILES+=bin/omshell OLD_FILES+=sbin/omshell OLD_FILES+=usr/share/man/man1/omshell.1.gz OLD_FILES+=usr/share/man/man5/dhcp-eval.5.gz # 200504XX: ipf tools moved from /usr to / OLD_FILES+=rescue/ipfs OLD_FILES+=rescue/ipfstat OLD_FILES+=rescue/ipmon OLD_FILES+=rescue/ipnat OLD_FILES+=usr/sbin/ipftest OLD_FILES+=usr/sbin/ipresend OLD_FILES+=usr/sbin/ipsend OLD_FILES+=usr/sbin/iptest OLD_FILES+=usr/share/man/man1/ipnat.1.gz OLD_FILES+=usr/share/man/man1/ipsend.1.gz OLD_FILES+=usr/share/man/man1/iptest.1.gz OLD_FILES+=usr/share/man/man5/ipsend.5.gz # 200503XX: bsdtar takes over gtar OLD_FILES+=usr/bin/gtar OLD_FILES+=usr/share/man/man1/gtar.1.gz # 200503XX OLD_FILES+=share/man/man3/exp10.3.gz OLD_FILES+=share/man/man3/exp10f.3.gz OLD_FILES+=share/man/man3/fpsetsticky.3.gz # 20050324: updated release infrastructure OLD_FILES+=usr/share/man/man5/drivers.conf.5.gz # 20050317: removed from BIND 9 distribution OLD_FILES+=usr/share/doc/bind9/KNOWN_DEFECTS # 2005XXXX: OLD_FILES+=sbin/mount_autofs OLD_FILES+=usr/lib/libautofs.a OLD_FILES+=usr/lib/libautofs.so OLD_FILES+=usr/share/man/man8/mount_autofs.8.gz # 20050203: Merged with fortunes OLD_FILES+=usr/share/games/fortune/fortunes2 OLD_FILES+=usr/share/games/fortune/fortunes2.dat # 200501XX: OLD_FILES+=usr/libexec/getNAME # 200411XX: gvinum replaces vinum OLD_FILES+=bin/vinum OLD_FILES+=rescue/vinum OLD_FILES+=sbin/vinum OLD_FILES+=usr/share/man/man8/vinum.8.gz # 200411XX: libxpg4 removal OLD_FILES+=usr/lib/libxpg4.a OLD_FILES+=usr/lib/libxpg4.so OLD_FILES+=usr/lib/libxpg4_p.a # 20041109: replaced by em(4) OLD_FILES+=usr/share/man/man4/gx.4.gz OLD_FILES+=usr/share/man/man4/if_gx.4.gz # 20041017: rune interface removed OLD_FILES+=usr/include/rune.h OLD_FILES+=usr/share/man/man3/fgetrune.3.gz OLD_FILES+=usr/share/man/man3/fputrune.3.gz OLD_FILES+=usr/share/man/man3/fungetrune.3.gz OLD_FILES+=usr/share/man/man3/mbrrune.3.gz OLD_FILES+=usr/share/man/man3/mbrune.3.gz OLD_FILES+=usr/share/man/man3/rune.3.gz OLD_FILES+=usr/share/man/man3/setinvalidrune.3.gz OLD_FILES+=usr/share/man/man3/sgetrune.3.gz OLD_FILES+=usr/share/man/man3/sputrune.3.gz # 20040925: bind9 import OLD_FILES+=usr/bin/dnskeygen OLD_FILES+=usr/bin/dnsquery OLD_FILES+=usr/lib/libisc.a OLD_FILES+=usr/lib/libisc.so OLD_FILES+=usr/lib/libisc_p.a OLD_FILES+=usr/libexec/named-xfer OLD_FILES+=usr/sbin/named.restart OLD_FILES+=usr/sbin/ndc OLD_FILES+=usr/sbin/nslookup OLD_FILES+=usr/sbin/nsupdate OLD_FILES+=usr/share/doc/bind/html/acl.html OLD_FILES+=usr/share/doc/bind/html/address_list.html OLD_FILES+=usr/share/doc/bind/html/comments.html OLD_FILES+=usr/share/doc/bind/html/config.html OLD_FILES+=usr/share/doc/bind/html/controls.html OLD_FILES+=usr/share/doc/bind/html/docdef.html OLD_FILES+=usr/share/doc/bind/html/example.html OLD_FILES+=usr/share/doc/bind/html/include.html OLD_FILES+=usr/share/doc/bind/html/index.html OLD_FILES+=usr/share/doc/bind/html/key.html OLD_FILES+=usr/share/doc/bind/html/logging.html OLD_FILES+=usr/share/doc/bind/html/master.html OLD_FILES+=usr/share/doc/bind/html/options.html OLD_FILES+=usr/share/doc/bind/html/server.html OLD_FILES+=usr/share/doc/bind/html/trusted-keys.html OLD_FILES+=usr/share/doc/bind/html/zone.html OLD_FILES+=usr/share/doc/bind/misc/DynamicUpdate OLD_FILES+=usr/share/doc/bind/misc/FAQ.1of2 OLD_FILES+=usr/share/doc/bind/misc/FAQ.2of2 OLD_FILES+=usr/share/doc/bind/misc/rfc2317-notes.txt OLD_FILES+=usr/share/doc/bind/misc/style.txt OLD_FILES+=usr/share/man/man1/dnskeygen.1.gz OLD_FILES+=usr/share/man/man1/dnsquery.1.gz OLD_FILES+=usr/share/man/man8/named-bootconf.8.gz OLD_FILES+=usr/share/man/man8/named-xfer.8.gz OLD_FILES+=usr/share/man/man8/named.restart.8.gz OLD_FILES+=usr/share/man/man8/ndc.8.gz OLD_FILES+=usr/share/man/man8/nslookup.8.gz # 200409XX OLD_FILES+=usr/share/man/man3/ENSURE.3.gz OLD_FILES+=usr/share/man/man3/ENSURE_ERR.3.gz OLD_FILES+=usr/share/man/man3/INSIST.3.gz OLD_FILES+=usr/share/man/man3/INSIST_ERR.3.gz OLD_FILES+=usr/share/man/man3/INVARIANT.3.gz OLD_FILES+=usr/share/man/man3/INVARIANT_ERR.3.gz OLD_FILES+=usr/share/man/man3/REQUIRE.3.gz OLD_FILES+=usr/share/man/man3/REQUIRE_ERR.3.gz OLD_FILES+=usr/share/man/man3/assertion_type_to_text.3.gz OLD_FILES+=usr/share/man/man3/assertions.3.gz OLD_FILES+=usr/share/man/man3/bitncmp.3.gz OLD_FILES+=usr/share/man/man3/evAddTime.3.gz OLD_FILES+=usr/share/man/man3/evCancelConn.3.gz OLD_FILES+=usr/share/man/man3/evCancelRW.3.gz OLD_FILES+=usr/share/man/man3/evClearIdleTimer.3.gz OLD_FILES+=usr/share/man/man3/evClearTimer.3.gz OLD_FILES+=usr/share/man/man3/evCmpTime.3.gz OLD_FILES+=usr/share/man/man3/evConnFunc.3.gz OLD_FILES+=usr/share/man/man3/evConnect.3.gz OLD_FILES+=usr/share/man/man3/evConsIovec.3.gz OLD_FILES+=usr/share/man/man3/evConsTime.3.gz OLD_FILES+=usr/share/man/man3/evCreate.3.gz OLD_FILES+=usr/share/man/man3/evDefer.3.gz OLD_FILES+=usr/share/man/man3/evDeselectFD.3.gz OLD_FILES+=usr/share/man/man3/evDestroy.3.gz OLD_FILES+=usr/share/man/man3/evDispatch.3.gz OLD_FILES+=usr/share/man/man3/evDo.3.gz OLD_FILES+=usr/share/man/man3/evDrop.3.gz OLD_FILES+=usr/share/man/man3/evFileFunc.3.gz OLD_FILES+=usr/share/man/man3/evGetNext.3.gz OLD_FILES+=usr/share/man/man3/evHold.3.gz OLD_FILES+=usr/share/man/man3/evInitID.3.gz OLD_FILES+=usr/share/man/man3/evLastEventTime.3.gz OLD_FILES+=usr/share/man/man3/evListen.3.gz OLD_FILES+=usr/share/man/man3/evMainLoop.3.gz OLD_FILES+=usr/share/man/man3/evNowTime.3.gz OLD_FILES+=usr/share/man/man3/evPrintf.3.gz OLD_FILES+=usr/share/man/man3/evRead.3.gz OLD_FILES+=usr/share/man/man3/evResetTimer.3.gz OLD_FILES+=usr/share/man/man3/evSelectFD.3.gz OLD_FILES+=usr/share/man/man3/evSetDebug.3.gz OLD_FILES+=usr/share/man/man3/evSetIdleTimer.3.gz OLD_FILES+=usr/share/man/man3/evSetTimer.3.gz OLD_FILES+=usr/share/man/man3/evStreamFunc.3.gz OLD_FILES+=usr/share/man/man3/evSubTime.3.gz OLD_FILES+=usr/share/man/man3/evTestID.3.gz OLD_FILES+=usr/share/man/man3/evTimeRW.3.gz OLD_FILES+=usr/share/man/man3/evTimeSpec.3.gz OLD_FILES+=usr/share/man/man3/evTimeVal.3.gz OLD_FILES+=usr/share/man/man3/evTimerFunc.3.gz OLD_FILES+=usr/share/man/man3/evTouchIdleTimer.3.gz OLD_FILES+=usr/share/man/man3/evTryAccept.3.gz OLD_FILES+=usr/share/man/man3/evUnhold.3.gz OLD_FILES+=usr/share/man/man3/evUntimeRW.3.gz OLD_FILES+=usr/share/man/man3/evUnwait.3.gz OLD_FILES+=usr/share/man/man3/evWaitFor.3.gz OLD_FILES+=usr/share/man/man3/evWaitFunc.3.gz OLD_FILES+=usr/share/man/man3/evWrite.3.gz OLD_FILES+=usr/share/man/man3/eventlib.3.gz OLD_FILES+=usr/share/man/man3/heap.3.gz OLD_FILES+=usr/share/man/man3/heap_decreased.3.gz OLD_FILES+=usr/share/man/man3/heap_delete.3.gz OLD_FILES+=usr/share/man/man3/heap_element.3.gz OLD_FILES+=usr/share/man/man3/heap_for_each.3.gz OLD_FILES+=usr/share/man/man3/heap_free.3.gz OLD_FILES+=usr/share/man/man3/heap_increased.3.gz OLD_FILES+=usr/share/man/man3/heap_insert.3.gz OLD_FILES+=usr/share/man/man3/heap_new.3.gz OLD_FILES+=usr/share/man/man3/log_add_channel.3.gz OLD_FILES+=usr/share/man/man3/log_category_is_active.3.gz OLD_FILES+=usr/share/man/man3/log_close_stream.3.gz OLD_FILES+=usr/share/man/man3/log_dec_references.3.gz OLD_FILES+=usr/share/man/man3/log_free_channel.3.gz OLD_FILES+=usr/share/man/man3/log_free_context.3.gz OLD_FILES+=usr/share/man/man3/log_get_filename.3.gz OLD_FILES+=usr/share/man/man3/log_get_stream.3.gz OLD_FILES+=usr/share/man/man3/log_inc_references.3.gz OLD_FILES+=usr/share/man/man3/log_new_context.3.gz OLD_FILES+=usr/share/man/man3/log_new_file_channel.3.gz OLD_FILES+=usr/share/man/man3/log_new_null_channel.3.gz OLD_FILES+=usr/share/man/man3/log_new_syslog_channel.3.gz OLD_FILES+=usr/share/man/man3/log_open_stream.3.gz OLD_FILES+=usr/share/man/man3/log_option.3.gz OLD_FILES+=usr/share/man/man3/log_remove_channel.3.gz OLD_FILES+=usr/share/man/man3/log_set_file_owner.3.gz OLD_FILES+=usr/share/man/man3/log_vwrite.3.gz OLD_FILES+=usr/share/man/man3/log_write.3.gz OLD_FILES+=usr/share/man/man3/logging.3.gz OLD_FILES+=usr/share/man/man3/memcluster.3.gz OLD_FILES+=usr/share/man/man3/memget.3.gz OLD_FILES+=usr/share/man/man3/memput.3.gz OLD_FILES+=usr/share/man/man3/memstats.3.gz OLD_FILES+=usr/share/man/man3/set_assertion_failure_callback.3. OLD_FILES+=usr/share/man/man3/sigwait.3.gz OLD_FILES+=usr/share/man/man3/tree_add.3.gz OLD_FILES+=usr/share/man/man3/tree_delete.3.gz OLD_FILES+=usr/share/man/man3/tree_init.3.gz OLD_FILES+=usr/share/man/man3/tree_mung.3.gz OLD_FILES+=usr/share/man/man3/tree_srch.3.gz OLD_FILES+=usr/share/man/man3/tree_trav.3.gz # 2004XXYY: OS internal libs, no ports use them, no need to use OLD_LIBS OLD_FILES+=lib/geom/geom_concat.so.1 OLD_FILES+=lib/geom/geom_label.so.1 OLD_FILES+=lib/geom/geom_nop.so.1 OLD_FILES+=lib/geom/geom_stripe.so.1 # 200407XX OLD_FILES+=usr/sbin/kernbb OLD_FILES+=usr/sbin/ntp-genkeys OLD_FILES+=usr/sbin/ntptimeset OLD_FILES+=usr/share/man/man8/kernbb.8.gz OLD_FILES+=usr/share/man/man8/ntp-genkeys.8.gz # 20040627: usbdevs.h and usbdevs_data.h removal OLD_FILES+=usr/include/dev/usb/usbdevs.h OLD_FILES+=usr/include/dev/usb/usbdevs_data.h # 200406XX OLD_FILES+=usr/bin/gasp OLD_FILES+=usr/bin/gdbreplay OLD_FILES+=usr/share/man/man1/gasp.1.gz OLD_FILES+=sbin/mountd OLD_FILES+=sbin/mount_fdesc OLD_FILES+=sbin/mount_umap OLD_FILES+=sbin/mount_union OLD_FILES+=sbin/mount_msdos OLD_FILES+=sbin/mount_null OLD_FILES+=sbin/mount_kernfs # 200405XX: arl OLD_FILES+=usr/sbin/arlconfig OLD_FILES+=usr/share/man/man8/arlconfig.8.gz # 200403XX OLD_FILES+=bin/raidctl OLD_FILES+=sbin/raidctl OLD_FILES+=usr/bin/sasc OLD_FILES+=usr/sbin/sgsc OLD_FILES+=usr/sbin/stlload OLD_FILES+=usr/sbin/stlstats OLD_FILES+=usr/share/man/man1/sasc.1.gz OLD_FILES+=usr/share/man/man1/sgsc.1.gz OLD_FILES+=usr/share/man/man4/i386/stl.4.gz OLD_FILES+=usr/share/man/man8/raidctl.8.gz # 20040229: clean_environment() was removed after 3 days OLD_FILES+=usr/share/man/man3/clean_environment.3.gz # 20040119: installed as `isdntel' in newer systems OLD_FILES+=etc/isdn/isdntel.sh # 200XYYZZ: /lib transition clitches OLD_FILES+=lib/libalias.so OLD_FILES+=lib/libatm.so OLD_FILES+=lib/libbsdxml.so OLD_FILES+=lib/libc.so OLD_FILES+=lib/libcam.so OLD_FILES+=lib/libcrypt.so OLD_FILES+=lib/libcrypto.so OLD_FILES+=lib/libdevstat.so OLD_FILES+=lib/libedit.so OLD_FILES+=lib/libgeom.so OLD_FILES+=lib/libipsec.so OLD_FILES+=lib/libipx.so OLD_FILES+=lib/libkvm.so OLD_FILES+=lib/libm.so OLD_FILES+=lib/libmd.so OLD_FILES+=lib/libncurses.so OLD_FILES+=lib/libreadline.so OLD_FILES+=lib/libsbuf.so OLD_FILES+=lib/libufs.so OLD_FILES+=lib/libz.so # 200312XX OLD_FILES+=bin/cxconfig OLD_FILES+=sbin/cxconfig OLD_FILES+=usr/share/man/man8/cxconfig.8.gz # 200309XX OLD_FILES+=usr/bin/symorder OLD_FILES+=usr/share/man/man1/symorder.1.gz # 200308XX OLD_FILES+=usr/sbin/amldb OLD_FILES+=usr/share/man/man8/amldb.8.gz # 200307XX OLD_FILES+=sbin/mount_nwfs OLD_FILES+=sbin/mount_portalfs OLD_FILES+=sbin/mount_smbfs # 200306XX OLD_FILES+=usr/sbin/dev_mkdb OLD_FILES+=usr/share/man/man8/dev_mkdb.8.gz # 200304XX OLD_FILES+=usr/lib/libcipher.a OLD_FILES+=usr/lib/libcipher.so OLD_FILES+=usr/lib/libcipher_p.a OLD_FILES+=usr/lib/libgmp.a OLD_FILES+=usr/lib/libgmp.so OLD_FILES+=usr/lib/libgmp_p.a OLD_FILES+=usr/lib/libperl.a OLD_FILES+=usr/lib/libperl.so OLD_FILES+=usr/lib/libperl_p.a OLD_FILES+=usr/lib/libposix1e.a OLD_FILES+=usr/lib/libposix1e.so OLD_FILES+=usr/lib/libposix1e_p.a OLD_FILES+=usr/lib/libskey.a OLD_FILES+=usr/lib/libskey.so OLD_FILES+=usr/lib/libskey_p.a OLD_FILES+=usr/libexec/tradcpp0 OLD_FILES+=usr/libexec/cpp0 # 200304XX: removal of xten OLD_FILES+=usr/libexec/xtend OLD_FILES+=usr/sbin/xten OLD_FILES+=usr/share/man/man1/xten.1.gz OLD_FILES+=usr/share/man/man8/xtend.8.gz # 200303XX OLD_FILES+=usr/lib/libacl.so OLD_FILES+=usr/lib/libdescrypt.so OLD_FILES+=usr/lib/libf2c.so OLD_FILES+=usr/lib/libg++.so OLD_FILES+=usr/lib/libkdb.so OLD_FILES+=usr/lib/librsaINTL.so OLD_FILES+=usr/lib/libscrypt.so OLD_FILES+=usr/lib/libss.so # 200302XX OLD_FILES+=usr/lib/libacl.a OLD_FILES+=usr/lib/libacl_p.a OLD_FILES+=usr/lib/libkadm.a OLD_FILES+=usr/lib/libkadm.so OLD_FILES+=usr/lib/libkadm_p.a OLD_FILES+=usr/lib/libkafs.a OLD_FILES+=usr/lib/libkafs.so OLD_FILES+=usr/lib/libkafs_p.a OLD_FILES+=usr/lib/libkdb.a OLD_FILES+=usr/lib/libkdb_p.a OLD_FILES+=usr/lib/libkrb.a OLD_FILES+=usr/lib/libkrb.so OLD_FILES+=usr/lib/libkrb_p.a OLD_FILES+=usr/share/man/man3/SSL_CIPHER_get_name.3.gz OLD_FILES+=usr/share/man/man3/SSL_COMP_add_compression_method.3 OLD_FILES+=usr/share/man/man3/SSL_CTX_add_extra_chain_cert.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_add_session.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_ctrl.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_flush_sessions.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_free.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_get_verify_mode.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_load_verify_locations.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_new.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_sess_number.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_sess_set_cache_size.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_sess_set_get_cb.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_sessions.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_cert_store.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_cert_verify_callback.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_cipher_list.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_client_CA_list.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_client_cert_cb.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_default_passwd_cb.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_generate_session_id.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_info_callback.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_max_cert_list.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_mode.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_msg_callback.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_options.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_quiet_shutdown.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_session_cache_mode.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_session_id_context.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_ssl_version.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_timeout.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_tmp_dh_callback.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_tmp_rsa_callback.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_set_verify.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_use_certificate.3.gz OLD_FILES+=usr/share/man/man3/SSL_SESSION_free.3.gz OLD_FILES+=usr/share/man/man3/SSL_SESSION_get_ex_new_index.3.gz OLD_FILES+=usr/share/man/man3/SSL_SESSION_get_time.3.gz OLD_FILES+=usr/share/man/man3/SSL_accept.3.gz OLD_FILES+=usr/share/man/man3/SSL_alert_type_string.3.gz OLD_FILES+=usr/share/man/man3/SSL_clear.3.gz OLD_FILES+=usr/share/man/man3/SSL_connect.3.gz OLD_FILES+=usr/share/man/man3/SSL_do_handshake.3.gz OLD_FILES+=usr/share/man/man3/SSL_free.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_SSL_CTX.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_ciphers.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_client_CA_list.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_current_cipher.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_default_timeout.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_error.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_ex_data_X509_STORE_CTX_idx.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_ex_new_index.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_fd.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_peer_cert_chain.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_peer_certificate.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_rbio.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_session.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_verify_result.3.gz OLD_FILES+=usr/share/man/man3/SSL_get_version.3.gz OLD_FILES+=usr/share/man/man3/SSL_library_init.3.gz OLD_FILES+=usr/share/man/man3/SSL_load_client_CA_file.3.gz OLD_FILES+=usr/share/man/man3/SSL_new.3.gz OLD_FILES+=usr/share/man/man3/SSL_pending.3.gz OLD_FILES+=usr/share/man/man3/SSL_read.3.gz OLD_FILES+=usr/share/man/man3/SSL_rstate_string.3.gz OLD_FILES+=usr/share/man/man3/SSL_session_reused.3.gz OLD_FILES+=usr/share/man/man3/SSL_set_bio.3.gz OLD_FILES+=usr/share/man/man3/SSL_set_connect_state.3.gz OLD_FILES+=usr/share/man/man3/SSL_set_fd.3.gz OLD_FILES+=usr/share/man/man3/SSL_set_session.3.gz OLD_FILES+=usr/share/man/man3/SSL_set_shutdown.3.gz OLD_FILES+=usr/share/man/man3/SSL_set_verify_result.3.gz OLD_FILES+=usr/share/man/man3/SSL_shutdown.3.gz OLD_FILES+=usr/share/man/man3/SSL_state_string.3.gz OLD_FILES+=usr/share/man/man3/SSL_want.3.gz OLD_FILES+=usr/share/man/man3/SSL_write.3.gz OLD_FILES+=usr/share/man/man3/d2i_SSL_SESSION.3.gz # 200301XX OLD_FILES+=usr/share/man/man3/des_3cbc_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_3ecb_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_cbc_cksum.3.gz OLD_FILES+=usr/share/man/man3/des_cbc_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_cfb_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_ecb_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_enc_read.3.gz OLD_FILES+=usr/share/man/man3/des_enc_write.3.gz OLD_FILES+=usr/share/man/man3/des_is_weak_key.3.gz OLD_FILES+=usr/share/man/man3/des_key_sched.3.gz OLD_FILES+=usr/share/man/man3/des_ofb_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_pcbc_encrypt.3.gz OLD_FILES+=usr/share/man/man3/des_quad_cksum.3.gz OLD_FILES+=usr/share/man/man3/des_random_key.3.gz OLD_FILES+=usr/share/man/man3/des_read_2password.3.gz OLD_FILES+=usr/share/man/man3/des_read_password.3.gz OLD_FILES+=usr/share/man/man3/des_read_pw_string.3.gz OLD_FILES+=usr/share/man/man3/des_set_key.3.gz OLD_FILES+=usr/share/man/man3/des_set_odd_parity.3.gz OLD_FILES+=usr/share/man/man3/des_string_to_2key.3.gz OLD_FILES+=usr/share/man/man3/des_string_to_key.3.gz # 200212XX OLD_FILES+=usr/sbin/kenv OLD_FILES+=usr/bin/kenv OLD_FILES+=usr/sbin/elf2aout # 200210XX OLD_FILES+=usr/include/libusb.h OLD_FILES+=usr/include/libusbhid.h OLD_FILES+=usr/share/man/man3/All_FreeBSD.3.gz OLD_FILES+=usr/share/man/man3/CheckRules.3.gz OLD_FILES+=usr/share/man/man3/ChunkCanBeRoot.3.gz OLD_FILES+=usr/share/man/man3/Clone_Disk.3.gz OLD_FILES+=usr/share/man/man3/Collapse_Chunk.3.gz OLD_FILES+=usr/share/man/man3/Collapse_Disk.3.gz OLD_FILES+=usr/share/man/man3/Create_Chunk.3.gz OLD_FILES+=usr/share/man/man3/Create_Chunk_DWIM.3.gz OLD_FILES+=usr/share/man/man3/Cyl_Aligned.3.gz OLD_FILES+=usr/share/man/man3/Debug_Disk.3.gz OLD_FILES+=usr/share/man/man3/Delete_Chunk.3.gz OLD_FILES+=usr/share/man/man3/Disk_Names.3.gz OLD_FILES+=usr/share/man/man3/Free_Disk.3.gz OLD_FILES+=usr/share/man/man3/MakeDev.3.gz OLD_FILES+=usr/share/man/man3/MakeDevDisk.3.gz OLD_FILES+=usr/share/man/man3/Next_Cyl_Aligned.3.gz OLD_FILES+=usr/share/man/man3/Next_Track_Aligned.3.gz OLD_FILES+=usr/share/man/man3/Open_Disk.3.gz OLD_FILES+=usr/share/man/man3/Prev_Cyl_Aligned.3.gz OLD_FILES+=usr/share/man/man3/Prev_Track_Aligned.3.gz OLD_FILES+=usr/share/man/man3/Set_Bios_Geom.3.gz OLD_FILES+=usr/share/man/man3/Set_Boot_Blocks.3.gz OLD_FILES+=usr/share/man/man3/Set_Boot_Mgr.3.gz OLD_FILES+=usr/share/man/man3/ShowChunkFlags.3.gz OLD_FILES+=usr/share/man/man3/Track_Aligned.3.gz OLD_FILES+=usr/share/man/man3/Write_Disk.3.gz OLD_FILES+=usr/share/man/man3/slice_type_name.3.gz # 200210XX: most games moved to ports OLD_FILES+=usr/share/man/man6/adventure.6.gz OLD_FILES+=usr/share/man/man6/arithmetic.6.gz OLD_FILES+=usr/share/man/man6/atc.6.gz OLD_FILES+=usr/share/man/man6/backgammon.6.gz OLD_FILES+=usr/share/man/man6/battlestar.6.gz OLD_FILES+=usr/share/man/man6/bs.6.gz OLD_FILES+=usr/share/man/man6/canfield.6.gz OLD_FILES+=usr/share/man/man6/cfscores.6.gz OLD_FILES+=usr/share/man/man6/cribbage.6.gz OLD_FILES+=usr/share/man/man6/fish.6.gz OLD_FILES+=usr/share/man/man6/hack.6.gz OLD_FILES+=usr/share/man/man6/hangman.6.gz OLD_FILES+=usr/share/man/man6/larn.6.gz OLD_FILES+=usr/share/man/man6/mille.6.gz OLD_FILES+=usr/share/man/man6/phantasia.6.gz OLD_FILES+=usr/share/man/man6/piano.6.gz OLD_FILES+=usr/share/man/man6/pig.6.gz OLD_FILES+=usr/share/man/man6/quiz.6.gz OLD_FILES+=usr/share/man/man6/rain.6.gz OLD_FILES+=usr/share/man/man6/robots.6.gz OLD_FILES+=usr/share/man/man6/rogue.6.gz OLD_FILES+=usr/share/man/man6/sail.6.gz OLD_FILES+=usr/share/man/man6/snake.6.gz OLD_FILES+=usr/share/man/man6/snscore.6.gz OLD_FILES+=usr/share/man/man6/trek.6.gz OLD_FILES+=usr/share/man/man6/wargames.6.gz OLD_FILES+=usr/share/man/man6/worm.6.gz OLD_FILES+=usr/share/man/man6/worms.6.gz OLD_FILES+=usr/share/man/man6/wump.6.gz # 200207XX OLD_FILES+=usr/share/man/man1aout/ar.1aout.gz OLD_FILES+=usr/share/man/man1aout/as.1aout.gz OLD_FILES+=usr/share/man/man1aout/ld.1aout.gz OLD_FILES+=usr/share/man/man1aout/nm.1aout.gz OLD_FILES+=usr/share/man/man1aout/ranlib.1aout.gz OLD_FILES+=usr/share/man/man1aout/size.1aout.gz OLD_FILES+=usr/share/man/man1aout/strings.1aout.gz OLD_FILES+=usr/share/man/man1aout/strip.1aout.gz OLD_FILES+=bin/mountd OLD_FILES+=bin/nfsd # 20020707 sbin/nfsd -> usr.sbin/nfsd OLD_FILES+=sbin/nfsd # 200206XX OLD_FILES+=usr/lib/libpam_ssh.a OLD_FILES+=usr/lib/libpam_ssh_p.a OLD_FILES+=usr/bin/help OLD_FILES+=usr/bin/sccs .if ${TARGET_ARCH} != "i386" OLD_FILES+=usr/bin/gdbserver .endif OLD_FILES+=usr/bin/ssh-keysign OLD_FILES+=usr/sbin/gifconfig OLD_FILES+=usr/sbin/prefix # 200205XX OLD_FILES+=usr/bin/doscmd # 200204XX OLD_FILES+=usr/bin/a2p OLD_FILES+=usr/bin/ptx OLD_FILES+=usr/bin/pod2text OLD_FILES+=usr/bin/pod2man OLD_FILES+=usr/bin/pod2latex OLD_FILES+=usr/bin/pod2html OLD_FILES+=usr/bin/h2ph OLD_FILES+=usr/bin/dprofpp OLD_FILES+=usr/bin/c2ph OLD_FILES+=usr/bin/h2xs OLD_FILES+=usr/bin/pl2pm OLD_FILES+=usr/bin/splain OLD_FILES+=usr/bin/s2p OLD_FILES+=usr/bin/find2perl OLD_FILES+=usr/sbin/pkg_update OLD_FILES+=usr/sbin/scriptdump # 20020409 GC kget(1), userconfig is long dead. OLD_FILES+=sbin/kget OLD_FILES+=usr/share/man/man8/kget.8.gz # 200203XX OLD_FILES+=usr/lib/libss.a OLD_FILES+=usr/lib/libss_p.a OLD_FILES+=usr/lib/libtelnet.a OLD_FILES+=usr/lib/libtelnet_p.a OLD_FILES+=usr/lib/libusb.a OLD_FILES+=usr/lib/libusb.so OLD_FILES+=usr/lib/libusb_p.a OLD_FILES+=usr/sbin/diskpart # 200202XX OLD_FILES+=usr/bin/gprof4 # 200201XX OLD_FILES+=usr/sbin/linux # 2001XXXX OLD_FILES+=usr/bin/joy OLD_FILES+=usr/sbin/ibcs2 OLD_FILES+=usr/sbin/svr4 OLD_FILES+=usr/bin/chflags OLD_FILES+=usr/sbin/uuconv OLD_FILES+=usr/sbin/uuchk OLD_FILES+=usr/sbin/portmap OLD_FILES+=usr/sbin/pmap_set OLD_FILES+=usr/sbin/pmap_dump OLD_FILES+=usr/sbin/mcon OLD_FILES+=usr/sbin/stlstty OLD_FILES+=usr/sbin/ispppcontrol OLD_FILES+=usr/sbin/rndcontrol # 20011001: UUCP migration to ports OLD_FILES+=usr/bin/uucp OLD_FILES+=usr/bin/uulog OLD_FILES+=usr/bin/uuname OLD_FILES+=usr/bin/uupick OLD_FILES+=usr/bin/uusched OLD_FILES+=usr/bin/uustat OLD_FILES+=usr/bin/uuto OLD_FILES+=usr/bin/uux OLD_FILES+=usr/libexec/uucp/uucico OLD_FILES+=usr/libexec/uucp/uuxqt OLD_FILES+=usr/libexec/uucpd OLD_FILES+=usr/share/man/man1/uuconv.1.gz OLD_FILES+=usr/share/man/man1/uucp.1.gz OLD_FILES+=usr/share/man/man1/uulog.1.gz OLD_FILES+=usr/share/man/man1/uuname.1.gz OLD_FILES+=usr/share/man/man1/uupick.1.gz OLD_FILES+=usr/share/man/man1/uustat.1.gz OLD_FILES+=usr/share/man/man1/uuto.1.gz OLD_FILES+=usr/share/man/man1/uux.1.gz OLD_FILES+=usr/share/man/man8/uuchk.8.gz OLD_FILES+=usr/share/man/man8/uucico.8.gz OLD_FILES+=usr/share/man/man8/uucpd.8.gz OLD_FILES+=usr/share/man/man8/uusched.8.gz OLD_FILES+=usr/share/man/man8/uuxqt.8.gz # 20010523 mount_portal -> mount_portalfs OLD_FILES+=sbin/mount_portal OLD_FILES+=usr/share/man/man8/mount_portal.8.gz # 200104XX OLD_FILES+=usr/lib/libdescrypt.a OLD_FILES+=usr/lib/libscrypt.a OLD_FILES+=usr/lib/libscrypt_p.a OLD_FILES+=usr/sbin/pim6stat OLD_FILES+=usr/sbin/pim6sd OLD_FILES+=usr/sbin/pim6dd # 20010217 OLD_FILES+=usr/share/doc/bind/misc/dns-setup # 20001200 OLD_FILES+=usr/lib/libgcc_r_pic.a # 200009XX OLD_FILES+=usr/lib/libRSAglue.a OLD_FILES+=usr/lib/libRSAglue.so OLD_FILES+=usr/lib/librsaINTL.a OLD_FILES+=usr/lib/librsaUSA.a OLD_FILES+=usr/lib/librsaUSA.so # 200002XX ? OLD_FILES+=usr/lib/libf2c.a OLD_FILES+=usr/lib/libf2c_p.a OLD_FILES+=usr/lib/libg++.a OLD_FILES+=usr/lib/libg++_p.a # 20001006 OLD_FILES+=usr/bin/miniperl # 20000810 OLD_FILES+=usr/bin/sperl # 200001XX OLD_FILES+=usr/sbin/apmconf # 199911XX OLD_FILES+=usr/sbin/ipfstat OLD_FILES+=usr/sbin/ipmon OLD_FILES+=usr/sbin/ipnat OLD_FILES+=usr/sbin/bad144 OLD_FILES+=usr/sbin/wormcontrol OLD_FILES+=usr/sbin/named-bootconf OLD_FILES+=usr/sbin/kvm_mkdb OLD_FILES+=usr/sbin/keyadmin # 199909XX OLD_FILES+=usr/lib/libdesrypt_p.a OLD_FILES+=sbin/ft # 199903XX OLD_FILES+=sbin/modload OLD_FILES+=sbin/modunload OLD_FILES+=usr/sbin/natd # 199812XX OLD_FILES+=sbin/dset # 199809XX OLD_FILES+=sbin/scsi OLD_FILES+=sbin/scsiformat OLD_FILES+=usr/sbin/ncrcontrol OLD_FILES+=usr/sbin/tickadj # 199806XX OLD_FILES+=usr/sbin/mkdosfs # 199801XX OLD_FILES+=sbin/mount_lfs OLD_FILES+=sbin/newlfs OLD_FILES+=sbin/dumplfs OLD_FILES+=usr/sbin/qcamcontrol OLD_FILES+=usr/sbin/supscan # 1997XXXX OLD_FILES+=usr/sbin/sysctl OLD_FILES+=usr/sbin/ctm_scan OLD_FILES+=usr/sbin/addgroup OLD_FILES+=usr/sbin/rmgroup # 1996XXXX OLD_FILES+=sbin/rdisc OLD_FILES+=usr/sbin/cdplay OLD_FILES+=usr/sbin/supfilesrv OLD_FILES+=usr/sbin/routed OLD_FILES+=usr/sbin/lsdev OLD_FILES+=usr/sbin/yppasswdd ## unsorted # do we still support aout builds? #OLD_FILES+=usr/lib/aout/c++rt0.o #OLD_FILES+=usr/lib/aout/crt0.o #OLD_FILES+=usr/lib/aout/gcrt0.o #OLD_FILES+=usr/lib/aout/scrt0.o #OLD_FILES+=usr/lib/aout/sgcrt0.o OLD_FILES+=usr/bin/sperl5 OLD_FILES+=usr/bin/perl5.6.0 OLD_FILES+=usr/bin/sperl5.6.0 OLD_FILES+=usr/bin/perlbc OLD_FILES+=usr/bin/perl5.00503 OLD_FILES+=usr/bin/sperl5.00503 OLD_FILES+=usr/bin/perlbug OLD_FILES+=usr/bin/perlcc OLD_FILES+=usr/bin/perldoc OLD_FILES+=usr/bin/suidperl OLD_FILES+=usr/lib/pam_ftp.so OLD_FILES+=usr/libdata/perl/5.00503/CGI/Apache.pm OLD_FILES+=usr/libdata/perl/5.00503/CGI/Carp.pm OLD_FILES+=usr/libdata/perl/5.00503/CGI/Cookie.pm OLD_FILES+=usr/libdata/perl/5.00503/CGI/Fast.pm OLD_FILES+=usr/libdata/perl/5.00503/CGI/Push.pm OLD_FILES+=usr/libdata/perl/5.00503/CGI/Switch.pm OLD_FILES+=usr/libdata/perl/5.00503/CPAN/FirstTime.pm OLD_FILES+=usr/libdata/perl/5.00503/CPAN/Nox.pm OLD_FILES+=usr/libdata/perl/5.00503/Class/Struct.pm OLD_FILES+=usr/libdata/perl/5.00503/Devel/SelfStubber.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Command.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Embed.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Install.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Installed.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Liblist.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/MM_OS2.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/MM_Unix.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/MM_VMS.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/MM_Win32.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/MakeMaker.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Manifest.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Mkbootstrap.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Mksymlists.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/Packlist.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/inst OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/testlib.pm OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/typemap OLD_FILES+=usr/libdata/perl/5.00503/ExtUtils/xsubpp OLD_FILES+=usr/libdata/perl/5.00503/File/Spec/Mac.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Spec/OS2.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Spec/Unix.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Spec/VMS.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Spec/Win32.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Basename.pm OLD_FILES+=usr/libdata/perl/5.00503/File/CheckTree.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Compare.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Copy.pm OLD_FILES+=usr/libdata/perl/5.00503/File/DosGlob.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Find.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Path.pm OLD_FILES+=usr/libdata/perl/5.00503/File/Spec.pm OLD_FILES+=usr/libdata/perl/5.00503/File/stat.pm OLD_FILES+=usr/libdata/perl/5.00503/Getopt/Long.pm OLD_FILES+=usr/libdata/perl/5.00503/Getopt/Std.pm OLD_FILES+=usr/libdata/perl/5.00503/I18N/Collate.pm OLD_FILES+=usr/libdata/perl/5.00503/IPC/Open2.pm OLD_FILES+=usr/libdata/perl/5.00503/IPC/Open3.pm OLD_FILES+=usr/libdata/perl/5.00503/Math/BigFloat.pm OLD_FILES+=usr/libdata/perl/5.00503/Math/BigInt.pm OLD_FILES+=usr/libdata/perl/5.00503/Math/Complex.pm OLD_FILES+=usr/libdata/perl/5.00503/Math/Trig.pm OLD_FILES+=usr/libdata/perl/5.00503/Net/Ping.pm OLD_FILES+=usr/libdata/perl/5.00503/Net/hostent.pm OLD_FILES+=usr/libdata/perl/5.00503/Net/netent.pm OLD_FILES+=usr/libdata/perl/5.00503/Net/protoent.pm OLD_FILES+=usr/libdata/perl/5.00503/Net/servent.pm OLD_FILES+=usr/libdata/perl/5.00503/Pod/Functions.pm OLD_FILES+=usr/libdata/perl/5.00503/Pod/Html.pm OLD_FILES+=usr/libdata/perl/5.00503/Pod/Text.pm OLD_FILES+=usr/libdata/perl/5.00503/Search/Dict.pm OLD_FILES+=usr/libdata/perl/5.00503/Sys/Hostname.pm OLD_FILES+=usr/libdata/perl/5.00503/Sys/Syslog.pm OLD_FILES+=usr/libdata/perl/5.00503/Term/Cap.pm OLD_FILES+=usr/libdata/perl/5.00503/Term/Complete.pm OLD_FILES+=usr/libdata/perl/5.00503/Term/ReadLine.pm OLD_FILES+=usr/libdata/perl/5.00503/Test/Harness.pm OLD_FILES+=usr/libdata/perl/5.00503/Text/Abbrev.pm OLD_FILES+=usr/libdata/perl/5.00503/Text/ParseWords.pm OLD_FILES+=usr/libdata/perl/5.00503/Text/Soundex.pm OLD_FILES+=usr/libdata/perl/5.00503/Text/Tabs.pm OLD_FILES+=usr/libdata/perl/5.00503/Text/Wrap.pm OLD_FILES+=usr/libdata/perl/5.00503/Tie/Array.pm OLD_FILES+=usr/libdata/perl/5.00503/Tie/Handle.pm OLD_FILES+=usr/libdata/perl/5.00503/Tie/Hash.pm OLD_FILES+=usr/libdata/perl/5.00503/Tie/RefHash.pm OLD_FILES+=usr/libdata/perl/5.00503/Tie/Scalar.pm OLD_FILES+=usr/libdata/perl/5.00503/Tie/SubstrHash.pm OLD_FILES+=usr/libdata/perl/5.00503/Time/Local.pm OLD_FILES+=usr/libdata/perl/5.00503/Time/gmtime.pm OLD_FILES+=usr/libdata/perl/5.00503/Time/localtime.pm OLD_FILES+=usr/libdata/perl/5.00503/Time/tm.pm OLD_FILES+=usr/libdata/perl/5.00503/User/grent.pm OLD_FILES+=usr/libdata/perl/5.00503/User/pwent.pm OLD_FILES+=usr/libdata/perl/5.00503/auto/Getopt/Long/GetOptions.al OLD_FILES+=usr/libdata/perl/5.00503/auto/Getopt/Long/FindOption.al OLD_FILES+=usr/libdata/perl/5.00503/auto/Getopt/Long/Configure.al OLD_FILES+=usr/libdata/perl/5.00503/auto/Getopt/Long/config.al OLD_FILES+=usr/libdata/perl/5.00503/auto/Getopt/Long/Croak.al OLD_FILES+=usr/libdata/perl/5.00503/auto/Getopt/Long/autosplit.ix OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Deparse.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/CC.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Debug.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Showlex.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/makeliblinks OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Bblock.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/cc_harness OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Bytecode.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Stackobj.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Xref.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Lint.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Asmdata.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Assembler.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Disassembler.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/disassemble OLD_FILES+=usr/libdata/perl/5.00503/mach/B/assemble OLD_FILES+=usr/libdata/perl/5.00503/mach/B/Terse.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B/C.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/EXTERN.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/INTERN.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/XSUB.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/XSlock.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/av.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/bytecode.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/byterun.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/cc_runtime.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/config.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/cop.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/cv.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/dosish.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/embed.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/embedvar.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/fakethr.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/form.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/gv.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/handy.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/hv.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/intrpvar.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/iperlsys.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/keywords.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/mg.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/nostdio.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/objXSUB.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/objpp.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/op.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/opcode.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/patchlevel.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/perl.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/perlio.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/perlsdio.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/perlsfio.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/perlvars.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/perly.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/pp.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/pp_proto.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/proto.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/regcomp.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/regexp.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/regnodes.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/scope.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/sv.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/thrdvar.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/thread.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/unixish.h OLD_FILES+=usr/libdata/perl/5.00503/mach/CORE/util.h OLD_FILES+=usr/libdata/perl/5.00503/mach/Data/Dumper.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO/File.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO/Select.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO/Socket.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO/Handle.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO/Seekable.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO/Pipe.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IPC/SysV.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IPC/Msg.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IPC/Semaphore.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/B/B.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/B/B.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/B/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DB_File/autosplit.ix OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DB_File/DB_File.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DB_File/DB_File.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DB_File/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Data/Dumper/Dumper.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Data/Dumper/Dumper.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Data/Dumper/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/.exists OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/dl_findfile.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/dl_expandspec.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/dl_find_symbol_anywhere.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/autosplit.ix OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/DynaLoader.a OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/DynaLoader/extralibs.ld OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Fcntl/Fcntl.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Fcntl/Fcntl.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Fcntl/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/IO/IO.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/IO/IO.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/IO/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/IPC/SysV/SysV.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/IPC/SysV/SysV.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/IPC/SysV/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/NDBM_File/NDBM_File.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/NDBM_File/NDBM_File.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/NDBM_File/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Opcode/Opcode.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Opcode/Opcode.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Opcode/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/assert.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/tolower.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/toupper.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/closedir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/opendir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/readdir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/rewinddir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/errno.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/creat.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fcntl.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getgrgid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getgrnam.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/atan2.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/cos.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/exp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fabs.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/log.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/pow.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/sin.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/sqrt.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getpwnam.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getpwuid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/longjmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/setjmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/kill.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/feof.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/siglongjmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/sigsetjmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/raise.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/offsetof.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/clearerr.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fclose.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fdopen.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fgetc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fgets.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fileno.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fopen.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fprintf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fputc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fputs.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fread.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/freopen.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fscanf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fseek.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/ferror.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fflush.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fgetpos.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fsetpos.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/ftell.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fwrite.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getchar.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/gets.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/perror.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/printf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/putc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/putchar.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/puts.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/remove.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/rename.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/rewind.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/scanf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/sprintf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/sscanf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/tmpfile.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/ungetc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/vfprintf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/vprintf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/vsprintf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/abs.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/atexit.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/atof.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/atoi.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/atol.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/bsearch.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/calloc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/div.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/exit.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/free.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getenv.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/labs.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/ldiv.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/malloc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/qsort.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/rand.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/realloc.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/srand.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/system.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/memchr.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/memcmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/memcpy.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/memmove.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/memset.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strcat.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strchr.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strcmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strcpy.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strcspn.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strerror.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strlen.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strncat.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strncmp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strncpy.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strpbrk.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strrchr.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strspn.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strstr.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/strtok.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/chmod.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fstat.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/mkdir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/stat.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/umask.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/wait.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/waitpid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/gmtime.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/localtime.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/time.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/alarm.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/chdir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/chown.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/execl.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/execle.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/execlp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/execv.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/execve.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/execvp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/fork.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getcwd.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getegid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/geteuid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getgid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getgroups.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getlogin.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getpgrp.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getpid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getppid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/getuid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/isatty.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/link.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/rmdir.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/setbuf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/setgid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/setuid.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/setvbuf.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/sleep.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/unlink.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/utime.al OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/autosplit.ix OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/POSIX.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/POSIX.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/POSIX/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/SDBM_File/SDBM_File.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/SDBM_File/SDBM_File.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/SDBM_File/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Socket/Socket.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Socket/Socket.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Socket/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/attrs/attrs.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/attrs/attrs.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/attrs/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/re/re.so OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/re/re.bs OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/re/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/sdbm/extralibs.ld OLD_FILES+=usr/libdata/perl/5.00503/mach/auto/Errno/.packlist OLD_FILES+=usr/libdata/perl/5.00503/mach/Config.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/B.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/O.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/perllocal.pod OLD_FILES+=usr/libdata/perl/5.00503/mach/DB_File.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/Errno.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/Fcntl.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/IO.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/NDBM_File.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/Safe.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/Opcode.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/ops.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/POSIX.pod OLD_FILES+=usr/libdata/perl/5.00503/mach/POSIX.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/SDBM_File.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/Socket.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/attrs.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/re.pm OLD_FILES+=usr/libdata/perl/5.00503/mach/_h2ph_pre.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/a.out.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_ccb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_extend.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_periph.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_queue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_sim.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_xpt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_xpt_periph.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/cam/cam_xpt_sim.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/aio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/alias.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/assert.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/bitstring.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/calendar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/camlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/com_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/com_right.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ctype.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/curses.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/db.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/des.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/devstat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/dialog.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/dirent.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/disktab.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/dlfcn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/elf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/errno.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/eti.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/fcntl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/fetch.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/float.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/floatingpoint.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/fnmatch.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/form.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/fstab.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ftpio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/fts.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/glob.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/gmp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/gnuregex.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/grp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/histedit.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ieeefp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ifaddrs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/inttypes.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/iso646.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/kvm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/libatm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/libdisk.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/libgen.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/libusb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/libutil.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/limits.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/link.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/linker_set.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/locale.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/login_cap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/malloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/FlexLexer.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/PlotFile.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/SFile.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/_G_config.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/algo.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/algobase.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/alloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/builtinbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/bvector.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/complex.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/defalloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/deque.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/editbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/floatio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/fstream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/function.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/hash_map.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/hash_set.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/hashtable.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/heap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/indstream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/iolibio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/iomanip.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/list.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/iostdio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/iostream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/iostreamP.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/istream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/iterator.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/libio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/libioP.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/map.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/multimap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/multiset.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/new.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/ostream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/pair.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/parsestream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/pfstream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/procbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/pthread_alloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/rope.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/ropeimpl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/set.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/slist.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stack.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stdiostream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_algo.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/tree.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_algobase.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_alloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_bvector.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_config.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_construct.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_deque.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_function.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_hash_fun.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_hash_map.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_hash_set.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_hashtable.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_heap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_iterator.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_list.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_map.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_multimap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_multiset.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_numeric.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_pair.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_queue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_raw_storage_iter.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_relops.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_rope.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_set.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_slist.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_stack.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_tempbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_tree.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_uninitialized.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stl_vector.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/stream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/streambuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/strfile.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/strstream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/tempbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/type_traits.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g++/vector.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/math.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/md2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/md4.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/md5.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/memory.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/menu.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/mp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/mpool.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/mqueue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ncurses.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ndbm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netdb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nl_types.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nlist.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objformat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/opie.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/osreldate.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/panel.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/paths.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pcap-int.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pcap-namedb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pcap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/poll.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pthread.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pthread_np.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pwd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/radlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ranlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/regex.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/regexp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/resolv.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ripemd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rune.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/runetype.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sched.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/search.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/semaphore.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/setjmp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sgtty.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sha.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/signal.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/skey.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stab.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stand.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stdarg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stddef.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stdio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stdlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/strhash.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/string.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stringlist.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/strings.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/struct.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sysexits.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/syslog.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/taclib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/tar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/tcpd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/term.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/termcap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/termios.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/time.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/timers.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ttyent.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ucontext.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/unctrl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/unistd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/utime.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/utmp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/values.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/varargs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vgl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vis.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/zconf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/zlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/arpa/ftp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/arpa/inet.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/arpa/nameser.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/arpa/nameser_compat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/arpa/telnet.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/arpa/tftp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/assertions.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/ctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/dst.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/eventlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/heap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/irpmarshall.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/logging.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/memcluster.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/misc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/tree.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/isc/list.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ansi.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/apic.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/apm_bios.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/apm_segments.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/asc_ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/asm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/asmacros.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/asnames.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/atomic.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bootinfo.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus_at386.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus_memio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus_pc98.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus_pio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus_pio_ind.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/cdk.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/clock.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/comstats.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/console.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/cpu.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/cpufunc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/cputypes.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/cronyx.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/db_machdep.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/dvcfg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/elf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/endian.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/exec.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/float.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/floatingpoint.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/frame.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/globaldata.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/globals.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/gsc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_cause.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_rbch_ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_tel_ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_trace.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ieeefp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/if_wavelan_ieee.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/if_wl_wavelan.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/iic.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/in_cksum.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ioctl_bt848.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ioctl_ctx.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ioctl_fd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ioctl_meteor.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ipl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/joystick.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/limits.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/lock.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/md_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/mouse.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/mpapic.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/mtpr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/bus_dma.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/npx.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/param.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/pcaudioio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/pcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/pcb_ext.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/pcvt_ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/perfmon.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/physio_proc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/pmap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/proc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/profile.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/psl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ptrace.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/reg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/reloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/resource.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/segments.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/setjmp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/sigframe.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/signal.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/smb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/smp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/smptests.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/soundcard.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/speaker.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/specialreg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/spigot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/stdarg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/sysarch.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/trap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/tss.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/types.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/uc_device.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ucontext.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/ultrasound.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/varargs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/vm86.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/vmparam.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/wtio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/i4b_isppp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/machine/pci_cfgreg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/msdosfs/bootsect.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/msdosfs/bpb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/msdosfs/denode.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/msdosfs/direntry.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/msdosfs/fat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/msdosfs/msdosfsmount.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/bpf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/bpf_compat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/bpfdesc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/bridge.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/ethernet.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/hostcache.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_arp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_atm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_dl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_gif.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_ieee80211.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_llc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_media.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_mib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_ppp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_pppvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_slvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_sppp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_stf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_tap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_tapvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_tun.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/slip.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_tunvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_types.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_vlan_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/intrq.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/iso88025.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/net_osdep.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/netisr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/pfkeyv2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/ppp_comp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/ppp_defs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/radix.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/raw_cb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/route.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/slcompress.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/zlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_faith.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_arc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/net/if_gre.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/krpc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsdiskless.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsm_subs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsmount.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsnode.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsproto.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsrtt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsrvcache.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nfsv2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/nqnfs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/rpcv2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nfs/xdr_subs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/aarp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/at.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/at_extern.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/at_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/ddp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/ddp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/endian.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatalk/phase2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_cm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_if.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_pcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_sap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_sigmgr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_stack.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_sys.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/atm_vc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/kern_include.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/port.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netatm/queue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/netgraph.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_UI.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_async.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_bpf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_bridge.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_cisco.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_echo.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_ether.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_frame_relay.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_hole.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_iface.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_ksocket.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_lmi.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_message.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_mppc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_one2many.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_parse.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_ppp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_pppoe.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_pptpgre.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_rfc1490.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_sample.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_socket.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_socketvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_tee.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_tty.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_vjc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_eiface.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_etf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_device.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_l2tp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netgraph/ng_fec.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/icmp6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/icmp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/if_atm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/if_ether.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/if_fddi.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/igmp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/igmp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/in.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/in_gif.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/in_hostcache.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/in_pcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/in_systm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/in_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_auth.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_compat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_dummynet.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_ecn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_encap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_fil.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_flow.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_frag.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_fw.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_icmp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_mroute.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_nat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_proxy.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_state.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ipl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ipprotosw.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcp_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcp_fsm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcp_seq.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcp_timer.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/tcpip.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/udp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/udp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_fw2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet/ip_gre.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ah.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ah6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/esp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/esp6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/icmp6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/in6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/in6_gif.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/in6_ifattach.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/in6_pcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/in6_prefix.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/in6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ip6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ip6_ecn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ip6_fw.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ip6_mroute.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ip6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ip6protosw.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ipcomp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ipcomp6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ipsec.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/ipsec6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/mld6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/nd6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/pim6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/pim6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/scope6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/tcp6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/udp6_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/esp_rijndael.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netinet6/raw_ip6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/ipx.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/ipx_if.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/ipx_ip.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/ipx_pcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/ipx_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/spx.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/spx_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/spx_timer.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipx/spx_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netkey/key.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netkey/key_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netkey/key_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netkey/keydb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netkey/keysock.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netnatm/natm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_cfg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_conn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_file.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_lib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_ncp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_nls.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_rcfile.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_rq.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_sock.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_subr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/ncp_user.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netncp/nwerror.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/idp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/idp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/ns.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/ns_error.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/ns_if.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/ns_pcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/sp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/spidp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/spp_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/spp_timer.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netns/spp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfs_compr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfs_ihash.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfs_inode.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfs_subr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfs_vfsops.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ntfs/ntfsmount.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nwfs/nwfs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nwfs/nwfs_mount.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nwfs/nwfs_node.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/nwfs/nwfs_subr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/NXConstStr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/Object.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/Protocol.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/encoding.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/hash.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/objc-api.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/objc-list.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/objc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/runtime.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/sarray.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/thr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/objc/typedstream.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/asn1.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/asn1_mac.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/bio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/blowfish.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/bn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/buffer.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/cast.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/comp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/conf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/conf_api.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/crypto.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/des.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/dh.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/dsa.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/dso.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/e_os.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/e_os2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ebcdic.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/evp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hmac.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/lhash.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/md2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/md4.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/md5.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/mdc2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/obj_mac.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/objects.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/opensslconf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/opensslv.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/pem.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/pem2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/pkcs12.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/pkcs7.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/rand.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/rc2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/rc4.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/rc5.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ripemd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/rsa.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/safestack.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/sha.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ssl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ssl2.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ssl23.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ssl3.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ssl_locl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/stack.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/symhacks.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/tls1.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/tmdiff.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/txt_db.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/x509.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/x509_vfy.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/x509v3.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/idea.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/aes.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/asn1t.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/cryptlib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/des_old.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ec.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/engine.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/krb5_asn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/kssl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ocsp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ossl_typ.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ui.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ui_compat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/aes_locl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/eng_int.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_4758_cca_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_aep_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_atalla_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_cswift_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_ncipher_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_nuron_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_sureware_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/hw_ubsec_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/openssl/ui_locl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/cardinfo.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/cis.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/driver.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/i82365.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/pccard_nbk.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/slot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/meciareg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/pcic_pci.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/pccard/pcicvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/posix4/aio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/posix4/mqueue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/posix4/posix4.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/posix4/sched.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/posix4/semaphore.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/protocols/dumprestore.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/protocols/routed.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/protocols/rwhod.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/protocols/talkd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/protocols/timed.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/chardefs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/history.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/keymaps.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/readline.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/rlconf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/rlstdc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readline/tilde.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/auth.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/auth_des.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/auth_unix.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/clnt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/des.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/des_crypt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/key_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/pmap_clnt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/pmap_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/pmap_rmt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/rpc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/rpc_com.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/rpc_msg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/svc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/svc_auth.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/types.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpc/xdr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/bootparam_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/crypt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/key_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/klm_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/mount.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nfs_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nis.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nis_cache.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nis_callback.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nis_db.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nis_tags.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nislib.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/nlm_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/rex.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/rnusers.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/rquota.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/rstat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/rwall.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/sm_inter.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/spray.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/yp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/yp_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/ypclnt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/yppasswd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/ypupdate_prot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/rpcsvc/ypxfrd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/_pam_compat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/_pam_macros.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/_pam_types.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/pam_appl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/pam_malloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/pam_misc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/pam_mod_misc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/security/pam_modules.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ss/mit-sipb-copyright.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ss/ss.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/ss/ss_err.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/_posix.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ata.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/acct.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/acl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/agpio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/aio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/assym.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/blist.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/buf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/bus.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/bus_private.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/callout.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ccdvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/cdefs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/cdio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/cdrio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/chio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/clist.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/endian.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/conf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/cons.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/consio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/copyright.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ctype.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/dir.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/dataacq.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/link_elf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/device_port.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/devicestat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/dirent.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/disk.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/disklabel.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/diskslice.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/dkstat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/dmap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/domain.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/dvdio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/elf32.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/elf64.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/elf_common.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/elf_generic.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/errno.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/event.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/eventhandler.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/eventvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/exec.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/extattr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/fbio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/fcntl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/file.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/filedesc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/filio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/gmon.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/imgact.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/imgact_aout.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/imgact_elf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/inflate.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/interrupt.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/inttypes.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ioccom.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ioctl_compat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ipc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/jail.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/joystick.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/kbio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/kernel.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/kthread.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ktrace.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/libkern.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/linker.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/linker_set.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/lock.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/lockf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/malloc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/mbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/md5.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/memrange.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/mman.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/module.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/mount.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/msg.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/msgbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/mtio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/namei.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/param.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/pciio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/pioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/pipe.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/poll.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/proc.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/procfs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/protosw.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ptio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ptrace.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/queue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/random.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/reboot.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/resource.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/resourcevar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/rman.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/rtprio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/sbuf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/select.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/sem.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/shm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/signal.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/signalvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/snoop.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/socket.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/socketvar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/sockio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/soundcard.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/stat.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/syscall-hide.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/syscall.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/sysctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/sysent.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/syslimits.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/syslog.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/sysproto.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/systm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/taskqueue.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/termios.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/time.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/timeb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/timepps.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/timers.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/times.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/timex.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/tprintf.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/tty.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ttychars.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ttycom.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ttydefaults.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ttydev.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/types.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ucontext.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/ucred.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/uio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/un.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/unistd.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/unpcb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/user.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/utsname.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/vmmeter.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/vnioctl.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/vnode.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/wait.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/wormio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/xrpuio.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/kobj.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/link_aout.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/nlist_aout.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/mchain.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/fnv_hash.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/iconv.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/sys/md4.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/pmap.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/swap_pager.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_extern.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_kern.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_map.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_object.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_page.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_pageout.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_pager.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_param.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vm_zone.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/vm/vnode_pager.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/complex.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/stdbool.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/langinfo.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/netbios.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb_conn.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb_dev.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb_rq.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb_subr.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb_tran.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netsmb/smb_trantcp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/g2c.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/telnet.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/elf-hints.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/libusbhid.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/radlib_vs.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/readpassphrase.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/wchar.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/wctype.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/cast.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/castsb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/cryptodev.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/cryptosoft.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/deflate.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/rijndael.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/rmd160.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/skipjack.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/crypto/xform.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ah.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ah_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/esp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/esp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ipcomp.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ipcomp_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ipip_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ipsec.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/ipsec6.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/key.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/key_debug.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/key_var.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/keydb.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/keysock.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/netipsec/xform.ph OLD_FILES+=usr/libdata/perl/5.00503/mach/bzlib.ph OLD_FILES+=usr/libdata/perl/5.00503/pod/perl.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perl5004delta.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlapio.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlbook.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlbot.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlcall.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perldata.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perldebug.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perldelta.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perldiag.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perldsc.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlembed.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq1.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq2.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq3.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq4.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq5.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq6.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq7.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlipc.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq8.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfaq9.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlform.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlfunc.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlguts.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlhist.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perllocale.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perllol.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlmod.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlmodinstall.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlmodlib.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlobj.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlop.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlopentut.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlpod.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlport.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlre.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlref.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlreftut.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlrun.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlsec.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlstyle.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlsub.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlsyn.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlthrtut.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perltie.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perltoc.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perltoot.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perltrap.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlvar.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlxs.pod OLD_FILES+=usr/libdata/perl/5.00503/pod/perlxstut.pod OLD_FILES+=usr/libdata/perl/5.00503/AnyDBM_File.pm OLD_FILES+=usr/libdata/perl/5.00503/AutoLoader.pm OLD_FILES+=usr/libdata/perl/5.00503/AutoSplit.pm OLD_FILES+=usr/libdata/perl/5.00503/Benchmark.pm OLD_FILES+=usr/libdata/perl/5.00503/CGI.pm OLD_FILES+=usr/libdata/perl/5.00503/CPAN.pm OLD_FILES+=usr/libdata/perl/5.00503/Carp.pm OLD_FILES+=usr/libdata/perl/5.00503/Cwd.pm OLD_FILES+=usr/libdata/perl/5.00503/DirHandle.pm OLD_FILES+=usr/libdata/perl/5.00503/Dumpvalue.pm OLD_FILES+=usr/libdata/perl/5.00503/English.pm OLD_FILES+=usr/libdata/perl/5.00503/Env.pm OLD_FILES+=usr/libdata/perl/5.00503/Exporter.pm OLD_FILES+=usr/libdata/perl/5.00503/Fatal.pm OLD_FILES+=usr/libdata/perl/5.00503/FileCache.pm OLD_FILES+=usr/libdata/perl/5.00503/FileHandle.pm OLD_FILES+=usr/libdata/perl/5.00503/FindBin.pm OLD_FILES+=usr/libdata/perl/5.00503/SelectSaver.pm OLD_FILES+=usr/libdata/perl/5.00503/SelfLoader.pm OLD_FILES+=usr/libdata/perl/5.00503/Shell.pm OLD_FILES+=usr/libdata/perl/5.00503/Symbol.pm OLD_FILES+=usr/libdata/perl/5.00503/Test.pm OLD_FILES+=usr/libdata/perl/5.00503/abbrev.pl OLD_FILES+=usr/libdata/perl/5.00503/UNIVERSAL.pm OLD_FILES+=usr/libdata/perl/5.00503/assert.pl OLD_FILES+=usr/libdata/perl/5.00503/autouse.pm OLD_FILES+=usr/libdata/perl/5.00503/base.pm OLD_FILES+=usr/libdata/perl/5.00503/bigfloat.pl OLD_FILES+=usr/libdata/perl/5.00503/bigint.pl OLD_FILES+=usr/libdata/perl/5.00503/bigrat.pl OLD_FILES+=usr/libdata/perl/5.00503/blib.pm OLD_FILES+=usr/libdata/perl/5.00503/cacheout.pl OLD_FILES+=usr/libdata/perl/5.00503/chat2.pl OLD_FILES+=usr/libdata/perl/5.00503/complete.pl OLD_FILES+=usr/libdata/perl/5.00503/constant.pm OLD_FILES+=usr/libdata/perl/5.00503/ctime.pl OLD_FILES+=usr/libdata/perl/5.00503/diagnostics.pm OLD_FILES+=usr/libdata/perl/5.00503/dotsh.pl OLD_FILES+=usr/libdata/perl/5.00503/dumpvar.pl OLD_FILES+=usr/libdata/perl/5.00503/exceptions.pl OLD_FILES+=usr/libdata/perl/5.00503/fastcwd.pl OLD_FILES+=usr/libdata/perl/5.00503/fields.pm OLD_FILES+=usr/libdata/perl/5.00503/find.pl OLD_FILES+=usr/libdata/perl/5.00503/finddepth.pl OLD_FILES+=usr/libdata/perl/5.00503/flush.pl OLD_FILES+=usr/libdata/perl/5.00503/ftp.pl OLD_FILES+=usr/libdata/perl/5.00503/getcwd.pl OLD_FILES+=usr/libdata/perl/5.00503/getopt.pl OLD_FILES+=usr/libdata/perl/5.00503/getopts.pl OLD_FILES+=usr/libdata/perl/5.00503/hostname.pl OLD_FILES+=usr/libdata/perl/5.00503/importenv.pl OLD_FILES+=usr/libdata/perl/5.00503/integer.pm OLD_FILES+=usr/libdata/perl/5.00503/less.pm OLD_FILES+=usr/libdata/perl/5.00503/lib.pm OLD_FILES+=usr/libdata/perl/5.00503/locale.pm OLD_FILES+=usr/libdata/perl/5.00503/look.pl OLD_FILES+=usr/libdata/perl/5.00503/newgetopt.pl OLD_FILES+=usr/libdata/perl/5.00503/open2.pl OLD_FILES+=usr/libdata/perl/5.00503/open3.pl OLD_FILES+=usr/libdata/perl/5.00503/overload.pm OLD_FILES+=usr/libdata/perl/5.00503/perl5db.pl OLD_FILES+=usr/libdata/perl/5.00503/pwd.pl OLD_FILES+=usr/libdata/perl/5.00503/shellwords.pl OLD_FILES+=usr/libdata/perl/5.00503/sigtrap.pm OLD_FILES+=usr/libdata/perl/5.00503/stat.pl OLD_FILES+=usr/libdata/perl/5.00503/strict.pm OLD_FILES+=usr/libdata/perl/5.00503/subs.pm OLD_FILES+=usr/libdata/perl/5.00503/syslog.pl OLD_FILES+=usr/libdata/perl/5.00503/tainted.pl OLD_FILES+=usr/libdata/perl/5.00503/termcap.pl OLD_FILES+=usr/libdata/perl/5.00503/timelocal.pl OLD_FILES+=usr/libdata/perl/5.00503/validate.pl OLD_FILES+=usr/libdata/perl/5.00503/vars.pm OLD_FILES+=usr/libdata/perl/5.00503/re.pm OLD_FILES+=usr/libdata/perl/5.00503/Config.pm OLD_FILES+=usr/libdata/perl/5.00503/.exists OLD_FILES+=usr/libdata/perl/5.00503/DynaLoader.pm OLD_FILES+=usr/share/perl/man/man3/AnyDBM_File.3.gz OLD_FILES+=usr/share/perl/man/man3/AutoLoader.3.gz OLD_FILES+=usr/share/perl/man/man3/AutoSplit.3.gz OLD_FILES+=usr/share/perl/man/man3/B.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Asmdata.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Assembler.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Bblock.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Bytecode.3.gz OLD_FILES+=usr/share/perl/man/man3/B::C.3.gz OLD_FILES+=usr/share/perl/man/man3/B::CC.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Debug.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Deparse.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Disassembler.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Lint.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Showlex.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Stackobj.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Terse.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Xref.3.gz OLD_FILES+=usr/share/perl/man/man3/Benchmark.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Apache.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Carp.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Cookie.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Fast.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Push.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Switch.3.gz OLD_FILES+=usr/share/perl/man/man3/CPAN.3.gz OLD_FILES+=usr/share/perl/man/man3/CPAN::FirstTime.3.gz OLD_FILES+=usr/share/perl/man/man3/CPAN::Nox.3.gz OLD_FILES+=usr/share/perl/man/man3/Carp.3.gz OLD_FILES+=usr/share/perl/man/man3/Class::Struct.3.gz OLD_FILES+=usr/share/perl/man/man3/Config.3.gz OLD_FILES+=usr/share/perl/man/man3/Cwd.3.gz OLD_FILES+=usr/share/perl/man/man3/DB_File.3.gz OLD_FILES+=usr/share/perl/man/man3/Data::Dumper.3.gz OLD_FILES+=usr/share/perl/man/man3/Devel::SelfStubber.3.gz OLD_FILES+=usr/share/perl/man/man3/DirHandle.3.gz OLD_FILES+=usr/share/perl/man/man3/Dumpvalue.3.gz OLD_FILES+=usr/share/perl/man/man3/DynaLoader.3.gz OLD_FILES+=usr/share/perl/man/man3/English.3.gz OLD_FILES+=usr/share/perl/man/man3/Env.3.gz OLD_FILES+=usr/share/perl/man/man3/Exporter.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Command.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Embed.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Install.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Installed.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Liblist.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::MM_OS2.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::MM_Unix.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::MM_VMS.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::MM_Win32.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::MakeMaker.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Manifest.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Mkbootstrap.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Mksymlists.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::Packlist.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::testlib.3.gz OLD_FILES+=usr/share/perl/man/man3/Fatal.3.gz OLD_FILES+=usr/share/perl/man/man3/Fcntl.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Basename.3.gz OLD_FILES+=usr/share/perl/man/man3/File::CheckTree.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Compare.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Copy.3.gz OLD_FILES+=usr/share/perl/man/man3/File::DosGlob.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Find.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Path.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Spec.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Spec::Mac.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Spec::OS2.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Spec::Unix.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Spec::VMS.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Spec::Win32.3.gz OLD_FILES+=usr/share/perl/man/man3/File::stat.3.gz OLD_FILES+=usr/share/perl/man/man3/FileCache.3.gz OLD_FILES+=usr/share/perl/man/man3/IO.3.gz OLD_FILES+=usr/share/perl/man/man3/FileHandle.3.gz OLD_FILES+=usr/share/perl/man/man3/FindBin.3.gz OLD_FILES+=usr/share/perl/man/man3/GDBM_File.3.gz OLD_FILES+=usr/share/perl/man/man3/Getopt::Long.3.gz OLD_FILES+=usr/share/perl/man/man3/Getopt::Std.3.gz OLD_FILES+=usr/share/perl/man/man3/I18N::Collate.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::File.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Handle.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Pipe.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Seekable.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Select.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Socket.3.gz OLD_FILES+=usr/share/perl/man/man3/IPC::Msg.3.gz OLD_FILES+=usr/share/perl/man/man3/IPC::Open2.3.gz OLD_FILES+=usr/share/perl/man/man3/IPC::Open3.3.gz OLD_FILES+=usr/share/perl/man/man3/IPC::Semaphore.3.gz OLD_FILES+=usr/share/perl/man/man3/IPC::SysV.3.gz OLD_FILES+=usr/share/perl/man/man3/Math::BigFloat.3.gz OLD_FILES+=usr/share/perl/man/man3/Math::BigInt.3.gz OLD_FILES+=usr/share/perl/man/man3/Math::Complex.3.gz OLD_FILES+=usr/share/perl/man/man3/Math::Trig.3.gz OLD_FILES+=usr/share/perl/man/man3/NDBM_File.3.gz OLD_FILES+=usr/share/perl/man/man3/Net::Ping.3.gz OLD_FILES+=usr/share/perl/man/man3/Net::hostent.3.gz OLD_FILES+=usr/share/perl/man/man3/Net::netent.3.gz OLD_FILES+=usr/share/perl/man/man3/Net::protoent.3.gz OLD_FILES+=usr/share/perl/man/man3/Net::servent.3.gz OLD_FILES+=usr/share/perl/man/man3/O.3.gz OLD_FILES+=usr/share/perl/man/man3/ODBM_File.3.gz OLD_FILES+=usr/share/perl/man/man3/Opcode.3.gz OLD_FILES+=usr/share/perl/man/man3/POSIX.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Html.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Text.3.gz OLD_FILES+=usr/share/perl/man/man3/SDBM_File.3.gz OLD_FILES+=usr/share/perl/man/man3/Safe.3.gz OLD_FILES+=usr/share/perl/man/man3/Search::Dict.3.gz OLD_FILES+=usr/share/perl/man/man3/SelectSaver.3.gz OLD_FILES+=usr/share/perl/man/man3/SelfLoader.3.gz OLD_FILES+=usr/share/perl/man/man3/Shell.3.gz OLD_FILES+=usr/share/perl/man/man3/Socket.3.gz OLD_FILES+=usr/share/perl/man/man3/Symbol.3.gz OLD_FILES+=usr/share/perl/man/man3/re.3.gz OLD_FILES+=usr/share/perl/man/man3/Sys::Hostname.3.gz OLD_FILES+=usr/share/perl/man/man3/Sys::Syslog.3.gz OLD_FILES+=usr/share/perl/man/man3/Term::Cap.3.gz OLD_FILES+=usr/share/perl/man/man3/Term::Complete.3.gz OLD_FILES+=usr/share/perl/man/man3/Term::ReadLine.3.gz OLD_FILES+=usr/share/perl/man/man3/Test.3.gz OLD_FILES+=usr/share/perl/man/man3/Test::Harness.3.gz OLD_FILES+=usr/share/perl/man/man3/Text::Abbrev.3.gz OLD_FILES+=usr/share/perl/man/man3/Text::ParseWords.3.gz OLD_FILES+=usr/share/perl/man/man3/Text::Soundex.3.gz OLD_FILES+=usr/share/perl/man/man3/Text::Tabs.3.gz OLD_FILES+=usr/share/perl/man/man3/Text::Wrap.3.gz OLD_FILES+=usr/share/perl/man/man3/Thread.3.gz OLD_FILES+=usr/share/perl/man/man3/Thread::Queue.3.gz OLD_FILES+=usr/share/perl/man/man3/Thread::Semaphore.3.gz OLD_FILES+=usr/share/perl/man/man3/Thread::Signal.3.gz OLD_FILES+=usr/share/perl/man/man3/Thread::Specific.3.gz OLD_FILES+=usr/share/perl/man/man3/Tie::Array.3.gz OLD_FILES+=usr/share/perl/man/man3/Tie::Handle.3.gz OLD_FILES+=usr/share/perl/man/man3/Tie::Hash.3.gz OLD_FILES+=usr/share/perl/man/man3/Tie::RefHash.3.gz OLD_FILES+=usr/share/perl/man/man3/Tie::Scalar.3.gz OLD_FILES+=usr/share/perl/man/man3/Tie::SubstrHash.3.gz OLD_FILES+=usr/share/perl/man/man3/Time::Local.3.gz OLD_FILES+=usr/share/perl/man/man3/Time::gmtime.3.gz OLD_FILES+=usr/share/perl/man/man3/Time::localtime.3.gz OLD_FILES+=usr/share/perl/man/man3/Time::tm.3.gz OLD_FILES+=usr/share/perl/man/man3/UNIVERSAL.3.gz OLD_FILES+=usr/share/perl/man/man3/User::grent.3.gz OLD_FILES+=usr/share/perl/man/man3/User::pwent.3.gz OLD_FILES+=usr/share/perl/man/man3/attrs.3.gz OLD_FILES+=usr/share/perl/man/man3/autouse.3.gz OLD_FILES+=usr/share/perl/man/man3/base.3.gz OLD_FILES+=usr/share/perl/man/man3/blib.3.gz OLD_FILES+=usr/share/perl/man/man3/constant.3.gz OLD_FILES+=usr/share/perl/man/man3/diagnostics.3.gz OLD_FILES+=usr/share/perl/man/man3/fields.3.gz OLD_FILES+=usr/share/perl/man/man3/integer.3.gz OLD_FILES+=usr/share/perl/man/man3/less.3.gz OLD_FILES+=usr/share/perl/man/man3/lib.3.gz OLD_FILES+=usr/share/perl/man/man3/locale.3.gz OLD_FILES+=usr/share/perl/man/man3/ops.3.gz OLD_FILES+=usr/share/perl/man/man3/overload.3.gz OLD_FILES+=usr/share/perl/man/man3/sigtrap.3.gz OLD_FILES+=usr/share/perl/man/man3/strict.3.gz OLD_FILES+=usr/share/perl/man/man3/subs.3.gz OLD_FILES+=usr/share/perl/man/man3/vars.3.gz OLD_FILES+=usr/share/perl/man/man3/B::Stash.3.gz OLD_FILES+=usr/share/perl/man/man3/ByteLoader.3.gz OLD_FILES+=usr/share/perl/man/man3/CGI::Pretty.3.gz OLD_FILES+=usr/share/perl/man/man3/Carp::Heavy.3.gz OLD_FILES+=usr/share/perl/man/man3/DB.3.gz OLD_FILES+=usr/share/perl/man/man3/DProf::DProf.3.gz OLD_FILES+=usr/share/perl/man/man3/Exporter::Heavy.3.gz OLD_FILES+=usr/share/perl/man/man3/ExtUtils::MM_Cygwin.3.gz OLD_FILES+=usr/share/perl/man/man3/File::Glob.3.gz OLD_FILES+=usr/share/perl/man/man3/Glob::Glob.3.gz OLD_FILES+=usr/share/perl/man/man3/Hostname::Hostname.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Dir.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Poll.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Socket::INET.3.gz OLD_FILES+=usr/share/perl/man/man3/IO::Socket::UNIX.3.gz OLD_FILES+=usr/share/perl/man/man3/Peek::Peek.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Checker.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Find.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::InputObjects.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Man.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::ParseUtils.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Parser.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Plainer.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Select.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Text::Color.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Text::Termcap.3.gz OLD_FILES+=usr/share/perl/man/man3/Pod::Usage.3.gz OLD_FILES+=usr/share/perl/man/man3/Syslog::Syslog.3.gz OLD_FILES+=usr/share/perl/man/man3/Term::ANSIColor.3.gz OLD_FILES+=usr/share/perl/man/man3/XSLoader.3.gz OLD_FILES+=usr/share/perl/man/man3/attributes.3.gz OLD_FILES+=usr/share/perl/man/man3/bytes.3.gz OLD_FILES+=usr/share/perl/man/man3/charnames.3.gz OLD_FILES+=usr/share/perl/man/man3/filetest.3.gz OLD_FILES+=usr/share/perl/man/man3/open.3.gz OLD_FILES+=usr/share/perl/man/man3/utf8.3.gz OLD_FILES+=usr/share/perl/man/man3/warnings.3.gz OLD_FILES+=usr/share/perl/man/man3/warnings::register.3.gz OLD_FILES+=usr/share/perl/man/whatis OLD_FILES+=usr/share/man/man1/CA.pl.1.gz OLD_FILES+=usr/share/man/man1/asn1parse.1.gz OLD_FILES+=usr/share/man/man1/ca.1.gz OLD_FILES+=usr/share/man/man1/ciphers.1.gz OLD_FILES+=usr/share/man/man1/config.1.gz OLD_FILES+=usr/share/man/man1/crl.1.gz OLD_FILES+=usr/share/man/man1/crl2pkcs7.1.gz OLD_FILES+=usr/share/man/man1/dgst.1.gz OLD_FILES+=usr/share/man/man1/dhparam.1.gz OLD_FILES+=usr/share/man/man1/doscmd.1.gz OLD_FILES+=usr/share/man/man1/dsa.1.gz OLD_FILES+=usr/share/man/man1/dsaparam.1.gz OLD_FILES+=usr/share/man/man1/enc.1.gz OLD_FILES+=usr/share/man/man1/gendsa.1.gz OLD_FILES+=usr/share/man/man1/genrsa.1.gz OLD_FILES+=usr/share/man/man1/getNAME.1.gz OLD_FILES+=usr/share/man/man1/nseq.1.gz OLD_FILES+=usr/share/man/man1/ocsp.1.gz OLD_FILES+=usr/share/man/man1/openssl.1.gz OLD_FILES+=usr/share/man/man1/perl.1.gz OLD_FILES+=usr/share/man/man1/perl5004delta.1.gz OLD_FILES+=usr/share/man/man1/perlapio.1.gz OLD_FILES+=usr/share/man/man1/perlbook.1.gz OLD_FILES+=usr/share/man/man1/perlbot.1.gz OLD_FILES+=usr/share/man/man1/perlcall.1.gz OLD_FILES+=usr/share/man/man1/perldata.1.gz OLD_FILES+=usr/share/man/man1/perldebug.1.gz OLD_FILES+=usr/share/man/man1/perldelta.1.gz OLD_FILES+=usr/share/man/man1/perldiag.1.gz OLD_FILES+=usr/share/man/man1/perldsc.1.gz OLD_FILES+=usr/share/man/man1/perlembed.1.gz OLD_FILES+=usr/share/man/man1/perlfaq.1.gz OLD_FILES+=usr/share/man/man1/perlfaq1.1.gz OLD_FILES+=usr/share/man/man1/perlfaq2.1.gz OLD_FILES+=usr/share/man/man1/perlfaq3.1.gz OLD_FILES+=usr/share/man/man1/perlfaq4.1.gz OLD_FILES+=usr/share/man/man1/perlfaq5.1.gz OLD_FILES+=usr/share/man/man1/perlfaq6.1.gz OLD_FILES+=usr/share/man/man1/perlfaq7.1.gz OLD_FILES+=usr/share/man/man1/perlfaq8.1.gz OLD_FILES+=usr/share/man/man1/perlfaq9.1.gz OLD_FILES+=usr/share/man/man1/perlform.1.gz OLD_FILES+=usr/share/man/man1/perlfunc.1.gz OLD_FILES+=usr/share/man/man1/perlguts.1.gz OLD_FILES+=usr/share/man/man1/perlhist.1.gz OLD_FILES+=usr/share/man/man1/perlipc.1.gz OLD_FILES+=usr/share/man/man1/perllocale.1.gz OLD_FILES+=usr/share/man/man1/perllol.1.gz OLD_FILES+=usr/share/man/man1/perlmod.1.gz OLD_FILES+=usr/share/man/man1/perlmodinstall.1.gz OLD_FILES+=usr/share/man/man1/perlmodlib.1.gz OLD_FILES+=usr/share/man/man1/perlobj.1.gz OLD_FILES+=usr/share/man/man1/perlop.1.gz OLD_FILES+=usr/share/man/man1/perlopentut.1.gz OLD_FILES+=usr/share/man/man1/perlpod.1.gz OLD_FILES+=usr/share/man/man1/perlport.1.gz OLD_FILES+=usr/share/man/man1/perlre.1.gz OLD_FILES+=usr/share/man/man1/perlref.1.gz OLD_FILES+=usr/share/man/man1/perlreftut.1.gz OLD_FILES+=usr/share/man/man1/perlrun.1.gz OLD_FILES+=usr/share/man/man1/perlsec.1.gz OLD_FILES+=usr/share/man/man1/perlstyle.1.gz OLD_FILES+=usr/share/man/man1/perlsub.1.gz OLD_FILES+=usr/share/man/man1/perlsyn.1.gz OLD_FILES+=usr/share/man/man1/perlthrtut.1.gz OLD_FILES+=usr/share/man/man1/perltie.1.gz OLD_FILES+=usr/share/man/man1/perltoc.1.gz OLD_FILES+=usr/share/man/man1/perltoot.1.gz OLD_FILES+=usr/share/man/man1/perltrap.1.gz OLD_FILES+=usr/share/man/man1/perlvar.1.gz OLD_FILES+=usr/share/man/man1/perlxs.1.gz OLD_FILES+=usr/share/man/man1/perlxstut.1.gz OLD_FILES+=usr/share/man/man1/perlbug.1.gz OLD_FILES+=usr/share/man/man1/perlcc.1.gz OLD_FILES+=usr/share/man/man1/perldoc.1.gz OLD_FILES+=usr/share/man/man1/perl5005delta.1.gz OLD_FILES+=usr/share/man/man1/perlfork.1.gz OLD_FILES+=usr/share/man/man1/perlboot.1.gz OLD_FILES+=usr/share/man/man1/perltootc.1.gz OLD_FILES+=usr/share/man/man1/perldbmfilter.1.gz OLD_FILES+=usr/share/man/man1/perldebguts.1.gz OLD_FILES+=usr/share/man/man1/perlnumber.1.gz OLD_FILES+=usr/share/man/man1/perlcompile.1.gz OLD_FILES+=usr/share/man/man1/perltodo.1.gz OLD_FILES+=usr/share/man/man1/perlapi.1.gz OLD_FILES+=usr/share/man/man1/perlintern.1.gz OLD_FILES+=usr/share/man/man1/perlhack.1.gz OLD_FILES+=usr/share/man/man1/perlbc.1.gz OLD_FILES+=usr/share/man/man1/pkcs12.1.gz OLD_FILES+=usr/share/man/man1/pkcs7.1.gz OLD_FILES+=usr/share/man/man1/pkcs8.1.gz OLD_FILES+=usr/share/man/man1/rand.1.gz OLD_FILES+=usr/share/man/man1/req.1.gz OLD_FILES+=usr/share/man/man1/rsa.1.gz OLD_FILES+=usr/share/man/man1/rsautl.1.gz OLD_FILES+=usr/share/man/man1/s_client.1.gz OLD_FILES+=usr/share/man/man1/s_server.1.gz OLD_FILES+=usr/share/man/man1/sess_id.1.gz OLD_FILES+=usr/share/man/man1/smime.1.gz OLD_FILES+=usr/share/man/man1/speed.1.gz OLD_FILES+=usr/share/man/man1/spkac.1.gz OLD_FILES+=usr/share/man/man1/verify.1.gz OLD_FILES+=usr/share/man/man1/version.1.gz OLD_FILES+=usr/share/man/man1/x509.1.gz OLD_FILES+=usr/share/man/man3/SSL_COMP_add_compression_method.3.gz OLD_FILES+=usr/share/man/man3/SSL_CTX_get_ex_new_index.3.gz OLD_FILES+=usr/share/man/man3/archive_entry_dup.3.gz OLD_FILES+=usr/share/man/man3/archive_entry_hardlink_w.3.gz OLD_FILES+=usr/share/man/man3/archive_entry_set_tartype.3.gz OLD_FILES+=usr/share/man/man3/archive_entry_symlink_w.3.gz OLD_FILES+=usr/share/man/man3/archive_entry_tartype.3.gz OLD_FILES+=usr/share/man/man3/archive_read_data_into_file.3.gz OLD_FILES+=usr/share/man/man3/archive_read_open_tar.3.gz OLD_FILES+=usr/share/man/man3/archive_read_support_format_gnutar.3.gz OLD_FILES+=usr/share/man/man3/cipher.3.gz OLD_FILES+=usr/share/man/man3/des_cipher.3.gz OLD_FILES+=usr/share/man/man3/des_setkey.3.gz OLD_FILES+=usr/share/man/man3/encrypt.3.gz OLD_FILES+=usr/share/man/man3/endvfsent.3.gz OLD_FILES+=usr/share/man/man3/getvfsbytype.3.gz OLD_FILES+=usr/share/man/man3/getvfsent.3.gz OLD_FILES+=usr/share/man/man3/isnanf.3.gz OLD_FILES+=usr/share/man/man3/libautofs.3.gz OLD_FILES+=usr/share/man/man3/pthread_attr_setsstack.3.gz OLD_FILES+=usr/share/man/man3/pthread_getcancelstate.3.gz OLD_FILES+=usr/share/man/man3/pthread_mutexattr_getpshared.3.gz OLD_FILES+=usr/share/man/man3/pthread_mutexattr_setpshared.3.gz OLD_FILES+=usr/share/man/man3/set_assertion_failure_callback.3.gz OLD_FILES+=usr/share/man/man3/setkey.3.gz OLD_FILES+=usr/share/man/man3/setvfsent.3.gz OLD_FILES+=usr/share/man/man3/ssl.3.gz OLD_FILES+=usr/share/man/man3/vfsisloadable.3.gz OLD_FILES+=usr/share/man/man3/vfsload.3.gz OLD_FILES+=usr/share/man/man4/als4000.4.gz OLD_FILES+=usr/share/man/man4/csa.4.gz OLD_FILES+=usr/share/man/man4/emu10k1.4.gz OLD_FILES+=usr/share/man/man4/euc.4.gz OLD_FILES+=usr/share/man/man4/gusc.4.gz OLD_FILES+=usr/share/man/man4/if_fwp.4.gz OLD_FILES+=usr/share/man/man4/lomac.4.gz OLD_FILES+=usr/share/man/man4/maestro3.4.gz OLD_FILES+=usr/share/man/man4/raid.4.gz OLD_FILES+=usr/share/man/man4/sbc.4.gz OLD_FILES+=usr/share/man/man4/sd.4.gz OLD_FILES+=usr/share/man/man4/snc.4.gz OLD_FILES+=usr/share/man/man4/st.4.gz OLD_FILES+=usr/share/man/man4/uaudio.4.gz OLD_FILES+=usr/share/man/man4/utf2.4.gz OLD_FILES+=usr/share/man/man4/vinumdebug.4.gz OLD_FILES+=usr/share/man/man5/disklabel.5.gz OLD_FILES+=usr/share/man/man5/dm.conf.5.gz OLD_FILES+=usr/share/man/man5/ranlib.5.gz OLD_FILES+=usr/share/man/man5/utf2.5.gz OLD_FILES+=usr/share/man/man7/groff_mwww.7.gz OLD_FILES+=usr/share/man/man7/mmroff.7.gz OLD_FILES+=usr/share/man/man7/mwww.7.gz OLD_FILES+=usr/share/man/man7/style.perl.7.gz OLD_FILES+=usr/share/man/man8/apm.8.gz OLD_FILES+=usr/share/man/man8/apmconf.8.gz OLD_FILES+=usr/share/man/man8/apmd.8.gz OLD_FILES+=usr/share/man/man8/dm.8.gz OLD_FILES+=usr/share/man/man8/pam_ftp.8.gz OLD_FILES+=usr/share/man/man8/pam_wheel.8.gz OLD_FILES+=usr/share/man/man8/sconfig.8.gz OLD_FILES+=usr/share/man/man8/ssl.8.gz OLD_FILES+=usr/share/man/man8/wlconfig.8.gz OLD_FILES+=usr/share/man/man9/CURSIG.9.gz OLD_FILES+=usr/share/man/man9/VFS_INIT.9.gz OLD_FILES+=usr/share/man/man9/at_exit.9.gz OLD_FILES+=usr/share/man/man9/at_fork.9.gz OLD_FILES+=usr/share/man/man9/cdevsw_add.9.gz OLD_FILES+=usr/share/man/man9/cdevsw_remove.9.gz OLD_FILES+=usr/share/man/man9/cv_waitq_empty.9.gz OLD_FILES+=usr/share/man/man9/cv_waitq_remove.9.gz OLD_FILES+=usr/share/man/man9/endtsleep.9.gz OLD_FILES+=usr/share/man/man9/jumbo.9.gz OLD_FILES+=usr/share/man/man9/jumbo_freem.9.gz OLD_FILES+=usr/share/man/man9/jumbo_pg_alloc.9.gz OLD_FILES+=usr/share/man/man9/jumbo_pg_free.9.gz OLD_FILES+=usr/share/man/man9/jumbo_pg_steal.9.gz OLD_FILES+=usr/share/man/man9/jumbo_phys_to_kva.9.gz OLD_FILES+=usr/share/man/man9/jumbo_vm_init.9.gz OLD_FILES+=usr/share/man/man9/mac_biba.9.gz OLD_FILES+=usr/share/man/man9/mac_bsdextended.9.gz OLD_FILES+=usr/share/man/man9/mono_time.9.gz OLD_FILES+=usr/share/man/man9/p1003_1b.9.gz OLD_FILES+=usr/share/man/man9/pmap_prefault.9.gz OLD_FILES+=usr/share/man/man9/posix4.9.gz OLD_FILES+=usr/share/man/man9/resource_query_name.9.gz OLD_FILES+=usr/share/man/man9/resource_query_string.9.gz OLD_FILES+=usr/share/man/man9/resource_query_unit.9.gz OLD_FILES+=usr/share/man/man9/rm_at_exit.9.gz OLD_FILES+=usr/share/man/man9/rm_at_fork.9.gz OLD_FILES+=usr/share/man/man9/runtime.9.gz OLD_FILES+=usr/share/man/man9/sleepinit.9.gz OLD_FILES+=usr/share/man/man9/unsleep.9.gz OLD_FILES+=usr/share/man/ja/man1/perl.1.gz OLD_FILES+=usr/share/games/atc/Game_List OLD_FILES+=usr/share/games/atc/Killer OLD_FILES+=usr/share/games/atc/crossover OLD_FILES+=usr/share/games/atc/default OLD_FILES+=usr/share/games/atc/easy OLD_FILES+=usr/share/games/atc/game_2 OLD_FILES+=usr/share/games/larn/larnmaze OLD_FILES+=usr/share/games/larn/larnopts OLD_FILES+=usr/share/games/larn/larn.help OLD_FILES+=usr/share/games/quiz.db/africa OLD_FILES+=usr/share/games/quiz.db/america OLD_FILES+=usr/share/games/quiz.db/areas OLD_FILES+=usr/share/games/quiz.db/arith OLD_FILES+=usr/share/games/quiz.db/asia OLD_FILES+=usr/share/games/quiz.db/babies OLD_FILES+=usr/share/games/quiz.db/bard OLD_FILES+=usr/share/games/quiz.db/chinese OLD_FILES+=usr/share/games/quiz.db/collectives OLD_FILES+=usr/share/games/quiz.db/ed OLD_FILES+=usr/share/games/quiz.db/elements OLD_FILES+=usr/share/games/quiz.db/europe OLD_FILES+=usr/share/games/quiz.db/flowers OLD_FILES+=usr/share/games/quiz.db/greek OLD_FILES+=usr/share/games/quiz.db/inca OLD_FILES+=usr/share/games/quiz.db/index OLD_FILES+=usr/share/games/quiz.db/latin OLD_FILES+=usr/share/games/quiz.db/locomotive OLD_FILES+=usr/share/games/quiz.db/midearth OLD_FILES+=usr/share/games/quiz.db/morse OLD_FILES+=usr/share/games/quiz.db/murders OLD_FILES+=usr/share/games/quiz.db/poetry OLD_FILES+=usr/share/games/quiz.db/posneg OLD_FILES+=usr/share/games/quiz.db/pres OLD_FILES+=usr/share/games/quiz.db/province OLD_FILES+=usr/share/games/quiz.db/seq-easy OLD_FILES+=usr/share/games/quiz.db/seq-hard OLD_FILES+=usr/share/games/quiz.db/sexes OLD_FILES+=usr/share/games/quiz.db/sov OLD_FILES+=usr/share/games/quiz.db/spell OLD_FILES+=usr/share/games/quiz.db/state OLD_FILES+=usr/share/games/quiz.db/trek OLD_FILES+=usr/share/games/quiz.db/ucc OLD_FILES+=usr/share/games/cribbage.instr OLD_FILES+=usr/share/games/fish.instr OLD_FILES+=usr/share/games/wump.info OLD_FILES+=usr/games/hide/adventure OLD_FILES+=usr/games/hide/arithmetic OLD_FILES+=usr/games/hide/atc OLD_FILES+=usr/games/hide/backgammon OLD_FILES+=usr/games/hide/teachgammon OLD_FILES+=usr/games/hide/battlestar OLD_FILES+=usr/games/hide/bs OLD_FILES+=usr/games/hide/canfield OLD_FILES+=usr/games/hide/cribbage OLD_FILES+=usr/games/hide/fish OLD_FILES+=usr/games/hide/hack OLD_FILES+=usr/games/hide/hangman OLD_FILES+=usr/games/hide/larn OLD_FILES+=usr/games/hide/mille OLD_FILES+=usr/games/hide/phantasia OLD_FILES+=usr/games/hide/quiz OLD_FILES+=usr/games/hide/robots OLD_FILES+=usr/games/hide/rogue OLD_FILES+=usr/games/hide/sail OLD_FILES+=usr/games/hide/snake OLD_FILES+=usr/games/hide/trek OLD_FILES+=usr/games/hide/worm OLD_FILES+=usr/games/hide/wump OLD_FILES+=usr/games/adventure OLD_FILES+=usr/games/arithmetic OLD_FILES+=usr/games/atc OLD_FILES+=usr/games/backgammon OLD_FILES+=usr/games/teachgammon OLD_FILES+=usr/games/battlestar OLD_FILES+=usr/games/bs OLD_FILES+=usr/games/canfield OLD_FILES+=usr/games/cfscores OLD_FILES+=usr/games/cribbage OLD_FILES+=usr/games/dm OLD_FILES+=usr/games/fish OLD_FILES+=usr/games/hack OLD_FILES+=usr/games/hangman OLD_FILES+=usr/games/larn OLD_FILES+=usr/games/mille OLD_FILES+=usr/games/phantasia OLD_FILES+=usr/games/piano OLD_FILES+=usr/games/pig OLD_FILES+=usr/games/quiz OLD_FILES+=usr/games/rain OLD_FILES+=usr/games/robots OLD_FILES+=usr/games/rogue OLD_FILES+=usr/games/sail OLD_FILES+=usr/games/snake OLD_FILES+=usr/games/snscore OLD_FILES+=usr/games/trek OLD_FILES+=usr/games/wargames OLD_FILES+=usr/games/worm OLD_FILES+=usr/games/worms OLD_FILES+=usr/games/wump OLD_FILES+=sbin/mount_reiserfs OLD_FILES+=usr/include/c++/3.4/ext/demangle.h OLD_FILES+=usr/include/cam/cam_extend.h OLD_FILES+=usr/include/dev/wi/wi_hostap.h OLD_FILES+=usr/include/disktab.h OLD_FILES+=usr/include/g++/FlexLexer.h OLD_FILES+=usr/include/g++/PlotFile.h OLD_FILES+=usr/include/g++/SFile.h OLD_FILES+=usr/include/g++/_G_config.h OLD_FILES+=usr/include/g++/algo.h OLD_FILES+=usr/include/g++/algobase.h OLD_FILES+=usr/include/g++/algorithm OLD_FILES+=usr/include/g++/alloc.h OLD_FILES+=usr/include/g++/bitset OLD_FILES+=usr/include/g++/builtinbuf.h OLD_FILES+=usr/include/g++/bvector.h OLD_FILES+=usr/include/g++/cassert OLD_FILES+=usr/include/g++/cctype OLD_FILES+=usr/include/g++/cerrno OLD_FILES+=usr/include/g++/cfloat OLD_FILES+=usr/include/g++/ciso646 OLD_FILES+=usr/include/g++/climits OLD_FILES+=usr/include/g++/clocale OLD_FILES+=usr/include/g++/cmath OLD_FILES+=usr/include/g++/complex OLD_FILES+=usr/include/g++/complex.h OLD_FILES+=usr/include/g++/csetjmp OLD_FILES+=usr/include/g++/csignal OLD_FILES+=usr/include/g++/cstdarg OLD_FILES+=usr/include/g++/cstddef OLD_FILES+=usr/include/g++/cstdio OLD_FILES+=usr/include/g++/cstdlib OLD_FILES+=usr/include/g++/cstring OLD_FILES+=usr/include/g++/ctime OLD_FILES+=usr/include/g++/cwchar OLD_FILES+=usr/include/g++/cwctype OLD_FILES+=usr/include/g++/defalloc.h OLD_FILES+=usr/include/g++/deque OLD_FILES+=usr/include/g++/deque.h OLD_FILES+=usr/include/g++/editbuf.h OLD_FILES+=usr/include/g++/exception OLD_FILES+=usr/include/g++/floatio.h OLD_FILES+=usr/include/g++/fstream OLD_FILES+=usr/include/g++/fstream.h OLD_FILES+=usr/include/g++/function.h OLD_FILES+=usr/include/g++/functional OLD_FILES+=usr/include/g++/hash_map OLD_FILES+=usr/include/g++/hash_map.h OLD_FILES+=usr/include/g++/hash_set OLD_FILES+=usr/include/g++/hash_set.h OLD_FILES+=usr/include/g++/hashtable.h OLD_FILES+=usr/include/g++/heap.h OLD_FILES+=usr/include/g++/indstream.h OLD_FILES+=usr/include/g++/iolibio.h OLD_FILES+=usr/include/g++/iomanip OLD_FILES+=usr/include/g++/iomanip.h OLD_FILES+=usr/include/g++/iosfwd OLD_FILES+=usr/include/g++/iostdio.h OLD_FILES+=usr/include/g++/iostream OLD_FILES+=usr/include/g++/iostream.h OLD_FILES+=usr/include/g++/iostreamP.h OLD_FILES+=usr/include/g++/istream.h OLD_FILES+=usr/include/g++/iterator OLD_FILES+=usr/include/g++/iterator.h OLD_FILES+=usr/include/g++/libio.h OLD_FILES+=usr/include/g++/libioP.h OLD_FILES+=usr/include/g++/list OLD_FILES+=usr/include/g++/list.h OLD_FILES+=usr/include/g++/map OLD_FILES+=usr/include/g++/map.h OLD_FILES+=usr/include/g++/memory OLD_FILES+=usr/include/g++/multimap.h OLD_FILES+=usr/include/g++/multiset.h OLD_FILES+=usr/include/g++/new OLD_FILES+=usr/include/g++/new.h OLD_FILES+=usr/include/g++/numeric OLD_FILES+=usr/include/g++/ostream.h OLD_FILES+=usr/include/g++/pair.h OLD_FILES+=usr/include/g++/parsestream.h OLD_FILES+=usr/include/g++/pfstream.h OLD_FILES+=usr/include/g++/procbuf.h OLD_FILES+=usr/include/g++/pthread_alloc OLD_FILES+=usr/include/g++/pthread_alloc.h OLD_FILES+=usr/include/g++/queue OLD_FILES+=usr/include/g++/rope OLD_FILES+=usr/include/g++/rope.h OLD_FILES+=usr/include/g++/ropeimpl.h OLD_FILES+=usr/include/g++/set OLD_FILES+=usr/include/g++/set.h OLD_FILES+=usr/include/g++/slist OLD_FILES+=usr/include/g++/slist.h OLD_FILES+=usr/include/g++/sstream OLD_FILES+=usr/include/g++/stack OLD_FILES+=usr/include/g++/stack.h OLD_FILES+=usr/include/g++/std/bastring.cc OLD_FILES+=usr/include/g++/std/bastring.h OLD_FILES+=usr/include/g++/std/complext.cc OLD_FILES+=usr/include/g++/std/complext.h OLD_FILES+=usr/include/g++/std/dcomplex.h OLD_FILES+=usr/include/g++/std/fcomplex.h OLD_FILES+=usr/include/g++/std/gslice.h OLD_FILES+=usr/include/g++/std/gslice_array.h OLD_FILES+=usr/include/g++/std/indirect_array.h OLD_FILES+=usr/include/g++/std/ldcomplex.h OLD_FILES+=usr/include/g++/std/mask_array.h OLD_FILES+=usr/include/g++/std/slice.h OLD_FILES+=usr/include/g++/std/slice_array.h OLD_FILES+=usr/include/g++/std/std_valarray.h OLD_FILES+=usr/include/g++/std/straits.h OLD_FILES+=usr/include/g++/std/valarray_array.h OLD_FILES+=usr/include/g++/std/valarray_array.tcc OLD_FILES+=usr/include/g++/std/valarray_meta.h OLD_FILES+=usr/include/g++/stdexcept OLD_FILES+=usr/include/g++/stdiostream.h OLD_FILES+=usr/include/g++/stl.h OLD_FILES+=usr/include/g++/stl_algo.h OLD_FILES+=usr/include/g++/stl_algobase.h OLD_FILES+=usr/include/g++/stl_alloc.h OLD_FILES+=usr/include/g++/stl_bvector.h OLD_FILES+=usr/include/g++/stl_config.h OLD_FILES+=usr/include/g++/stl_construct.h OLD_FILES+=usr/include/g++/stl_deque.h OLD_FILES+=usr/include/g++/stl_function.h OLD_FILES+=usr/include/g++/stl_hash_fun.h OLD_FILES+=usr/include/g++/stl_hash_map.h OLD_FILES+=usr/include/g++/stl_hash_set.h OLD_FILES+=usr/include/g++/stl_hashtable.h OLD_FILES+=usr/include/g++/stl_heap.h OLD_FILES+=usr/include/g++/stl_iterator.h OLD_FILES+=usr/include/g++/stl_list.h OLD_FILES+=usr/include/g++/stl_map.h OLD_FILES+=usr/include/g++/stl_multimap.h OLD_FILES+=usr/include/g++/stl_multiset.h OLD_FILES+=usr/include/g++/stl_numeric.h OLD_FILES+=usr/include/g++/stl_pair.h OLD_FILES+=usr/include/g++/stl_queue.h OLD_FILES+=usr/include/g++/stl_raw_storage_iter.h OLD_FILES+=usr/include/g++/stl_relops.h OLD_FILES+=usr/include/g++/stl_rope.h OLD_FILES+=usr/include/g++/stl_set.h OLD_FILES+=usr/include/g++/stl_slist.h OLD_FILES+=usr/include/g++/stl_stack.h OLD_FILES+=usr/include/g++/stl_tempbuf.h OLD_FILES+=usr/include/g++/stl_tree.h OLD_FILES+=usr/include/g++/stl_uninitialized.h OLD_FILES+=usr/include/g++/stl_vector.h OLD_FILES+=usr/include/g++/stream.h OLD_FILES+=usr/include/g++/streambuf.h OLD_FILES+=usr/include/g++/strfile.h OLD_FILES+=usr/include/g++/string OLD_FILES+=usr/include/g++/strstream OLD_FILES+=usr/include/g++/strstream.h OLD_FILES+=usr/include/g++/tempbuf.h OLD_FILES+=usr/include/g++/tree.h OLD_FILES+=usr/include/g++/type_traits.h OLD_FILES+=usr/include/g++/typeinfo OLD_FILES+=usr/include/g++/utility OLD_FILES+=usr/include/g++/valarray OLD_FILES+=usr/include/g++/vector OLD_FILES+=usr/include/g++/vector.h OLD_FILES+=usr/include/gmp.h OLD_FILES+=usr/include/isc/assertions.h OLD_FILES+=usr/include/isc/ctl.h OLD_FILES+=usr/include/isc/dst.h OLD_FILES+=usr/include/isc/eventlib.h OLD_FILES+=usr/include/isc/heap.h OLD_FILES+=usr/include/isc/irpmarshall.h OLD_FILES+=usr/include/isc/list.h OLD_FILES+=usr/include/isc/logging.h OLD_FILES+=usr/include/isc/memcluster.h OLD_FILES+=usr/include/isc/misc.h OLD_FILES+=usr/include/isc/tree.h OLD_FILES+=usr/include/machine/ansi.h OLD_FILES+=usr/include/machine/apic.h OLD_FILES+=usr/include/machine/asc_ioctl.h OLD_FILES+=usr/include/machine/asnames.h OLD_FILES+=usr/include/machine/bus_at386.h OLD_FILES+=usr/include/machine/bus_memio.h OLD_FILES+=usr/include/machine/bus_pc98.h OLD_FILES+=usr/include/machine/bus_pio.h OLD_FILES+=usr/include/machine/cdk.h OLD_FILES+=usr/include/machine/comstats.h OLD_FILES+=usr/include/machine/console.h OLD_FILES+=usr/include/machine/critical.h OLD_FILES+=usr/include/machine/cronyx.h OLD_FILES+=usr/include/machine/dvcfg.h OLD_FILES+=usr/include/machine/globaldata.h OLD_FILES+=usr/include/machine/globals.h OLD_FILES+=usr/include/machine/gsc.h OLD_FILES+=usr/include/machine/i4b_isppp.h OLD_FILES+=usr/include/machine/if_wavelan_ieee.h OLD_FILES+=usr/include/machine/iic.h OLD_FILES+=usr/include/machine/ioctl_ctx.h OLD_FILES+=usr/include/machine/ioctl_fd.h OLD_FILES+=usr/include/machine/ipl.h OLD_FILES+=usr/include/machine/lock.h OLD_FILES+=usr/include/machine/mouse.h OLD_FILES+=usr/include/machine/mpapic.h OLD_FILES+=usr/include/machine/mtpr.h OLD_FILES+=usr/include/machine/pc/msdos.h OLD_FILES+=usr/include/machine/physio_proc.h OLD_FILES+=usr/include/machine/smb.h OLD_FILES+=usr/include/machine/spigot.h OLD_FILES+=usr/include/machine/types.h OLD_FILES+=usr/include/machine/uc_device.h OLD_FILES+=usr/include/machine/ultrasound.h OLD_FILES+=usr/include/machine/wtio.h OLD_FILES+=usr/include/msdosfs/bootsect.h OLD_FILES+=usr/include/msdosfs/bpb.h OLD_FILES+=usr/include/msdosfs/denode.h OLD_FILES+=usr/include/msdosfs/direntry.h OLD_FILES+=usr/include/msdosfs/fat.h OLD_FILES+=usr/include/msdosfs/msdosfsmount.h OLD_FILES+=usr/include/net/hostcache.h OLD_FILES+=usr/include/net/if_faith.h OLD_FILES+=usr/include/net/if_ieee80211.h OLD_FILES+=usr/include/net/if_tunvar.h OLD_FILES+=usr/include/net/intrq.h OLD_FILES+=usr/include/netatm/kern_include.h OLD_FILES+=usr/include/netinet/if_fddi.h OLD_FILES+=usr/include/netinet/in_hostcache.h OLD_FILES+=usr/include/netinet/ip_flow.h OLD_FILES+=usr/include/netinet/ip_fw2.h OLD_FILES+=usr/include/netinet6/in6_prefix.h OLD_FILES+=usr/include/netns/idp.h OLD_FILES+=usr/include/netns/idp_var.h OLD_FILES+=usr/include/netns/ns.h OLD_FILES+=usr/include/netns/ns_error.h OLD_FILES+=usr/include/netns/ns_if.h OLD_FILES+=usr/include/netns/ns_pcb.h OLD_FILES+=usr/include/netns/sp.h OLD_FILES+=usr/include/netns/spidp.h OLD_FILES+=usr/include/netns/spp_debug.h OLD_FILES+=usr/include/netns/spp_timer.h OLD_FILES+=usr/include/netns/spp_var.h OLD_FILES+=usr/include/nfs/krpc.h OLD_FILES+=usr/include/nfs/nfs.h OLD_FILES+=usr/include/nfs/nfsdiskless.h OLD_FILES+=usr/include/nfs/nfsm_subs.h OLD_FILES+=usr/include/nfs/nfsmount.h OLD_FILES+=usr/include/nfs/nfsnode.h OLD_FILES+=usr/include/nfs/nfsrtt.h OLD_FILES+=usr/include/nfs/nfsrvcache.h OLD_FILES+=usr/include/nfs/nfsv2.h OLD_FILES+=usr/include/nfs/nqnfs.h OLD_FILES+=usr/include/ntfs/ntfs.h OLD_FILES+=usr/include/ntfs/ntfs_compr.h OLD_FILES+=usr/include/ntfs/ntfs_ihash.h OLD_FILES+=usr/include/ntfs/ntfs_inode.h OLD_FILES+=usr/include/ntfs/ntfs_subr.h OLD_FILES+=usr/include/ntfs/ntfs_vfsops.h OLD_FILES+=usr/include/ntfs/ntfsmount.h OLD_FILES+=usr/include/nwfs/nwfs.h OLD_FILES+=usr/include/nwfs/nwfs_mount.h OLD_FILES+=usr/include/nwfs/nwfs_node.h OLD_FILES+=usr/include/nwfs/nwfs_subr.h OLD_FILES+=usr/include/posix4/_semaphore.h OLD_FILES+=usr/include/posix4/aio.h OLD_FILES+=usr/include/posix4/ksem.h OLD_FILES+=usr/include/posix4/mqueue.h OLD_FILES+=usr/include/posix4/posix4.h OLD_FILES+=usr/include/posix4/sched.h OLD_FILES+=usr/include/posix4/semaphore.h OLD_DIRS+=usr/include/posix4 OLD_FILES+=usr/include/rune.h OLD_FILES+=usr/include/security/_pam_compat.h OLD_FILES+=usr/include/security/_pam_macros.h OLD_FILES+=usr/include/security/_pam_types.h OLD_FILES+=usr/include/security/pam_malloc.h OLD_FILES+=usr/include/security/pam_misc.h OLD_FILES+=usr/include/skey.h OLD_FILES+=usr/include/strhash.h OLD_FILES+=usr/include/struct.h OLD_FILES+=usr/include/sys/_label.h OLD_FILES+=usr/include/sys/_posix.h OLD_FILES+=usr/include/sys/bus_private.h OLD_FILES+=usr/include/sys/ccdvar.h OLD_FILES+=usr/include/sys/diskslice.h OLD_FILES+=usr/include/sys/dmap.h OLD_FILES+=usr/include/sys/inttypes.h OLD_FILES+=usr/include/sys/jumbo.h OLD_FILES+=usr/include/sys/mac_policy.h OLD_FILES+=usr/include/sys/pbioio.h OLD_FILES+=usr/include/sys/syscall-hide.h OLD_FILES+=usr/include/sys/tprintf.h OLD_FILES+=usr/include/sys/vnioctl.h OLD_FILES+=usr/include/sys/wormio.h OLD_FILES+=usr/include/telnet.h OLD_FILES+=usr/include/ufs/mfs/mfs_extern.h OLD_FILES+=usr/include/ufs/mfs/mfsnode.h OLD_FILES+=usr/include/values.h OLD_FILES+=usr/include/vm/vm_zone.h OLD_FILES+=usr/share/examples/etc/usbd.conf OLD_FILES+=usr/share/examples/meteor/README OLD_FILES+=usr/share/examples/meteor/rgb16.c OLD_FILES+=usr/share/examples/meteor/rgb24.c OLD_FILES+=usr/share/examples/meteor/test-n.c OLD_FILES+=usr/share/examples/meteor/yuvpk.c OLD_FILES+=usr/share/examples/meteor/yuvpl.c OLD_FILES+=usr/share/examples/worm/README OLD_FILES+=usr/share/examples/worm/makecdfs.sh OLD_FILES+=usr/share/groff_font/devlj4/Makefile OLD_FILES+=usr/share/groff_font/devlj4/text.map OLD_FILES+=usr/share/groff_font/devlj4/special.map OLD_FILES+=usr/share/misc/nslookup.help OLD_FILES+=usr/share/sendmail/cf/feature/nodns.m4 OLD_FILES+=usr/share/syscons/keymaps/lat-amer.kbd OLD_FILES+=usr/share/vi/catalog/ru_SU.KOI8-R OLD_FILES+=usr/share/zoneinfo/Africa/Timbuktu OLD_FILES+=usr/share/zoneinfo/Africa/Asmera OLD_FILES+=usr/share/zoneinfo/America/Buenos_Aires OLD_FILES+=usr/share/zoneinfo/America/Cordoba OLD_FILES+=usr/share/zoneinfo/America/Jujuy OLD_FILES+=usr/share/zoneinfo/America/Catamarca OLD_FILES+=usr/share/zoneinfo/America/Mendoza OLD_FILES+=usr/share/zoneinfo/America/Indianapolis OLD_FILES+=usr/share/zoneinfo/America/Louisville OLD_FILES+=usr/share/zoneinfo/America/Argentina/ComodRivadavia OLD_FILES+=usr/share/zoneinfo/Atlantic/Faeroe OLD_FILES+=usr/share/zoneinfo/Europe/Belfast OLD_FILES+=usr/share/zoneinfo/Pacific/Yap OLD_FILES+=usr/share/zoneinfo/SystemV/YST9 OLD_FILES+=usr/share/zoneinfo/SystemV/PST8 OLD_FILES+=usr/share/zoneinfo/SystemV/EST5EDT OLD_FILES+=usr/share/zoneinfo/SystemV/CST6CDT OLD_FILES+=usr/share/zoneinfo/SystemV/MST7MDT OLD_FILES+=usr/share/zoneinfo/SystemV/PST8PDT OLD_FILES+=usr/share/zoneinfo/SystemV/YST9YDT OLD_FILES+=usr/share/zoneinfo/SystemV/HST10 OLD_FILES+=usr/share/zoneinfo/SystemV/MST7 OLD_FILES+=usr/share/zoneinfo/SystemV/EST5 OLD_FILES+=usr/share/zoneinfo/SystemV/AST4ADT OLD_FILES+=usr/share/zoneinfo/SystemV/CST6 OLD_FILES+=usr/share/zoneinfo/SystemV/AST4 OLD_FILES+=usr/share/doc/ntp/accopt.htm OLD_FILES+=usr/share/doc/ntp/assoc.htm OLD_FILES+=usr/share/doc/ntp/audio.htm OLD_FILES+=usr/share/doc/ntp/authopt.htm OLD_FILES+=usr/share/doc/ntp/biblio.htm OLD_FILES+=usr/share/doc/ntp/build.htm OLD_FILES+=usr/share/doc/ntp/clockopt.htm OLD_FILES+=usr/share/doc/ntp/config.htm OLD_FILES+=usr/share/doc/ntp/confopt.htm OLD_FILES+=usr/share/doc/ntp/copyright.htm OLD_FILES+=usr/share/doc/ntp/debug.htm OLD_FILES+=usr/share/doc/ntp/driver1.htm OLD_FILES+=usr/share/doc/ntp/driver10.htm OLD_FILES+=usr/share/doc/ntp/driver11.htm OLD_FILES+=usr/share/doc/ntp/driver12.htm OLD_FILES+=usr/share/doc/ntp/driver16.htm OLD_FILES+=usr/share/doc/ntp/driver18.htm OLD_FILES+=usr/share/doc/ntp/driver19.htm OLD_FILES+=usr/share/doc/ntp/driver2.htm OLD_FILES+=usr/share/doc/ntp/driver20.htm OLD_FILES+=usr/share/doc/ntp/driver22.htm OLD_FILES+=usr/share/doc/ntp/driver23.htm OLD_FILES+=usr/share/doc/ntp/driver24.htm OLD_FILES+=usr/share/doc/ntp/driver26.htm OLD_FILES+=usr/share/doc/ntp/driver27.htm OLD_FILES+=usr/share/doc/ntp/driver28.htm OLD_FILES+=usr/share/doc/ntp/driver29.htm OLD_FILES+=usr/share/doc/ntp/driver3.htm OLD_FILES+=usr/share/doc/ntp/driver30.htm OLD_FILES+=usr/share/doc/ntp/driver32.htm OLD_FILES+=usr/share/doc/ntp/driver33.htm OLD_FILES+=usr/share/doc/ntp/driver34.htm OLD_FILES+=usr/share/doc/ntp/driver35.htm OLD_FILES+=usr/share/doc/ntp/driver36.htm OLD_FILES+=usr/share/doc/ntp/driver37.htm OLD_FILES+=usr/share/doc/ntp/driver4.htm OLD_FILES+=usr/share/doc/ntp/driver5.htm OLD_FILES+=usr/share/doc/ntp/driver6.htm OLD_FILES+=usr/share/doc/ntp/driver7.htm OLD_FILES+=usr/share/doc/ntp/driver8.htm OLD_FILES+=usr/share/doc/ntp/driver9.htm OLD_FILES+=usr/share/doc/ntp/exec.htm OLD_FILES+=usr/share/doc/ntp/extern.htm OLD_FILES+=usr/share/doc/ntp/gadget.htm OLD_FILES+=usr/share/doc/ntp/hints.htm OLD_FILES+=usr/share/doc/ntp/howto.htm OLD_FILES+=usr/share/doc/ntp/htmlprimer.htm OLD_FILES+=usr/share/doc/ntp/index.htm OLD_FILES+=usr/share/doc/ntp/kern.htm OLD_FILES+=usr/share/doc/ntp/kernpps.htm OLD_FILES+=usr/share/doc/ntp/ldisc.htm OLD_FILES+=usr/share/doc/ntp/measure.htm OLD_FILES+=usr/share/doc/ntp/miscopt.htm OLD_FILES+=usr/share/doc/ntp/monopt.htm OLD_FILES+=usr/share/doc/ntp/mx4200data.htm OLD_FILES+=usr/share/doc/ntp/notes.htm OLD_FILES+=usr/share/doc/ntp/ntpd.htm OLD_FILES+=usr/share/doc/ntp/ntpdate.htm OLD_FILES+=usr/share/doc/ntp/ntpdc.htm OLD_FILES+=usr/share/doc/ntp/ntpq.htm OLD_FILES+=usr/share/doc/ntp/ntptime.htm OLD_FILES+=usr/share/doc/ntp/ntptrace.htm OLD_FILES+=usr/share/doc/ntp/parsedata.htm OLD_FILES+=usr/share/doc/ntp/parsenew.htm OLD_FILES+=usr/share/doc/ntp/patches.htm OLD_FILES+=usr/share/doc/ntp/porting.htm OLD_FILES+=usr/share/doc/ntp/pps.htm OLD_FILES+=usr/share/doc/ntp/prefer.htm OLD_FILES+=usr/share/doc/ntp/qth.htm OLD_FILES+=usr/share/doc/ntp/quick.htm OLD_FILES+=usr/share/doc/ntp/rdebug.htm OLD_FILES+=usr/share/doc/ntp/refclock.htm OLD_FILES+=usr/share/doc/ntp/release.htm OLD_FILES+=usr/share/doc/ntp/tickadj.htm OLD_FILES+=usr/share/doc/papers/nqnfs.ascii.gz OLD_FILES+=usr/share/doc/papers/px.ascii.gz OLD_FILES+=usr/share/man/man3/mbrune.3.gz OLD_FILES+=usr/share/man/man3/rune.3.gz OLD_FILES+=usr/share/man/man3/mac_is_present_np.3.gz OLD_FILES+=usr/share/man/man3/fpsetsticky.3.gz OLD_FILES+=usr/share/man/man3/gss_krb5_copy_ccache.3.gz OLD_FILES+=usr/share/man/man3/gss_krb5_compat_des3_mic.3.gz OLD_FILES+=usr/share/man/man3/exp10f.3.gz OLD_FILES+=usr/share/man/man3/exp10.3.gz OLD_FILES+=usr/share/man/man3/mbrrune.3.gz OLD_FILES+=usr/share/man/man3/mbmb.3.gz OLD_FILES+=usr/share/man/man3/sputrune.3.gz OLD_FILES+=usr/share/man/man3/sgetrune.3.gz OLD_FILES+=usr/share/man/man3/setrunelocale.3.gz OLD_FILES+=usr/share/man/man3/setinvalidrune.3.gz OLD_FILES+=usr/share/man/man3/mbrune.3.gz OLD_FILES+=usr/share/man/man3/rune.3.gz OLD_FILES+=usr/share/man/man3/mac_is_present_np.3.gz OLD_FILES+=usr/share/man/man3/fpsetsticky.3.gz OLD_FILES+=usr/share/man/man3/gss_krb5_copy_ccache.3.gz OLD_FILES+=usr/share/man/man3/gss_krb5_compat_des3_mic.3.gz OLD_FILES+=usr/share/man/man3/exp10f.3.gz OLD_FILES+=usr/share/man/man3/exp10.3.gz OLD_FILES+=usr/share/man/man3/mbrrune.3.gz OLD_FILES+=usr/share/man/man3/mbmb.3.gz OLD_FILES+=usr/share/man/man3/sputrune.3.gz OLD_FILES+=usr/share/man/man3/sgetrune.3.gz OLD_FILES+=usr/share/man/man3/setrunelocale.3.gz OLD_FILES+=usr/share/man/man3/setinvalidrune.3.gz OLD_FILES+=usr/share/man/man3/fungetrune.3.gz OLD_FILES+=usr/share/man/man3/fputrune.3.gz OLD_FILES+=usr/share/man/man3/fgetrune.3.gz OLD_FILES+=usr/share/man/man5/usbd.conf.5.gz .if ${TARGET_ARCH} != "i386" && ${TARGET_ARCH} != "amd64" OLD_FILES+=usr/share/man/man8/boot_i386.8.gz .endif .if ${TARGET_ARCH} != "powerpc" && ${TARGET_ARCH} != "sparc64" OLD_FILES+=usr/share/man/man8/ofwdump.8.gz .endif OLD_FILES+=usr/share/man/man8/mount_reiserfs.8.gz OLD_FILES+=usr/share/man/man9/VFS_START.9.gz OLD_FILES+=usr/share/man/man9/cpu_critical_exit.9.gz OLD_FILES+=usr/share/man/man9/cpu_critical_enter.9.gz OLD_FILES+=usr/share/info/annotate.info.gz OLD_FILES+=usr/share/info/tar.info.gz OLD_FILES+=usr/share/bsnmp/defs/tree.def OLD_FILES+=usr/share/bsnmp/defs/mibII_tree.def OLD_FILES+=usr/share/bsnmp/defs/netgraph_tree.def OLD_FILES+=usr/share/bsnmp/mibs/FOKUS-MIB.txt OLD_FILES+=usr/share/bsnmp/mibs/BEGEMOT-MIB.txt OLD_FILES+=usr/share/bsnmp/mibs/BEGEMOT-SNMPD.txt OLD_FILES+=usr/share/bsnmp/mibs/BEGEMOT-NETGRAPH.txt OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.x OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xbn OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xn OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xr OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xs OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xu OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xc OLD_FILES+=usr/libdata/ldscripts/elf64_sparc.xsc OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.x OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xbn OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xn OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xr OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xs OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xu OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xc OLD_FILES+=usr/libdata/ldscripts/elf32_sparc.xsc OLD_FILES+=usr/libdata/msdosfs/iso22dos OLD_FILES+=usr/libdata/msdosfs/iso72dos OLD_FILES+=usr/libdata/msdosfs/koi2dos OLD_FILES+=usr/libdata/msdosfs/koi8u2dos # The following files are *not* obsolete, they just don't get touched at # install, so don't add them: # - boot/loader.rc # - usr/share/tmac/man.local # - usr/share/tmac/mm/locale # - usr/share/tmac/mm/se_locale # - var/yp/Makefile # 20071120: shared library version bump OLD_LIBS+=usr/lib/libasn1.so.8 OLD_LIBS+=usr/lib/libgssapi.so.8 OLD_LIBS+=usr/lib/libgssapi_krb5.so.8 OLD_LIBS+=usr/lib/libhdb.so.8 OLD_LIBS+=usr/lib/libkadm5clnt.so.8 OLD_LIBS+=usr/lib/libkadm5srv.so.8 OLD_LIBS+=usr/lib/libkafs5.so.8 OLD_LIBS+=usr/lib/libkrb5.so.8 OLD_LIBS+=usr/lib/libobjc.so.2 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libgssapi.so.8 OLD_LIBS+=usr/lib32/libobjc.so.2 .endif # 20070519: GCC 4.2 OLD_LIBS+=usr/lib/libg2c.a OLD_LIBS+=usr/lib/libg2c.so OLD_LIBS+=usr/lib/libg2c.so.2 OLD_LIBS+=usr/lib/libg2c_p.a OLD_LIBS+=usr/lib/libgcc_pic.a .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libg2c.a OLD_LIBS+=usr/lib32/libg2c.so OLD_LIBS+=usr/lib32/libg2c.so.2 OLD_LIBS+=usr/lib32/libg2c_p.a OLD_LIBS+=usr/lib32/libgcc_pic.a .endif # 20060729: OpenSSL 0.9.7e -> 0.9.8b upgrade OLD_LIBS+=lib/libcrypto.so.4 OLD_LIBS+=usr/lib/libssl.so.4 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libcrypto.so.4 OLD_LIBS+=usr/lib32/libssl.so.4 .endif # 20060521: gethostbyaddr(3) ABI change OLD_LIBS+=usr/lib/libroken.so.8 OLD_LIBS+=lib/libatm.so.3 OLD_LIBS+=lib/libc.so.6 OLD_LIBS+=lib/libutil.so.5 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libatm.so.3 OLD_LIBS+=usr/lib32/libc.so.6 OLD_LIBS+=usr/lib32/libutil.so.5 .endif # 20060413: shared library moved to /usr/lib OLD_LIBS+=lib/libgpib.so.1 # 20060413: libpcap.so.4 moved to /lib/ OLD_LIBS+=usr/lib/libpcap.so.4 # 20060412: libpthread.so.2 moved to /lib/ .if ${TARGET_ARCH} != "sparc64" OLD_LIBS+=usr/lib/libpthread.so.2 .else OLD_LIBS+=usr/lib/libthr.so.2 .endif # 20060127: revert libdisk to static-only OLD_LIBS+=usr/lib/libdisk.so.3 # 20051027: libc_r discontinued OLD_LIBS+=usr/lib/libc_r.a OLD_LIBS+=usr/lib/libc_r.so OLD_LIBS+=usr/lib/libc_r.so.7 OLD_LIBS+=usr/lib/libc_r_p.a .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libc_r.a OLD_LIBS+=usr/lib32/libc_r.so OLD_LIBS+=usr/lib32/libc_r.so.7 OLD_LIBS+=usr/lib32/libc_r_p.a .endif # 20050722: bump for 6.0-RELEASE OLD_LIBS+=lib/libalias.so.4 OLD_LIBS+=lib/libatm.so.2 OLD_LIBS+=lib/libbegemot.so.1 OLD_LIBS+=lib/libbsdxml.so.1 OLD_LIBS+=lib/libbsnmp.so.2 OLD_LIBS+=lib/libc.so.5 OLD_LIBS+=lib/libcam.so.2 OLD_LIBS+=lib/libcrypt.so.2 OLD_LIBS+=lib/libcrypto.so.3 OLD_LIBS+=lib/libdevstat.so.4 OLD_LIBS+=lib/libedit.so.4 OLD_LIBS+=lib/libgeom.so.2 OLD_LIBS+=lib/libgpib.so.0 OLD_LIBS+=lib/libipsec.so.1 OLD_LIBS+=lib/libipx.so.2 OLD_LIBS+=lib/libkiconv.so.1 OLD_LIBS+=lib/libkvm.so.2 OLD_LIBS+=lib/libm.so.3 OLD_LIBS+=lib/libmd.so.2 OLD_LIBS+=lib/libncurses.so.5 OLD_LIBS+=lib/libreadline.so.5 OLD_LIBS+=lib/libsbuf.so.2 OLD_LIBS+=lib/libufs.so.2 OLD_LIBS+=lib/libutil.so.4 OLD_LIBS+=lib/libz.so.2 OLD_LIBS+=usr/lib/libarchive.so.1 OLD_LIBS+=usr/lib/libasn1.so.7 OLD_LIBS+=usr/lib/libbluetooth.so.1 OLD_LIBS+=usr/lib/libbz2.so.1 OLD_LIBS+=usr/lib/libc_r.so.5 OLD_LIBS+=usr/lib/libcalendar.so.2 OLD_LIBS+=usr/lib/libcom_err.so.2 OLD_LIBS+=usr/lib/libdevinfo.so.2 OLD_LIBS+=usr/lib/libdialog.so.4 OLD_LIBS+=usr/lib/libfetch.so.3 OLD_LIBS+=usr/lib/libform.so.2 OLD_LIBS+=usr/lib/libftpio.so.5 OLD_LIBS+=usr/lib/libg2c.so.1 OLD_LIBS+=usr/lib/libgnuregex.so.2 OLD_LIBS+=usr/lib/libgssapi.so.7 OLD_LIBS+=usr/lib/libhdb.so.7 OLD_LIBS+=usr/lib/libhistory.so.5 OLD_LIBS+=usr/lib/libkadm5clnt.so.7 OLD_LIBS+=usr/lib/libkadm5srv.so.7 OLD_LIBS+=usr/lib/libkafs5.so.7 OLD_LIBS+=usr/lib/libkrb5.so.7 OLD_LIBS+=usr/lib/libmagic.so.1 OLD_LIBS+=usr/lib/libmenu.so.2 OLD_LIBS+=usr/lib/libmilter.so.2 OLD_LIBS+=usr/lib/libmp.so.4 OLD_LIBS+=usr/lib/libncp.so.1 OLD_LIBS+=usr/lib/libnetgraph.so.1 OLD_LIBS+=usr/lib/libngatm.so.1 OLD_LIBS+=usr/lib/libobjc.so.1 OLD_LIBS+=usr/lib/libopie.so.3 OLD_LIBS+=usr/lib/libpam.so.2 OLD_LIBS+=usr/lib/libpanel.so.2 OLD_LIBS+=usr/lib/libpcap.so.3 OLD_LIBS+=usr/lib/libpmc.so.2 OLD_LIBS+=usr/lib/libpthread.so.1 OLD_LIBS+=usr/lib/libradius.so.1 OLD_LIBS+=usr/lib/libroken.so.7 OLD_LIBS+=usr/lib/librpcsvc.so.2 OLD_LIBS+=usr/lib/libsdp.so.1 OLD_LIBS+=usr/lib/libsmb.so.1 OLD_LIBS+=usr/lib/libssh.so.2 OLD_LIBS+=usr/lib/libssl.so.3 OLD_LIBS+=usr/lib/libstdc++.so.4 OLD_LIBS+=usr/lib/libtacplus.so.1 OLD_LIBS+=usr/lib/libthr.so.1 OLD_LIBS+=usr/lib/libthread_db.so.1 OLD_LIBS+=usr/lib/libugidfw.so.1 OLD_LIBS+=usr/lib/libusbhid.so.1 OLD_LIBS+=usr/lib/libvgl.so.3 OLD_LIBS+=usr/lib/libwrap.so.3 OLD_LIBS+=usr/lib/libypclnt.so.1 OLD_LIBS+=usr/lib/pam_chroot.so.2 OLD_LIBS+=usr/lib/pam_deny.so.2 OLD_LIBS+=usr/lib/pam_echo.so.2 OLD_LIBS+=usr/lib/pam_exec.so.2 OLD_LIBS+=usr/lib/pam_ftpusers.so.2 OLD_LIBS+=usr/lib/pam_group.so.2 OLD_LIBS+=usr/lib/pam_guest.so.2 OLD_LIBS+=usr/lib/pam_krb5.so.2 OLD_LIBS+=usr/lib/pam_ksu.so.2 OLD_LIBS+=usr/lib/pam_lastlog.so.2 OLD_LIBS+=usr/lib/pam_login_access.so.2 OLD_LIBS+=usr/lib/pam_nologin.so.2 OLD_LIBS+=usr/lib/pam_opie.so.2 OLD_LIBS+=usr/lib/pam_opieaccess.so.2 OLD_LIBS+=usr/lib/pam_passwdqc.so.2 OLD_LIBS+=usr/lib/pam_permit.so.2 OLD_LIBS+=usr/lib/pam_radius.so.2 OLD_LIBS+=usr/lib/pam_rhosts.so.2 OLD_LIBS+=usr/lib/pam_rootok.so.2 OLD_LIBS+=usr/lib/pam_securetty.so.2 OLD_LIBS+=usr/lib/pam_self.so.2 OLD_LIBS+=usr/lib/pam_ssh.so.2 OLD_LIBS+=usr/lib/pam_tacplus.so.2 OLD_LIBS+=usr/lib/pam_unix.so.2 OLD_LIBS+=usr/lib/snmp_atm.so.3 OLD_LIBS+=usr/lib/snmp_mibII.so.3 OLD_LIBS+=usr/lib/snmp_netgraph.so.3 OLD_LIBS+=usr/lib/snmp_pf.so.3 # 20050603: network related ABI change on 64bit systems OLD_LIBS+=usr/lib/libpcap.so.3 # 200505XX: ? OLD_LIBS+=usr/lib/snmp_atm.so.2 OLD_LIBS+=usr/lib/snmp_mibII.so.2 OLD_LIBS+=usr/lib/snmp_netgraph.so.2 OLD_LIBS+=usr/lib/snmp_pf.so.2 # 2005XXXX: not ready for primetime yet OLD_LIBS+=usr/lib/libautofs.so.1 # 200411XX: libxpg4 removal OLD_LIBS+=usr/lib/libxpg4.so.3 # 200410XX: libm compatibility fix OLD_LIBS+=lib/libm.so.2 # 20041001: version bump OLD_LIBS+=lib/libreadline.so.4 OLD_LIBS+=usr/lib/libhistory.so.4 OLD_LIBS+=usr/lib/libopie.so.2 OLD_LIBS+=usr/lib/libpcap.so.2 # 20040925: bind9 import OLD_LIBS+=usr/lib/libisc.so.1 # 200408XX OLD_LIBS+=usr/lib/snmp_netgraph.so.1 .if ${TARGET_ARCH} != "sparc64" # 20040130: libkse renamed to libpthread OLD_LIBS+=usr/lib/libkse.so.1 .endif # 200404XX OLD_LIBS+=usr/lib/libsnmp.so.1 OLD_LIBS+=usr/lib/snmp_mibII.so.1 # 200309XX OLD_LIBS+=usr/lib/libasn1.so.6 OLD_LIBS+=usr/lib/libhdb.so.6 OLD_LIBS+=usr/lib/libkadm5clnt.so.6 OLD_LIBS+=usr/lib/libkadm5srv.so.6 OLD_LIBS+=usr/lib/libkrb5.so.6 OLD_LIBS+=usr/lib/libroken.so.6 # 200304XX OLD_LIBS+=usr/lib/libc.so.4 OLD_LIBS+=usr/lib/libc_r.so.4 OLD_LIBS+=usr/lib/libdevstat.so.2 OLD_LIBS+=usr/lib/libedit.so.3 OLD_LIBS+=usr/lib/libgmp.so.3 OLD_LIBS+=usr/lib/libmp.so.3 OLD_LIBS+=usr/lib/libpam.so.1 OLD_LIBS+=usr/lib/libposix1e.so.2 OLD_LIBS+=usr/lib/libskey.so.2 OLD_LIBS+=usr/lib/libusbhid.so.0 OLD_LIBS+=usr/lib/libvgl.so.2 # 200302XX OLD_LIBS+=usr/lib/libacl.so.3 OLD_LIBS+=usr/lib/libasn1.so.5 OLD_LIBS+=usr/lib/libcrypto.so.2 OLD_LIBS+=usr/lib/libgssapi.so.5 OLD_LIBS+=usr/lib/libhdb.so.5 OLD_LIBS+=usr/lib/libkadm.so.3 OLD_LIBS+=usr/lib/libkadm5clnt.so.5 OLD_LIBS+=usr/lib/libkadm5srv.so.5 OLD_LIBS+=usr/lib/libkafs.so.3 OLD_LIBS+=usr/lib/libkafs5.so.5 OLD_LIBS+=usr/lib/libkdb.so.3 OLD_LIBS+=usr/lib/libkrb.so.3 OLD_LIBS+=usr/lib/libroken.so. OLD_LIBS+=usr/lib/libssl.so.2 OLD_LIBS+=usr/lib/pam_kerberosIV.so # 200208XX OLD_LIBS+=usr/lib/libgssapi.so.4 # 200203XX OLD_LIBS+=usr/lib/libss.so.3 OLD_LIBS+=usr/lib/libusb.so.0 # 200112XX OLD_LIBS+=usr/lib/libfetch.so.2 # 200110XX OLD_LIBS+=usr/lib/libgssapi.so.3 # 200104XX OLD_LIBS+=usr/lib/libdescrypt.so.2 OLD_LIBS+=usr/lib/libscrypt.so.2 # 200102XX OLD_LIBS+=usr/lib/libcrypto.so.1 OLD_LIBS+=usr/lib/libssl.so.1 # 200009XX OLD_LIBS+=usr/lib/libRSAglue.so.1 OLD_LIBS+=usr/lib/librsaINTL.so.1 OLD_LIBS+=usr/lib/librsaUSA.so.1 # 200006XX OLD_LIBS+=usr/lib/libalias.so.3 OLD_LIBS+=usr/lib/libfetch.so.1 OLD_LIBS+=usr/lib/libipsec.so.0 # 200005XX OLD_LIBS+=usr/lib/libxpg4.so.2 # 200002XX OLD_LIBS+=usr/lib/libc.so.3 OLD_LIBS+=usr/lib/libcurses.so.2 OLD_LIBS+=usr/lib/libdialog.so.3 OLD_LIBS+=usr/lib/libedit.so.2 OLD_LIBS+=usr/lib/libf2c.so.2 OLD_LIBS+=usr/lib/libftpio.so.4 OLD_LIBS+=usr/lib/libg++.so.4 OLD_LIBS+=usr/lib/libhistory.so.3 OLD_LIBS+=usr/lib/libmytinfo.so.2 OLD_LIBS+=usr/lib/libncurses.so.3 OLD_LIBS+=usr/lib/libreadline.so.3 OLD_LIBS+=usr/lib/libss.so.2 OLD_LIBS+=usr/lib/libtermcap.so.2 OLD_LIBS+=usr/lib/libutil.so.2 OLD_LIBS+=usr/lib/libvgl.so.1 OLD_LIBS+=usr/lib/libwrap.so.2 # 199909XX OLD_LIBS+=usr/lib/libc_r.so.3 # ??? OLD_LIBS+=usr/lib/libarchive.so.2 OLD_LIBS+=usr/lib/libbsnmp.so.1 OLD_LIBS+=usr/lib/libc_r.so.6 .if ${TARGET_ARCH} == "amd64" OLD_LIBS+=usr/lib32/libarchive.so.2 OLD_LIBS+=usr/lib32/libc_r.so.6 .endif OLD_LIBS+=usr/lib/libcipher.so.2 OLD_LIBS+=usr/lib/libgssapi.so.6 OLD_LIBS+=usr/lib/libkse.so.1 OLD_LIBS+=usr/lib/liblwres.so.3 OLD_LIBS+=usr/lib/pam_ftp.so.2 # 20040925: bind9 import OLD_DIRS+=usr/share/doc/bind/html OLD_DIRS+=usr/share/doc/bind/misc OLD_DIRS+=usr/share/doc/bind/ # ??? OLD_DIRS+=usr/include/g++/std OLD_DIRS+=usr/include/msdosfs OLD_DIRS+=usr/include/ntfs OLD_DIRS+=usr/include/nwfs OLD_DIRS+=usr/include/ufs/mfs # 20011001: UUCP migration to ports OLD_DIRS+=usr/libexec/uucp .include "tools/build/mk/OptionalObsoleteFiles.inc" Index: projects/cambria/etc/devd/asus.conf =================================================================== --- projects/cambria/etc/devd/asus.conf (revision 186459) +++ projects/cambria/etc/devd/asus.conf (revision 186460) @@ -1,52 +1,74 @@ # $FreeBSD$ # # ASUS specific devd events +# The next blocks enable volume hotkeys that can be found on the Asus laptops +notify 0 { + match "system" "ACPI"; + match "subsystem" "ASUS"; + match "notify" "0x32"; + action "mixer 0"; +}; + +notify 0 { + match "system" "ACPI"; + match "subsystem" "ASUS"; + match "notify" "0x31"; + action "mixer vol -10"; +}; + +notify 0 { + match "system" "ACPI"; + match "subsystem" "ASUS"; + match "notify" "0x30"; + action "mixer vol +10"; +}; + # The next blocks enable volume hotkeys that can be found on the Asus EeePC notify 0 { match "system" "ACPI"; match "subsystem" "ASUS-Eee"; match "notify" "0x13"; action "mixer 0"; }; notify 0 { match "system" "ACPI"; match "subsystem" "ASUS-Eee"; match "notify" "0x14"; action "mixer vol -10"; }; notify 0 { match "system" "ACPI"; match "subsystem" "ASUS-Eee"; match "notify" "0x15"; action "mixer vol +10"; }; # Enable user hotkeys that can be found on the Asus EeePC # The four keys above the keyboard notify 0x1a through to 0x1d respectively #notify 0 { # match "system" "ACPI"; # match "subsystem" "ASUS-Eee"; # match "notify" "0x1a"; # action ""; #}; #notify 0 { # match "system" "ACPI"; # match "subsystem" "ASUS-Eee"; # match "notify" "0x1b"; # action ""; #}; #notify 0 { # match "system" "ACPI"; # match "subsystem" "ASUS-Eee"; # match "notify" "0x1c"; # action ""; #}; #notify 0 { # match "system" "ACPI"; # match "subsystem" "ASUS-Eee"; # match "notify" "0x1d"; # action ""; #}; Index: projects/cambria/etc/devd.conf =================================================================== --- projects/cambria/etc/devd.conf (revision 186459) +++ projects/cambria/etc/devd.conf (revision 186460) @@ -1,330 +1,308 @@ # $FreeBSD$ # # Refer to devd.conf(5) and devd(8) man pages for the details on how to # run and configure devd. # # NB: All regular expressions have an implicit ^$ around them. # NB: device-name is shorthand for 'match device-name' options { # Each directory directive adds a directory the list of directories # that we scan for files. Files are read-in in the order that they # are returned from readdir(3). The rule-sets are combined to # create a DFA that's used to match events to actions. directory "/etc/devd"; directory "/usr/local/etc/devd"; pid-file "/var/run/devd.pid"; # Setup some shorthand for regex that we use later in the file. #XXX Yes, these are gross -- imp set scsi-controller-regex "(aac|adv|adw|aha|ahb|ahc|ahd|aic|amd|amr|asr|bt|ciss|ct|dpt|\ esp|ida|iir|ips|isp|mlx|mly|mpt|ncr|ncv|nsp|stg|sym|trm|wds)\ [0-9]+"; }; # Note that the attach/detach with the highest value wins, so that one can # override these general rules. # # Configure the interface on attach. Due to a historical accident, this # script is called pccard_ether. # notify 0 { match "system" "IFNET"; match "type" "ATTACH"; action "/etc/pccard_ether $subsystem start"; }; notify 0 { match "system" "IFNET"; match "type" "DETACH"; action "/etc/pccard_ether $subsystem stop"; }; # # Try to start dhclient on Ethernet like interfaces when the link comes # up. Only devices that are configured to support DHCP will actually # run it. No link down rule exists because dhclient automaticly exits # when the link goes down. # notify 0 { match "system" "IFNET"; match "type" "LINK_UP"; media-type "ethernet"; action "/etc/rc.d/dhclient quietstart $subsystem"; }; # # Like Ethernet devices, but separate because # they have a different media type. We may want # to exploit this later. # detach 0 { media-type "802.11"; action "/etc/pccard_ether $device-name stop"; }; attach 0 { media-type "802.11"; action "/etc/pccard_ether $device-name start"; }; notify 0 { match "system" "IFNET"; match "type" "LINK_UP"; media-type "802.11"; action "/etc/rc.d/dhclient quietstart $subsystem"; }; # An entry like this might be in a different file, but is included here # as an example of how to override things. Normally 'ed50' would match # the above attach/detach stuff, but the value of 100 makes it # hard wired to 1.2.3.4. attach 100 { device-name "ed50"; action "ifconfig $device-name inet 1.2.3.4 netmask 0xffff0000"; }; detach 100 { device-name "ed50"; }; # When a USB Bluetooth dongle appears activate it attach 100 { device-name "ubt[0-9]+"; action "/etc/rc.d/bluetooth quietstart $device-name"; }; detach 100 { device-name "ubt[0-9]+"; action "/etc/rc.d/bluetooth quietstop $device-name"; }; # When a USB keyboard arrives, attach it as the console keyboard. attach 100 { device-name "ukbd0"; action "/etc/rc.d/syscons setkeyboard /dev/ukbd0"; }; detach 100 { device-name "ukbd0"; action "/etc/rc.d/syscons setkeyboard /dev/kbd0"; }; attach 100 { device-name "ums[0-9]+"; action "/etc/rc.d/moused quietstart $device-name"; }; detach 100 { device-name "ums[0-9]+"; action "/etc/rc.d/moused stop $device-name"; }; # Firmware download into the ActiveWire board. After the firmware download is # done the device detaches and reappears as something new and shiny # automatically. attach 100 { match "vendor" "0x0854"; match "product" "0x0100"; match "release" "0x0000"; action "/usr/local/bin/ezdownload -f /usr/local/share/usb/firmware/0854.0100.0_01.hex $device-name"; }; # Firmware download for Entrega Serial DB25 adapter. attach 100 { match "vendor" "0x1645"; match "product" "0x8001"; match "release" "0x0101"; action "if ! kldstat -n usio > /dev/null 2>&1 ; then kldload usio; fi /usr/sbin/ezdownload -v -f /usr/share/usb/firmware/1645.8001.0101 /dev/$device-name"; }; # This entry starts the ColdSync tool in daemon mode. Make sure you have an up # to date /usr/local/etc/palms. We override the 'listen' settings for port and # type in /usr/local/etc/coldsync.conf. attach 100 { device-name "ugen[0-9]+"; match "vendor" "0x082d"; match "product" "0x0100"; match "release" "0x0100"; action "/usr/local/bin/coldsync -md -p /dev/$device-name -t usb"; }; # # Rescan scsi device-names on attach, but not detach. However, it is # disabled by default due to reports of problems. # attach 0 { device-name "$scsi-controller-regex"; // action "camcontrol rescan all"; }; # Don't even try to second guess what to do about drivers that don't # match here. Instead, pass it off to syslog. Commented out for the # moment, as the pnpinfo variable isn't set in devd yet. Individual # variables within the bus supplied pnpinfo are set. nomatch 0 { # action "logger Unknown device: $pnpinfo $location $bus"; }; # Various logging of unknown devices. nomatch 10 { match "bus" "uhub[0-9]+"; action "logger Unknown USB device: vendor $vendor product $product \ bus $bus"; }; # Some PC-CARDs don't offer numerical manufacturer/product IDs, just # show the CIS info there. nomatch 20 { match "bus" "pccard[0-9]+"; match "manufacturer" "0xffffffff"; match "product" "0xffffffff"; action "logger Unknown PCCARD device: CISproduct $cisproduct \ CIS-vendor $cisvendor bus $bus"; }; nomatch 10 { match "bus" "pccard[0-9]+"; action "logger Unknown PCCARD device: manufacturer $manufacturer \ product $product CISproduct $cisproduct CIS-vendor \ $cisvendor bus $bus"; }; nomatch 10 { match "bus" "cardbus[0-9]+"; action "logger Unknown Cardbus device: device $device class $class \ vendor $vendor bus $bus"; }; # Switch power profiles when the AC line state changes. notify 10 { match "system" "ACPI"; match "subsystem" "ACAD"; action "/etc/rc.d/power_profile $notify"; }; # Notify all users before beginning emergency shutdown when we get # a _CRT or _HOT thermal event and we're going to power down the system # very soon. notify 10 { match "system" "ACPI"; match "subsystem" "Thermal"; match "notify" "0xcc"; action "logger -p kern.emerg 'WARNING: system temperature too high, shutting down soon!'"; }; # Sample ZFS problem reports handling. notify 10 { match "system" "ZFS"; match "type" "zpool"; action "logger -p kern.err 'ZFS: failed to load zpool $pool'"; }; notify 10 { match "system" "ZFS"; match "type" "vdev"; action "logger -p kern.err 'ZFS: vdev failure, zpool=$pool type=$type'"; }; notify 10 { match "system" "ZFS"; match "type" "data"; action "logger -p kern.warn 'ZFS: zpool I/O failure, zpool=$pool error=$zio_err'"; }; notify 10 { match "system" "ZFS"; match "type" "io"; action "logger -p kern.warn 'ZFS: vdev I/O failure, zpool=$pool path=$vdev_path offset=$zio_offset size=$zio_size error=$zio_err'"; }; notify 10 { match "system" "ZFS"; match "type" "checksum"; action "logger -p kern.warn 'ZFS: checksum mismatch, zpool=$pool path=$vdev_path offset=$zio_offset size=$zio_size'"; }; # User requested suspend, so perform preparation steps and then execute # the actual suspend process. notify 10 { match "system" "ACPI"; match "subsystem" "Suspend"; action "/etc/rc.suspend acpi $notify"; }; notify 10 { match "system" "ACPI"; match "subsystem" "Resume"; action "/etc/rc.resume acpi $notify"; }; -# The next blocks enable volume hotkeys that can be found on the Asus laptops -notify 0 { - match "system" "ACPI"; - match "subsystem" "ASUS"; - match "notify" "0x32"; - action "mixer 0"; -}; - -notify 0 { - match "system" "ACPI"; - match "subsystem" "ASUS"; - match "notify" "0x31"; - action "mixer vol -10"; -}; - -notify 0 { - match "system" "ACPI"; - match "subsystem" "ASUS"; - match "notify" "0x30"; - action "mixer vol +10"; -}; - /* EXAMPLES TO END OF FILE # The following might be an example of something that a vendor might # install if you were to add their device. This might reside in # /usr/local/etc/devd/deqna.conf. A deqna is, in this hypothetical # example, a pccard ethernet-like device. Students of history may # know other devices by this name, and will get the in-jokes in this # entry. nomatch 10 { match "bus" "pccard[0-9]+"; match "manufacturer" "0x1234"; match "product" "0x2323"; action "kldload if_deqna"; }; attach 10 { device-name "deqna[0-9]+"; action "/etc/pccard_ether $device-name start"; }; detach 10 { device-name "deqna[0-9]+"; action "/etc/pccard_ether $device-name stop"; }; # Examples of notify hooks. A notify is a generic way for a kernel # subsystem to send event notification to userland. # # Here are some examples of ACPI notify handlers. ACPI subsystems that # generate notifies include the AC adapter, power/sleep buttons, # control method batteries, lid switch, and thermal zones. # # Information returned is not always the same as the ACPI notify # events. See the ACPI specification for more information about # notifies. Here is the information returned for each subsystem: # # ACAD: AC line state (0 is offline, 1 is online) # Button: Button pressed (0 for power, 1 for sleep) # CMBAT: ACPI battery events # Lid: Lid state (0 is closed, 1 is open) # Suspend, Resume: Suspend and resume notification # Thermal: ACPI thermal zone events # # This example calls a script when the AC state changes, passing the # notify value as the first argument. If the state is 0x00, it might # call some sysctls to implement economy mode. If 0x01, it might set # the mode to performance. notify 10 { match "system" "ACPI"; match "subsystem" "ACAD"; action "/etc/acpi_ac $notify"; }; */ Index: projects/cambria/lib/bind/dns/code.h =================================================================== --- projects/cambria/lib/bind/dns/code.h (revision 186459) +++ projects/cambria/lib/bind/dns/code.h (revision 186460) @@ -1,1649 +1,1649 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /*************** *************** *************** THIS FILE IS AUTOMATICALLY GENERATED BY gen.c. *************** DO NOT EDIT! *************** ***************/ /*! \file */ #ifndef DNS_CODE_H #define DNS_CODE_H 1 #include #include #include #include "rdata/in_1/a_1.c" #include "rdata/ch_3/a_1.c" #include "rdata/hs_4/a_1.c" #include "rdata/generic/ns_2.c" #include "rdata/generic/md_3.c" #include "rdata/generic/mf_4.c" #include "rdata/generic/cname_5.c" #include "rdata/generic/soa_6.c" #include "rdata/generic/mb_7.c" #include "rdata/generic/mg_8.c" #include "rdata/generic/mr_9.c" #include "rdata/generic/null_10.c" #include "rdata/in_1/wks_11.c" #include "rdata/generic/ptr_12.c" #include "rdata/generic/hinfo_13.c" #include "rdata/generic/minfo_14.c" #include "rdata/generic/mx_15.c" #include "rdata/generic/txt_16.c" #include "rdata/generic/rp_17.c" #include "rdata/generic/afsdb_18.c" #include "rdata/generic/x25_19.c" #include "rdata/generic/isdn_20.c" #include "rdata/generic/rt_21.c" #include "rdata/in_1/nsap_22.c" #include "rdata/in_1/nsap-ptr_23.c" #include "rdata/generic/sig_24.c" #include "rdata/generic/key_25.c" #include "rdata/in_1/px_26.c" #include "rdata/generic/gpos_27.c" #include "rdata/in_1/aaaa_28.c" #include "rdata/generic/loc_29.c" #include "rdata/generic/nxt_30.c" #include "rdata/in_1/srv_33.c" #include "rdata/in_1/naptr_35.c" #include "rdata/in_1/kx_36.c" #include "rdata/generic/cert_37.c" #include "rdata/in_1/a6_38.c" #include "rdata/generic/dname_39.c" #include "rdata/generic/opt_41.c" #include "rdata/in_1/apl_42.c" #include "rdata/generic/ds_43.c" #include "rdata/generic/sshfp_44.c" #include "rdata/generic/ipseckey_45.c" #include "rdata/generic/rrsig_46.c" #include "rdata/generic/nsec_47.c" #include "rdata/generic/dnskey_48.c" #include "rdata/generic/spf_99.c" #include "rdata/generic/unspec_103.c" #include "rdata/generic/tkey_249.c" #include "rdata/any_255/tsig_250.c" #include "rdata/generic/dlv_32769.c" #define FROMTEXTSWITCH \ switch (type) { \ case 1: switch (rdclass) { \ case 1: result = fromtext_in_a(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 3: result = fromtext_ch_a(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 4: result = fromtext_hs_a(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 2: result = fromtext_ns(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 3: result = fromtext_md(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 4: result = fromtext_mf(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 5: result = fromtext_cname(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 6: result = fromtext_soa(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 7: result = fromtext_mb(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 8: result = fromtext_mg(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 9: result = fromtext_mr(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 10: result = fromtext_null(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 11: switch (rdclass) { \ case 1: result = fromtext_in_wks(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 12: result = fromtext_ptr(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 13: result = fromtext_hinfo(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 14: result = fromtext_minfo(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 15: result = fromtext_mx(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 16: result = fromtext_txt(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 17: result = fromtext_rp(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 18: result = fromtext_afsdb(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 19: result = fromtext_x25(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 20: result = fromtext_isdn(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 21: result = fromtext_rt(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 22: switch (rdclass) { \ case 1: result = fromtext_in_nsap(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 23: switch (rdclass) { \ case 1: result = fromtext_in_nsap_ptr(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 24: result = fromtext_sig(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 25: result = fromtext_key(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 26: switch (rdclass) { \ case 1: result = fromtext_in_px(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 27: result = fromtext_gpos(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 28: switch (rdclass) { \ case 1: result = fromtext_in_aaaa(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 29: result = fromtext_loc(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 30: result = fromtext_nxt(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 33: switch (rdclass) { \ case 1: result = fromtext_in_srv(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 35: switch (rdclass) { \ case 1: result = fromtext_in_naptr(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 36: switch (rdclass) { \ case 1: result = fromtext_in_kx(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 37: result = fromtext_cert(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 38: switch (rdclass) { \ case 1: result = fromtext_in_a6(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 39: result = fromtext_dname(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 41: result = fromtext_opt(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 42: switch (rdclass) { \ case 1: result = fromtext_in_apl(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 43: result = fromtext_ds(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 44: result = fromtext_sshfp(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 45: result = fromtext_ipseckey(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 46: result = fromtext_rrsig(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 47: result = fromtext_nsec(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 48: result = fromtext_dnskey(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 99: result = fromtext_spf(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 103: result = fromtext_unspec(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 249: result = fromtext_tkey(rdclass, type, lexer, origin, options, target, callbacks); break; \ case 250: switch (rdclass) { \ case 255: result = fromtext_any_tsig(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } \ break; \ case 32769: result = fromtext_dlv(rdclass, type, lexer, origin, options, target, callbacks); break; \ default: result = DNS_R_UNKNOWN; break; \ } #define TOTEXTSWITCH \ switch (rdata->type) { \ case 1: switch (rdata->rdclass) { \ case 1: result = totext_in_a(rdata, tctx, target); break; \ case 3: result = totext_ch_a(rdata, tctx, target); break; \ case 4: result = totext_hs_a(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = totext_ns(rdata, tctx, target); break; \ case 3: result = totext_md(rdata, tctx, target); break; \ case 4: result = totext_mf(rdata, tctx, target); break; \ case 5: result = totext_cname(rdata, tctx, target); break; \ case 6: result = totext_soa(rdata, tctx, target); break; \ case 7: result = totext_mb(rdata, tctx, target); break; \ case 8: result = totext_mg(rdata, tctx, target); break; \ case 9: result = totext_mr(rdata, tctx, target); break; \ case 10: result = totext_null(rdata, tctx, target); break; \ case 11: switch (rdata->rdclass) { \ case 1: result = totext_in_wks(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = totext_ptr(rdata, tctx, target); break; \ case 13: result = totext_hinfo(rdata, tctx, target); break; \ case 14: result = totext_minfo(rdata, tctx, target); break; \ case 15: result = totext_mx(rdata, tctx, target); break; \ case 16: result = totext_txt(rdata, tctx, target); break; \ case 17: result = totext_rp(rdata, tctx, target); break; \ case 18: result = totext_afsdb(rdata, tctx, target); break; \ case 19: result = totext_x25(rdata, tctx, target); break; \ case 20: result = totext_isdn(rdata, tctx, target); break; \ case 21: result = totext_rt(rdata, tctx, target); break; \ case 22: switch (rdata->rdclass) { \ case 1: result = totext_in_nsap(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata->rdclass) { \ case 1: result = totext_in_nsap_ptr(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = totext_sig(rdata, tctx, target); break; \ case 25: result = totext_key(rdata, tctx, target); break; \ case 26: switch (rdata->rdclass) { \ case 1: result = totext_in_px(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = totext_gpos(rdata, tctx, target); break; \ case 28: switch (rdata->rdclass) { \ case 1: result = totext_in_aaaa(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = totext_loc(rdata, tctx, target); break; \ case 30: result = totext_nxt(rdata, tctx, target); break; \ case 33: switch (rdata->rdclass) { \ case 1: result = totext_in_srv(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata->rdclass) { \ case 1: result = totext_in_naptr(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata->rdclass) { \ case 1: result = totext_in_kx(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = totext_cert(rdata, tctx, target); break; \ case 38: switch (rdata->rdclass) { \ case 1: result = totext_in_a6(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = totext_dname(rdata, tctx, target); break; \ case 41: result = totext_opt(rdata, tctx, target); break; \ case 42: switch (rdata->rdclass) { \ case 1: result = totext_in_apl(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = totext_ds(rdata, tctx, target); break; \ case 44: result = totext_sshfp(rdata, tctx, target); break; \ case 45: result = totext_ipseckey(rdata, tctx, target); break; \ case 46: result = totext_rrsig(rdata, tctx, target); break; \ case 47: result = totext_nsec(rdata, tctx, target); break; \ case 48: result = totext_dnskey(rdata, tctx, target); break; \ case 99: result = totext_spf(rdata, tctx, target); break; \ case 103: result = totext_unspec(rdata, tctx, target); break; \ case 249: result = totext_tkey(rdata, tctx, target); break; \ case 250: switch (rdata->rdclass) { \ case 255: result = totext_any_tsig(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = totext_dlv(rdata, tctx, target); break; \ default: use_default = ISC_TRUE; break; \ } #define FROMWIRESWITCH \ switch (type) { \ case 1: switch (rdclass) { \ case 1: result = fromwire_in_a(rdclass, type, source, dctx, options, target); break; \ case 3: result = fromwire_ch_a(rdclass, type, source, dctx, options, target); break; \ case 4: result = fromwire_hs_a(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = fromwire_ns(rdclass, type, source, dctx, options, target); break; \ case 3: result = fromwire_md(rdclass, type, source, dctx, options, target); break; \ case 4: result = fromwire_mf(rdclass, type, source, dctx, options, target); break; \ case 5: result = fromwire_cname(rdclass, type, source, dctx, options, target); break; \ case 6: result = fromwire_soa(rdclass, type, source, dctx, options, target); break; \ case 7: result = fromwire_mb(rdclass, type, source, dctx, options, target); break; \ case 8: result = fromwire_mg(rdclass, type, source, dctx, options, target); break; \ case 9: result = fromwire_mr(rdclass, type, source, dctx, options, target); break; \ case 10: result = fromwire_null(rdclass, type, source, dctx, options, target); break; \ case 11: switch (rdclass) { \ case 1: result = fromwire_in_wks(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = fromwire_ptr(rdclass, type, source, dctx, options, target); break; \ case 13: result = fromwire_hinfo(rdclass, type, source, dctx, options, target); break; \ case 14: result = fromwire_minfo(rdclass, type, source, dctx, options, target); break; \ case 15: result = fromwire_mx(rdclass, type, source, dctx, options, target); break; \ case 16: result = fromwire_txt(rdclass, type, source, dctx, options, target); break; \ case 17: result = fromwire_rp(rdclass, type, source, dctx, options, target); break; \ case 18: result = fromwire_afsdb(rdclass, type, source, dctx, options, target); break; \ case 19: result = fromwire_x25(rdclass, type, source, dctx, options, target); break; \ case 20: result = fromwire_isdn(rdclass, type, source, dctx, options, target); break; \ case 21: result = fromwire_rt(rdclass, type, source, dctx, options, target); break; \ case 22: switch (rdclass) { \ case 1: result = fromwire_in_nsap(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdclass) { \ case 1: result = fromwire_in_nsap_ptr(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = fromwire_sig(rdclass, type, source, dctx, options, target); break; \ case 25: result = fromwire_key(rdclass, type, source, dctx, options, target); break; \ case 26: switch (rdclass) { \ case 1: result = fromwire_in_px(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = fromwire_gpos(rdclass, type, source, dctx, options, target); break; \ case 28: switch (rdclass) { \ case 1: result = fromwire_in_aaaa(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = fromwire_loc(rdclass, type, source, dctx, options, target); break; \ case 30: result = fromwire_nxt(rdclass, type, source, dctx, options, target); break; \ case 33: switch (rdclass) { \ case 1: result = fromwire_in_srv(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdclass) { \ case 1: result = fromwire_in_naptr(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdclass) { \ case 1: result = fromwire_in_kx(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = fromwire_cert(rdclass, type, source, dctx, options, target); break; \ case 38: switch (rdclass) { \ case 1: result = fromwire_in_a6(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = fromwire_dname(rdclass, type, source, dctx, options, target); break; \ case 41: result = fromwire_opt(rdclass, type, source, dctx, options, target); break; \ case 42: switch (rdclass) { \ case 1: result = fromwire_in_apl(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = fromwire_ds(rdclass, type, source, dctx, options, target); break; \ case 44: result = fromwire_sshfp(rdclass, type, source, dctx, options, target); break; \ case 45: result = fromwire_ipseckey(rdclass, type, source, dctx, options, target); break; \ case 46: result = fromwire_rrsig(rdclass, type, source, dctx, options, target); break; \ case 47: result = fromwire_nsec(rdclass, type, source, dctx, options, target); break; \ case 48: result = fromwire_dnskey(rdclass, type, source, dctx, options, target); break; \ case 99: result = fromwire_spf(rdclass, type, source, dctx, options, target); break; \ case 103: result = fromwire_unspec(rdclass, type, source, dctx, options, target); break; \ case 249: result = fromwire_tkey(rdclass, type, source, dctx, options, target); break; \ case 250: switch (rdclass) { \ case 255: result = fromwire_any_tsig(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = fromwire_dlv(rdclass, type, source, dctx, options, target); break; \ default: use_default = ISC_TRUE; break; \ } #define TOWIRESWITCH \ switch (rdata->type) { \ case 1: switch (rdata->rdclass) { \ case 1: result = towire_in_a(rdata, cctx, target); break; \ case 3: result = towire_ch_a(rdata, cctx, target); break; \ case 4: result = towire_hs_a(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = towire_ns(rdata, cctx, target); break; \ case 3: result = towire_md(rdata, cctx, target); break; \ case 4: result = towire_mf(rdata, cctx, target); break; \ case 5: result = towire_cname(rdata, cctx, target); break; \ case 6: result = towire_soa(rdata, cctx, target); break; \ case 7: result = towire_mb(rdata, cctx, target); break; \ case 8: result = towire_mg(rdata, cctx, target); break; \ case 9: result = towire_mr(rdata, cctx, target); break; \ case 10: result = towire_null(rdata, cctx, target); break; \ case 11: switch (rdata->rdclass) { \ case 1: result = towire_in_wks(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = towire_ptr(rdata, cctx, target); break; \ case 13: result = towire_hinfo(rdata, cctx, target); break; \ case 14: result = towire_minfo(rdata, cctx, target); break; \ case 15: result = towire_mx(rdata, cctx, target); break; \ case 16: result = towire_txt(rdata, cctx, target); break; \ case 17: result = towire_rp(rdata, cctx, target); break; \ case 18: result = towire_afsdb(rdata, cctx, target); break; \ case 19: result = towire_x25(rdata, cctx, target); break; \ case 20: result = towire_isdn(rdata, cctx, target); break; \ case 21: result = towire_rt(rdata, cctx, target); break; \ case 22: switch (rdata->rdclass) { \ case 1: result = towire_in_nsap(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata->rdclass) { \ case 1: result = towire_in_nsap_ptr(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = towire_sig(rdata, cctx, target); break; \ case 25: result = towire_key(rdata, cctx, target); break; \ case 26: switch (rdata->rdclass) { \ case 1: result = towire_in_px(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = towire_gpos(rdata, cctx, target); break; \ case 28: switch (rdata->rdclass) { \ case 1: result = towire_in_aaaa(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = towire_loc(rdata, cctx, target); break; \ case 30: result = towire_nxt(rdata, cctx, target); break; \ case 33: switch (rdata->rdclass) { \ case 1: result = towire_in_srv(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata->rdclass) { \ case 1: result = towire_in_naptr(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata->rdclass) { \ case 1: result = towire_in_kx(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = towire_cert(rdata, cctx, target); break; \ case 38: switch (rdata->rdclass) { \ case 1: result = towire_in_a6(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = towire_dname(rdata, cctx, target); break; \ case 41: result = towire_opt(rdata, cctx, target); break; \ case 42: switch (rdata->rdclass) { \ case 1: result = towire_in_apl(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = towire_ds(rdata, cctx, target); break; \ case 44: result = towire_sshfp(rdata, cctx, target); break; \ case 45: result = towire_ipseckey(rdata, cctx, target); break; \ case 46: result = towire_rrsig(rdata, cctx, target); break; \ case 47: result = towire_nsec(rdata, cctx, target); break; \ case 48: result = towire_dnskey(rdata, cctx, target); break; \ case 99: result = towire_spf(rdata, cctx, target); break; \ case 103: result = towire_unspec(rdata, cctx, target); break; \ case 249: result = towire_tkey(rdata, cctx, target); break; \ case 250: switch (rdata->rdclass) { \ case 255: result = towire_any_tsig(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = towire_dlv(rdata, cctx, target); break; \ default: use_default = ISC_TRUE; break; \ } #define COMPARESWITCH \ switch (rdata1->type) { \ case 1: switch (rdata1->rdclass) { \ case 1: result = compare_in_a(rdata1, rdata2); break; \ case 3: result = compare_ch_a(rdata1, rdata2); break; \ case 4: result = compare_hs_a(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = compare_ns(rdata1, rdata2); break; \ case 3: result = compare_md(rdata1, rdata2); break; \ case 4: result = compare_mf(rdata1, rdata2); break; \ case 5: result = compare_cname(rdata1, rdata2); break; \ case 6: result = compare_soa(rdata1, rdata2); break; \ case 7: result = compare_mb(rdata1, rdata2); break; \ case 8: result = compare_mg(rdata1, rdata2); break; \ case 9: result = compare_mr(rdata1, rdata2); break; \ case 10: result = compare_null(rdata1, rdata2); break; \ case 11: switch (rdata1->rdclass) { \ case 1: result = compare_in_wks(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = compare_ptr(rdata1, rdata2); break; \ case 13: result = compare_hinfo(rdata1, rdata2); break; \ case 14: result = compare_minfo(rdata1, rdata2); break; \ case 15: result = compare_mx(rdata1, rdata2); break; \ case 16: result = compare_txt(rdata1, rdata2); break; \ case 17: result = compare_rp(rdata1, rdata2); break; \ case 18: result = compare_afsdb(rdata1, rdata2); break; \ case 19: result = compare_x25(rdata1, rdata2); break; \ case 20: result = compare_isdn(rdata1, rdata2); break; \ case 21: result = compare_rt(rdata1, rdata2); break; \ case 22: switch (rdata1->rdclass) { \ case 1: result = compare_in_nsap(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata1->rdclass) { \ case 1: result = compare_in_nsap_ptr(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = compare_sig(rdata1, rdata2); break; \ case 25: result = compare_key(rdata1, rdata2); break; \ case 26: switch (rdata1->rdclass) { \ case 1: result = compare_in_px(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = compare_gpos(rdata1, rdata2); break; \ case 28: switch (rdata1->rdclass) { \ case 1: result = compare_in_aaaa(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = compare_loc(rdata1, rdata2); break; \ case 30: result = compare_nxt(rdata1, rdata2); break; \ case 33: switch (rdata1->rdclass) { \ case 1: result = compare_in_srv(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata1->rdclass) { \ case 1: result = compare_in_naptr(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata1->rdclass) { \ case 1: result = compare_in_kx(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = compare_cert(rdata1, rdata2); break; \ case 38: switch (rdata1->rdclass) { \ case 1: result = compare_in_a6(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = compare_dname(rdata1, rdata2); break; \ case 41: result = compare_opt(rdata1, rdata2); break; \ case 42: switch (rdata1->rdclass) { \ case 1: result = compare_in_apl(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = compare_ds(rdata1, rdata2); break; \ case 44: result = compare_sshfp(rdata1, rdata2); break; \ case 45: result = compare_ipseckey(rdata1, rdata2); break; \ case 46: result = compare_rrsig(rdata1, rdata2); break; \ case 47: result = compare_nsec(rdata1, rdata2); break; \ case 48: result = compare_dnskey(rdata1, rdata2); break; \ case 99: result = compare_spf(rdata1, rdata2); break; \ case 103: result = compare_unspec(rdata1, rdata2); break; \ case 249: result = compare_tkey(rdata1, rdata2); break; \ case 250: switch (rdata1->rdclass) { \ case 255: result = compare_any_tsig(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = compare_dlv(rdata1, rdata2); break; \ default: use_default = ISC_TRUE; break; \ } #define FROMSTRUCTSWITCH \ switch (type) { \ case 1: switch (rdclass) { \ case 1: result = fromstruct_in_a(rdclass, type, source, target); break; \ case 3: result = fromstruct_ch_a(rdclass, type, source, target); break; \ case 4: result = fromstruct_hs_a(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = fromstruct_ns(rdclass, type, source, target); break; \ case 3: result = fromstruct_md(rdclass, type, source, target); break; \ case 4: result = fromstruct_mf(rdclass, type, source, target); break; \ case 5: result = fromstruct_cname(rdclass, type, source, target); break; \ case 6: result = fromstruct_soa(rdclass, type, source, target); break; \ case 7: result = fromstruct_mb(rdclass, type, source, target); break; \ case 8: result = fromstruct_mg(rdclass, type, source, target); break; \ case 9: result = fromstruct_mr(rdclass, type, source, target); break; \ case 10: result = fromstruct_null(rdclass, type, source, target); break; \ case 11: switch (rdclass) { \ case 1: result = fromstruct_in_wks(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = fromstruct_ptr(rdclass, type, source, target); break; \ case 13: result = fromstruct_hinfo(rdclass, type, source, target); break; \ case 14: result = fromstruct_minfo(rdclass, type, source, target); break; \ case 15: result = fromstruct_mx(rdclass, type, source, target); break; \ case 16: result = fromstruct_txt(rdclass, type, source, target); break; \ case 17: result = fromstruct_rp(rdclass, type, source, target); break; \ case 18: result = fromstruct_afsdb(rdclass, type, source, target); break; \ case 19: result = fromstruct_x25(rdclass, type, source, target); break; \ case 20: result = fromstruct_isdn(rdclass, type, source, target); break; \ case 21: result = fromstruct_rt(rdclass, type, source, target); break; \ case 22: switch (rdclass) { \ case 1: result = fromstruct_in_nsap(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdclass) { \ case 1: result = fromstruct_in_nsap_ptr(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = fromstruct_sig(rdclass, type, source, target); break; \ case 25: result = fromstruct_key(rdclass, type, source, target); break; \ case 26: switch (rdclass) { \ case 1: result = fromstruct_in_px(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = fromstruct_gpos(rdclass, type, source, target); break; \ case 28: switch (rdclass) { \ case 1: result = fromstruct_in_aaaa(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = fromstruct_loc(rdclass, type, source, target); break; \ case 30: result = fromstruct_nxt(rdclass, type, source, target); break; \ case 33: switch (rdclass) { \ case 1: result = fromstruct_in_srv(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdclass) { \ case 1: result = fromstruct_in_naptr(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdclass) { \ case 1: result = fromstruct_in_kx(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = fromstruct_cert(rdclass, type, source, target); break; \ case 38: switch (rdclass) { \ case 1: result = fromstruct_in_a6(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = fromstruct_dname(rdclass, type, source, target); break; \ case 41: result = fromstruct_opt(rdclass, type, source, target); break; \ case 42: switch (rdclass) { \ case 1: result = fromstruct_in_apl(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = fromstruct_ds(rdclass, type, source, target); break; \ case 44: result = fromstruct_sshfp(rdclass, type, source, target); break; \ case 45: result = fromstruct_ipseckey(rdclass, type, source, target); break; \ case 46: result = fromstruct_rrsig(rdclass, type, source, target); break; \ case 47: result = fromstruct_nsec(rdclass, type, source, target); break; \ case 48: result = fromstruct_dnskey(rdclass, type, source, target); break; \ case 99: result = fromstruct_spf(rdclass, type, source, target); break; \ case 103: result = fromstruct_unspec(rdclass, type, source, target); break; \ case 249: result = fromstruct_tkey(rdclass, type, source, target); break; \ case 250: switch (rdclass) { \ case 255: result = fromstruct_any_tsig(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = fromstruct_dlv(rdclass, type, source, target); break; \ default: use_default = ISC_TRUE; break; \ } #define TOSTRUCTSWITCH \ switch (rdata->type) { \ case 1: switch (rdata->rdclass) { \ case 1: result = tostruct_in_a(rdata, target, mctx); break; \ case 3: result = tostruct_ch_a(rdata, target, mctx); break; \ case 4: result = tostruct_hs_a(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = tostruct_ns(rdata, target, mctx); break; \ case 3: result = tostruct_md(rdata, target, mctx); break; \ case 4: result = tostruct_mf(rdata, target, mctx); break; \ case 5: result = tostruct_cname(rdata, target, mctx); break; \ case 6: result = tostruct_soa(rdata, target, mctx); break; \ case 7: result = tostruct_mb(rdata, target, mctx); break; \ case 8: result = tostruct_mg(rdata, target, mctx); break; \ case 9: result = tostruct_mr(rdata, target, mctx); break; \ case 10: result = tostruct_null(rdata, target, mctx); break; \ case 11: switch (rdata->rdclass) { \ case 1: result = tostruct_in_wks(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = tostruct_ptr(rdata, target, mctx); break; \ case 13: result = tostruct_hinfo(rdata, target, mctx); break; \ case 14: result = tostruct_minfo(rdata, target, mctx); break; \ case 15: result = tostruct_mx(rdata, target, mctx); break; \ case 16: result = tostruct_txt(rdata, target, mctx); break; \ case 17: result = tostruct_rp(rdata, target, mctx); break; \ case 18: result = tostruct_afsdb(rdata, target, mctx); break; \ case 19: result = tostruct_x25(rdata, target, mctx); break; \ case 20: result = tostruct_isdn(rdata, target, mctx); break; \ case 21: result = tostruct_rt(rdata, target, mctx); break; \ case 22: switch (rdata->rdclass) { \ case 1: result = tostruct_in_nsap(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata->rdclass) { \ case 1: result = tostruct_in_nsap_ptr(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = tostruct_sig(rdata, target, mctx); break; \ case 25: result = tostruct_key(rdata, target, mctx); break; \ case 26: switch (rdata->rdclass) { \ case 1: result = tostruct_in_px(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = tostruct_gpos(rdata, target, mctx); break; \ case 28: switch (rdata->rdclass) { \ case 1: result = tostruct_in_aaaa(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = tostruct_loc(rdata, target, mctx); break; \ case 30: result = tostruct_nxt(rdata, target, mctx); break; \ case 33: switch (rdata->rdclass) { \ case 1: result = tostruct_in_srv(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata->rdclass) { \ case 1: result = tostruct_in_naptr(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata->rdclass) { \ case 1: result = tostruct_in_kx(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = tostruct_cert(rdata, target, mctx); break; \ case 38: switch (rdata->rdclass) { \ case 1: result = tostruct_in_a6(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = tostruct_dname(rdata, target, mctx); break; \ case 41: result = tostruct_opt(rdata, target, mctx); break; \ case 42: switch (rdata->rdclass) { \ case 1: result = tostruct_in_apl(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = tostruct_ds(rdata, target, mctx); break; \ case 44: result = tostruct_sshfp(rdata, target, mctx); break; \ case 45: result = tostruct_ipseckey(rdata, target, mctx); break; \ case 46: result = tostruct_rrsig(rdata, target, mctx); break; \ case 47: result = tostruct_nsec(rdata, target, mctx); break; \ case 48: result = tostruct_dnskey(rdata, target, mctx); break; \ case 99: result = tostruct_spf(rdata, target, mctx); break; \ case 103: result = tostruct_unspec(rdata, target, mctx); break; \ case 249: result = tostruct_tkey(rdata, target, mctx); break; \ case 250: switch (rdata->rdclass) { \ case 255: result = tostruct_any_tsig(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = tostruct_dlv(rdata, target, mctx); break; \ default: use_default = ISC_TRUE; break; \ } #define FREESTRUCTSWITCH \ switch (common->rdtype) { \ case 1: switch (common->rdclass) { \ case 1: freestruct_in_a(source); break; \ case 3: freestruct_ch_a(source); break; \ case 4: freestruct_hs_a(source); break; \ default: break; \ } \ break; \ case 2: freestruct_ns(source); break; \ case 3: freestruct_md(source); break; \ case 4: freestruct_mf(source); break; \ case 5: freestruct_cname(source); break; \ case 6: freestruct_soa(source); break; \ case 7: freestruct_mb(source); break; \ case 8: freestruct_mg(source); break; \ case 9: freestruct_mr(source); break; \ case 10: freestruct_null(source); break; \ case 11: switch (common->rdclass) { \ case 1: freestruct_in_wks(source); break; \ default: break; \ } \ break; \ case 12: freestruct_ptr(source); break; \ case 13: freestruct_hinfo(source); break; \ case 14: freestruct_minfo(source); break; \ case 15: freestruct_mx(source); break; \ case 16: freestruct_txt(source); break; \ case 17: freestruct_rp(source); break; \ case 18: freestruct_afsdb(source); break; \ case 19: freestruct_x25(source); break; \ case 20: freestruct_isdn(source); break; \ case 21: freestruct_rt(source); break; \ case 22: switch (common->rdclass) { \ case 1: freestruct_in_nsap(source); break; \ default: break; \ } \ break; \ case 23: switch (common->rdclass) { \ case 1: freestruct_in_nsap_ptr(source); break; \ default: break; \ } \ break; \ case 24: freestruct_sig(source); break; \ case 25: freestruct_key(source); break; \ case 26: switch (common->rdclass) { \ case 1: freestruct_in_px(source); break; \ default: break; \ } \ break; \ case 27: freestruct_gpos(source); break; \ case 28: switch (common->rdclass) { \ case 1: freestruct_in_aaaa(source); break; \ default: break; \ } \ break; \ case 29: freestruct_loc(source); break; \ case 30: freestruct_nxt(source); break; \ case 33: switch (common->rdclass) { \ case 1: freestruct_in_srv(source); break; \ default: break; \ } \ break; \ case 35: switch (common->rdclass) { \ case 1: freestruct_in_naptr(source); break; \ default: break; \ } \ break; \ case 36: switch (common->rdclass) { \ case 1: freestruct_in_kx(source); break; \ default: break; \ } \ break; \ case 37: freestruct_cert(source); break; \ case 38: switch (common->rdclass) { \ case 1: freestruct_in_a6(source); break; \ default: break; \ } \ break; \ case 39: freestruct_dname(source); break; \ case 41: freestruct_opt(source); break; \ case 42: switch (common->rdclass) { \ case 1: freestruct_in_apl(source); break; \ default: break; \ } \ break; \ case 43: freestruct_ds(source); break; \ case 44: freestruct_sshfp(source); break; \ case 45: freestruct_ipseckey(source); break; \ case 46: freestruct_rrsig(source); break; \ case 47: freestruct_nsec(source); break; \ case 48: freestruct_dnskey(source); break; \ case 99: freestruct_spf(source); break; \ case 103: freestruct_unspec(source); break; \ case 249: freestruct_tkey(source); break; \ case 250: switch (common->rdclass) { \ case 255: freestruct_any_tsig(source); break; \ default: break; \ } \ break; \ case 32769: freestruct_dlv(source); break; \ default: break; \ } #define ADDITIONALDATASWITCH \ switch (rdata->type) { \ case 1: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_a(rdata, add, arg); break; \ case 3: result = additionaldata_ch_a(rdata, add, arg); break; \ case 4: result = additionaldata_hs_a(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = additionaldata_ns(rdata, add, arg); break; \ case 3: result = additionaldata_md(rdata, add, arg); break; \ case 4: result = additionaldata_mf(rdata, add, arg); break; \ case 5: result = additionaldata_cname(rdata, add, arg); break; \ case 6: result = additionaldata_soa(rdata, add, arg); break; \ case 7: result = additionaldata_mb(rdata, add, arg); break; \ case 8: result = additionaldata_mg(rdata, add, arg); break; \ case 9: result = additionaldata_mr(rdata, add, arg); break; \ case 10: result = additionaldata_null(rdata, add, arg); break; \ case 11: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_wks(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = additionaldata_ptr(rdata, add, arg); break; \ case 13: result = additionaldata_hinfo(rdata, add, arg); break; \ case 14: result = additionaldata_minfo(rdata, add, arg); break; \ case 15: result = additionaldata_mx(rdata, add, arg); break; \ case 16: result = additionaldata_txt(rdata, add, arg); break; \ case 17: result = additionaldata_rp(rdata, add, arg); break; \ case 18: result = additionaldata_afsdb(rdata, add, arg); break; \ case 19: result = additionaldata_x25(rdata, add, arg); break; \ case 20: result = additionaldata_isdn(rdata, add, arg); break; \ case 21: result = additionaldata_rt(rdata, add, arg); break; \ case 22: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_nsap(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_nsap_ptr(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = additionaldata_sig(rdata, add, arg); break; \ case 25: result = additionaldata_key(rdata, add, arg); break; \ case 26: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_px(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = additionaldata_gpos(rdata, add, arg); break; \ case 28: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_aaaa(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = additionaldata_loc(rdata, add, arg); break; \ case 30: result = additionaldata_nxt(rdata, add, arg); break; \ case 33: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_srv(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_naptr(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_kx(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = additionaldata_cert(rdata, add, arg); break; \ case 38: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_a6(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = additionaldata_dname(rdata, add, arg); break; \ case 41: result = additionaldata_opt(rdata, add, arg); break; \ case 42: switch (rdata->rdclass) { \ case 1: result = additionaldata_in_apl(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = additionaldata_ds(rdata, add, arg); break; \ case 44: result = additionaldata_sshfp(rdata, add, arg); break; \ case 45: result = additionaldata_ipseckey(rdata, add, arg); break; \ case 46: result = additionaldata_rrsig(rdata, add, arg); break; \ case 47: result = additionaldata_nsec(rdata, add, arg); break; \ case 48: result = additionaldata_dnskey(rdata, add, arg); break; \ case 99: result = additionaldata_spf(rdata, add, arg); break; \ case 103: result = additionaldata_unspec(rdata, add, arg); break; \ case 249: result = additionaldata_tkey(rdata, add, arg); break; \ case 250: switch (rdata->rdclass) { \ case 255: result = additionaldata_any_tsig(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = additionaldata_dlv(rdata, add, arg); break; \ default: use_default = ISC_TRUE; break; \ } #define DIGESTSWITCH \ switch (rdata->type) { \ case 1: switch (rdata->rdclass) { \ case 1: result = digest_in_a(rdata, digest, arg); break; \ case 3: result = digest_ch_a(rdata, digest, arg); break; \ case 4: result = digest_hs_a(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 2: result = digest_ns(rdata, digest, arg); break; \ case 3: result = digest_md(rdata, digest, arg); break; \ case 4: result = digest_mf(rdata, digest, arg); break; \ case 5: result = digest_cname(rdata, digest, arg); break; \ case 6: result = digest_soa(rdata, digest, arg); break; \ case 7: result = digest_mb(rdata, digest, arg); break; \ case 8: result = digest_mg(rdata, digest, arg); break; \ case 9: result = digest_mr(rdata, digest, arg); break; \ case 10: result = digest_null(rdata, digest, arg); break; \ case 11: switch (rdata->rdclass) { \ case 1: result = digest_in_wks(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 12: result = digest_ptr(rdata, digest, arg); break; \ case 13: result = digest_hinfo(rdata, digest, arg); break; \ case 14: result = digest_minfo(rdata, digest, arg); break; \ case 15: result = digest_mx(rdata, digest, arg); break; \ case 16: result = digest_txt(rdata, digest, arg); break; \ case 17: result = digest_rp(rdata, digest, arg); break; \ case 18: result = digest_afsdb(rdata, digest, arg); break; \ case 19: result = digest_x25(rdata, digest, arg); break; \ case 20: result = digest_isdn(rdata, digest, arg); break; \ case 21: result = digest_rt(rdata, digest, arg); break; \ case 22: switch (rdata->rdclass) { \ case 1: result = digest_in_nsap(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata->rdclass) { \ case 1: result = digest_in_nsap_ptr(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 24: result = digest_sig(rdata, digest, arg); break; \ case 25: result = digest_key(rdata, digest, arg); break; \ case 26: switch (rdata->rdclass) { \ case 1: result = digest_in_px(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 27: result = digest_gpos(rdata, digest, arg); break; \ case 28: switch (rdata->rdclass) { \ case 1: result = digest_in_aaaa(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 29: result = digest_loc(rdata, digest, arg); break; \ case 30: result = digest_nxt(rdata, digest, arg); break; \ case 33: switch (rdata->rdclass) { \ case 1: result = digest_in_srv(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata->rdclass) { \ case 1: result = digest_in_naptr(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata->rdclass) { \ case 1: result = digest_in_kx(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 37: result = digest_cert(rdata, digest, arg); break; \ case 38: switch (rdata->rdclass) { \ case 1: result = digest_in_a6(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 39: result = digest_dname(rdata, digest, arg); break; \ case 41: result = digest_opt(rdata, digest, arg); break; \ case 42: switch (rdata->rdclass) { \ case 1: result = digest_in_apl(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 43: result = digest_ds(rdata, digest, arg); break; \ case 44: result = digest_sshfp(rdata, digest, arg); break; \ case 45: result = digest_ipseckey(rdata, digest, arg); break; \ case 46: result = digest_rrsig(rdata, digest, arg); break; \ case 47: result = digest_nsec(rdata, digest, arg); break; \ case 48: result = digest_dnskey(rdata, digest, arg); break; \ case 99: result = digest_spf(rdata, digest, arg); break; \ case 103: result = digest_unspec(rdata, digest, arg); break; \ case 249: result = digest_tkey(rdata, digest, arg); break; \ case 250: switch (rdata->rdclass) { \ case 255: result = digest_any_tsig(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } \ break; \ case 32769: result = digest_dlv(rdata, digest, arg); break; \ default: use_default = ISC_TRUE; break; \ } #define CHECKOWNERSWITCH \ switch (type) { \ case 1: switch (rdclass) { \ case 1: result = checkowner_in_a(name, rdclass, type, wildcard); break; \ case 3: result = checkowner_ch_a(name, rdclass, type, wildcard); break; \ case 4: result = checkowner_hs_a(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 2: result = checkowner_ns(name, rdclass, type, wildcard); break; \ case 3: result = checkowner_md(name, rdclass, type, wildcard); break; \ case 4: result = checkowner_mf(name, rdclass, type, wildcard); break; \ case 5: result = checkowner_cname(name, rdclass, type, wildcard); break; \ case 6: result = checkowner_soa(name, rdclass, type, wildcard); break; \ case 7: result = checkowner_mb(name, rdclass, type, wildcard); break; \ case 8: result = checkowner_mg(name, rdclass, type, wildcard); break; \ case 9: result = checkowner_mr(name, rdclass, type, wildcard); break; \ case 10: result = checkowner_null(name, rdclass, type, wildcard); break; \ case 11: switch (rdclass) { \ case 1: result = checkowner_in_wks(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 12: result = checkowner_ptr(name, rdclass, type, wildcard); break; \ case 13: result = checkowner_hinfo(name, rdclass, type, wildcard); break; \ case 14: result = checkowner_minfo(name, rdclass, type, wildcard); break; \ case 15: result = checkowner_mx(name, rdclass, type, wildcard); break; \ case 16: result = checkowner_txt(name, rdclass, type, wildcard); break; \ case 17: result = checkowner_rp(name, rdclass, type, wildcard); break; \ case 18: result = checkowner_afsdb(name, rdclass, type, wildcard); break; \ case 19: result = checkowner_x25(name, rdclass, type, wildcard); break; \ case 20: result = checkowner_isdn(name, rdclass, type, wildcard); break; \ case 21: result = checkowner_rt(name, rdclass, type, wildcard); break; \ case 22: switch (rdclass) { \ case 1: result = checkowner_in_nsap(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdclass) { \ case 1: result = checkowner_in_nsap_ptr(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 24: result = checkowner_sig(name, rdclass, type, wildcard); break; \ case 25: result = checkowner_key(name, rdclass, type, wildcard); break; \ case 26: switch (rdclass) { \ case 1: result = checkowner_in_px(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 27: result = checkowner_gpos(name, rdclass, type, wildcard); break; \ case 28: switch (rdclass) { \ case 1: result = checkowner_in_aaaa(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 29: result = checkowner_loc(name, rdclass, type, wildcard); break; \ case 30: result = checkowner_nxt(name, rdclass, type, wildcard); break; \ case 33: switch (rdclass) { \ case 1: result = checkowner_in_srv(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdclass) { \ case 1: result = checkowner_in_naptr(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdclass) { \ case 1: result = checkowner_in_kx(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 37: result = checkowner_cert(name, rdclass, type, wildcard); break; \ case 38: switch (rdclass) { \ case 1: result = checkowner_in_a6(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 39: result = checkowner_dname(name, rdclass, type, wildcard); break; \ case 41: result = checkowner_opt(name, rdclass, type, wildcard); break; \ case 42: switch (rdclass) { \ case 1: result = checkowner_in_apl(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 43: result = checkowner_ds(name, rdclass, type, wildcard); break; \ case 44: result = checkowner_sshfp(name, rdclass, type, wildcard); break; \ case 45: result = checkowner_ipseckey(name, rdclass, type, wildcard); break; \ case 46: result = checkowner_rrsig(name, rdclass, type, wildcard); break; \ case 47: result = checkowner_nsec(name, rdclass, type, wildcard); break; \ case 48: result = checkowner_dnskey(name, rdclass, type, wildcard); break; \ case 99: result = checkowner_spf(name, rdclass, type, wildcard); break; \ case 103: result = checkowner_unspec(name, rdclass, type, wildcard); break; \ case 249: result = checkowner_tkey(name, rdclass, type, wildcard); break; \ case 250: switch (rdclass) { \ case 255: result = checkowner_any_tsig(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 32769: result = checkowner_dlv(name, rdclass, type, wildcard); break; \ default: result = ISC_TRUE; break; \ } #define CHECKNAMESSWITCH \ switch (rdata->type) { \ case 1: switch (rdata->rdclass) { \ case 1: result = checknames_in_a(rdata, owner, bad); break; \ case 3: result = checknames_ch_a(rdata, owner, bad); break; \ case 4: result = checknames_hs_a(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 2: result = checknames_ns(rdata, owner, bad); break; \ case 3: result = checknames_md(rdata, owner, bad); break; \ case 4: result = checknames_mf(rdata, owner, bad); break; \ case 5: result = checknames_cname(rdata, owner, bad); break; \ case 6: result = checknames_soa(rdata, owner, bad); break; \ case 7: result = checknames_mb(rdata, owner, bad); break; \ case 8: result = checknames_mg(rdata, owner, bad); break; \ case 9: result = checknames_mr(rdata, owner, bad); break; \ case 10: result = checknames_null(rdata, owner, bad); break; \ case 11: switch (rdata->rdclass) { \ case 1: result = checknames_in_wks(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 12: result = checknames_ptr(rdata, owner, bad); break; \ case 13: result = checknames_hinfo(rdata, owner, bad); break; \ case 14: result = checknames_minfo(rdata, owner, bad); break; \ case 15: result = checknames_mx(rdata, owner, bad); break; \ case 16: result = checknames_txt(rdata, owner, bad); break; \ case 17: result = checknames_rp(rdata, owner, bad); break; \ case 18: result = checknames_afsdb(rdata, owner, bad); break; \ case 19: result = checknames_x25(rdata, owner, bad); break; \ case 20: result = checknames_isdn(rdata, owner, bad); break; \ case 21: result = checknames_rt(rdata, owner, bad); break; \ case 22: switch (rdata->rdclass) { \ case 1: result = checknames_in_nsap(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 23: switch (rdata->rdclass) { \ case 1: result = checknames_in_nsap_ptr(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 24: result = checknames_sig(rdata, owner, bad); break; \ case 25: result = checknames_key(rdata, owner, bad); break; \ case 26: switch (rdata->rdclass) { \ case 1: result = checknames_in_px(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 27: result = checknames_gpos(rdata, owner, bad); break; \ case 28: switch (rdata->rdclass) { \ case 1: result = checknames_in_aaaa(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 29: result = checknames_loc(rdata, owner, bad); break; \ case 30: result = checknames_nxt(rdata, owner, bad); break; \ case 33: switch (rdata->rdclass) { \ case 1: result = checknames_in_srv(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 35: switch (rdata->rdclass) { \ case 1: result = checknames_in_naptr(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 36: switch (rdata->rdclass) { \ case 1: result = checknames_in_kx(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 37: result = checknames_cert(rdata, owner, bad); break; \ case 38: switch (rdata->rdclass) { \ case 1: result = checknames_in_a6(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 39: result = checknames_dname(rdata, owner, bad); break; \ case 41: result = checknames_opt(rdata, owner, bad); break; \ case 42: switch (rdata->rdclass) { \ case 1: result = checknames_in_apl(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 43: result = checknames_ds(rdata, owner, bad); break; \ case 44: result = checknames_sshfp(rdata, owner, bad); break; \ case 45: result = checknames_ipseckey(rdata, owner, bad); break; \ case 46: result = checknames_rrsig(rdata, owner, bad); break; \ case 47: result = checknames_nsec(rdata, owner, bad); break; \ case 48: result = checknames_dnskey(rdata, owner, bad); break; \ case 99: result = checknames_spf(rdata, owner, bad); break; \ case 103: result = checknames_unspec(rdata, owner, bad); break; \ case 249: result = checknames_tkey(rdata, owner, bad); break; \ case 250: switch (rdata->rdclass) { \ case 255: result = checknames_any_tsig(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } \ break; \ case 32769: result = checknames_dlv(rdata, owner, bad); break; \ default: result = ISC_TRUE; break; \ } #define RDATATYPE_COMPARE(_s, _d, _tn, _n, _tp) \ do { \ if (sizeof(_s) - 1 == _n && \ strncasecmp(_s,(_tn),(sizeof(_s) - 1)) == 0) { \ if ((dns_rdatatype_attributes(_d) & DNS_RDATATYPEATTR_RESERVED) != 0) \ return (ISC_R_NOTIMPLEMENTED); \ *(_tp) = _d; \ return (ISC_R_SUCCESS); \ } \ } while (0) #define RDATATYPE_FROMTEXT_SW(_hash,_typename,_length,_typep) \ switch (_hash) { \ case 16: \ RDATATYPE_COMPARE("reserved0", 0, _typename, _length, _typep); \ break; \ case 34: \ RDATATYPE_COMPARE("a", 1, _typename, _length, _typep); \ break; \ case 80: \ RDATATYPE_COMPARE("ns", 2, _typename, _length, _typep); \ break; \ case 92: \ RDATATYPE_COMPARE("md", 3, _typename, _length, _typep); \ break; \ case 58: \ RDATATYPE_COMPARE("mf", 4, _typename, _length, _typep); \ break; \ case 8: \ RDATATYPE_COMPARE("cname", 5, _typename, _length, _typep); \ RDATATYPE_COMPARE("mx", 15, _typename, _length, _typep); \ break; \ case 182: \ RDATATYPE_COMPARE("soa", 6, _typename, _length, _typep); \ break; \ case 126: \ RDATATYPE_COMPARE("mb", 7, _typename, _length, _typep); \ break; \ case 169: \ RDATATYPE_COMPARE("mg", 8, _typename, _length, _typep); \ break; \ case 110: \ RDATATYPE_COMPARE("mr", 9, _typename, _length, _typep); \ RDATATYPE_COMPARE("minfo", 14, _typename, _length, _typep); \ break; \ case 24: \ RDATATYPE_COMPARE("null", 10, _typename, _length, _typep); \ RDATATYPE_COMPARE("kx", 36, _typename, _length, _typep); \ break; \ case 206: \ RDATATYPE_COMPARE("wks", 11, _typename, _length, _typep); \ break; \ case 54: \ RDATATYPE_COMPARE("ptr", 12, _typename, _length, _typep); \ RDATATYPE_COMPARE("naptr", 35, _typename, _length, _typep); \ break; \ case 67: \ RDATATYPE_COMPARE("hinfo", 13, _typename, _length, _typep); \ break; \ case 236: \ RDATATYPE_COMPARE("txt", 16, _typename, _length, _typep); \ break; \ case 192: \ RDATATYPE_COMPARE("rp", 17, _typename, _length, _typep); \ break; \ case 12: \ RDATATYPE_COMPARE("afsdb", 18, _typename, _length, _typep); \ break; \ case 119: \ RDATATYPE_COMPARE("x25", 19, _typename, _length, _typep); \ break; \ case 214: \ RDATATYPE_COMPARE("isdn", 20, _typename, _length, _typep); \ break; \ case 144: \ RDATATYPE_COMPARE("rt", 21, _typename, _length, _typep); \ break; \ case 224: \ RDATATYPE_COMPARE("nsap", 22, _typename, _length, _typep); \ RDATATYPE_COMPARE("uid", 101, _typename, _length, _typep); \ break; \ case 140: \ RDATATYPE_COMPARE("nsap-ptr", 23, _typename, _length, _typep); \ break; \ case 122: \ RDATATYPE_COMPARE("sig", 24, _typename, _length, _typep); \ RDATATYPE_COMPARE("dlv", 32769, _typename, _length, _typep); \ break; \ case 254: \ RDATATYPE_COMPARE("key", 25, _typename, _length, _typep); \ break; \ case 112: \ RDATATYPE_COMPARE("px", 26, _typename, _length, _typep); \ break; \ case 17: \ RDATATYPE_COMPARE("gpos", 27, _typename, _length, _typep); \ break; \ case 69: \ RDATATYPE_COMPARE("aaaa", 28, _typename, _length, _typep); \ RDATATYPE_COMPARE("atma", 34, _typename, _length, _typep); \ break; \ case 237: \ RDATATYPE_COMPARE("loc", 29, _typename, _length, _typep); \ break; \ case 52: \ RDATATYPE_COMPARE("nxt", 30, _typename, _length, _typep); \ break; \ case 160: \ RDATATYPE_COMPARE("eid", 31, _typename, _length, _typep); \ break; \ case 220: \ RDATATYPE_COMPARE("nimloc", 32, _typename, _length, _typep); \ break; \ case 100: \ RDATATYPE_COMPARE("srv", 33, _typename, _length, _typep); \ break; \ case 172: \ RDATATYPE_COMPARE("cert", 37, _typename, _length, _typep); \ break; \ case 226: \ RDATATYPE_COMPARE("a6", 38, _typename, _length, _typep); \ break; \ case 109: \ RDATATYPE_COMPARE("dname", 39, _typename, _length, _typep); \ break; \ case 168: \ RDATATYPE_COMPARE("opt", 41, _typename, _length, _typep); \ break; \ case 48: \ RDATATYPE_COMPARE("apl", 42, _typename, _length, _typep); \ break; \ case 210: \ RDATATYPE_COMPARE("ds", 43, _typename, _length, _typep); \ break; \ case 128: \ RDATATYPE_COMPARE("sshfp", 44, _typename, _length, _typep); \ break; \ case 105: \ RDATATYPE_COMPARE("ipseckey", 45, _typename, _length, _typep); \ break; \ case 225: \ RDATATYPE_COMPARE("rrsig", 46, _typename, _length, _typep); \ break; \ case 22: \ RDATATYPE_COMPARE("nsec", 47, _typename, _length, _typep); \ break; \ case 26: \ RDATATYPE_COMPARE("dnskey", 48, _typename, _length, _typep); \ break; \ case 4: \ RDATATYPE_COMPARE("spf", 99, _typename, _length, _typep); \ break; \ case 230: \ RDATATYPE_COMPARE("uinfo", 100, _typename, _length, _typep); \ break; \ case 104: \ RDATATYPE_COMPARE("gid", 102, _typename, _length, _typep); \ break; \ case 145: \ RDATATYPE_COMPARE("unspec", 103, _typename, _length, _typep); \ break; \ case 184: \ RDATATYPE_COMPARE("tkey", 249, _typename, _length, _typep); \ break; \ case 72: \ RDATATYPE_COMPARE("tsig", 250, _typename, _length, _typep); \ break; \ case 138: \ RDATATYPE_COMPARE("ixfr", 251, _typename, _length, _typep); \ break; \ case 250: \ RDATATYPE_COMPARE("axfr", 252, _typename, _length, _typep); \ break; \ case 164: \ RDATATYPE_COMPARE("mailb", 253, _typename, _length, _typep); \ break; \ case 50: \ RDATATYPE_COMPARE("maila", 254, _typename, _length, _typep); \ break; \ case 68: \ RDATATYPE_COMPARE("any", 255, _typename, _length, _typep); \ break; \ } #define RDATATYPE_ATTRIBUTE_SW \ switch (type) { \ case 0: return (DNS_RDATATYPEATTR_RESERVED); \ case 1: return (RRTYPE_A_ATTRIBUTES); \ case 2: return (RRTYPE_NS_ATTRIBUTES); \ case 3: return (RRTYPE_MD_ATTRIBUTES); \ case 4: return (RRTYPE_MF_ATTRIBUTES); \ case 5: return (RRTYPE_CNAME_ATTRIBUTES); \ case 6: return (RRTYPE_SOA_ATTRIBUTES); \ case 7: return (RRTYPE_MB_ATTRIBUTES); \ case 8: return (RRTYPE_MG_ATTRIBUTES); \ case 9: return (RRTYPE_MR_ATTRIBUTES); \ case 10: return (RRTYPE_NULL_ATTRIBUTES); \ case 11: return (RRTYPE_WKS_ATTRIBUTES); \ case 12: return (RRTYPE_PTR_ATTRIBUTES); \ case 13: return (RRTYPE_HINFO_ATTRIBUTES); \ case 14: return (RRTYPE_MINFO_ATTRIBUTES); \ case 15: return (RRTYPE_MX_ATTRIBUTES); \ case 16: return (RRTYPE_TXT_ATTRIBUTES); \ case 17: return (RRTYPE_RP_ATTRIBUTES); \ case 18: return (RRTYPE_AFSDB_ATTRIBUTES); \ case 19: return (RRTYPE_X25_ATTRIBUTES); \ case 20: return (RRTYPE_ISDN_ATTRIBUTES); \ case 21: return (RRTYPE_RT_ATTRIBUTES); \ case 22: return (RRTYPE_NSAP_ATTRIBUTES); \ case 23: return (RRTYPE_NSAP_PTR_ATTRIBUTES); \ case 24: return (RRTYPE_SIG_ATTRIBUTES); \ case 25: return (RRTYPE_KEY_ATTRIBUTES); \ case 26: return (RRTYPE_PX_ATTRIBUTES); \ case 27: return (RRTYPE_GPOS_ATTRIBUTES); \ case 28: return (RRTYPE_AAAA_ATTRIBUTES); \ case 29: return (RRTYPE_LOC_ATTRIBUTES); \ case 30: return (RRTYPE_NXT_ATTRIBUTES); \ case 31: return (DNS_RDATATYPEATTR_RESERVED); \ case 32: return (DNS_RDATATYPEATTR_RESERVED); \ case 33: return (RRTYPE_SRV_ATTRIBUTES); \ case 34: return (DNS_RDATATYPEATTR_RESERVED); \ case 35: return (RRTYPE_NAPTR_ATTRIBUTES); \ case 36: return (RRTYPE_KX_ATTRIBUTES); \ case 37: return (RRTYPE_CERT_ATTRIBUTES); \ case 38: return (RRTYPE_A6_ATTRIBUTES); \ case 39: return (RRTYPE_DNAME_ATTRIBUTES); \ case 41: return (RRTYPE_OPT_ATTRIBUTES); \ case 42: return (RRTYPE_APL_ATTRIBUTES); \ case 43: return (RRTYPE_DS_ATTRIBUTES); \ case 44: return (RRTYPE_SSHFP_ATTRIBUTES); \ case 45: return (RRTYPE_IPSECKEY_ATTRIBUTES); \ case 46: return (RRTYPE_RRSIG_ATTRIBUTES); \ case 47: return (RRTYPE_NSEC_ATTRIBUTES); \ case 48: return (RRTYPE_DNSKEY_ATTRIBUTES); \ case 99: return (RRTYPE_SPF_ATTRIBUTES); \ case 100: return (DNS_RDATATYPEATTR_RESERVED); \ case 101: return (DNS_RDATATYPEATTR_RESERVED); \ case 102: return (DNS_RDATATYPEATTR_RESERVED); \ case 103: return (RRTYPE_UNSPEC_ATTRIBUTES); \ case 249: return (RRTYPE_TKEY_ATTRIBUTES); \ case 250: return (RRTYPE_TSIG_ATTRIBUTES); \ case 251: return (DNS_RDATATYPEATTR_META | DNS_RDATATYPEATTR_QUESTIONONLY); \ case 252: return (DNS_RDATATYPEATTR_META | DNS_RDATATYPEATTR_QUESTIONONLY); \ case 253: return (DNS_RDATATYPEATTR_META | DNS_RDATATYPEATTR_QUESTIONONLY); \ case 254: return (DNS_RDATATYPEATTR_META | DNS_RDATATYPEATTR_QUESTIONONLY); \ case 255: return (DNS_RDATATYPEATTR_META | DNS_RDATATYPEATTR_QUESTIONONLY); \ case 32769: return (RRTYPE_DLV_ATTRIBUTES); \ } #define RDATATYPE_TOTEXT_SW \ switch (type) { \ case 0: return (str_totext("RESERVED0", target)); \ case 1: return (str_totext("A", target)); \ case 2: return (str_totext("NS", target)); \ case 3: return (str_totext("MD", target)); \ case 4: return (str_totext("MF", target)); \ case 5: return (str_totext("CNAME", target)); \ case 6: return (str_totext("SOA", target)); \ case 7: return (str_totext("MB", target)); \ case 8: return (str_totext("MG", target)); \ case 9: return (str_totext("MR", target)); \ case 10: return (str_totext("NULL", target)); \ case 11: return (str_totext("WKS", target)); \ case 12: return (str_totext("PTR", target)); \ case 13: return (str_totext("HINFO", target)); \ case 14: return (str_totext("MINFO", target)); \ case 15: return (str_totext("MX", target)); \ case 16: return (str_totext("TXT", target)); \ case 17: return (str_totext("RP", target)); \ case 18: return (str_totext("AFSDB", target)); \ case 19: return (str_totext("X25", target)); \ case 20: return (str_totext("ISDN", target)); \ case 21: return (str_totext("RT", target)); \ case 22: return (str_totext("NSAP", target)); \ case 23: return (str_totext("NSAP-PTR", target)); \ case 24: return (str_totext("SIG", target)); \ case 25: return (str_totext("KEY", target)); \ case 26: return (str_totext("PX", target)); \ case 27: return (str_totext("GPOS", target)); \ case 28: return (str_totext("AAAA", target)); \ case 29: return (str_totext("LOC", target)); \ case 30: return (str_totext("NXT", target)); \ case 31: return (str_totext("EID", target)); \ case 32: return (str_totext("NIMLOC", target)); \ case 33: return (str_totext("SRV", target)); \ case 34: return (str_totext("ATMA", target)); \ case 35: return (str_totext("NAPTR", target)); \ case 36: return (str_totext("KX", target)); \ case 37: return (str_totext("CERT", target)); \ case 38: return (str_totext("A6", target)); \ case 39: return (str_totext("DNAME", target)); \ case 41: return (str_totext("OPT", target)); \ case 42: return (str_totext("APL", target)); \ case 43: return (str_totext("DS", target)); \ case 44: return (str_totext("SSHFP", target)); \ case 45: return (str_totext("IPSECKEY", target)); \ case 46: return (str_totext("RRSIG", target)); \ case 47: return (str_totext("NSEC", target)); \ case 48: return (str_totext("DNSKEY", target)); \ case 99: return (str_totext("SPF", target)); \ case 100: return (str_totext("UINFO", target)); \ case 101: return (str_totext("UID", target)); \ case 102: return (str_totext("GID", target)); \ case 103: return (str_totext("UNSPEC", target)); \ case 249: return (str_totext("TKEY", target)); \ case 250: return (str_totext("TSIG", target)); \ case 251: return (str_totext("IXFR", target)); \ case 252: return (str_totext("AXFR", target)); \ case 253: return (str_totext("MAILB", target)); \ case 254: return (str_totext("MAILA", target)); \ case 255: return (str_totext("ANY", target)); \ case 32769: return (str_totext("DLV", target)); \ } #endif /* DNS_CODE_H */ Index: projects/cambria/lib/bind/dns/dns/enumclass.h =================================================================== --- projects/cambria/lib/bind/dns/dns/enumclass.h (revision 186459) +++ projects/cambria/lib/bind/dns/dns/enumclass.h (revision 186460) @@ -1,50 +1,50 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /*************** *************** *************** THIS FILE IS AUTOMATICALLY GENERATED BY gen.c. *************** DO NOT EDIT! *************** ***************/ /*! \file */ #ifndef DNS_ENUMCLASS_H #define DNS_ENUMCLASS_H 1 enum { dns_rdataclass_reserved0 = 0, #define dns_rdataclass_reserved0 \ ((dns_rdataclass_t)dns_rdataclass_reserved0) dns_rdataclass_in = 1, #define dns_rdataclass_in ((dns_rdataclass_t)dns_rdataclass_in) dns_rdataclass_chaos = 3, #define dns_rdataclass_chaos ((dns_rdataclass_t)dns_rdataclass_chaos) dns_rdataclass_ch = 3, #define dns_rdataclass_ch ((dns_rdataclass_t)dns_rdataclass_ch) dns_rdataclass_hs = 4, #define dns_rdataclass_hs ((dns_rdataclass_t)dns_rdataclass_hs) dns_rdataclass_none = 254, #define dns_rdataclass_none ((dns_rdataclass_t)dns_rdataclass_none) dns_rdataclass_any = 255 #define dns_rdataclass_any ((dns_rdataclass_t)dns_rdataclass_any) }; #endif /* DNS_ENUMCLASS_H */ Index: projects/cambria/lib/bind/dns/dns/enumtype.h =================================================================== --- projects/cambria/lib/bind/dns/dns/enumtype.h (revision 186459) +++ projects/cambria/lib/bind/dns/dns/enumtype.h (revision 186460) @@ -1,146 +1,146 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /*************** *************** *************** THIS FILE IS AUTOMATICALLY GENERATED BY gen.c. *************** DO NOT EDIT! *************** ***************/ /*! \file */ #ifndef DNS_ENUMTYPE_H #define DNS_ENUMTYPE_H 1 enum { dns_rdatatype_none = 0, dns_rdatatype_a = 1, dns_rdatatype_ns = 2, dns_rdatatype_md = 3, dns_rdatatype_mf = 4, dns_rdatatype_cname = 5, dns_rdatatype_soa = 6, dns_rdatatype_mb = 7, dns_rdatatype_mg = 8, dns_rdatatype_mr = 9, dns_rdatatype_null = 10, dns_rdatatype_wks = 11, dns_rdatatype_ptr = 12, dns_rdatatype_hinfo = 13, dns_rdatatype_minfo = 14, dns_rdatatype_mx = 15, dns_rdatatype_txt = 16, dns_rdatatype_rp = 17, dns_rdatatype_afsdb = 18, dns_rdatatype_x25 = 19, dns_rdatatype_isdn = 20, dns_rdatatype_rt = 21, dns_rdatatype_nsap = 22, dns_rdatatype_nsap_ptr = 23, dns_rdatatype_sig = 24, dns_rdatatype_key = 25, dns_rdatatype_px = 26, dns_rdatatype_gpos = 27, dns_rdatatype_aaaa = 28, dns_rdatatype_loc = 29, dns_rdatatype_nxt = 30, dns_rdatatype_srv = 33, dns_rdatatype_naptr = 35, dns_rdatatype_kx = 36, dns_rdatatype_cert = 37, dns_rdatatype_a6 = 38, dns_rdatatype_dname = 39, dns_rdatatype_opt = 41, dns_rdatatype_apl = 42, dns_rdatatype_ds = 43, dns_rdatatype_sshfp = 44, dns_rdatatype_ipseckey = 45, dns_rdatatype_rrsig = 46, dns_rdatatype_nsec = 47, dns_rdatatype_dnskey = 48, dns_rdatatype_spf = 99, dns_rdatatype_unspec = 103, dns_rdatatype_tkey = 249, dns_rdatatype_tsig = 250, dns_rdatatype_dlv = 32769, dns_rdatatype_ixfr = 251, dns_rdatatype_axfr = 252, dns_rdatatype_mailb = 253, dns_rdatatype_maila = 254, dns_rdatatype_any = 255 }; #define dns_rdatatype_none ((dns_rdatatype_t)dns_rdatatype_none) #define dns_rdatatype_a ((dns_rdatatype_t)dns_rdatatype_a) #define dns_rdatatype_ns ((dns_rdatatype_t)dns_rdatatype_ns) #define dns_rdatatype_md ((dns_rdatatype_t)dns_rdatatype_md) #define dns_rdatatype_mf ((dns_rdatatype_t)dns_rdatatype_mf) #define dns_rdatatype_cname ((dns_rdatatype_t)dns_rdatatype_cname) #define dns_rdatatype_soa ((dns_rdatatype_t)dns_rdatatype_soa) #define dns_rdatatype_mb ((dns_rdatatype_t)dns_rdatatype_mb) #define dns_rdatatype_mg ((dns_rdatatype_t)dns_rdatatype_mg) #define dns_rdatatype_mr ((dns_rdatatype_t)dns_rdatatype_mr) #define dns_rdatatype_null ((dns_rdatatype_t)dns_rdatatype_null) #define dns_rdatatype_wks ((dns_rdatatype_t)dns_rdatatype_wks) #define dns_rdatatype_ptr ((dns_rdatatype_t)dns_rdatatype_ptr) #define dns_rdatatype_hinfo ((dns_rdatatype_t)dns_rdatatype_hinfo) #define dns_rdatatype_minfo ((dns_rdatatype_t)dns_rdatatype_minfo) #define dns_rdatatype_mx ((dns_rdatatype_t)dns_rdatatype_mx) #define dns_rdatatype_txt ((dns_rdatatype_t)dns_rdatatype_txt) #define dns_rdatatype_rp ((dns_rdatatype_t)dns_rdatatype_rp) #define dns_rdatatype_afsdb ((dns_rdatatype_t)dns_rdatatype_afsdb) #define dns_rdatatype_x25 ((dns_rdatatype_t)dns_rdatatype_x25) #define dns_rdatatype_isdn ((dns_rdatatype_t)dns_rdatatype_isdn) #define dns_rdatatype_rt ((dns_rdatatype_t)dns_rdatatype_rt) #define dns_rdatatype_nsap ((dns_rdatatype_t)dns_rdatatype_nsap) #define dns_rdatatype_nsap_ptr ((dns_rdatatype_t)dns_rdatatype_nsap_ptr) #define dns_rdatatype_sig ((dns_rdatatype_t)dns_rdatatype_sig) #define dns_rdatatype_key ((dns_rdatatype_t)dns_rdatatype_key) #define dns_rdatatype_px ((dns_rdatatype_t)dns_rdatatype_px) #define dns_rdatatype_gpos ((dns_rdatatype_t)dns_rdatatype_gpos) #define dns_rdatatype_aaaa ((dns_rdatatype_t)dns_rdatatype_aaaa) #define dns_rdatatype_loc ((dns_rdatatype_t)dns_rdatatype_loc) #define dns_rdatatype_nxt ((dns_rdatatype_t)dns_rdatatype_nxt) #define dns_rdatatype_srv ((dns_rdatatype_t)dns_rdatatype_srv) #define dns_rdatatype_naptr ((dns_rdatatype_t)dns_rdatatype_naptr) #define dns_rdatatype_kx ((dns_rdatatype_t)dns_rdatatype_kx) #define dns_rdatatype_cert ((dns_rdatatype_t)dns_rdatatype_cert) #define dns_rdatatype_a6 ((dns_rdatatype_t)dns_rdatatype_a6) #define dns_rdatatype_dname ((dns_rdatatype_t)dns_rdatatype_dname) #define dns_rdatatype_opt ((dns_rdatatype_t)dns_rdatatype_opt) #define dns_rdatatype_apl ((dns_rdatatype_t)dns_rdatatype_apl) #define dns_rdatatype_ds ((dns_rdatatype_t)dns_rdatatype_ds) #define dns_rdatatype_sshfp ((dns_rdatatype_t)dns_rdatatype_sshfp) #define dns_rdatatype_ipseckey ((dns_rdatatype_t)dns_rdatatype_ipseckey) #define dns_rdatatype_rrsig ((dns_rdatatype_t)dns_rdatatype_rrsig) #define dns_rdatatype_nsec ((dns_rdatatype_t)dns_rdatatype_nsec) #define dns_rdatatype_dnskey ((dns_rdatatype_t)dns_rdatatype_dnskey) #define dns_rdatatype_spf ((dns_rdatatype_t)dns_rdatatype_spf) #define dns_rdatatype_unspec ((dns_rdatatype_t)dns_rdatatype_unspec) #define dns_rdatatype_tkey ((dns_rdatatype_t)dns_rdatatype_tkey) #define dns_rdatatype_tsig ((dns_rdatatype_t)dns_rdatatype_tsig) #define dns_rdatatype_dlv ((dns_rdatatype_t)dns_rdatatype_dlv) #define dns_rdatatype_ixfr ((dns_rdatatype_t)dns_rdatatype_ixfr) #define dns_rdatatype_axfr ((dns_rdatatype_t)dns_rdatatype_axfr) #define dns_rdatatype_mailb ((dns_rdatatype_t)dns_rdatatype_mailb) #define dns_rdatatype_maila ((dns_rdatatype_t)dns_rdatatype_maila) #define dns_rdatatype_any ((dns_rdatatype_t)dns_rdatatype_any) #endif /* DNS_ENUMTYPE_H */ Index: projects/cambria/lib/bind/dns/dns/rdatastruct.h =================================================================== --- projects/cambria/lib/bind/dns/dns/rdatastruct.h (revision 186459) +++ projects/cambria/lib/bind/dns/dns/rdatastruct.h (revision 186460) @@ -1,1896 +1,1896 @@ /* $FreeBSD$ */ /* - * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /*************** *************** *************** THIS FILE IS AUTOMATICALLY GENERATED BY gen.c. *************** DO NOT EDIT! *************** ***************/ /*! \file */ /* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: rdatastructpre.h,v 1.14 2004/03/05 05:10:04 marka Exp $ */ #ifndef DNS_RDATASTRUCT_H #define DNS_RDATASTRUCT_H 1 #include #include #include #include ISC_LANG_BEGINDECLS typedef struct dns_rdatacommon { dns_rdataclass_t rdclass; dns_rdatatype_t rdtype; ISC_LINK(struct dns_rdatacommon) link; } dns_rdatacommon_t; #define DNS_RDATACOMMON_INIT(_data, _rdtype, _rdclass) \ do { \ (_data)->common.rdtype = (_rdtype); \ (_data)->common.rdclass = (_rdclass); \ ISC_LINK_INIT(&(_data)->common, link); \ } while (0) /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef IN_1_A_1_H #define IN_1_A_1_H 1 /* $Id: a_1.h,v 1.24.18.2 2005/04/29 00:16:41 marka Exp $ */ typedef struct dns_rdata_in_a { dns_rdatacommon_t common; struct in_addr in_addr; } dns_rdata_in_a_t; #endif /* IN_1_A_1_H */ /* * Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: a_1.h,v 1.2.2.2 2005/06/05 00:02:22 marka Exp $ */ /* by Bjorn.Victor@it.uu.se, 2005-05-07 */ /* Based on generic/mx_15.h */ #ifndef CH_3_A_1_H #define CH_3_A_1_H 1 typedef isc_uint16_t ch_addr_t; typedef struct dns_rdata_ch_a { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t ch_addr_dom; /* ch-addr domain for back mapping */ ch_addr_t ch_addr; /* chaos address (16 bit) network order */ } dns_rdata_ch_a_t; #endif /* CH_3_A_1_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef HS_4_A_1_H #define HS_4_A_1_H 1 /* $Id: a_1.h,v 1.8.18.2 2005/04/29 00:16:41 marka Exp $ */ typedef struct dns_rdata_hs_a { dns_rdatacommon_t common; struct in_addr in_addr; } dns_rdata_hs_a_t; #endif /* HS_4_A_1_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_NS_2_H #define GENERIC_NS_2_H 1 /* $Id: ns_2.h,v 1.23.18.2 2005/04/29 00:16:37 marka Exp $ */ typedef struct dns_rdata_ns { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t name; } dns_rdata_ns_t; #endif /* GENERIC_NS_2_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MD_3_H #define GENERIC_MD_3_H 1 /* $Id: md_3.h,v 1.24.18.2 2005/04/29 00:16:35 marka Exp $ */ typedef struct dns_rdata_md { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t md; } dns_rdata_md_t; #endif /* GENERIC_MD_3_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MF_4_H #define GENERIC_MF_4_H 1 /* $Id: mf_4.h,v 1.22.18.2 2005/04/29 00:16:35 marka Exp $ */ typedef struct dns_rdata_mf { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t mf; } dns_rdata_mf_t; #endif /* GENERIC_MF_4_H */ /* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: cname_5.h,v 1.24 2004/03/05 05:10:10 marka Exp $ */ #ifndef GENERIC_CNAME_5_H #define GENERIC_CNAME_5_H 1 typedef struct dns_rdata_cname { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t cname; } dns_rdata_cname_t; #endif /* GENERIC_CNAME_5_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_SOA_6_H #define GENERIC_SOA_6_H 1 /* $Id: soa_6.h,v 1.28.18.2 2005/04/29 00:16:40 marka Exp $ */ typedef struct dns_rdata_soa { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t origin; dns_name_t contact; isc_uint32_t serial; /*%< host order */ isc_uint32_t refresh; /*%< host order */ isc_uint32_t retry; /*%< host order */ isc_uint32_t expire; /*%< host order */ isc_uint32_t minimum; /*%< host order */ } dns_rdata_soa_t; #endif /* GENERIC_SOA_6_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MB_7_H #define GENERIC_MB_7_H 1 /* $Id: mb_7.h,v 1.23.18.2 2005/04/29 00:16:34 marka Exp $ */ typedef struct dns_rdata_mb { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t mb; } dns_rdata_mb_t; #endif /* GENERIC_MB_7_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MG_8_H #define GENERIC_MG_8_H 1 /* $Id: mg_8.h,v 1.22.18.2 2005/04/29 00:16:35 marka Exp $ */ typedef struct dns_rdata_mg { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t mg; } dns_rdata_mg_t; #endif /* GENERIC_MG_8_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MR_9_H #define GENERIC_MR_9_H 1 /* $Id: mr_9.h,v 1.22.18.2 2005/04/29 00:16:36 marka Exp $ */ typedef struct dns_rdata_mr { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t mr; } dns_rdata_mr_t; #endif /* GENERIC_MR_9_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_NULL_10_H #define GENERIC_NULL_10_H 1 /* $Id: null_10.h,v 1.21.18.2 2005/04/29 00:16:37 marka Exp $ */ typedef struct dns_rdata_null { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t length; unsigned char *data; } dns_rdata_null_t; #endif /* GENERIC_NULL_10_H */ /* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_WKS_11_H #define IN_1_WKS_11_H 1 /* $Id: wks_11.h,v 1.20 2004/03/05 05:10:25 marka Exp $ */ typedef struct dns_rdata_in_wks { dns_rdatacommon_t common; isc_mem_t *mctx; struct in_addr in_addr; isc_uint16_t protocol; unsigned char *map; isc_uint16_t map_len; } dns_rdata_in_wks_t; #endif /* IN_1_WKS_11_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_PTR_12_H #define GENERIC_PTR_12_H 1 /* $Id: ptr_12.h,v 1.23.18.2 2005/04/29 00:16:39 marka Exp $ */ typedef struct dns_rdata_ptr { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t ptr; } dns_rdata_ptr_t; #endif /* GENERIC_PTR_12_H */ /* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_HINFO_13_H #define GENERIC_HINFO_13_H 1 /* $Id: hinfo_13.h,v 1.23 2004/03/05 05:10:12 marka Exp $ */ typedef struct dns_rdata_hinfo { dns_rdatacommon_t common; isc_mem_t *mctx; char *cpu; char *os; isc_uint8_t cpu_len; isc_uint8_t os_len; } dns_rdata_hinfo_t; #endif /* GENERIC_HINFO_13_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MINFO_14_H #define GENERIC_MINFO_14_H 1 /* $Id: minfo_14.h,v 1.23.18.2 2005/04/29 00:16:35 marka Exp $ */ typedef struct dns_rdata_minfo { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t rmailbox; dns_name_t emailbox; } dns_rdata_minfo_t; #endif /* GENERIC_MINFO_14_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_MX_15_H #define GENERIC_MX_15_H 1 /* $Id: mx_15.h,v 1.25.18.2 2005/04/29 00:16:36 marka Exp $ */ typedef struct dns_rdata_mx { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t pref; dns_name_t mx; } dns_rdata_mx_t; #endif /* GENERIC_MX_15_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_TXT_16_H #define GENERIC_TXT_16_H 1 /* $Id: txt_16.h,v 1.24.18.2 2005/04/29 00:16:40 marka Exp $ */ typedef struct dns_rdata_txt_string { isc_uint8_t length; unsigned char *data; } dns_rdata_txt_string_t; typedef struct dns_rdata_txt { dns_rdatacommon_t common; isc_mem_t *mctx; unsigned char *txt; isc_uint16_t txt_len; /* private */ isc_uint16_t offset; } dns_rdata_txt_t; /* * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done * via rdatastructpre.h and rdatastructsuf.h. */ isc_result_t dns_rdata_txt_first(dns_rdata_txt_t *); isc_result_t dns_rdata_txt_next(dns_rdata_txt_t *); isc_result_t dns_rdata_txt_current(dns_rdata_txt_t *, dns_rdata_txt_string_t *); #endif /* GENERIC_TXT_16_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_RP_17_H #define GENERIC_RP_17_H 1 /* $Id: rp_17.h,v 1.17.18.2 2005/04/29 00:16:39 marka Exp $ */ /*! * \brief Per RFC1183 */ typedef struct dns_rdata_rp { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t mail; dns_name_t text; } dns_rdata_rp_t; #endif /* GENERIC_RP_17_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_AFSDB_18_H #define GENERIC_AFSDB_18_H 1 /* $Id: afsdb_18.h,v 1.16.18.2 2005/04/29 00:16:30 marka Exp $ */ /*! * \brief Per RFC1183 */ typedef struct dns_rdata_afsdb { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t subtype; dns_name_t server; } dns_rdata_afsdb_t; #endif /* GENERIC_AFSDB_18_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_X25_19_H #define GENERIC_X25_19_H 1 /* $Id: x25_19.h,v 1.14.18.2 2005/04/29 00:16:40 marka Exp $ */ /*! * \brief Per RFC1183 */ typedef struct dns_rdata_x25 { dns_rdatacommon_t common; isc_mem_t *mctx; unsigned char *x25; isc_uint8_t x25_len; } dns_rdata_x25_t; #endif /* GENERIC_X25_19_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_ISDN_20_H #define GENERIC_ISDN_20_H 1 /* $Id: isdn_20.h,v 1.14.18.2 2005/04/29 00:16:33 marka Exp $ */ /*! * \brief Per RFC1183 */ typedef struct dns_rdata_isdn { dns_rdatacommon_t common; isc_mem_t *mctx; char *isdn; char *subaddress; isc_uint8_t isdn_len; isc_uint8_t subaddress_len; } dns_rdata_isdn_t; #endif /* GENERIC_ISDN_20_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_RT_21_H #define GENERIC_RT_21_H 1 /* $Id: rt_21.h,v 1.17.18.2 2005/04/29 00:16:40 marka Exp $ */ /*! * \brief Per RFC1183 */ typedef struct dns_rdata_rt { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t preference; dns_name_t host; } dns_rdata_rt_t; #endif /* GENERIC_RT_21_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_NSAP_22_H #define IN_1_NSAP_22_H 1 /* $Id: nsap_22.h,v 1.14.18.2 2005/04/29 00:16:43 marka Exp $ */ /*! * \brief Per RFC1706 */ typedef struct dns_rdata_in_nsap { dns_rdatacommon_t common; isc_mem_t *mctx; unsigned char *nsap; isc_uint16_t nsap_len; } dns_rdata_in_nsap_t; #endif /* IN_1_NSAP_22_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_NSAP_PTR_23_H #define IN_1_NSAP_PTR_23_H 1 /* $Id: nsap-ptr_23.h,v 1.15.18.2 2005/04/29 00:16:43 marka Exp $ */ /*! * \brief Per RFC1348. Obsoleted in RFC 1706 - use PTR instead. */ typedef struct dns_rdata_in_nsap_ptr { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t owner; } dns_rdata_in_nsap_ptr_t; #endif /* IN_1_NSAP_PTR_23_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_SIG_24_H #define GENERIC_SIG_24_H 1 /* $Id: sig_24.h,v 1.22.18.2 2005/04/29 00:16:40 marka Exp $ */ /*! * \brief Per RFC2535 */ typedef struct dns_rdata_sig_t { dns_rdatacommon_t common; isc_mem_t * mctx; dns_rdatatype_t covered; dns_secalg_t algorithm; isc_uint8_t labels; isc_uint32_t originalttl; isc_uint32_t timeexpire; isc_uint32_t timesigned; isc_uint16_t keyid; dns_name_t signer; isc_uint16_t siglen; unsigned char * signature; } dns_rdata_sig_t; #endif /* GENERIC_SIG_24_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_KEY_25_H #define GENERIC_KEY_25_H 1 /* $Id: key_25.h,v 1.15.18.2 2005/04/29 00:16:33 marka Exp $ */ /*! * \brief Per RFC2535 */ typedef struct dns_rdata_key_t { dns_rdatacommon_t common; isc_mem_t * mctx; isc_uint16_t flags; isc_uint8_t protocol; isc_uint8_t algorithm; isc_uint16_t datalen; unsigned char * data; } dns_rdata_key_t; #endif /* GENERIC_KEY_25_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_PX_26_H #define IN_1_PX_26_H 1 /* $Id: px_26.h,v 1.15.18.2 2005/04/29 00:16:43 marka Exp $ */ /*! * \brief Per RFC2163 */ typedef struct dns_rdata_in_px { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t preference; dns_name_t map822; dns_name_t mapx400; } dns_rdata_in_px_t; #endif /* IN_1_PX_26_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_GPOS_27_H #define GENERIC_GPOS_27_H 1 /* $Id: gpos_27.h,v 1.13.18.2 2005/04/29 00:16:32 marka Exp $ */ /*! * \brief per RFC1712 */ typedef struct dns_rdata_gpos { dns_rdatacommon_t common; isc_mem_t *mctx; char *longitude; char *latitude; char *altitude; isc_uint8_t long_len; isc_uint8_t lat_len; isc_uint8_t alt_len; } dns_rdata_gpos_t; #endif /* GENERIC_GPOS_27_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_AAAA_28_H #define IN_1_AAAA_28_H 1 /* $Id: aaaa_28.h,v 1.17.18.2 2005/04/29 00:16:42 marka Exp $ */ /*! * \brief Per RFC1886 */ typedef struct dns_rdata_in_aaaa { dns_rdatacommon_t common; struct in6_addr in6_addr; } dns_rdata_in_aaaa_t; #endif /* IN_1_AAAA_28_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_LOC_29_H #define GENERIC_LOC_29_H 1 /* $Id: loc_29.h,v 1.15.18.2 2005/04/29 00:16:34 marka Exp $ */ /*! * \brief Per RFC1876 */ typedef struct dns_rdata_loc_0 { isc_uint8_t version; /* must be first and zero */ isc_uint8_t size; isc_uint8_t horizontal; isc_uint8_t vertical; isc_uint32_t latitude; isc_uint32_t longitude; isc_uint32_t altitude; } dns_rdata_loc_0_t; typedef struct dns_rdata_loc { dns_rdatacommon_t common; union { dns_rdata_loc_0_t v0; } v; } dns_rdata_loc_t; #endif /* GENERIC_LOC_29_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2002 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_NXT_30_H #define GENERIC_NXT_30_H 1 /* $Id: nxt_30.h,v 1.21.18.2 2005/04/29 00:16:38 marka Exp $ */ /*! * \brief RFC2535 */ typedef struct dns_rdata_nxt { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t next; unsigned char *typebits; isc_uint16_t len; } dns_rdata_nxt_t; #endif /* GENERIC_NXT_30_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_SRV_33_H #define IN_1_SRV_33_H 1 /* $Id: srv_33.h,v 1.15.18.2 2005/04/29 00:16:43 marka Exp $ */ /* Reviewed: Fri Mar 17 13:01:00 PST 2000 by bwelling */ /*! * \brief Per RFC2782 */ typedef struct dns_rdata_in_srv { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t priority; isc_uint16_t weight; isc_uint16_t port; dns_name_t target; } dns_rdata_in_srv_t; #endif /* IN_1_SRV_33_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_NAPTR_35_H #define IN_1_NAPTR_35_H 1 /* $Id: naptr_35.h,v 1.19.18.2 2005/04/29 00:16:42 marka Exp $ */ /*! * \brief Per RFC2915 */ typedef struct dns_rdata_in_naptr { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t order; isc_uint16_t preference; char *flags; isc_uint8_t flags_len; char *service; isc_uint8_t service_len; char *regexp; isc_uint8_t regexp_len; dns_name_t replacement; } dns_rdata_in_naptr_t; #endif /* IN_1_NAPTR_35_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_KX_36_H #define IN_1_KX_36_H 1 /* $Id: kx_36.h,v 1.16.18.2 2005/04/29 00:16:42 marka Exp $ */ /*! * \brief Per RFC2230 */ typedef struct dns_rdata_in_kx { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t preference; dns_name_t exchange; } dns_rdata_in_kx_t; #endif /* IN_1_KX_36_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: cert_37.h,v 1.16.18.2 2005/04/29 00:16:31 marka Exp $ */ #ifndef GENERIC_CERT_37_H #define GENERIC_CERT_37_H 1 /*% RFC2538 */ typedef struct dns_rdata_cert { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t type; isc_uint16_t key_tag; isc_uint8_t algorithm; isc_uint16_t length; unsigned char *certificate; } dns_rdata_cert_t; #endif /* GENERIC_CERT_37_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef IN_1_A6_38_H #define IN_1_A6_38_H 1 /* $Id: a6_38.h,v 1.20.18.2 2005/04/29 00:16:41 marka Exp $ */ /*! * \brief Per RFC2874 */ typedef struct dns_rdata_in_a6 { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t prefix; isc_uint8_t prefixlen; struct in6_addr in6_addr; } dns_rdata_in_a6_t; #endif /* IN_1_A6_38_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_DNAME_39_H #define GENERIC_DNAME_39_H 1 /* $Id: dname_39.h,v 1.17.18.2 2005/04/29 00:16:31 marka Exp $ */ /*! * \brief per RFC2672 */ typedef struct dns_rdata_dname { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t dname; } dns_rdata_dname_t; #endif /* GENERIC_DNAME_39_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_OPT_41_H #define GENERIC_OPT_41_H 1 /* $Id: opt_41.h,v 1.14.18.2 2005/04/29 00:16:38 marka Exp $ */ /*! * \brief Per RFC2671 */ typedef struct dns_rdata_opt_opcode { isc_uint16_t opcode; isc_uint16_t length; unsigned char *data; } dns_rdata_opt_opcode_t; typedef struct dns_rdata_opt { dns_rdatacommon_t common; isc_mem_t *mctx; unsigned char *options; isc_uint16_t length; /* private */ isc_uint16_t offset; } dns_rdata_opt_t; /* * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done * via rdatastructpre.h and rdatastructsuf.h. */ isc_result_t dns_rdata_opt_first(dns_rdata_opt_t *); isc_result_t dns_rdata_opt_next(dns_rdata_opt_t *); isc_result_t dns_rdata_opt_current(dns_rdata_opt_t *, dns_rdata_opt_opcode_t *); #endif /* GENERIC_OPT_41_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2002 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef IN_1_APL_42_H #define IN_1_APL_42_H 1 /* $Id: apl_42.h,v 1.2.18.2 2005/04/29 00:16:42 marka Exp $ */ typedef struct dns_rdata_apl_ent { isc_boolean_t negative; isc_uint16_t family; isc_uint8_t prefix; isc_uint8_t length; unsigned char *data; } dns_rdata_apl_ent_t; typedef struct dns_rdata_in_apl { dns_rdatacommon_t common; isc_mem_t *mctx; /* type & class specific elements */ unsigned char *apl; isc_uint16_t apl_len; /* private */ isc_uint16_t offset; } dns_rdata_in_apl_t; /* * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done * via rdatastructpre.h and rdatastructsuf.h. */ isc_result_t dns_rdata_apl_first(dns_rdata_in_apl_t *); isc_result_t dns_rdata_apl_next(dns_rdata_in_apl_t *); isc_result_t dns_rdata_apl_current(dns_rdata_in_apl_t *, dns_rdata_apl_ent_t *); #endif /* IN_1_APL_42_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2002 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: ds_43.h,v 1.3.20.2 2005/04/29 00:16:32 marka Exp $ */ #ifndef GENERIC_DS_43_H #define GENERIC_DS_43_H 1 /*! * \brief per draft-ietf-dnsext-delegation-signer-05.txt */ typedef struct dns_rdata_ds { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t key_tag; isc_uint8_t algorithm; isc_uint8_t digest_type; isc_uint16_t length; unsigned char *digest; } dns_rdata_ds_t; #endif /* GENERIC_DS_43_H */ /* * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: sshfp_44.h,v 1.2.18.3 2006/03/10 04:04:32 marka Exp $ */ /*! * \brief Per RFC 4255 */ #ifndef GENERIC_SSHFP_44_H #define GENERIC_SSHFP_44_H 1 typedef struct dns_rdata_sshfp { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint8_t algorithm; isc_uint8_t digest_type; isc_uint16_t length; unsigned char *digest; } dns_rdata_sshfp_t; #endif /* GENERIC_SSHFP_44_H */ /* * Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: ipseckey_45.h,v 1.2.2.1 2005/07/07 03:17:36 marka Exp $ */ #ifndef GENERIC_IPSECKEY_45_H #define GENERIC_IPSECKEY_45_H 1 typedef struct dns_rdata_ipseckey { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint8_t precedence; isc_uint8_t gateway_type; isc_uint8_t algorithm; struct in_addr in_addr; /* gateway type 1 */ struct in6_addr in6_addr; /* gateway type 2 */ dns_name_t gateway; /* gateway type 3 */ unsigned char *key; isc_uint16_t keylength; } dns_rdata_ipseckey_t; #endif /* GENERIC_IPSECKEY_45_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_DNSSIG_46_H #define GENERIC_DNSSIG_46_H 1 /* $Id: rrsig_46.h,v 1.3.20.2 2005/04/29 00:16:39 marka Exp $ */ /*! * \brief Per RFC2535 */ typedef struct dns_rdata_rrsig { dns_rdatacommon_t common; isc_mem_t * mctx; dns_rdatatype_t covered; dns_secalg_t algorithm; isc_uint8_t labels; isc_uint32_t originalttl; isc_uint32_t timeexpire; isc_uint32_t timesigned; isc_uint16_t keyid; dns_name_t signer; isc_uint16_t siglen; unsigned char * signature; } dns_rdata_rrsig_t; #endif /* GENERIC_DNSSIG_46_H */ /* - * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2003 Internet Software Consortium. * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_NSEC_47_H #define GENERIC_NSEC_47_H 1 -/* $Id: nsec_47.h,v 1.4.20.2 2005/04/29 00:16:37 marka Exp $ */ +/* $Id: nsec_47.h,v 1.4.20.4 2008/07/15 23:46:14 tbox Exp $ */ /*! - * \brief Per draft-ietf-dnsext-nsec-rdata-01.txt */ + * \brief Per RFC 3845 */ typedef struct dns_rdata_nsec { dns_rdatacommon_t common; isc_mem_t *mctx; dns_name_t next; unsigned char *typebits; isc_uint16_t len; } dns_rdata_nsec_t; #endif /* GENERIC_NSEC_47_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_DNSKEY_48_H #define GENERIC_DNSKEY_48_H 1 /* $Id: dnskey_48.h,v 1.3.20.2 2005/04/29 00:16:32 marka Exp $ */ /*! * \brief per RFC2535 */ typedef struct dns_rdata_dnskey { dns_rdatacommon_t common; isc_mem_t * mctx; isc_uint16_t flags; isc_uint8_t protocol; isc_uint8_t algorithm; isc_uint16_t datalen; unsigned char * data; } dns_rdata_dnskey_t; #endif /* GENERIC_DNSKEY_48_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_SPF_99_H #define GENERIC_SPF_99_H 1 /* $Id: spf_99.h,v 1.1.2.2 2005/07/16 00:40:54 marka Exp $ */ typedef struct dns_rdata_spf_string { isc_uint8_t length; unsigned char *data; } dns_rdata_spf_string_t; typedef struct dns_rdata_spf { dns_rdatacommon_t common; isc_mem_t *mctx; unsigned char *txt; isc_uint16_t txt_len; /* private */ isc_uint16_t offset; } dns_rdata_spf_t; /* * ISC_LANG_BEGINDECLS and ISC_LANG_ENDDECLS are already done * via rdatastructpre.h and rdatastructsuf.h. */ isc_result_t dns_rdata_spf_first(dns_rdata_spf_t *); isc_result_t dns_rdata_spf_next(dns_rdata_spf_t *); isc_result_t dns_rdata_spf_current(dns_rdata_spf_t *, dns_rdata_spf_string_t *); #endif /* GENERIC_SPF_99_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* */ #ifndef GENERIC_UNSPEC_103_H #define GENERIC_UNSPEC_103_H 1 /* $Id: unspec_103.h,v 1.13.18.2 2005/04/29 00:16:40 marka Exp $ */ typedef struct dns_rdata_unspec_t { dns_rdatacommon_t common; isc_mem_t *mctx; unsigned char *data; isc_uint16_t datalen; } dns_rdata_unspec_t; #endif /* GENERIC_UNSPEC_103_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifndef GENERIC_TKEY_249_H #define GENERIC_TKEY_249_H 1 /* $Id: tkey_249.h,v 1.20.18.2 2005/04/29 00:16:40 marka Exp $ */ /*! * \brief Per draft-ietf-dnsind-tkey-00.txt */ typedef struct dns_rdata_tkey { dns_rdatacommon_t common; isc_mem_t * mctx; dns_name_t algorithm; isc_uint32_t inception; isc_uint32_t expire; isc_uint16_t mode; isc_uint16_t error; isc_uint16_t keylen; unsigned char * key; isc_uint16_t otherlen; unsigned char * other; } dns_rdata_tkey_t; #endif /* GENERIC_TKEY_249_H */ /* * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: tsig_250.h,v 1.21.18.2 2005/04/29 00:16:29 marka Exp $ */ #ifndef ANY_255_TSIG_250_H #define ANY_255_TSIG_250_H 1 /*% RFC2845 */ typedef struct dns_rdata_any_tsig { dns_rdatacommon_t common; isc_mem_t * mctx; dns_name_t algorithm; isc_uint64_t timesigned; isc_uint16_t fudge; isc_uint16_t siglen; unsigned char * signature; isc_uint16_t originalid; isc_uint16_t error; isc_uint16_t otherlen; unsigned char * other; } dns_rdata_any_tsig_t; #endif /* ANY_255_TSIG_250_H */ /* * Copyright (C) 2004, 2006 Internet Systems Consortium, Inc. ("ISC") * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: dlv_32769.h,v 1.2.2.2 2006/02/19 06:50:47 marka Exp $ */ /* draft-ietf-dnsext-delegation-signer-05.txt */ #ifndef GENERIC_DLV_32769_H #define GENERIC_DLV_32769_H 1 typedef struct dns_rdata_dlv { dns_rdatacommon_t common; isc_mem_t *mctx; isc_uint16_t key_tag; isc_uint8_t algorithm; isc_uint8_t digest_type; isc_uint16_t length; unsigned char *digest; } dns_rdata_dlv_t; #endif /* GENERIC_DLV_32769_H */ /* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* $Id: rdatastructsuf.h,v 1.8 2004/03/05 05:10:04 marka Exp $ */ ISC_LANG_ENDDECLS #endif /* DNS_RDATASTRUCT_H */ Index: projects/cambria/lib/libarchive/archive_entry.c =================================================================== --- projects/cambria/lib/libarchive/archive_entry.c (revision 186459) +++ projects/cambria/lib/libarchive/archive_entry.c (revision 186460) @@ -1,2150 +1,2151 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef MAJOR_IN_MKDEV #include #else #ifdef MAJOR_IN_SYSMACROS #include #endif #endif #ifdef HAVE_EXT2FS_EXT2_FS_H #include /* for Linux file flags */ #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_entry_private.h" #undef max #define max(a, b) ((a)>(b)?(a):(b)) /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || defined _WIN32 || defined __WIN32__ /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define ae_makedev(maj, min) makedev((maj), (min)) #endif static void aes_clean(struct aes *); static void aes_copy(struct aes *dest, struct aes *src); static const char * aes_get_mbs(struct aes *); static const wchar_t * aes_get_wcs(struct aes *); static int aes_set_mbs(struct aes *, const char *mbs); static int aes_copy_mbs(struct aes *, const char *mbs); /* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */ static int aes_copy_wcs(struct aes *, const wchar_t *wcs); static int aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t); static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id); static void append_id_w(wchar_t **wp, int id); static int acl_special(struct archive_entry *entry, int type, int permset, int tag); static struct ae_acl *acl_new_entry(struct archive_entry *entry, int type, int permset, int tag, int id); static int isint_w(const wchar_t *start, const wchar_t *end, int *result); static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep); static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test); static void archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name, size_t); #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p != L'\0') ++p; return p - s; } #endif #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif #ifndef HAVE_WMEMCPY #define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t)) #endif static void aes_clean(struct aes *aes) { if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } archive_string_free(&(aes->aes_mbs)); archive_string_free(&(aes->aes_utf8)); aes->aes_set = 0; } static void aes_copy(struct aes *dest, struct aes *src) { wchar_t *wp; dest->aes_set = src->aes_set; archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs)); archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8)); if (src->aes_wcs != NULL) { wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1) * sizeof(wchar_t)); if (wp == NULL) __archive_errx(1, "No memory for aes_copy()"); wcscpy(wp, src->aes_wcs); dest->aes_wcs = wp; } } static const char * aes_get_utf8(struct aes *aes) { if (aes->aes_set & AES_SET_UTF8) return (aes->aes_utf8.s); if ((aes->aes_set & AES_SET_WCS) && archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) { aes->aes_set |= AES_SET_UTF8; return (aes->aes_utf8.s); } return (NULL); } static const char * aes_get_mbs(struct aes *aes) { /* If we already have an MBS form, return that immediately. */ if (aes->aes_set & AES_SET_MBS) return (aes->aes_mbs.s); /* If there's a WCS form, try converting with the native locale. */ if ((aes->aes_set & AES_SET_WCS) && archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) { aes->aes_set |= AES_SET_MBS; return (aes->aes_mbs.s); } /* We'll use UTF-8 for MBS if all else fails. */ return (aes_get_utf8(aes)); } static const wchar_t * aes_get_wcs(struct aes *aes) { wchar_t *w; int r; /* Return WCS form if we already have it. */ if (aes->aes_set & AES_SET_WCS) return (aes->aes_wcs); if (aes->aes_set & AES_SET_MBS) { /* Try converting MBS to WCS using native locale. */ /* * No single byte will be more than one wide character, * so this length estimate will always be big enough. */ size_t wcs_length = aes->aes_mbs.length; w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t)); if (w == NULL) __archive_errx(1, "No memory for aes_get_wcs()"); r = mbstowcs(w, aes->aes_mbs.s, wcs_length); w[wcs_length] = 0; if (r > 0) { aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs = w); } free(w); } if (aes->aes_set & AES_SET_UTF8) { /* Try converting UTF8 to WCS. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); - aes->aes_set |= AES_SET_WCS; + if (aes->aes_wcs != NULL) + aes->aes_set |= AES_SET_WCS; return (aes->aes_wcs); } return (NULL); } static int aes_set_mbs(struct aes *aes, const char *mbs) { return (aes_copy_mbs(aes, mbs)); } static int aes_copy_mbs(struct aes *aes, const char *mbs) { if (mbs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */ archive_strcpy(&(aes->aes_mbs), mbs); archive_string_empty(&(aes->aes_utf8)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } return (0); } /* * The 'update' form tries to proactively update all forms of * this string (WCS and MBS) and returns an error if any of * them fail. This is used by the 'pax' handler, for instance, * to detect and report character-conversion failures early while * still allowing clients to get potentially useful values from * the more tolerant lazy conversions. (get_mbs and get_wcs will * strive to give the user something useful, so you can get hopefully * usable values even if some of the character conversions are failing.) */ static int aes_update_utf8(struct aes *aes, const char *utf8) { if (utf8 == NULL) { aes->aes_set = 0; return (1); /* Succeeded in clearing everything. */ } /* Save the UTF8 string. */ archive_strcpy(&(aes->aes_utf8), utf8); /* Empty the mbs and wcs strings. */ archive_string_empty(&(aes->aes_mbs)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */ /* TODO: We should just do a direct UTF-8 to MBS conversion * here. That would be faster, use less space, and give the * same information. (If a UTF-8 to MBS conversion succeeds, * then UTF-8->WCS and Unicode->MBS conversions will both * succeed.) */ /* Try converting UTF8 to WCS, return false on failure. */ aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8)); if (aes->aes_wcs == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */ /* Try converting WCS to MBS, return false on failure. */ if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL) return (0); aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS; /* All conversions succeeded. */ return (1); } static int aes_copy_wcs(struct aes *aes, const wchar_t *wcs) { return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs)); } static int aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len) { wchar_t *w; if (wcs == NULL) { aes->aes_set = 0; return (0); } aes->aes_set = AES_SET_WCS; /* Only WCS form set. */ archive_string_empty(&(aes->aes_mbs)); archive_string_empty(&(aes->aes_utf8)); if (aes->aes_wcs) { free((wchar_t *)(uintptr_t)aes->aes_wcs); aes->aes_wcs = NULL; } w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); if (w == NULL) __archive_errx(1, "No memory for aes_copy_wcs()"); wmemcpy(w, wcs, len); w[len] = L'\0'; aes->aes_wcs = w; return (0); } /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); aes_clean(&entry->ae_fflags_text); aes_clean(&entry->ae_gname); aes_clean(&entry->ae_hardlink); aes_clean(&entry->ae_pathname); aes_clean(&entry->ae_symlink); aes_clean(&entry->ae_uname); archive_entry_acl_clear(entry); archive_entry_xattr_clear(entry); free(entry->stat); memset(entry, 0, sizeof(*entry)); return entry; } struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; struct ae_acl *ap, *ap2; struct ae_xattr *xp; /* Allocate new structure and copy over all of the fields. */ entry2 = (struct archive_entry *)malloc(sizeof(*entry2)); if (entry2 == NULL) return (NULL); memset(entry2, 0, sizeof(*entry2)); entry2->ae_stat = entry->ae_stat; entry2->ae_fflags_set = entry->ae_fflags_set; entry2->ae_fflags_clear = entry->ae_fflags_clear; aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); aes_copy(&entry2->ae_gname, &entry->ae_gname); aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink); aes_copy(&entry2->ae_pathname, &entry->ae_pathname); aes_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; aes_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy ACL data over. */ ap = entry->acl_head; while (ap != NULL) { ap2 = acl_new_entry(entry2, ap->type, ap->permset, ap->tag, ap->id); if (ap2 != NULL) aes_copy(&ap2->name, &ap->name); ap = ap->next; } /* Copy xattr data over. */ xp = entry->xattr_head; while (xp != NULL) { archive_entry_xattr_add_entry(entry2, xp->name, xp->value, xp->size); xp = xp->next; } return (entry2); } void archive_entry_free(struct archive_entry *entry) { archive_entry_clear(entry); free(entry); } struct archive_entry * archive_entry_new(void) { struct archive_entry *entry; entry = (struct archive_entry *)malloc(sizeof(*entry)); if (entry == NULL) return (NULL); memset(entry, 0, sizeof(*entry)); return (entry); } /* * Functions for reading fields from an archive_entry. */ time_t archive_entry_atime(struct archive_entry *entry) { return (entry->ae_stat.aest_atime); } long archive_entry_atime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_atime_nsec); } int archive_entry_atime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_ATIME); } time_t archive_entry_birthtime(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime); } long archive_entry_birthtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime_nsec); } int archive_entry_birthtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_BIRTHTIME); } time_t archive_entry_ctime(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime); } int archive_entry_ctime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_CTIME); } long archive_entry_ctime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime_nsec); } dev_t archive_entry_dev(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return ae_makedev(entry->ae_stat.aest_devmajor, entry->ae_stat.aest_devminor); else return (entry->ae_stat.aest_dev); } dev_t archive_entry_devmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devmajor); else return major(entry->ae_stat.aest_dev); } dev_t archive_entry_devminor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devminor); else return minor(entry->ae_stat.aest_dev); } mode_t archive_entry_filetype(struct archive_entry *entry) { return (AE_IFMT & entry->ae_stat.aest_mode); } void archive_entry_fflags(struct archive_entry *entry, unsigned long *set, unsigned long *clear) { *set = entry->ae_fflags_set; *clear = entry->ae_fflags_clear; } /* * Note: if text was provided, this just returns that text. If you * really need the text to be rebuilt in a canonical form, set the * text, ask for the bitmaps, then set the bitmaps. (Setting the * bitmaps clears any stored text.) This design is deliberate: if * we're editing archives, we don't want to discard flags just because * they aren't supported on the current system. The bitmap<->text * conversions are platform-specific (see below). */ const char * archive_entry_fflags_text(struct archive_entry *entry) { const char *f; char *p; f = aes_get_mbs(&entry->ae_fflags_text); if (f != NULL) return (f); if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) return (NULL); p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); if (p == NULL) return (NULL); aes_copy_mbs(&entry->ae_fflags_text, p); free(p); f = aes_get_mbs(&entry->ae_fflags_text); return (f); } gid_t archive_entry_gid(struct archive_entry *entry) { return (entry->ae_stat.aest_gid); } const char * archive_entry_gname(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_gname)); } const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { return (aes_get_wcs(&entry->ae_gname)); } const char * archive_entry_hardlink(struct archive_entry *entry) { if (entry->ae_set & AE_SET_HARDLINK) return (aes_get_mbs(&entry->ae_hardlink)); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { if (entry->ae_set & AE_SET_HARDLINK) return (aes_get_wcs(&entry->ae_hardlink)); return (NULL); } ino_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->ae_stat.aest_mode); } time_t archive_entry_mtime(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime); } long archive_entry_mtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime_nsec); } int archive_entry_mtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_MTIME); } unsigned int archive_entry_nlink(struct archive_entry *entry) { return (entry->ae_stat.aest_nlink); } const char * archive_entry_pathname(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_pathname)); } const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { return (aes_get_wcs(&entry->ae_pathname)); } dev_t archive_entry_rdev(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return ae_makedev(entry->ae_stat.aest_rdevmajor, entry->ae_stat.aest_rdevminor); else return (entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevmajor); else return major(entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevminor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevminor); else return minor(entry->ae_stat.aest_rdev); } int64_t archive_entry_size(struct archive_entry *entry) { return (entry->ae_stat.aest_size); } int archive_entry_size_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_SIZE); } const char * archive_entry_sourcepath(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_sourcepath)); } const char * archive_entry_symlink(struct archive_entry *entry) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_get_mbs(&entry->ae_symlink)); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_get_wcs(&entry->ae_symlink)); return (NULL); } uid_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_uid); } const char * archive_entry_uname(struct archive_entry *entry) { return (aes_get_mbs(&entry->ae_uname)); } const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { return (aes_get_wcs(&entry->ae_uname)); } /* * Functions to set archive_entry properties. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->ae_stat.aest_mode &= ~AE_IFMT; entry->ae_stat.aest_mode |= AE_IFMT & type; } void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { aes_clean(&entry->ae_fflags_text); entry->ae_fflags_set = set; entry->ae_fflags_clear = clear; } const char * archive_entry_copy_fflags_text(struct archive_entry *entry, const char *flags) { aes_copy_mbs(&entry->ae_fflags_text, flags); return (ae_strtofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) { aes_copy_wcs(&entry->ae_fflags_text, flags); return (ae_wcstofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } void archive_entry_set_gid(struct archive_entry *entry, gid_t g) { entry->stat_valid = 0; entry->ae_stat.aest_gid = g; } void archive_entry_set_gname(struct archive_entry *entry, const char *name) { aes_set_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { aes_copy_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_gname, name); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_gname, name)); } void archive_entry_set_ino(struct archive_entry *entry, unsigned long ino) { entry->stat_valid = 0; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { aes_set_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { aes_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { aes_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_ATIME; entry->ae_stat.aest_atime = t; entry->ae_stat.aest_atime_nsec = ns; } void archive_entry_unset_atime(struct archive_entry *entry) { archive_entry_set_atime(entry, 0, 0); entry->ae_set &= ~AE_SET_ATIME; } void archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = m; entry->ae_stat.aest_birthtime_nsec = ns; } void archive_entry_unset_birthtime(struct archive_entry *entry) { archive_entry_set_birthtime(entry, 0, 0); entry->ae_set &= ~AE_SET_BIRTHTIME; } void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_CTIME; entry->ae_stat.aest_ctime = t; entry->ae_stat.aest_ctime_nsec = ns; } void archive_entry_unset_ctime(struct archive_entry *entry) { archive_entry_set_ctime(entry, 0, 0); entry->ae_set &= ~AE_SET_CTIME; } void archive_entry_set_dev(struct archive_entry *entry, dev_t d) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 0; entry->ae_stat.aest_dev = d; } void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devmajor = m; } void archive_entry_set_devminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devminor = m; } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_set_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_set_mbs(&entry->ae_symlink, target); else aes_set_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_copy_mbs(&entry->ae_symlink, target); else aes_copy_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { if (entry->ae_set & AE_SET_SYMLINK) aes_copy_wcs(&entry->ae_symlink, target); else aes_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) return (aes_update_utf8(&entry->ae_symlink, target)); else return (aes_update_utf8(&entry->ae_hardlink, target)); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->ae_stat.aest_mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns) { entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = m; entry->ae_stat.aest_mtime_nsec = ns; } void archive_entry_unset_mtime(struct archive_entry *entry) { archive_entry_set_mtime(entry, 0, 0); entry->ae_set &= ~AE_SET_MTIME; } void archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) { entry->stat_valid = 0; entry->ae_stat.aest_nlink = nlink; } void archive_entry_set_pathname(struct archive_entry *entry, const char *name) { aes_set_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { aes_copy_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_pathname, name); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_pathname, name)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->ae_stat.aest_mode &= AE_IFMT; entry->ae_stat.aest_mode |= ~AE_IFMT & p; } void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; } void archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevmajor = m; } void archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevminor = m; } void archive_entry_set_size(struct archive_entry *entry, int64_t s) { entry->stat_valid = 0; entry->ae_stat.aest_size = s; entry->ae_set |= AE_SET_SIZE; } void archive_entry_unset_size(struct archive_entry *entry) { archive_entry_set_size(entry, 0); entry->ae_set &= ~AE_SET_SIZE; } void archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) { aes_set_mbs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { aes_set_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { aes_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { aes_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_set_uid(struct archive_entry *entry, uid_t u) { entry->stat_valid = 0; entry->ae_stat.aest_uid = u; } void archive_entry_set_uname(struct archive_entry *entry, const char *name) { aes_set_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { aes_copy_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) { aes_copy_wcs(&entry->ae_uname, name); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { return (aes_update_utf8(&entry->ae_uname, name)); } /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and * complete standard that addressed the needs of ACL archiving and 2) * everyone followed it faithfully. Alas, neither is true, so the * following is a lot more complex than might seem necessary to the * uninitiated. */ void archive_entry_acl_clear(struct archive_entry *entry) { struct ae_acl *ap; while (entry->acl_head != NULL) { ap = entry->acl_head->next; aes_clean(&entry->acl_head->name); free(entry->acl_head); entry->acl_head = ap; } if (entry->acl_text_w != NULL) { free(entry->acl_text_w); entry->acl_text_w = NULL; } entry->acl_p = NULL; entry->acl_state = 0; /* Not counting. */ } /* * Add a single ACL entry to the internal list of ACL data. */ void archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { struct ae_acl *ap; if (acl_special(entry, type, permset, tag) == 0) return; ap = acl_new_entry(entry, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return; } if (name != NULL && *name != '\0') aes_copy_mbs(&ap->name, name); else aes_clean(&ap->name); } /* * As above, but with a wide-character name. */ void archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name)); } void archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name, size_t len) { struct ae_acl *ap; if (acl_special(entry, type, permset, tag) == 0) return; ap = acl_new_entry(entry, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return; } if (name != NULL && *name != L'\0' && len > 0) aes_copy_wcs_len(&ap->name, name, len); else aes_clean(&ap->name); } /* * If this ACL entry is part of the standard POSIX permissions set, * store the permissions in the stat structure and return zero. */ static int acl_special(struct archive_entry *entry, int type, int permset, int tag) { if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) { switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: entry->ae_stat.aest_mode &= ~0700; entry->ae_stat.aest_mode |= (permset & 7) << 6; return (0); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: entry->ae_stat.aest_mode &= ~0070; entry->ae_stat.aest_mode |= (permset & 7) << 3; return (0); case ARCHIVE_ENTRY_ACL_OTHER: entry->ae_stat.aest_mode &= ~0007; entry->ae_stat.aest_mode |= permset & 7; return (0); } } return (1); } /* * Allocate and populate a new ACL entry with everything but the * name. */ static struct ae_acl * acl_new_entry(struct archive_entry *entry, int type, int permset, int tag, int id) { struct ae_acl *ap; if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS && type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) return (NULL); if (entry->acl_text_w != NULL) { free(entry->acl_text_w); entry->acl_text_w = NULL; } /* XXX TODO: More sanity-checks on the arguments XXX */ /* If there's a matching entry already in the list, overwrite it. */ for (ap = entry->acl_head; ap != NULL; ap = ap->next) { if (ap->type == type && ap->tag == tag && ap->id == id) { ap->permset = permset; return (ap); } } /* Add a new entry to the list. */ ap = (struct ae_acl *)malloc(sizeof(*ap)); if (ap == NULL) return (NULL); memset(ap, 0, sizeof(*ap)); ap->next = entry->acl_head; entry->acl_head = ap; ap->type = type; ap->tag = tag; ap->id = id; ap->permset = permset; return (ap); } /* * Return a count of entries matching "want_type". */ int archive_entry_acl_count(struct archive_entry *entry, int want_type) { int count; struct ae_acl *ap; count = 0; ap = entry->acl_head; while (ap != NULL) { if ((ap->type & want_type) != 0) count++; ap = ap->next; } if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) count += 3; return (count); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_entry_acl_reset(struct archive_entry *entry, int want_type) { int count, cutoff; count = archive_entry_acl_count(entry, want_type); /* * If the only entries are the three standard ones, * then don't return any ACL data. (In this case, * client can just use chmod(2) to set permissions.) */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) cutoff = 3; else cutoff = 0; if (count > cutoff) entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; else entry->acl_state = 0; entry->acl_p = entry->acl_head; return (count); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { *name = NULL; *id = -1; /* * The acl_state is either zero (no entries available), -1 * (reading from list), or an entry type (retrieve that type * from ae_stat.aest_mode). */ if (entry->acl_state == 0) return (ARCHIVE_WARN); /* The first three access entries are special. */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { switch (entry->acl_state) { case ARCHIVE_ENTRY_ACL_USER_OBJ: *permset = (entry->ae_stat.aest_mode >> 6) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: *permset = (entry->ae_stat.aest_mode >> 3) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_OTHER: *permset = entry->ae_stat.aest_mode & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_OTHER; entry->acl_state = -1; entry->acl_p = entry->acl_head; return (ARCHIVE_OK); default: break; } } while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0) entry->acl_p = entry->acl_p->next; if (entry->acl_p == NULL) { entry->acl_state = 0; *type = 0; *permset = 0; *tag = 0; *id = -1; *name = NULL; return (ARCHIVE_EOF); /* End of ACL entries. */ } *type = entry->acl_p->type; *permset = entry->acl_p->permset; *tag = entry->acl_p->tag; *id = entry->acl_p->id; *name = aes_get_mbs(&entry->acl_p->name); entry->acl_p = entry->acl_p->next; return (ARCHIVE_OK); } /* * Generate a text version of the ACL. The flags parameter controls * the style of the generated ACL. */ const wchar_t * archive_entry_acl_text_w(struct archive_entry *entry, int flags) { int count; size_t length; const wchar_t *wname; const wchar_t *prefix; wchar_t separator; struct ae_acl *ap; int id; wchar_t *wp; if (entry->acl_text_w != NULL) { free (entry->acl_text_w); entry->acl_text_w = NULL; } separator = L','; count = 0; length = 0; ap = entry->acl_head; while (ap != NULL) { if ((ap->type & flags) != 0) { count++; if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)) length += 8; /* "default:" */ length += 5; /* tag name */ length += 1; /* colon */ wname = aes_get_wcs(&ap->name); if (wname != NULL) length += wcslen(wname); else length += sizeof(uid_t) * 3 + 1; length ++; /* colon */ length += 3; /* rwx */ length += 1; /* colon */ length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1; length ++; /* newline */ } ap = ap->next; } if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) { length += 10; /* "user::rwx\n" */ length += 11; /* "group::rwx\n" */ length += 11; /* "other::rwx\n" */ } if (count == 0) return (NULL); /* Now, allocate the string and actually populate it. */ wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wp == NULL) __archive_errx(1, "No memory to generate the text version of the ACL"); count = 0; if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL, entry->ae_stat.aest_mode & 0700, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL, entry->ae_stat.aest_mode & 0070, -1); *wp++ = ','; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL, entry->ae_stat.aest_mode & 0007, -1); count += 3; ap = entry->acl_head; while (ap != NULL) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { wname = aes_get_wcs(&ap->name); *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, NULL, ap->tag, wname, ap->permset, id); count++; } ap = ap->next; } } if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) prefix = L"default:"; else prefix = NULL; ap = entry->acl_head; count = 0; while (ap != NULL) { if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) { wname = aes_get_wcs(&ap->name); if (count > 0) *wp++ = separator; if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) id = ap->id; else id = -1; append_entry_w(&wp, prefix, ap->tag, wname, ap->permset, id); count ++; } ap = ap->next; } } return (entry->acl_text_w); } static void append_id_w(wchar_t **wp, int id) { if (id < 0) id = 0; if (id > 9) append_id_w(wp, id / 10); *(*wp)++ = L"0123456789"[id % 10]; } static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag, const wchar_t *wname, int perm, int id) { if (prefix != NULL) { wcscpy(*wp, prefix); *wp += wcslen(*wp); } switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_USER: wcscpy(*wp, L"user"); break; case ARCHIVE_ENTRY_ACL_GROUP_OBJ: wname = NULL; id = -1; /* FALLTHROUGH */ case ARCHIVE_ENTRY_ACL_GROUP: wcscpy(*wp, L"group"); break; case ARCHIVE_ENTRY_ACL_MASK: wcscpy(*wp, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: wcscpy(*wp, L"other"); wname = NULL; id = -1; break; } *wp += wcslen(*wp); *(*wp)++ = L':'; if (wname != NULL) { wcscpy(*wp, wname); *wp += wcslen(*wp); } else if (tag == ARCHIVE_ENTRY_ACL_USER || tag == ARCHIVE_ENTRY_ACL_GROUP) { append_id_w(wp, id); id = -1; } *(*wp)++ = L':'; *(*wp)++ = (perm & 0444) ? L'r' : L'-'; *(*wp)++ = (perm & 0222) ? L'w' : L'-'; *(*wp)++ = (perm & 0111) ? L'x' : L'-'; if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); } **wp = L'\0'; } /* * Parse a textual ACL. This automatically recognizes and supports * extensions described above. The 'type' argument is used to * indicate the type that should be used for any entries not * explicitly marked as "default:". */ int __archive_entry_acl_parse_w(struct archive_entry *entry, const wchar_t *text, int default_type) { struct { const wchar_t *start; const wchar_t *end; } field[4]; int fields; int type, tag, permset, id; const wchar_t *p; wchar_t sep; while (text != NULL && *text != L'\0') { /* * Parse the fields out of the next entry, * advance 'text' to start of next entry. */ fields = 0; do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); if (fields < 4) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == L':'); if (fields < 3) return (ARCHIVE_WARN); /* Check for a numeric ID in field 1 or 3. */ id = -1; isint_w(field[1].start, field[1].end, &id); /* Field 3 is optional. */ if (id == -1 && fields > 3) isint_w(field[3].start, field[3].end, &id); /* Parse the permissions from field 2. */ permset = 0; p = field[2].start; while (p < field[2].end) { switch (*p++) { case 'r': case 'R': permset |= ARCHIVE_ENTRY_ACL_READ; break; case 'w': case 'W': permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case 'x': case 'X': permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; case '-': break; default: return (ARCHIVE_WARN); } } /* * Solaris extension: "defaultuser::rwx" is the * default ACL corresponding to "user::rwx", etc. */ if (field[0].end-field[0].start > 7 && wmemcmp(field[0].start, L"default", 7) == 0) { type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; field[0].start += 7; } else type = default_type; if (prefix_w(field[0].start, field[0].end, L"user")) { if (id != -1 || field[1].start < field[1].end) tag = ARCHIVE_ENTRY_ACL_USER; else tag = ARCHIVE_ENTRY_ACL_USER_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"group")) { if (id != -1 || field[1].start < field[1].end) tag = ARCHIVE_ENTRY_ACL_GROUP; else tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; } else if (prefix_w(field[0].start, field[0].end, L"other")) { if (id != -1 || field[1].start < field[1].end) return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_OTHER; } else if (prefix_w(field[0].start, field[0].end, L"mask")) { if (id != -1 || field[1].start < field[1].end) return (ARCHIVE_WARN); tag = ARCHIVE_ENTRY_ACL_MASK; } else return (ARCHIVE_WARN); /* Add entry to the internal list. */ archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, field[1].start, field[1].end - field[1].start); } return (ARCHIVE_OK); } /* * extended attribute handling */ void archive_entry_xattr_clear(struct archive_entry *entry) { struct ae_xattr *xp; while (entry->xattr_head != NULL) { xp = entry->xattr_head->next; free(entry->xattr_head->name); free(entry->xattr_head->value); free(entry->xattr_head); entry->xattr_head = xp; } entry->xattr_head = NULL; } void archive_entry_xattr_add_entry(struct archive_entry *entry, const char *name, const void *value, size_t size) { struct ae_xattr *xp; for (xp = entry->xattr_head; xp != NULL; xp = xp->next) ; if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL) /* XXX Error XXX */ return; xp->name = strdup(name); if ((xp->value = malloc(size)) != NULL) { memcpy(xp->value, value, size); xp->size = size; } else xp->size = 0; xp->next = entry->xattr_head; entry->xattr_head = xp; } /* * returns number of the extended attribute entries */ int archive_entry_xattr_count(struct archive_entry *entry) { struct ae_xattr *xp; int count = 0; for (xp = entry->xattr_head; xp != NULL; xp = xp->next) count++; return count; } int archive_entry_xattr_reset(struct archive_entry * entry) { entry->xattr_p = entry->xattr_head; return archive_entry_xattr_count(entry); } int archive_entry_xattr_next(struct archive_entry * entry, const char **name, const void **value, size_t *size) { if (entry->xattr_p) { *name = entry->xattr_p->name; *value = entry->xattr_p->value; *size = entry->xattr_p->size; entry->xattr_p = entry->xattr_p->next; return (ARCHIVE_OK); } else { *name = NULL; *value = NULL; *size = (size_t)0; return (ARCHIVE_WARN); } } /* * end of xattr handling */ /* * Parse a string to a positive decimal integer. Returns true if * the string is non-empty and consists only of decimal digits, * false otherwise. */ static int isint_w(const wchar_t *start, const wchar_t *end, int *result) { int n = 0; if (start >= end) return (0); while (start < end) { if (*start < '0' || *start > '9') return (0); if (n > (INT_MAX / 10)) n = INT_MAX; else { n *= 10; n += *start - '0'; } start++; } *result = n; return (1); } /* * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated * to point to just after the separator. *start points to the first * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep) { /* Skip leading whitespace to find start of field. */ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { (*wp)++; } *start = *wp; /* Scan for the separator. */ while (**wp != L'\0' && **wp != L',' && **wp != L':' && **wp != L'\n') { (*wp)++; } *sep = **wp; /* Trim trailing whitespace to locate end of field. */ *end = *wp - 1; while (**end == L' ' || **end == L'\t' || **end == L'\n') { (*end)--; } (*end)++; /* Adjust scanner location. */ if (**wp != L'\0') (*wp)++; } /* * Return true if the characters [start...end) are a prefix of 'test'. * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc. */ static int prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) { if (start == end) return (0); if (*start++ != *test++) return (0); while (start < end && *start++ == *test++) ; if (start < end) return (0); return (1); } /* * Following code is modified from UC Berkeley sources, and * is subject to the following copyright notice. */ /*- * Copyright (c) 1993 * 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. * 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. */ static struct flag { const char *name; const wchar_t *wname; unsigned long set; unsigned long clear; } flags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND { "nosappnd", L"nosappnd", SF_APPEND, 0 }, { "nosappend", L"nosappend", SF_APPEND, 0 }, #endif #ifdef EXT2_APPEND_FL /* 'a' */ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, #endif #ifdef SF_ARCHIVED { "noarch", L"noarch", SF_ARCHIVED, 0 }, { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, #endif #ifdef SF_IMMUTABLE { "noschg", L"noschg", SF_IMMUTABLE, 0 }, { "noschange", L"noschange", SF_IMMUTABLE, 0 }, { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, #endif #ifdef EXT2_IMMUTABLE_FL /* 'i' */ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, #endif #ifdef SF_NOUNLINK { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, #endif #ifdef SF_SNAPSHOT { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, #endif #ifdef UF_APPEND { "nouappnd", L"nouappnd", UF_APPEND, 0 }, { "nouappend", L"nouappend", UF_APPEND, 0 }, #endif #ifdef UF_IMMUTABLE { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #ifdef EXT2_NODUMP_FL /* 'd' */ { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE { "noopaque", L"noopaque", UF_OPAQUE, 0 }, #endif #ifdef UF_NOUNLINK { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, #endif #ifdef EXT2_COMPR_FL /* 'c' */ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, #endif #ifdef EXT2_NOATIME_FL /* 'A' */ { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif { NULL, NULL, 0, 0 } }; /* * fflagstostr -- * Convert file flags to a comma-separated string. If no flags * are set, return the empty string. */ static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; const char *sp; unsigned long bits; struct flag *flag; size_t length; bits = bitset | bitclear; length = 0; for (flag = flags; flag->name != NULL; flag++) if (bits & (flag->set | flag->clear)) { length += strlen(flag->name) + 1; bits &= ~(flag->set | flag->clear); } if (length == 0) return (NULL); string = (char *)malloc(length); if (string == NULL) return (NULL); dp = string; for (flag = flags; flag->name != NULL; flag++) { if (bitset & flag->set || bitclear & flag->clear) { sp = flag->name + 2; } else if (bitset & flag->clear || bitclear & flag->set) { sp = flag->name; } else continue; bitset &= ~(flag->set | flag->clear); bitclear &= ~(flag->set | flag->clear); if (dp > string) *dp++ = ','; while ((*dp++ = *sp++) != '\0') ; dp--; } *dp = '\0'; return (string); } /* * strtofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const char * ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) { const char *start, *end; struct flag *flag; unsigned long set, clear; const char *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; for (flag = flags; flag->name != NULL; flag++) { if (memcmp(start, flag->name, end - start) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (memcmp(start, flag->name + 2, end - start) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->name == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == '\t' || *start == ' ' || *start == ',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } /* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const wchar_t * ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) { const wchar_t *start, *end; struct flag *flag; unsigned long set, clear; const wchar_t *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == L'\t' || *start == L' ' || *start == L',') start++; while (*start != L'\0') { /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; for (flag = flags; flag->wname != NULL; flag++) { if (wmemcmp(start, flag->wname, end - start) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (wmemcmp(start, flag->wname + 2, end - start) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->wname == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == L'\t' || *start == L' ' || *start == L',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } #ifdef TEST #include int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); } #endif Index: projects/cambria/lib/libarchive/test/main.c =================================================================== --- projects/cambria/lib/libarchive/test/main.c (revision 186459) +++ projects/cambria/lib/libarchive/test/main.c (revision 186460) @@ -1,1068 +1,1079 @@ /* * Copyright (c) 2003-2007 Tim Kientzle * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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. */ /* * Various utility routines useful for test programs. * Each test program is linked against this file. */ #include "test.h" #include #include #include #include /* * This same file is used pretty much verbatim for all test harnesses. * * The next few lines are the only differences. */ #undef PROGRAM /* Testing a library, not a program. */ #define LIBRARY "libarchive" #define ENVBASE "LIBARCHIVE" /* Prefix for environment variables. */ #define EXTRA_DUMP(x) archive_error_string((struct archive *)(x)) #define EXTRA_VERSION archive_version() #define KNOWNREF "test_compat_gtar_1.tgz.uu" __FBSDID("$FreeBSD$"); /* * "list.h" is simply created by "grep DEFINE_TEST"; it has * a line like * DEFINE_TEST(test_function) * for each test. * Include it here with a suitable DEFINE_TEST to declare all of the * test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Interix doesn't define these in a standard header. */ #if __INTERIX__ extern char *optarg; extern int optind; #endif /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs for successful tests. */ static int keep_temp_files = 0; /* Default is to print some basic information about each test. */ static int quiet_flag = 0; /* Default is to summarize repeated failures. */ static int verbose = 0; /* Cumulative count of component failures. */ static int failures = 0; /* Cumulative count of skipped component tests. */ static int skips = 0; /* Cumulative count of assertions. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static char *refdir; /* * My own implementation of the standard assert() macro emits the * message in the same format as GCC (file:line: message). * It also includes some additional useful information. * This makes it a lot easier to skim through test failures in * Emacs. ;-) * * It also supports a few special features specifically to simplify * test harnesses: * failure(fmt, args) -- Stores a text string that gets * printed if the following assertion fails, good for * explaining subtle tests. */ static char msg[4096]; /* * For each test source file, we remember how many times each * failure was reported. */ static const char *failed_filename = NULL; static struct line { int line; int count; } failed_lines[1000]; /* * Count this failure; return the number of previous failures. */ static int previous_failures(const char *filename, int line) { unsigned int i; int count; if (failed_filename == NULL || strcmp(failed_filename, filename) != 0) memset(failed_lines, 0, sizeof(failed_lines)); failed_filename = filename; for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].line == line) { count = failed_lines[i].count; failed_lines[i].count++; return (count); } if (failed_lines[i].line == 0) { failed_lines[i].line = line; failed_lines[i].count = 1; return (0); } } return (0); } /* * Copy arguments into file-local variables. */ static const char *test_filename; static int test_line; static void *test_extra; void test_setup(const char *filename, int line) { test_filename = filename; test_line = line; } /* * Inform user that we're skipping a test. */ void test_skipping(const char *fmt, ...) { va_list ap; if (previous_failures(test_filename, test_line)) return; va_start(ap, fmt); fprintf(stderr, " *** SKIPPING: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); ++skips; } /* Common handling of failed tests. */ static void report_failure(void *extra) { if (msg[0] != '\0') { fprintf(stderr, " Description: %s\n", msg); msg[0] = '\0'; } #ifdef EXTRA_DUMP if (extra != NULL) fprintf(stderr, " detail: %s\n", EXTRA_DUMP(extra)); #else (void)extra; /* UNUSED */ #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); *(char *)(NULL) = 0; exit(1); } } /* * Summarize repeated failures in the just-completed test file. * The reports above suppress multiple failures from the same source * line; this reports on any tests that did fail multiple times. */ static int summarize_comparator(const void *a0, const void *b0) { const struct line *a = a0, *b = b0; if (a->line == 0 && b->line == 0) return (0); if (a->line == 0) return (1); if (b->line == 0) return (-1); return (a->line - b->line); } static void summarize(void) { unsigned int i; qsort(failed_lines, sizeof(failed_lines)/sizeof(failed_lines[0]), sizeof(failed_lines[0]), summarize_comparator); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].line == 0) break; if (failed_lines[i].count > 1) fprintf(stderr, "%s:%d: Failed %d times\n", failed_filename, failed_lines[i].line, failed_lines[i].count); } /* Clear the failure history for the next file. */ memset(failed_lines, 0, sizeof(failed_lines)); } /* Set up a message to display only after a test fails. */ void failure(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsprintf(msg, fmt, ap); va_end(ap); } /* Generic assert() just displays the failed condition. */ int test_assert(const char *file, int line, int value, const char *condition, void *extra) { ++assertions; if (value) { msg[0] = '\0'; return (value); } failures ++; if (!verbose && previous_failures(file, line)) return (value); fprintf(stderr, "%s:%d: Assertion failed\n", file, line); fprintf(stderr, " Condition: %s\n", condition); report_failure(extra); return (value); } /* assertEqualInt() displays the values of the two integers. */ int test_assert_equal_int(const char *file, int line, int v1, const char *e1, int v2, const char *e2, void *extra) { ++assertions; if (v1 == v2) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Ints not equal\n", file, line); fprintf(stderr, " %s=%d\n", e1, v1); fprintf(stderr, " %s=%d\n", e2, v2); report_failure(extra); return (0); } static void strdump(const char *p) { if (p == NULL) { fprintf(stderr, "(null)"); return; } fprintf(stderr, "\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': fprintf(stderr, "\a"); break; case '\b': fprintf(stderr, "\b"); break; case '\n': fprintf(stderr, "\n"); break; case '\r': fprintf(stderr, "\r"); break; default: if (c >= 32 && c < 127) fprintf(stderr, "%c", c); else fprintf(stderr, "\\x%02X", c); } } fprintf(stderr, "\""); } /* assertEqualString() displays the values of the two strings. */ int test_assert_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra) { ++assertions; if (v1 == NULL || v2 == NULL) { if (v1 == v2) { msg[0] = '\0'; return (1); } } else if (strcmp(v1, v2) == 0) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Strings not equal\n", file, line); fprintf(stderr, " %s = ", e1); strdump(v1); fprintf(stderr, " (length %d)\n", v1 == NULL ? 0 : strlen(v1)); fprintf(stderr, " %s = ", e2); strdump(v2); fprintf(stderr, " (length %d)\n", v2 == NULL ? 0 : strlen(v2)); report_failure(extra); return (0); } static void wcsdump(const wchar_t *w) { if (w == NULL) { fprintf(stderr, "(null)"); return; } fprintf(stderr, "\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) fprintf(stderr, "%c", c); else if (c < 256) fprintf(stderr, "\\x%02X", c); else if (c < 0x10000) fprintf(stderr, "\\u%04X", c); else fprintf(stderr, "\\U%08X", c); } fprintf(stderr, "\""); } /* assertEqualWString() displays the values of the two strings. */ int test_assert_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { ++assertions; if (v1 == NULL) { if (v2 == NULL) { msg[0] = '\0'; return (1); } } else if (v2 == NULL) { if (v1 == NULL) { msg[0] = '\0'; return (1); } } else if (wcscmp(v1, v2) == 0) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: Unicode strings not equal\n", file, line); fprintf(stderr, " %s = ", e1); wcsdump(v1); fprintf(stderr, "\n"); fprintf(stderr, " %s = ", e2); wcsdump(v2); fprintf(stderr, "\n"); report_failure(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; for(i=0; i < l; i+=16) { fprintf(stderr, "%04x", i + offset); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; fprintf(stderr, "%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { fprintf(stderr, "%c ", sep); sep = ' '; } fprintf(stderr, "%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) fprintf(stderr, "%c", c); else fprintf(stderr, "."); } fprintf(stderr, "\n"); } } /* assertEqualMem() displays the values of the two memory blocks. */ /* TODO: For long blocks, hexdump the first bytes that actually differ. */ int test_assert_equal_mem(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, size_t l, const char *ld, void *extra) { ++assertions; if (v1 == NULL || v2 == NULL) { if (v1 == v2) { msg[0] = '\0'; return (1); } } else if (memcmp(v1, v2, l) == 0) { msg[0] = '\0'; return (1); } failures ++; if (!verbose && previous_failures(file, line)) return (0); fprintf(stderr, "%s:%d: Assertion failed: memory not equal\n", file, line); fprintf(stderr, " size %s = %d\n", ld, (int)l); fprintf(stderr, " Dump of %s\n", e1); hexdump(v1, v2, l < 32 ? l : 32, 0); fprintf(stderr, " Dump of %s\n", e2); hexdump(v2, v1, l < 32 ? l : 32, 0); fprintf(stderr, "\n"); report_failure(extra); return (0); } int test_assert_empty_file(const char *f1fmt, ...) { char buff[1024]; char f1[1024]; struct stat st; va_list ap; ssize_t s; int fd; va_start(ap, f1fmt); vsprintf(f1, f1fmt, ap); va_end(ap); if (stat(f1, &st) != 0) { fprintf(stderr, "%s:%d: Could not stat: %s\n", test_filename, test_line, f1); report_failure(NULL); return (0); } if (st.st_size == 0) return (1); failures ++; if (!verbose && previous_failures(test_filename, test_line)) return (0); fprintf(stderr, "%s:%d: File not empty: %s\n", test_filename, test_line, f1); fprintf(stderr, " File size: %d\n", (int)st.st_size); fprintf(stderr, " Contents:\n"); fd = open(f1, O_RDONLY); if (fd < 0) { fprintf(stderr, " Unable to open %s\n", f1); } else { s = sizeof(buff) < st.st_size ? sizeof(buff) : st.st_size; s = read(fd, buff, s); hexdump(buff, NULL, s, 0); } report_failure(NULL); return (0); } /* assertEqualFile() asserts that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int test_assert_equal_file(const char *f1, const char *f2pattern, ...) { char f2[1024]; va_list ap; char buff1[1024]; char buff2[1024]; int fd1, fd2; int n1, n2; va_start(ap, f2pattern); vsprintf(f2, f2pattern, ap); va_end(ap); fd1 = open(f1, O_RDONLY); fd2 = open(f2, O_RDONLY); for (;;) { n1 = read(fd1, buff1, sizeof(buff1)); n2 = read(fd2, buff2, sizeof(buff2)); if (n1 != n2) break; if (n1 == 0 && n2 == 0) return (1); if (memcmp(buff1, buff2, n1) != 0) break; } failures ++; if (!verbose && previous_failures(test_filename, test_line)) return (0); fprintf(stderr, "%s:%d: Files are not identical\n", test_filename, test_line); fprintf(stderr, " file1=\"%s\"\n", f1); fprintf(stderr, " file2=\"%s\"\n", f2); report_failure(test_extra); return (0); } int test_assert_file_exists(const char *fpattern, ...) { char f[1024]; va_list ap; va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); if (!access(f, F_OK)) return (1); if (!previous_failures(test_filename, test_line)) { fprintf(stderr, "%s:%d: File doesn't exist\n", test_filename, test_line); fprintf(stderr, " file=\"%s\"\n", f); report_failure(test_extra); } return (0); } int test_assert_file_not_exists(const char *fpattern, ...) { char f[1024]; va_list ap; va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); if (access(f, F_OK)) return (1); if (!previous_failures(test_filename, test_line)) { fprintf(stderr, "%s:%d: File exists and shouldn't\n", test_filename, test_line); fprintf(stderr, " file=\"%s\"\n", f); report_failure(test_extra); } return (0); } /* assertFileContents() asserts the contents of a file. */ int test_assert_file_contents(const void *buff, int s, const char *fpattern, ...) { char f[1024]; va_list ap; char *contents; int fd; int n; va_start(ap, fpattern); vsprintf(f, fpattern, ap); va_end(ap); fd = open(f, O_RDONLY); contents = malloc(s * 2); n = read(fd, contents, s * 2); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failures ++; if (!previous_failures(test_filename, test_line)) { fprintf(stderr, "%s:%d: File contents don't match\n", test_filename, test_line); fprintf(stderr, " file=\"%s\"\n", f); if (n > 0) hexdump(contents, buff, n, 0); else { fprintf(stderr, " File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } report_failure(test_extra); } free(contents); return (0); } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; int fd; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); fd = open(filename, O_RDONLY); if (fd < 0) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fd, &st); if (r != 0) { fprintf(stderr, "Can't stat file %s\n", filename); close(fd); return (NULL); } p = malloc(st.st_size + 1); if (p == NULL) { fprintf(stderr, "Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); close(fd); return (NULL); } bytes_read = read(fd, p, st.st_size); if (bytes_read < st.st_size) { fprintf(stderr, "Can't read file %s\n", filename); close(fd); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; close(fd); return (p); } /* * "list.h" is automatically generated; it just has a lot of lines like: * DEFINE_TEST(function_name) * It's used above to declare all of the test functions. * We reuse it here to define a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n }, struct { void (*func)(void); const char *name; } tests[] = { #include "list.h" }; /* * Each test is run in a private work dir. Those work dirs * do have consistent and predictable names, in case a group * of tests need to collaborate. However, there is no provision * for requiring that tests run in a certain order. */ static int test_run(int i, const char *tmpdir) { int failures_before = failures; if (!quiet_flag) { printf("%d: %s\n", i, tests[i].name); fflush(stdout); } /* * Always explicitly chdir() in case the last test moved us to * a strange place. */ if (chdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Create a temp directory for this specific test. */ if (mkdir(tests[i].name, 0755)) { fprintf(stderr, "ERROR: Couldn't create temp dir ``%s''\n", tests[i].name); exit(1); } /* Chdir() to that work directory. */ if (chdir(tests[i].name)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir ``%s''\n", tests[i].name); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Run the actual test. */ (*tests[i].func)(); /* Summarize the results of this test. */ summarize(); /* If there were no failures, we can remove the work dir. */ if (failures == failures_before) { if (!keep_temp_files && chdir(tmpdir) == 0) { systemf("rm -rf %s", tests[i].name); } } /* Return appropriate status. */ return (failures == failures_before ? 0 : 1); } static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "w"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } static char * get_refdir(const char *tmpdir) { - char *ref, *p; + char tried[512] = { '\0' }; + char buff[128]; + char *pwd, *p; /* Get the current dir. */ systemf("/bin/pwd > %s/refdir", tmpdir); - ref = slurpfile(NULL, "%s/refdir", tmpdir); - p = ref + strlen(ref); - while (p[-1] == '\n') { - --p; - *p = '\0'; - } + pwd = slurpfile(NULL, "%s/refdir", tmpdir); + while (pwd[strlen(pwd) - 1] == '\n') + pwd[strlen(pwd) - 1] = '\0'; + printf("PWD: %s\n", pwd); systemf("rm %s/refdir", tmpdir); + /* Look for a known file. */ - p = slurpfile(NULL, "%s/%s", ref, KNOWNREF); - if (p != NULL) { - free(p); - return (ref); + snprintf(buff, sizeof(buff), "%s", pwd); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + snprintf(buff, sizeof(buff), "%s/test", pwd); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + snprintf(buff, sizeof(buff), "%s/%s/test", pwd, LIBRARY); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + if (memcmp(pwd, "/usr/obj", 8) == 0) { + snprintf(buff, sizeof(buff), "%s", pwd + 8); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); + + snprintf(buff, sizeof(buff), "%s/test", pwd + 8); + p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); + if (p != NULL) goto success; + strncat(tried, buff, sizeof(tried) - strlen(tried) - 1); + strncat(tried, "\n", sizeof(tried) - strlen(tried) - 1); } - p = slurpfile(NULL, "%s/test/%s", ref, KNOWNREF); - if (p != NULL) { - free(p); - p = malloc(strlen(ref) + strlen("/test") + 1); - strcpy(p, ref); - strcat(p, "/test"); - free(ref); - return (p); - } - p = slurpfile(NULL, "%s/%s/test/%s", ref, LIBRARY, KNOWNREF); - if (p != NULL) { - free(p); - p = malloc(strlen(ref) + 1 + strlen(LIBRARY) + strlen("/test") + 1); - strcpy(p, ref); - strcat(p, "/"); - strcat(p, LIBRARY); - strcat(p, "/test"); - free(ref); - return (p); - } + printf("Unable to locate known reference file %s\n", KNOWNREF); - printf(" Checked directory %s\n", ref); - printf(" Checked directory %s/test\n", ref); - printf(" Checked directory %s/%s/test\n", ref, LIBRARY); + printf(" Checked following directories:\n%s\n", tried); exit(1); + +success: + free(p); + free(pwd); + return strdup(buff); } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i, tests_run = 0, tests_failed = 0, opt; time_t now; char *refdir_alloc = NULL; char *progname, *p; const char *tmp; char tmpdir[256]; char tmpdir_timestamp[256]; /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; while (*p != '\0') { if (*p == '/') progname = p + 1; ++p; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprog = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options. */ while ((opt = getopt(argc, argv, "dkp:qr:v")) != -1) { switch (opt) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprog = optarg; #else usage(progname); #endif break; case 'q': quiet_flag++; break; case 'r': refdir = optarg; break; case 'v': verbose = 1; break; case '?': default: usage(progname); } } argc -= optind; argv += optind; /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprog == NULL) usage(progname); #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; i < 1000; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); sprintf(tmpdir, "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (mkdir(tmpdir,0755) == 0) break; if (errno == EEXIST) continue; fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ if (refdir == NULL) refdir = refdir_alloc = get_refdir(tmpdir); /* * Banner with basic information. */ if (!quiet_flag) { printf("Running tests in: %s\n", tmpdir); printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } /* * Run some or all of the individual tests. */ if (argc == 0) { /* Default: Run all tests. */ for (i = 0; i < limit; i++) { if (test_run(i, tmpdir)) tests_failed++; tests_run++; } } else { while (*(argv) != NULL) { i = atoi(*argv); if (**argv < '0' || **argv > '9' || i < 0 || i >= limit) { printf("*** INVALID Test %s\n", *argv); usage(progname); } else { if (test_run(i, tmpdir)) tests_failed++; tests_run++; } argv++; } } /* * Report summary statistics. */ if (!quiet_flag) { printf("\n"); printf("%d of %d tests reported failures\n", tests_failed, tests_run); printf(" Total of %d assertions checked.\n", assertions); printf(" Total of %d assertions failed.\n", failures); printf(" Total of %d assertions skipped.\n", skips); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ rmdir(tmpdir); return (tests_failed); } Index: projects/cambria/libexec/ftpd/extern.h =================================================================== --- projects/cambria/libexec/ftpd/extern.h (revision 186459) +++ projects/cambria/libexec/ftpd/extern.h (revision 186460) @@ -1,89 +1,89 @@ /*- * Copyright (c) 1992, 1993 * 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. * * @(#)extern.h 8.2 (Berkeley) 4/4/94 * $FreeBSD$ */ #include #include void blkfree(char **); char **copyblk(char **); void cwd(char *); void delete(char *); void dologout(int); void fatalerror(char *); void ftpd_logwtmp(char *, char *, struct sockaddr *addr); int ftpd_pclose(FILE *); FILE *ftpd_popen(char *, char *); -char *getline(char *, int, FILE *); +int getline(char *, int, FILE *); void lreply(int, const char *, ...) __printflike(2, 3); void makedir(char *); void nack(char *); void pass(char *); void passive(void); void long_passive(char *, int); void perror_reply(int, char *); void pwd(void); void removedir(char *); void renamecmd(char *, char *); char *renamefrom(char *); void reply(int, const char *, ...) __printflike(2, 3); void retrieve(char *, char *); void send_file_list(char *); #ifdef OLD_SETPROCTITLE void setproctitle(const char *, ...); #endif void statcmd(void); void statfilecmd(char *); void store(char *, char *, int); void upper(char *); void user(char *); void yyerror(char *); int yyparse(void); int ls_main(int, char **); struct sockaddr_in; struct sockaddr_in6; union sockunion { struct sockinet { u_char si_len; u_char si_family; u_short si_port; } su_si; struct sockaddr_in su_sin; struct sockaddr_in6 su_sin6; }; #define su_len su_si.si_len #define su_family su_si.si_family #define su_port su_si.si_port Index: projects/cambria/libexec/ftpd/ftpcmd.y =================================================================== --- projects/cambria/libexec/ftpd/ftpcmd.y (revision 186459) +++ projects/cambria/libexec/ftpd/ftpcmd.y (revision 186460) @@ -1,1828 +1,1844 @@ /* * Copyright (c) 1985, 1988, 1993, 1994 * 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. * * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 */ /* * Grammar for FTP commands. * See RFC 959. */ %{ #ifndef lint #if 0 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #include "pathnames.h" extern union sockunion data_dest, his_addr; extern int hostinfo; extern int logged_in; extern struct passwd *pw; extern int guest; extern char *homedir; extern int paranoid; extern int logging; extern int type; extern int form; extern int ftpdebug; extern int timeout; extern int maxtimeout; extern int pdata; extern char *hostname; extern char proctitle[]; extern int usedefault; extern char tmpline[]; extern int readonly; extern int assumeutf8; extern int noepsv; extern int noretr; extern int noguestretr; extern char *typenames[]; /* defined in included from ftpd.c */ off_t restart_point; static int cmd_type; static int cmd_form; static int cmd_bytesz; static int state; char cbuf[512]; char *fromname = NULL; extern int epsvall; %} %union { struct { off_t o; int i; } u; char *s; } %token A B C E F I L N P R S T ALL SP CRLF COMMA USER PASS ACCT REIN QUIT PORT PASV TYPE STRU MODE RETR STOR APPE MLFL MAIL MSND MSOM MSAM MRSQ MRCP ALLO REST RNFR RNTO ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM LPRT LPSV EPRT EPSV FEAT UMASK IDLE CHMOD MDFIVE LEXERR NOTIMPL %token STRING %token NUMBER %type check_login octal_number byte_size %type check_login_ro check_login_epsv %type struct_code mode_code type_code form_code %type pathstring pathname password username %type ALL NOTIMPL %start cmd_list %% cmd_list : /* empty */ | cmd_list cmd { if (fromname) free(fromname); fromname = NULL; restart_point = 0; } | cmd_list rcmd ; cmd : USER SP username CRLF { user($3); free($3); } | PASS SP password CRLF { pass($3); free($3); } | PASS CRLF { pass(""); } | PORT check_login SP host_port CRLF { if (epsvall) { reply(501, "No PORT allowed after EPSV ALL."); goto port_done; } if (!$2) goto port_done; if (port_check("PORT") == 1) goto port_done; #ifdef INET6 if ((his_addr.su_family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { /* shoud never happen */ usedefault = 1; reply(500, "Invalid address rejected."); goto port_done; } port_check_v6("pcmd"); #endif port_done: ; } | LPRT check_login SP host_long_port CRLF { if (epsvall) { reply(501, "No LPRT allowed after EPSV ALL."); goto lprt_done; } if (!$2) goto lprt_done; if (port_check("LPRT") == 1) goto lprt_done; #ifdef INET6 if (his_addr.su_family != AF_INET6) { usedefault = 1; reply(500, "Invalid address rejected."); goto lprt_done; } if (port_check_v6("LPRT") == 1) goto lprt_done; #endif lprt_done: ; } | EPRT check_login SP STRING CRLF { char delim; char *tmp = NULL; char *p, *q; char *result[3]; struct addrinfo hints; struct addrinfo *res; int i; if (epsvall) { reply(501, "No EPRT allowed after EPSV ALL."); goto eprt_done; } if (!$2) goto eprt_done; memset(&data_dest, 0, sizeof(data_dest)); tmp = strdup($4); if (ftpdebug) syslog(LOG_DEBUG, "%s", tmp); if (!tmp) { fatalerror("not enough core"); /*NOTREACHED*/ } p = tmp; delim = p[0]; p++; memset(result, 0, sizeof(result)); for (i = 0; i < 3; i++) { q = strchr(p, delim); if (!q || *q != delim) { parsefail: reply(500, "Invalid argument, rejected."); if (tmp) free(tmp); usedefault = 1; goto eprt_done; } *q++ = '\0'; result[i] = p; if (ftpdebug) syslog(LOG_DEBUG, "%d: %s", i, p); p = q; } /* some more sanity check */ p = result[0]; while (*p) { if (!isdigit(*p)) goto parsefail; p++; } p = result[2]; while (*p) { if (!isdigit(*p)) goto parsefail; p++; } /* grab address */ memset(&hints, 0, sizeof(hints)); if (atoi(result[0]) == 1) hints.ai_family = PF_INET; #ifdef INET6 else if (atoi(result[0]) == 2) hints.ai_family = PF_INET6; #endif else hints.ai_family = PF_UNSPEC; /*XXX*/ hints.ai_socktype = SOCK_STREAM; i = getaddrinfo(result[1], result[2], &hints, &res); if (i) goto parsefail; memcpy(&data_dest, res->ai_addr, res->ai_addrlen); #ifdef INET6 if (his_addr.su_family == AF_INET6 && data_dest.su_family == AF_INET6) { /* XXX more sanity checks! */ data_dest.su_sin6.sin6_scope_id = his_addr.su_sin6.sin6_scope_id; } #endif free(tmp); tmp = NULL; if (port_check("EPRT") == 1) goto eprt_done; #ifdef INET6 if (his_addr.su_family != AF_INET6) { usedefault = 1; reply(500, "Invalid address rejected."); goto eprt_done; } if (port_check_v6("EPRT") == 1) goto eprt_done; #endif eprt_done: free($4); } | PASV check_login CRLF { if (epsvall) reply(501, "No PASV allowed after EPSV ALL."); else if ($2) passive(); } | LPSV check_login CRLF { if (epsvall) reply(501, "No LPSV allowed after EPSV ALL."); else if ($2) long_passive("LPSV", PF_UNSPEC); } | EPSV check_login_epsv SP NUMBER CRLF { if ($2) { int pf; switch ($4.i) { case 1: pf = PF_INET; break; #ifdef INET6 case 2: pf = PF_INET6; break; #endif default: pf = -1; /*junk value*/ break; } long_passive("EPSV", pf); } } | EPSV check_login_epsv SP ALL CRLF { if ($2) { reply(200, "EPSV ALL command successful."); epsvall++; } } | EPSV check_login_epsv CRLF { if ($2) long_passive("EPSV", PF_UNSPEC); } | TYPE check_login SP type_code CRLF { if ($2) { switch (cmd_type) { case TYPE_A: if (cmd_form == FORM_N) { reply(200, "Type set to A."); type = cmd_type; form = cmd_form; } else reply(504, "Form must be N."); break; case TYPE_E: reply(504, "Type E not implemented."); break; case TYPE_I: reply(200, "Type set to I."); type = cmd_type; break; case TYPE_L: #if CHAR_BIT == 8 if (cmd_bytesz == 8) { reply(200, "Type set to L (byte size 8)."); type = cmd_type; } else reply(504, "Byte size must be 8."); #else /* CHAR_BIT == 8 */ UNIMPLEMENTED for CHAR_BIT != 8 #endif /* CHAR_BIT == 8 */ } } } | STRU check_login SP struct_code CRLF { if ($2) { switch ($4) { case STRU_F: reply(200, "STRU F accepted."); break; default: reply(504, "Unimplemented STRU type."); } } } | MODE check_login SP mode_code CRLF { if ($2) { switch ($4) { case MODE_S: reply(200, "MODE S accepted."); break; default: reply(502, "Unimplemented MODE type."); } } } | ALLO check_login SP NUMBER CRLF { if ($2) { reply(202, "ALLO command ignored."); } } | ALLO check_login SP NUMBER SP R SP NUMBER CRLF { if ($2) { reply(202, "ALLO command ignored."); } } | RETR check_login SP pathname CRLF { if (noretr || (guest && noguestretr)) reply(500, "RETR command disabled."); else if ($2 && $4 != NULL) retrieve(NULL, $4); if ($4 != NULL) free($4); } | STOR check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 0); if ($4 != NULL) free($4); } | APPE check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) store($4, "a", 0); if ($4 != NULL) free($4); } | NLST check_login CRLF { if ($2) send_file_list("."); } | NLST check_login SP pathstring CRLF { if ($2) send_file_list($4); free($4); } | LIST check_login CRLF { if ($2) retrieve(_PATH_LS " -lgA", ""); } | LIST check_login SP pathstring CRLF { if ($2) retrieve(_PATH_LS " -lgA %s", $4); free($4); } | STAT check_login SP pathname CRLF { if ($2 && $4 != NULL) statfilecmd($4); if ($4 != NULL) free($4); } | STAT check_login CRLF { if ($2) { statcmd(); } } | DELE check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) delete($4); if ($4 != NULL) free($4); } | RNTO check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) { if (fromname) { renamecmd(fromname, $4); free(fromname); fromname = NULL; } else { reply(503, "Bad sequence of commands."); } } if ($4 != NULL) free($4); } | ABOR check_login CRLF { if ($2) reply(225, "ABOR command successful."); } | CWD check_login CRLF { if ($2) { cwd(homedir); } } | CWD check_login SP pathname CRLF { if ($2 && $4 != NULL) cwd($4); if ($4 != NULL) free($4); } | HELP CRLF { help(cmdtab, NULL); } | HELP SP STRING CRLF { char *cp = $3; if (strncasecmp(cp, "SITE", 4) == 0) { cp = $3 + 4; if (*cp == ' ') cp++; if (*cp) help(sitetab, cp); else help(sitetab, NULL); } else help(cmdtab, $3); free($3); } | NOOP CRLF { reply(200, "NOOP command successful."); } | MKD check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) makedir($4); if ($4 != NULL) free($4); } | RMD check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) removedir($4); if ($4 != NULL) free($4); } | PWD check_login CRLF { if ($2) pwd(); } | CDUP check_login CRLF { if ($2) cwd(".."); } | SITE SP HELP CRLF { help(sitetab, NULL); } | SITE SP HELP SP STRING CRLF { help(sitetab, $5); free($5); } | SITE SP MDFIVE check_login SP pathname CRLF { char p[64], *q; if ($4 && $6) { q = MD5File($6, p); if (q != NULL) reply(200, "MD5(%s) = %s", $6, p); else perror_reply(550, $6); } if ($6) free($6); } | SITE SP UMASK check_login CRLF { int oldmask; if ($4) { oldmask = umask(0); (void) umask(oldmask); reply(200, "Current UMASK is %03o.", oldmask); } } | SITE SP UMASK check_login SP octal_number CRLF { int oldmask; if ($4) { if (($6 == -1) || ($6 > 0777)) { reply(501, "Bad UMASK value."); } else { oldmask = umask($6); reply(200, "UMASK set to %03o (was %03o).", $6, oldmask); } } } | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF { if ($4 && ($8 != NULL)) { if (($6 == -1 ) || ($6 > 0777)) reply(501, "Bad mode value."); else if (chmod($8, $6) < 0) perror_reply(550, $8); else reply(200, "CHMOD command successful."); } if ($8 != NULL) free($8); } | SITE SP check_login IDLE CRLF { if ($3) reply(200, "Current IDLE time limit is %d seconds; max %d.", timeout, maxtimeout); } | SITE SP check_login IDLE SP NUMBER CRLF { if ($3) { if ($6.i < 30 || $6.i > maxtimeout) { reply(501, "Maximum IDLE time must be between 30 and %d seconds.", maxtimeout); } else { timeout = $6.i; (void) alarm(timeout); reply(200, "Maximum IDLE time set to %d seconds.", timeout); } } } | STOU check_login_ro SP pathname CRLF { if ($2 && $4 != NULL) store($4, "w", 1); if ($4 != NULL) free($4); } | FEAT CRLF { lreply(211, "Extensions supported:"); #if 0 /* XXX these two keywords are non-standard */ printf(" EPRT\r\n"); if (!noepsv) printf(" EPSV\r\n"); #endif printf(" MDTM\r\n"); printf(" REST STREAM\r\n"); printf(" SIZE\r\n"); if (assumeutf8) { /* TVFS requires UTF8, see RFC 3659 */ printf(" TVFS\r\n"); printf(" UTF8\r\n"); } reply(211, "End."); } | SYST check_login CRLF { if ($2) { if (hostinfo) #ifdef BSD reply(215, "UNIX Type: L%d Version: BSD-%d", CHAR_BIT, BSD); #else /* BSD */ reply(215, "UNIX Type: L%d", CHAR_BIT); #endif /* BSD */ else reply(215, "UNKNOWN Type: L%d", CHAR_BIT); } } /* * SIZE is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return size of file in a format suitable for * using with RESTART (we just count bytes). */ | SIZE check_login SP pathname CRLF { if ($2 && $4 != NULL) sizecmd($4); if ($4 != NULL) free($4); } /* * MDTM is not in RFC959, but Postel has blessed it and * it will be in the updated RFC. * * Return modification time of file as an ISO 3307 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx * where xxx is the fractional second (of any precision, * not necessarily 3 digits) */ | MDTM check_login SP pathname CRLF { if ($2 && $4 != NULL) { struct stat stbuf; if (stat($4, &stbuf) < 0) perror_reply(550, $4); else if (!S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", $4); } else { struct tm *t; t = gmtime(&stbuf.st_mtime); reply(213, "%04d%02d%02d%02d%02d%02d", 1900 + t->tm_year, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); } } if ($4 != NULL) free($4); } | QUIT CRLF { reply(221, "Goodbye."); dologout(0); } | NOTIMPL { nack($1); } | error { yyclearin; /* discard lookahead data */ yyerrok; /* clear error condition */ state = CMD; /* reset lexer state */ } ; rcmd : RNFR check_login_ro SP pathname CRLF { restart_point = 0; if ($2 && $4) { if (fromname) free(fromname); fromname = NULL; if (renamefrom($4)) fromname = $4; else free($4); } else if ($4) { free($4); } } | REST check_login SP NUMBER CRLF { if ($2) { if (fromname) free(fromname); fromname = NULL; restart_point = $4.o; reply(350, "Restarting at %jd. %s", (intmax_t)restart_point, "Send STORE or RETRIEVE to initiate transfer."); } } ; username : STRING ; password : /* empty */ { $$ = (char *)calloc(1, sizeof(char)); } | STRING ; byte_size : NUMBER { $$ = $1.i; } ; host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; data_dest.su_len = sizeof(struct sockaddr_in); data_dest.su_family = AF_INET; p = (char *)&data_dest.su_sin.sin_port; p[0] = $9.i; p[1] = $11.i; a = (char *)&data_dest.su_sin.sin_addr; a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; } ; host_long_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; memset(&data_dest, 0, sizeof(data_dest)); data_dest.su_len = sizeof(struct sockaddr_in6); data_dest.su_family = AF_INET6; p = (char *)&data_dest.su_port; p[0] = $39.i; p[1] = $41.i; a = (char *)&data_dest.su_sin6.sin6_addr; a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; if (his_addr.su_family == AF_INET6) { /* XXX more sanity checks! */ data_dest.su_sin6.sin6_scope_id = his_addr.su_sin6.sin6_scope_id; } if ($1.i != 6 || $3.i != 16 || $37.i != 2) memset(&data_dest, 0, sizeof(data_dest)); } | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER { char *a, *p; memset(&data_dest, 0, sizeof(data_dest)); data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); data_dest.su_family = AF_INET; p = (char *)&data_dest.su_port; p[0] = $15.i; p[1] = $17.i; a = (char *)&data_dest.su_sin.sin_addr; a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; if ($1.i != 4 || $3.i != 4 || $13.i != 2) memset(&data_dest, 0, sizeof(data_dest)); } ; form_code : N { $$ = FORM_N; } | T { $$ = FORM_T; } | C { $$ = FORM_C; } ; type_code : A { cmd_type = TYPE_A; cmd_form = FORM_N; } | A SP form_code { cmd_type = TYPE_A; cmd_form = $3; } | E { cmd_type = TYPE_E; cmd_form = FORM_N; } | E SP form_code { cmd_type = TYPE_E; cmd_form = $3; } | I { cmd_type = TYPE_I; } | L { cmd_type = TYPE_L; cmd_bytesz = CHAR_BIT; } | L SP byte_size { cmd_type = TYPE_L; cmd_bytesz = $3; } /* this is for a bug in the BBN ftp */ | L byte_size { cmd_type = TYPE_L; cmd_bytesz = $2; } ; struct_code : F { $$ = STRU_F; } | R { $$ = STRU_R; } | P { $$ = STRU_P; } ; mode_code : S { $$ = MODE_S; } | B { $$ = MODE_B; } | C { $$ = MODE_C; } ; pathname : pathstring { if (logged_in && $1) { char *p; /* * Expand ~user manually since glob(3) * will return the unexpanded pathname * if the corresponding file/directory * doesn't exist yet. Using sole glob(3) * would break natural commands like * MKD ~user/newdir * or * RNTO ~/newfile */ if ((p = exptilde($1)) != NULL) { $$ = expglob(p); free(p); } else $$ = NULL; free($1); } else $$ = $1; } ; pathstring : STRING ; octal_number : NUMBER { int ret, dec, multby, digit; /* * Convert a number that was read as decimal number * to what it would be if it had been read as octal. */ dec = $1.i; multby = 1; ret = 0; while (dec) { digit = dec%10; if (digit > 7) { ret = -1; break; } ret += digit * multby; multby *= 8; dec /= 10; } $$ = ret; } ; check_login : /* empty */ { $$ = check_login1(); } ; check_login_epsv : /* empty */ { if (noepsv) { reply(500, "EPSV command disabled."); $$ = 0; } else $$ = check_login1(); } ; check_login_ro : /* empty */ { if (readonly) { reply(550, "Permission denied."); $$ = 0; } else $$ = check_login1(); } ; %% #define CMD 0 /* beginning of command */ #define ARGS 1 /* expect miscellaneous arguments */ #define STR1 2 /* expect SP followed by STRING */ #define STR2 3 /* expect STRING */ #define OSTR 4 /* optional SP then STRING */ #define ZSTR1 5 /* optional SP then optional STRING */ #define ZSTR2 6 /* optional STRING after SP */ #define SITECMD 7 /* SITE command */ #define NSTR 8 /* Number followed by a string */ #define MAXGLOBARGS 1000 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ struct tab { char *name; short token; short state; short implemented; /* 1 if command is implemented */ char *help; }; struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "USER", USER, STR1, 1, " username" }, { "PASS", PASS, ZSTR1, 1, "[ [password]]" }, { "ACCT", ACCT, STR1, 0, "(specify account)" }, { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4, b5" }, { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2..." }, { "EPRT", EPRT, STR1, 1, " |af|addr|port|" }, { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, { "EPSV", EPSV, ARGS, 1, "[ af|ALL]" }, { "TYPE", TYPE, ARGS, 1, " { A | E | I | L }" }, { "STRU", STRU, ARGS, 1, "(specify file structure)" }, { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, { "RETR", RETR, STR1, 1, " file-name" }, { "STOR", STOR, STR1, 1, " file-name" }, { "APPE", APPE, STR1, 1, " file-name" }, { "MLFL", MLFL, OSTR, 0, "(mail file)" }, { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, { "REST", REST, ARGS, 1, " offset (restart command)" }, { "RNFR", RNFR, STR1, 1, " file-name" }, { "RNTO", RNTO, STR1, 1, " file-name" }, { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, { "DELE", DELE, STR1, 1, " file-name" }, { "CWD", CWD, OSTR, 1, "[ directory-name ]" }, { "XCWD", CWD, OSTR, 1, "[ directory-name ]" }, { "LIST", LIST, OSTR, 1, "[ path-name ]" }, { "NLST", NLST, OSTR, 1, "[ path-name ]" }, { "SITE", SITE, SITECMD, 1, "site-cmd [ arguments ]" }, { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, { "STAT", STAT, OSTR, 1, "[ path-name ]" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { "NOOP", NOOP, ARGS, 1, "" }, { "MKD", MKD, STR1, 1, " path-name" }, { "XMKD", MKD, STR1, 1, " path-name" }, { "RMD", RMD, STR1, 1, " path-name" }, { "XRMD", RMD, STR1, 1, " path-name" }, { "PWD", PWD, ARGS, 1, "(return current directory)" }, { "XPWD", PWD, ARGS, 1, "(return current directory)" }, { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, { "STOU", STOU, STR1, 1, " file-name" }, { "SIZE", SIZE, OSTR, 1, " path-name" }, { "MDTM", MDTM, OSTR, 1, " path-name" }, { NULL, 0, 0, 0, 0 } }; struct tab sitetab[] = { { "MD5", MDFIVE, STR1, 1, "[ file-name ]" }, { "UMASK", UMASK, ARGS, 1, "[ umask ]" }, { "IDLE", IDLE, ARGS, 1, "[ maximum-idle-time ]" }, { "CHMOD", CHMOD, NSTR, 1, " mode file-name" }, { "HELP", HELP, OSTR, 1, "[ ]" }, { NULL, 0, 0, 0, 0 } }; static char *copy(char *); static char *expglob(char *); static char *exptilde(char *); static void help(struct tab *, char *); static struct tab * lookup(struct tab *, char *); static int port_check(const char *); #ifdef INET6 static int port_check_v6(const char *); #endif static void sizecmd(char *); static void toolong(int); #ifdef INET6 static void v4map_data_dest(void); #endif static int yylex(void); static struct tab * lookup(struct tab *p, char *cmd) { for (; p->name != NULL; p++) if (strcmp(cmd, p->name) == 0) return (p); return (0); } #include /* * getline - a hacked up version of fgets to ignore TELNET escape codes. */ -char * +int getline(char *s, int n, FILE *iop) { int c; register char *cs; sigset_t sset, osset; cs = s; /* tmpline may contain saved command from urgent mode interruption */ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { *cs++ = tmpline[c]; if (tmpline[c] == '\n') { *cs++ = '\0'; if (ftpdebug) syslog(LOG_DEBUG, "command: %s", s); tmpline[0] = '\0'; - return(s); + return(0); } if (c == 0) tmpline[0] = '\0'; } /* SIGURG would interrupt stdio if not blocked during the read loop */ sigemptyset(&sset); sigaddset(&sset, SIGURG); sigprocmask(SIG_BLOCK, &sset, &osset); while ((c = getc(iop)) != EOF) { c &= 0377; if (c == IAC) { if ((c = getc(iop)) == EOF) goto got_eof; c &= 0377; switch (c) { case WILL: case WONT: if ((c = getc(iop)) == EOF) goto got_eof; printf("%c%c%c", IAC, DONT, 0377&c); (void) fflush(stdout); continue; case DO: case DONT: if ((c = getc(iop)) == EOF) goto got_eof; printf("%c%c%c", IAC, WONT, 0377&c); (void) fflush(stdout); continue; case IAC: break; default: continue; /* ignore command */ } } *cs++ = c; - if (--n <= 0 || c == '\n') + if (--n <= 0) { + /* + * If command doesn't fit into buffer, discard the + * rest of the command and indicate truncation. + * This prevents the command to be split up into + * multiple commands. + */ + while (c != '\n' && (c = getc(iop)) != EOF) + ; + return (-2); + } + if (c == '\n') break; } got_eof: sigprocmask(SIG_SETMASK, &osset, NULL); if (c == EOF && cs == s) - return (NULL); + return (-1); *cs++ = '\0'; if (ftpdebug) { if (!guest && strncasecmp("pass ", s, 5) == 0) { /* Don't syslog passwords */ syslog(LOG_DEBUG, "command: %.5s ???", s); } else { register char *cp; register int len; /* Don't syslog trailing CR-LF */ len = strlen(s); cp = s + len - 1; while (cp >= s && (*cp == '\n' || *cp == '\r')) { --cp; --len; } syslog(LOG_DEBUG, "command: %.*s", len, s); } } - return (s); + return (0); } static void toolong(int signo) { reply(421, "Timeout (%d seconds): closing control connection.", timeout); if (logging) syslog(LOG_INFO, "User %s timed out after %d seconds", (pw ? pw -> pw_name : "unknown"), timeout); dologout(1); } static int yylex(void) { static int cpos; char *cp, *cp2; struct tab *p; int n; char c; for (;;) { switch (state) { case CMD: (void) signal(SIGALRM, toolong); (void) alarm(timeout); - if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { + n = getline(cbuf, sizeof(cbuf)-1, stdin); + if (n == -1) { reply(221, "You could at least say goodbye."); dologout(0); + } else if (n == -2) { + reply(500, "Command too long."); + (void) alarm(0); + continue; } (void) alarm(0); #ifdef SETPROCTITLE if (strncasecmp(cbuf, "PASS", 4) != 0) setproctitle("%s: %s", proctitle, cbuf); #endif /* SETPROCTITLE */ if ((cp = strchr(cbuf, '\r'))) { *cp++ = '\n'; *cp = '\0'; } if ((cp = strpbrk(cbuf, " \n"))) cpos = cp - cbuf; if (cpos == 0) cpos = 4; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cbuf); p = lookup(cmdtab, cbuf); cbuf[cpos] = c; if (p != 0) { yylval.s = p->name; if (!p->implemented) return (NOTIMPL); /* state remains CMD */ state = p->state; return (p->token); } break; case SITECMD: if (cbuf[cpos] == ' ') { cpos++; return (SP); } cp = &cbuf[cpos]; if ((cp2 = strpbrk(cp, " \n"))) cpos = cp2 - cbuf; c = cbuf[cpos]; cbuf[cpos] = '\0'; upper(cp); p = lookup(sitetab, cp); cbuf[cpos] = c; if (guest == 0 && p != 0) { yylval.s = p->name; if (!p->implemented) { state = CMD; return (NOTIMPL); } state = p->state; return (p->token); } state = CMD; break; case ZSTR1: case OSTR: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR1: dostr1: if (cbuf[cpos] == ' ') { cpos++; state = state == OSTR ? STR2 : state+1; return (SP); } break; case ZSTR2: if (cbuf[cpos] == '\n') { state = CMD; return (CRLF); } /* FALLTHROUGH */ case STR2: cp = &cbuf[cpos]; n = strlen(cp); cpos += n - 1; /* * Make sure the string is nonempty and \n terminated. */ if (n > 1 && cbuf[cpos] == '\n') { cbuf[cpos] = '\0'; yylval.s = copy(cp); cbuf[cpos] = '\n'; state = ARGS; return (STRING); } break; case NSTR: if (cbuf[cpos] == ' ') { cpos++; return (SP); } if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.u.i = atoi(cp); cbuf[cpos] = c; state = STR1; return (NUMBER); } state = STR1; goto dostr1; case ARGS: if (isdigit(cbuf[cpos])) { cp = &cbuf[cpos]; while (isdigit(cbuf[++cpos])) ; c = cbuf[cpos]; cbuf[cpos] = '\0'; yylval.u.i = atoi(cp); yylval.u.o = strtoull(cp, NULL, 10); cbuf[cpos] = c; return (NUMBER); } if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 && !isalnum(cbuf[cpos + 3])) { cpos += 3; return ALL; } switch (cbuf[cpos++]) { case '\n': state = CMD; return (CRLF); case ' ': return (SP); case ',': return (COMMA); case 'A': case 'a': return (A); case 'B': case 'b': return (B); case 'C': case 'c': return (C); case 'E': case 'e': return (E); case 'F': case 'f': return (F); case 'I': case 'i': return (I); case 'L': case 'l': return (L); case 'N': case 'n': return (N); case 'P': case 'p': return (P); case 'R': case 'r': return (R); case 'S': case 's': return (S); case 'T': case 't': return (T); } break; default: fatalerror("Unknown state in scanner."); } state = CMD; return (LEXERR); } } void upper(char *s) { while (*s != '\0') { if (islower(*s)) *s = toupper(*s); s++; } } static char * copy(char *s) { char *p; p = malloc(strlen(s) + 1); if (p == NULL) fatalerror("Ran out of memory."); (void) strcpy(p, s); return (p); } static void help(struct tab *ctab, char *s) { struct tab *c; int width, NCMDS; char *type; if (ctab == sitetab) type = "SITE "; else type = ""; width = 0, NCMDS = 0; for (c = ctab; c->name != NULL; c++) { int len = strlen(c->name); if (len > width) width = len; NCMDS++; } width = (width + 8) &~ 7; if (s == 0) { int i, j, w; int columns, lines; lreply(214, "The following %scommands are recognized %s.", type, "(* =>'s unimplemented)"); columns = 76 / width; if (columns == 0) columns = 1; lines = (NCMDS + columns - 1) / columns; for (i = 0; i < lines; i++) { printf(" "); for (j = 0; j < columns; j++) { c = ctab + j * lines + i; printf("%s%c", c->name, c->implemented ? ' ' : '*'); if (c + lines >= &ctab[NCMDS]) break; w = strlen(c->name) + 1; while (w < width) { putchar(' '); w++; } } printf("\r\n"); } (void) fflush(stdout); if (hostinfo) reply(214, "Direct comments to ftp-bugs@%s.", hostname); else reply(214, "End."); return; } upper(s); c = lookup(ctab, s); if (c == NULL) { reply(502, "Unknown command %s.", s); return; } if (c->implemented) reply(214, "Syntax: %s%s %s", type, c->name, c->help); else reply(214, "%s%-*s\t%s; unimplemented.", type, width, c->name, c->help); } static void sizecmd(char *filename) { switch (type) { case TYPE_L: case TYPE_I: { struct stat stbuf; if (stat(filename, &stbuf) < 0) perror_reply(550, filename); else if (!S_ISREG(stbuf.st_mode)) reply(550, "%s: not a plain file.", filename); else reply(213, "%jd", (intmax_t)stbuf.st_size); break; } case TYPE_A: { FILE *fin; int c; off_t count; struct stat stbuf; fin = fopen(filename, "r"); if (fin == NULL) { perror_reply(550, filename); return; } if (fstat(fileno(fin), &stbuf) < 0) { perror_reply(550, filename); (void) fclose(fin); return; } else if (!S_ISREG(stbuf.st_mode)) { reply(550, "%s: not a plain file.", filename); (void) fclose(fin); return; } else if (stbuf.st_size > MAXASIZE) { reply(550, "%s: too large for type A SIZE.", filename); (void) fclose(fin); return; } count = 0; while((c=getc(fin)) != EOF) { if (c == '\n') /* will get expanded to \r\n */ count++; count++; } (void) fclose(fin); reply(213, "%jd", (intmax_t)count); break; } default: reply(504, "SIZE not implemented for type %s.", typenames[type]); } } /* Return 1, if port check is done. Return 0, if not yet. */ static int port_check(const char *pcmd) { if (his_addr.su_family == AF_INET) { if (data_dest.su_family != AF_INET) { usedefault = 1; reply(500, "Invalid address rejected."); return 1; } if (paranoid && ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || memcmp(&data_dest.su_sin.sin_addr, &his_addr.su_sin.sin_addr, sizeof(data_dest.su_sin.sin_addr)))) { usedefault = 1; reply(500, "Illegal PORT range rejected."); } else { usedefault = 0; if (pdata >= 0) { (void) close(pdata); pdata = -1; } reply(200, "%s command successful.", pcmd); } return 1; } return 0; } static int check_login1(void) { if (logged_in) return 1; else { reply(530, "Please login with USER and PASS."); return 0; } } /* * Replace leading "~user" in a pathname by the user's login directory. * Returned string will be in a freshly malloced buffer unless it's NULL. */ static char * exptilde(char *s) { char *p, *q; char *path, *user; struct passwd *ppw; if ((p = strdup(s)) == NULL) return (NULL); if (*p != '~') return (p); user = p + 1; /* skip tilde */ if ((path = strchr(p, '/')) != NULL) *(path++) = '\0'; /* separate ~user from the rest of path */ if (*user == '\0') /* no user specified, use the current user */ user = pw->pw_name; /* read passwd even for the current user since we may be chrooted */ if ((ppw = getpwnam(user)) != NULL) { /* user found, substitute login directory for ~user */ if (path) asprintf(&q, "%s/%s", ppw->pw_dir, path); else q = strdup(ppw->pw_dir); free(p); p = q; } else { /* user not found, undo the damage */ if (path) path[-1] = '/'; } return (p); } /* * Expand glob(3) patterns possibly present in a pathname. * Avoid expanding to a pathname including '\r' or '\n' in order to * not disrupt the FTP protocol. * The expansion found must be unique. * Return the result as a malloced string, or NULL if an error occured. * * Problem: this production is used for all pathname * processing, but only gives a 550 error reply. * This is a valid reply in some cases but not in others. */ static char * expglob(char *s) { char *p, **pp, *rval; int flags = GLOB_BRACE | GLOB_NOCHECK; int n; glob_t gl; memset(&gl, 0, sizeof(gl)); flags |= GLOB_LIMIT; gl.gl_matchc = MAXGLOBARGS; if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { p = *pp; n++; } if (n == 0) rval = strdup(s); else if (n == 1) rval = strdup(p); else { reply(550, "Wildcard is ambiguous."); rval = NULL; } } else { reply(550, "Wildcard expansion error."); rval = NULL; } globfree(&gl); return (rval); } #ifdef INET6 /* Return 1, if port check is done. Return 0, if not yet. */ static int port_check_v6(const char *pcmd) { if (his_addr.su_family == AF_INET6) { if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) /* Convert data_dest into v4 mapped sockaddr.*/ v4map_data_dest(); if (data_dest.su_family != AF_INET6) { usedefault = 1; reply(500, "Invalid address rejected."); return 1; } if (paranoid && ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || memcmp(&data_dest.su_sin6.sin6_addr, &his_addr.su_sin6.sin6_addr, sizeof(data_dest.su_sin6.sin6_addr)))) { usedefault = 1; reply(500, "Illegal PORT range rejected."); } else { usedefault = 0; if (pdata >= 0) { (void) close(pdata); pdata = -1; } reply(200, "%s command successful.", pcmd); } return 1; } return 0; } static void v4map_data_dest(void) { struct in_addr savedaddr; int savedport; if (data_dest.su_family != AF_INET) { usedefault = 1; reply(500, "Invalid address rejected."); return; } savedaddr = data_dest.su_sin.sin_addr; savedport = data_dest.su_port; memset(&data_dest, 0, sizeof(data_dest)); data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); data_dest.su_sin6.sin6_family = AF_INET6; data_dest.su_sin6.sin6_port = savedport; memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], (caddr_t)&savedaddr, sizeof(savedaddr)); } #endif Index: projects/cambria/libexec/ftpd/ftpd.c =================================================================== --- projects/cambria/libexec/ftpd/ftpd.c (revision 186459) +++ projects/cambria/libexec/ftpd/ftpd.c (revision 186460) @@ -1,3466 +1,3471 @@ /* * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 * 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 0 #ifndef lint static char copyright[] = "@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #endif #ifndef lint #if 0 static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); /* * FTP server. */ #include #include #include #include #include #include #include #include #include #include #include #define FTP_NAMES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LOGIN_CAP #include #endif #ifdef USE_PAM #include #endif #include "pathnames.h" #include "extern.h" #include static char version[] = "Version 6.00LS"; #undef main extern off_t restart_point; extern char cbuf[]; union sockunion ctrl_addr; union sockunion data_source; union sockunion data_dest; union sockunion his_addr; union sockunion pasv_addr; int daemon_mode; int data; int dataport; int hostinfo = 1; /* print host-specific info in messages */ int logged_in; struct passwd *pw; char *homedir; int ftpdebug; int timeout = 900; /* timeout after 15 minutes of inactivity */ int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ int logging; int restricted_data_ports = 1; int paranoid = 1; /* be extra careful about security */ int anon_only = 0; /* Only anonymous ftp allowed */ int assumeutf8 = 0; /* Assume that server file names are in UTF-8 */ int guest; int dochroot; char *chrootdir; int dowtmp = 1; int stats; int statfd = -1; int type; int form; int stru; /* avoid C keyword */ int mode; int usedefault = 1; /* for data transfers */ int pdata = -1; /* for passive mode */ int readonly = 0; /* Server is in readonly mode. */ int noepsv = 0; /* EPSV command is disabled. */ int noretr = 0; /* RETR command is disabled. */ int noguestretr = 0; /* RETR command is disabled for anon users. */ int noguestmkd = 0; /* MKD command is disabled for anon users. */ int noguestmod = 1; /* anon users may not modify existing files. */ off_t file_size; off_t byte_count; #if !defined(CMASK) || CMASK == 0 #undef CMASK #define CMASK 027 #endif int defumask = CMASK; /* default umask value */ char tmpline[7]; char *hostname; int epsvall = 0; #ifdef VIRTUAL_HOSTING char *ftpuser; static struct ftphost { struct ftphost *next; struct addrinfo *hostinfo; char *hostname; char *anonuser; char *statfile; char *welcome; char *loginmsg; } *thishost, *firsthost; #endif char remotehost[NI_MAXHOST]; char *ident = NULL; static char ttyline[20]; char *tty = ttyline; /* for klogin */ #ifdef USE_PAM static int auth_pam(struct passwd**, const char*); pam_handle_t *pamh = NULL; #endif static struct opie opiedata; static char opieprompt[OPIE_CHALLENGE_MAX+1]; static int pwok; char *pid_file = NULL; /* means default location to pidfile(3) */ /* * Limit number of pathnames that glob can return. * A limit of 0 indicates the number of pathnames is unlimited. */ #define MAXGLOBARGS 16384 # /* * Timeout intervals for retrying connections * to hosts that don't accept PORT cmds. This * is a kludge, but given the problems with TCP... */ #define SWAITMAX 90 /* wait at most 90 seconds */ #define SWAITINT 5 /* interval between retries */ int swaitmax = SWAITMAX; int swaitint = SWAITINT; #ifdef SETPROCTITLE #ifdef OLD_SETPROCTITLE char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ #endif /* OLD_SETPROCTITLE */ char proctitle[LINE_MAX]; /* initial part of title */ #endif /* SETPROCTITLE */ #define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1) #define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1) #define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt)) static volatile sig_atomic_t recvurg; static int transflag; /* NB: for debugging only */ #define STARTXFER flagxfer(1) #define ENDXFER flagxfer(0) #define START_UNSAFE maskurg(1) #define END_UNSAFE maskurg(0) /* It's OK to put an `else' clause after this macro. */ #define CHECKOOB(action) \ if (recvurg) { \ recvurg = 0; \ if (myoob()) { \ ENDXFER; \ action; \ } \ } #ifdef VIRTUAL_HOSTING static void inithosts(int); static void selecthost(union sockunion *); #endif static void ack(char *); static void sigurg(int); static void maskurg(int); static void flagxfer(int); static int myoob(void); static int checkuser(char *, char *, int, char **); static FILE *dataconn(char *, off_t, char *); static void dolog(struct sockaddr *); static void end_login(void); static FILE *getdatasock(char *); static int guniquefd(char *, char **); static void lostconn(int); static void sigquit(int); static int receive_data(FILE *, FILE *); static int send_data(FILE *, FILE *, size_t, off_t, int); static struct passwd * sgetpwnam(char *); static char *sgetsave(char *); static void reapchild(int); static void appendf(char **, char *, ...) __printflike(2, 3); static void logcmd(char *, char *, char *, off_t); static void logxfer(char *, off_t, time_t); static char *doublequote(char *); static int *socksetup(int, char *, const char *); int main(int argc, char *argv[], char **envp) { socklen_t addrlen; int ch, on = 1, tos; char *cp, line[LINE_MAX]; FILE *fd; char *bindname = NULL; const char *bindport = "ftp"; int family = AF_UNSPEC; struct sigaction sa; tzset(); /* in case no timezone database in ~ftp */ sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; #ifdef OLD_SETPROCTITLE /* * Save start and extent of argv for setproctitle. */ Argv = argv; while (*envp) envp++; LastArgv = envp[-1] + strlen(envp[-1]); #endif /* OLD_SETPROCTITLE */ /* * Prevent diagnostic messages from appearing on stderr. * We run as a daemon or from inetd; in both cases, there's * more reason in logging to syslog. */ (void) freopen(_PATH_DEVNULL, "w", stderr); opterr = 0; /* * LOG_NDELAY sets up the logging connection immediately, * necessary for anonymous ftp's that chroot and can't do it later. */ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); while ((ch = getopt(argc, argv, "468a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) { switch (ch) { case '4': family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; break; case '6': family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; break; case '8': assumeutf8 = 1; break; case 'a': bindname = optarg; break; case 'A': anon_only = 1; break; case 'd': ftpdebug++; break; case 'D': daemon_mode++; break; case 'E': noepsv = 1; break; case 'h': hostinfo = 0; break; case 'l': logging++; /* > 1 == extra logging */ break; case 'm': noguestmod = 0; break; case 'M': noguestmkd = 1; break; case 'o': noretr = 1; break; case 'O': noguestretr = 1; break; case 'p': pid_file = optarg; break; case 'P': bindport = optarg; break; case 'r': readonly = 1; break; case 'R': paranoid = 0; break; case 'S': stats++; break; case 't': timeout = atoi(optarg); if (maxtimeout < timeout) maxtimeout = timeout; break; case 'T': maxtimeout = atoi(optarg); if (timeout > maxtimeout) timeout = maxtimeout; break; case 'u': { long val = 0; val = strtol(optarg, &optarg, 8); if (*optarg != '\0' || val < 0) syslog(LOG_WARNING, "bad value for -u"); else defumask = val; break; } case 'U': restricted_data_ports = 0; break; case 'v': ftpdebug++; break; case 'W': dowtmp = 0; break; default: syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); break; } } if (daemon_mode) { int *ctl_sock, fd, maxfd = -1, nfds, i; fd_set defreadfds, readfds; pid_t pid; struct pidfh *pfh; if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) { if (errno == EEXIST) { syslog(LOG_ERR, "%s already running, pid %d", getprogname(), (int)pid); exit(1); } syslog(LOG_WARNING, "pidfile_open: %m"); } /* * Detach from parent. */ if (daemon(1, 1) < 0) { syslog(LOG_ERR, "failed to become a daemon"); exit(1); } if (pfh != NULL && pidfile_write(pfh) == -1) syslog(LOG_WARNING, "pidfile_write: %m"); sa.sa_handler = reapchild; (void)sigaction(SIGCHLD, &sa, NULL); #ifdef VIRTUAL_HOSTING inithosts(family); #endif /* * Open a socket, bind it to the FTP port, and start * listening. */ ctl_sock = socksetup(family, bindname, bindport); if (ctl_sock == NULL) exit(1); FD_ZERO(&defreadfds); for (i = 1; i <= *ctl_sock; i++) { FD_SET(ctl_sock[i], &defreadfds); if (listen(ctl_sock[i], 32) < 0) { syslog(LOG_ERR, "control listen: %m"); exit(1); } if (maxfd < ctl_sock[i]) maxfd = ctl_sock[i]; } /* * Loop forever accepting connection requests and forking off * children to handle them. */ while (1) { FD_COPY(&defreadfds, &readfds); nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); if (nfds <= 0) { if (nfds < 0 && errno != EINTR) syslog(LOG_WARNING, "select: %m"); continue; } pid = -1; for (i = 1; i <= *ctl_sock; i++) if (FD_ISSET(ctl_sock[i], &readfds)) { addrlen = sizeof(his_addr); fd = accept(ctl_sock[i], (struct sockaddr *)&his_addr, &addrlen); if (fd == -1) { syslog(LOG_WARNING, "accept: %m"); continue; } switch (pid = fork()) { case 0: /* child */ (void) dup2(fd, 0); (void) dup2(fd, 1); (void) close(fd); for (i = 1; i <= *ctl_sock; i++) close(ctl_sock[i]); if (pfh != NULL) pidfile_close(pfh); goto gotchild; case -1: syslog(LOG_WARNING, "fork: %m"); /* FALLTHROUGH */ default: close(fd); } } } } else { addrlen = sizeof(his_addr); if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } #ifdef VIRTUAL_HOSTING if (his_addr.su_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) family = AF_INET; else family = his_addr.su_family; inithosts(family); #endif } gotchild: sa.sa_handler = SIG_DFL; (void)sigaction(SIGCHLD, &sa, NULL); sa.sa_handler = sigurg; sa.sa_flags = 0; /* don't restart syscalls for SIGURG */ (void)sigaction(SIGURG, &sa, NULL); sigfillset(&sa.sa_mask); /* block all signals in handler */ sa.sa_flags = SA_RESTART; sa.sa_handler = sigquit; (void)sigaction(SIGHUP, &sa, NULL); (void)sigaction(SIGINT, &sa, NULL); (void)sigaction(SIGQUIT, &sa, NULL); (void)sigaction(SIGTERM, &sa, NULL); sa.sa_handler = lostconn; (void)sigaction(SIGPIPE, &sa, NULL); addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ #ifdef VIRTUAL_HOSTING /* select our identity from virtual host table */ selecthost(&ctrl_addr); #endif #ifdef IP_TOS if (ctrl_addr.su_family == AF_INET) { tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); } #endif /* * Disable Nagle on the control channel so that we don't have to wait * for peer's ACK before issuing our next reply. */ if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); /* set this here so klogin can use it... */ (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); /* Try to handle urgent data inline */ #ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); #endif #ifdef F_SETOWN if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif dolog((struct sockaddr *)&his_addr); /* * Set up default state */ data = -1; type = TYPE_A; form = FORM_N; stru = STRU_F; mode = MODE_S; tmpline[0] = '\0'; /* If logins are disabled, print out the message. */ if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(530, "%s", line); } (void) fflush(stdout); (void) fclose(fd); reply(530, "System not available."); exit(0); } #ifdef VIRTUAL_HOSTING fd = fopen(thishost->welcome, "r"); #else fd = fopen(_PATH_FTPWELCOME, "r"); #endif if (fd != NULL) { while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(220, "%s", line); } (void) fflush(stdout); (void) fclose(fd); /* reply(220,) must follow */ } #ifndef VIRTUAL_HOSTING if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) fatalerror("Ran out of memory."); if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) hostname[0] = '\0'; hostname[MAXHOSTNAMELEN - 1] = '\0'; #endif if (hostinfo) reply(220, "%s FTP server (%s) ready.", hostname, version); else reply(220, "FTP server ready."); for (;;) (void) yyparse(); /* NOTREACHED */ } static void lostconn(int signo) { if (ftpdebug) syslog(LOG_DEBUG, "lost connection"); dologout(1); } static void sigquit(int signo) { syslog(LOG_ERR, "got signal %d", signo); dologout(1); } #ifdef VIRTUAL_HOSTING /* * read in virtual host tables (if they exist) */ static void inithosts(int family) { int insert; size_t len; FILE *fp; char *cp, *mp, *line; char *hostname; char *vhost, *anonuser, *statfile, *welcome, *loginmsg; struct ftphost *hrp, *lhrp; struct addrinfo hints, *res, *ai; /* * Fill in the default host information */ if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) fatalerror("Ran out of memory."); if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) hostname[0] = '\0'; hostname[MAXHOSTNAMELEN - 1] = '\0'; if ((hrp = malloc(sizeof(struct ftphost))) == NULL) fatalerror("Ran out of memory."); hrp->hostname = hostname; hrp->hostinfo = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) hrp->hostinfo = res; hrp->statfile = _PATH_FTPDSTATFILE; hrp->welcome = _PATH_FTPWELCOME; hrp->loginmsg = _PATH_FTPLOGINMESG; hrp->anonuser = "ftp"; hrp->next = NULL; thishost = firsthost = lhrp = hrp; if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { int addrsize, gothost; void *addr; struct hostent *hp; while ((line = fgetln(fp, &len)) != NULL) { int i, hp_error; /* skip comments */ if (line[0] == '#') continue; if (line[len - 1] == '\n') { line[len - 1] = '\0'; mp = NULL; } else { if ((mp = malloc(len + 1)) == NULL) fatalerror("Ran out of memory."); memcpy(mp, line, len); mp[len] = '\0'; line = mp; } cp = strtok(line, " \t"); /* skip empty lines */ if (cp == NULL) goto nextline; vhost = cp; /* set defaults */ anonuser = "ftp"; statfile = _PATH_FTPDSTATFILE; welcome = _PATH_FTPWELCOME; loginmsg = _PATH_FTPLOGINMESG; /* * Preparse the line so we can use its info * for all the addresses associated with * the virtual host name. * Field 0, the virtual host name, is special: * it's already parsed off and will be strdup'ed * later, after we know its canonical form. */ for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) if (*cp != '-' && (cp = strdup(cp))) switch (i) { case 1: /* anon user permissions */ anonuser = cp; break; case 2: /* statistics file */ statfile = cp; break; case 3: /* welcome message */ welcome = cp; break; case 4: /* login message */ loginmsg = cp; break; default: /* programming error */ abort(); /* NOTREACHED */ } hints.ai_flags = AI_PASSIVE; hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(vhost, NULL, &hints, &res) != 0) goto nextline; for (ai = res; ai != NULL && ai->ai_addr != NULL; ai = ai->ai_next) { gothost = 0; for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { struct addrinfo *hi; for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) if (hi->ai_addrlen == ai->ai_addrlen && memcmp(hi->ai_addr, ai->ai_addr, ai->ai_addr->sa_len) == 0) { gothost++; break; } if (gothost) break; } if (hrp == NULL) { if ((hrp = malloc(sizeof(struct ftphost))) == NULL) goto nextline; hrp->hostname = NULL; insert = 1; } else { if (hrp->hostinfo && hrp->hostinfo != res) freeaddrinfo(hrp->hostinfo); insert = 0; /* host already in the chain */ } hrp->hostinfo = res; /* * determine hostname to use. * force defined name if there is a valid alias * otherwise fallback to primary hostname */ /* XXX: getaddrinfo() can't do alias check */ switch(hrp->hostinfo->ai_family) { case AF_INET: addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; addrsize = sizeof(struct in_addr); break; case AF_INET6: addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; addrsize = sizeof(struct in6_addr); break; default: /* should not reach here */ freeaddrinfo(hrp->hostinfo); if (insert) free(hrp); /*not in chain, can free*/ else hrp->hostinfo = NULL; /*mark as blank*/ goto nextline; /* NOTREACHED */ } if ((hp = getipnodebyaddr(addr, addrsize, hrp->hostinfo->ai_family, &hp_error)) != NULL) { if (strcmp(vhost, hp->h_name) != 0) { if (hp->h_aliases == NULL) vhost = hp->h_name; else { i = 0; while (hp->h_aliases[i] && strcmp(vhost, hp->h_aliases[i]) != 0) ++i; if (hp->h_aliases[i] == NULL) vhost = hp->h_name; } } } if (hrp->hostname && strcmp(hrp->hostname, vhost) != 0) { free(hrp->hostname); hrp->hostname = NULL; } if (hrp->hostname == NULL && (hrp->hostname = strdup(vhost)) == NULL) { freeaddrinfo(hrp->hostinfo); hrp->hostinfo = NULL; /* mark as blank */ if (hp) freehostent(hp); goto nextline; } hrp->anonuser = anonuser; hrp->statfile = statfile; hrp->welcome = welcome; hrp->loginmsg = loginmsg; if (insert) { hrp->next = NULL; lhrp->next = hrp; lhrp = hrp; } if (hp) freehostent(hp); } nextline: if (mp) free(mp); } (void) fclose(fp); } } static void selecthost(union sockunion *su) { struct ftphost *hrp; u_int16_t port; #ifdef INET6 struct in6_addr *mapped_in6 = NULL; #endif struct addrinfo *hi; #ifdef INET6 /* * XXX IPv4 mapped IPv6 addr consideraton, * specified in rfc2373. */ if (su->su_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) mapped_in6 = &su->su_sin6.sin6_addr; #endif hrp = thishost = firsthost; /* default */ port = su->su_port; su->su_port = 0; while (hrp != NULL) { for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { thishost = hrp; goto found; } #ifdef INET6 /* XXX IPv4 mapped IPv6 addr consideraton */ if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && (memcmp(&mapped_in6->s6_addr[12], &((struct sockaddr_in *)hi->ai_addr)->sin_addr, sizeof(struct in_addr)) == 0)) { thishost = hrp; goto found; } #endif } hrp = hrp->next; } found: su->su_port = port; /* setup static variables as appropriate */ hostname = thishost->hostname; ftpuser = thishost->anonuser; } #endif /* * Helper function for sgetpwnam(). */ static char * sgetsave(char *s) { char *new = malloc(strlen(s) + 1); if (new == NULL) { reply(421, "Ran out of memory."); dologout(1); /* NOTREACHED */ } (void) strcpy(new, s); return (new); } /* * Save the result of a getpwnam. Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). * NB: The data returned by sgetpwnam() will remain valid until * the next call to this function. Its difference from getpwnam() * is that sgetpwnam() is known to be called from ftpd code only. */ static struct passwd * sgetpwnam(char *name) { static struct passwd save; struct passwd *p; if ((p = getpwnam(name)) == NULL) return (p); if (save.pw_name) { free(save.pw_name); free(save.pw_passwd); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); save.pw_passwd = sgetsave(p->pw_passwd); save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return (&save); } static int login_attempts; /* number of failed login attempts */ static int askpasswd; /* had user command, ask for passwd */ static char curname[MAXLOGNAME]; /* current USER name */ /* * USER command. * Sets global passwd pointer pw if named account exists and is acceptable; * sets askpasswd if a PASS command is expected. If logged in previously, * need to reset state. If name is "ftp" or "anonymous", the name is not in * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. Otherwise, check user * requesting login privileges. Disallow anyone who does not have a standard * shell as returned by getusershell(). Disallow anyone mentioned in the file * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. */ void user(char *name) { char *cp, *shell; if (logged_in) { if (guest) { reply(530, "Can't change user from guest login."); return; } else if (dochroot) { reply(530, "Can't change user from chroot user."); return; } end_login(); } guest = 0; #ifdef VIRTUAL_HOSTING pw = sgetpwnam(thishost->anonuser); #else pw = sgetpwnam("ftp"); #endif if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL) || checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL)) reply(530, "User %s access denied.", name); else if (pw != NULL) { guest = 1; askpasswd = 1; reply(331, "Guest login ok, send your email address as password."); } else reply(530, "User %s unknown.", name); if (!askpasswd && logging) syslog(LOG_NOTICE, "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); return; } if (anon_only != 0) { reply(530, "Sorry, only anonymous ftp allowed."); return; } if ((pw = sgetpwnam(name))) { if ((shell = pw->pw_shell) == NULL || *shell == 0) shell = _PATH_BSHELL; setusershell(); while ((cp = getusershell()) != NULL) if (strcmp(cp, shell) == 0) break; endusershell(); if (cp == NULL || checkuser(_PATH_FTPUSERS, name, 1, NULL)) { reply(530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, "FTP LOGIN REFUSED FROM %s, %s", remotehost, name); pw = NULL; return; } } if (logging) strncpy(curname, name, sizeof(curname)-1); pwok = 0; #ifdef USE_PAM /* XXX Kluge! The conversation mechanism needs to be fixed. */ #endif if (opiechallenge(&opiedata, name, opieprompt) == 0) { pwok = (pw != NULL) && opieaccessfile(remotehost) && opiealways(pw->pw_dir); reply(331, "Response to %s %s for %s.", opieprompt, pwok ? "requested" : "required", name); } else { pwok = 1; reply(331, "Password required for %s.", name); } askpasswd = 1; /* * Delay before reading passwd after first failed * attempt to slow down passwd-guessing programs. */ if (login_attempts) sleep(login_attempts); } /* * Check if a user is in the file "fname", * return a pointer to a malloc'd string with the rest * of the matching line in "residue" if not NULL. */ static int checkuser(char *fname, char *name, int pwset, char **residue) { FILE *fd; int found = 0; size_t len; char *line, *mp, *p; if ((fd = fopen(fname, "r")) != NULL) { while (!found && (line = fgetln(fd, &len)) != NULL) { /* skip comments */ if (line[0] == '#') continue; if (line[len - 1] == '\n') { line[len - 1] = '\0'; mp = NULL; } else { if ((mp = malloc(len + 1)) == NULL) fatalerror("Ran out of memory."); memcpy(mp, line, len); mp[len] = '\0'; line = mp; } /* avoid possible leading and trailing whitespace */ p = strtok(line, " \t"); /* skip empty lines */ if (p == NULL) goto nextline; /* * if first chr is '@', check group membership */ if (p[0] == '@') { int i = 0; struct group *grp; if (p[1] == '\0') /* single @ matches anyone */ found = 1; else { if ((grp = getgrnam(p+1)) == NULL) goto nextline; /* * Check user's default group */ if (pwset && grp->gr_gid == pw->pw_gid) found = 1; /* * Check supplementary groups */ while (!found && grp->gr_mem[i]) found = strcmp(name, grp->gr_mem[i++]) == 0; } } /* * Otherwise, just check for username match */ else found = strcmp(p, name) == 0; /* * Save the rest of line to "residue" if matched */ if (found && residue) { if ((p = strtok(NULL, "")) != NULL) p += strspn(p, " \t"); if (p && *p) { if ((*residue = strdup(p)) == NULL) fatalerror("Ran out of memory."); } else *residue = NULL; } nextline: if (mp) free(mp); } (void) fclose(fd); } return (found); } /* * Terminate login as previous user, if any, resetting state; * used when USER command is given or login fails. */ static void end_login(void) { #ifdef USE_PAM int e; #endif (void) seteuid(0); if (logged_in && dowtmp) ftpd_logwtmp(ttyline, "", NULL); pw = NULL; #ifdef LOGIN_CAP setusercontext(NULL, getpwuid(0), 0, LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK| LOGIN_SETMAC); #endif #ifdef USE_PAM if (pamh) { if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); if ((e = pam_end(pamh, e)) != PAM_SUCCESS) syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); pamh = NULL; } #endif logged_in = 0; guest = 0; dochroot = 0; } #ifdef USE_PAM /* * the following code is stolen from imap-uw PAM authentication module and * login.c */ #define COPY_STRING(s) (s ? strdup(s) : NULL) struct cred_t { const char *uname; /* user name */ const char *pass; /* password */ }; typedef struct cred_t cred_t; static int auth_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata) { int i; cred_t *cred = (cred_t *) appdata; struct pam_response *reply; reply = calloc(num_msg, sizeof *reply); if (reply == NULL) return PAM_BUF_ERR; for (i = 0; i < num_msg; i++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: /* assume want user name */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING(cred->uname); /* PAM frees resp. */ break; case PAM_PROMPT_ECHO_OFF: /* assume want password */ reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING(cred->pass); /* PAM frees resp. */ break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: /* unknown message style */ free(reply); return PAM_CONV_ERR; } } *resp = reply; return PAM_SUCCESS; } /* * Attempt to authenticate the user using PAM. Returns 0 if the user is * authenticated, or 1 if not authenticated. If some sort of PAM system * error occurs (e.g., the "/etc/pam.conf" file is missing) then this * function returns -1. This can be used as an indication that we should * fall back to a different authentication mechanism. */ static int auth_pam(struct passwd **ppw, const char *pass) { const char *tmpl_user; const void *item; int rval; int e; cred_t auth_cred = { (*ppw)->pw_name, pass }; struct pam_conv conv = { &auth_conv, &auth_cred }; e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); if (e != PAM_SUCCESS) { /* * In OpenPAM, it's OK to pass NULL to pam_strerror() * if context creation has failed in the first place. */ syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); return -1; } e = pam_set_item(pamh, PAM_RHOST, remotehost); if (e != PAM_SUCCESS) { syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, e)); if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); } pamh = NULL; return -1; } e = pam_authenticate(pamh, 0); switch (e) { case PAM_SUCCESS: /* * With PAM we support the concept of a "template" * user. The user enters a login name which is * authenticated by PAM, usually via a remote service * such as RADIUS or TACACS+. If authentication * succeeds, a different but related "template" name * is used for setting the credentials, shell, and * home directory. The name the user enters need only * exist on the remote authentication server, but the * template name must be present in the local password * database. * * This is supported by two various mechanisms in the * individual modules. However, from the application's * point of view, the template user is always passed * back as a changed value of the PAM_USER item. */ if ((e = pam_get_item(pamh, PAM_USER, &item)) == PAM_SUCCESS) { tmpl_user = (const char *) item; if (strcmp((*ppw)->pw_name, tmpl_user) != 0) *ppw = getpwnam(tmpl_user); } else syslog(LOG_ERR, "Couldn't get PAM_USER: %s", pam_strerror(pamh, e)); rval = 0; break; case PAM_AUTH_ERR: case PAM_USER_UNKNOWN: case PAM_MAXTRIES: rval = 1; break; default: syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); rval = -1; break; } if (rval == 0) { e = pam_acct_mgmt(pamh, 0); if (e != PAM_SUCCESS) { syslog(LOG_ERR, "pam_acct_mgmt: %s", pam_strerror(pamh, e)); rval = 1; } } if (rval != 0) { if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); } pamh = NULL; } return rval; } #endif /* USE_PAM */ void pass(char *passwd) { int rval; FILE *fd; #ifdef LOGIN_CAP login_cap_t *lc = NULL; #endif #ifdef USE_PAM int e; #endif char *residue = NULL; char *xpasswd; if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); return; } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ if (pw == NULL) { rval = 1; /* failure below */ goto skip; } #ifdef USE_PAM rval = auth_pam(&pw, passwd); if (rval >= 0) { opieunlock(); goto skip; } #endif if (opieverify(&opiedata, passwd) == 0) xpasswd = pw->pw_passwd; else if (pwok) { xpasswd = crypt(passwd, pw->pw_passwd); if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') xpasswd = ":"; } else { rval = 1; goto skip; } rval = strcmp(pw->pw_passwd, xpasswd); if (pw->pw_expire && time(NULL) >= pw->pw_expire) rval = 1; /* failure */ skip: /* * If rval == 1, the user failed the authentication check * above. If rval == 0, either PAM or local authentication * succeeded. */ if (rval) { reply(530, "Login incorrect."); if (logging) { syslog(LOG_NOTICE, "FTP LOGIN FAILED FROM %s", remotehost); syslog(LOG_AUTHPRIV | LOG_NOTICE, "FTP LOGIN FAILED FROM %s, %s", remotehost, curname); } pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s", remotehost); exit(0); } return; } } login_attempts = 0; /* this time successful */ if (setegid(pw->pw_gid) < 0) { reply(550, "Can't set gid."); return; } /* May be overridden by login.conf */ (void) umask(defumask); #ifdef LOGIN_CAP if ((lc = login_getpwclass(pw)) != NULL) { char remote_ip[NI_MAXHOST]; if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, remote_ip, sizeof(remote_ip) - 1, NULL, 0, NI_NUMERICHOST)) *remote_ip = 0; remote_ip[sizeof(remote_ip) - 1] = 0; if (!auth_hostok(lc, remotehost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, "FTP LOGIN FAILED (HOST) as %s: permission denied.", pw->pw_name); reply(530, "Permission denied."); pw = NULL; return; } if (!auth_timeok(lc, time(NULL))) { reply(530, "Login not available right now."); pw = NULL; return; } } setusercontext(lc, pw, 0, LOGIN_SETLOGIN|LOGIN_SETGROUP|LOGIN_SETPRIORITY| LOGIN_SETRESOURCES|LOGIN_SETUMASK|LOGIN_SETMAC); #else setlogin(pw->pw_name); (void) initgroups(pw->pw_name, pw->pw_gid); #endif #ifdef USE_PAM if (pamh) { if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); } } #endif /* open wtmp before chroot */ if (dowtmp) ftpd_logwtmp(ttyline, pw->pw_name, (struct sockaddr *)&his_addr); logged_in = 1; if (guest && stats && statfd < 0) #ifdef VIRTUAL_HOSTING statfd = open(thishost->statfile, O_WRONLY|O_APPEND); #else statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); #endif if (statfd < 0) stats = 0; dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue) #ifdef LOGIN_CAP /* Allow login.conf configuration as well */ || login_getcapbool(lc, "ftp-chroot", 0) #endif ; chrootdir = NULL; /* * For a chrooted local user, * a) see whether ftpchroot(5) specifies a chroot directory, * b) extract the directory pathname from the line, * c) expand it to the absolute pathname if necessary. */ if (dochroot && residue && (chrootdir = strtok(residue, " \t")) != NULL) { if (chrootdir[0] != '/') asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); else chrootdir = strdup(chrootdir); /* make it permanent */ if (chrootdir == NULL) fatalerror("Ran out of memory."); } if (guest || dochroot) { /* * If no chroot directory set yet, use the login directory. * Copy it so it can be modified while pw->pw_dir stays intact. */ if (chrootdir == NULL && (chrootdir = strdup(pw->pw_dir)) == NULL) fatalerror("Ran out of memory."); /* * Check for the "/chroot/./home" syntax, * separate the chroot and home directory pathnames. */ if ((homedir = strstr(chrootdir, "/./")) != NULL) { *(homedir++) = '\0'; /* wipe '/' */ homedir++; /* skip '.' */ } else { /* * We MUST do a chdir() after the chroot. Otherwise * the old current directory will be accessible as "." * outside the new root! */ homedir = "/"; } /* * Finally, do chroot() */ if (chroot(chrootdir) < 0) { reply(550, "Can't change root."); goto bad; } } else /* real user w/o chroot */ homedir = pw->pw_dir; /* * Set euid *before* doing chdir() so * a) the user won't be carried to a directory that he couldn't reach * on his own due to no permission to upper path components, * b) NFS mounted homedirs w/restrictive permissions will be accessible * (uid 0 has no root power over NFS if not mapped explicitly.) */ if (seteuid(pw->pw_uid) < 0) { reply(550, "Can't set uid."); goto bad; } if (chdir(homedir) < 0) { if (guest || dochroot) { reply(550, "Can't change to base directory."); goto bad; } else { if (chdir("/") < 0) { reply(550, "Root is inaccessible."); goto bad; } lreply(230, "No directory! Logging in with home=/."); } } /* * Display a login message, if it exists. * N.B. reply(230,) must follow the message. */ #ifdef VIRTUAL_HOSTING fd = fopen(thishost->loginmsg, "r"); #else fd = fopen(_PATH_FTPLOGINMESG, "r"); #endif if (fd != NULL) { char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; lreply(230, "%s", line); } (void) fflush(stdout); (void) fclose(fd); } if (guest) { if (ident != NULL) free(ident); ident = strdup(passwd); if (ident == NULL) fatalerror("Ran out of memory."); reply(230, "Guest login ok, access restrictions apply."); #ifdef SETPROCTITLE #ifdef VIRTUAL_HOSTING if (thishost != firsthost) snprintf(proctitle, sizeof(proctitle), "%s: anonymous(%s)/%s", remotehost, hostname, passwd); else #endif snprintf(proctitle, sizeof(proctitle), "%s: anonymous/%s", remotehost, passwd); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", remotehost, passwd); } else { if (dochroot) reply(230, "User %s logged in, " "access restrictions apply.", pw->pw_name); else reply(230, "User %s logged in.", pw->pw_name); #ifdef SETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: user/%s", remotehost, pw->pw_name); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", remotehost, pw->pw_name); } if (logging && (guest || dochroot)) syslog(LOG_INFO, "session root changed to %s", chrootdir); #ifdef LOGIN_CAP login_close(lc); #endif if (residue) free(residue); return; bad: /* Forget all about it... */ #ifdef LOGIN_CAP login_close(lc); #endif if (residue) free(residue); end_login(); } void retrieve(char *cmd, char *name) { FILE *fin, *dout; struct stat st; int (*closefunc)(FILE *); time_t start; if (cmd == 0) { fin = fopen(name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; (void) snprintf(line, sizeof(line), cmd, name), name = line; fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; st.st_size = -1; st.st_blksize = BUFSIZ; } if (fin == NULL) { if (errno != 0) { perror_reply(550, name); if (cmd == 0) { LOGCMD("get", name); } } return; } byte_count = -1; if (cmd == 0) { if (fstat(fileno(fin), &st) < 0) { perror_reply(550, name); goto done; } if (!S_ISREG(st.st_mode)) { /* * Never sending a raw directory is a workaround * for buggy clients that will attempt to RETR * a directory before listing it, e.g., Mozilla. * Preventing a guest from getting irregular files * is a simple security measure. */ if (S_ISDIR(st.st_mode) || guest) { reply(550, "%s: not a plain file.", name); goto done; } st.st_size = -1; /* st.st_blksize is set for all descriptor types */ } } if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { if ((c=getc(fin)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; time(&start); send_data(fin, dout, st.st_blksize, st.st_size, restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); if (cmd == 0 && guest && stats && byte_count > 0) logxfer(name, byte_count, start); (void) fclose(dout); data = -1; pdata = -1; done: if (cmd == 0) LOGBYTES("get", name, byte_count); (*closefunc)(fin); } void store(char *name, char *mode, int unique) { int fd; FILE *fout, *din; int (*closefunc)(FILE *); if (*mode == 'a') { /* APPE */ if (unique) { /* Programming error */ syslog(LOG_ERR, "Internal: unique flag to APPE"); unique = 0; } if (guest && noguestmod) { reply(550, "Appending to existing file denied."); goto err; } restart_point = 0; /* not affected by preceding REST */ } if (unique) /* STOU overrides REST */ restart_point = 0; if (guest && noguestmod) { if (restart_point) { /* guest STOR w/REST */ reply(550, "Modifying existing file denied."); goto err; } else /* treat guest STOR as STOU */ unique = 1; } if (restart_point) mode = "r+"; /* so ASCII manual seek can work */ if (unique) { if ((fd = guniquefd(name, &name)) < 0) goto err; fout = fdopen(fd, mode); } else fout = fopen(name, mode); closefunc = fclose; if (fout == NULL) { perror_reply(553, name); goto err; } byte_count = -1; if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { if ((c=getc(fout)) == EOF) { perror_reply(550, name); goto done; } if (c == '\n') i++; } /* * We must do this seek to "current" position * because we are changing from reading to * writing. */ if (fseeko(fout, 0, SEEK_CUR) < 0) { perror_reply(550, name); goto done; } } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { perror_reply(550, name); goto done; } } din = dataconn(name, -1, "r"); if (din == NULL) goto done; if (receive_data(din, fout) == 0) { if (unique) reply(226, "Transfer complete (unique file name:%s).", name); else reply(226, "Transfer complete."); } (void) fclose(din); data = -1; pdata = -1; done: LOGBYTES(*mode == 'a' ? "append" : "put", name, byte_count); (*closefunc)(fout); return; err: LOGCMD(*mode == 'a' ? "append" : "put" , name); return; } static FILE * getdatasock(char *mode) { int on = 1, s, t, tries; if (data >= 0) return (fdopen(data, mode)); s = socket(data_dest.su_family, SOCK_STREAM, 0); if (s < 0) goto bad; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); /* anchor socket to avoid multi-homing problems */ data_source = ctrl_addr; data_source.su_port = htons(dataport); (void) seteuid(0); for (tries = 1; ; tries++) { /* * We should loop here since it's possible that * another ftpd instance has passed this point and is * trying to open a data connection in active mode now. * Until the other connection is opened, we'll be getting * EADDRINUSE because no SOCK_STREAM sockets in the system * can share both local and remote addresses, localIP:20 * and *:* in this case. */ if (bind(s, (struct sockaddr *)&data_source, data_source.su_len) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; sleep(tries); } (void) seteuid(pw->pw_uid); #ifdef IP_TOS if (data_source.su_family == AF_INET) { on = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) syslog(LOG_WARNING, "data setsockopt (IP_TOS): %m"); } #endif #ifdef TCP_NOPUSH /* * Turn off push flag to keep sender TCP from sending short packets * at the boundaries of each write(). */ on = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); #endif return (fdopen(s, mode)); bad: /* Return the real value of errno (close may change it) */ t = errno; (void) seteuid(pw->pw_uid); (void) close(s); errno = t; return (NULL); } static FILE * dataconn(char *name, off_t size, char *mode) { char sizebuf[32]; FILE *file; int retry = 0, tos, conerrno; file_size = size; byte_count = 0; if (size != -1) (void) snprintf(sizebuf, sizeof(sizebuf), " (%jd bytes)", (intmax_t)size); else *sizebuf = '\0'; if (pdata >= 0) { union sockunion from; socklen_t fromlen = ctrl_addr.su_len; int flags, s; struct timeval timeout; fd_set set; FD_ZERO(&set); FD_SET(pdata, &set); timeout.tv_usec = 0; timeout.tv_sec = 120; /* * Granted a socket is in the blocking I/O mode, * accept() will block after a successful select() * if the selected connection dies in between. * Therefore set the non-blocking I/O flag here. */ if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) goto pdata_err; if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) goto pdata_err; (void) close(pdata); pdata = s; /* * Unset the inherited non-blocking I/O flag * on the child socket so stdio can work on it. */ if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) goto pdata_err; #ifdef IP_TOS if (from.su_family == AF_INET) { tos = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); } #endif reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (fdopen(pdata, mode)); pdata_err: reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; return (NULL); } if (data >= 0) { reply(125, "Using existing data connection for '%s'%s.", name, sizebuf); usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = his_addr; usedefault = 1; do { file = getdatasock(mode); if (file == NULL) { char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; if (getnameinfo((struct sockaddr *)&data_source, data_source.su_len, hostbuf, sizeof(hostbuf) - 1, portbuf, sizeof(portbuf) - 1, NI_NUMERICHOST|NI_NUMERICSERV)) *hostbuf = *portbuf = 0; hostbuf[sizeof(hostbuf) - 1] = 0; portbuf[sizeof(portbuf) - 1] = 0; reply(425, "Can't create data socket (%s,%s): %s.", hostbuf, portbuf, strerror(errno)); return (NULL); } data = fileno(file); conerrno = 0; if (connect(data, (struct sockaddr *)&data_dest, data_dest.su_len) == 0) break; conerrno = errno; (void) fclose(file); data = -1; if (conerrno == EADDRINUSE) { sleep(swaitint); retry += swaitint; } else { break; } } while (retry <= swaitmax); if (conerrno != 0) { reply(425, "Can't build data connection: %s.", strerror(conerrno)); return (NULL); } reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); return (file); } /* * A helper macro to avoid code duplication * in send_data() and receive_data(). * * XXX We have to block SIGURG during putc() because BSD stdio * is unable to restart interrupted write operations and hence * the entire buffer contents will be lost as soon as a write() * call indicates EINTR to stdio. */ #define FTPD_PUTC(ch, file, label) \ do { \ int ret; \ \ do { \ START_UNSAFE; \ ret = putc((ch), (file)); \ END_UNSAFE; \ CHECKOOB(return (-1)) \ else if (ferror(file)) \ goto label; \ clearerr(file); \ } while (ret == EOF); \ } while (0) /* * Tranfer the contents of "instr" to "outstr" peer using the appropriate * encapsulation of the data subject to Mode, Structure, and Type. * * NB: Form isn't handled. */ static int send_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg) { int c, cp, filefd, netfd; char *buf; STARTXFER; switch (type) { case TYPE_A: cp = EOF; for (;;) { c = getc(instr); CHECKOOB(return (-1)) else if (c == EOF && ferror(instr)) goto file_err; if (c == EOF) { if (ferror(instr)) { /* resume after OOB */ clearerr(instr); continue; } if (feof(instr)) /* EOF */ break; syslog(LOG_ERR, "Internal: impossible condition" " on file after getc()"); goto file_err; } if (c == '\n' && cp != '\r') { FTPD_PUTC('\r', outstr, data_err); byte_count++; } FTPD_PUTC(c, outstr, data_err); byte_count++; cp = c; } #ifdef notyet /* BSD stdio isn't ready for that */ while (fflush(outstr) == EOF) { CHECKOOB(return (-1)) else goto data_err; clearerr(outstr); } ENDXFER; #else ENDXFER; if (fflush(outstr) == EOF) goto data_err; #endif reply(226, "Transfer complete."); return (0); case TYPE_I: case TYPE_L: /* * isreg is only set if we are not doing restart and we * are sending a regular file */ netfd = fileno(outstr); filefd = fileno(instr); if (isreg) { char *msg = "Transfer complete."; off_t cnt, offset; int err; cnt = offset = 0; while (filesize > 0) { err = sendfile(filefd, netfd, offset, 0, NULL, &cnt, 0); /* * Calculate byte_count before OOB processing. * It can be used in myoob() later. */ byte_count += cnt; offset += cnt; filesize -= cnt; CHECKOOB(return (-1)) else if (err == -1) { if (errno != EINTR && cnt == 0 && offset == 0) goto oldway; goto data_err; } if (err == -1) /* resume after OOB */ continue; /* * We hit the EOF prematurely. * Perhaps the file was externally truncated. */ if (cnt == 0) { msg = "Transfer finished due to " "premature end of file."; break; } } ENDXFER; reply(226, msg); return (0); } oldway: if ((buf = malloc(blksize)) == NULL) { ENDXFER; reply(451, "Ran out of memory."); return (-1); } for (;;) { int cnt, len; char *bp; cnt = read(filefd, buf, blksize); CHECKOOB(free(buf); return (-1)) else if (cnt < 0) { free(buf); goto file_err; } if (cnt < 0) /* resume after OOB */ continue; if (cnt == 0) /* EOF */ break; for (len = cnt, bp = buf; len > 0;) { cnt = write(netfd, bp, len); CHECKOOB(free(buf); return (-1)) else if (cnt < 0) { free(buf); goto data_err; } if (cnt <= 0) continue; len -= cnt; bp += cnt; byte_count += cnt; } } ENDXFER; free(buf); reply(226, "Transfer complete."); return (0); default: ENDXFER; reply(550, "Unimplemented TYPE %d in send_data.", type); return (-1); } data_err: ENDXFER; perror_reply(426, "Data connection"); return (-1); file_err: ENDXFER; perror_reply(551, "Error on input file"); return (-1); } /* * Transfer data from peer to "outstr" using the appropriate encapulation of * the data subject to Mode, Structure, and Type. * * N.B.: Form isn't handled. */ static int receive_data(FILE *instr, FILE *outstr) { int c, cp; int bare_lfs = 0; STARTXFER; switch (type) { case TYPE_I: case TYPE_L: for (;;) { int cnt, len; char *bp; char buf[BUFSIZ]; cnt = read(fileno(instr), buf, sizeof(buf)); CHECKOOB(return (-1)) else if (cnt < 0) goto data_err; if (cnt < 0) /* resume after OOB */ continue; if (cnt == 0) /* EOF */ break; for (len = cnt, bp = buf; len > 0;) { cnt = write(fileno(outstr), bp, len); CHECKOOB(return (-1)) else if (cnt < 0) goto file_err; if (cnt <= 0) continue; len -= cnt; bp += cnt; byte_count += cnt; } } ENDXFER; return (0); case TYPE_E: ENDXFER; reply(553, "TYPE E not implemented."); return (-1); case TYPE_A: cp = EOF; for (;;) { c = getc(instr); CHECKOOB(return (-1)) else if (c == EOF && ferror(instr)) goto data_err; if (c == EOF && ferror(instr)) { /* resume after OOB */ clearerr(instr); continue; } if (cp == '\r') { if (c != '\n') FTPD_PUTC('\r', outstr, file_err); } else if (c == '\n') bare_lfs++; if (c == '\r') { byte_count++; cp = c; continue; } /* Check for EOF here in order not to lose last \r. */ if (c == EOF) { if (feof(instr)) /* EOF */ break; syslog(LOG_ERR, "Internal: impossible condition" " on data stream after getc()"); goto data_err; } byte_count++; FTPD_PUTC(c, outstr, file_err); cp = c; } #ifdef notyet /* BSD stdio isn't ready for that */ while (fflush(outstr) == EOF) { CHECKOOB(return (-1)) else goto file_err; clearerr(outstr); } ENDXFER; #else ENDXFER; if (fflush(outstr) == EOF) goto file_err; #endif if (bare_lfs) { lreply(226, "WARNING! %d bare linefeeds received in ASCII mode.", bare_lfs); (void)printf(" File may not have transferred correctly.\r\n"); } return (0); default: ENDXFER; reply(550, "Unimplemented TYPE %d in receive_data.", type); return (-1); } data_err: ENDXFER; perror_reply(426, "Data connection"); return (-1); file_err: ENDXFER; perror_reply(452, "Error writing to file"); return (-1); } void statfilecmd(char *filename) { FILE *fin; int atstart; int c, code; char line[LINE_MAX]; struct stat st; code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); fin = ftpd_popen(line, "r"); lreply(code, "Status of %s:", filename); atstart = 1; while ((c = getc(fin)) != EOF) { if (c == '\n') { if (ferror(stdout)){ perror_reply(421, "Control connection"); (void) ftpd_pclose(fin); dologout(1); /* NOTREACHED */ } if (ferror(fin)) { perror_reply(551, filename); (void) ftpd_pclose(fin); return; } (void) putc('\r', stdout); } /* * RFC 959 says neutral text should be prepended before * a leading 3-digit number followed by whitespace, but * many ftp clients can be confused by any leading digits, * as a matter of fact. */ if (atstart && isdigit(c)) (void) putc(' ', stdout); (void) putc(c, stdout); atstart = (c == '\n'); } (void) ftpd_pclose(fin); reply(code, "End of status."); } void statcmd(void) { union sockunion *su; u_char *a, *p; char hname[NI_MAXHOST]; int ispassive; if (hostinfo) { lreply(211, "%s FTP server status:", hostname); printf(" %s\r\n", version); } else lreply(211, "FTP server status:"); printf(" Connected to %s", remotehost); if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { hname[sizeof(hname) - 1] = 0; if (strcmp(hname, remotehost) != 0) printf(" (%s)", hname); } printf("\r\n"); if (logged_in) { if (guest) printf(" Logged in anonymously\r\n"); else printf(" Logged in as %s\r\n", pw->pw_name); } else if (askpasswd) printf(" Waiting for password\r\n"); else printf(" Waiting for user name\r\n"); printf(" TYPE: %s", typenames[type]); if (type == TYPE_A || type == TYPE_E) printf(", FORM: %s", formnames[form]); if (type == TYPE_L) #if CHAR_BIT == 8 printf(" %d", CHAR_BIT); #else printf(" %d", bytesize); /* need definition! */ #endif printf("; STRUcture: %s; transfer MODE: %s\r\n", strunames[stru], modenames[mode]); if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { ispassive = 1; su = &pasv_addr; goto printaddr; } else if (usedefault == 0) { ispassive = 0; su = &data_dest; printaddr: #define UC(b) (((int) b) & 0xff) if (epsvall) { printf(" EPSV only mode (EPSV ALL)\r\n"); goto epsvonly; } /* PORT/PASV */ if (su->su_family == AF_INET) { a = (u_char *) &su->su_sin.sin_addr; p = (u_char *) &su->su_sin.sin_port; printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", ispassive ? "PASV" : "PORT", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); } /* LPRT/LPSV */ { int alen, af, i; switch (su->su_family) { case AF_INET: a = (u_char *) &su->su_sin.sin_addr; p = (u_char *) &su->su_sin.sin_port; alen = sizeof(su->su_sin.sin_addr); af = 4; break; case AF_INET6: a = (u_char *) &su->su_sin6.sin6_addr; p = (u_char *) &su->su_sin6.sin6_port; alen = sizeof(su->su_sin6.sin6_addr); af = 6; break; default: af = 0; break; } if (af) { printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", af, alen); for (i = 0; i < alen; i++) printf("%d,", UC(a[i])); printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); } } epsvonly:; /* EPRT/EPSV */ { int af; switch (su->su_family) { case AF_INET: af = 1; break; case AF_INET6: af = 2; break; default: af = 0; break; } if (af) { union sockunion tmp; tmp = *su; if (tmp.su_family == AF_INET6) tmp.su_sin6.sin6_scope_id = 0; if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { hname[sizeof(hname) - 1] = 0; printf(" %s |%d|%s|%d|\r\n", ispassive ? "EPSV" : "EPRT", af, hname, htons(tmp.su_port)); } } } #undef UC } else printf(" No data connection\r\n"); reply(211, "End of status."); } void fatalerror(char *s) { reply(451, "Error in server: %s", s); reply(221, "Closing connection due to server error."); dologout(0); /* NOTREACHED */ } void reply(int n, const char *fmt, ...) { va_list ap; (void)printf("%d ", n); va_start(ap, fmt); (void)vprintf(fmt, ap); va_end(ap); (void)printf("\r\n"); (void)fflush(stdout); if (ftpdebug) { syslog(LOG_DEBUG, "<--- %d ", n); va_start(ap, fmt); vsyslog(LOG_DEBUG, fmt, ap); va_end(ap); } } void lreply(int n, const char *fmt, ...) { va_list ap; (void)printf("%d- ", n); va_start(ap, fmt); (void)vprintf(fmt, ap); va_end(ap); (void)printf("\r\n"); (void)fflush(stdout); if (ftpdebug) { syslog(LOG_DEBUG, "<--- %d- ", n); va_start(ap, fmt); vsyslog(LOG_DEBUG, fmt, ap); va_end(ap); } } static void ack(char *s) { reply(250, "%s command successful.", s); } void nack(char *s) { reply(502, "%s command not implemented.", s); } /* ARGSUSED */ void yyerror(char *s) { char *cp; if ((cp = strchr(cbuf,'\n'))) *cp = '\0'; reply(500, "%s: command not understood.", cbuf); } void delete(char *name) { struct stat st; LOGCMD("delete", name); if (lstat(name, &st) < 0) { perror_reply(550, name); return; } if (S_ISDIR(st.st_mode)) { if (rmdir(name) < 0) { perror_reply(550, name); return; } goto done; } if (guest && noguestmod) { reply(550, "Operation not permitted."); return; } if (unlink(name) < 0) { perror_reply(550, name); return; } done: ack("DELE"); } void cwd(char *path) { if (chdir(path) < 0) perror_reply(550, path); else ack("CWD"); } void makedir(char *name) { char *s; LOGCMD("mkdir", name); if (guest && noguestmkd) reply(550, "Operation not permitted."); else if (mkdir(name, 0777) < 0) perror_reply(550, name); else { if ((s = doublequote(name)) == NULL) fatalerror("Ran out of memory."); reply(257, "\"%s\" directory created.", s); free(s); } } void removedir(char *name) { LOGCMD("rmdir", name); if (rmdir(name) < 0) perror_reply(550, name); else ack("RMD"); } void pwd(void) { char *s, path[MAXPATHLEN + 1]; if (getcwd(path, sizeof(path)) == NULL) perror_reply(550, "Get current directory"); else { if ((s = doublequote(path)) == NULL) fatalerror("Ran out of memory."); reply(257, "\"%s\" is current directory.", s); free(s); } } char * renamefrom(char *name) { struct stat st; if (guest && noguestmod) { reply(550, "Operation not permitted."); return (NULL); } if (lstat(name, &st) < 0) { perror_reply(550, name); return (NULL); } reply(350, "File exists, ready for destination name."); return (name); } void renamecmd(char *from, char *to) { struct stat st; LOGCMD2("rename", from, to); if (guest && (stat(to, &st) == 0)) { reply(550, "%s: permission denied.", to); return; } if (rename(from, to) < 0) perror_reply(550, "rename"); else ack("RNTO"); } static void dolog(struct sockaddr *who) { char who_name[NI_MAXHOST]; realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); remotehost[sizeof(remotehost) - 1] = 0; if (getnameinfo(who, who->sa_len, who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) *who_name = 0; who_name[sizeof(who_name) - 1] = 0; #ifdef SETPROCTITLE #ifdef VIRTUAL_HOSTING if (thishost != firsthost) snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", remotehost, hostname); else #endif snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) { #ifdef VIRTUAL_HOSTING if (thishost != firsthost) syslog(LOG_INFO, "connection from %s (%s) to %s", remotehost, who_name, hostname); else #endif syslog(LOG_INFO, "connection from %s (%s)", remotehost, who_name); } } /* * Record logout in wtmp file * and exit with supplied status. */ void dologout(int status) { if (logged_in && dowtmp) { (void) seteuid(0); ftpd_logwtmp(ttyline, "", NULL); } /* beware of flushing buffers after a SIGPIPE */ _exit(status); } static void sigurg(int signo) { recvurg = 1; } static void maskurg(int flag) { int oerrno; sigset_t sset; if (!transflag) { syslog(LOG_ERR, "Internal: maskurg() while no transfer"); return; } oerrno = errno; sigemptyset(&sset); sigaddset(&sset, SIGURG); sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); errno = oerrno; } static void flagxfer(int flag) { if (flag) { if (transflag) syslog(LOG_ERR, "Internal: flagxfer(1): " "transfer already under way"); transflag = 1; maskurg(0); recvurg = 0; } else { if (!transflag) syslog(LOG_ERR, "Internal: flagxfer(0): " "no active transfer"); maskurg(1); transflag = 0; } } /* * Returns 0 if OK to resume or -1 if abort requested. */ static int myoob(void) { char *cp; + int ret; if (!transflag) { syslog(LOG_ERR, "Internal: myoob() while no transfer"); return (0); } cp = tmpline; - if (getline(cp, 7, stdin) == NULL) { + ret = getline(cp, 7, stdin); + if (ret == -1) { reply(221, "You could at least say goodbye."); dologout(0); + } else if (ret == -2) { + /* Ignore truncated command. */ + return (0); } upper(cp); if (strcmp(cp, "ABOR\r\n") == 0) { tmpline[0] = '\0'; reply(426, "Transfer aborted. Data connection closed."); reply(226, "Abort successful."); return (-1); } if (strcmp(cp, "STAT\r\n") == 0) { tmpline[0] = '\0'; if (file_size != -1) reply(213, "Status: %jd of %jd bytes transferred.", (intmax_t)byte_count, (intmax_t)file_size); else reply(213, "Status: %jd bytes transferred.", (intmax_t)byte_count); } return (0); } /* * Note: a response of 425 is not mentioned as a possible response to * the PASV command in RFC959. However, it has been blessed as * a legitimate response by Jon Postel in a telephone conversation * with Rick Adams on 25 Jan 89. */ void passive(void) { socklen_t len; int on; char *p, *a; if (pdata >= 0) /* close old port if one set */ close(pdata); pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } on = 1; if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); (void) seteuid(0); #ifdef IP_PORTRANGE if (ctrl_addr.su_family == AF_INET) { on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, &on, sizeof(on)) < 0) goto pasv_error; } #endif #ifdef IPV6_PORTRANGE if (ctrl_addr.su_family == AF_INET6) { on = restricted_data_ports ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, &on, sizeof(on)) < 0) goto pasv_error; } #endif pasv_addr = ctrl_addr; pasv_addr.su_port = 0; if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) goto pasv_error; (void) seteuid(pw->pw_uid); len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; if (pasv_addr.su_family == AF_INET) a = (char *) &pasv_addr.su_sin.sin_addr; else if (pasv_addr.su_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; else goto pasv_error; p = (char *) &pasv_addr.su_port; #define UC(b) (((int) b) & 0xff) reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); return; pasv_error: (void) seteuid(pw->pw_uid); (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return; } /* * Long Passive defined in RFC 1639. * 228 Entering Long Passive Mode * (af, hal, h1, h2, h3,..., pal, p1, p2...) */ void long_passive(char *cmd, int pf) { socklen_t len; int on; char *p, *a; if (pdata >= 0) /* close old port if one set */ close(pdata); if (pf != PF_UNSPEC) { if (ctrl_addr.su_family != pf) { switch (ctrl_addr.su_family) { case AF_INET: pf = 1; break; case AF_INET6: pf = 2; break; default: pf = 0; break; } /* * XXX * only EPRT/EPSV ready clients will understand this */ if (strcmp(cmd, "EPSV") == 0 && pf) { reply(522, "Network protocol mismatch, " "use (%d)", pf); } else reply(501, "Network protocol mismatch."); /*XXX*/ return; } } pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } on = 1; if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); (void) seteuid(0); pasv_addr = ctrl_addr; pasv_addr.su_port = 0; len = pasv_addr.su_len; #ifdef IP_PORTRANGE if (ctrl_addr.su_family == AF_INET) { on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, &on, sizeof(on)) < 0) goto pasv_error; } #endif #ifdef IPV6_PORTRANGE if (ctrl_addr.su_family == AF_INET6) { on = restricted_data_ports ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT; if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, &on, sizeof(on)) < 0) goto pasv_error; } #endif if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) goto pasv_error; (void) seteuid(pw->pw_uid); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; #define UC(b) (((int) b) & 0xff) if (strcmp(cmd, "LPSV") == 0) { p = (char *)&pasv_addr.su_port; switch (pasv_addr.su_family) { case AF_INET: a = (char *) &pasv_addr.su_sin.sin_addr; v4_reply: reply(228, "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), 2, UC(p[0]), UC(p[1])); return; case AF_INET6: if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; goto v4_reply; } a = (char *) &pasv_addr.su_sin6.sin6_addr; reply(228, "Entering Long Passive Mode " "(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), 2, UC(p[0]), UC(p[1])); return; } } else if (strcmp(cmd, "EPSV") == 0) { switch (pasv_addr.su_family) { case AF_INET: case AF_INET6: reply(229, "Entering Extended Passive Mode (|||%d|)", ntohs(pasv_addr.su_port)); return; } } else { /* more proper error code? */ } pasv_error: (void) seteuid(pw->pw_uid); (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); return; } /* * Generate unique name for file with basename "local" * and open the file in order to avoid possible races. * Try "local" first, then "local.1", "local.2" etc, up to "local.99". * Return descriptor to the file, set "name" to its name. * * Generates failure reply on error. */ static int guniquefd(char *local, char **name) { static char new[MAXPATHLEN]; struct stat st; char *cp; int count; int fd; cp = strrchr(local, '/'); if (cp) *cp = '\0'; if (stat(cp ? local : ".", &st) < 0) { perror_reply(553, cp ? local : "."); return (-1); } if (cp) { /* * Let not overwrite dirname with counter suffix. * -4 is for /nn\0 * In this extreme case dot won't be put in front of suffix. */ if (strlen(local) > sizeof(new) - 4) { reply(553, "Pathname too long."); return (-1); } *cp = '/'; } /* -4 is for the .nn we put on the end below */ (void) snprintf(new, sizeof(new) - 4, "%s", local); cp = new + strlen(new); /* * Don't generate dotfile unless requested explicitly. * This covers the case when basename gets truncated off * by buffer size. */ if (cp > new && cp[-1] != '/') *cp++ = '.'; for (count = 0; count < 100; count++) { /* At count 0 try unmodified name */ if (count) (void)sprintf(cp, "%d", count); if ((fd = open(count ? new : local, O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { *name = count ? new : local; return (fd); } if (errno != EEXIST) { perror_reply(553, count ? new : local); return (-1); } } reply(452, "Unique file name cannot be created."); return (-1); } /* * Format and send reply containing system error number. */ void perror_reply(int code, char *string) { reply(code, "%s: %s.", string, strerror(errno)); } static char *onefile[] = { "", 0 }; void send_file_list(char *whichf) { struct stat st; DIR *dirp = NULL; struct dirent *dir; FILE *dout = NULL; char **dirlist, *dirname; int simple = 0; int freeglob = 0; glob_t gl; if (strpbrk(whichf, "~{[*?") != NULL) { int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; memset(&gl, 0, sizeof(gl)); gl.gl_matchc = MAXGLOBARGS; flags |= GLOB_LIMIT; freeglob = 1; if (glob(whichf, flags, 0, &gl)) { reply(550, "No matching files found."); goto out; } else if (gl.gl_pathc == 0) { errno = ENOENT; perror_reply(550, whichf); goto out; } dirlist = gl.gl_pathv; } else { onefile[0] = whichf; dirlist = onefile; simple = 1; } while ((dirname = *dirlist++)) { if (stat(dirname, &st) < 0) { /* * If user typed "ls -l", etc, and the client * used NLST, do what the user meant. */ if (dirname[0] == '-' && *dirlist == NULL && dout == NULL) retrieve(_PATH_LS " %s", dirname); else perror_reply(550, whichf); goto out; } if (S_ISREG(st.st_mode)) { if (dout == NULL) { dout = dataconn("file list", -1, "w"); if (dout == NULL) goto out; STARTXFER; } START_UNSAFE; fprintf(dout, "%s%s\n", dirname, type == TYPE_A ? "\r" : ""); END_UNSAFE; if (ferror(dout)) goto data_err; byte_count += strlen(dirname) + (type == TYPE_A ? 2 : 1); CHECKOOB(goto abrt); continue; } else if (!S_ISDIR(st.st_mode)) continue; if ((dirp = opendir(dirname)) == NULL) continue; while ((dir = readdir(dirp)) != NULL) { char nbuf[MAXPATHLEN]; CHECKOOB(goto abrt); if (dir->d_name[0] == '.' && dir->d_namlen == 1) continue; if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && dir->d_namlen == 2) continue; snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name); /* * We have to do a stat to insure it's * not a directory or special file. */ if (simple || (stat(nbuf, &st) == 0 && S_ISREG(st.st_mode))) { if (dout == NULL) { dout = dataconn("file list", -1, "w"); if (dout == NULL) goto out; STARTXFER; } START_UNSAFE; if (nbuf[0] == '.' && nbuf[1] == '/') fprintf(dout, "%s%s\n", &nbuf[2], type == TYPE_A ? "\r" : ""); else fprintf(dout, "%s%s\n", nbuf, type == TYPE_A ? "\r" : ""); END_UNSAFE; if (ferror(dout)) goto data_err; byte_count += strlen(nbuf) + (type == TYPE_A ? 2 : 1); CHECKOOB(goto abrt); } } (void) closedir(dirp); dirp = NULL; } if (dout == NULL) reply(550, "No files found."); else if (ferror(dout)) data_err: perror_reply(550, "Data connection"); else reply(226, "Transfer complete."); out: if (dout) { ENDXFER; abrt: (void) fclose(dout); data = -1; pdata = -1; } if (dirp) (void) closedir(dirp); if (freeglob) { freeglob = 0; globfree(&gl); } } void reapchild(int signo) { while (waitpid(-1, NULL, WNOHANG) > 0); } #ifdef OLD_SETPROCTITLE /* * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) * Warning, since this is usually started from inetd.conf, it often doesn't * have much of an environment or arglist to overwrite. */ void setproctitle(const char *fmt, ...) { int i; va_list ap; char *p, *bp, ch; char buf[LINE_MAX]; va_start(ap, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, ap); /* make ps print our process name */ p = Argv[0]; *p++ = '-'; i = strlen(buf); if (i > LastArgv - p - 2) { i = LastArgv - p - 2; buf[i] = '\0'; } bp = buf; while (ch = *bp++) if (ch != '\n' && ch != '\r') *p++ = ch; while (p < LastArgv) *p++ = ' '; } #endif /* OLD_SETPROCTITLE */ static void appendf(char **strp, char *fmt, ...) { va_list ap; char *ostr, *p; va_start(ap, fmt); vasprintf(&p, fmt, ap); va_end(ap); if (p == NULL) fatalerror("Ran out of memory."); if (*strp == NULL) *strp = p; else { ostr = *strp; asprintf(strp, "%s%s", ostr, p); if (*strp == NULL) fatalerror("Ran out of memory."); free(ostr); } } static void logcmd(char *cmd, char *file1, char *file2, off_t cnt) { char *msg = NULL; char wd[MAXPATHLEN + 1]; if (logging <= 1) return; if (getcwd(wd, sizeof(wd) - 1) == NULL) strcpy(wd, strerror(errno)); appendf(&msg, "%s", cmd); if (file1) appendf(&msg, " %s", file1); if (file2) appendf(&msg, " %s", file2); if (cnt >= 0) appendf(&msg, " = %jd bytes", (intmax_t)cnt); appendf(&msg, " (wd: %s", wd); if (guest || dochroot) appendf(&msg, "; chrooted"); appendf(&msg, ")"); syslog(LOG_INFO, "%s", msg); free(msg); } static void logxfer(char *name, off_t size, time_t start) { char buf[MAXPATHLEN + 1024]; char path[MAXPATHLEN + 1]; time_t now; if (statfd >= 0) { time(&now); if (realpath(name, path) == NULL) { syslog(LOG_NOTICE, "realpath failed on %s: %m", path); return; } snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", ctime(&now)+4, ident, remotehost, path, (intmax_t)size, (long)(now - start + (now == start))); write(statfd, buf, strlen(buf)); } } static char * doublequote(char *s) { int n; char *p, *s2; for (p = s, n = 0; *p; p++) if (*p == '"') n++; if ((s2 = malloc(p - s + n + 1)) == NULL) return (NULL); for (p = s2; *s; s++, p++) { if ((*p = *s) == '"') *(++p) = '"'; } *p = '\0'; return (s2); } /* setup server socket for specified address family */ /* if af is PF_UNSPEC more than one socket may be returned */ /* the returned list is dynamically allocated, so caller needs to free it */ static int * socksetup(int af, char *bindname, const char *bindport) { struct addrinfo hints, *res, *r; int error, maxs, *s, *socks; const int on = 1; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(bindname, bindport, &hints, &res); if (error) { syslog(LOG_ERR, "%s", gai_strerror(error)); if (error == EAI_SYSTEM) syslog(LOG_ERR, "%s", strerror(errno)); return NULL; } /* Count max number of sockets we may open */ for (maxs = 0, r = res; r; r = r->ai_next, maxs++) ; socks = malloc((maxs + 1) * sizeof(int)); if (!socks) { freeaddrinfo(res); syslog(LOG_ERR, "couldn't allocate memory for sockets"); return NULL; } *socks = 0; /* num of sockets counter at start of array */ s = socks + 1; for (r = res; r; r = r->ai_next) { *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); if (*s < 0) { syslog(LOG_DEBUG, "control socket: %m"); continue; } if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt (SO_REUSEADDR): %m"); if (r->ai_family == AF_INET6) { if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt (IPV6_V6ONLY): %m"); } if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { syslog(LOG_DEBUG, "control bind: %m"); close(*s); continue; } (*socks)++; s++; } if (res) freeaddrinfo(res); if (*socks == 0) { syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); free(socks); return NULL; } return(socks); } Index: projects/cambria/sbin/kldstat/kldstat.c =================================================================== --- projects/cambria/sbin/kldstat/kldstat.c (revision 186459) +++ projects/cambria/sbin/kldstat/kldstat.c (revision 186460) @@ -1,159 +1,161 @@ /*- * Copyright (c) 1997 Doug Rabson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #define POINTER_WIDTH ((int)(sizeof(void *) * 2 + 2)) static void printmod(int modid) { struct module_stat stat; stat.version = sizeof(struct module_stat); if (modstat(modid, &stat) < 0) warn("can't stat module id %d", modid); else printf("\t\t%2d %s\n", stat.id, stat.name); } static void printfile(int fileid, int verbose) { struct kld_file_stat stat; int modid; stat.version = sizeof(struct kld_file_stat); if (kldstat(fileid, &stat) < 0) warn("can't stat file id %d", fileid); else - printf("%2d %4d %p %-8jx %s (%s)\n", + printf("%2d %4d %p %-8jx %s", stat.id, stat.refs, stat.address, (uintmax_t)stat.size, - stat.name, stat.pathname); + stat.name); if (verbose) { + printf(" (%s)\n", stat.pathname); printf("\tContains modules:\n"); printf("\t\tId Name\n"); for (modid = kldfirstmod(fileid); modid > 0; modid = modfnext(modid)) printmod(modid); - } + } else + printf("\n"); } static void usage(void) { fprintf(stderr, "usage: kldstat [-v] [-i id] [-n filename]\n"); fprintf(stderr, " kldstat [-q] [-m modname]\n"); exit(1); } int main(int argc, char** argv) { int c; int verbose = 0; int fileid = 0; int quiet = 0; char* filename = NULL; char* modname = NULL; char* p; while ((c = getopt(argc, argv, "i:m:n:qv")) != -1) switch (c) { case 'i': fileid = (int)strtoul(optarg, &p, 10); if (*p != '\0') usage(); break; case 'm': modname = optarg; break; case 'n': filename = optarg; break; case 'q': quiet = 1; break; case 'v': verbose = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc != 0) usage(); if (modname != NULL) { int modid; struct module_stat stat; if ((modid = modfind(modname)) < 0) { if (!quiet) warn("can't find module %s", modname); return 1; } else if (quiet) { return 0; } stat.version = sizeof(struct module_stat); if (modstat(modid, &stat) < 0) warn("can't stat module id %d", modid); else { printf("Id Refs Name\n"); printf("%3d %4d %s\n", stat.id, stat.refs, stat.name); } return 0; } if (filename != NULL) { if ((fileid = kldfind(filename)) < 0) err(1, "can't find file %s", filename); } printf("Id Refs Address%*c Size Name\n", POINTER_WIDTH - 7, ' '); if (fileid != 0) printfile(fileid, verbose); else for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) printfile(fileid, verbose); return 0; } Index: projects/cambria/sbin/mount_msdosfs/mount_msdosfs.8 =================================================================== --- projects/cambria/sbin/mount_msdosfs/mount_msdosfs.8 (revision 186459) +++ projects/cambria/sbin/mount_msdosfs/mount_msdosfs.8 (revision 186460) @@ -1,226 +1,231 @@ .\" $NetBSD: mount_msdos.8,v 1.13 1998/02/06 05:57:00 perry Exp $ .\" .\" Copyright (c) 1993,1994 Christopher G. Demetriou .\" 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 acknowledgment: .\" This product includes software developed by Christopher G. Demetriou. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. .\" .\" $FreeBSD$ .\" -.Dd April 7, 1994 +.Dd December 23, 2008 .Dt MOUNT_MSDOSFS 8 .Os .Sh NAME .Nm mount_msdosfs .Nd mount an MS-DOS file system .Sh SYNOPSIS .Nm .Op Fl 9ls .Op Fl D Ar DOS_codepage .Op Fl g Ar gid .Op Fl L Ar locale .Op Fl M Ar mask .Op Fl m Ar mask .Op Fl o Ar options .Op Fl u Ar uid .Op Fl W Ar table .Ar special node .Sh DESCRIPTION The .Nm utility attaches the MS-DOS file system residing on the device .Pa special to the global file system namespace at the location indicated by .Pa node . This command is normally executed by .Xr mount 8 at boot time, but can be used by any user to mount an MS-DOS file system on any directory that they own (provided, of course, that they have appropriate access to the device that contains the file system). .Pp The options are as follows: .Bl -tag -width Ds .It Fl o Ar options Use the specified mount .Ar options , as described in .Xr mount 8 . The following MSDOS file system-specific options are available: .Bl -tag -width indent +.It Cm large +Support file systems larger than 128 gigabytes at the expense +of 32 bytes of kernel memory. +This memory will not be reclaimed until the file system has +been unmounted. .It Cm longnames Force Windows 95 long filenames to be visible. .It Cm shortnames Force only the old MS-DOS 8.3 style filenames to be visible. .It Cm nowin95 Completely ignore Windows 95 extended file information. .El .It Fl u Ar uid Set the owner of the files in the file system to .Ar uid . The default owner is the owner of the directory on which the file system is being mounted. .It Fl g Ar gid Set the group of the files in the file system to .Ar gid . The default group is the group of the directory on which the file system is being mounted. .It Fl m Ar mask Specify the maximum file permissions for files in the file system. (For example, a .Ar mask of .Li 755 specifies that, by default, the owner should have read, write, and execute permissions for files, but others should only have read and execute permissions. See .Xr chmod 1 for more information about octal file modes. Only the nine low-order bits of .Ar mask are used. The value of .Ar -M is used if it is supplied and .Ar -m is omitted. The default .Ar mask is taken from the directory on which the file system is being mounted. .It Fl M Ar mask Specify the maximum file permissions for directories in the file system. The value of .Ar -m is used if it is supplied and .Ar -M is omitted. See the previous option's description for details. .It Fl s Force behaviour to ignore and not generate Win'95 long filenames. .It Fl l Force listing and generation of Win'95 long filenames and separate creation/modification/access dates. .Pp If neither .Fl s nor .Fl l are given, .Nm searches the root directory of the file system to be mounted for any existing Win'95 long filenames. If no such entries are found, but short DOS filenames are found, .Fl s is the default. Otherwise .Fl l is assumed. .It Fl 9 Ignore the special Win'95 directory entries even if deleting or renaming a file. This forces .Fl s . .\".It Fl G .\"This option causes the file system to be interpreted as an Atari-Gemdos .\"file system. .\"The differences to the MS-DOS file system are minimal and .\"limited to the boot block. .\"This option enforces .\".Fl s . .It Fl L Ar locale Specify locale name used for file name conversions for DOS and Win'95 names. By default ISO 8859-1 assumed as local character set. .It Fl D Ar DOS_codepage Specify the MS-DOS code page (aka IBM/OEM code page) name used for file name conversions for DOS names. .It Fl W Ar table .Bf Em This option is preserved for backward compatibility purpose only, and will be removed in the future. Please avoid using this option. .Ef .Pp Specify text file name with conversion table: .Pa iso22dos , iso72dos , koi2dos , koi8u2dos . .El .Sh EXAMPLES To mount a Russian MS-DOS file system located in .Pa /dev/ad1s1 : .Pp .Dl "mount_msdosfs -L ru_RU.KOI8-R -D CP866 /dev/ad1s1 /mnt" .Pp To mount a Japanese MS-DOS file system located in .Pa /dev/ad1s1 : .Pp .Dl "mount_msdosfs -L ja_JP.eucJP -D CP932 /dev/ad1s1 /mnt" .Sh SEE ALSO .Xr mount 2 , .Xr unmount 2 , .Xr fstab 5 , .Xr msdosfs 5 , .Xr mount 8 .Pp List of Localized MS Operating Systems: .Pa http://www.microsoft.com/globaldev/reference/oslocversion.mspx . .Sh CAVEATS The use of the .Fl 9 flag could result in damaged file systems, albeit the damage is in part taken care of by procedures similar to the ones used in Win'95. .Pp .Fx 2.1 and earlier versions could not handle cluster sizes larger than 16K. Just mounting an MS-DOS file system could cause corruption to any mounted file system. Cluster sizes larger than 16K are unavoidable for file system sizes larger than 1G, and also occur when file systems larger than 1G are shrunk to smaller than 1G using FIPS. .Sh HISTORY The .Nm utility first appeared in .Fx 2.0 . Its predecessor, the .Nm mount_pcfs utility appeared in .Fx 1.0 , and was abandoned in favor of the more aptly-named .Nm . .Pp The character code conversion routine was added by .An Ryuichiro Imura Aq imura@ryu16.org at 2003. Index: projects/cambria/sbin/shutdown/shutdown.8 =================================================================== --- projects/cambria/sbin/shutdown/shutdown.8 (revision 186459) +++ projects/cambria/sbin/shutdown/shutdown.8 (revision 186460) @@ -1,190 +1,196 @@ .\" Copyright (c) 1988, 1991, 1993 .\" 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. .\" 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. .\" .\" @(#)shutdown.8 8.2 (Berkeley) 4/27/95 .\" $FreeBSD$ .\" -.Dd December 11, 1998 +.Dd December 23, 2008 .Dt SHUTDOWN 8 .Os .Sh NAME .Nm shutdown .Nd "close down the system at a given time" .Sh SYNOPSIS .Nm .Op Fl .Oo .Fl h | Fl p | .Fl r | Fl k .Oc .Oo .Fl o .Op Fl n .Oc .Ar time .Op Ar warning-message ... .Sh DESCRIPTION The .Nm utility provides an automated shutdown procedure for super-users to nicely notify users when the system is shutting down, saving them from system administrators, hackers, and gurus, who would otherwise not bother with such niceties. .Pp The following options are available: .Bl -tag -width indent .It Fl h The system is halted at the specified .Ar time . .It Fl p The system is halted and the power is turned off (hardware support required) at the specified .Ar time . .It Fl r The system is rebooted at the specified .Ar time . .It Fl k Kick everybody off. The .Fl k option does not actually halt the system, but leaves the system multi-user with logins disabled (for all but super-user). .It Fl o If one of the .Fl h , .Fl p or .Fl r is specified, .Nm will execute .Xr halt 8 or .Xr reboot 8 instead of sending signal to .Xr init 8 . .It Fl n If the .Fl o is specified, prevent the file system cache from being flushed by passing .Fl n option to .Xr halt 8 or .Xr reboot 8 . This option should probably not be used. .It Ar time .Ar Time is the time at which .Nm will bring the system down and may be the word .Ar now (indicating an immediate shutdown) or specify a future time in one of two formats: .Ar +number , or .Ar yymmddhhmm , where the year, month, and day may be defaulted to the current system values. The first form brings the system down in .Ar number minutes and the second at the absolute time specified. .It Ar warning-message Any other arguments comprise the warning message that is broadcast to users currently logged into the system. .It Fl If .Sq Fl is supplied as an option, the warning message is read from the standard input. .El .Pp At intervals, becoming more frequent as apocalypse approaches and starting at ten hours before shutdown, warning messages are displayed on the terminals of all users logged in. Five minutes before shutdown, or immediately if shutdown is in less than 5 minutes, logins are disabled by creating .Pa /var/run/nologin and copying the warning message there. If this file exists when a user attempts to log in, .Xr login 1 prints its contents and exits. The file is removed just before .Nm exits. .Pp At shutdown time a message is written to the system log, containing the time of shutdown, the person who initiated the shutdown and the reason. Corresponding signal is then sent to .Xr init 8 to respectively halt, reboot or bring the system down to single-user state (depending on the above options). The time of the shutdown and the warning message are placed in .Pa /var/run/nologin and should be used to inform the users about when the system will be back up and why it is going down (or anything else). .Pp A scheduled shutdown can be canceled by killing the .Nm process (a .Dv SIGTERM should suffice). The .Pa /var/run/nologin file that .Nm created will be removed automatically. +.Pp +When run without options, the +.Nm +utility will place the system into single user mode at the +.Ar time +specified. .Sh FILES .Bl -tag -width /var/run/nologin -compact .It Pa /var/run/nologin tells login not to let anyone log in .El .Sh COMPATIBILITY The hours and minutes in the second time format may be separated by a colon (``:'') for backward compatibility. .Sh SEE ALSO .Xr kill 1 , .Xr login 1 , .Xr wall 1 , .Xr nologin 5 , .Xr halt 8 , .Xr init 8 , .Xr reboot 8 .Sh HISTORY The .Nm utility appeared in .Bx 4.0 . Index: projects/cambria/share/man/man4/uscanner.4 =================================================================== --- projects/cambria/share/man/man4/uscanner.4 (revision 186459) +++ projects/cambria/share/man/man4/uscanner.4 (revision 186460) @@ -1,163 +1,164 @@ .\" Copyright (c) 2000, Jeroen Ruigrok van der Werven .\" 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 Bill Paul. .\" 4. Neither the name of the author nor the names of any co-contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY NICK HIBMA 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 NICK HIBMA OR THE VOICES IN HIS HEAD .\" 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. .\" .\" $FreeBSD$ .\" -.Dd April 28, 2008 +.Dd December 23, 2008 .Dt USCANNER 4 .Os .Sh NAME .Nm uscanner .Nd USB Scanners .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device uscanner" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent uscanner_load="YES" .Ed .Sh DESCRIPTION The .Nm driver provides support for scanners that attach to the USB port. For each scanner attached it will provide an entry like .Pa /dev/uscanner0 in .Pa /dev . User-space programs, e.g. SANE, can in turn use the device entry throgh a suitable backend driver (see .Nm ports/graphics/sane-backends ) to control the scanner itself. .Pp In case of multifunction USB devices (e.g. scanner/printer/card reader), this driver only attaches to the USB interface that controls the scanner; for the other USB interface you need separate drivers, e.g. .Nm ulpt , .Nm umass , and so on. .Pp .Nm usb and one of .Nm uhci or .Nm ohci must be configured in the kernel as well. .Sh HARDWARE Because there is no standard device class for USB scanners, this driver will only recognise devices whose USB IDs are explicitly listed in the table in the driver itself. .Pp The following devices are supported to date: .Pp .Bl -bullet -compact .It Acer Acerscan: 320U, 620U, 640U, 640BT, 1240U, C310U; .It AGFA SnapScan: 1212U, 1236U, e20, e25, e26, e40, e50, e52, SnapScan Touch; .It Avision 1200U .It Canon CanoScan: D660U, N656U, N676U, N1220U, LIDE 20, LIDE 25, LIDE 30; .It Epson Perfection: 610, 636U / 636Photo, 640U, 1200U / 1200Photo, 1240U / 1240Photo, 1250, 1260, 1270, 1600, 1640SU, 1650, 1660, 1670, 2480, 3200, 3500, 3590, 4200, 4990; .It Epson: GT-8400UF, GT-9300UF, GT-9700F; .It Epson Stylus: Photo RX425, CX3650, DX-5000, DX-5050, CX5400, DX-6000, DX-6050, DX-8400/50 (and possibly more in the CX-5000 and DX-3800..DX-7000 family); .It Hewlett Packard: Photosmart S20 .It Hewlett Packard Scanjet: 2200C, 3300C, 3400CSE, 4100C, 4200C, 4300C, +4470C, 5200C, 5300C, 5400C, 6200C, 6300C, 8200C, 8250C, 8290C; .It KYE ColorPage Vivid-Pro .It Microtek Phantom: 336CX, C6; .It Microtek ScanMaker: V6UL V6USL, X6U; .It Minolta 5400 .It Mustek: 600 CU, 1200 CU, 1200 UB, 1200 USB; .It Mustek BearPaw: 1200F, 1200TA; .It NatSemi BearPaw 1200 .It Nikon CoolScan LS40 ED .It Primax 6200 .It Primax Colorado: 1200u, 600u, USB 19200, USB 9600; .It Primax: G2-200, G2-300, G2-600, G2600, G2E-300, G2E-3002, G2E-600, G2E600, G2X-300, G600, ReadyScan 636i; .It Ultima 1200 UB Plus .It UMAX Astra: 1220U, 1236U, 2000U, 2100U, 2200U, 3400; .It Visioneer OneTouch: 3000, 5300, 7600, 6100, 6200, 8100, 8600; .El .Sh SEE ALSO .Xr ohci 4 , .Xr uhci 4 , .Xr usb 4 .\".Sh HISTORY .Sh AUTHORS .An -nosplit The .Nm driver was written by .An Nick Hibma Aq n_hibma@FreeBSD.org . .Pp This manual page was written by .An Jeroen Ruigrok van der Werven Aq asmodai@FreeBSD.org . Index: projects/cambria/share/man/man5/nsswitch.conf.5 =================================================================== --- projects/cambria/share/man/man5/nsswitch.conf.5 (revision 186459) +++ projects/cambria/share/man/man5/nsswitch.conf.5 (revision 186460) @@ -1,380 +1,384 @@ .\" $NetBSD: nsswitch.conf.5,v 1.14 1999/03/17 20:19:47 garbled Exp $ .\" .\" Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation .\" by Luke Mewburn. .\" .\" 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 Luke Mewburn. .\" 4. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. .\" .\" $FreeBSD$ .\" -.Dd January 22, 2007 +.Dd December 23, 2008 .Dt NSSWITCH.CONF 5 .Os .Sh NAME .Nm nsswitch.conf .Nd name-service switch configuration file .Sh DESCRIPTION The .Nm file specifies how the .Xr nsdispatch 3 (name-service switch dispatcher) routines in the C library should operate. .Pp The configuration file controls how a process looks up various databases containing information regarding hosts, users (passwords), groups, etc. Each database comes from a source (such as local files, DNS, .Tn NIS , and cache), and the order to look up the sources is specified in .Nm . .Pp Each entry in .Nm consists of a database name, and a space separated list of sources. Each source can have an optional trailing criterion that determines whether the next listed source is used, or the search terminates at the current source. Each criterion consists of one or more status codes, and actions to take if that status code occurs. .Ss Sources The following sources are implemented: .Pp .Bl -tag -width Source -compact .It Sy Source .Sy Description .It files Local files, such as .Pa /etc/hosts , and .Pa /etc/passwd . .It dns Internet Domain Name System. .Dq hosts and .Sq networks use .Sy IN class entries, all other databases use .Sy HS class (Hesiod) entries. .It nis NIS (formerly YP) .It compat support .Sq +/- in the .Dq passwd and .Dq group databases. If this is present, it must be the only source for that entry. .It cache makes use of the .Xr nscd 8 daemon. .El .Ss Databases The following databases are used by the following C library functions: .Pp .Bl -tag -width networks -compact .It Sy Database .Sy "Used by" .It group .Xr getgrent 3 , .Xr getgrent_r 3 , .Xr getgrgid_r 3 , .Xr getgrnam_r 3 , .Xr setgrent 3 , .Xr endgrent 3 .It hosts .Xr getaddrinfo 3 , .Xr gethostbyaddr 3 , .Xr gethostbyaddr_r 3 , .Xr gethostbyname 3 , .Xr gethostbyname2 3 , .Xr gethostbyname_r 3 , .Xr getipnodebyaddr 3 , .Xr getipnodebyname 3 .It networks .Xr getnetbyaddr 3 , .Xr getnetbyaddr_r 3 , .Xr getnetbyname 3 , .Xr getnetbyname_r 3 .It passwd .Xr getpwent 3 , .Xr getpwent_r 3 , .Xr getpwnam_r 3 , .Xr getpwuid_r 3 , .Xr setpwent 3 , .Xr endpwent 3 .It shells .Xr getusershell 3 .It services .Xr getservent 3 .It rpc .Xr getrpcbyname 3 , .Xr getrpcbynumber 3 , .Xr getrpcent 3 .It proto -.Xr getprotobyname 3 +.Xr getprotobyname 3 , .Xr getprotobynumber 3 , .Xr getprotoent 3 +.It netgroup +.Xr getnetgrent 3 , +.Xr setnetgrent 3 , +.Xr innetgr 3 .El .Ss Status codes The following status codes are available: .Pp .Bl -tag -width tryagain -compact .It Sy Status .Sy Description .It success The requested entry was found. .It notfound The entry is not present at this source. .It tryagain The source is busy, and may respond to retries. .It unavail The source is not responding, or entry is corrupt. .El .Ss Actions For each of the status codes, one of two actions is possible: .Pp .Bl -tag -width continue -compact .It Sy Action .Sy Description .It continue Try the next source .It return Return with the current result .El .Ss Format of file A .Tn BNF description of the syntax of .Nm is: .Pp .Bl -tag -width -compact .It ::= ":" [ []]* .It ::= "[" + "]" .It ::= "=" .It ::= "success" | "notfound" | "unavail" | "tryagain" .It ::= "return" | "continue" .El .Pp Each entry starts on a new line in the file. A .Sq # delimits a comment to end of line. Blank lines are ignored. A .Sq \e at the end of a line escapes the newline, and causes the next line to be a continuation of the current line. All entries are case-insensitive. .Pp The default criteria is to return on .Dq success , and continue on anything else (i.e, .Li "[success=return notfound=continue unavail=continue tryagain=continue]" ) . .Ss Cache You can enable caching for the particular database by specifying .Dq cache as the first source in the .Xr nsswitch.conf 5 file. You should also enable caching for this database in .Xr nscd.conf 5 . If for the particular query .Dq cache source returns success, no further sources are queried. On the other hand, if there are no previously cached data, the query result will be placed into the cache right after all other sources are processed. Note, that .Dq cache requires .Xr nscd 8 daemon to be running. .Ss Compat mode: +/- syntax In historical multi-source implementations, the .Sq + and .Sq - characters are used to specify the importing of user password and group information from .Tn NIS . Although .Nm provides alternative methods of accessing distributed sources such as .Tn NIS , specifying a sole source of .Dq compat will provide the historical behaviour. .Pp An alternative source for the information accessed via .Sq +/- can be used by specifying .Dq passwd_compat: source . .Dq source in this case can be .Sq dns , .Sq nis , or any other source except for .Sq files and .Sq compat . .Ss Notes Historically, many of the databases had enumeration functions, often of the form .Fn getXXXent . These made sense when the databases were in local files, but do not make sense or have lesser relevance when there are possibly multiple sources, each of an unknown size. The interfaces are still provided for compatibility, but the source may not be able to provide complete entries, or duplicate entries may be retrieved if multiple sources that contain similar information are specified. .Pp To ensure compatibility with previous and current implementations, the .Dq compat source must appear alone for a given database. .Ss Default source lists If, for any reason, .Nm does not exist, or it has missing or corrupt entries, .Xr nsdispatch 3 will default to an entry of .Dq files for the requested database. Exceptions are: .Pp .Bl -tag -width services_compat -compact .It Sy Database .Sy "Default source list" .It group compat .It group_compat nis .It hosts files dns .It passwd compat .It passwd_compat nis .It services compat .It services_compat nis .El .Sh FILES .Bl -tag -width /etc/nsswitch.conf -compact .It Pa /etc/nsswitch.conf The file .Nm resides in .Pa /etc . .El .Sh EXAMPLES To lookup hosts in cache, then in .Pa /etc/hosts and then from the DNS, and lookup user information from .Tn NIS then files, use: .Pp .Bl -tag -width passwd: -compact .It hosts: cache files dns .It passwd: nis [notfound=return] files .It group: nis [notfound=return] files .El .Pp The criteria .Dq [notfound=return] sets a policy of "if the user is notfound in nis, do not try files." This treats nis as the authoritative source of information, except when the server is down. .Sh NOTES If system got compiled with .Va WITHOUT_NIS you have to remove .Sq nis entries. .Pp .Fx Ns 's .Lb libc provides stubs for compatibility with NSS modules written for the .Tn GNU C Library .Nm nsswitch interface. However, these stubs only support the use of the .Dq Li passwd and .Dq Li group databases. .Sh SEE ALSO .Xr nsdispatch 3 , .Xr nscd.conf 5 , .Xr resolv.conf 5 , .Xr nscd 8 , .Xr named 8 , .Xr ypbind 8 .Sh HISTORY The .Nm file format first appeared in .Fx 5.0 . It was imported from the .Nx Project, where it appeared first in .Nx 1.4 . .Sh AUTHORS Luke Mewburn .Aq lukem@netbsd.org wrote this freely distributable name-service switch implementation, using ideas from the .Tn ULTRIX .Xr svc.conf 5 and .Tn Solaris .Xr nsswitch.conf 4 manual pages. Index: projects/cambria/share/man/man9/domain.9 =================================================================== --- projects/cambria/share/man/man9/domain.9 (revision 186459) +++ projects/cambria/share/man/man9/domain.9 (revision 186460) @@ -1,229 +1,228 @@ .\" .\" Copyright (C) 2001 Chad David . 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(s), this list of conditions and the following disclaimer as .\" the first lines of this file unmodified other than the possible .\" addition of one or more copyright notices. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice(s), this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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. .\" .\" $FreeBSD$ .\" -.Dd December 7, 2001 +.Dd December 23, 2008 .Dt DOMAIN 9 .Os .Sh NAME .Nm net_add_domain , .Nm pfctlinput , .Nm pfctlinput2 , .Nm pffindproto , .Nm pffindtype , .Nm DOMAIN_SET .Nd "network domain management" .Sh SYNOPSIS .In sys/param.h .In sys/kernel.h .In sys/protosw.h .In sys/domain.h .Ft void .Fn net_add_domain "void *data" .Ft void .Fn pfctlinput "int cmd" "struct sockaddr *sa" .Ft void .Fn pfctlinput2 "int cmd" "struct sockaddr *sa" "void *ctlparam" .Ft struct protosw * .Fn pffindproto "int family" "int protocol" "int type" .Ft struct protosw * .Fn pffindtype "int family" "int type" .Ft void .Fn DOMAIN_SET "name" .Sh DESCRIPTION Network protocols installed in the system are maintained within what are called domains (for example the .Va inetdomain and .Va localdomain ) . .Bd -literal struct domain { int dom_family; /* AF_xxx */ char *dom_name; void (*dom_init) /* initialize domain data structures */ (void); int (*dom_externalize) /* externalize access rights */ (struct mbuf *, struct mbuf **); void (*dom_dispose) /* dispose of internalized rights */ (struct mbuf *); struct protosw *dom_protosw, *dom_protoswNPROTOSW; struct domain *dom_next; int (*dom_rtattach) /* initialize routing table */ (void **, int); int dom_rtoffset; /* an arg to rtattach, in bits */ int dom_maxrtkey; /* for routing layer */ }; .Ed .Pp Each domain contains an array of protocol switch structures .Pq Vt "struct protosw *" , one for each socket type supported. .Bd -literal struct protosw { short pr_type; /* socket type used for */ struct domain *pr_domain; /* domain protocol a member of */ short pr_protocol; /* protocol number */ short pr_flags; /* see below */ /* protocol-protocol hooks */ pr_input_t *pr_input; /* input to protocol (from below) */ pr_output_t *pr_output; /* output to protocol (from above) */ pr_ctlinput_t *pr_ctlinput; /* control input (from below) */ pr_ctloutput_t *pr_ctloutput; /* control output (from above) */ /* user-protocol hook */ pr_usrreq_t *pr_ousrreq; /* utility hooks */ pr_init_t *pr_init; pr_fasttimo_t *pr_fasttimo; /* fast timeout (200ms) */ pr_slowtimo_t *pr_slowtimo; /* slow timeout (500ms) */ pr_drain_t *pr_drain; /* flush any excess space possible */ struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */ - struct pfil_head pr_pfh; }; .Ed .Pp The following functions handle the registration of a new domain, lookups of specific protocols and protocol types within those domains, and handle control messages from the system. .Pp .Fn pfctlinput is called by the system whenever an event occurs that could affect every domain. Examples of those types of events are routing table changes, interface shutdowns or certain .Tn ICMP message types. When called, .Fn pfctlinput calls the protocol specific .Fn pr_ctlinput function for each protocol in that has defined one, in every domain. .Pp .Fn pfctlinput2 provides that same functionality of .Fn pfctlinput , but with a few additional checks and a new .Vt "void *" argument that is passed directly to the protocol's .Fn pr_ctlinput function. Unlike .Fn pfctlinput , .Fn pfctlinput2 verifies that .Fa sa is not .Dv NULL , and that only the protocol families that are the same as .Fa sa have their .Fn pr_ctlinput function called. .Pp .Fn net_add_domain adds a new protocol domain to the system. The argument .Fa data is cast directly to .Vt "struct domain *" within the function, but is declared .Vt "void *" in order to prevent compiler warnings when new domains are registered with .Fn SYSINIT . In most cases .Fn net_add_domain is not called directly, instead .Fn DOMAIN_SET is used. .Pp If the new domain has defined an initialization routine, it is called by .Fn net_add_domain ; as well, each of the protocols within the domain that have defined an initialization routine will have theirs called. .Pp Once a domain is added it cannot be unloaded. This is because there is no reference counting system in place to determine if there are any active references from sockets within that domain. .Pp .Fn pffindtype and .Fn pffindproto look up a protocol by its number or by its type. In most cases, if the protocol or type cannot be found, .Dv NULL is returned, but .Fn pffindproto may return the default if the requested type is .Dv SOCK_RAW , a protocol switch type of .Dv SOCK_RAW is found, and the domain has a default raw protocol. .Pp Both functions are called by .Fn socreate in order to resolve the protocol for the socket currently being created. .Pp .Fn DOMAIN_SET is a macro that simplifies the registration of a domain via .Fn SYSINIT . The code resulting from the macro expects there to be a domain structure named .Dq Fa name Ns Li domain where .Fa name is the argument to .Fn DOMAIN_SET : .Bd -literal struct domain localdomain = { AF_LOCAL, "local", unp_init, unp_externalize, unp_dispose, localsw, &localsw[sizeof(localsw)/sizeof(localsw[0])] }; DOMAIN_SET(local); .Ed .Sh RETURN VALUES Both .Fn pffindtype and .Fn pffindproto return a .Vt "struct protosw *" for the protocol requested. If the protocol or socket type is not found, .Dv NULL is returned. In the case of .Fn pffindproto , the default protocol may be returned for .Dv SOCK_RAW types if the domain has a default raw protocol. .Sh SEE ALSO .Xr socket 2 .Sh AUTHORS This manual page was written by .An Chad David Aq davidc@acns.ab.ca . Index: projects/cambria/sys/cam/cam_sim.h =================================================================== --- projects/cambria/sys/cam/cam_sim.h (revision 186459) +++ projects/cambria/sys/cam/cam_sim.h (revision 186460) @@ -1,158 +1,158 @@ /*- * Data structures and definitions for SCSI Interface Modules (SIMs). * * Copyright (c) 1997 Justin T. Gibbs. * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _CAM_CAM_SIM_H #define _CAM_CAM_SIM_H 1 #ifdef _KERNEL /* * The sim driver creates a sim for each controller. The sim device * queue is separately created in order to allow resource sharing between * sims. For instance, a driver may create one sim for each channel of * a multi-channel controller and use the same queue for each channel. * In this way, the queue resources are shared across all the channels * of the multi-channel controller. */ struct cam_sim; struct cam_devq; typedef void (*sim_action_func)(struct cam_sim *sim, union ccb *ccb); typedef void (*sim_poll_func)(struct cam_sim *sim); struct cam_devq * cam_simq_alloc(u_int32_t max_sim_transactions); void cam_simq_free(struct cam_devq *devq); struct cam_sim * cam_sim_alloc(sim_action_func sim_action, sim_poll_func sim_poll, const char *sim_name, void *softc, u_int32_t unit, struct mtx *mtx, int max_dev_transactions, int max_tagged_dev_transactions, struct cam_devq *queue); void cam_sim_free(struct cam_sim *sim, int free_devq); void cam_sim_hold(struct cam_sim *sim); void cam_sim_release(struct cam_sim *sim); /* Optional sim attributes may be set with these. */ void cam_sim_set_path(struct cam_sim *sim, u_int32_t path_id); /* Convenience routines for accessing sim attributes. */ static __inline u_int32_t cam_sim_path(struct cam_sim *sim); static __inline const char * cam_sim_name(struct cam_sim *sim); static __inline void * cam_sim_softc(struct cam_sim *sim); static __inline u_int32_t cam_sim_unit(struct cam_sim *sim); static __inline u_int32_t cam_sim_bus(struct cam_sim *sim); /* Generically useful offsets into the sim private area */ #define spriv_ptr0 sim_priv.entries[0].ptr #define spriv_ptr1 sim_priv.entries[1].ptr #define spriv_field0 sim_priv.entries[0].field #define spriv_field1 sim_priv.entries[1].field /* * The sim driver should not access anything directly from this * structure. */ struct cam_sim { sim_action_func sim_action; sim_poll_func sim_poll; const char *sim_name; void *softc; struct mtx *mtx; TAILQ_HEAD(, ccb_hdr) sim_doneq; TAILQ_ENTRY(cam_sim) links; u_int32_t path_id;/* The Boot device may set this to 0? */ u_int32_t unit_number; u_int32_t bus_id; int max_tagged_dev_openings; int max_dev_openings; u_int32_t flags; #define CAM_SIM_REL_TIMEOUT_PENDING 0x01 #define CAM_SIM_MPSAFE 0x02 #define CAM_SIM_ON_DONEQ 0x04 struct callout callout; struct cam_devq *devq; /* Device Queue to use for this SIM */ int refcount; /* References to the SIM. */ - /* "Pool" of inactive ccbs managed by xpt_alloc_ccb and xpt_free_ccb */ + /* "Pool" of inactive ccbs managed by xpt_get_ccb and xpt_release_ccb */ SLIST_HEAD(,ccb_hdr) ccb_freeq; /* * Maximum size of ccb pool. Modified as devices are added/removed * or have their * opening counts changed. */ u_int max_ccbs; /* Current count of allocated ccbs */ u_int ccb_count; }; #define CAM_SIM_LOCK(sim) mtx_lock((sim)->mtx); #define CAM_SIM_UNLOCK(sim) mtx_unlock((sim)->mtx); static __inline u_int32_t cam_sim_path(struct cam_sim *sim) { return (sim->path_id); } static __inline const char * cam_sim_name(struct cam_sim *sim) { return (sim->sim_name); } static __inline void * cam_sim_softc(struct cam_sim *sim) { return (sim->softc); } static __inline u_int32_t cam_sim_unit(struct cam_sim *sim) { return (sim->unit_number); } static __inline u_int32_t cam_sim_bus(struct cam_sim *sim) { return (sim->bus_id); } #endif /* _KERNEL */ #endif /* _CAM_CAM_SIM_H */ Index: projects/cambria/sys/cam/scsi/scsi_cd.c =================================================================== --- projects/cambria/sys/cam/scsi/scsi_cd.c (revision 186459) +++ projects/cambria/sys/cam/scsi/scsi_cd.c (revision 186460) @@ -1,4257 +1,4259 @@ /*- * Copyright (c) 1997 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Kenneth D. Merry. * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /*- * Portions of this driver taken from the original FreeBSD cd driver. * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * * from: cd.c,v 1.83 1997/05/04 15:24:22 joerg Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_cd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LEADOUT 0xaa /* leadout toc entry */ struct cd_params { u_int32_t blksize; u_long disksize; }; typedef enum { CD_Q_NONE = 0x00, CD_Q_NO_TOUCH = 0x01, CD_Q_BCD_TRACKS = 0x02, CD_Q_NO_CHANGER = 0x04, CD_Q_CHANGER = 0x08, CD_Q_10_BYTE_ONLY = 0x10 } cd_quirks; typedef enum { CD_FLAG_INVALID = 0x0001, CD_FLAG_NEW_DISC = 0x0002, CD_FLAG_DISC_LOCKED = 0x0004, CD_FLAG_DISC_REMOVABLE = 0x0008, CD_FLAG_TAGGED_QUEUING = 0x0010, CD_FLAG_CHANGER = 0x0040, CD_FLAG_ACTIVE = 0x0080, CD_FLAG_SCHED_ON_COMP = 0x0100, CD_FLAG_RETRY_UA = 0x0200, CD_FLAG_VALID_MEDIA = 0x0400, CD_FLAG_VALID_TOC = 0x0800, CD_FLAG_SCTX_INIT = 0x1000, CD_FLAG_OPEN = 0x2000 } cd_flags; typedef enum { CD_CCB_PROBE = 0x01, CD_CCB_BUFFER_IO = 0x02, CD_CCB_WAITING = 0x03, CD_CCB_TYPE_MASK = 0x0F, CD_CCB_RETRY_UA = 0x10 } cd_ccb_state; typedef enum { CHANGER_TIMEOUT_SCHED = 0x01, CHANGER_SHORT_TMOUT_SCHED = 0x02, CHANGER_MANUAL_CALL = 0x04, CHANGER_NEED_TIMEOUT = 0x08 } cd_changer_flags; #define ccb_state ppriv_field0 #define ccb_bp ppriv_ptr1 struct cd_tocdata { struct ioc_toc_header header; struct cd_toc_entry entries[100]; }; struct cd_toc_single { struct ioc_toc_header header; struct cd_toc_entry entry; }; typedef enum { CD_STATE_PROBE, CD_STATE_NORMAL } cd_state; struct cd_softc { cam_pinfo pinfo; cd_state state; volatile cd_flags flags; struct bio_queue_head bio_queue; LIST_HEAD(, ccb_hdr) pending_ccbs; struct cd_params params; union ccb saved_ccb; cd_quirks quirks; STAILQ_ENTRY(cd_softc) changer_links; struct cdchanger *changer; int bufs_left; struct cam_periph *periph; int minimum_command_size; int outstanding_cmds; struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; STAILQ_HEAD(, cd_mode_params) mode_queue; struct cd_tocdata toc; struct disk *disk; }; struct cd_page_sizes { int page; int page_size; }; static struct cd_page_sizes cd_page_size_table[] = { { AUDIO_PAGE, sizeof(struct cd_audio_page)} }; struct cd_quirk_entry { struct scsi_inquiry_pattern inq_pat; cd_quirks quirks; }; /* * The changer quirk entries aren't strictly necessary. Basically, what * they do is tell cdregister() up front that a device is a changer. * Otherwise, it will figure that fact out once it sees a LUN on the device * that is greater than 0. If it is known up front that a device is a changer, * all I/O to the device will go through the changer scheduling routines, as * opposed to the "normal" CD code. * * NOTE ON 10_BYTE_ONLY quirks: Any 10_BYTE_ONLY quirks MUST be because * your device hangs when it gets a 10 byte command. Adding a quirk just * to get rid of the informative diagnostic message is not acceptable. All * 10_BYTE_ONLY quirks must be documented in full in a PR (which should be * referenced in a comment along with the quirk) , and must be approved by * ken@FreeBSD.org. Any quirks added that don't adhere to this policy may * be removed until the submitter can explain why they are needed. * 10_BYTE_ONLY quirks will be removed (as they will no longer be necessary) * when the CAM_NEW_TRAN_CODE work is done. */ static struct cd_quirk_entry cd_quirk_table[] = { { { T_CDROM, SIP_MEDIA_REMOVABLE, "NRC", "MBR-7", "*"}, /*quirks*/ CD_Q_CHANGER }, { { T_CDROM, SIP_MEDIA_REMOVABLE, "PIONEER", "CD-ROM DRM*", "*"}, /* quirks */ CD_Q_CHANGER }, { { T_CDROM, SIP_MEDIA_REMOVABLE, "NAKAMICH", "MJ-*", "*"}, /* quirks */ CD_Q_CHANGER }, { { T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"}, /* quirks */ CD_Q_BCD_TRACKS } }; static disk_open_t cdopen; static disk_close_t cdclose; static disk_ioctl_t cdioctl; static disk_strategy_t cdstrategy; static periph_init_t cdinit; static periph_ctor_t cdregister; static periph_dtor_t cdcleanup; static periph_start_t cdstart; static periph_oninv_t cdoninvalidate; static void cdasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS); static void cdshorttimeout(void *arg); static void cdschedule(struct cam_periph *periph, int priority); static void cdrunchangerqueue(void *arg); static void cdchangerschedule(struct cd_softc *softc); static int cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags), u_int32_t cam_flags, u_int32_t sense_flags); static union ccb *cdgetccb(struct cam_periph *periph, u_int32_t priority); static void cddone(struct cam_periph *periph, union ccb *start_ccb); static union cd_pages *cdgetpage(struct cd_mode_params *mode_params); static int cdgetpagesize(int page_num); static void cdprevent(struct cam_periph *periph, int action); static int cdcheckmedia(struct cam_periph *periph); static int cdsize(struct cam_periph *periph, u_int32_t *size); static int cd6byteworkaround(union ccb *ccb); static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static int cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, u_int8_t *data, u_int32_t len, u_int32_t sense_flags); static int cdgetmode(struct cam_periph *periph, struct cd_mode_params *data, u_int32_t page); static int cdsetmode(struct cam_periph *periph, struct cd_mode_params *data); static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len); static int cdreadsubchannel(struct cam_periph *periph, u_int32_t mode, u_int32_t format, int track, struct cd_sub_channel_info *data, u_int32_t len); static int cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts, u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf); static int cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex, u_int32_t etrack, u_int32_t eindex); static int cdpause(struct cam_periph *periph, u_int32_t go); static int cdstopunit(struct cam_periph *periph, u_int32_t eject); static int cdstartunit(struct cam_periph *periph, int load); static int cdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed); static int cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo); static int cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo); static int cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct); static struct periph_driver cddriver = { cdinit, "cd", TAILQ_HEAD_INITIALIZER(cddriver.units), /* generation */ 0 }; PERIPHDRIVER_DECLARE(cd, cddriver); #ifndef CHANGER_MIN_BUSY_SECONDS #define CHANGER_MIN_BUSY_SECONDS 5 #endif #ifndef CHANGER_MAX_BUSY_SECONDS #define CHANGER_MAX_BUSY_SECONDS 15 #endif static int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS; static int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS; SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver"); SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer"); SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW, &changer_min_busy_seconds, 0, "Minimum changer scheduling quantum"); TUNABLE_INT("kern.cam.cd.changer.min_busy_seconds", &changer_min_busy_seconds); SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW, &changer_max_busy_seconds, 0, "Maximum changer scheduling quantum"); TUNABLE_INT("kern.cam.cd.changer.max_busy_seconds", &changer_max_busy_seconds); struct cdchanger { path_id_t path_id; target_id_t target_id; int num_devices; struct camq devq; struct timeval start_time; struct cd_softc *cur_device; struct callout short_handle; struct callout long_handle; volatile cd_changer_flags flags; STAILQ_ENTRY(cdchanger) changer_links; STAILQ_HEAD(chdevlist, cd_softc) chluns; }; static struct mtx changerq_mtx; static STAILQ_HEAD(changerlist, cdchanger) changerq; static int num_changers; MALLOC_DEFINE(M_SCSICD, "scsi_cd", "scsi_cd buffers"); static void cdinit(void) { cam_status status; mtx_init(&changerq_mtx, "cdchangerq", "SCSI CD Changer List", MTX_DEF); STAILQ_INIT(&changerq); /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, cdasync, NULL, NULL); if (status != CAM_REQ_CMP) { printf("cd: Failed to attach master async callback " "due to status 0x%x!\n", status); } } static void cdoninvalidate(struct cam_periph *periph) { struct cd_softc *softc; softc = (struct cd_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_register_async(0, cdasync, periph, periph->path); softc->flags |= CD_FLAG_INVALID; /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ bioq_flush(&softc->bio_queue, NULL, ENXIO); /* * If this device is part of a changer, and it was scheduled * to run, remove it from the run queue since we just nuked * all of its scheduled I/O. */ if ((softc->flags & CD_FLAG_CHANGER) && (softc->pinfo.index != CAM_UNQUEUED_INDEX)) camq_remove(&softc->changer->devq, softc->pinfo.index); disk_gone(softc->disk); xpt_print(periph->path, "lost device\n"); } static void cdcleanup(struct cam_periph *periph) { struct cd_softc *softc; softc = (struct cd_softc *)periph->softc; xpt_print(periph->path, "removing device entry\n"); if ((softc->flags & CD_FLAG_SCTX_INIT) != 0 && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { xpt_print(periph->path, "can't remove sysctl context\n"); } /* * In the queued, non-active case, the device in question * has already been removed from the changer run queue. Since this * device is active, we need to de-activate it, and schedule * another device to run. (if there is another one to run) */ if ((softc->flags & CD_FLAG_CHANGER) && (softc->flags & CD_FLAG_ACTIVE)) { /* * The purpose of the short timeout is soley to determine * whether the current device has finished or not. Well, * since we're removing the active device, we know that it * is finished. So, get rid of the short timeout. * Otherwise, if we're in the time period before the short * timeout fires, and there are no other devices in the * queue to run, there won't be any other device put in the * active slot. i.e., when we call cdrunchangerqueue() * below, it won't do anything. Then, when the short * timeout fires, it'll look at the "current device", which * we are free below, and possibly panic the kernel on a * bogus pointer reference. * * The long timeout doesn't really matter, since we * decrement the qfrozen_cnt to indicate that there is * nothing in the active slot now. Therefore, there won't * be any bogus pointer references there. */ if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { callout_stop(&softc->changer->short_handle); softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } softc->changer->devq.qfrozen_cnt--; softc->changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(softc->changer); } /* * If we're removing the last device on the changer, go ahead and * remove the changer device structure. */ if ((softc->flags & CD_FLAG_CHANGER) && (--softc->changer->num_devices == 0)) { /* * Theoretically, there shouldn't be any timeouts left, but * I'm not completely sure that that will be the case. So, * it won't hurt to check and see if there are any left. */ if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) { callout_stop(&softc->changer->long_handle); softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED; } if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { callout_stop(&softc->changer->short_handle); softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } mtx_lock(&changerq_mtx); STAILQ_REMOVE(&changerq, softc->changer, cdchanger, changer_links); num_changers--; mtx_unlock(&changerq_mtx); xpt_print(periph->path, "removing changer entry\n"); free(softc->changer, M_DEVBUF); } cam_periph_unlock(periph); disk_destroy(softc->disk); cam_periph_lock(periph); free(softc, M_DEVBUF); } static void cdasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; if (SID_TYPE(&cgd->inq_data) != T_CDROM && SID_TYPE(&cgd->inq_data) != T_WORM) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ status = cam_periph_alloc(cdregister, cdoninvalidate, cdcleanup, cdstart, "cd", CAM_PERIPH_BIO, cgd->ccb_h.path, cdasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) printf("cdasync: Unable to attach new device " "due to status 0x%x\n", status); break; } case AC_SENT_BDR: case AC_BUS_RESET: { struct cd_softc *softc; struct ccb_hdr *ccbh; softc = (struct cd_softc *)periph->softc; /* * Don't fail on the expected unit attention * that will occur. */ softc->flags |= CD_FLAG_RETRY_UA; LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le) ccbh->ccb_state |= CD_CCB_RETRY_UA; /* FALLTHROUGH */ } default: cam_periph_async(periph, code, path, arg); break; } } static void cdsysctlinit(void *context, int pending) { struct cam_periph *periph; struct cd_softc *softc; char tmpstr[80], tmpstr2[80]; periph = (struct cam_periph *)context; if (cam_periph_acquire(periph) != CAM_REQ_CMP) return; softc = (struct cd_softc *)periph->softc; snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); mtx_lock(&Giant); sysctl_ctx_init(&softc->sysctl_ctx); softc->flags |= CD_FLAG_SCTX_INIT; softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kern_cam_cd), OID_AUTO, tmpstr2, CTLFLAG_RD, 0, tmpstr); if (softc->sysctl_tree == NULL) { printf("cdsysctlinit: unable to allocate sysctl tree\n"); mtx_unlock(&Giant); cam_periph_release(periph); return; } /* * Now register the sysctl handler, so the user can the value on * the fly. */ SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, &softc->minimum_command_size, 0, cdcmdsizesysctl, "I", "Minimum CDB size"); mtx_unlock(&Giant); cam_periph_release(periph); } /* * We have a handler function for this so we can check the values when the * user sets them, instead of every time we look at them. */ static int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS) { int error, value; value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if ((error != 0) || (req->newptr == NULL)) return (error); /* * The only real values we can have here are 6 or 10. I don't * really forsee having 12 be an option at any time in the future. * So if the user sets something less than or equal to 6, we'll set * it to 6. If he sets something greater than 6, we'll set it to 10. * * I suppose we could just return an error here for the wrong values, * but I don't think it's necessary to do so, as long as we can * determine the user's intent without too much trouble. */ if (value < 6) value = 6; else if (value > 6) value = 10; *(int *)arg1 = value; return (0); } static cam_status cdregister(struct cam_periph *periph, void *arg) { struct cd_softc *softc; struct ccb_pathinq cpi; struct ccb_getdev *cgd; char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { printf("cdregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (cgd == NULL) { printf("cdregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct cd_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); if (softc == NULL) { printf("cdregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } bzero(softc, sizeof(*softc)); LIST_INIT(&softc->pending_ccbs); STAILQ_INIT(&softc->mode_queue); softc->state = CD_STATE_PROBE; bioq_init(&softc->bio_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) softc->flags |= CD_FLAG_DISC_REMOVABLE; if ((cgd->inq_data.flags & SID_CmdQue) != 0) softc->flags |= CD_FLAG_TAGGED_QUEUING; periph->softc = softc; softc->periph = periph; /* * See if this device has any quirks. */ match = cam_quirkmatch((caddr_t)&cgd->inq_data, (caddr_t)cd_quirk_table, sizeof(cd_quirk_table)/sizeof(*cd_quirk_table), sizeof(*cd_quirk_table), scsi_inquiry_match); if (match != NULL) softc->quirks = ((struct cd_quirk_entry *)match)->quirks; else softc->quirks = CD_Q_NONE; /* Check if the SIM does not want 6 byte commands */ xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) softc->quirks |= CD_Q_10_BYTE_ONLY; TASK_INIT(&softc->sysctl_task, 0, cdsysctlinit, periph); /* The default is 6 byte commands, unless quirked otherwise */ if (softc->quirks & CD_Q_10_BYTE_ONLY) softc->minimum_command_size = 10; else softc->minimum_command_size = 6; /* * Load the user's default, if any. */ snprintf(tmpstr, sizeof(tmpstr), "kern.cam.cd.%d.minimum_cmd_size", periph->unit_number); TUNABLE_INT_FETCH(tmpstr, &softc->minimum_command_size); /* 6 and 10 are the only permissible values here. */ if (softc->minimum_command_size < 6) softc->minimum_command_size = 6; else if (softc->minimum_command_size > 6) softc->minimum_command_size = 10; /* * We need to register the statistics structure for this device, * but we don't have the blocksize yet for it. So, we register * the structure and indicate that we don't have the blocksize * yet. Unlike other SCSI peripheral drivers, we explicitly set * the device type here to be CDROM, rather than just ORing in * the device type. This is because this driver can attach to either * CDROM or WORM devices, and we want this peripheral driver to * show up in the devstat list as a CD peripheral driver, not a * WORM peripheral driver. WORM drives will also have the WORM * driver attached to them. */ cam_periph_unlock(periph); softc->disk = disk_alloc(); softc->disk->d_devstat = devstat_new_entry("cd", periph->unit_number, 0, DEVSTAT_BS_UNAVAILABLE, DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_CD); softc->disk->d_open = cdopen; softc->disk->d_close = cdclose; softc->disk->d_strategy = cdstrategy; softc->disk->d_ioctl = cdioctl; softc->disk->d_name = "cd"; softc->disk->d_unit = periph->unit_number; softc->disk->d_drv1 = periph; softc->disk->d_flags = 0; disk_create(softc->disk, DISK_VERSION); cam_periph_lock(periph); /* * Add an async callback so that we get * notified if this device goes away. */ xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE, cdasync, periph, periph->path); /* * If the target lun is greater than 0, we most likely have a CD * changer device. Check the quirk entries as well, though, just * in case someone has a CD tower with one lun per drive or * something like that. Also, if we know up front that a * particular device is a changer, we can mark it as such starting * with lun 0, instead of lun 1. It shouldn't be necessary to have * a quirk entry to define something as a changer, however. */ if (((cgd->ccb_h.target_lun > 0) && ((softc->quirks & CD_Q_NO_CHANGER) == 0)) || ((softc->quirks & CD_Q_CHANGER) != 0)) { struct cdchanger *nchanger; struct cam_periph *nperiph; struct cam_path *path; cam_status status; int found; /* Set the changer flag in the current device's softc */ softc->flags |= CD_FLAG_CHANGER; /* * Now, look around for an existing changer device with the * same path and target ID as the current device. */ mtx_lock(&changerq_mtx); for (found = 0, nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq); nchanger != NULL; nchanger = STAILQ_NEXT(nchanger, changer_links)){ if ((nchanger->path_id == cgd->ccb_h.path_id) && (nchanger->target_id == cgd->ccb_h.target_id)) { found = 1; break; } } mtx_unlock(&changerq_mtx); /* * If we found a matching entry, just add this device to * the list of devices on this changer. */ if (found == 1) { struct chdevlist *chlunhead; chlunhead = &nchanger->chluns; /* * XXX KDM look at consolidating this code with the * code below in a separate function. */ /* * Create a path with lun id 0, and see if we can * find a matching device */ status = xpt_create_path(&path, /*periph*/ periph, cgd->ccb_h.path_id, cgd->ccb_h.target_id, 0); if ((status == CAM_REQ_CMP) && ((nperiph = cam_periph_find(path, "cd")) != NULL)){ struct cd_softc *nsoftc; nsoftc = (struct cd_softc *)nperiph->softc; if ((nsoftc->flags & CD_FLAG_CHANGER) == 0){ nsoftc->flags |= CD_FLAG_CHANGER; nchanger->num_devices++; if (camq_resize(&nchanger->devq, nchanger->num_devices)!=CAM_REQ_CMP){ printf("cdregister: " "camq_resize " "failed, changer " "support may " "be messed up\n"); } nsoftc->changer = nchanger; nsoftc->pinfo.index =CAM_UNQUEUED_INDEX; STAILQ_INSERT_TAIL(&nchanger->chluns, nsoftc,changer_links); } xpt_free_path(path); } else if (status == CAM_REQ_CMP) xpt_free_path(path); else { printf("cdregister: unable to allocate path\n" "cdregister: changer support may be " "broken\n"); } nchanger->num_devices++; softc->changer = nchanger; softc->pinfo.index = CAM_UNQUEUED_INDEX; if (camq_resize(&nchanger->devq, nchanger->num_devices) != CAM_REQ_CMP) { printf("cdregister: camq_resize " "failed, changer support may " "be messed up\n"); } STAILQ_INSERT_TAIL(chlunhead, softc, changer_links); } /* * In this case, we don't already have an entry for this * particular changer, so we need to create one, add it to * the queue, and queue this device on the list for this * changer. Before we queue this device, however, we need * to search for lun id 0 on this target, and add it to the * queue first, if it exists. (and if it hasn't already * been marked as part of the changer.) */ else { nchanger = malloc(sizeof(struct cdchanger), M_DEVBUF, M_NOWAIT); if (nchanger == NULL) { softc->flags &= ~CD_FLAG_CHANGER; printf("cdregister: unable to malloc " "changer structure\ncdregister: " "changer support disabled\n"); /* * Yes, gotos can be gross but in this case * I think it's justified.. */ goto cdregisterexit; } /* zero the structure */ bzero(nchanger, sizeof(struct cdchanger)); if (camq_init(&nchanger->devq, 1) != 0) { softc->flags &= ~CD_FLAG_CHANGER; printf("cdregister: changer support " "disabled\n"); goto cdregisterexit; } nchanger->path_id = cgd->ccb_h.path_id; nchanger->target_id = cgd->ccb_h.target_id; /* this is superfluous, but it makes things clearer */ nchanger->num_devices = 0; STAILQ_INIT(&nchanger->chluns); callout_init_mtx(&nchanger->long_handle, periph->sim->mtx, 0); callout_init_mtx(&nchanger->short_handle, periph->sim->mtx, 0); mtx_lock(&changerq_mtx); num_changers++; STAILQ_INSERT_TAIL(&changerq, nchanger, changer_links); mtx_unlock(&changerq_mtx); /* * Create a path with lun id 0, and see if we can * find a matching device */ status = xpt_create_path(&path, /*periph*/ periph, cgd->ccb_h.path_id, cgd->ccb_h.target_id, 0); /* * If we were able to allocate the path, and if we * find a matching device and it isn't already * marked as part of a changer, then we add it to * the current changer. */ if ((status == CAM_REQ_CMP) && ((nperiph = cam_periph_find(path, "cd")) != NULL) && ((((struct cd_softc *)periph->softc)->flags & CD_FLAG_CHANGER) == 0)) { struct cd_softc *nsoftc; nsoftc = (struct cd_softc *)nperiph->softc; nsoftc->flags |= CD_FLAG_CHANGER; nchanger->num_devices++; if (camq_resize(&nchanger->devq, nchanger->num_devices) != CAM_REQ_CMP) { printf("cdregister: camq_resize " "failed, changer support may " "be messed up\n"); } nsoftc->changer = nchanger; nsoftc->pinfo.index = CAM_UNQUEUED_INDEX; STAILQ_INSERT_TAIL(&nchanger->chluns, nsoftc, changer_links); xpt_free_path(path); } else if (status == CAM_REQ_CMP) xpt_free_path(path); else { printf("cdregister: unable to allocate path\n" "cdregister: changer support may be " "broken\n"); } softc->changer = nchanger; softc->pinfo.index = CAM_UNQUEUED_INDEX; nchanger->num_devices++; if (camq_resize(&nchanger->devq, nchanger->num_devices) != CAM_REQ_CMP) { printf("cdregister: camq_resize " "failed, changer support may " "be messed up\n"); } STAILQ_INSERT_TAIL(&nchanger->chluns, softc, changer_links); } } cdregisterexit: /* * Refcount and block open attempts until we are setup * Can't block */ (void)cam_periph_hold(periph, PRIBIO); if ((softc->flags & CD_FLAG_CHANGER) == 0) xpt_schedule(periph, /*priority*/5); else cdschedule(periph, /*priority*/ 5); return(CAM_REQ_CMP); } static int cdopen(struct disk *dp) { struct cam_periph *periph; struct cd_softc *softc; int error; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) return (ENXIO); softc = (struct cd_softc *)periph->softc; if (cam_periph_acquire(periph) != CAM_REQ_CMP) return(ENXIO); cam_periph_lock(periph); if (softc->flags & CD_FLAG_INVALID) { cam_periph_unlock(periph); cam_periph_release(periph); return(ENXIO); } if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (error); } - /* Closes aren't symmetrical with opens, so fix up the refcounting. */ - if (softc->flags & CD_FLAG_OPEN) - cam_periph_release(periph); - else - softc->flags |= CD_FLAG_OPEN; - /* * Check for media, and set the appropriate flags. We don't bail * if we don't have media, but then we don't allow anything but the * CDIOCEJECT/CDIOCCLOSE ioctls if there is no media. */ cdcheckmedia(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n")); cam_periph_unhold(periph); - cam_periph_unlock(periph); + + /* Closes aren't symmetrical with opens, so fix up the refcounting. */ + if ((softc->flags & CD_FLAG_OPEN) == 0) { + softc->flags |= CD_FLAG_OPEN; + cam_periph_unlock(periph); + } else { + cam_periph_unlock(periph); + cam_periph_release(periph); + } return (0); } static int cdclose(struct disk *dp) { struct cam_periph *periph; struct cd_softc *softc; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) return (ENXIO); softc = (struct cd_softc *)periph->softc; cam_periph_lock(periph); cam_periph_hold(periph, PRIBIO); if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0) cdprevent(periph, PR_ALLOW); /* * Since we're closing this CD, mark the blocksize as unavailable. * It will be marked as available when the CD is opened again. */ softc->disk->d_devstat->flags |= DEVSTAT_BS_UNAVAILABLE; /* * We'll check the media and toc again at the next open(). */ softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC|CD_FLAG_OPEN); cam_periph_unhold(periph); cam_periph_unlock(periph); cam_periph_release(periph); return (0); } static void cdshorttimeout(void *arg) { struct cdchanger *changer; changer = (struct cdchanger *)arg; /* Always clear the short timeout flag, since that's what we're in */ changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; /* * Check to see if there is any more pending or outstanding I/O for * this device. If not, move it out of the active slot. */ if ((bioq_first(&changer->cur_device->bio_queue) == NULL) && (changer->cur_device->outstanding_cmds == 0)) { changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(changer); } } /* * This is a wrapper for xpt_schedule. It only applies to changers. */ static void cdschedule(struct cam_periph *periph, int priority) { struct cd_softc *softc; softc = (struct cd_softc *)periph->softc; /* * If this device isn't currently queued, and if it isn't * the active device, then we queue this device and run the * changer queue if there is no timeout scheduled to do it. * If this device is the active device, just schedule it * to run again. If this device is queued, there should be * a timeout in place already that will make sure it runs. */ if ((softc->pinfo.index == CAM_UNQUEUED_INDEX) && ((softc->flags & CD_FLAG_ACTIVE) == 0)) { /* * We don't do anything with the priority here. * This is strictly a fifo queue. */ softc->pinfo.priority = 1; softc->pinfo.generation = ++softc->changer->devq.generation; camq_insert(&softc->changer->devq, (cam_pinfo *)softc); /* * Since we just put a device in the changer queue, * check and see if there is a timeout scheduled for * this changer. If so, let the timeout handle * switching this device into the active slot. If * not, manually call the timeout routine to * bootstrap things. */ if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0) && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0) && ((softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED)==0)){ softc->changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(softc->changer); } } else if ((softc->flags & CD_FLAG_ACTIVE) && ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0)) xpt_schedule(periph, priority); } static void cdrunchangerqueue(void *arg) { struct cd_softc *softc; struct cdchanger *changer; int called_from_timeout; changer = (struct cdchanger *)arg; /* * If we have NOT been called from cdstrategy() or cddone(), and * instead from a timeout routine, go ahead and clear the * timeout flag. */ if ((changer->flags & CHANGER_MANUAL_CALL) == 0) { changer->flags &= ~CHANGER_TIMEOUT_SCHED; called_from_timeout = 1; } else called_from_timeout = 0; /* Always clear the manual call flag */ changer->flags &= ~CHANGER_MANUAL_CALL; /* nothing to do if the queue is empty */ if (changer->devq.entries <= 0) { return; } /* * If the changer queue is frozen, that means we have an active * device. */ if (changer->devq.qfrozen_cnt > 0) { /* * We always need to reset the frozen count and clear the * active flag. */ changer->devq.qfrozen_cnt--; changer->cur_device->flags &= ~CD_FLAG_ACTIVE; changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP; if (changer->cur_device->outstanding_cmds > 0) { changer->cur_device->flags |= CD_FLAG_SCHED_ON_COMP; changer->cur_device->bufs_left = changer->cur_device->outstanding_cmds; if (called_from_timeout) { callout_reset(&changer->long_handle, changer_max_busy_seconds * hz, cdrunchangerqueue, changer); changer->flags |= CHANGER_TIMEOUT_SCHED; } return; } /* * Check to see whether the current device has any I/O left * to do. If so, requeue it at the end of the queue. If * not, there is no need to requeue it. */ if (bioq_first(&changer->cur_device->bio_queue) != NULL) { changer->cur_device->pinfo.generation = ++changer->devq.generation; camq_insert(&changer->devq, (cam_pinfo *)changer->cur_device); } } softc = (struct cd_softc *)camq_remove(&changer->devq, CAMQ_HEAD); changer->cur_device = softc; changer->devq.qfrozen_cnt++; softc->flags |= CD_FLAG_ACTIVE; /* Just in case this device is waiting */ wakeup(&softc->changer); xpt_schedule(softc->periph, /*priority*/ 1); /* * Get rid of any pending timeouts, and set a flag to schedule new * ones so this device gets its full time quantum. */ if (changer->flags & CHANGER_TIMEOUT_SCHED) { callout_stop(&changer->long_handle); changer->flags &= ~CHANGER_TIMEOUT_SCHED; } if (changer->flags & CHANGER_SHORT_TMOUT_SCHED) { callout_stop(&changer->short_handle); changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } /* * We need to schedule timeouts, but we only do this after the * first transaction has completed. This eliminates the changer * switch time. */ changer->flags |= CHANGER_NEED_TIMEOUT; } static void cdchangerschedule(struct cd_softc *softc) { struct cdchanger *changer; changer = softc->changer; /* * If this is a changer, and this is the current device, * and this device has at least the minimum time quantum to * run, see if we can switch it out. */ if ((softc->flags & CD_FLAG_ACTIVE) && ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) && ((changer->flags & CHANGER_NEED_TIMEOUT) == 0)) { /* * We try three things here. The first is that we * check to see whether the schedule on completion * flag is set. If it is, we decrement the number * of buffers left, and if it's zero, we reschedule. * Next, we check to see whether the pending buffer * queue is empty and whether there are no * outstanding transactions. If so, we reschedule. * Next, we see if the pending buffer queue is empty. * If it is, we set the number of buffers left to * the current active buffer count and set the * schedule on complete flag. */ if (softc->flags & CD_FLAG_SCHED_ON_COMP) { if (--softc->bufs_left == 0) { softc->changer->flags |= CHANGER_MANUAL_CALL; softc->flags &= ~CD_FLAG_SCHED_ON_COMP; cdrunchangerqueue(softc->changer); } } else if ((bioq_first(&softc->bio_queue) == NULL) && (softc->outstanding_cmds == 0)) { softc->changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(softc->changer); } } else if ((softc->changer->flags & CHANGER_NEED_TIMEOUT) && (softc->flags & CD_FLAG_ACTIVE)) { /* * Now that the first transaction to this * particular device has completed, we can go ahead * and schedule our timeouts. */ if ((changer->flags & CHANGER_TIMEOUT_SCHED) == 0) { callout_reset(&changer->long_handle, changer_max_busy_seconds * hz, cdrunchangerqueue, changer); changer->flags |= CHANGER_TIMEOUT_SCHED; } else printf("cdchangerschedule: already have a long" " timeout!\n"); if ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) { callout_reset(&changer->short_handle, changer_min_busy_seconds * hz, cdshorttimeout, changer); changer->flags |= CHANGER_SHORT_TMOUT_SCHED; } else printf("cdchangerschedule: already have a short " "timeout!\n"); /* * We just scheduled timeouts, no need to schedule * more. */ changer->flags &= ~CHANGER_NEED_TIMEOUT; } } static int cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags), u_int32_t cam_flags, u_int32_t sense_flags) { struct cd_softc *softc; struct cam_periph *periph; int error; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; error = cam_periph_runccb(ccb, error_routine, cam_flags, sense_flags, softc->disk->d_devstat); if (softc->flags & CD_FLAG_CHANGER) cdchangerschedule(softc); return(error); } static union ccb * cdgetccb(struct cam_periph *periph, u_int32_t priority) { struct cd_softc *softc; softc = (struct cd_softc *)periph->softc; if (softc->flags & CD_FLAG_CHANGER) { /* * This should work the first time this device is woken up, * but just in case it doesn't, we use a while loop. */ while ((softc->flags & CD_FLAG_ACTIVE) == 0) { /* * If this changer isn't already queued, queue it up. */ if (softc->pinfo.index == CAM_UNQUEUED_INDEX) { softc->pinfo.priority = 1; softc->pinfo.generation = ++softc->changer->devq.generation; camq_insert(&softc->changer->devq, (cam_pinfo *)softc); } if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0) && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0) && ((softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED)==0)) { softc->changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(softc->changer); } else msleep(&softc->changer, periph->sim->mtx, PRIBIO, "cgticb", 0); } } return(cam_periph_getccb(periph, priority)); } /* * Actually translate the requested transfer into one the physical driver * can understand. The transfer is described by a buf and will include * only one physical transfer. */ static void cdstrategy(struct bio *bp) { struct cam_periph *periph; struct cd_softc *softc; periph = (struct cam_periph *)bp->bio_disk->d_drv1; if (periph == NULL) { biofinish(bp, NULL, ENXIO); return; } cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstrategy\n")); softc = (struct cd_softc *)periph->softc; /* * If the device has been made invalid, error out */ if ((softc->flags & CD_FLAG_INVALID)) { cam_periph_unlock(periph); biofinish(bp, NULL, ENXIO); return; } /* * If we don't have valid media, look for it before trying to * schedule the I/O. */ if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) { int error; error = cdcheckmedia(periph); if (error != 0) { cam_periph_unlock(periph); biofinish(bp, NULL, error); return; } } /* * Place it in the queue of disk activities for this disk */ bioq_disksort(&softc->bio_queue, bp); /* * Schedule ourselves for performing the work. We do things * differently for changers. */ if ((softc->flags & CD_FLAG_CHANGER) == 0) xpt_schedule(periph, /* XXX priority */1); else cdschedule(periph, /* priority */ 1); cam_periph_unlock(periph); return; } static void cdstart(struct cam_periph *periph, union ccb *start_ccb) { struct cd_softc *softc; struct bio *bp; struct ccb_scsiio *csio; struct scsi_read_capacity_data *rcap; softc = (struct cd_softc *)periph->softc; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstart\n")); switch (softc->state) { case CD_STATE_NORMAL: { bp = bioq_first(&softc->bio_queue); if (periph->immediate_priority <= periph->pinfo.priority) { start_ccb->ccb_h.ccb_state = CD_CCB_WAITING; SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, periph_links.sle); periph->immediate_priority = CAM_PRIORITY_NONE; wakeup(&periph->ccb_list); } else if (bp == NULL) { xpt_release_ccb(start_ccb); } else { bioq_remove(&softc->bio_queue, bp); devstat_start_transaction_bio(softc->disk->d_devstat, bp); scsi_read_write(&start_ccb->csio, /*retries*/4, /* cbfcnp */ cddone, MSG_SIMPLE_Q_TAG, /* read */bp->bio_cmd == BIO_READ, /* byte2 */ 0, /* minimum_cmd_size */ 10, /* lba */ bp->bio_offset / softc->params.blksize, bp->bio_bcount / softc->params.blksize, /* data_ptr */ bp->bio_data, /* dxfer_len */ bp->bio_bcount, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 30000); start_ccb->ccb_h.ccb_state = CD_CCB_BUFFER_IO; LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h, periph_links.le); softc->outstanding_cmds++; /* We expect a unit attention from this device */ if ((softc->flags & CD_FLAG_RETRY_UA) != 0) { start_ccb->ccb_h.ccb_state |= CD_CCB_RETRY_UA; softc->flags &= ~CD_FLAG_RETRY_UA; } start_ccb->ccb_h.ccb_bp = bp; bp = bioq_first(&softc->bio_queue); xpt_action(start_ccb); } if (bp != NULL) { /* Have more work to do, so ensure we stay scheduled */ xpt_schedule(periph, /* XXX priority */1); } break; } case CD_STATE_PROBE: { rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap), M_SCSICD, M_NOWAIT); if (rcap == NULL) { xpt_print(periph->path, "cdstart: Couldn't malloc read_capacity data\n"); /* cd_free_periph??? */ break; } csio = &start_ccb->csio; scsi_read_capacity(csio, /*retries*/1, cddone, MSG_SIMPLE_Q_TAG, rcap, SSD_FULL_SIZE, /*timeout*/20000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = CD_CCB_PROBE; xpt_action(start_ccb); break; } } } static void cddone(struct cam_periph *periph, union ccb *done_ccb) { struct cd_softc *softc; struct ccb_scsiio *csio; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cddone\n")); softc = (struct cd_softc *)periph->softc; csio = &done_ccb->csio; switch (csio->ccb_h.ccb_state & CD_CCB_TYPE_MASK) { case CD_CCB_BUFFER_IO: { struct bio *bp; int error; bp = (struct bio *)done_ccb->ccb_h.ccb_bp; error = 0; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { int sf; if ((done_ccb->ccb_h.ccb_state & CD_CCB_RETRY_UA) != 0) sf = SF_RETRY_UA; else sf = 0; error = cderror(done_ccb, CAM_RETRY_SELTO, sf); if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } } if (error != 0) { xpt_print(periph->path, "cddone: got error %#x back\n", error); bioq_flush(&softc->bio_queue, NULL, EIO); bp->bio_resid = bp->bio_bcount; bp->bio_error = error; bp->bio_flags |= BIO_ERROR; cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } else { bp->bio_resid = csio->resid; bp->bio_error = 0; if (bp->bio_resid != 0) { /* * Short transfer ??? * XXX: not sure this is correct for partial * transfers at EOM */ bp->bio_flags |= BIO_ERROR; } } LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); softc->outstanding_cmds--; if (softc->flags & CD_FLAG_CHANGER) cdchangerschedule(softc); biofinish(bp, NULL, 0); break; } case CD_CCB_PROBE: { struct scsi_read_capacity_data *rdcap; char announce_buf[120]; /* * Currently (9/30/97) the * longest possible announce * buffer is 108 bytes, for the * first error case below. * That is 39 bytes for the * basic string, 16 bytes for the * biggest sense key (hardware * error), 52 bytes for the * text of the largest sense * qualifier valid for a CDROM, * (0x72, 0x03 or 0x04, * 0x03), and one byte for the * null terminating character. * To allow for longer strings, * the announce buffer is 120 * bytes. */ struct cd_params *cdp; cdp = &softc->params; rdcap = (struct scsi_read_capacity_data *)csio->data_ptr; cdp->disksize = scsi_4btoul (rdcap->addr) + 1; cdp->blksize = scsi_4btoul (rdcap->length); if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { snprintf(announce_buf, sizeof(announce_buf), "cd present [%lu x %lu byte records]", cdp->disksize, (u_long)cdp->blksize); } else { int error; /* * Retry any UNIT ATTENTION type errors. They * are expected at boot. */ error = cderror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA | SF_NO_PRINT); if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } else if (error != 0) { struct scsi_sense_data *sense; int asc, ascq; int sense_key, error_code; int have_sense; cam_status status; struct ccb_getdev cgd; /* Don't wedge this device's queue */ cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); status = done_ccb->ccb_h.status; xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, /* priority */ 1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); if (((csio->ccb_h.flags & CAM_SENSE_PHYS) != 0) || ((csio->ccb_h.flags & CAM_SENSE_PTR) != 0) || ((status & CAM_AUTOSNS_VALID) == 0)) have_sense = FALSE; else have_sense = TRUE; if (have_sense) { sense = &csio->sense_data; scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); } /* * Attach to anything that claims to be a * CDROM or WORM device, as long as it * doesn't return a "Logical unit not * supported" (0x25) error. */ if ((have_sense) && (asc != 0x25) && (error_code == SSD_CURRENT_ERROR)) { const char *sense_key_desc; const char *asc_desc; scsi_sense_desc(sense_key, asc, ascq, &cgd.inq_data, &sense_key_desc, &asc_desc); snprintf(announce_buf, sizeof(announce_buf), "Attempt to query device " "size failed: %s, %s", sense_key_desc, asc_desc); } else if ((have_sense == 0) && ((status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) && (csio->scsi_status == SCSI_STATUS_BUSY)) { snprintf(announce_buf, sizeof(announce_buf), "Attempt to query device " "size failed: SCSI Status: %s", scsi_status_string(csio)); } else if (SID_TYPE(&cgd.inq_data) == T_CDROM) { /* * We only print out an error for * CDROM type devices. For WORM * devices, we don't print out an * error since a few WORM devices * don't support CDROM commands. * If we have sense information, go * ahead and print it out. * Otherwise, just say that we * couldn't attach. */ /* * Just print out the error, not * the full probe message, when we * don't attach. */ if (have_sense) scsi_sense_print( &done_ccb->csio); else { xpt_print(periph->path, "got CAM status %#x\n", done_ccb->ccb_h.status); } xpt_print(periph->path, "fatal error, " "failed to attach to device\n"); /* * Invalidate this peripheral. */ cam_periph_invalidate(periph); announce_buf[0] = '\0'; } else { /* * Invalidate this peripheral. */ cam_periph_invalidate(periph); announce_buf[0] = '\0'; } } } free(rdcap, M_SCSICD); if (announce_buf[0] != '\0') { xpt_announce_periph(periph, announce_buf); if (softc->flags & CD_FLAG_CHANGER) cdchangerschedule(softc); /* * Create our sysctl variables, now that we know * we have successfully attached. */ taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task); } softc->state = CD_STATE_NORMAL; /* * Since our peripheral may be invalidated by an error * above or an external event, we must release our CCB * before releasing the probe lock on the peripheral. * The peripheral will only go away once the last lock * is removed, and we need it around for the CCB release * operation. */ xpt_release_ccb(done_ccb); cam_periph_unhold(periph); return; } case CD_CCB_WAITING: { /* Caller will release the CCB */ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("trying to wakeup ccbwait\n")); wakeup(&done_ccb->ccb_h.cbfcnp); return; } default: break; } xpt_release_ccb(done_ccb); } static union cd_pages * cdgetpage(struct cd_mode_params *mode_params) { union cd_pages *page; if (mode_params->cdb_size == 10) page = (union cd_pages *)find_mode_page_10( (struct scsi_mode_header_10 *)mode_params->mode_buf); else page = (union cd_pages *)find_mode_page_6( (struct scsi_mode_header_6 *)mode_params->mode_buf); return (page); } static int cdgetpagesize(int page_num) { int i; for (i = 0; i < (sizeof(cd_page_size_table)/ sizeof(cd_page_size_table[0])); i++) { if (cd_page_size_table[i].page == page_num) return (cd_page_size_table[i].page_size); } return (-1); } static int cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) { struct cam_periph *periph; struct cd_softc *softc; int nocopyout, error = 0; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) return(ENXIO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdioctl\n")); softc = (struct cd_softc *)periph->softc; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("trying to do ioctl %#lx\n", cmd)); if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (error); } /* * If we don't have media loaded, check for it. If still don't * have media loaded, we can only do a load or eject. * * We only care whether media is loaded if this is a cd-specific ioctl * (thus the IOCGROUP check below). Note that this will break if * anyone adds any ioctls into the switch statement below that don't * have their ioctl group set to 'c'. */ if (((softc->flags & CD_FLAG_VALID_MEDIA) == 0) && ((cmd != CDIOCCLOSE) && (cmd != CDIOCEJECT)) && (IOCGROUP(cmd) == 'c')) { error = cdcheckmedia(periph); if (error != 0) { cam_periph_unhold(periph); cam_periph_unlock(periph); return (error); } } /* * Drop the lock here so later mallocs can use WAITOK. The periph * is essentially locked still with the cam_periph_hold call above. */ cam_periph_unlock(periph); nocopyout = 0; switch (cmd) { case CDIOCPLAYTRACKS: { struct ioc_play_track *args = (struct ioc_play_track *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.flags &= ~CD_PA_SOTC; page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); if (error) { cam_periph_unlock(periph); break; } /* * This was originally implemented with the PLAY * AUDIO TRACK INDEX command, but that command was * deprecated after SCSI-2. Most (all?) SCSI CDROM * drives support it but ATAPI and ATAPI-derivative * drives don't seem to support it. So we keep a * cache of the table of contents and translate * track numbers to MSF format. */ if (softc->flags & CD_FLAG_VALID_TOC) { union msf_lba *sentry, *eentry; int st, et; if (args->end_track < softc->toc.header.ending_track + 1) args->end_track++; if (args->end_track > softc->toc.header.ending_track + 1) args->end_track = softc->toc.header.ending_track + 1; st = args->start_track - softc->toc.header.starting_track; et = args->end_track - softc->toc.header.starting_track; if ((st < 0) || (et < 0) || (st > (softc->toc.header.ending_track - softc->toc.header.starting_track))) { error = EINVAL; break; } sentry = &softc->toc.entries[st].addr; eentry = &softc->toc.entries[et].addr; error = cdplaymsf(periph, sentry->msf.minute, sentry->msf.second, sentry->msf.frame, eentry->msf.minute, eentry->msf.second, eentry->msf.frame); } else { /* * If we don't have a valid TOC, try the * play track index command. It is part of * the SCSI-2 spec, but was removed in the * MMC specs. ATAPI and ATAPI-derived * drives don't support it. */ if (softc->quirks & CD_Q_BCD_TRACKS) { args->start_track = bin2bcd(args->start_track); args->end_track = bin2bcd(args->end_track); } error = cdplaytracks(periph, args->start_track, args->start_index, args->end_track, args->end_index); } cam_periph_unlock(periph); } break; case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.flags &= ~CD_PA_SOTC; page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); if (error) { cam_periph_unlock(periph); break; } error = cdplaymsf(periph, args->start_m, args->start_s, args->start_f, args->end_m, args->end_s, args->end_f); cam_periph_unlock(periph); } break; case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args = (struct ioc_play_blocks *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYBLOCKS\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.flags &= ~CD_PA_SOTC; page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); if (error) { cam_periph_unlock(periph); break; } error = cdplay(periph, args->blk, args->len); cam_periph_unlock(periph); } break; case CDIOCREADSUBCHANNEL_SYSSPACE: nocopyout = 1; /* Fallthrough */ case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel *) addr; struct cd_sub_channel_info *data; u_int32_t len = args->data_len; data = malloc(sizeof(struct cd_sub_channel_info), M_SCSICD, M_WAITOK); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCREADSUBCHANNEL\n")); if ((len > sizeof(struct cd_sub_channel_info)) || (len < sizeof(struct cd_sub_channel_header))) { printf( "scsi_cd: cdioctl: " "cdioreadsubchannel: error, len=%d\n", len); error = EINVAL; free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) args->track = bin2bcd(args->track); error = cdreadsubchannel(periph, args->address_format, args->data_format, args->track, data, len); if (error) { free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) data->what.track_info.track_number = bcd2bin(data->what.track_info.track_number); len = min(len, ((data->header.data_len[0] << 8) + data->header.data_len[1] + sizeof(struct cd_sub_channel_header))); cam_periph_unlock(periph); if (nocopyout == 0) { if (copyout(data, args->data, len) != 0) { error = EFAULT; } } else { bcopy(data, args->data, len); } free(data, M_SCSICD); } break; case CDIOREADTOCHEADER: { struct ioc_toc_header *th; th = malloc(sizeof(struct ioc_toc_header), M_SCSICD, M_WAITOK); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCHEADER\n")); error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, sizeof (*th), /*sense_flags*/0); if (error) { free(th, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { /* we are going to have to convert the BCD * encoding on the cd to what is expected */ th->starting_track = bcd2bin(th->starting_track); th->ending_track = bcd2bin(th->ending_track); } th->len = ntohs(th->len); bcopy(th, addr, sizeof(*th)); free(th, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOREADTOCENTRYS: { struct cd_tocdata *data; struct cd_toc_single *lead; struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *) addr; struct ioc_toc_header *th; u_int32_t len, readlen, idx, num; u_int32_t starting_track = te->starting_track; data = malloc(sizeof(*data), M_SCSICD, M_WAITOK); lead = malloc(sizeof(*lead), M_SCSICD, M_WAITOK); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCENTRYS\n")); if (te->data_len < sizeof(struct cd_toc_entry) || (te->data_len % sizeof(struct cd_toc_entry)) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT)) { error = EINVAL; printf("scsi_cd: error in readtocentries, " "returning EINVAL\n"); free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } th = &data->header; error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { /* we are going to have to convert the BCD * encoding on the cd to what is expected */ th->starting_track = bcd2bin(th->starting_track); th->ending_track = bcd2bin(th->ending_track); } if (starting_track == 0) starting_track = th->starting_track; else if (starting_track == LEADOUT) starting_track = th->ending_track + 1; else if (starting_track < th->starting_track || starting_track > th->ending_track + 1) { printf("scsi_cd: error in readtocentries, " "returning EINVAL\n"); free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); error = EINVAL; break; } /* calculate reading length without leadout entry */ readlen = (th->ending_track - starting_track + 1) * sizeof(struct cd_toc_entry); /* and with leadout entry */ len = readlen + sizeof(struct cd_toc_entry); if (te->data_len < len) { len = te->data_len; if (readlen > len) readlen = len; } if (len > sizeof(data->entries)) { printf("scsi_cd: error in readtocentries, " "returning EINVAL\n"); error = EINVAL; free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } num = len / sizeof(struct cd_toc_entry); if (readlen > 0) { error = cdreadtoc(periph, te->address_format, starting_track, (u_int8_t *)data, readlen + sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } } /* make leadout entry if needed */ idx = starting_track + num - 1; if (softc->quirks & CD_Q_BCD_TRACKS) th->ending_track = bcd2bin(th->ending_track); if (idx == th->ending_track + 1) { error = cdreadtoc(periph, te->address_format, LEADOUT, (u_int8_t *)lead, sizeof(*lead), /*sense_flags*/0); if (error) { free(data, M_SCSICD); free(lead, M_SCSICD); cam_periph_unlock(periph); break; } data->entries[idx - starting_track] = lead->entry; } if (softc->quirks & CD_Q_BCD_TRACKS) { for (idx = 0; idx < num - 1; idx++) { data->entries[idx].track = bcd2bin(data->entries[idx].track); } } cam_periph_unlock(periph); error = copyout(data->entries, te->data, len); free(data, M_SCSICD); free(lead, M_SCSICD); } break; case CDIOREADTOCENTRY: { struct cd_toc_single *data; struct ioc_read_toc_single_entry *te = (struct ioc_read_toc_single_entry *) addr; struct ioc_toc_header *th; u_int32_t track; data = malloc(sizeof(*data), M_SCSICD, M_WAITOK); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCENTRY\n")); if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) { printf("error in readtocentry, " " returning EINVAL\n"); free(data, M_SCSICD); error = EINVAL; cam_periph_unlock(periph); break; } th = &data->header; error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { /* we are going to have to convert the BCD * encoding on the cd to what is expected */ th->starting_track = bcd2bin(th->starting_track); th->ending_track = bcd2bin(th->ending_track); } track = te->track; if (track == 0) track = th->starting_track; else if (track == LEADOUT) /* OK */; else if (track < th->starting_track || track > th->ending_track + 1) { printf("error in readtocentry, " " returning EINVAL\n"); free(data, M_SCSICD); error = EINVAL; cam_periph_unlock(periph); break; } error = cdreadtoc(periph, te->address_format, track, (u_int8_t *)data, sizeof(*data), /*sense_flags*/0); if (error) { free(data, M_SCSICD); cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) data->entry.track = bcd2bin(data->entry.track); bcopy(&data->entry, &te->entry, sizeof(struct cd_toc_entry)); free(data, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch *)addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETPATCH\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = arg->patch[0]; page->audio.port[RIGHT_PORT].channels = arg->patch[1]; page->audio.port[2].channels = arg->patch[2]; page->audio.port[3].channels = arg->patch[3]; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCGETVOL\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); arg->vol[LEFT_PORT] = page->audio.port[LEFT_PORT].volume; arg->vol[RIGHT_PORT] = page->audio.port[RIGHT_PORT].volume; arg->vol[2] = page->audio.port[2].volume; arg->vol[3] = page->audio.port[3].volume; free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *) addr; struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETVOL\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = CHANNEL_0; page->audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; page->audio.port[RIGHT_PORT].channels = CHANNEL_1; page->audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; page->audio.port[2].volume = arg->vol[2]; page->audio.port[3].volume = arg->vol[3]; error = cdsetmode(periph, ¶ms); cam_periph_unlock(periph); free(params.mode_buf, M_SCSICD); } break; case CDIOCSETMONO: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMONO\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL | RIGHT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); cam_periph_unlock(periph); free(params.mode_buf, M_SCSICD); } break; case CDIOCSETSTEREO: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETSTEREO\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETMUTE: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETMUTE\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(¶ms, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = 0; page->audio.port[RIGHT_PORT].channels = 0; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETLEFT: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETLEFT\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCSETRIGHT: { struct cd_mode_params params; union cd_pages *page; params.alloc_len = sizeof(union cd_mode_data_6_10); params.mode_buf = malloc(params.alloc_len, M_SCSICD, M_WAITOK | M_ZERO); cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCSETRIGHT\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_SCSICD); cam_periph_unlock(periph); } break; case CDIOCRESUME: cam_periph_lock(periph); error = cdpause(periph, 1); cam_periph_unlock(periph); break; case CDIOCPAUSE: cam_periph_lock(periph); error = cdpause(periph, 0); cam_periph_unlock(periph); break; case CDIOCSTART: cam_periph_lock(periph); error = cdstartunit(periph, 0); cam_periph_unlock(periph); break; case CDIOCCLOSE: cam_periph_lock(periph); error = cdstartunit(periph, 1); cam_periph_unlock(periph); break; case CDIOCSTOP: cam_periph_lock(periph); error = cdstopunit(periph, 0); cam_periph_unlock(periph); break; case CDIOCEJECT: cam_periph_lock(periph); error = cdstopunit(periph, 1); cam_periph_unlock(periph); break; case CDIOCALLOW: cam_periph_lock(periph); cdprevent(periph, PR_ALLOW); cam_periph_unlock(periph); break; case CDIOCPREVENT: cam_periph_lock(periph); cdprevent(periph, PR_PREVENT); cam_periph_unlock(periph); break; case CDIOCSETDEBUG: /* sc_link->flags |= (SDEV_DB1 | SDEV_DB2); */ error = ENOTTY; break; case CDIOCCLRDEBUG: /* sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); */ error = ENOTTY; break; case CDIOCRESET: /* return (cd_reset(periph)); */ error = ENOTTY; break; case CDRIOCREADSPEED: cam_periph_lock(periph); error = cdsetspeed(periph, *(u_int32_t *)addr, CDR_MAX_SPEED); cam_periph_unlock(periph); break; case CDRIOCWRITESPEED: cam_periph_lock(periph); error = cdsetspeed(periph, CDR_MAX_SPEED, *(u_int32_t *)addr); cam_periph_unlock(periph); break; case DVDIOCSENDKEY: case DVDIOCREPORTKEY: { struct dvd_authinfo *authinfo; authinfo = (struct dvd_authinfo *)addr; cam_periph_lock(periph); if (cmd == DVDIOCREPORTKEY) error = cdreportkey(periph, authinfo); else error = cdsendkey(periph, authinfo); cam_periph_unlock(periph); break; } case DVDIOCREADSTRUCTURE: { struct dvd_struct *dvdstruct; dvdstruct = (struct dvd_struct *)addr; cam_periph_lock(periph); error = cdreaddvdstructure(periph, dvdstruct); cam_periph_unlock(periph); break; } default: cam_periph_lock(periph); error = cam_periph_ioctl(periph, cmd, addr, cderror); cam_periph_unlock(periph); break; } cam_periph_lock(periph); cam_periph_unhold(periph); CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdioctl\n")); if (error && bootverbose) { printf("scsi_cd.c::ioctl cmd=%08lx error=%d\n", cmd, error); } cam_periph_unlock(periph); return (error); } static void cdprevent(struct cam_periph *periph, int action) { union ccb *ccb; struct cd_softc *softc; int error; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdprevent\n")); softc = (struct cd_softc *)periph->softc; if (((action == PR_ALLOW) && (softc->flags & CD_FLAG_DISC_LOCKED) == 0) || ((action == PR_PREVENT) && (softc->flags & CD_FLAG_DISC_LOCKED) != 0)) { return; } ccb = cdgetccb(periph, /* priority */ 1); scsi_prevent(&ccb->csio, /*retries*/ 1, cddone, MSG_SIMPLE_Q_TAG, action, SSD_FULL_SIZE, /* timeout */60000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT); xpt_release_ccb(ccb); if (error == 0) { if (action == PR_ALLOW) softc->flags &= ~CD_FLAG_DISC_LOCKED; else softc->flags |= CD_FLAG_DISC_LOCKED; } } /* * XXX: the disk media and sector size is only really able to change * XXX: while the device is closed. */ static int cdcheckmedia(struct cam_periph *periph) { struct cd_softc *softc; struct ioc_toc_header *toch; struct cd_toc_single leadout; u_int32_t size, toclen; int error, num_entries, cdindex; softc = (struct cd_softc *)periph->softc; cdprevent(periph, PR_PREVENT); softc->disk->d_maxsize = DFLTPHYS; softc->disk->d_sectorsize = 2048; softc->disk->d_mediasize = 0; /* * Get the disc size and block size. If we can't get it, we don't * have media, most likely. */ if ((error = cdsize(periph, &size)) != 0) { softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC); cdprevent(periph, PR_ALLOW); return (error); } else softc->flags |= CD_FLAG_VALID_MEDIA; /* * Now we check the table of contents. This (currently) is only * used for the CDIOCPLAYTRACKS ioctl. It may be used later to do * things like present a separate entry in /dev for each track, * like that acd(4) driver does. */ bzero(&softc->toc, sizeof(softc->toc)); toch = &softc->toc.header; /* * We will get errors here for media that doesn't have a table of * contents. According to the MMC-3 spec: "When a Read TOC/PMA/ATIP * command is presented for a DDCD/CD-R/RW media, where the first TOC * has not been recorded (no complete session) and the Format codes * 0000b, 0001b, or 0010b are specified, this command shall be rejected * with an INVALID FIELD IN CDB. Devices that are not capable of * reading an incomplete session on DDC/CD-R/RW media shall report * CANNOT READ MEDIUM - INCOMPATIBLE FORMAT." * * So this isn't fatal if we can't read the table of contents, it * just means that the user won't be able to issue the play tracks * ioctl, and likely lots of other stuff won't work either. They * need to burn the CD before we can do a whole lot with it. So * we don't print anything here if we get an error back. */ error = cdreadtoc(periph, 0, 0, (u_int8_t *)toch, sizeof(*toch), SF_NO_PRINT); /* * Errors in reading the table of contents aren't fatal, we just * won't have a valid table of contents cached. */ if (error != 0) { error = 0; bzero(&softc->toc, sizeof(softc->toc)); goto bailout; } if (softc->quirks & CD_Q_BCD_TRACKS) { toch->starting_track = bcd2bin(toch->starting_track); toch->ending_track = bcd2bin(toch->ending_track); } /* Number of TOC entries, plus leadout */ num_entries = (toch->ending_track - toch->starting_track) + 2; if (num_entries <= 0) goto bailout; toclen = num_entries * sizeof(struct cd_toc_entry); error = cdreadtoc(periph, CD_MSF_FORMAT, toch->starting_track, (u_int8_t *)&softc->toc, toclen + sizeof(*toch), SF_NO_PRINT); if (error != 0) { error = 0; bzero(&softc->toc, sizeof(softc->toc)); goto bailout; } if (softc->quirks & CD_Q_BCD_TRACKS) { toch->starting_track = bcd2bin(toch->starting_track); toch->ending_track = bcd2bin(toch->ending_track); } /* * XXX KDM is this necessary? Probably only if the drive doesn't * return leadout information with the table of contents. */ cdindex = toch->starting_track + num_entries -1; if (cdindex == toch->ending_track + 1) { error = cdreadtoc(periph, CD_MSF_FORMAT, LEADOUT, (u_int8_t *)&leadout, sizeof(leadout), SF_NO_PRINT); if (error != 0) { error = 0; goto bailout; } softc->toc.entries[cdindex - toch->starting_track] = leadout.entry; } if (softc->quirks & CD_Q_BCD_TRACKS) { for (cdindex = 0; cdindex < num_entries - 1; cdindex++) { softc->toc.entries[cdindex].track = bcd2bin(softc->toc.entries[cdindex].track); } } softc->flags |= CD_FLAG_VALID_TOC; softc->disk->d_maxsize = DFLTPHYS; softc->disk->d_sectorsize = softc->params.blksize; softc->disk->d_mediasize = (off_t)softc->params.blksize * softc->params.disksize; bailout: /* * We unconditionally (re)set the blocksize each time the * CD device is opened. This is because the CD can change, * and therefore the blocksize might change. * XXX problems here if some slice or partition is still * open with the old size? */ if ((softc->disk->d_devstat->flags & DEVSTAT_BS_UNAVAILABLE) != 0) softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE; softc->disk->d_devstat->block_size = softc->params.blksize; return (error); } static int cdsize(struct cam_periph *periph, u_int32_t *size) { struct cd_softc *softc; union ccb *ccb; struct scsi_read_capacity_data *rcap_buf; int error; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n")); softc = (struct cd_softc *)periph->softc; ccb = cdgetccb(periph, /* priority */ 1); /* XXX Should be M_WAITOK */ rcap_buf = malloc(sizeof(struct scsi_read_capacity_data), M_SCSICD, M_NOWAIT); if (rcap_buf == NULL) return (ENOMEM); scsi_read_capacity(&ccb->csio, /*retries*/ 1, cddone, MSG_SIMPLE_Q_TAG, rcap_buf, SSD_FULL_SIZE, /* timeout */20000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT); xpt_release_ccb(ccb); softc->params.disksize = scsi_4btoul(rcap_buf->addr) + 1; softc->params.blksize = scsi_4btoul(rcap_buf->length); /* * SCSI-3 mandates that the reported blocksize shall be 2048. * Older drives sometimes report funny values, trim it down to * 2048, or other parts of the kernel will get confused. * * XXX we leave drives alone that might report 512 bytes, as * well as drives reporting more weird sizes like perhaps 4K. */ if (softc->params.blksize > 2048 && softc->params.blksize <= 2352) softc->params.blksize = 2048; free(rcap_buf, M_SCSICD); *size = softc->params.disksize; return (error); } static int cd6byteworkaround(union ccb *ccb) { u_int8_t *cdb; struct cam_periph *periph; struct cd_softc *softc; struct cd_mode_params *params; int frozen, found; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; cdb = ccb->csio.cdb_io.cdb_bytes; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) || ((cdb[0] != MODE_SENSE_6) && (cdb[0] != MODE_SELECT_6))) return (0); /* * Because there is no convenient place to stash the overall * cd_mode_params structure pointer, we have to grab it like this. * This means that ALL MODE_SENSE and MODE_SELECT requests in the * cd(4) driver MUST go through cdgetmode() and cdsetmode()! * * XXX It would be nice if, at some point, we could increase the * number of available peripheral private pointers. Both pointers * are currently used in most every peripheral driver. */ found = 0; STAILQ_FOREACH(params, &softc->mode_queue, links) { if (params->mode_buf == ccb->csio.data_ptr) { found = 1; break; } } /* * This shouldn't happen. All mode sense and mode select * operations in the cd(4) driver MUST go through cdgetmode() and * cdsetmode()! */ if (found == 0) { xpt_print(periph->path, "mode buffer not found in mode queue!\n"); return (0); } params->cdb_size = 10; softc->minimum_command_size = 10; xpt_print(ccb->ccb_h.path, "%s(6) failed, increasing minimum CDB size to 10 bytes\n", (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT"); if (cdb[0] == MODE_SENSE_6) { struct scsi_mode_sense_10 ms10; struct scsi_mode_sense_6 *ms6; int len; ms6 = (struct scsi_mode_sense_6 *)cdb; bzero(&ms10, sizeof(ms10)); ms10.opcode = MODE_SENSE_10; ms10.byte2 = ms6->byte2; ms10.page = ms6->page; /* * 10 byte mode header, block descriptor, * sizeof(union cd_pages) */ len = sizeof(struct cd_mode_data_10); ccb->csio.dxfer_len = len; scsi_ulto2b(len, ms10.length); ms10.control = ms6->control; bcopy(&ms10, cdb, 10); ccb->csio.cdb_len = 10; } else { struct scsi_mode_select_10 ms10; struct scsi_mode_select_6 *ms6; struct scsi_mode_header_6 *header6; struct scsi_mode_header_10 *header10; struct scsi_mode_page_header *page_header; int blk_desc_len, page_num, page_size, len; ms6 = (struct scsi_mode_select_6 *)cdb; bzero(&ms10, sizeof(ms10)); ms10.opcode = MODE_SELECT_10; ms10.byte2 = ms6->byte2; header6 = (struct scsi_mode_header_6 *)params->mode_buf; header10 = (struct scsi_mode_header_10 *)params->mode_buf; page_header = find_mode_page_6(header6); page_num = page_header->page_code; blk_desc_len = header6->blk_desc_len; page_size = cdgetpagesize(page_num); if (page_size != (page_header->page_length + sizeof(*page_header))) page_size = page_header->page_length + sizeof(*page_header); len = sizeof(*header10) + blk_desc_len + page_size; len = min(params->alloc_len, len); /* * Since the 6 byte parameter header is shorter than the 10 * byte parameter header, we need to copy the actual mode * page data, and the block descriptor, if any, so things wind * up in the right place. The regions will overlap, but * bcopy() does the right thing. */ bcopy(params->mode_buf + sizeof(*header6), params->mode_buf + sizeof(*header10), len - sizeof(*header10)); /* Make sure these fields are set correctly. */ scsi_ulto2b(0, header10->data_length); header10->medium_type = 0; scsi_ulto2b(blk_desc_len, header10->blk_desc_len); ccb->csio.dxfer_len = len; scsi_ulto2b(len, ms10.length); ms10.control = ms6->control; bcopy(&ms10, cdb, 10); ccb->csio.cdb_len = 10; } frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_action(ccb); if (frozen) { cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*openings*/0, /*timeout*/0, /*getcount_only*/0); } return (ERESTART); } static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct cd_softc *softc; struct cam_periph *periph; int error; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; error = 0; /* * We use a status of CAM_REQ_INVALID as shorthand -- if a 6 byte * CDB comes back with this particular error, try transforming it * into the 10 byte version. */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) { error = cd6byteworkaround(ccb); } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { int sense_key, error_code, asc, ascq; scsi_extract_sense(&ccb->csio.sense_data, &error_code, &sense_key, &asc, &ascq); if (sense_key == SSD_KEY_ILLEGAL_REQUEST) error = cd6byteworkaround(ccb); } if (error == ERESTART) return (error); /* * XXX * Until we have a better way of doing pack validation, * don't treat UAs as errors. */ sense_flags |= SF_RETRY_UA; return (cam_periph_error(ccb, cam_flags, sense_flags, &softc->saved_ccb)); } /* * Read table of contents */ static int cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, u_int8_t *data, u_int32_t len, u_int32_t sense_flags) { struct scsi_read_toc *scsi_cmd; u_int32_t ntoc; struct ccb_scsiio *csio; union ccb *ccb; int error; ntoc = len; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ 1, /* cbfcnp */ cddone, /* flags */ CAM_DIR_IN, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ data, /* dxfer_len */ len, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_read_toc), /* timeout */ 50000); scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); if (mode == CD_MSF_FORMAT) scsi_cmd->byte2 |= CD_MSF; scsi_cmd->from_track = start; /* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */ scsi_cmd->data_len[0] = (ntoc) >> 8; scsi_cmd->data_len[1] = (ntoc) & 0xff; scsi_cmd->op_code = READ_TOC; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA | sense_flags); xpt_release_ccb(ccb); return(error); } static int cdreadsubchannel(struct cam_periph *periph, u_int32_t mode, u_int32_t format, int track, struct cd_sub_channel_info *data, u_int32_t len) { struct scsi_read_subchannel *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ 1, /* cbfcnp */ cddone, /* flags */ CAM_DIR_IN, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ (u_int8_t *)data, /* dxfer_len */ len, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_read_subchannel), /* timeout */ 50000); scsi_cmd = (struct scsi_read_subchannel *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = READ_SUBCHANNEL; if (mode == CD_MSF_FORMAT) scsi_cmd->byte1 |= CD_MSF; scsi_cmd->byte2 = SRS_SUBQ; scsi_cmd->subchan_format = format; scsi_cmd->track = track; scsi_ulto2b(len, (u_int8_t *)scsi_cmd->data_len); scsi_cmd->control = 0; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } /* * All MODE_SENSE requests in the cd(4) driver MUST go through this * routine. See comments in cd6byteworkaround() for details. */ static int cdgetmode(struct cam_periph *periph, struct cd_mode_params *data, u_int32_t page) { struct ccb_scsiio *csio; struct cd_softc *softc; union ccb *ccb; int param_len; int error; softc = (struct cd_softc *)periph->softc; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; data->cdb_size = softc->minimum_command_size; if (data->cdb_size < 10) param_len = sizeof(struct cd_mode_data); else param_len = sizeof(struct cd_mode_data_10); /* Don't say we've got more room than we actually allocated */ param_len = min(param_len, data->alloc_len); scsi_mode_sense_len(csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* dbd */ 0, /* page_code */ SMS_PAGE_CTRL_CURRENT, /* page */ page, /* param_buf */ data->mode_buf, /* param_len */ param_len, /* minimum_cmd_size */ softc->minimum_command_size, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); /* * It would be nice not to have to do this, but there's no * available pointer in the CCB that would allow us to stuff the * mode params structure in there and retrieve it in * cd6byteworkaround(), so we can set the cdb size. The cdb size * lets the caller know what CDB size we ended up using, so they * can find the actual mode page offset. */ STAILQ_INSERT_TAIL(&softc->mode_queue, data, links); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); /* * This is a bit of belt-and-suspenders checking, but if we run * into a situation where the target sends back multiple block * descriptors, we might not have enough space in the buffer to * see the whole mode page. Better to return an error than * potentially access memory beyond our malloced region. */ if (error == 0) { u_int32_t data_len; if (data->cdb_size == 10) { struct scsi_mode_header_10 *hdr10; hdr10 = (struct scsi_mode_header_10 *)data->mode_buf; data_len = scsi_2btoul(hdr10->data_length); data_len += sizeof(hdr10->data_length); } else { struct scsi_mode_header_6 *hdr6; hdr6 = (struct scsi_mode_header_6 *)data->mode_buf; data_len = hdr6->data_length; data_len += sizeof(hdr6->data_length); } /* * Complain if there is more mode data available than we * allocated space for. This could potentially happen if * we miscalculated the page length for some reason, if the * drive returns multiple block descriptors, or if it sets * the data length incorrectly. */ if (data_len > data->alloc_len) { xpt_print(periph->path, "allocated modepage %d length " "%d < returned length %d\n", page, data->alloc_len, data_len); error = ENOSPC; } } return (error); } /* * All MODE_SELECT requests in the cd(4) driver MUST go through this * routine. See comments in cd6byteworkaround() for details. */ static int cdsetmode(struct cam_periph *periph, struct cd_mode_params *data) { struct ccb_scsiio *csio; struct cd_softc *softc; union ccb *ccb; int cdb_size, param_len; int error; softc = (struct cd_softc *)periph->softc; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; error = 0; /* * If the data is formatted for the 10 byte version of the mode * select parameter list, we need to use the 10 byte CDB. * Otherwise, we use whatever the stored minimum command size. */ if (data->cdb_size == 10) cdb_size = data->cdb_size; else cdb_size = softc->minimum_command_size; if (cdb_size >= 10) { struct scsi_mode_header_10 *mode_header; u_int32_t data_len; mode_header = (struct scsi_mode_header_10 *)data->mode_buf; data_len = scsi_2btoul(mode_header->data_length); scsi_ulto2b(0, mode_header->data_length); /* * SONY drives do not allow a mode select with a medium_type * value that has just been returned by a mode sense; use a * medium_type of 0 (Default) instead. */ mode_header->medium_type = 0; /* * Pass back whatever the drive passed to us, plus the size * of the data length field. */ param_len = data_len + sizeof(mode_header->data_length); } else { struct scsi_mode_header_6 *mode_header; mode_header = (struct scsi_mode_header_6 *)data->mode_buf; param_len = mode_header->data_length + 1; mode_header->data_length = 0; /* * SONY drives do not allow a mode select with a medium_type * value that has just been returned by a mode sense; use a * medium_type of 0 (Default) instead. */ mode_header->medium_type = 0; } /* Don't say we've got more room than we actually allocated */ param_len = min(param_len, data->alloc_len); scsi_mode_select_len(csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* scsi_page_fmt */ 1, /* save_pages */ 0, /* param_buf */ data->mode_buf, /* param_len */ param_len, /* minimum_cmd_size */ cdb_size, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); /* See comments in cdgetmode() and cd6byteworkaround(). */ STAILQ_INSERT_TAIL(&softc->mode_queue, data, links); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); return (error); } static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len) { struct ccb_scsiio *csio; union ccb *ccb; int error; u_int8_t cdb_len; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; /* * Use the smallest possible command to perform the operation. */ if ((len & 0xffff0000) == 0) { /* * We can fit in a 10 byte cdb. */ struct scsi_play_10 *scsi_cmd; scsi_cmd = (struct scsi_play_10 *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_10; scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr); scsi_ulto2b(len, (u_int8_t *)scsi_cmd->xfer_len); cdb_len = sizeof(*scsi_cmd); } else { struct scsi_play_12 *scsi_cmd; scsi_cmd = (struct scsi_play_12 *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_12; scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr); scsi_ulto4b(len, (u_int8_t *)scsi_cmd->xfer_len); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, /*retries*/2, cddone, /*flags*/CAM_DIR_NONE, MSG_SIMPLE_Q_TAG, /*dataptr*/NULL, /*datalen*/0, /*sense_len*/SSD_FULL_SIZE, cdb_len, /*timeout*/50 * 1000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts, u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf) { struct scsi_play_msf *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ 1, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_play_msf), /* timeout */ 50000); scsi_cmd = (struct scsi_play_msf *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_MSF; scsi_cmd->start_m = startm; scsi_cmd->start_s = starts; scsi_cmd->start_f = startf; scsi_cmd->end_m = endm; scsi_cmd->end_s = ends; scsi_cmd->end_f = endf; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex, u_int32_t etrack, u_int32_t eindex) { struct scsi_play_track *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ 1, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_play_track), /* timeout */ 50000); scsi_cmd = (struct scsi_play_track *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PLAY_TRACK; scsi_cmd->start_track = strack; scsi_cmd->start_index = sindex; scsi_cmd->end_track = etrack; scsi_cmd->end_index = eindex; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdpause(struct cam_periph *periph, u_int32_t go) { struct scsi_pause *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; cam_fill_csio(csio, /* retries */ 1, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_pause), /* timeout */ 50000); scsi_cmd = (struct scsi_pause *)&csio->cdb_io.cdb_bytes; bzero (scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->op_code = PAUSE; scsi_cmd->resume = go; error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdstartunit(struct cam_periph *periph, int load) { union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); scsi_start_stop(&ccb->csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* start */ TRUE, /* load_eject */ load, /* immediate */ FALSE, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdstopunit(struct cam_periph *periph, u_int32_t eject) { union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); scsi_start_stop(&ccb->csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* start */ FALSE, /* load_eject */ eject, /* immediate */ FALSE, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed) { struct scsi_set_speed *scsi_cmd; struct ccb_scsiio *csio; union ccb *ccb; int error; error = 0; ccb = cdgetccb(periph, /* priority */ 1); csio = &ccb->csio; /* Preserve old behavior: units in multiples of CDROM speed */ if (rdspeed < 177) rdspeed *= 177; if (wrspeed < 177) wrspeed *= 177; cam_fill_csio(csio, /* retries */ 1, /* cbfcnp */ cddone, /* flags */ CAM_DIR_NONE, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ NULL, /* dxfer_len */ 0, /* sense_len */ SSD_FULL_SIZE, sizeof(struct scsi_set_speed), /* timeout */ 50000); scsi_cmd = (struct scsi_set_speed *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SET_CD_SPEED; scsi_ulto2b(rdspeed, scsi_cmd->readspeed); scsi_ulto2b(wrspeed, scsi_cmd->writespeed); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); xpt_release_ccb(ccb); return(error); } static int cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) { union ccb *ccb; u_int8_t *databuf; u_int32_t lba; int error; int length; error = 0; databuf = NULL; lba = 0; ccb = cdgetccb(periph, /* priority */ 1); switch (authinfo->format) { case DVD_REPORT_AGID: length = sizeof(struct scsi_report_key_data_agid); break; case DVD_REPORT_CHALLENGE: length = sizeof(struct scsi_report_key_data_challenge); break; case DVD_REPORT_KEY1: length = sizeof(struct scsi_report_key_data_key1_key2); break; case DVD_REPORT_TITLE_KEY: length = sizeof(struct scsi_report_key_data_title); /* The lba field is only set for the title key */ lba = authinfo->lba; break; case DVD_REPORT_ASF: length = sizeof(struct scsi_report_key_data_asf); break; case DVD_REPORT_RPC: length = sizeof(struct scsi_report_key_data_rpc); break; case DVD_INVALIDATE_AGID: length = 0; break; default: error = EINVAL; goto bailout; break; /* NOTREACHED */ } if (length != 0) { databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); } else databuf = NULL; scsi_report_key(&ccb->csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* lba */ lba, /* agid */ authinfo->agid, /* key_format */ authinfo->format, /* data_ptr */ databuf, /* dxfer_len */ length, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); if (error != 0) goto bailout; if (ccb->csio.resid != 0) { xpt_print(periph->path, "warning, residual for report key " "command is %d\n", ccb->csio.resid); } switch(authinfo->format) { case DVD_REPORT_AGID: { struct scsi_report_key_data_agid *agid_data; agid_data = (struct scsi_report_key_data_agid *)databuf; authinfo->agid = (agid_data->agid & RKD_AGID_MASK) >> RKD_AGID_SHIFT; break; } case DVD_REPORT_CHALLENGE: { struct scsi_report_key_data_challenge *chal_data; chal_data = (struct scsi_report_key_data_challenge *)databuf; bcopy(chal_data->challenge_key, authinfo->keychal, min(sizeof(chal_data->challenge_key), sizeof(authinfo->keychal))); break; } case DVD_REPORT_KEY1: { struct scsi_report_key_data_key1_key2 *key1_data; key1_data = (struct scsi_report_key_data_key1_key2 *)databuf; bcopy(key1_data->key1, authinfo->keychal, min(sizeof(key1_data->key1), sizeof(authinfo->keychal))); break; } case DVD_REPORT_TITLE_KEY: { struct scsi_report_key_data_title *title_data; title_data = (struct scsi_report_key_data_title *)databuf; authinfo->cpm = (title_data->byte0 & RKD_TITLE_CPM) >> RKD_TITLE_CPM_SHIFT; authinfo->cp_sec = (title_data->byte0 & RKD_TITLE_CP_SEC) >> RKD_TITLE_CP_SEC_SHIFT; authinfo->cgms = (title_data->byte0 & RKD_TITLE_CMGS_MASK) >> RKD_TITLE_CMGS_SHIFT; bcopy(title_data->title_key, authinfo->keychal, min(sizeof(title_data->title_key), sizeof(authinfo->keychal))); break; } case DVD_REPORT_ASF: { struct scsi_report_key_data_asf *asf_data; asf_data = (struct scsi_report_key_data_asf *)databuf; authinfo->asf = asf_data->success & RKD_ASF_SUCCESS; break; } case DVD_REPORT_RPC: { struct scsi_report_key_data_rpc *rpc_data; rpc_data = (struct scsi_report_key_data_rpc *)databuf; authinfo->reg_type = (rpc_data->byte4 & RKD_RPC_TYPE_MASK) >> RKD_RPC_TYPE_SHIFT; authinfo->vend_rsts = (rpc_data->byte4 & RKD_RPC_VENDOR_RESET_MASK) >> RKD_RPC_VENDOR_RESET_SHIFT; authinfo->user_rsts = rpc_data->byte4 & RKD_RPC_USER_RESET_MASK; authinfo->region = rpc_data->region_mask; authinfo->rpc_scheme = rpc_data->rpc_scheme1; break; } case DVD_INVALIDATE_AGID: break; default: /* This should be impossible, since we checked above */ error = EINVAL; goto bailout; break; /* NOTREACHED */ } bailout: if (databuf != NULL) free(databuf, M_DEVBUF); xpt_release_ccb(ccb); return(error); } static int cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) { union ccb *ccb; u_int8_t *databuf; int length; int error; error = 0; databuf = NULL; ccb = cdgetccb(periph, /* priority */ 1); switch(authinfo->format) { case DVD_SEND_CHALLENGE: { struct scsi_report_key_data_challenge *challenge_data; length = sizeof(*challenge_data); challenge_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); databuf = (u_int8_t *)challenge_data; scsi_ulto2b(length - sizeof(challenge_data->data_len), challenge_data->data_len); bcopy(authinfo->keychal, challenge_data->challenge_key, min(sizeof(authinfo->keychal), sizeof(challenge_data->challenge_key))); break; } case DVD_SEND_KEY2: { struct scsi_report_key_data_key1_key2 *key2_data; length = sizeof(*key2_data); key2_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); databuf = (u_int8_t *)key2_data; scsi_ulto2b(length - sizeof(key2_data->data_len), key2_data->data_len); bcopy(authinfo->keychal, key2_data->key1, min(sizeof(authinfo->keychal), sizeof(key2_data->key1))); break; } case DVD_SEND_RPC: { struct scsi_send_key_data_rpc *rpc_data; length = sizeof(*rpc_data); rpc_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); databuf = (u_int8_t *)rpc_data; scsi_ulto2b(length - sizeof(rpc_data->data_len), rpc_data->data_len); rpc_data->region_code = authinfo->region; break; } default: error = EINVAL; goto bailout; break; /* NOTREACHED */ } scsi_send_key(&ccb->csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* agid */ authinfo->agid, /* key_format */ authinfo->format, /* data_ptr */ databuf, /* dxfer_len */ length, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); bailout: if (databuf != NULL) free(databuf, M_DEVBUF); xpt_release_ccb(ccb); return(error); } static int cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct) { union ccb *ccb; u_int8_t *databuf; u_int32_t address; int error; int length; error = 0; databuf = NULL; /* The address is reserved for many of the formats */ address = 0; ccb = cdgetccb(periph, /* priority */ 1); switch(dvdstruct->format) { case DVD_STRUCT_PHYSICAL: length = sizeof(struct scsi_read_dvd_struct_data_physical); break; case DVD_STRUCT_COPYRIGHT: length = sizeof(struct scsi_read_dvd_struct_data_copyright); break; case DVD_STRUCT_DISCKEY: length = sizeof(struct scsi_read_dvd_struct_data_disc_key); break; case DVD_STRUCT_BCA: length = sizeof(struct scsi_read_dvd_struct_data_bca); break; case DVD_STRUCT_MANUFACT: length = sizeof(struct scsi_read_dvd_struct_data_manufacturer); break; case DVD_STRUCT_CMI: error = ENODEV; goto bailout; #ifdef notyet length = sizeof(struct scsi_read_dvd_struct_data_copy_manage); address = dvdstruct->address; #endif break; /* NOTREACHED */ case DVD_STRUCT_PROTDISCID: length = sizeof(struct scsi_read_dvd_struct_data_prot_discid); break; case DVD_STRUCT_DISCKEYBLOCK: length = sizeof(struct scsi_read_dvd_struct_data_disc_key_blk); break; case DVD_STRUCT_DDS: length = sizeof(struct scsi_read_dvd_struct_data_dds); break; case DVD_STRUCT_MEDIUM_STAT: length = sizeof(struct scsi_read_dvd_struct_data_medium_status); break; case DVD_STRUCT_SPARE_AREA: length = sizeof(struct scsi_read_dvd_struct_data_spare_area); break; case DVD_STRUCT_RMD_LAST: error = ENODEV; goto bailout; #ifdef notyet length = sizeof(struct scsi_read_dvd_struct_data_rmd_borderout); address = dvdstruct->address; #endif break; /* NOTREACHED */ case DVD_STRUCT_RMD_RMA: error = ENODEV; goto bailout; #ifdef notyet length = sizeof(struct scsi_read_dvd_struct_data_rmd); address = dvdstruct->address; #endif break; /* NOTREACHED */ case DVD_STRUCT_PRERECORDED: length = sizeof(struct scsi_read_dvd_struct_data_leadin); break; case DVD_STRUCT_UNIQUEID: length = sizeof(struct scsi_read_dvd_struct_data_disc_id); break; case DVD_STRUCT_DCB: error = ENODEV; goto bailout; #ifdef notyet length = sizeof(struct scsi_read_dvd_struct_data_dcb); address = dvdstruct->address; #endif break; /* NOTREACHED */ case DVD_STRUCT_LIST: /* * This is the maximum allocation length for the READ DVD * STRUCTURE command. There's nothing in the MMC3 spec * that indicates a limit in the amount of data that can * be returned from this call, other than the limits * imposed by the 2-byte length variables. */ length = 65535; break; default: error = EINVAL; goto bailout; break; /* NOTREACHED */ } if (length != 0) { databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); } else databuf = NULL; scsi_read_dvd_structure(&ccb->csio, /* retries */ 1, /* cbfcnp */ cddone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* lba */ address, /* layer_number */ dvdstruct->layer_num, /* key_format */ dvdstruct->format, /* agid */ dvdstruct->agid, /* data_ptr */ databuf, /* dxfer_len */ length, /* sense_len */ SSD_FULL_SIZE, /* timeout */ 50000); error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, /*sense_flags*/SF_RETRY_UA); if (error != 0) goto bailout; switch(dvdstruct->format) { case DVD_STRUCT_PHYSICAL: { struct scsi_read_dvd_struct_data_layer_desc *inlayer; struct dvd_layer *outlayer; struct scsi_read_dvd_struct_data_physical *phys_data; phys_data = (struct scsi_read_dvd_struct_data_physical *)databuf; inlayer = &phys_data->layer_desc; outlayer = (struct dvd_layer *)&dvdstruct->data; dvdstruct->length = sizeof(*inlayer); outlayer->book_type = (inlayer->book_type_version & RDSD_BOOK_TYPE_MASK) >> RDSD_BOOK_TYPE_SHIFT; outlayer->book_version = (inlayer->book_type_version & RDSD_BOOK_VERSION_MASK); outlayer->disc_size = (inlayer->disc_size_max_rate & RDSD_DISC_SIZE_MASK) >> RDSD_DISC_SIZE_SHIFT; outlayer->max_rate = (inlayer->disc_size_max_rate & RDSD_MAX_RATE_MASK); outlayer->nlayers = (inlayer->layer_info & RDSD_NUM_LAYERS_MASK) >> RDSD_NUM_LAYERS_SHIFT; outlayer->track_path = (inlayer->layer_info & RDSD_TRACK_PATH_MASK) >> RDSD_TRACK_PATH_SHIFT; outlayer->layer_type = (inlayer->layer_info & RDSD_LAYER_TYPE_MASK); outlayer->linear_density = (inlayer->density & RDSD_LIN_DENSITY_MASK) >> RDSD_LIN_DENSITY_SHIFT; outlayer->track_density = (inlayer->density & RDSD_TRACK_DENSITY_MASK); outlayer->bca = (inlayer->bca & RDSD_BCA_MASK) >> RDSD_BCA_SHIFT; outlayer->start_sector = scsi_3btoul(inlayer->main_data_start); outlayer->end_sector = scsi_3btoul(inlayer->main_data_end); outlayer->end_sector_l0 = scsi_3btoul(inlayer->end_sector_layer0); break; } case DVD_STRUCT_COPYRIGHT: { struct scsi_read_dvd_struct_data_copyright *copy_data; copy_data = (struct scsi_read_dvd_struct_data_copyright *) databuf; dvdstruct->cpst = copy_data->cps_type; dvdstruct->rmi = copy_data->region_info; dvdstruct->length = 0; break; } default: /* * Tell the user what the overall length is, no matter * what we can actually fit in the data buffer. */ dvdstruct->length = length - ccb->csio.resid - sizeof(struct scsi_read_dvd_struct_data_header); /* * But only actually copy out the smaller of what we read * in or what the structure can take. */ bcopy(databuf + sizeof(struct scsi_read_dvd_struct_data_header), dvdstruct->data, min(sizeof(dvdstruct->data), dvdstruct->length)); break; } bailout: if (databuf != NULL) free(databuf, M_DEVBUF); xpt_release_ccb(ccb); return(error); } void scsi_report_key(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t lba, u_int8_t agid, u_int8_t key_format, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_report_key *scsi_cmd; scsi_cmd = (struct scsi_report_key *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REPORT_KEY; scsi_ulto4b(lba, scsi_cmd->lba); scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len); scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) | (key_format & RK_KF_KEYFORMAT_MASK); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ (dxfer_len == 0) ? CAM_DIR_NONE : CAM_DIR_IN, tag_action, /*data_ptr*/ data_ptr, /*dxfer_len*/ dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_send_key(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t agid, u_int8_t key_format, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_send_key *scsi_cmd; scsi_cmd = (struct scsi_send_key *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SEND_KEY; scsi_ulto2b(dxfer_len, scsi_cmd->param_len); scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) | (key_format & RK_KF_KEYFORMAT_MASK); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ CAM_DIR_OUT, tag_action, /*data_ptr*/ data_ptr, /*dxfer_len*/ dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } void scsi_read_dvd_structure(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t address, u_int8_t layer_number, u_int8_t format, u_int8_t agid, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_read_dvd_structure *scsi_cmd; scsi_cmd = (struct scsi_read_dvd_structure *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_DVD_STRUCTURE; scsi_ulto4b(address, scsi_cmd->address); scsi_cmd->layer_number = layer_number; scsi_cmd->format = format; scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len); /* The AGID is the top two bits of this byte */ scsi_cmd->agid = agid << 6; cam_fill_csio(csio, retries, cbfcnp, /*flags*/ CAM_DIR_IN, tag_action, /*data_ptr*/ data_ptr, /*dxfer_len*/ dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } Index: projects/cambria/sys/cam/scsi/scsi_da.c =================================================================== --- projects/cambria/sys/cam/scsi/scsi_da.c (revision 186459) +++ projects/cambria/sys/cam/scsi/scsi_da.c (revision 186460) @@ -1,2120 +1,2128 @@ /*- * Implementation of SCSI Direct Access Peripheral driver for CAM. * * Copyright (c) 1997 Justin T. Gibbs. * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* _KERNEL */ #ifndef _KERNEL #include #include #endif /* _KERNEL */ #include #include #include #include #include #include #ifndef _KERNEL #include #endif /* !_KERNEL */ #ifdef _KERNEL typedef enum { DA_STATE_PROBE, DA_STATE_PROBE2, DA_STATE_NORMAL } da_state; typedef enum { DA_FLAG_PACK_INVALID = 0x001, DA_FLAG_NEW_PACK = 0x002, DA_FLAG_PACK_LOCKED = 0x004, DA_FLAG_PACK_REMOVABLE = 0x008, DA_FLAG_TAGGED_QUEUING = 0x010, DA_FLAG_NEED_OTAG = 0x020, DA_FLAG_WENT_IDLE = 0x040, DA_FLAG_RETRY_UA = 0x080, DA_FLAG_OPEN = 0x100, DA_FLAG_SCTX_INIT = 0x200 } da_flags; typedef enum { DA_Q_NONE = 0x00, DA_Q_NO_SYNC_CACHE = 0x01, DA_Q_NO_6_BYTE = 0x02, DA_Q_NO_PREVENT = 0x04 } da_quirks; typedef enum { DA_CCB_PROBE = 0x01, DA_CCB_PROBE2 = 0x02, DA_CCB_BUFFER_IO = 0x03, DA_CCB_WAITING = 0x04, DA_CCB_DUMP = 0x05, DA_CCB_TYPE_MASK = 0x0F, DA_CCB_RETRY_UA = 0x10 } da_ccb_state; /* Offsets into our private area for storing information */ #define ccb_state ppriv_field0 #define ccb_bp ppriv_ptr1 struct disk_params { u_int8_t heads; u_int32_t cylinders; u_int8_t secs_per_track; u_int32_t secsize; /* Number of bytes/sector */ u_int64_t sectors; /* total number sectors */ }; struct da_softc { struct bio_queue_head bio_queue; SLIST_ENTRY(da_softc) links; LIST_HEAD(, ccb_hdr) pending_ccbs; da_state state; da_flags flags; da_quirks quirks; int minimum_cmd_size; int ordered_tag_count; int outstanding_cmds; struct disk_params params; struct disk *disk; union ccb saved_ccb; struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; struct callout sendordered_c; }; struct da_quirk_entry { struct scsi_inquiry_pattern inq_pat; da_quirks quirks; }; static const char quantum[] = "QUANTUM"; static const char microp[] = "MICROP"; static struct da_quirk_entry da_quirk_table[] = { /* SPI, FC devices */ { /* * Fujitsu M2513A MO drives. * Tested devices: M2513A2 firmware versions 1200 & 1300. * (dip switch selects whether T_DIRECT or T_OPTICAL device) * Reported by: W.Scholten */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "FUJITSU", "M2513A", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* See above. */ {T_OPTICAL, SIP_MEDIA_REMOVABLE, "FUJITSU", "M2513A", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * This particular Fujitsu drive doesn't like the * synchronize cache command. * Reported by: Tom Jackson */ {T_DIRECT, SIP_MEDIA_FIXED, "FUJITSU", "M2954*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * This drive doesn't like the synchronize cache command * either. Reported by: Matthew Jacob * in NetBSD PR kern/6027, August 24, 1998. */ {T_DIRECT, SIP_MEDIA_FIXED, microp, "2217*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * This drive doesn't like the synchronize cache command * either. Reported by: Hellmuth Michaelis (hm@kts.org) * (PR 8882). */ {T_DIRECT, SIP_MEDIA_FIXED, microp, "2112*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. * Reported by: Blaz Zupan */ {T_DIRECT, SIP_MEDIA_FIXED, "NEC", "D3847*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. * Reported by: Blaz Zupan */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "MAVERICK 540S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS525S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't like the synchronize cache command. * Reported by: walter@pelissero.de */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS540S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Doesn't work correctly with 6 byte reads/writes. * Returns illegal request, and points to byte 9 of the * 6-byte CDB. * Reported by: Adam McDougall */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "VIKING 4*", "*"}, /*quirks*/ DA_Q_NO_6_BYTE }, { /* See above. */ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "VIKING 2*", "*"}, /*quirks*/ DA_Q_NO_6_BYTE }, { /* * Doesn't like the synchronize cache command. * Reported by: walter@pelissero.de */ {T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CP3500*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * The CISS RAID controllers do not support SYNC_CACHE */ {T_DIRECT, SIP_MEDIA_FIXED, "COMPAQ", "RAID*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, /* USB mass storage devices supported by umass(4) */ { /* * EXATELECOM (Sigmatel) i-Bead 100/105 USB Flash MP3 Player * PR: kern/51675 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "EXATEL", "i-BEAD10*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Power Quotient Int. (PQI) USB flash key * PR: kern/53067 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Generic*", "USB Flash Disk*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Creative Nomad MUVO mp3 player (USB) * PR: kern/53094 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "CREATIVE", "NOMAD_MUVO", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * Jungsoft NEXDISK USB flash key * PR: kern/54737 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "JUNGSOFT", "NEXDISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * FreeDik USB Mini Data Drive * PR: kern/54786 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "FreeDik*", "Mini Data Drive", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Sigmatel USB Flash MP3 Player * PR: kern/57046 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SigmaTel", "MSCN", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * Neuros USB Digital Audio Computer * PR: kern/63645 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "NEUROS", "dig. audio comp.", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * SEAGRAND NP-900 MP3 Player * PR: kern/64563 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SEAGRAND", "NP-900*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * iRiver iFP MP3 player (with UMS Firmware) * PR: kern/54881, i386/63941, kern/66124 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "iRiver", "iFP*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Frontier Labs NEX IA+ Digital Audio Player, rev 1.10/0.01 * PR: kern/70158 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "FL" , "Nex*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * ZICPlay USB MP3 Player with FM * PR: kern/75057 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "ACTIONS*" , "USB DISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * TEAC USB floppy mechanisms */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "TEAC" , "FD-05*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Kingston DataTraveler II+ USB Pen-Drive. * Reported by: Pawel Jakub Dawidek */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Kingston" , "DataTraveler II+", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Motorola E398 Mobile Phone (TransFlash memory card). * Reported by: Wojciech A. Koszek * PR: usb/89889 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Motorola" , "Motorola Phone", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Qware BeatZkey! Pro * PR: usb/79164 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "GENERIC", "USB DISK DEVICE", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Time DPA20B 1GB MP3 Player * PR: usb/81846 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB2.0*", "(FS) FLASH DISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Samsung USB key 128Mb * PR: usb/90081 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB-DISK", "FreeDik-FlashUsb", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Kingston DataTraveler 2.0 USB Flash memory. * PR: usb/89196 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Kingston", "DataTraveler 2.0", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Creative MUVO Slim mp3 player (USB) * PR: usb/86131 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "CREATIVE", "MuVo Slim", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE|DA_Q_NO_PREVENT }, { /* * United MP5512 Portable MP3 Player (2-in-1 USB DISK/MP3) * PR: usb/80487 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Generic*", "MUSIC DISK", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * SanDisk Micro Cruzer 128MB * PR: usb/75970 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SanDisk" , "Micro Cruzer", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * TOSHIBA TransMemory USB sticks * PR: kern/94660 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "TOSHIBA", "TransMemory", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * PNY USB Flash keys * PR: usb/75578, usb/72344, usb/65436 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "*" , "USB DISK*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Genesys 6-in-1 Card Reader * PR: usb/94647 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Generic*", "STORAGE DEVICE*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Rekam Digital CAMERA * PR: usb/98713 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "CAMERA*", "4MP-9J6*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * iRiver H10 MP3 player * PR: usb/102547 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "iriver", "H10*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * iRiver U10 MP3 player * PR: usb/92306 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "iriver", "U10*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * X-Micro Flash Disk * PR: usb/96901 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "X-Micro", "Flash Disk", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * EasyMP3 EM732X USB 2.0 Flash MP3 Player * PR: usb/96546 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "EM732X", "MP3 Player*", "1.00"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Denver MP3 player * PR: usb/107101 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "DENVER", "MP3 PLAYER", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * Philips USB Key Audio KEY013 * PR: usb/68412 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "PHILIPS", "Key*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE | DA_Q_NO_PREVENT }, { /* * JNC MP3 Player * PR: usb/94439 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "JNC*" , "MP3 Player*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * SAMSUNG MP0402H * PR: usb/108427 */ {T_DIRECT, SIP_MEDIA_FIXED, "SAMSUNG", "MP0402H", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * I/O Magic USB flash - Giga Bank * PR: usb/108810 */ {T_DIRECT, SIP_MEDIA_FIXED, "GS-Magic", "stor*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * JoyFly 128mb USB Flash Drive * PR: 96133 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "USB 2.0", "Flash Disk*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* * ChipsBnk usb stick * PR: 103702 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "ChipsBnk", "USB*", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { /* + * Storcase (Kingston) InfoStation IFS FC2/SATA-R 201A + * PR: 129858 + */ + {T_DIRECT, SIP_MEDIA_FIXED, "IFS", "FC2/SATA-R*", + "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE + }, + { + /* * Samsung YP-U3 mp3-player * PR: 125398 */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "Samsung", "YP-U3", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, { {T_DIRECT, SIP_MEDIA_REMOVABLE, "Netac", "OnlyDisk*", "2000"}, /*quirks*/ DA_Q_NO_SYNC_CACHE } }; static disk_strategy_t dastrategy; static dumper_t dadump; static periph_init_t dainit; static void daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void dasysctlinit(void *context, int pending); static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS); static periph_ctor_t daregister; static periph_dtor_t dacleanup; static periph_start_t dastart; static periph_oninv_t daoninvalidate; static void dadone(struct cam_periph *periph, union ccb *done_ccb); static int daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static void daprevent(struct cam_periph *periph, int action); static int dagetcapacity(struct cam_periph *periph); static void dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector); static timeout_t dasendorderedtag; static void dashutdown(void *arg, int howto); #ifndef DA_DEFAULT_TIMEOUT #define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */ #endif #ifndef DA_DEFAULT_RETRY #define DA_DEFAULT_RETRY 4 #endif #ifndef DA_DEFAULT_SEND_ORDERED #define DA_DEFAULT_SEND_ORDERED 1 #endif static int da_retry_count = DA_DEFAULT_RETRY; static int da_default_timeout = DA_DEFAULT_TIMEOUT; static int da_send_ordered = DA_DEFAULT_SEND_ORDERED; SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0, "CAM Direct Access Disk driver"); SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RW, &da_retry_count, 0, "Normal I/O retry count"); TUNABLE_INT("kern.cam.da.retry_count", &da_retry_count); SYSCTL_INT(_kern_cam_da, OID_AUTO, default_timeout, CTLFLAG_RW, &da_default_timeout, 0, "Normal I/O timeout (in seconds)"); TUNABLE_INT("kern.cam.da.default_timeout", &da_default_timeout); SYSCTL_INT(_kern_cam_da, OID_AUTO, da_send_ordered, CTLFLAG_RW, &da_send_ordered, 0, "Send Ordered Tags"); TUNABLE_INT("kern.cam.da.da_send_ordered", &da_send_ordered); /* * DA_ORDEREDTAG_INTERVAL determines how often, relative * to the default timeout, we check to see whether an ordered * tagged transaction is appropriate to prevent simple tag * starvation. Since we'd like to ensure that there is at least * 1/2 of the timeout length left for a starved transaction to * complete after we've sent an ordered tag, we must poll at least * four times in every timeout period. This takes care of the worst * case where a starved transaction starts during an interval that * meets the requirement "don't send an ordered tag" test so it takes * us two intervals to determine that a tag must be sent. */ #ifndef DA_ORDEREDTAG_INTERVAL #define DA_ORDEREDTAG_INTERVAL 4 #endif static struct periph_driver dadriver = { dainit, "da", TAILQ_HEAD_INITIALIZER(dadriver.units), /* generation */ 0 }; PERIPHDRIVER_DECLARE(da, dadriver); MALLOC_DEFINE(M_SCSIDA, "scsi_da", "scsi_da buffers"); static int daopen(struct disk *dp) { struct cam_periph *periph; struct da_softc *softc; int unit; int error; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) { return (ENXIO); } if (cam_periph_acquire(periph) != CAM_REQ_CMP) { return(ENXIO); } cam_periph_lock(periph); if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (error); } unit = periph->unit_number; softc = (struct da_softc *)periph->softc; softc->flags |= DA_FLAG_OPEN; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("daopen: disk=%s%d (unit %d)\n", dp->d_name, dp->d_unit, unit)); if ((softc->flags & DA_FLAG_PACK_INVALID) != 0) { /* Invalidate our pack information. */ softc->flags &= ~DA_FLAG_PACK_INVALID; } error = dagetcapacity(periph); if (error == 0) { softc->disk->d_sectorsize = softc->params.secsize; softc->disk->d_mediasize = softc->params.secsize * (off_t)softc->params.sectors; /* XXX: these are not actually "firmware" values, so they may be wrong */ softc->disk->d_fwsectors = softc->params.secs_per_track; softc->disk->d_fwheads = softc->params.heads; softc->disk->d_devstat->block_size = softc->params.secsize; softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE; if ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0 && (softc->quirks & DA_Q_NO_PREVENT) == 0) daprevent(periph, PR_PREVENT); } else softc->flags &= ~DA_FLAG_OPEN; cam_periph_unhold(periph); cam_periph_unlock(periph); if (error != 0) { cam_periph_release(periph); } return (error); } static int daclose(struct disk *dp) { struct cam_periph *periph; struct da_softc *softc; int error; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) return (ENXIO); cam_periph_lock(periph); if ((error = cam_periph_hold(periph, PRIBIO)) != 0) { cam_periph_unlock(periph); cam_periph_release(periph); return (error); } softc = (struct da_softc *)periph->softc; if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) { union ccb *ccb; ccb = cam_periph_getccb(periph, /*priority*/1); scsi_synchronize_cache(&ccb->csio, /*retries*/1, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0,/* Cover the whole disk */ /*lb_count*/0, SSD_FULL_SIZE, 5 * 60 * 1000); cam_periph_runccb(ccb, /*error_routine*/NULL, /*cam_flags*/0, /*sense_flags*/SF_RETRY_UA, softc->disk->d_devstat); if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) { int asc, ascq; int sense_key, error_code; scsi_extract_sense(&ccb->csio.sense_data, &error_code, &sense_key, &asc, &ascq); if (sense_key != SSD_KEY_ILLEGAL_REQUEST) scsi_sense_print(&ccb->csio); } else { xpt_print(periph->path, "Synchronize cache " "failed, status == 0x%x, scsi status == " "0x%x\n", ccb->csio.ccb_h.status, ccb->csio.scsi_status); } } if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); xpt_release_ccb(ccb); } if ((softc->flags & DA_FLAG_PACK_REMOVABLE) != 0) { if ((softc->quirks & DA_Q_NO_PREVENT) == 0) daprevent(periph, PR_ALLOW); /* * If we've got removeable media, mark the blocksize as * unavailable, since it could change when new media is * inserted. */ softc->disk->d_devstat->flags |= DEVSTAT_BS_UNAVAILABLE; } softc->flags &= ~DA_FLAG_OPEN; cam_periph_unhold(periph); cam_periph_unlock(periph); cam_periph_release(periph); return (0); } /* * Actually translate the requested transfer into one the physical driver * can understand. The transfer is described by a buf and will include * only one physical transfer. */ static void dastrategy(struct bio *bp) { struct cam_periph *periph; struct da_softc *softc; periph = (struct cam_periph *)bp->bio_disk->d_drv1; if (periph == NULL) { biofinish(bp, NULL, ENXIO); return; } softc = (struct da_softc *)periph->softc; cam_periph_lock(periph); #if 0 /* * check it's not too big a transfer for our adapter */ scsi_minphys(bp,&sd_switch); #endif /* * Mask interrupts so that the pack cannot be invalidated until * after we are in the queue. Otherwise, we might not properly * clean up one of the buffers. */ /* * If the device has been made invalid, error out */ if ((softc->flags & DA_FLAG_PACK_INVALID)) { cam_periph_unlock(periph); biofinish(bp, NULL, ENXIO); return; } /* * Place it in the queue of disk activities for this disk */ bioq_disksort(&softc->bio_queue, bp); /* * Schedule ourselves for performing the work. */ xpt_schedule(periph, /* XXX priority */1); cam_periph_unlock(periph); return; } static int dadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct cam_periph *periph; struct da_softc *softc; u_int secsize; struct ccb_scsiio csio; struct disk *dp; dp = arg; periph = dp->d_drv1; if (periph == NULL) return (ENXIO); softc = (struct da_softc *)periph->softc; cam_periph_lock(periph); secsize = softc->params.secsize; if ((softc->flags & DA_FLAG_PACK_INVALID) != 0) { cam_periph_unlock(periph); return (ENXIO); } if (length > 0) { periph->flags |= CAM_PERIPH_POLLED; xpt_setup_ccb(&csio.ccb_h, periph->path, /*priority*/1); csio.ccb_h.ccb_state = DA_CCB_DUMP; scsi_read_write(&csio, /*retries*/1, dadone, MSG_ORDERED_Q_TAG, /*read*/FALSE, /*byte2*/0, /*minimum_cmd_size*/ softc->minimum_cmd_size, offset / secsize, length / secsize, /*data_ptr*/(u_int8_t *) virtual, /*dxfer_len*/length, /*sense_len*/SSD_FULL_SIZE, DA_DEFAULT_TIMEOUT * 1000); xpt_polled_action((union ccb *)&csio); if ((csio.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { printf("Aborting dump due to I/O error.\n"); if ((csio.ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(&csio); else printf("status == 0x%x, scsi status == 0x%x\n", csio.ccb_h.status, csio.scsi_status); periph->flags |= CAM_PERIPH_POLLED; return(EIO); } cam_periph_unlock(periph); return(0); } /* * Sync the disk cache contents to the physical media. */ if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) { xpt_setup_ccb(&csio.ccb_h, periph->path, /*priority*/1); csio.ccb_h.ccb_state = DA_CCB_DUMP; scsi_synchronize_cache(&csio, /*retries*/1, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0,/* Cover the whole disk */ /*lb_count*/0, SSD_FULL_SIZE, 5 * 60 * 1000); xpt_polled_action((union ccb *)&csio); if ((csio.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if ((csio.ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) { int asc, ascq; int sense_key, error_code; scsi_extract_sense(&csio.sense_data, &error_code, &sense_key, &asc, &ascq); if (sense_key != SSD_KEY_ILLEGAL_REQUEST) scsi_sense_print(&csio); } else { xpt_print(periph->path, "Synchronize cache " "failed, status == 0x%x, scsi status == " "0x%x\n", csio.ccb_h.status, csio.scsi_status); } } } periph->flags &= ~CAM_PERIPH_POLLED; cam_periph_unlock(periph); return (0); } static void dainit(void) { cam_status status; /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, daasync, NULL, NULL); if (status != CAM_REQ_CMP) { printf("da: Failed to attach master async callback " "due to status 0x%x!\n", status); } else if (da_send_ordered) { /* Register our shutdown event handler */ if ((EVENTHANDLER_REGISTER(shutdown_post_sync, dashutdown, NULL, SHUTDOWN_PRI_DEFAULT)) == NULL) printf("dainit: shutdown event registration failed!\n"); } } static void daoninvalidate(struct cam_periph *periph) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_register_async(0, daasync, periph, periph->path); softc->flags |= DA_FLAG_PACK_INVALID; /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ bioq_flush(&softc->bio_queue, NULL, ENXIO); disk_gone(softc->disk); xpt_print(periph->path, "lost device\n"); } static void dacleanup(struct cam_periph *periph) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; xpt_print(periph->path, "removing device entry\n"); /* * If we can't free the sysctl tree, oh well... */ if ((softc->flags & DA_FLAG_SCTX_INIT) != 0 && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { xpt_print(periph->path, "can't remove sysctl context\n"); } cam_periph_unlock(periph); disk_destroy(softc->disk); callout_drain(&softc->sendordered_c); cam_periph_lock(periph); free(softc, M_DEVBUF); } static void daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; struct cam_sim *sim; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; if (SID_TYPE(&cgd->inq_data) != T_DIRECT && SID_TYPE(&cgd->inq_data) != T_RBC && SID_TYPE(&cgd->inq_data) != T_OPTICAL) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ sim = xpt_path_sim(cgd->ccb_h.path); status = cam_periph_alloc(daregister, daoninvalidate, dacleanup, dastart, "da", CAM_PERIPH_BIO, cgd->ccb_h.path, daasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) printf("daasync: Unable to attach to new device " "due to status 0x%x\n", status); break; } case AC_SENT_BDR: case AC_BUS_RESET: { struct da_softc *softc; struct ccb_hdr *ccbh; softc = (struct da_softc *)periph->softc; /* * Don't fail on the expected unit attention * that will occur. */ softc->flags |= DA_FLAG_RETRY_UA; LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le) ccbh->ccb_state |= DA_CCB_RETRY_UA; /* FALLTHROUGH*/ } default: cam_periph_async(periph, code, path, arg); break; } } static void dasysctlinit(void *context, int pending) { struct cam_periph *periph; struct da_softc *softc; char tmpstr[80], tmpstr2[80]; periph = (struct cam_periph *)context; if (cam_periph_acquire(periph) != CAM_REQ_CMP) return; softc = (struct da_softc *)periph->softc; snprintf(tmpstr, sizeof(tmpstr), "CAM DA unit %d", periph->unit_number); snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); mtx_lock(&Giant); sysctl_ctx_init(&softc->sysctl_ctx); softc->flags |= DA_FLAG_SCTX_INIT; softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_kern_cam_da), OID_AUTO, tmpstr2, CTLFLAG_RD, 0, tmpstr); if (softc->sysctl_tree == NULL) { printf("dasysctlinit: unable to allocate sysctl tree\n"); mtx_unlock(&Giant); cam_periph_release(periph); return; } /* * Now register the sysctl handler, so the user can the value on * the fly. */ SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, &softc->minimum_cmd_size, 0, dacmdsizesysctl, "I", "Minimum CDB size"); mtx_unlock(&Giant); cam_periph_release(periph); } static int dacmdsizesysctl(SYSCTL_HANDLER_ARGS) { int error, value; value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if ((error != 0) || (req->newptr == NULL)) return (error); /* * Acceptable values here are 6, 10, 12 or 16. */ if (value < 6) value = 6; else if ((value > 6) && (value <= 10)) value = 10; else if ((value > 10) && (value <= 12)) value = 12; else if (value > 12) value = 16; *(int *)arg1 = value; return (0); } static cam_status daregister(struct cam_periph *periph, void *arg) { struct da_softc *softc; struct ccb_pathinq cpi; struct ccb_getdev *cgd; char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { printf("daregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (cgd == NULL) { printf("daregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct da_softc *)malloc(sizeof(*softc), M_DEVBUF, M_NOWAIT|M_ZERO); if (softc == NULL) { printf("daregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } LIST_INIT(&softc->pending_ccbs); softc->state = DA_STATE_PROBE; bioq_init(&softc->bio_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) softc->flags |= DA_FLAG_PACK_REMOVABLE; if ((cgd->inq_data.flags & SID_CmdQue) != 0) softc->flags |= DA_FLAG_TAGGED_QUEUING; periph->softc = softc; /* * See if this device has any quirks. */ match = cam_quirkmatch((caddr_t)&cgd->inq_data, (caddr_t)da_quirk_table, sizeof(da_quirk_table)/sizeof(*da_quirk_table), sizeof(*da_quirk_table), scsi_inquiry_match); if (match != NULL) softc->quirks = ((struct da_quirk_entry *)match)->quirks; else softc->quirks = DA_Q_NONE; /* Check if the SIM does not want 6 byte commands */ xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) softc->quirks |= DA_Q_NO_6_BYTE; TASK_INIT(&softc->sysctl_task, 0, dasysctlinit, periph); /* * RBC devices don't have to support READ(6), only READ(10). */ if (softc->quirks & DA_Q_NO_6_BYTE || SID_TYPE(&cgd->inq_data) == T_RBC) softc->minimum_cmd_size = 10; else softc->minimum_cmd_size = 6; /* * Load the user's default, if any. */ snprintf(tmpstr, sizeof(tmpstr), "kern.cam.da.%d.minimum_cmd_size", periph->unit_number); TUNABLE_INT_FETCH(tmpstr, &softc->minimum_cmd_size); /* * 6, 10, 12 and 16 are the currently permissible values. */ if (softc->minimum_cmd_size < 6) softc->minimum_cmd_size = 6; else if ((softc->minimum_cmd_size > 6) && (softc->minimum_cmd_size <= 10)) softc->minimum_cmd_size = 10; else if ((softc->minimum_cmd_size > 10) && (softc->minimum_cmd_size <= 12)) softc->minimum_cmd_size = 12; else if (softc->minimum_cmd_size > 12) softc->minimum_cmd_size = 16; /* * Register this media as a disk */ mtx_unlock(periph->sim->mtx); softc->disk = disk_alloc(); softc->disk->d_open = daopen; softc->disk->d_close = daclose; softc->disk->d_strategy = dastrategy; softc->disk->d_dump = dadump; softc->disk->d_name = "da"; softc->disk->d_drv1 = periph; softc->disk->d_maxsize = DFLTPHYS; /* XXX: probably not arbitrary */ softc->disk->d_unit = periph->unit_number; softc->disk->d_flags = 0; if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; disk_create(softc->disk, DISK_VERSION); mtx_lock(periph->sim->mtx); /* * Add async callbacks for bus reset and * bus device reset calls. I don't bother * checking if this fails as, in most cases, * the system will function just fine without * them and the only alternative would be to * not attach the device on failure. */ xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE, daasync, periph, periph->path); /* * Take an exclusive refcount on the periph while dastart is called * to finish the probe. The reference will be dropped in dadone at * the end of probe. */ (void)cam_periph_hold(periph, PRIBIO); xpt_schedule(periph, /*priority*/5); /* * Schedule a periodic event to occasionally send an * ordered tag to a device. */ callout_init_mtx(&softc->sendordered_c, periph->sim->mtx, 0); callout_reset(&softc->sendordered_c, (DA_DEFAULT_TIMEOUT * hz) / DA_ORDEREDTAG_INTERVAL, dasendorderedtag, softc); return(CAM_REQ_CMP); } static void dastart(struct cam_periph *periph, union ccb *start_ccb) { struct da_softc *softc; softc = (struct da_softc *)periph->softc; switch (softc->state) { case DA_STATE_NORMAL: { /* Pull a buffer from the queue and get going on it */ struct bio *bp; /* * See if there is a buf with work for us to do.. */ bp = bioq_first(&softc->bio_queue); if (periph->immediate_priority <= periph->pinfo.priority) { CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, ("queuing for immediate ccb\n")); start_ccb->ccb_h.ccb_state = DA_CCB_WAITING; SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, periph_links.sle); periph->immediate_priority = CAM_PRIORITY_NONE; wakeup(&periph->ccb_list); } else if (bp == NULL) { xpt_release_ccb(start_ccb); } else { u_int8_t tag_code; bioq_remove(&softc->bio_queue, bp); if ((softc->flags & DA_FLAG_NEED_OTAG) != 0) { softc->flags &= ~DA_FLAG_NEED_OTAG; softc->ordered_tag_count++; tag_code = MSG_ORDERED_Q_TAG; } else { tag_code = MSG_SIMPLE_Q_TAG; } switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: scsi_read_write(&start_ccb->csio, /*retries*/da_retry_count, /*cbfcnp*/dadone, /*tag_action*/tag_code, /*read_op*/bp->bio_cmd == BIO_READ, /*byte2*/0, softc->minimum_cmd_size, /*lba*/bp->bio_pblkno, /*block_count*/bp->bio_bcount / softc->params.secsize, /*data_ptr*/ bp->bio_data, /*dxfer_len*/ bp->bio_bcount, /*sense_len*/SSD_FULL_SIZE, /*timeout*/da_default_timeout*1000); break; case BIO_FLUSH: scsi_synchronize_cache(&start_ccb->csio, /*retries*/1, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0,/* Cover the whole disk */ /*lb_count*/0, SSD_FULL_SIZE, /*timeout*/da_default_timeout*1000); break; } start_ccb->ccb_h.ccb_state = DA_CCB_BUFFER_IO; /* * Block out any asyncronous callbacks * while we touch the pending ccb list. */ LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h, periph_links.le); softc->outstanding_cmds++; /* We expect a unit attention from this device */ if ((softc->flags & DA_FLAG_RETRY_UA) != 0) { start_ccb->ccb_h.ccb_state |= DA_CCB_RETRY_UA; softc->flags &= ~DA_FLAG_RETRY_UA; } start_ccb->ccb_h.ccb_bp = bp; bp = bioq_first(&softc->bio_queue); xpt_action(start_ccb); } if (bp != NULL) { /* Have more work to do, so ensure we stay scheduled */ xpt_schedule(periph, /* XXX priority */1); } break; } case DA_STATE_PROBE: { struct ccb_scsiio *csio; struct scsi_read_capacity_data *rcap; rcap = (struct scsi_read_capacity_data *) malloc(sizeof(*rcap), M_SCSIDA, M_NOWAIT|M_ZERO); if (rcap == NULL) { printf("dastart: Couldn't malloc read_capacity data\n"); /* da_free_periph??? */ break; } csio = &start_ccb->csio; scsi_read_capacity(csio, /*retries*/4, dadone, MSG_SIMPLE_Q_TAG, rcap, SSD_FULL_SIZE, /*timeout*/5000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE; xpt_action(start_ccb); break; } case DA_STATE_PROBE2: { struct ccb_scsiio *csio; struct scsi_read_capacity_data_long *rcaplong; rcaplong = (struct scsi_read_capacity_data_long *) malloc(sizeof(*rcaplong), M_SCSIDA, M_NOWAIT|M_ZERO); if (rcaplong == NULL) { printf("dastart: Couldn't malloc read_capacity data\n"); /* da_free_periph??? */ break; } csio = &start_ccb->csio; scsi_read_capacity_16(csio, /*retries*/ 4, /*cbfcnp*/ dadone, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*lba*/ 0, /*reladr*/ 0, /*pmi*/ 0, rcaplong, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ 60000); start_ccb->ccb_h.ccb_bp = NULL; start_ccb->ccb_h.ccb_state = DA_CCB_PROBE2; xpt_action(start_ccb); break; } } } static int cmd6workaround(union ccb *ccb) { struct scsi_rw_6 cmd6; struct scsi_rw_10 *cmd10; struct da_softc *softc; u_int8_t *cdb; int frozen; cdb = ccb->csio.cdb_io.cdb_bytes; /* Translation only possible if CDB is an array and cmd is R/W6 */ if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0 || (*cdb != READ_6 && *cdb != WRITE_6)) return 0; xpt_print(ccb->ccb_h.path, "READ(6)/WRITE(6) not supported, " "increasing minimum_cmd_size to 10.\n"); softc = (struct da_softc *)xpt_path_periph(ccb->ccb_h.path)->softc; softc->minimum_cmd_size = 10; bcopy(cdb, &cmd6, sizeof(struct scsi_rw_6)); cmd10 = (struct scsi_rw_10 *)cdb; cmd10->opcode = (cmd6.opcode == READ_6) ? READ_10 : WRITE_10; cmd10->byte2 = 0; scsi_ulto4b(scsi_3btoul(cmd6.addr), cmd10->addr); cmd10->reserved = 0; scsi_ulto2b(cmd6.length, cmd10->length); cmd10->control = cmd6.control; ccb->csio.cdb_len = sizeof(*cmd10); /* Requeue request, unfreezing queue if necessary */ frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_action(ccb); if (frozen) { cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } return (ERESTART); } static void dadone(struct cam_periph *periph, union ccb *done_ccb) { struct da_softc *softc; struct ccb_scsiio *csio; softc = (struct da_softc *)periph->softc; csio = &done_ccb->csio; switch (csio->ccb_h.ccb_state & DA_CCB_TYPE_MASK) { case DA_CCB_BUFFER_IO: { struct bio *bp; bp = (struct bio *)done_ccb->ccb_h.ccb_bp; if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { int error; int sf; if ((csio->ccb_h.ccb_state & DA_CCB_RETRY_UA) != 0) sf = SF_RETRY_UA; else sf = 0; error = daerror(done_ccb, CAM_RETRY_SELTO, sf); if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } if (error != 0) { if (error == ENXIO) { /* * Catastrophic error. Mark our pack as * invalid. */ /* * XXX See if this is really a media * XXX change first? */ xpt_print(periph->path, "Invalidating pack\n"); softc->flags |= DA_FLAG_PACK_INVALID; } /* * return all queued I/O with EIO, so that * the client can retry these I/Os in the * proper order should it attempt to recover. */ bioq_flush(&softc->bio_queue, NULL, EIO); bp->bio_error = error; bp->bio_resid = bp->bio_bcount; bp->bio_flags |= BIO_ERROR; } else { bp->bio_resid = csio->resid; bp->bio_error = 0; if (bp->bio_resid != 0) bp->bio_flags |= BIO_ERROR; } if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); } else { if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) panic("REQ_CMP with QFRZN"); bp->bio_resid = csio->resid; if (csio->resid > 0) bp->bio_flags |= BIO_ERROR; } /* * Block out any asyncronous callbacks * while we touch the pending ccb list. */ LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); softc->outstanding_cmds--; if (softc->outstanding_cmds == 0) softc->flags |= DA_FLAG_WENT_IDLE; biodone(bp); break; } case DA_CCB_PROBE: case DA_CCB_PROBE2: { struct scsi_read_capacity_data *rdcap; struct scsi_read_capacity_data_long *rcaplong; char announce_buf[80]; rdcap = NULL; rcaplong = NULL; if (softc->state == DA_STATE_PROBE) rdcap =(struct scsi_read_capacity_data *)csio->data_ptr; else rcaplong = (struct scsi_read_capacity_data_long *) csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { struct disk_params *dp; uint32_t block_size; uint64_t maxsector; if (softc->state == DA_STATE_PROBE) { block_size = scsi_4btoul(rdcap->length); maxsector = scsi_4btoul(rdcap->addr); /* * According to SBC-2, if the standard 10 * byte READ CAPACITY command returns 2^32, * we should issue the 16 byte version of * the command, since the device in question * has more sectors than can be represented * with the short version of the command. */ if (maxsector == 0xffffffff) { softc->state = DA_STATE_PROBE2; free(rdcap, M_SCSIDA); xpt_release_ccb(done_ccb); xpt_schedule(periph, /*priority*/5); return; } } else { block_size = scsi_4btoul(rcaplong->length); maxsector = scsi_8btou64(rcaplong->addr); } /* * Because GEOM code just will panic us if we * give them an 'illegal' value we'll avoid that * here. */ if (block_size >= MAXPHYS || block_size == 0) { xpt_print(periph->path, "unsupportable block size %ju\n", (uintmax_t) block_size); announce_buf[0] = '\0'; cam_periph_invalidate(periph); } else { dasetgeom(periph, block_size, maxsector); dp = &softc->params; snprintf(announce_buf, sizeof(announce_buf), "%juMB (%ju %u byte sectors: %dH %dS/T " "%dC)", (uintmax_t) (((uintmax_t)dp->secsize * dp->sectors) / (1024*1024)), (uintmax_t)dp->sectors, dp->secsize, dp->heads, dp->secs_per_track, dp->cylinders); } } else { int error; announce_buf[0] = '\0'; /* * Retry any UNIT ATTENTION type errors. They * are expected at boot. */ error = daerror(done_ccb, CAM_RETRY_SELTO, SF_RETRY_UA|SF_NO_PRINT); if (error == ERESTART) { /* * A retry was scheuled, so * just return. */ return; } else if (error != 0) { struct scsi_sense_data *sense; int asc, ascq; int sense_key, error_code; int have_sense; cam_status status; struct ccb_getdev cgd; /* Don't wedge this device's queue */ status = done_ccb->ccb_h.status; if ((status & CAM_DEV_QFRZN) != 0) cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, /* priority */ 1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); if (((csio->ccb_h.flags & CAM_SENSE_PHYS) != 0) || ((csio->ccb_h.flags & CAM_SENSE_PTR) != 0) || ((status & CAM_AUTOSNS_VALID) == 0)) have_sense = FALSE; else have_sense = TRUE; if (have_sense) { sense = &csio->sense_data; scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); } /* * Attach to anything that claims to be a * direct access or optical disk device, * as long as it doesn't return a "Logical * unit not supported" (0x25) error. */ if ((have_sense) && (asc != 0x25) && (error_code == SSD_CURRENT_ERROR)) { const char *sense_key_desc; const char *asc_desc; scsi_sense_desc(sense_key, asc, ascq, &cgd.inq_data, &sense_key_desc, &asc_desc); snprintf(announce_buf, sizeof(announce_buf), "Attempt to query device " "size failed: %s, %s", sense_key_desc, asc_desc); } else { if (have_sense) scsi_sense_print( &done_ccb->csio); else { xpt_print(periph->path, "got CAM status %#x\n", done_ccb->ccb_h.status); } xpt_print(periph->path, "fatal error, " "failed to attach to device\n"); /* * Free up resources. */ cam_periph_invalidate(periph); } } } free(csio->data_ptr, M_SCSIDA); if (announce_buf[0] != '\0') { xpt_announce_periph(periph, announce_buf); /* * Create our sysctl variables, now that we know * we have successfully attached. */ taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task); } softc->state = DA_STATE_NORMAL; /* * Since our peripheral may be invalidated by an error * above or an external event, we must release our CCB * before releasing the probe lock on the peripheral. * The peripheral will only go away once the last lock * is removed, and we need it around for the CCB release * operation. */ xpt_release_ccb(done_ccb); cam_periph_unhold(periph); return; } case DA_CCB_WAITING: { /* Caller will release the CCB */ wakeup(&done_ccb->ccb_h.cbfcnp); return; } case DA_CCB_DUMP: /* No-op. We're polling */ return; default: break; } xpt_release_ccb(done_ccb); } static int daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct da_softc *softc; struct cam_periph *periph; int error; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct da_softc *)periph->softc; /* * Automatically detect devices that do not support * READ(6)/WRITE(6) and upgrade to using 10 byte cdbs. */ error = 0; if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) { error = cmd6workaround(ccb); } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { int sense_key, error_code, asc, ascq; scsi_extract_sense(&ccb->csio.sense_data, &error_code, &sense_key, &asc, &ascq); if (sense_key == SSD_KEY_ILLEGAL_REQUEST) error = cmd6workaround(ccb); } if (error == ERESTART) return (ERESTART); /* * XXX * Until we have a better way of doing pack validation, * don't treat UAs as errors. */ sense_flags |= SF_RETRY_UA; return(cam_periph_error(ccb, cam_flags, sense_flags, &softc->saved_ccb)); } static void daprevent(struct cam_periph *periph, int action) { struct da_softc *softc; union ccb *ccb; int error; softc = (struct da_softc *)periph->softc; if (((action == PR_ALLOW) && (softc->flags & DA_FLAG_PACK_LOCKED) == 0) || ((action == PR_PREVENT) && (softc->flags & DA_FLAG_PACK_LOCKED) != 0)) { return; } ccb = cam_periph_getccb(periph, /*priority*/1); scsi_prevent(&ccb->csio, /*retries*/1, /*cbcfp*/dadone, MSG_SIMPLE_Q_TAG, action, SSD_FULL_SIZE, 5000); error = cam_periph_runccb(ccb, /*error_routine*/NULL, CAM_RETRY_SELTO, SF_RETRY_UA, softc->disk->d_devstat); if (error == 0) { if (action == PR_ALLOW) softc->flags &= ~DA_FLAG_PACK_LOCKED; else softc->flags |= DA_FLAG_PACK_LOCKED; } xpt_release_ccb(ccb); } static int dagetcapacity(struct cam_periph *periph) { struct da_softc *softc; union ccb *ccb; struct scsi_read_capacity_data *rcap; struct scsi_read_capacity_data_long *rcaplong; uint32_t block_len; uint64_t maxsector; int error; u_int32_t sense_flags; softc = (struct da_softc *)periph->softc; block_len = 0; maxsector = 0; error = 0; sense_flags = SF_RETRY_UA; if (softc->flags & DA_FLAG_PACK_REMOVABLE) sense_flags |= SF_NO_PRINT; /* Do a read capacity */ rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcaplong), M_SCSIDA, M_NOWAIT); if (rcap == NULL) return (ENOMEM); ccb = cam_periph_getccb(periph, /*priority*/1); scsi_read_capacity(&ccb->csio, /*retries*/4, /*cbfncp*/dadone, MSG_SIMPLE_Q_TAG, rcap, SSD_FULL_SIZE, /*timeout*/60000); ccb->ccb_h.ccb_bp = NULL; error = cam_periph_runccb(ccb, daerror, /*cam_flags*/CAM_RETRY_SELTO, sense_flags, softc->disk->d_devstat); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); if (error == 0) { block_len = scsi_4btoul(rcap->length); maxsector = scsi_4btoul(rcap->addr); if (maxsector != 0xffffffff) goto done; } else goto done; rcaplong = (struct scsi_read_capacity_data_long *)rcap; scsi_read_capacity_16(&ccb->csio, /*retries*/ 4, /*cbfcnp*/ dadone, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*lba*/ 0, /*reladr*/ 0, /*pmi*/ 0, rcaplong, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ 60000); ccb->ccb_h.ccb_bp = NULL; error = cam_periph_runccb(ccb, daerror, /*cam_flags*/CAM_RETRY_SELTO, sense_flags, softc->disk->d_devstat); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(ccb->ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); if (error == 0) { block_len = scsi_4btoul(rcaplong->length); maxsector = scsi_8btou64(rcaplong->addr); } done: if (error == 0) dasetgeom(periph, block_len, maxsector); xpt_release_ccb(ccb); free(rcap, M_SCSIDA); return (error); } static void dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector) { struct ccb_calc_geometry ccg; struct da_softc *softc; struct disk_params *dp; softc = (struct da_softc *)periph->softc; dp = &softc->params; dp->secsize = block_len; dp->sectors = maxsector + 1; /* * Have the controller provide us with a geometry * for this disk. The only time the geometry * matters is when we boot and the controller * is the only one knowledgeable enough to come * up with something that will make this a bootable * device. */ xpt_setup_ccb(&ccg.ccb_h, periph->path, /*priority*/1); ccg.ccb_h.func_code = XPT_CALC_GEOMETRY; ccg.block_size = dp->secsize; ccg.volume_size = dp->sectors; ccg.heads = 0; ccg.secs_per_track = 0; ccg.cylinders = 0; xpt_action((union ccb*)&ccg); if ((ccg.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { /* * We don't know what went wrong here- but just pick * a geometry so we don't have nasty things like divide * by zero. */ dp->heads = 255; dp->secs_per_track = 255; dp->cylinders = dp->sectors / (255 * 255); if (dp->cylinders == 0) { dp->cylinders = 1; } } else { dp->heads = ccg.heads; dp->secs_per_track = ccg.secs_per_track; dp->cylinders = ccg.cylinders; } } static void dasendorderedtag(void *arg) { struct da_softc *softc = arg; if (da_send_ordered) { if ((softc->ordered_tag_count == 0) && ((softc->flags & DA_FLAG_WENT_IDLE) == 0)) { softc->flags |= DA_FLAG_NEED_OTAG; } if (softc->outstanding_cmds > 0) softc->flags &= ~DA_FLAG_WENT_IDLE; softc->ordered_tag_count = 0; } /* Queue us up again */ callout_reset(&softc->sendordered_c, (DA_DEFAULT_TIMEOUT * hz) / DA_ORDEREDTAG_INTERVAL, dasendorderedtag, softc); } /* * Step through all DA peripheral drivers, and if the device is still open, * sync the disk cache to physical media. */ static void dashutdown(void * arg, int howto) { struct cam_periph *periph; struct da_softc *softc; TAILQ_FOREACH(periph, &dadriver.units, unit_links) { union ccb ccb; cam_periph_lock(periph); softc = (struct da_softc *)periph->softc; /* * We only sync the cache if the drive is still open, and * if the drive is capable of it.. */ if (((softc->flags & DA_FLAG_OPEN) == 0) || (softc->quirks & DA_Q_NO_SYNC_CACHE)) { cam_periph_unlock(periph); continue; } xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/1); ccb.ccb_h.ccb_state = DA_CCB_DUMP; scsi_synchronize_cache(&ccb.csio, /*retries*/1, /*cbfcnp*/dadone, MSG_SIMPLE_Q_TAG, /*begin_lba*/0, /* whole disk */ /*lb_count*/0, SSD_FULL_SIZE, 60 * 60 * 1000); xpt_polled_action(&ccb); if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) && (ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND)){ int error_code, sense_key, asc, ascq; scsi_extract_sense(&ccb.csio.sense_data, &error_code, &sense_key, &asc, &ascq); if (sense_key != SSD_KEY_ILLEGAL_REQUEST) scsi_sense_print(&ccb.csio); } else { xpt_print(periph->path, "Synchronize " "cache failed, status == 0x%x, scsi status " "== 0x%x\n", ccb.ccb_h.status, ccb.csio.scsi_status); } } if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(ccb.ccb_h.path, /*relsim_flags*/0, /*reduction*/0, /*timeout*/0, /*getcount_only*/0); cam_periph_unlock(periph); } } #else /* !_KERNEL */ /* * XXX This is only left out of the kernel build to silence warnings. If, * for some reason this function is used in the kernel, the ifdefs should * be moved so it is included both in the kernel and userland. */ void scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t byte2, u_int16_t ileave, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_format_unit *scsi_cmd; scsi_cmd = (struct scsi_format_unit *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = FORMAT_UNIT; scsi_cmd->byte2 = byte2; scsi_ulto2b(ileave, scsi_cmd->interleave); cam_fill_csio(csio, retries, cbfcnp, /*flags*/ (dxfer_len > 0) ? CAM_DIR_OUT : CAM_DIR_NONE, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); } #endif /* _KERNEL */ Index: projects/cambria/sys/cam/scsi/scsi_pass.c =================================================================== --- projects/cambria/sys/cam/scsi/scsi_pass.c (revision 186459) +++ projects/cambria/sys/cam/scsi/scsi_pass.c (revision 186460) @@ -1,598 +1,598 @@ /*- * Copyright (c) 1997, 1998, 2000 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { PASS_FLAG_OPEN = 0x01, PASS_FLAG_LOCKED = 0x02, PASS_FLAG_INVALID = 0x04 } pass_flags; typedef enum { PASS_STATE_NORMAL } pass_state; typedef enum { PASS_CCB_BUFFER_IO, PASS_CCB_WAITING } pass_ccb_types; #define ccb_type ppriv_field0 #define ccb_bp ppriv_ptr1 struct pass_softc { pass_state state; pass_flags flags; u_int8_t pd_type; union ccb saved_ccb; struct devstat *device_stats; struct cdev *dev; }; static d_open_t passopen; static d_close_t passclose; static d_ioctl_t passioctl; static periph_init_t passinit; static periph_ctor_t passregister; static periph_oninv_t passoninvalidate; static periph_dtor_t passcleanup; static periph_start_t passstart; static void passasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void passdone(struct cam_periph *periph, union ccb *done_ccb); static int passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static int passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb); static struct periph_driver passdriver = { passinit, "pass", TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0 }; PERIPHDRIVER_DECLARE(pass, passdriver); static struct cdevsw pass_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = passopen, .d_close = passclose, .d_ioctl = passioctl, .d_name = "pass", }; static void passinit(void) { cam_status status; /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, passasync, NULL, NULL); if (status != CAM_REQ_CMP) { printf("pass: Failed to attach master async callback " "due to status 0x%x!\n", status); } } static void passoninvalidate(struct cam_periph *periph) { struct pass_softc *softc; softc = (struct pass_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_register_async(0, passasync, periph, periph->path); softc->flags |= PASS_FLAG_INVALID; /* * XXX Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ if (bootverbose) { xpt_print(periph->path, "lost device\n"); } } static void passcleanup(struct cam_periph *periph) { struct pass_softc *softc; softc = (struct pass_softc *)periph->softc; devstat_remove_entry(softc->device_stats); destroy_dev(softc->dev); if (bootverbose) { xpt_print(periph->path, "removing device entry\n"); } free(softc, M_DEVBUF); } static void passasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; struct cam_sim *sim; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ sim = xpt_path_sim(cgd->ccb_h.path); status = cam_periph_alloc(passregister, passoninvalidate, passcleanup, passstart, "pass", CAM_PERIPH_BIO, cgd->ccb_h.path, passasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { const struct cam_status_entry *entry; entry = cam_fetch_status_entry(status); printf("passasync: Unable to attach new device " "due to status %#x: %s\n", status, entry ? entry->status_text : "Unknown"); } break; } default: cam_periph_async(periph, code, path, arg); break; } } static cam_status passregister(struct cam_periph *periph, void *arg) { struct pass_softc *softc; struct ccb_getdev *cgd; int no_tags; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { printf("passregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (cgd == NULL) { printf("passregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct pass_softc *)malloc(sizeof(*softc), M_DEVBUF, M_NOWAIT); if (softc == NULL) { printf("passregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } bzero(softc, sizeof(*softc)); softc->state = PASS_STATE_NORMAL; softc->pd_type = SID_TYPE(&cgd->inq_data); periph->softc = softc; /* * We pass in 0 for a blocksize, since we don't * know what the blocksize of this device is, if * it even has a blocksize. */ mtx_unlock(periph->sim->mtx); no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0; softc->device_stats = devstat_new_entry("pass", periph->unit_number, 0, DEVSTAT_NO_BLOCKSIZE | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0), softc->pd_type | DEVSTAT_TYPE_IF_SCSI | DEVSTAT_TYPE_PASS, DEVSTAT_PRIORITY_PASS); /* Register the device */ softc->dev = make_dev(&pass_cdevsw, periph->unit_number, UID_ROOT, GID_OPERATOR, 0600, "%s%d", periph->periph_name, periph->unit_number); mtx_lock(periph->sim->mtx); softc->dev->si_drv1 = periph; /* * Add an async callback so that we get * notified if this device goes away. */ xpt_register_async(AC_LOST_DEVICE, passasync, periph, periph->path); if (bootverbose) xpt_announce_periph(periph, NULL); return(CAM_REQ_CMP); } static int passopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct cam_periph *periph; struct pass_softc *softc; int error; error = 0; /* default to no error */ periph = (struct cam_periph *)dev->si_drv1; if (cam_periph_acquire(periph) != CAM_REQ_CMP) return (ENXIO); cam_periph_lock(periph); softc = (struct pass_softc *)periph->softc; if (softc->flags & PASS_FLAG_INVALID) { cam_periph_unlock(periph); cam_periph_release(periph); return(ENXIO); } /* * Don't allow access when we're running at a high securelevel. */ error = securelevel_gt(td->td_ucred, 1); if (error) { cam_periph_unlock(periph); cam_periph_release(periph); return(error); } /* * Only allow read-write access. */ if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) { cam_periph_unlock(periph); cam_periph_release(periph); return(EPERM); } /* * We don't allow nonblocking access. */ if ((flags & O_NONBLOCK) != 0) { xpt_print(periph->path, "can't do nonblocking access\n"); cam_periph_unlock(periph); cam_periph_release(periph); return(EINVAL); } if ((softc->flags & PASS_FLAG_OPEN) == 0) { softc->flags |= PASS_FLAG_OPEN; + cam_periph_unlock(periph); } else { /* Device closes aren't symmertical, so fix up the refcount */ + cam_periph_unlock(periph); cam_periph_release(periph); } - - cam_periph_unlock(periph); return (error); } static int passclose(struct cdev *dev, int flag, int fmt, struct thread *td) { struct cam_periph *periph; struct pass_softc *softc; periph = (struct cam_periph *)dev->si_drv1; if (periph == NULL) return (ENXIO); cam_periph_lock(periph); softc = (struct pass_softc *)periph->softc; softc->flags &= ~PASS_FLAG_OPEN; cam_periph_unlock(periph); cam_periph_release(periph); return (0); } static void passstart(struct cam_periph *periph, union ccb *start_ccb) { struct pass_softc *softc; softc = (struct pass_softc *)periph->softc; switch (softc->state) { case PASS_STATE_NORMAL: start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING; SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, periph_links.sle); periph->immediate_priority = CAM_PRIORITY_NONE; wakeup(&periph->ccb_list); break; } } static void passdone(struct cam_periph *periph, union ccb *done_ccb) { struct pass_softc *softc; struct ccb_scsiio *csio; softc = (struct pass_softc *)periph->softc; csio = &done_ccb->csio; switch (csio->ccb_h.ccb_type) { case PASS_CCB_WAITING: /* Caller will release the CCB */ wakeup(&done_ccb->ccb_h.cbfcnp); return; } xpt_release_ccb(done_ccb); } static int passioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct cam_periph *periph; struct pass_softc *softc; int error; periph = (struct cam_periph *)dev->si_drv1; if (periph == NULL) return(ENXIO); cam_periph_lock(periph); softc = (struct pass_softc *)periph->softc; error = 0; switch (cmd) { case CAMIOCOMMAND: { union ccb *inccb; union ccb *ccb; int ccb_malloced; inccb = (union ccb *)addr; /* * Some CCB types, like scan bus and scan lun can only go * through the transport layer device. */ if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) { xpt_print(periph->path, "CCB function code %#x is " "restricted to the XPT device\n", inccb->ccb_h.func_code); error = ENODEV; break; } /* * Non-immediate CCBs need a CCB from the per-device pool * of CCBs, which is scheduled by the transport layer. * Immediate CCBs and user-supplied CCBs should just be * malloced. */ if ((inccb->ccb_h.func_code & XPT_FC_QUEUED) && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) { ccb = cam_periph_getccb(periph, inccb->ccb_h.pinfo.priority); ccb_malloced = 0; } else { ccb = xpt_alloc_ccb_nowait(); if (ccb != NULL) xpt_setup_ccb(&ccb->ccb_h, periph->path, inccb->ccb_h.pinfo.priority); ccb_malloced = 1; } if (ccb == NULL) { xpt_print(periph->path, "unable to allocate CCB\n"); error = ENOMEM; break; } error = passsendccb(periph, ccb, inccb); if (ccb_malloced) xpt_free_ccb(ccb); else xpt_release_ccb(ccb); break; } default: error = cam_periph_ioctl(periph, cmd, addr, passerror); break; } cam_periph_unlock(periph); return(error); } /* * Generally, "ccb" should be the CCB supplied by the kernel. "inccb" * should be the CCB that is copied in from the user. */ static int passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) { struct pass_softc *softc; struct cam_periph_map_info mapinfo; int error, need_unmap; softc = (struct pass_softc *)periph->softc; need_unmap = 0; /* * There are some fields in the CCB header that need to be * preserved, the rest we get from the user. */ xpt_merge_ccb(ccb, inccb); /* * There's no way for the user to have a completion * function, so we put our own completion function in here. */ ccb->ccb_h.cbfcnp = passdone; /* * We only attempt to map the user memory into kernel space * if they haven't passed in a physical memory pointer, * and if there is actually an I/O operation to perform. * Right now cam_periph_mapmem() only supports SCSI and device * match CCBs. For the SCSI CCBs, we only pass the CCB in if * there's actually data to map. cam_periph_mapmem() will do the * right thing, even if there isn't data to map, but since CCBs * without data are a reasonably common occurance (e.g. test unit * ready), it will save a few cycles if we check for it here. */ if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0) && (((ccb->ccb_h.func_code == XPT_SCSI_IO) && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)) || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) { bzero(&mapinfo, sizeof(mapinfo)); /* * cam_periph_mapmem calls into proc and vm functions that can * sleep as well as trigger I/O, so we can't hold the lock. * Dropping it here is reasonably safe. */ cam_periph_unlock(periph); error = cam_periph_mapmem(ccb, &mapinfo); cam_periph_lock(periph); /* * cam_periph_mapmem returned an error, we can't continue. * Return the error to the user. */ if (error) return(error); /* * We successfully mapped the memory in, so we need to * unmap it when the transaction is done. */ need_unmap = 1; } /* * If the user wants us to perform any error recovery, then honor * that request. Otherwise, it's up to the user to perform any * error recovery. */ error = cam_periph_runccb(ccb, (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ? passerror : NULL, /* cam_flags */ CAM_RETRY_SELTO, /* sense_flags */SF_RETRY_UA, softc->device_stats); if (need_unmap != 0) cam_periph_unmapmem(ccb, &mapinfo); ccb->ccb_h.cbfcnp = NULL; ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv; bcopy(ccb, inccb, sizeof(union ccb)); return(error); } static int passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { struct cam_periph *periph; struct pass_softc *softc; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct pass_softc *)periph->softc; return(cam_periph_error(ccb, cam_flags, sense_flags, &softc->saved_ccb)); } Index: projects/cambria/sys/cam/scsi/scsi_sg.c =================================================================== --- projects/cambria/sys/cam/scsi/scsi_sg.c (revision 186459) +++ projects/cambria/sys/cam/scsi/scsi_sg.c (revision 186460) @@ -1,983 +1,983 @@ /*- * Copyright (c) 2007 Scott Long * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * scsi_sg peripheral driver. This driver is meant to implement the Linux * SG passthrough interface for SCSI. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { SG_FLAG_OPEN = 0x01, SG_FLAG_LOCKED = 0x02, SG_FLAG_INVALID = 0x04 } sg_flags; typedef enum { SG_STATE_NORMAL } sg_state; typedef enum { SG_RDWR_FREE, SG_RDWR_INPROG, SG_RDWR_DONE } sg_rdwr_state; typedef enum { SG_CCB_RDWR_IO, SG_CCB_WAITING } sg_ccb_types; #define ccb_type ppriv_field0 #define ccb_rdwr ppriv_ptr1 struct sg_rdwr { TAILQ_ENTRY(sg_rdwr) rdwr_link; int tag; int state; int buf_len; char *buf; union ccb *ccb; union { struct sg_header hdr; struct sg_io_hdr io_hdr; } hdr; }; struct sg_softc { sg_state state; sg_flags flags; struct devstat *device_stats; TAILQ_HEAD(, sg_rdwr) rdwr_done; struct cdev *dev; int sg_timeout; int sg_user_timeout; uint8_t pd_type; union ccb saved_ccb; }; static d_open_t sgopen; static d_close_t sgclose; static d_ioctl_t sgioctl; static d_write_t sgwrite; static d_read_t sgread; static periph_init_t sginit; static periph_ctor_t sgregister; static periph_oninv_t sgoninvalidate; static periph_dtor_t sgcleanup; static periph_start_t sgstart; static void sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg); static void sgdone(struct cam_periph *periph, union ccb *done_ccb); static int sgsendccb(struct cam_periph *periph, union ccb *ccb); static int sgsendrdwr(struct cam_periph *periph, union ccb *ccb); static int sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags); static void sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat); static int scsi_group_len(u_char cmd); static struct periph_driver sgdriver = { sginit, "sg", TAILQ_HEAD_INITIALIZER(sgdriver.units), /* gen */ 0 }; PERIPHDRIVER_DECLARE(sg, sgdriver); static struct cdevsw sg_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = sgopen, .d_close = sgclose, .d_ioctl = sgioctl, .d_write = sgwrite, .d_read = sgread, .d_name = "sg", }; static int sg_version = 30125; static void sginit(void) { cam_status status; /* * Install a global async callback. This callback will receive aync * callbacks like "new device found". */ status = xpt_register_async(AC_FOUND_DEVICE, sgasync, NULL, NULL); if (status != CAM_REQ_CMP) { printf("sg: Failed to attach master async callbac " "due to status 0x%x!\n", status); } } static void sgoninvalidate(struct cam_periph *periph) { struct sg_softc *softc; softc = (struct sg_softc *)periph->softc; /* * Deregister any async callbacks. */ xpt_register_async(0, sgasync, periph, periph->path); softc->flags |= SG_FLAG_INVALID; /* * XXX Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ if (bootverbose) { xpt_print(periph->path, "lost device\n"); } } static void sgcleanup(struct cam_periph *periph) { struct sg_softc *softc; softc = (struct sg_softc *)periph->softc; devstat_remove_entry(softc->device_stats); destroy_dev(softc->dev); if (bootverbose) { xpt_print(periph->path, "removing device entry\n"); } free(softc, M_DEVBUF); } static void sgasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; /* * Allocate a peripheral instance for this device and * start the probe process. */ status = cam_periph_alloc(sgregister, sgoninvalidate, sgcleanup, sgstart, "sg", CAM_PERIPH_BIO, cgd->ccb_h.path, sgasync, AC_FOUND_DEVICE, cgd); if ((status != CAM_REQ_CMP) && (status != CAM_REQ_INPROG)) { const struct cam_status_entry *entry; entry = cam_fetch_status_entry(status); printf("sgasync: Unable to attach new device " "due to status %#x: %s\n", status, entry ? entry->status_text : "Unknown"); } break; } default: cam_periph_async(periph, code, path, arg); break; } } static cam_status sgregister(struct cam_periph *periph, void *arg) { struct sg_softc *softc; struct ccb_getdev *cgd; int no_tags; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { printf("sgregister: periph was NULL!!\n"); return (CAM_REQ_CMP_ERR); } if (cgd == NULL) { printf("sgregister: no getdev CCB, can't register device\n"); return (CAM_REQ_CMP_ERR); } softc = malloc(sizeof(*softc), M_DEVBUF, M_ZERO | M_NOWAIT); if (softc == NULL) { printf("sgregister: Unable to allocate softc\n"); return (CAM_REQ_CMP_ERR); } softc->state = SG_STATE_NORMAL; softc->pd_type = SID_TYPE(&cgd->inq_data); softc->sg_timeout = SG_DEFAULT_TIMEOUT / SG_DEFAULT_HZ * hz; softc->sg_user_timeout = SG_DEFAULT_TIMEOUT; TAILQ_INIT(&softc->rdwr_done); periph->softc = softc; /* * We pass in 0 for all blocksize, since we don't know what the * blocksize of the device is, if it even has a blocksize. */ cam_periph_unlock(periph); no_tags = (cgd->inq_data.flags & SID_CmdQue) == 0; softc->device_stats = devstat_new_entry("sg", periph->unit_number, 0, DEVSTAT_NO_BLOCKSIZE | (no_tags ? DEVSTAT_NO_ORDERED_TAGS : 0), softc->pd_type | DEVSTAT_TYPE_IF_SCSI | DEVSTAT_TYPE_PASS, DEVSTAT_PRIORITY_PASS); /* Register the device */ softc->dev = make_dev(&sg_cdevsw, periph->unit_number, UID_ROOT, GID_OPERATOR, 0600, "%s%d", periph->periph_name, periph->unit_number); (void)make_dev_alias(softc->dev, "sg%c", 'a' + periph->unit_number); cam_periph_lock(periph); softc->dev->si_drv1 = periph; /* * Add as async callback so that we get * notified if this device goes away. */ xpt_register_async(AC_LOST_DEVICE, sgasync, periph, periph->path); if (bootverbose) xpt_announce_periph(periph, NULL); return (CAM_REQ_CMP); } static void sgstart(struct cam_periph *periph, union ccb *start_ccb) { struct sg_softc *softc; softc = (struct sg_softc *)periph->softc; switch (softc->state) { case SG_STATE_NORMAL: start_ccb->ccb_h.ccb_type = SG_CCB_WAITING; SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, periph_links.sle); periph->immediate_priority = CAM_PRIORITY_NONE; wakeup(&periph->ccb_list); break; } } static void sgdone(struct cam_periph *periph, union ccb *done_ccb) { struct sg_softc *softc; struct ccb_scsiio *csio; softc = (struct sg_softc *)periph->softc; csio = &done_ccb->csio; switch (csio->ccb_h.ccb_type) { case SG_CCB_WAITING: /* Caller will release the CCB */ wakeup(&done_ccb->ccb_h.cbfcnp); return; case SG_CCB_RDWR_IO: { struct sg_rdwr *rdwr; int state; devstat_end_transaction(softc->device_stats, csio->dxfer_len, csio->tag_action & 0xf, ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) ? DEVSTAT_NO_DATA : (csio->ccb_h.flags & CAM_DIR_OUT) ? DEVSTAT_WRITE : DEVSTAT_READ, NULL, NULL); rdwr = done_ccb->ccb_h.ccb_rdwr; state = rdwr->state; rdwr->state = SG_RDWR_DONE; wakeup(rdwr); break; } default: panic("unknown sg CCB type"); } } static int sgopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct cam_periph *periph; struct sg_softc *softc; int error = 0; periph = (struct cam_periph *)dev->si_drv1; if (periph == NULL) return (ENXIO); /* * Don't allow access when we're running at a high securelevel. */ error = securelevel_gt(td->td_ucred, 1); if (error) return (error); cam_periph_lock(periph); softc = (struct sg_softc *)periph->softc; if (softc->flags & SG_FLAG_INVALID) { cam_periph_unlock(periph); return (ENXIO); } if ((softc->flags & SG_FLAG_OPEN) == 0) { softc->flags |= SG_FLAG_OPEN; + cam_periph_unlock(periph); } else { /* Device closes aren't symmetrical, fix up the refcount. */ + cam_periph_unlock(periph); cam_periph_release(periph); } - - cam_periph_unlock(periph); return (error); } static int sgclose(struct cdev *dev, int flag, int fmt, struct thread *td) { struct cam_periph *periph; struct sg_softc *softc; periph = (struct cam_periph *)dev->si_drv1; if (periph == NULL) return (ENXIO); cam_periph_lock(periph); softc = (struct sg_softc *)periph->softc; softc->flags &= ~SG_FLAG_OPEN; cam_periph_unlock(periph); cam_periph_release(periph); return (0); } static int sgioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { union ccb *ccb; struct ccb_scsiio *csio; struct cam_periph *periph; struct sg_softc *softc; struct sg_io_hdr req; int dir, error; periph = (struct cam_periph *)dev->si_drv1; if (periph == NULL) return (ENXIO); cam_periph_lock(periph); softc = (struct sg_softc *)periph->softc; error = 0; switch (cmd) { case LINUX_SCSI_GET_BUS_NUMBER: { int busno; busno = xpt_path_path_id(periph->path); error = copyout(&busno, arg, sizeof(busno)); break; } case LINUX_SCSI_GET_IDLUN: { struct scsi_idlun idlun; struct cam_sim *sim; idlun.dev_id = xpt_path_target_id(periph->path); sim = xpt_path_sim(periph->path); idlun.host_unique_id = sim->unit_number; error = copyout(&idlun, arg, sizeof(idlun)); break; } case SG_GET_VERSION_NUM: case LINUX_SG_GET_VERSION_NUM: error = copyout(&sg_version, arg, sizeof(sg_version)); break; case SG_SET_TIMEOUT: case LINUX_SG_SET_TIMEOUT: { u_int user_timeout; error = copyin(arg, &user_timeout, sizeof(u_int)); if (error == 0) { softc->sg_user_timeout = user_timeout; softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz; } break; } case SG_GET_TIMEOUT: case LINUX_SG_GET_TIMEOUT: /* * The value is returned directly to the syscall. */ td->td_retval[0] = softc->sg_user_timeout; error = 0; break; case SG_IO: case LINUX_SG_IO: error = copyin(arg, &req, sizeof(req)); if (error) break; if (req.cmd_len > IOCDBLEN) { error = EINVAL; break; } if (req.iovec_count != 0) { error = EOPNOTSUPP; break; } ccb = cam_periph_getccb(periph, /*priority*/5); csio = &ccb->csio; error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes, req.cmd_len); if (error) { xpt_release_ccb(ccb); break; } switch(req.dxfer_direction) { case SG_DXFER_TO_DEV: dir = CAM_DIR_OUT; break; case SG_DXFER_FROM_DEV: dir = CAM_DIR_IN; break; case SG_DXFER_TO_FROM_DEV: dir = CAM_DIR_IN | CAM_DIR_OUT; break; case SG_DXFER_NONE: default: dir = CAM_DIR_NONE; break; } cam_fill_csio(csio, /*retries*/1, sgdone, dir|CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, req.dxferp, req.dxfer_len, req.mx_sb_len, req.cmd_len, req.timeout); error = sgsendccb(periph, ccb); if (error) { req.host_status = DID_ERROR; req.driver_status = DRIVER_INVALID; xpt_release_ccb(ccb); break; } req.status = csio->scsi_status; req.masked_status = (csio->scsi_status >> 1) & 0x7f; sg_scsiio_status(csio, &req.host_status, &req.driver_status); req.resid = csio->resid; req.duration = csio->ccb_h.timeout; req.info = 0; error = copyout(&req, arg, sizeof(req)); if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID) && (req.sbp != NULL)) { req.sb_len_wr = req.mx_sb_len - csio->sense_resid; error = copyout(&csio->sense_data, req.sbp, req.sb_len_wr); } xpt_release_ccb(ccb); break; case SG_GET_RESERVED_SIZE: case LINUX_SG_GET_RESERVED_SIZE: { int size = 32768; error = copyout(&size, arg, sizeof(size)); break; } case SG_GET_SCSI_ID: case LINUX_SG_GET_SCSI_ID: { struct sg_scsi_id id; id.host_no = 0; /* XXX */ id.channel = xpt_path_path_id(periph->path); id.scsi_id = xpt_path_target_id(periph->path); id.lun = xpt_path_lun_id(periph->path); id.scsi_type = softc->pd_type; id.h_cmd_per_lun = 1; id.d_queue_depth = 1; id.unused[0] = 0; id.unused[1] = 0; error = copyout(&id, arg, sizeof(id)); break; } case SG_EMULATED_HOST: case SG_SET_TRANSFORM: case SG_GET_TRANSFORM: case SG_GET_NUM_WAITING: case SG_SCSI_RESET: case SG_GET_REQUEST_TABLE: case SG_SET_KEEP_ORPHAN: case SG_GET_KEEP_ORPHAN: case SG_GET_ACCESS_COUNT: case SG_SET_FORCE_LOW_DMA: case SG_GET_LOW_DMA: case SG_GET_SG_TABLESIZE: case SG_SET_FORCE_PACK_ID: case SG_GET_PACK_ID: case SG_SET_RESERVED_SIZE: case SG_GET_COMMAND_Q: case SG_SET_COMMAND_Q: case SG_SET_DEBUG: case SG_NEXT_CMD_LEN: case LINUX_SG_EMULATED_HOST: case LINUX_SG_SET_TRANSFORM: case LINUX_SG_GET_TRANSFORM: case LINUX_SG_GET_NUM_WAITING: case LINUX_SG_SCSI_RESET: case LINUX_SG_GET_REQUEST_TABLE: case LINUX_SG_SET_KEEP_ORPHAN: case LINUX_SG_GET_KEEP_ORPHAN: case LINUX_SG_GET_ACCESS_COUNT: case LINUX_SG_SET_FORCE_LOW_DMA: case LINUX_SG_GET_LOW_DMA: case LINUX_SG_GET_SG_TABLESIZE: case LINUX_SG_SET_FORCE_PACK_ID: case LINUX_SG_GET_PACK_ID: case LINUX_SG_SET_RESERVED_SIZE: case LINUX_SG_GET_COMMAND_Q: case LINUX_SG_SET_COMMAND_Q: case LINUX_SG_SET_DEBUG: case LINUX_SG_NEXT_CMD_LEN: default: #ifdef CAMDEBUG printf("sgioctl: rejecting cmd 0x%lx\n", cmd); #endif error = ENODEV; break; } cam_periph_unlock(periph); return (error); } static int sgwrite(struct cdev *dev, struct uio *uio, int ioflag) { union ccb *ccb; struct cam_periph *periph; struct ccb_scsiio *csio; struct sg_softc *sc; struct sg_header *hdr; struct sg_rdwr *rdwr; u_char cdb_cmd; char *buf; int error = 0, cdb_len, buf_len, dir; periph = dev->si_drv1; rdwr = malloc(sizeof(*rdwr), M_DEVBUF, M_WAITOK | M_ZERO); hdr = &rdwr->hdr.hdr; /* Copy in the header block and sanity check it */ if (uio->uio_resid < sizeof(*hdr)) { error = EINVAL; goto out_hdr; } error = uiomove(hdr, sizeof(*hdr), uio); if (error) goto out_hdr; ccb = xpt_alloc_ccb(); if (ccb == NULL) { error = ENOMEM; goto out_hdr; } csio = &ccb->csio; /* * Copy in the CDB block. The designers of the interface didn't * bother to provide a size for this in the header, so we have to * figure it out ourselves. */ if (uio->uio_resid < 1) goto out_ccb; error = uiomove(&cdb_cmd, 1, uio); if (error) goto out_ccb; if (hdr->twelve_byte) cdb_len = 12; else cdb_len = scsi_group_len(cdb_cmd); /* * We've already read the first byte of the CDB and advanced the uio * pointer. Just read the rest. */ csio->cdb_io.cdb_bytes[0] = cdb_cmd; error = uiomove(&csio->cdb_io.cdb_bytes[1], cdb_len - 1, uio); if (error) goto out_ccb; /* * Now set up the data block. Again, the designers didn't bother * to make this reliable. */ buf_len = uio->uio_resid; if (buf_len != 0) { buf = malloc(buf_len, M_DEVBUF, M_WAITOK | M_ZERO); error = uiomove(buf, buf_len, uio); if (error) goto out_buf; dir = CAM_DIR_OUT; } else if (hdr->reply_len != 0) { buf = malloc(hdr->reply_len, M_DEVBUF, M_WAITOK | M_ZERO); buf_len = hdr->reply_len; dir = CAM_DIR_IN; } else { buf = NULL; buf_len = 0; dir = CAM_DIR_NONE; } cam_periph_lock(periph); sc = periph->softc; xpt_setup_ccb(&ccb->ccb_h, periph->path, /*priority*/5); cam_fill_csio(csio, /*retries*/1, sgdone, dir|CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, buf, buf_len, SG_MAX_SENSE, cdb_len, sc->sg_timeout); /* * Send off the command and hope that it works. This path does not * go through sgstart because the I/O is supposed to be asynchronous. */ rdwr->buf = buf; rdwr->buf_len = buf_len; rdwr->tag = hdr->pack_id; rdwr->ccb = ccb; rdwr->state = SG_RDWR_INPROG; ccb->ccb_h.ccb_rdwr = rdwr; ccb->ccb_h.ccb_type = SG_CCB_RDWR_IO; TAILQ_INSERT_TAIL(&sc->rdwr_done, rdwr, rdwr_link); error = sgsendrdwr(periph, ccb); cam_periph_unlock(periph); return (error); out_buf: free(buf, M_DEVBUF); out_ccb: xpt_free_ccb(ccb); out_hdr: free(rdwr, M_DEVBUF); return (error); } static int sgread(struct cdev *dev, struct uio *uio, int ioflag) { struct ccb_scsiio *csio; struct cam_periph *periph; struct sg_softc *sc; struct sg_header *hdr; struct sg_rdwr *rdwr; u_short hstat, dstat; int error, pack_len, reply_len, pack_id; periph = dev->si_drv1; /* XXX The pack len field needs to be updated and written out instead * of discarded. Not sure how to do that. */ uio->uio_rw = UIO_WRITE; if ((error = uiomove(&pack_len, 4, uio)) != 0) return (error); if ((error = uiomove(&reply_len, 4, uio)) != 0) return (error); if ((error = uiomove(&pack_id, 4, uio)) != 0) return (error); uio->uio_rw = UIO_READ; cam_periph_lock(periph); sc = periph->softc; search: TAILQ_FOREACH(rdwr, &sc->rdwr_done, rdwr_link) { if (rdwr->tag == pack_id) break; } if ((rdwr == NULL) || (rdwr->state != SG_RDWR_DONE)) { if (msleep(rdwr, periph->sim->mtx, PCATCH, "sgread", 0) == ERESTART) return (EAGAIN); goto search; } TAILQ_REMOVE(&sc->rdwr_done, rdwr, rdwr_link); cam_periph_unlock(periph); hdr = &rdwr->hdr.hdr; csio = &rdwr->ccb->csio; sg_scsiio_status(csio, &hstat, &dstat); hdr->host_status = hstat; hdr->driver_status = dstat; hdr->target_status = csio->scsi_status >> 1; switch (hstat) { case DID_OK: case DID_PASSTHROUGH: case DID_SOFT_ERROR: hdr->result = 0; break; case DID_NO_CONNECT: case DID_BUS_BUSY: case DID_TIME_OUT: hdr->result = EBUSY; break; case DID_BAD_TARGET: case DID_ABORT: case DID_PARITY: case DID_RESET: case DID_BAD_INTR: case DID_ERROR: default: hdr->result = EIO; break; } if (dstat == DRIVER_SENSE) { bcopy(&csio->sense_data, hdr->sense_buffer, min(csio->sense_len, SG_MAX_SENSE)); #ifdef CAMDEBUG scsi_sense_print(csio); #endif } error = uiomove(&hdr->result, sizeof(*hdr) - offsetof(struct sg_header, result), uio); if ((error == 0) && (hdr->result == 0)) error = uiomove(rdwr->buf, rdwr->buf_len, uio); cam_periph_lock(periph); xpt_free_ccb(rdwr->ccb); cam_periph_unlock(periph); free(rdwr->buf, M_DEVBUF); free(rdwr, M_DEVBUF); return (error); } static int sgsendccb(struct cam_periph *periph, union ccb *ccb) { struct sg_softc *softc; struct cam_periph_map_info mapinfo; int error, need_unmap = 0; softc = periph->softc; if (((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) && (ccb->csio.data_ptr != NULL)) { bzero(&mapinfo, sizeof(mapinfo)); /* * cam_periph_mapmem calls into proc and vm functions that can * sleep as well as trigger I/O, so we can't hold the lock. * Dropping it here is reasonably safe. */ cam_periph_unlock(periph); error = cam_periph_mapmem(ccb, &mapinfo); cam_periph_lock(periph); if (error) return (error); need_unmap = 1; } error = cam_periph_runccb(ccb, sgerror, CAM_RETRY_SELTO, SF_RETRY_UA, softc->device_stats); if (need_unmap) cam_periph_unmapmem(ccb, &mapinfo); return (error); } static int sgsendrdwr(struct cam_periph *periph, union ccb *ccb) { struct sg_softc *softc; softc = periph->softc; devstat_start_transaction(softc->device_stats, NULL); xpt_action(ccb); return (0); } static int sgerror(union ccb *ccb, uint32_t cam_flags, uint32_t sense_flags) { struct cam_periph *periph; struct sg_softc *softc; periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct sg_softc *)periph->softc; return (cam_periph_error(ccb, cam_flags, sense_flags, &softc->saved_ccb)); } static void sg_scsiio_status(struct ccb_scsiio *csio, u_short *hoststat, u_short *drvstat) { int status; status = csio->ccb_h.status; switch (status & CAM_STATUS_MASK) { case CAM_REQ_CMP: *hoststat = DID_OK; *drvstat = 0; break; case CAM_REQ_CMP_ERR: *hoststat = DID_ERROR; *drvstat = 0; break; case CAM_REQ_ABORTED: *hoststat = DID_ABORT; *drvstat = 0; break; case CAM_REQ_INVALID: *hoststat = DID_ERROR; *drvstat = DRIVER_INVALID; break; case CAM_DEV_NOT_THERE: *hoststat = DID_BAD_TARGET; *drvstat = 0; case CAM_SEL_TIMEOUT: *hoststat = DID_NO_CONNECT; *drvstat = 0; break; case CAM_CMD_TIMEOUT: *hoststat = DID_TIME_OUT; *drvstat = 0; break; case CAM_SCSI_STATUS_ERROR: *hoststat = DID_ERROR; *drvstat = 0; case CAM_SCSI_BUS_RESET: *hoststat = DID_RESET; *drvstat = 0; break; case CAM_UNCOR_PARITY: *hoststat = DID_PARITY; *drvstat = 0; break; case CAM_SCSI_BUSY: *hoststat = DID_BUS_BUSY; *drvstat = 0; default: *hoststat = DID_ERROR; *drvstat = DRIVER_ERROR; } if (status & CAM_AUTOSNS_VALID) *drvstat = DRIVER_SENSE; } static int scsi_group_len(u_char cmd) { int len[] = {6, 10, 10, 12, 12, 12, 10, 10}; int group; group = (cmd >> 5) & 0x7; return (len[group]); } Index: projects/cambria/sys/conf/files =================================================================== --- projects/cambria/sys/conf/files (revision 186459) +++ projects/cambria/sys/conf/files (revision 186460) @@ -1,2779 +1,2780 @@ # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # acpi_quirks.h optional acpi \ dependency "$S/tools/acpi_quirks2h.awk $S/dev/acpica/acpi_quirks" \ compile-with "${AWK} -f $S/tools/acpi_quirks2h.awk $S/dev/acpica/acpi_quirks" \ no-obj no-implicit-rule before-depend \ clean "acpi_quirks.h" aicasm optional ahc | ahd \ dependency "$S/dev/aic7xxx/aicasm/*.[chyl]" \ compile-with "CC='${CC}' ${MAKE} -f $S/dev/aic7xxx/aicasm/Makefile MAKESRCPATH=$S/dev/aic7xxx/aicasm" \ no-obj no-implicit-rule \ clean "aicasm* y.tab.h" aic7xxx_seq.h optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h -p aic7xxx_reg_print.c -i $S/dev/aic7xxx/aic7xxx_osm.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic7xxx_seq.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic7xxx_reg.h optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h -p aic7xxx_reg_print.c -i $S/dev/aic7xxx/aic7xxx_osm.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic7xxx_reg.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic7xxx_reg_print.c optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h -p aic7xxx_reg_print.c -i $S/dev/aic7xxx/aic7xxx_osm.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule local \ clean "aic7xxx_reg_print.c" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic7xxx_reg_print.o optional ahc ahc_reg_pretty_print \ compile-with "${NORMAL_C}" \ no-implicit-rule local aic79xx_seq.h optional ahd pci \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic79xx_seq.h -r aic79xx_reg.h -p aic79xx_reg_print.c -i $S/dev/aic7xxx/aic79xx_osm.h $S/dev/aic7xxx/aic79xx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic79xx_seq.h" \ dependency "$S/dev/aic7xxx/aic79xx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic79xx_reg.h optional ahd pci \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic79xx_seq.h -r aic79xx_reg.h -p aic79xx_reg_print.c -i $S/dev/aic7xxx/aic79xx_osm.h $S/dev/aic7xxx/aic79xx.seq" \ no-obj no-implicit-rule before-depend local \ clean "aic79xx_reg.h" \ dependency "$S/dev/aic7xxx/aic79xx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic79xx_reg_print.c optional ahd pci \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic79xx_seq.h -r aic79xx_reg.h -p aic79xx_reg_print.c -i $S/dev/aic7xxx/aic79xx_osm.h $S/dev/aic7xxx/aic79xx.seq" \ no-obj no-implicit-rule local \ clean "aic79xx_reg_print.c" \ dependency "$S/dev/aic7xxx/aic79xx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" aic79xx_reg_print.o optional ahd pci ahd_reg_pretty_print \ compile-with "${NORMAL_C}" \ no-implicit-rule local emu10k1-alsa%diked.h optional snd_emu10k1 | snd_emu10kx \ dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h" \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/emu10k1-alsa.h emu10k1-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "emu10k1-alsa%diked.h" p16v-alsa%diked.h optional snd_emu10kx pci \ dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p16v-alsa.h" \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p16v-alsa.h p16v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p16v-alsa%diked.h" p17v-alsa%diked.h optional snd_emu10kx pci \ dependency "$S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h" \ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p17v-alsa%diked.h" miidevs.h optional miibus | mii \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ no-obj no-implicit-rule before-depend \ clean "miidevs.h" pccarddevs.h standard \ dependency "$S/tools/pccarddevs2h.awk $S/dev/pccard/pccarddevs" \ compile-with "${AWK} -f $S/tools/pccarddevs2h.awk $S/dev/pccard/pccarddevs" \ no-obj no-implicit-rule before-depend \ clean "pccarddevs.h" usbdevs.h optional usb \ dependency "$S/tools/usbdevs2h.awk $S/dev/usb/usbdevs" \ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -h" \ no-obj no-implicit-rule before-depend \ clean "usbdevs.h" usbdevs_data.h optional usb \ dependency "$S/tools/usbdevs2h.awk $S/dev/usb/usbdevs" \ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -d" \ no-obj no-implicit-rule before-depend \ clean "usbdevs_data.h" cam/cam.c optional scbus cam/cam_periph.c optional scbus cam/cam_queue.c optional scbus cam/cam_sim.c optional scbus cam/cam_xpt.c optional scbus cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/scsi/scsi_da.c optional da cam/scsi/scsi_low.c optional ct | ncv | nsp | stg cam/scsi/scsi_low_pisa.c optional ct | ncv | nsp | stg cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa cam/scsi/scsi_ses.c optional ses cam/scsi/scsi_sg.c optional sg cam/scsi/scsi_targ_bh.c optional targbh cam/scsi/scsi_target.c optional targ contrib/altq/altq/altq_cbq.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_cdnr.c optional altq contrib/altq/altq/altq_hfsc.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_priq.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_red.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_rio.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/altq/altq/altq_rmclass.c optional altq contrib/altq/altq/altq_subr.c optional altq \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/dev/acpica/dbcmds.c optional acpi acpi_debug contrib/dev/acpica/dbdisply.c optional acpi acpi_debug contrib/dev/acpica/dbexec.c optional acpi acpi_debug contrib/dev/acpica/dbfileio.c optional acpi acpi_debug contrib/dev/acpica/dbhistry.c optional acpi acpi_debug contrib/dev/acpica/dbinput.c optional acpi acpi_debug contrib/dev/acpica/dbstats.c optional acpi acpi_debug contrib/dev/acpica/dbutils.c optional acpi acpi_debug contrib/dev/acpica/dbxface.c optional acpi acpi_debug contrib/dev/acpica/dmbuffer.c optional acpi acpi_debug contrib/dev/acpica/dmnames.c optional acpi acpi_debug contrib/dev/acpica/dmopcode.c optional acpi acpi_debug contrib/dev/acpica/dmobject.c optional acpi acpi_debug contrib/dev/acpica/dmresrc.c optional acpi acpi_debug contrib/dev/acpica/dmresrcl.c optional acpi acpi_debug contrib/dev/acpica/dmresrcs.c optional acpi acpi_debug contrib/dev/acpica/dmutils.c optional acpi acpi_debug contrib/dev/acpica/dmwalk.c optional acpi acpi_debug contrib/dev/acpica/dsfield.c optional acpi contrib/dev/acpica/dsinit.c optional acpi contrib/dev/acpica/dsmethod.c optional acpi contrib/dev/acpica/dsmthdat.c optional acpi contrib/dev/acpica/dsobject.c optional acpi contrib/dev/acpica/dsopcode.c optional acpi contrib/dev/acpica/dsutils.c optional acpi contrib/dev/acpica/dswexec.c optional acpi contrib/dev/acpica/dswload.c optional acpi contrib/dev/acpica/dswscope.c optional acpi contrib/dev/acpica/dswstate.c optional acpi contrib/dev/acpica/evevent.c optional acpi contrib/dev/acpica/evgpe.c optional acpi contrib/dev/acpica/evgpeblk.c optional acpi contrib/dev/acpica/evmisc.c optional acpi contrib/dev/acpica/evregion.c optional acpi contrib/dev/acpica/evrgnini.c optional acpi contrib/dev/acpica/evsci.c optional acpi contrib/dev/acpica/evxface.c optional acpi contrib/dev/acpica/evxfevnt.c optional acpi contrib/dev/acpica/evxfregn.c optional acpi contrib/dev/acpica/exconfig.c optional acpi contrib/dev/acpica/exconvrt.c optional acpi contrib/dev/acpica/excreate.c optional acpi contrib/dev/acpica/exdump.c optional acpi contrib/dev/acpica/exfield.c optional acpi contrib/dev/acpica/exfldio.c optional acpi contrib/dev/acpica/exmisc.c optional acpi contrib/dev/acpica/exmutex.c optional acpi contrib/dev/acpica/exnames.c optional acpi contrib/dev/acpica/exoparg1.c optional acpi contrib/dev/acpica/exoparg2.c optional acpi contrib/dev/acpica/exoparg3.c optional acpi contrib/dev/acpica/exoparg6.c optional acpi contrib/dev/acpica/exprep.c optional acpi contrib/dev/acpica/exregion.c optional acpi contrib/dev/acpica/exresnte.c optional acpi contrib/dev/acpica/exresolv.c optional acpi contrib/dev/acpica/exresop.c optional acpi contrib/dev/acpica/exstore.c optional acpi contrib/dev/acpica/exstoren.c optional acpi contrib/dev/acpica/exstorob.c optional acpi contrib/dev/acpica/exsystem.c optional acpi contrib/dev/acpica/exutils.c optional acpi contrib/dev/acpica/hwacpi.c optional acpi contrib/dev/acpica/hwgpe.c optional acpi contrib/dev/acpica/hwregs.c optional acpi contrib/dev/acpica/hwsleep.c optional acpi contrib/dev/acpica/hwtimer.c optional acpi contrib/dev/acpica/nsaccess.c optional acpi contrib/dev/acpica/nsalloc.c optional acpi contrib/dev/acpica/nsdump.c optional acpi contrib/dev/acpica/nseval.c optional acpi contrib/dev/acpica/nsinit.c optional acpi contrib/dev/acpica/nsload.c optional acpi contrib/dev/acpica/nsnames.c optional acpi contrib/dev/acpica/nsobject.c optional acpi contrib/dev/acpica/nsparse.c optional acpi contrib/dev/acpica/nssearch.c optional acpi contrib/dev/acpica/nsutils.c optional acpi contrib/dev/acpica/nswalk.c optional acpi contrib/dev/acpica/nsxfeval.c optional acpi contrib/dev/acpica/nsxfname.c optional acpi contrib/dev/acpica/nsxfobj.c optional acpi contrib/dev/acpica/psargs.c optional acpi contrib/dev/acpica/psloop.c optional acpi contrib/dev/acpica/psopcode.c optional acpi contrib/dev/acpica/psparse.c optional acpi contrib/dev/acpica/psscope.c optional acpi contrib/dev/acpica/pstree.c optional acpi contrib/dev/acpica/psutils.c optional acpi contrib/dev/acpica/pswalk.c optional acpi contrib/dev/acpica/psxface.c optional acpi contrib/dev/acpica/rsaddr.c optional acpi contrib/dev/acpica/rscalc.c optional acpi contrib/dev/acpica/rscreate.c optional acpi contrib/dev/acpica/rsdump.c optional acpi contrib/dev/acpica/rsinfo.c optional acpi contrib/dev/acpica/rsio.c optional acpi contrib/dev/acpica/rsirq.c optional acpi contrib/dev/acpica/rslist.c optional acpi contrib/dev/acpica/rsmemory.c optional acpi contrib/dev/acpica/rsmisc.c optional acpi contrib/dev/acpica/rsutils.c optional acpi contrib/dev/acpica/rsxface.c optional acpi contrib/dev/acpica/tbfadt.c optional acpi contrib/dev/acpica/tbfind.c optional acpi contrib/dev/acpica/tbinstal.c optional acpi contrib/dev/acpica/tbutils.c optional acpi contrib/dev/acpica/tbxface.c optional acpi contrib/dev/acpica/tbxfroot.c optional acpi contrib/dev/acpica/utalloc.c optional acpi contrib/dev/acpica/utcache.c optional acpi contrib/dev/acpica/utclib.c optional acpi contrib/dev/acpica/utcopy.c optional acpi contrib/dev/acpica/utdebug.c optional acpi contrib/dev/acpica/utdelete.c optional acpi contrib/dev/acpica/uteval.c optional acpi contrib/dev/acpica/utglobal.c optional acpi contrib/dev/acpica/utinit.c optional acpi contrib/dev/acpica/utmath.c optional acpi contrib/dev/acpica/utmisc.c optional acpi contrib/dev/acpica/utmutex.c optional acpi contrib/dev/acpica/utobject.c optional acpi contrib/dev/acpica/utresrc.c optional acpi contrib/dev/acpica/utstate.c optional acpi contrib/dev/acpica/utxface.c optional acpi contrib/ipfilter/netinet/fil.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_auth.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_fil_freebsd.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_frag.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_log.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_nat.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_proxy.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_state.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_lookup.c optional ipfilter inet \ compile-with "${NORMAL_C} -Wno-error -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_pool.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_htable.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/ip_sync.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ipfilter/netinet/mlfk_ipl.c optional ipfilter inet \ compile-with "${NORMAL_C} -I$S/contrib/ipfilter" contrib/ngatm/netnatm/api/cc_conn.c optional ngatm_ccatm \ compile-with "${NORMAL_C_NOWERROR} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_data.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_dump.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_port.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_sig.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/cc_user.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/api/unisap.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/misc/straddr.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/misc/unimsg_common.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/msg/traffic.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/msg/uni_ie.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/msg/uni_msg.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/saal/saal_sscfu.c optional ngatm_sscfu \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/saal/saal_sscop.c optional ngatm_sscop \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_call.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_coord.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_party.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_print.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_reset.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_uni.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_unimsgcpy.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/ngatm/netnatm/sig/sig_verify.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" contrib/pf/net/if_pflog.c optional pflog \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/if_pfsync.c optional pfsync \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_if.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_ioctl.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_norm.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_osfp.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_ruleset.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_subr.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/net/pf_table.c optional pf \ compile-with "${NORMAL_C} -I$S/contrib/pf" contrib/pf/netinet/in4_cksum.c optional pf inet crypto/blowfish/bf_ecb.c optional ipsec crypto/blowfish/bf_skey.c optional crypto | ipsec crypto/camellia/camellia.c optional crypto | ipsec crypto/camellia/camellia-api.c optional crypto | ipsec crypto/des/des_ecb.c optional crypto | ipsec | netsmb crypto/des/des_setkey.c optional crypto | ipsec | netsmb crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \ ipsec | random | wlan_ccmp crypto/rijndael/rijndael-api-fst.c optional geom_bde | random crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp crypto/sha1.c optional carp | crypto | ipsec | \ netgraph_mppc_encryption | sctp crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random | \ sctp ddb/db_access.c optional ddb ddb/db_break.c optional ddb ddb/db_capture.c optional ddb ddb/db_command.c optional ddb ddb/db_examine.c optional ddb ddb/db_expr.c optional ddb ddb/db_input.c optional ddb ddb/db_lex.c optional ddb ddb/db_main.c optional ddb ddb/db_output.c optional ddb ddb/db_print.c optional ddb ddb/db_ps.c optional ddb ddb/db_run.c optional ddb ddb/db_script.c optional ddb ddb/db_sym.c optional ddb ddb/db_thread.c optional ddb ddb/db_textdump.c optional ddb ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb #dev/dpt/dpt_control.c optional dpt dev/aac/aac.c optional aac dev/aac/aac_cam.c optional aacp aac dev/aac/aac_debug.c optional aac dev/aac/aac_disk.c optional aac dev/aac/aac_linux.c optional aac compat_linux dev/aac/aac_pci.c optional aac pci dev/acpi_support/acpi_aiboost.c optional acpi_aiboost acpi dev/acpi_support/acpi_asus.c optional acpi_asus acpi dev/acpi_support/acpi_fujitsu.c optional acpi_fujitsu acpi dev/acpi_support/acpi_ibm.c optional acpi_ibm acpi dev/acpi_support/acpi_panasonic.c optional acpi_panasonic acpi dev/acpi_support/acpi_sony.c optional acpi_sony acpi dev/acpi_support/acpi_toshiba.c optional acpi_toshiba acpi dev/acpica/Osd/OsdDebug.c optional acpi dev/acpica/Osd/OsdHardware.c optional acpi dev/acpica/Osd/OsdInterrupt.c optional acpi dev/acpica/Osd/OsdMemory.c optional acpi dev/acpica/Osd/OsdSchedule.c optional acpi dev/acpica/Osd/OsdStream.c optional acpi dev/acpica/Osd/OsdSynch.c optional acpi dev/acpica/Osd/OsdTable.c optional acpi dev/acpica/acpi.c optional acpi dev/acpica/acpi_acad.c optional acpi dev/acpica/acpi_battery.c optional acpi dev/acpica/acpi_button.c optional acpi dev/acpica/acpi_cmbat.c optional acpi dev/acpica/acpi_cpu.c optional acpi dev/acpica/acpi_ec.c optional acpi dev/acpica/acpi_hpet.c optional acpi dev/acpica/acpi_isab.c optional acpi isa dev/acpica/acpi_lid.c optional acpi dev/acpica/acpi_package.c optional acpi dev/acpica/acpi_pci.c optional acpi pci dev/acpica/acpi_pci_link.c optional acpi pci dev/acpica/acpi_pcib.c optional acpi pci dev/acpica/acpi_pcib_acpi.c optional acpi pci dev/acpica/acpi_pcib_pci.c optional acpi pci dev/acpica/acpi_perf.c optional acpi dev/acpica/acpi_powerres.c optional acpi dev/acpica/acpi_quirk.c optional acpi dev/acpica/acpi_resource.c optional acpi dev/acpica/acpi_smbat.c optional acpi dev/acpica/acpi_thermal.c optional acpi dev/acpica/acpi_throttle.c optional acpi dev/acpica/acpi_timer.c optional acpi dev/acpica/acpi_video.c optional acpi_video acpi dev/acpica/acpi_dock.c optional acpi_dock acpi dev/adlink/adlink.c optional adlink dev/advansys/adv_eisa.c optional adv eisa dev/advansys/adv_pci.c optional adv pci dev/advansys/advansys.c optional adv dev/advansys/advlib.c optional adv dev/advansys/advmcode.c optional adv dev/advansys/adw_pci.c optional adw pci dev/advansys/adwcam.c optional adw dev/advansys/adwlib.c optional adw dev/advansys/adwmcode.c optional adw dev/ae/if_ae.c optional ae pci dev/age/if_age.c optional age pci dev/agp/agp.c optional agp pci dev/agp/agp_if.m optional agp pci dev/aha/aha.c optional aha dev/aha/aha_isa.c optional aha isa dev/aha/aha_mca.c optional aha mca dev/ahb/ahb.c optional ahb eisa dev/aic/aic.c optional aic dev/aic/aic_pccard.c optional aic pccard dev/aic7xxx/ahc_eisa.c optional ahc eisa dev/aic7xxx/ahc_isa.c optional ahc isa dev/aic7xxx/ahc_pci.c optional ahc pci dev/aic7xxx/ahd_pci.c optional ahd pci dev/aic7xxx/aic7770.c optional ahc dev/aic7xxx/aic79xx.c optional ahd pci dev/aic7xxx/aic79xx_osm.c optional ahd pci dev/aic7xxx/aic79xx_pci.c optional ahd pci dev/aic7xxx/aic7xxx.c optional ahc dev/aic7xxx/aic7xxx_93cx6.c optional ahc dev/aic7xxx/aic7xxx_osm.c optional ahc dev/aic7xxx/aic7xxx_pci.c optional ahc pci dev/ale/if_ale.c optional ale pci dev/amd/amd.c optional amd dev/amr/amr.c optional amr dev/amr/amr_cam.c optional amrp amr dev/amr/amr_disk.c optional amr dev/amr/amr_linux.c optional amr compat_linux dev/amr/amr_pci.c optional amr pci dev/an/if_an.c optional an dev/an/if_an_isa.c optional an isa dev/an/if_an_pccard.c optional an pccard dev/an/if_an_pci.c optional an pci dev/asr/asr.c optional asr pci # dev/ata/ata_if.m optional ata | atacore dev/ata/ata-all.c optional ata | atacore dev/ata/ata-lowlevel.c optional ata | atacore dev/ata/ata-queue.c optional ata | atacore dev/ata/ata-card.c optional ata pccard | atapccard dev/ata/ata-cbus.c optional ata pc98 | atapc98 dev/ata/ata-isa.c optional ata isa | ataisa dev/ata/ata-pci.c optional ata pci | atapci dev/ata/ata-dma.c optional ata pci | atapci dev/ata/ata-sata.c optional ata pci | atapci dev/ata/chipsets/ata-ahci.c optional ata pci | ataahci | ataacerlabs | \ ataati | ataintel | atajmicron | atavia dev/ata/chipsets/ata-acard.c optional ata pci | ataacard dev/ata/chipsets/ata-acerlabs.c optional ata pci | ataacerlabs dev/ata/chipsets/ata-adaptec.c optional ata pci | ataadaptec dev/ata/chipsets/ata-amd.c optional ata pci | ataamd dev/ata/chipsets/ata-ati.c optional ata pci | ataati dev/ata/chipsets/ata-cenatek.c optional ata pci | atacenatek dev/ata/chipsets/ata-cypress.c optional ata pci | atacypress dev/ata/chipsets/ata-cyrix.c optional ata pci | atacyrix dev/ata/chipsets/ata-highpoint.c optional ata pci | atahighpoint dev/ata/chipsets/ata-intel.c optional ata pci | ataintel dev/ata/chipsets/ata-ite.c optional ata pci | ataite dev/ata/chipsets/ata-jmicron.c optional ata pci | atajmicron dev/ata/chipsets/ata-marvell.c optional ata pci | atamarvell dev/ata/chipsets/ata-micron.c optional ata pci | atamicron dev/ata/chipsets/ata-national.c optional ata pci | atanational dev/ata/chipsets/ata-netcell.c optional ata pci | atanetcell dev/ata/chipsets/ata-nvidia.c optional ata pci | atanvidia dev/ata/chipsets/ata-promise.c optional ata pci | atapromise dev/ata/chipsets/ata-serverworks.c optional ata pci | ataserverworks dev/ata/chipsets/ata-siliconimage.c optional ata pci | atasiliconimage dev/ata/chipsets/ata-sis.c optional ata pci | atasis dev/ata/chipsets/ata-via.c optional ata pci | atavia dev/ata/ata-disk.c optional atadisk dev/ata/ata-raid.c optional ataraid dev/ata/ata-usb.c optional atausb dev/ata/atapi-cd.c optional atapicd dev/ata/atapi-fd.c optional atapifd dev/ata/atapi-tape.c optional atapist dev/ata/atapi-cam.c optional atapicam # dev/ath/if_ath.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/if_ath_pci.c optional ath pci \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ah_osdep.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah_eeprom_v1.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah_eeprom_v3.c optional ath_hal | ath_ar5211 | ath_ar5212 \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah_eeprom_v14.c optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah_regdomain.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ar5210/ar5210_attach.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_beacon.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_interrupts.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_keycache.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_misc.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_phy.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_power.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_recv.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_reset.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5210/ar5210_xmit.c optional ath_hal | ath_ar5210 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_attach.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_beacon.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_interrupts.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_keycache.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_misc.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_phy.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_power.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_recv.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_reset.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5211/ar5211_xmit.c optional ath_hal | ath_ar5211 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_ani.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_attach.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_beacon.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_eeprom.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_gpio.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_interrupts.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_keycache.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_misc.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_phy.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_power.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_recv.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_reset.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_rfgain.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5212_xmit.c \ optional ath_hal | ath_ar5212 | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2316.c optional ath_rf2316 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2317.c optional ath_rf2317 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2413.c optional ath_hal | ath_rf2413 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar2425.c optional ath_hal | ath_rf2425 | ath_rf2417 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5111.c optional ath_hal | ath_rf5111 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5112.c optional ath_hal | ath_rf5112 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5212/ar5413.c optional ath_hal | ath_rf5413 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar2133.c optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_ani.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_attach.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_beacon.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal_iq.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal_adcgain.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_cal_adcdc.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_eeprom.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_gpio.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_interrupts.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_keycache.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_misc.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_phy.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_power.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_recv.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_reset.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar5416_xmit.c \ optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar9160_attach.c optional ath_hal | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_rate/amrr/amrr.c optional ath_rate_amrr \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_rate/onoe/onoe.c optional ath_rate_onoe \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_rate/sample/sample.c optional ath_rate_sample \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/bce/if_bce.c optional bce dev/bfe/if_bfe.c optional bfe dev/bge/if_bge.c optional bge dev/bktr/bktr_audio.c optional bktr pci dev/bktr/bktr_card.c optional bktr pci dev/bktr/bktr_core.c optional bktr pci dev/bktr/bktr_i2c.c optional bktr pci smbus dev/bktr/bktr_os.c optional bktr pci dev/bktr/bktr_tuner.c optional bktr pci dev/bktr/msp34xx.c optional bktr pci dev/buslogic/bt.c optional bt dev/buslogic/bt_eisa.c optional bt eisa dev/buslogic/bt_isa.c optional bt isa dev/buslogic/bt_mca.c optional bt mca dev/buslogic/bt_pci.c optional bt pci dev/cardbus/cardbus.c optional cardbus dev/cardbus/cardbus_cis.c optional cardbus dev/cardbus/cardbus_device.c optional cardbus dev/cfi/cfi_core.c optional cfi dev/cfi/cfi_dev.c optional cfi dev/ciss/ciss.c optional ciss dev/cm/smc90cx6.c optional cm dev/cmx/cmx.c optional cmx dev/cmx/cmx_pccard.c optional cmx pccard dev/cpufreq/ichss.c optional cpufreq dev/cs/if_cs.c optional cs dev/cs/if_cs_isa.c optional cs isa dev/cs/if_cs_pccard.c optional cs pccard dev/cxgb/cxgb_main.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/cxgb_offload.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/cxgb_sge.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/cxgb_multiq.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_mc5.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_vsc7323.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_vsc8211.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_ael1002.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_mv88e1xxx.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_xgmac.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_t3_hw.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/common/cxgb_tn1010.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/sys/uipc_mvec.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/sys/cxgb_support.c optional cxgb pci \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cxgb/cxgb_t3fw.c optional cxgb cxgb_t3fw \ compile-with "${NORMAL_C} -I$S/dev/cxgb" dev/cy/cy.c optional cy dev/cy/cy_isa.c optional cy isa dev/cy/cy_pci.c optional cy pci dev/dc/if_dc.c optional dc pci dev/dc/dcphy.c optional dc pci dev/dc/pnphy.c optional dc pci dev/dcons/dcons.c optional dcons dev/dcons/dcons_crom.c optional dcons_crom dev/dcons/dcons_os.c optional dcons dev/de/if_de.c optional de pci dev/digi/CX.c optional digi_CX dev/digi/CX_PCI.c optional digi_CX_PCI dev/digi/EPCX.c optional digi_EPCX dev/digi/EPCX_PCI.c optional digi_EPCX_PCI dev/digi/Xe.c optional digi_Xe dev/digi/Xem.c optional digi_Xem dev/digi/Xr.c optional digi_Xr dev/digi/digi.c optional digi dev/digi/digi_isa.c optional digi isa dev/digi/digi_pci.c optional digi pci dev/dpt/dpt_eisa.c optional dpt eisa dev/dpt/dpt_pci.c optional dpt pci dev/dpt/dpt_scsi.c optional dpt dev/drm/ati_pcigart.c optional drm dev/drm/drm_agpsupport.c optional drm dev/drm/drm_auth.c optional drm dev/drm/drm_bufs.c optional drm dev/drm/drm_context.c optional drm dev/drm/drm_dma.c optional drm dev/drm/drm_drawable.c optional drm dev/drm/drm_drv.c optional drm dev/drm/drm_fops.c optional drm dev/drm/drm_ioctl.c optional drm dev/drm/drm_irq.c optional drm dev/drm/drm_lock.c optional drm dev/drm/drm_memory.c optional drm dev/drm/drm_pci.c optional drm dev/drm/drm_scatter.c optional drm dev/drm/drm_sysctl.c optional drm dev/drm/drm_vm.c optional drm dev/drm/i915_dma.c optional i915drm dev/drm/i915_drv.c optional i915drm dev/drm/i915_irq.c optional i915drm dev/drm/i915_mem.c optional i915drm dev/drm/i915_suspend.c optional i915drm dev/drm/mach64_dma.c optional mach64drm dev/drm/mach64_drv.c optional mach64drm dev/drm/mach64_irq.c optional mach64drm dev/drm/mach64_state.c optional mach64drm dev/drm/mga_dma.c optional mgadrm dev/drm/mga_drv.c optional mgadrm dev/drm/mga_irq.c optional mgadrm dev/drm/mga_state.c optional mgadrm \ compile-with "${NORMAL_C} -finline-limit=13500" dev/drm/mga_warp.c optional mgadrm dev/drm/r128_cce.c optional r128drm dev/drm/r128_drv.c optional r128drm dev/drm/r128_irq.c optional r128drm dev/drm/r128_state.c optional r128drm \ compile-with "${NORMAL_C} -finline-limit=13500" dev/drm/r300_cmdbuf.c optional radeondrm dev/drm/radeon_cp.c optional radeondrm dev/drm/radeon_drv.c optional radeondrm dev/drm/radeon_irq.c optional radeondrm dev/drm/radeon_mem.c optional radeondrm dev/drm/radeon_state.c optional radeondrm dev/drm/savage_bci.c optional savagedrm dev/drm/savage_drv.c optional savagedrm dev/drm/savage_state.c optional savagedrm dev/drm/sis_drv.c optional sisdrm dev/drm/sis_ds.c optional sisdrm dev/drm/sis_mm.c optional sisdrm dev/drm/tdfx_drv.c optional tdfxdrm dev/ed/if_ed.c optional ed dev/ed/if_ed_novell.c optional ed dev/ed/if_ed_rtl80x9.c optional ed dev/ed/if_ed_pccard.c optional ed pccard dev/ed/if_ed_pci.c optional ed pci dev/eisa/eisa_if.m standard dev/eisa/eisaconf.c optional eisa dev/e1000/if_em.c optional em \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/if_igb.c optional igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_80003es2lan.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82540.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82541.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82542.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82543.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82571.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_82575.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/igb" dev/e1000/e1000_ich8lan.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_api.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_mac.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_manage.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_nvm.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_phy.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/e1000/e1000_osdep.c optional em | igb \ compile-with "${NORMAL_C} -I$S/dev/e1000" dev/et/if_et.c optional et dev/en/if_en_pci.c optional en pci dev/en/midway.c optional en dev/ep/if_ep.c optional ep dev/ep/if_ep_eisa.c optional ep eisa dev/ep/if_ep_isa.c optional ep isa dev/ep/if_ep_mca.c optional ep mca dev/ep/if_ep_pccard.c optional ep pccard dev/esp/ncr53c9x.c optional esp dev/ex/if_ex.c optional ex dev/ex/if_ex_isa.c optional ex isa dev/ex/if_ex_pccard.c optional ex pccard dev/exca/exca.c optional cbb dev/fatm/if_fatm.c optional fatm pci dev/fb/splash.c optional splash dev/fe/if_fe.c optional fe dev/fe/if_fe_pccard.c optional fe pccard dev/firewire/firewire.c optional firewire dev/firewire/fwcrom.c optional firewire dev/firewire/fwdev.c optional firewire dev/firewire/fwdma.c optional firewire dev/firewire/fwmem.c optional firewire dev/firewire/fwohci.c optional firewire dev/firewire/fwohci_pci.c optional firewire pci dev/firewire/if_fwe.c optional fwe dev/firewire/if_fwip.c optional fwip dev/firewire/sbp.c optional sbp dev/firewire/sbp_targ.c optional sbp_targ dev/flash/at45d.c optional at45d dev/fxp/if_fxp.c optional fxp dev/gem/if_gem.c optional gem dev/gem/if_gem_pci.c optional gem pci dev/hatm/if_hatm.c optional hatm pci dev/hatm/if_hatm_intr.c optional hatm pci dev/hatm/if_hatm_ioctl.c optional hatm pci dev/hatm/if_hatm_rx.c optional hatm pci dev/hatm/if_hatm_tx.c optional hatm pci dev/hifn/hifn7751.c optional hifn dev/hme/if_hme.c optional hme dev/hme/if_hme_pci.c optional hme pci dev/hme/if_hme_sbus.c optional hme sbus dev/hptiop/hptiop.c optional hptiop scbus dev/hwpmc/hwpmc_logging.c optional hwpmc dev/hwpmc/hwpmc_mod.c optional hwpmc dev/ichsmb/ichsmb.c optional ichsmb dev/ichsmb/ichsmb_pci.c optional ichsmb pci dev/ida/ida.c optional ida dev/ida/ida_disk.c optional ida dev/ida/ida_eisa.c optional ida eisa dev/ida/ida_pci.c optional ida pci dev/ie/if_ie.c optional ie isa nowerror dev/ie/if_ie_isa.c optional ie isa dev/ieee488/ibfoo.c optional pcii | tnt4882 dev/ieee488/pcii.c optional pcii dev/ieee488/tnt4882.c optional tnt4882 dev/ieee488/upd7210.c optional pcii | tnt4882 dev/iicbus/ad7418.c optional ad7418 dev/iicbus/ds133x.c optional ds133x dev/iicbus/ds1672.c optional ds1672 dev/iicbus/icee.c optional icee dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/iicbus/iicbb.c optional iicbb dev/iicbus/iicbb_if.m optional iicbb dev/iicbus/iicbus.c optional iicbus dev/iicbus/iicbus_if.m optional iicbus dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" dev/iir/iir.c optional iir dev/iir/iir_ctrl.c optional iir dev/iir/iir_pci.c optional iir pci dev/ips/ips.c optional ips dev/ips/ips_commands.c optional ips dev/ips/ips_disk.c optional ips dev/ips/ips_ioctl.c optional ips dev/ips/ips_pci.c optional ips pci dev/ipw/if_ipw.c optional ipw ipwbssfw.c optional ipwbssfw | ipwfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk ipw_bss.fw:ipw_bss:130 -lintel_ipw -mipw_bss -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "ipwbssfw.c" ipw_bss.fwo optional ipwbssfw | ipwfw \ dependency "ipw_bss.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} ipw_bss.fw" \ no-implicit-rule \ clean "ipw_bss.fwo" ipw_bss.fw optional ipwbssfw | ipwfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ipw/ipw2100-1.3.fw.uu" \ no-obj no-implicit-rule \ clean "ipw_bss.fw" ipwibssfw.c optional ipwibssfw | ipwfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk ipw_ibss.fw:ipw_ibss:130 -lintel_ipw -mipw_ibss -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "ipwibssfw.c" ipw_ibss.fwo optional ipwibssfw | ipwfw \ dependency "ipw_ibss.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} ipw_ibss.fw" \ no-implicit-rule \ clean "ipw_ibss.fwo" ipw_ibss.fw optional ipwibssfw | ipwfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ipw/ipw2100-1.3-i.fw.uu" \ no-obj no-implicit-rule \ clean "ipw_ibss.fw" ipwmonitorfw.c optional ipwmonitorfw | ipwfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk ipw_monitor.fw:ipw_monitor:130 -lintel_ipw -mipw_monitor -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "ipwmonitorfw.c" ipw_monitor.fwo optional ipwmonitorfw | ipwfw \ dependency "ipw_monitor.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} ipw_monitor.fw" \ no-implicit-rule \ clean "ipw_monitor.fwo" ipw_monitor.fw optional ipwmonitorfw | ipwfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ipw/ipw2100-1.3-p.fw.uu" \ no-obj no-implicit-rule \ clean "ipw_monitor.fw" dev/iscsi/initiator/iscsi.c optional iscsi_initiator scbus dev/iscsi/initiator/iscsi_subr.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_cam.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_soc.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_sm.c optional iscsi_initiator scbus dev/iscsi/initiator/isc_subr.c optional iscsi_initiator scbus dev/isp/isp.c optional isp dev/isp/isp_freebsd.c optional isp dev/isp/isp_library.c optional isp dev/isp/isp_pci.c optional isp pci dev/isp/isp_sbus.c optional isp sbus dev/isp/isp_target.c optional isp dev/ispfw/ispfw.c optional ispfw dev/iwi/if_iwi.c optional iwi iwibssfw.c optional iwibssfw | iwifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwi_bss.fw:iwi_bss:300 -lintel_iwi -miwi_bss -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "iwibssfw.c" iwi_bss.fwo optional iwibssfw | iwifw \ dependency "iwi_bss.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} iwi_bss.fw" \ no-implicit-rule \ clean "iwi_bss.fwo" iwi_bss.fw optional iwibssfw | iwifw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/iwi/ipw2200-bss.fw.uu" \ no-obj no-implicit-rule \ clean "iwi_bss.fw" iwiibssfw.c optional iwiibssfw | iwifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwi_ibss.fw:iwi_ibss:300 -lintel_iwi -miwi_ibss -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "iwiibssfw.c" iwi_ibss.fwo optional iwiibssfw | iwifw \ dependency "iwi_ibss.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} iwi_ibss.fw" \ no-implicit-rule \ clean "iwi_ibss.fwo" iwi_ibss.fw optional iwiibssfw | iwifw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/iwi/ipw2200-ibss.fw.uu" \ no-obj no-implicit-rule \ clean "iwi_ibss.fw" iwimonitorfw.c optional iwimonitorfw | iwifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwi_monitor.fw:iwi_monitor:300 -lintel_iwi -miwi_monitor -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "iwimonitorfw.c" iwi_monitor.fwo optional iwimonitorfw | iwifw \ dependency "iwi_monitor.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} iwi_monitor.fw" \ no-implicit-rule \ clean "iwi_monitor.fwo" iwi_monitor.fw optional iwimonitorfw | iwifw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/iwi/ipw2200-sniffer.fw.uu" \ no-obj no-implicit-rule \ clean "iwi_monitor.fw" dev/iwn/if_iwn.c optional iwn iwnfw.c optional iwnfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk iwn.fw:iwnfw:44417 -lintel_iwn -miwn -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "iwnfw.c" iwnfw.fwo optional iwnfw \ dependency "iwn.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} iwn.fw" \ no-implicit-rule \ clean "iwn.fwo" iwn.fw optional iwnfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/iwn/iwlwifi-4965-4.44.17.fw.uu" \ no-obj no-implicit-rule \ clean "iwn.fw" dev/ixgb/if_ixgb.c optional ixgb dev/ixgb/ixgb_ee.c optional ixgb dev/ixgb/ixgb_hw.c optional ixgb dev/ixgbe/ixgbe.c optional ixgbe \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_phy.c optional ixgbe \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_api.c optional ixgbe \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_common.c optional ixgbe \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/ixgbe/ixgbe_82598.c optional ixgbe \ compile-with "${NORMAL_C} -I$S/dev/ixgbe" dev/jme/if_jme.c optional jme pci dev/joy/joy.c optional joy dev/joy/joy_isa.c optional joy isa dev/joy/joy_pccard.c optional joy pccard dev/kbdmux/kbdmux.c optional kbdmux dev/le/am7990.c optional le dev/le/am79900.c optional le dev/le/if_le_pci.c optional le pci dev/le/lance.c optional le dev/led/led.c standard dev/lge/if_lge.c optional lge dev/lmc/if_lmc.c optional lmc dev/malo/if_malo.c optional malo dev/malo/if_malohal.c optional malo dev/malo/if_malo_pci.c optional malo pci dev/mc146818/mc146818.c optional mc146818 dev/mca/mca_bus.c optional mca dev/mcd/mcd.c optional mcd isa nowerror dev/mcd/mcd_isa.c optional mcd isa nowerror dev/md/md.c optional md dev/mem/memdev.c optional mem dev/mfi/mfi.c optional mfi dev/mfi/mfi_debug.c optional mfi dev/mfi/mfi_pci.c optional mfi pci dev/mfi/mfi_disk.c optional mfi dev/mfi/mfi_linux.c optional mfi compat_linux dev/mfi/mfi_cam.c optional mfip scbus dev/mii/acphy.c optional miibus | acphy dev/mii/amphy.c optional miibus | amphy dev/mii/atphy.c optional miibus | atphy dev/mii/bmtphy.c optional miibus | bmtphy dev/mii/brgphy.c optional miibus | brgphy dev/mii/ciphy.c optional miibus | ciphy dev/mii/e1000phy.c optional miibus | e1000phy # XXX only xl cards? dev/mii/exphy.c optional miibus | exphy dev/mii/gentbi.c optional miibus | gentbi dev/mii/icsphy.c optional miibus | icsphy # XXX only fxp cards? dev/mii/inphy.c optional miibus | inphy dev/mii/ip1000phy.c optional miibus | ip1000phy dev/mii/jmphy.c optional miibus | jmphy dev/mii/lxtphy.c optional miibus | lxtphy dev/mii/mii.c optional miibus | mii dev/mii/mii_physubr.c optional miibus | mii dev/mii/miibus_if.m optional miibus | mii dev/mii/mlphy.c optional miibus | mlphy dev/mii/nsgphy.c optional miibus | nsgphy dev/mii/nsphy.c optional miibus | nsphy dev/mii/nsphyter.c optional miibus | nsphyter dev/mii/pnaphy.c optional miibus | pnaphy dev/mii/qsphy.c optional miibus | qsphy dev/mii/rgephy.c optional miibus | rgephy dev/mii/rlphy.c optional miibus | rlphy dev/mii/rlswitch.c optional rlswitch # XXX rue only? dev/mii/ruephy.c optional miibus | ruephy dev/mii/smcphy.c optional miibus | smcphy dev/mii/tdkphy.c optional miibus | tdkphy dev/mii/tlphy.c optional miibus | tlphy dev/mii/truephy.c optional miibus | truephy dev/mii/ukphy.c optional miibus | mii dev/mii/ukphy_subr.c optional miibus | mii dev/mii/xmphy.c optional miibus | xmphy dev/mk48txx/mk48txx.c optional mk48txx dev/mlx/mlx.c optional mlx dev/mlx/mlx_disk.c optional mlx dev/mlx/mlx_pci.c optional mlx pci dev/mly/mly.c optional mly dev/mmc/mmc.c optional mmc dev/mmc/mmcbr_if.m standard dev/mmc/mmcbus_if.m standard dev/mmc/mmcsd.c optional mmcsd dev/mn/if_mn.c optional mn pci dev/mpt/mpt.c optional mpt dev/mpt/mpt_cam.c optional mpt dev/mpt/mpt_debug.c optional mpt dev/mpt/mpt_pci.c optional mpt pci dev/mpt/mpt_raid.c optional mpt dev/mpt/mpt_user.c optional mpt dev/msk/if_msk.c optional msk dev/mxge/if_mxge.c optional mxge pci dev/mxge/mxge_lro.c optional mxge pci dev/mxge/mxge_eth_z8e.c optional mxge pci dev/mxge/mxge_ethp_z8e.c optional mxge pci dev/mxge/mxge_rss_eth_z8e.c optional mxge pci dev/mxge/mxge_rss_ethp_z8e.c optional mxge pci dev/my/if_my.c optional my dev/ncv/ncr53c500.c optional ncv dev/ncv/ncr53c500_pccard.c optional ncv pccard dev/nge/if_nge.c optional nge dev/nxge/if_nxge.c optional nxge dev/nxge/xgehal/xgehal-device.c optional nxge dev/nxge/xgehal/xgehal-mm.c optional nxge dev/nxge/xgehal/xge-queue.c optional nxge dev/nxge/xgehal/xgehal-driver.c optional nxge dev/nxge/xgehal/xgehal-ring.c optional nxge dev/nxge/xgehal/xgehal-channel.c optional nxge dev/nxge/xgehal/xgehal-fifo.c optional nxge dev/nxge/xgehal/xgehal-stats.c optional nxge dev/nxge/xgehal/xgehal-config.c optional nxge dev/nxge/xgehal/xgehal-mgmt.c optional nxge dev/nmdm/nmdm.c optional nmdm dev/nsp/nsp.c optional nsp dev/nsp/nsp_pccard.c optional nsp pccard dev/null/null.c standard dev/patm/if_patm.c optional patm pci dev/patm/if_patm_attach.c optional patm pci dev/patm/if_patm_intr.c optional patm pci dev/patm/if_patm_ioctl.c optional patm pci dev/patm/if_patm_rtables.c optional patm pci dev/patm/if_patm_rx.c optional patm pci dev/patm/if_patm_tx.c optional patm pci dev/pbio/pbio.c optional pbio isa dev/pccard/card_if.m standard dev/pccard/pccard.c optional pccard dev/pccard/pccard_cis.c optional pccard dev/pccard/pccard_cis_quirks.c optional pccard dev/pccard/pccard_device.c optional pccard dev/pccard/power_if.m standard dev/pccbb/pccbb.c optional cbb dev/pccbb/pccbb_isa.c optional cbb isa dev/pccbb/pccbb_pci.c optional cbb pci dev/pcf/pcf.c optional pcf dev/pci/eisa_pci.c optional pci eisa dev/pci/fixup_pci.c optional pci dev/pci/hostb_pci.c optional pci dev/pci/ignore_pci.c optional pci dev/pci/isa_pci.c optional pci isa dev/pci/pci.c optional pci dev/pci/pci_if.m standard dev/pci/pci_pci.c optional pci dev/pci/pci_user.c optional pci dev/pci/pcib_if.m standard dev/pci/vga_pci.c optional pci dev/pcn/if_pcn.c optional pcn pci dev/pdq/if_fea.c optional fea eisa dev/pdq/if_fpa.c optional fpa pci dev/pdq/pdq.c optional nowerror fea eisa | fpa pci dev/pdq/pdq_ifsubr.c optional nowerror fea eisa | fpa pci dev/ppbus/if_plip.c optional plip dev/ppbus/immio.c optional vpo dev/ppbus/lpbb.c optional lpbb dev/ppbus/lpt.c optional lpt dev/ppbus/pcfclock.c optional pcfclock dev/ppbus/ppb_1284.c optional ppbus dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppbus_if.m optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppbus/vpo.c optional vpo dev/ppbus/vpoio.c optional vpo dev/ppc/ppc.c optional ppc dev/ppc/ppc_acpi.c optional ppc acpi dev/ppc/ppc_isa.c optional ppc isa dev/ppc/ppc_pci.c optional ppc pci dev/ppc/ppc_puc.c optional ppc puc dev/pst/pst-iop.c optional pst dev/pst/pst-pci.c optional pst pci dev/pst/pst-raid.c optional pst dev/puc/puc.c optional puc dev/puc/puc_cfg.c optional puc dev/puc/puc_pccard.c optional puc pccard dev/puc/puc_pci.c optional puc pci dev/puc/pucdata.c optional puc pci dev/quicc/quicc_core.c optional quicc dev/ral/rt2560.c optional ral dev/ral/rt2661.c optional ral dev/ral/if_ral_pci.c optional ral pci rt2561fw.c optional rt2561fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2561.fw:rt2561fw -mrt2561 -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "rt2561fw.c" rt2561fw.fwo optional rt2561fw | ralfw \ dependency "rt2561.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} rt2561.fw" \ no-implicit-rule \ clean "rt2561.fwo" rt2561.fw optional rt2561fw | ralfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ral/rt2561.fw.uu" \ no-obj no-implicit-rule \ clean "rt2561.fw" rt2561sfw.c optional rt2561sfw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2561s.fw:rt2561sfw -mrt2561s -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "rt2561sfw.c" rt2561sfw.fwo optional rt2561sfw | ralfw \ dependency "rt2561s.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} rt2561s.fw" \ no-implicit-rule \ clean "rt2561s.fwo" rt2561s.fw optional rt2561sfw | ralfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ral/rt2561s.fw.uu" \ no-obj no-implicit-rule \ clean "rt2561s.fw" rt2661fw.c optional rt2661fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2661.fw:rt2661fw -mrt2661 -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "rt2661fw.c" rt2661fw.fwo optional rt2661fw | ralfw \ dependency "rt2661.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} rt2661.fw" \ no-implicit-rule \ clean "rt2661.fwo" rt2661.fw optional rt2661fw | ralfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ral/rt2661.fw.uu" \ no-obj no-implicit-rule \ clean "rt2661.fw" rt2860fw.c optional rt2860fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2860.fw:rt2860fw -mrt2860 -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "rt2860fw.c" rt2860fw.fwo optional rt2860fw | ralfw \ dependency "rt2860.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} rt2860.fw" \ no-implicit-rule \ clean "rt2860.fwo" rt2860.fw optional rt2860fw | ralfw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/ral/rt2860.fw.uu" \ no-obj no-implicit-rule \ clean "rt2860.fw" dev/random/harvest.c standard dev/random/hash.c optional random dev/random/probe.c optional random dev/random/randomdev.c optional random dev/random/randomdev_soft.c optional random dev/random/yarrow.c optional random dev/ray/if_ray.c optional ray pccard dev/rc/rc.c optional rc dev/re/if_re.c optional re dev/rndtest/rndtest.c optional rndtest dev/rp/rp.c optional rp dev/rp/rp_isa.c optional rp isa dev/rp/rp_pci.c optional rp pci dev/safe/safe.c optional safe dev/scc/scc_if.m optional scc dev/scc/scc_bfe_ebus.c optional scc ebus dev/scc/scc_bfe_quicc.c optional scc quicc dev/scc/scc_bfe_sbus.c optional scc fhc | scc sbus dev/scc/scc_core.c optional scc dev/scc/scc_dev_quicc.c optional scc quicc dev/scc/scc_dev_sab82532.c optional scc dev/scc/scc_dev_z8530.c optional scc dev/scd/scd.c optional scd isa dev/scd/scd_isa.c optional scd isa dev/sdhci/sdhci.c optional sdhci pci dev/sf/if_sf.c optional sf pci dev/si/si.c optional si dev/si/si2_z280.c optional si dev/si/si3_t225.c optional si dev/si/si_eisa.c optional si eisa dev/si/si_isa.c optional si isa dev/si/si_pci.c optional si pci dev/sis/if_sis.c optional sis pci dev/sk/if_sk.c optional sk pci dev/smbus/smb.c optional smb dev/smbus/smbconf.c optional smbus dev/smbus/smbus.c optional smbus dev/smbus/smbus_if.m optional smbus dev/smc/if_smc.c optional smc dev/sn/if_sn.c optional sn dev/sn/if_sn_isa.c optional sn isa dev/sn/if_sn_pccard.c optional sn pccard dev/snp/snp.c optional snp dev/sound/clone.c optional sound dev/sound/unit.c optional sound dev/sound/isa/ad1816.c optional snd_ad1816 isa dev/sound/isa/ess.c optional snd_ess isa dev/sound/isa/gusc.c optional snd_gusc isa dev/sound/isa/mss.c optional snd_mss isa dev/sound/isa/sb16.c optional snd_sb16 isa dev/sound/isa/sb8.c optional snd_sb8 isa dev/sound/isa/sbc.c optional snd_sbc isa dev/sound/isa/sndbuf_dma.c optional sound isa dev/sound/pci/als4000.c optional snd_als4000 pci dev/sound/pci/atiixp.c optional snd_atiixp pci #dev/sound/pci/au88x0.c optional snd_au88x0 pci dev/sound/pci/cmi.c optional snd_cmi pci dev/sound/pci/cs4281.c optional snd_cs4281 pci dev/sound/pci/csa.c optional snd_csa pci \ warning "kernel contains GPL contaminated csaimg.h header" dev/sound/pci/csapcm.c optional snd_csa pci dev/sound/pci/ds1.c optional snd_ds1 pci dev/sound/pci/emu10k1.c optional snd_emu10k1 pci \ dependency "emu10k1-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10k1 headers" dev/sound/pci/emu10kx.c optional snd_emu10kx pci \ dependency "emu10k1-alsa%diked.h" \ dependency "p16v-alsa%diked.h" \ dependency "p17v-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/emu10kx-pcm.c optional snd_emu10kx pci \ dependency "emu10k1-alsa%diked.h" \ dependency "p16v-alsa%diked.h" \ dependency "p17v-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/emu10kx-midi.c optional snd_emu10kx pci \ dependency "emu10k1-alsa%diked.h" \ warning "kernel contains GPL contaminated emu10kx headers" dev/sound/pci/envy24.c optional snd_envy24 pci dev/sound/pci/envy24ht.c optional snd_envy24ht pci dev/sound/pci/es137x.c optional snd_es137x pci dev/sound/pci/fm801.c optional snd_fm801 pci dev/sound/pci/ich.c optional snd_ich pci dev/sound/pci/maestro.c optional snd_maestro pci dev/sound/pci/maestro3.c optional snd_maestro3 pci \ warning "kernel contains GPL contaminated maestro3 headers" dev/sound/pci/neomagic.c optional snd_neomagic pci dev/sound/pci/solo.c optional snd_solo pci dev/sound/pci/spicds.c optional snd_spicds pci dev/sound/pci/t4dwave.c optional snd_t4dwave pci dev/sound/pci/via8233.c optional snd_via8233 pci dev/sound/pci/via82c686.c optional snd_via82c686 pci dev/sound/pci/vibes.c optional snd_vibes pci dev/sound/pci/hda/hdac.c optional snd_hda pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_if.m optional sound dev/sound/pcm/ac97_patch.c optional sound dev/sound/pcm/buffer.c optional sound dev/sound/pcm/channel.c optional sound dev/sound/pcm/channel_if.m optional sound dev/sound/pcm/dsp.c optional sound dev/sound/pcm/fake.c optional sound dev/sound/pcm/feeder.c optional sound dev/sound/pcm/feeder_fmt.c optional sound dev/sound/pcm/feeder_if.m optional sound dev/sound/pcm/feeder_rate.c optional sound dev/sound/pcm/feeder_volume.c optional sound dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound dev/sound/pcm/sound.c optional sound dev/sound/pcm/vchan.c optional sound #dev/sound/usb/upcm.c optional snd_upcm usb dev/sound/usb/uaudio.c optional snd_uaudio usb dev/sound/usb/uaudio_pcm.c optional snd_uaudio usb dev/sound/midi/midi.c optional sound dev/sound/midi/mpu401.c optional sound dev/sound/midi/mpu_if.m optional sound dev/sound/midi/mpufoi_if.m optional sound dev/sound/midi/sequencer.c optional sound dev/sound/midi/synth_if.m optional sound dev/spibus/spibus.c optional spibus \ dependency "spibus_if.h" dev/spibus/spibus_if.m optional spibus dev/sr/if_sr.c optional sr dev/sr/if_sr_pci.c optional sr pci dev/ste/if_ste.c optional ste pci dev/stg/tmc18c30.c optional stg dev/stg/tmc18c30_isa.c optional stg isa dev/stg/tmc18c30_pccard.c optional stg pccard dev/stg/tmc18c30_pci.c optional stg pci dev/stg/tmc18c30_subr.c optional stg dev/stge/if_stge.c optional stge dev/streams/streams.c optional streams dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" dev/syscons/blank/blank_saver.c optional blank_saver dev/syscons/daemon/daemon_saver.c optional daemon_saver dev/syscons/dragon/dragon_saver.c optional dragon_saver dev/syscons/fade/fade_saver.c optional fade_saver dev/syscons/fire/fire_saver.c optional fire_saver dev/syscons/green/green_saver.c optional green_saver dev/syscons/logo/logo.c optional logo_saver dev/syscons/logo/logo_saver.c optional logo_saver dev/syscons/rain/rain_saver.c optional rain_saver dev/syscons/schistory.c optional sc dev/syscons/scmouse.c optional sc dev/syscons/scterm-dumb.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scvidctl.c optional sc dev/syscons/snake/snake_saver.c optional snake_saver dev/syscons/star/star_saver.c optional star_saver dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc dev/syscons/warp/warp_saver.c optional warp_saver dev/tdfx/tdfx_linux.c optional tdfx_linux tdfx compat_linux dev/tdfx/tdfx_pci.c optional tdfx pci dev/ti/if_ti.c optional ti pci dev/tl/if_tl.c optional tl pci dev/trm/trm.c optional trm dev/twa/tw_cl_init.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_cl_intr.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_cl_io.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_cl_misc.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_osl_cam.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twa/tw_osl_freebsd.c optional twa \ compile-with "${NORMAL_C} -I$S/dev/twa" dev/twe/twe.c optional twe dev/twe/twe_freebsd.c optional twe dev/tx/if_tx.c optional tx dev/txp/if_txp.c optional txp dev/uart/uart_bus_acpi.c optional uart acpi #dev/uart/uart_bus_cbus.c optional uart cbus dev/uart/uart_bus_ebus.c optional uart ebus dev/uart/uart_bus_isa.c optional uart isa dev/uart/uart_bus_pccard.c optional uart pccard dev/uart/uart_bus_pci.c optional uart pci dev/uart/uart_bus_puc.c optional uart puc dev/uart/uart_bus_scc.c optional uart scc dev/uart/uart_core.c optional uart dev/uart/uart_dbg.c optional uart gdb dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 dev/uart/uart_dev_quicc.c optional uart quicc dev/uart/uart_dev_sab82532.c optional uart uart_sab82532 dev/uart/uart_dev_sab82532.c optional uart scc dev/uart/uart_dev_z8530.c optional uart uart_z8530 dev/uart/uart_dev_z8530.c optional uart scc dev/uart/uart_if.m optional uart dev/uart/uart_subr.c optional uart dev/uart/uart_tty.c optional uart dev/ubsec/ubsec.c optional ubsec # # USB support dev/usb/ehci.c optional ehci dev/usb/ehci_ddb.c optional ehci dev/usb/ehci_pci.c optional ehci pci dev/usb/hid.c optional usb dev/usb/if_aue.c optional aue dev/usb/if_axe.c optional axe dev/usb/if_cdce.c optional cdce dev/usb/if_cue.c optional cue dev/usb/if_kue.c optional kue dev/usb/if_ural.c optional ural dev/usb/if_rue.c optional rue dev/usb/if_rum.c optional rum dev/usb/if_udav.c optional udav dev/usb/if_zyd.c optional zyd dev/usb/ohci.c optional ohci dev/usb/ohci_pci.c optional ohci pci dev/usb/sl811hs.c optional slhci dev/usb/slhci_pccard.c optional slhci pccard dev/usb/uark.c optional uark dev/usb/u3g.c optional u3g dev/usb/ubsa.c optional ubsa dev/usb/ubser.c optional ubser dev/usb/ucom.c optional ucom dev/usb/ucycom.c optional ucycom dev/usb/udbp.c optional udbp dev/usb/ufoma.c optional ufoma dev/usb/ufm.c optional ufm dev/usb/uftdi.c optional uftdi dev/usb/ugen.c optional ugen dev/usb/uhci.c optional uhci dev/usb/uhci_pci.c optional uhci pci dev/usb/uhid.c optional uhid dev/usb/uhub.c optional usb dev/usb/uipaq.c optional uipaq dev/usb/ukbd.c optional ukbd dev/usb/ulpt.c optional ulpt dev/usb/umass.c optional umass dev/usb/umct.c optional umct dev/usb/umodem.c optional umodem dev/usb/ums.c optional ums dev/usb/uplcom.c optional uplcom dev/usb/urio.c optional urio dev/usb/usb.c optional usb dev/usb/usb_ethersubr.c optional usb dev/usb/usb_if.m optional usb dev/usb/usb_mem.c optional usb dev/usb/usb_quirks.c optional usb dev/usb/usb_subr.c optional usb dev/usb/usbdi.c optional usb dev/usb/usbdi_util.c optional usb dev/usb/uscanner.c optional uscanner dev/usb/uslcom.c optional uslcom dev/usb/uvisor.c optional uvisor dev/usb/uvscom.c optional uvscom # # USB2 controller drivers # dev/usb2/controller/at91dci.c optional usb2_core usb2_controller usb2_controller_at91dci dev/usb2/controller/at91dci_atmelarm.c optional usb2_core usb2_controller usb2_controller_at91dci at91rm9200 dev/usb2/controller/musb2_otg.c optional usb2_core usb2_controller usb2_controller_musb dev/usb2/controller/musb2_otg_atmelarm.c optional usb2_core usb2_controller usb2_controller_musb at91rm9200 dev/usb2/controller/ehci2.c optional usb2_core usb2_controller usb2_controller_ehci dev/usb2/controller/ehci2_pci.c optional usb2_core usb2_controller usb2_controller_ehci pci dev/usb2/controller/ohci2.c optional usb2_core usb2_controller usb2_controller_ohci dev/usb2/controller/ohci2_atmelarm.c optional usb2_core usb2_controller usb2_controller_ohci at91rm9200 dev/usb2/controller/ohci2_pci.c optional usb2_core usb2_controller usb2_controller_ohci pci dev/usb2/controller/uhci2.c optional usb2_core usb2_controller usb2_controller_uhci dev/usb2/controller/uhci2_pci.c optional usb2_core usb2_controller usb2_controller_uhci pci dev/usb2/controller/uss820dci.c optional usb2_core usb2_controller usb2_controller_uss820dci dev/usb2/controller/uss820dci_atmelarm.c optional usb2_core usb2_controller usb2_controller_uss820dci at91rm9200 dev/usb2/controller/usb2_controller.c optional usb2_core usb2_controller # # USB2 storage drivers # dev/usb2/storage/ata-usb2.c optional usb2_core usb2_storage usb2_storage_ata dev/usb2/storage/umass2.c optional usb2_core usb2_storage usb2_storage_mass dev/usb2/storage/urio2.c optional usb2_core usb2_storage usb2_storage_rio dev/usb2/storage/usb2_storage.c optional usb2_core usb2_storage dev/usb2/storage/ustorage2_fs.c optional usb2_core usb2_storage usb2_storage_fs # # USB2 NDIS driver # dev/usb2/ndis/if_ndis_usb2.c optional usb2_core usb2_ndis dev/usb2/ndis/usb2_ndis.c optional usb2_core usb2_ndis # # USB2 core # dev/usb2/core/usb2_busdma.c optional usb2_core dev/usb2/core/usb2_compat_linux.c optional usb2_core dev/usb2/core/usb2_config_td.c optional usb2_core dev/usb2/core/usb2_core.c optional usb2_core dev/usb2/core/usb2_debug.c optional usb2_core dev/usb2/core/usb2_dev.c optional usb2_core dev/usb2/core/usb2_device.c optional usb2_core dev/usb2/core/usb2_dynamic.c optional usb2_core dev/usb2/core/usb2_error.c optional usb2_core dev/usb2/core/usb2_generic.c optional usb2_core dev/usb2/core/usb2_handle_request.c optional usb2_core dev/usb2/core/usb2_hid.c optional usb2_core dev/usb2/core/usb2_hub.c optional usb2_core dev/usb2/core/usb2_if.m optional usb2_core dev/usb2/core/usb2_lookup.c optional usb2_core dev/usb2/core/usb2_mbuf.c optional usb2_core dev/usb2/core/usb2_msctest.c optional usb2_core dev/usb2/core/usb2_parse.c optional usb2_core dev/usb2/core/usb2_process.c optional usb2_core dev/usb2/core/usb2_request.c optional usb2_core dev/usb2/core/usb2_sw_transfer.c optional usb2_core dev/usb2/core/usb2_transfer.c optional usb2_core dev/usb2/core/usb2_util.c optional usb2_core # # USB2 ethernet drivers # dev/usb2/ethernet/if_aue2.c optional usb2_core usb2_ethernet usb2_ethernet_aue dev/usb2/ethernet/if_axe2.c optional usb2_core usb2_ethernet usb2_ethernet_axe dev/usb2/ethernet/if_cdce2.c optional usb2_core usb2_ethernet usb2_ethernet_cdce dev/usb2/ethernet/if_cue2.c optional usb2_core usb2_ethernet usb2_ethernet_cue dev/usb2/ethernet/if_kue2.c optional usb2_core usb2_ethernet usb2_ethernet_kue dev/usb2/ethernet/if_rue2.c optional usb2_core usb2_ethernet usb2_ethernet_rue -dev/usb2/ethernet/if_udav2.c optional usb2_core usb2_ethernet usb2_ethernet_udav +dev/usb2/ethernet/if_udav2.c optional usb2_core usb2_ethernet usb2_ethernet_dav dev/usb2/ethernet/usb2_ethernet.c optional usb2_core usb2_ethernet # # USB2 WLAN drivers # dev/usb2/wlan/if_rum2.c optional usb2_core usb2_wlan usb2_wlan_rum dev/usb2/wlan/if_ural2.c optional usb2_core usb2_wlan usb2_wlan_ral dev/usb2/wlan/if_zyd2.c optional usb2_core usb2_wlan usb2_wlan_zyd dev/usb2/wlan/usb2_wlan.c optional usb2_core usb2_wlan # # USB2 serial and parallel port drivers # dev/usb2/serial/uark2.c optional usb2_core usb2_serial usb2_serial_ark dev/usb2/serial/ubsa2.c optional usb2_core usb2_serial usb2_serial_bsa dev/usb2/serial/ubser2.c optional usb2_core usb2_serial usb2_serial_bser dev/usb2/serial/uchcom2.c optional usb2_core usb2_serial usb2_serial_chcom dev/usb2/serial/ucycom2.c optional usb2_core usb2_serial usb2_serial_cycom dev/usb2/serial/ufoma2.c optional usb2_core usb2_serial usb2_serial_foma dev/usb2/serial/uftdi2.c optional usb2_core usb2_serial usb2_serial_ftdi dev/usb2/serial/ugensa2.c optional usb2_core usb2_serial usb2_serial_gensa dev/usb2/serial/uipaq2.c optional usb2_core usb2_serial usb2_serial_ipaq dev/usb2/serial/ulpt2.c optional usb2_core usb2_serial usb2_serial_lpt dev/usb2/serial/umct2.c optional usb2_core usb2_serial usb2_serial_mct dev/usb2/serial/umodem2.c optional usb2_core usb2_serial usb2_serial_modem dev/usb2/serial/umoscom2.c optional usb2_core usb2_serial usb2_serial_moscom dev/usb2/serial/uplcom2.c optional usb2_core usb2_serial usb2_serial_plcom dev/usb2/serial/usb2_serial.c optional usb2_core usb2_serial dev/usb2/serial/uvisor2.c optional usb2_core usb2_serial usb2_serial_visor dev/usb2/serial/uvscom2.c optional usb2_core usb2_serial usb2_serial_vscom +dev/usb2/serial/u3g2.c optional usb2_core usb2_serial usb2_serial_3g # # USB2 bluetooth drivers # dev/usb2/bluetooth/usb2_bluetooth.c optional usb2_core usb2_bluetooth dev/usb2/bluetooth/ng_ubt2.c optional usb2_core usb2_bluetooth usb2_bluetooth_ng dev/usb2/bluetooth/ubtbcmfw2.c optional usb2_core usb2_bluetooth usb2_bluetooth_fw # # USB2 misc drivers # dev/usb2/misc/usb2_misc.c optional usb2_core usb2_misc dev/usb2/misc/ufm2.c optional usb2_core usb2_misc usb2_misc_fm dev/usb2/misc/udbp2.c optional usb2_core usb2_misc usb2_misc_dbp # # USB2 input drivers # dev/usb2/input/uhid2.c optional usb2_core usb2_input usb2_input_hid dev/usb2/input/ukbd2.c optional usb2_core usb2_input usb2_input_kbd dev/usb2/input/ums2.c optional usb2_core usb2_input usb2_input_ms dev/usb2/input/usb2_input.c optional usb2_core usb2_input # # USB2 quirks # dev/usb2/quirk/usb2_quirk.c optional usb2_core usb2_quirk # # USB2 templates # dev/usb2/template/usb2_template.c optional usb2_core usb2_template dev/usb2/template/usb2_template_cdce.c optional usb2_core usb2_template dev/usb2/template/usb2_template_msc.c optional usb2_core usb2_template dev/usb2/template/usb2_template_mtp.c optional usb2_core usb2_template # # USB2 image drivers # dev/usb2/image/usb2_image.c optional usb2_core usb2_image dev/usb2/image/uscanner2.c optional usb2_core usb2_image usb2_scanner # # USB2 sound and MIDI drivers # dev/usb2/sound/usb2_sound.c optional usb2_core usb2_sound dev/usb2/sound/uaudio2.c optional usb2_core usb2_sound dev/usb2/sound/uaudio2_pcm.c optional usb2_core usb2_sound # # USB2 END # dev/utopia/idtphy.c optional utopia dev/utopia/suni.c optional utopia dev/utopia/utopia.c optional utopia dev/vge/if_vge.c optional vge dev/vkbd/vkbd.c optional vkbd dev/vr/if_vr.c optional vr pci dev/vx/if_vx.c optional vx dev/vx/if_vx_eisa.c optional vx eisa dev/vx/if_vx_pci.c optional vx pci dev/watchdog/watchdog.c standard dev/wb/if_wb.c optional wb pci dev/wds/wd7000.c optional wds isa dev/wi/if_wi.c optional wi dev/wi/if_wi_pccard.c optional wi pccard dev/wi/if_wi_pci.c optional wi pci dev/wl/if_wl.c optional wl isa wpifw.c optional wpifw \ compile-with "${AWK} -f $S/tools/fw_stub.awk wpi.fw:wpifw:2144 -lintel_wpi -mwpi -c${.TARGET}" \ no-implicit-rule before-depend local \ clean "wpifw.c" wpifw.fwo optional wpifw \ dependency "wpi.fw" \ compile-with "${LD} -b binary -d -warn-common -r -d -o ${.TARGET} wpi.fw" \ no-implicit-rule \ clean "wpi.fwo" wpi.fw optional wpifw \ dependency ".PHONY" \ compile-with "uudecode -o ${.TARGET} $S/contrib/dev/wpi/iwlwifi-3945-2.14.4.fw.uu" \ no-obj no-implicit-rule \ clean "wpi.fw" dev/xe/if_xe.c optional xe dev/xe/if_xe_pccard.c optional xe pccard dev/xl/if_xl.c optional xl pci fs/coda/coda_fbsd.c optional vcoda fs/coda/coda_psdev.c optional vcoda fs/coda/coda_subr.c optional vcoda fs/coda/coda_venus.c optional vcoda fs/coda/coda_vfsops.c optional vcoda fs/coda/coda_vnops.c optional vcoda fs/deadfs/dead_vnops.c standard fs/devfs/devfs_devs.c standard fs/devfs/devfs_rule.c standard fs/devfs/devfs_vfsops.c standard fs/devfs/devfs_vnops.c standard fs/fdescfs/fdesc_vfsops.c optional fdescfs fs/fdescfs/fdesc_vnops.c optional fdescfs fs/fifofs/fifo_vnops.c standard fs/hpfs/hpfs_alsubr.c optional hpfs fs/hpfs/hpfs_lookup.c optional hpfs fs/hpfs/hpfs_subr.c optional hpfs fs/hpfs/hpfs_vfsops.c optional hpfs fs/hpfs/hpfs_vnops.c optional hpfs fs/msdosfs/msdosfs_conv.c optional msdosfs fs/msdosfs/msdosfs_denode.c optional msdosfs fs/msdosfs/msdosfs_fat.c optional msdosfs fs/msdosfs/msdosfs_fileno.c optional msdosfs fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv fs/msdosfs/msdosfs_lookup.c optional msdosfs fs/msdosfs/msdosfs_vfsops.c optional msdosfs fs/msdosfs/msdosfs_vnops.c optional msdosfs fs/ntfs/ntfs_compr.c optional ntfs fs/ntfs/ntfs_iconv.c optional ntfs_iconv fs/ntfs/ntfs_ihash.c optional ntfs fs/ntfs/ntfs_subr.c optional ntfs fs/ntfs/ntfs_vfsops.c optional ntfs fs/ntfs/ntfs_vnops.c optional ntfs fs/nullfs/null_subr.c optional nullfs fs/nullfs/null_vfsops.c optional nullfs fs/nullfs/null_vnops.c optional nullfs fs/nwfs/nwfs_io.c optional nwfs fs/nwfs/nwfs_ioctl.c optional nwfs fs/nwfs/nwfs_node.c optional nwfs fs/nwfs/nwfs_subr.c optional nwfs fs/nwfs/nwfs_vfsops.c optional nwfs fs/nwfs/nwfs_vnops.c optional nwfs fs/portalfs/portal_vfsops.c optional portalfs fs/portalfs/portal_vnops.c optional portalfs fs/procfs/procfs.c optional procfs fs/procfs/procfs_ctl.c optional procfs fs/procfs/procfs_dbregs.c optional procfs fs/procfs/procfs_fpregs.c optional procfs fs/procfs/procfs_ioctl.c optional procfs fs/procfs/procfs_map.c optional procfs fs/procfs/procfs_mem.c optional procfs fs/procfs/procfs_note.c optional procfs fs/procfs/procfs_regs.c optional procfs fs/procfs/procfs_rlimit.c optional procfs fs/procfs/procfs_status.c optional procfs fs/procfs/procfs_type.c optional procfs fs/pseudofs/pseudofs.c optional pseudofs fs/pseudofs/pseudofs_fileno.c optional pseudofs fs/pseudofs/pseudofs_vncache.c optional pseudofs fs/pseudofs/pseudofs_vnops.c optional pseudofs fs/smbfs/smbfs_io.c optional smbfs fs/smbfs/smbfs_node.c optional smbfs fs/smbfs/smbfs_smb.c optional smbfs fs/smbfs/smbfs_subr.c optional smbfs fs/smbfs/smbfs_vfsops.c optional smbfs fs/smbfs/smbfs_vnops.c optional smbfs fs/udf/osta.c optional udf fs/udf/udf_iconv.c optional udf_iconv fs/udf/udf_vfsops.c optional udf fs/udf/udf_vnops.c optional udf fs/unionfs/union_subr.c optional unionfs fs/unionfs/union_vfsops.c optional unionfs fs/unionfs/union_vnops.c optional unionfs fs/tmpfs/tmpfs_vnops.c optional tmpfs fs/tmpfs/tmpfs_fifoops.c optional tmpfs fs/tmpfs/tmpfs_vfsops.c optional tmpfs fs/tmpfs/tmpfs_subr.c optional tmpfs gdb/gdb_cons.c optional gdb gdb/gdb_main.c optional gdb gdb/gdb_packet.c optional gdb geom/bde/g_bde.c optional geom_bde geom/bde/g_bde_crypt.c optional geom_bde geom/bde/g_bde_lock.c optional geom_bde geom/bde/g_bde_work.c optional geom_bde geom/cache/g_cache.c optional geom_cache geom/concat/g_concat.c optional geom_concat geom/eli/g_eli.c optional geom_eli geom/eli/g_eli_crypto.c optional geom_eli geom/eli/g_eli_ctl.c optional geom_eli geom/eli/g_eli_integrity.c optional geom_eli geom/eli/g_eli_key.c optional geom_eli geom/eli/g_eli_privacy.c optional geom_eli geom/eli/pkcs5v2.c optional geom_eli geom/gate/g_gate.c optional geom_gate geom/geom_aes.c optional geom_aes geom/geom_bsd.c optional geom_bsd geom/geom_bsd_enc.c optional geom_bsd geom/geom_ccd.c optional ccd | geom_ccd geom/geom_ctl.c standard geom/geom_dev.c standard geom/geom_disk.c standard geom/geom_dump.c standard geom/geom_event.c standard geom/geom_fox.c optional geom_fox geom/geom_io.c standard geom/geom_kern.c standard geom/geom_mbr.c optional geom_mbr geom/geom_mbr_enc.c optional geom_mbr geom/geom_pc98.c optional geom_pc98 geom/geom_pc98_enc.c optional geom_pc98 geom/geom_slice.c standard geom/geom_subr.c standard geom/geom_sunlabel.c optional geom_sunlabel geom/geom_sunlabel_enc.c optional geom_sunlabel geom/geom_vfs.c standard geom/geom_vol_ffs.c optional geom_vol geom/journal/g_journal.c optional geom_journal geom/journal/g_journal_ufs.c optional geom_journal geom/label/g_label.c optional geom_label geom/label/g_label_ext2fs.c optional geom_label geom/label/g_label_iso9660.c optional geom_label geom/label/g_label_msdosfs.c optional geom_label geom/label/g_label_ntfs.c optional geom_label geom/label/g_label_reiserfs.c optional geom_label geom/label/g_label_ufs.c optional geom_label geom/linux_lvm/g_linux_lvm.c optional geom_linux_lvm geom/mirror/g_mirror.c optional geom_mirror geom/mirror/g_mirror_ctl.c optional geom_mirror geom/multipath/g_multipath.c optional geom_multipath geom/nop/g_nop.c optional geom_nop geom/part/g_part.c standard geom/part/g_part_if.m standard geom/part/g_part_apm.c optional geom_part_apm geom/part/g_part_bsd.c optional geom_part_bsd geom/part/g_part_gpt.c optional geom_part_gpt geom/part/g_part_mbr.c optional geom_part_mbr geom/part/g_part_pc98.c optional geom_part_pc98 geom/part/g_part_vtoc8.c optional geom_part_vtoc8 geom/raid3/g_raid3.c optional geom_raid3 geom/raid3/g_raid3_ctl.c optional geom_raid3 geom/shsec/g_shsec.c optional geom_shsec geom/stripe/g_stripe.c optional geom_stripe geom/uzip/g_uzip.c optional geom_uzip geom/virstor/binstream.c optional geom_virstor geom/virstor/g_virstor.c optional geom_virstor geom/virstor/g_virstor_md.c optional geom_virstor geom/zero/g_zero.c optional geom_zero gnu/fs/ext2fs/ext2_alloc.c optional ext2fs \ warning "kernel contains GPL contaminated ext2fs filesystem" gnu/fs/ext2fs/ext2_balloc.c optional ext2fs gnu/fs/ext2fs/ext2_bmap.c optional ext2fs gnu/fs/ext2fs/ext2_inode.c optional ext2fs gnu/fs/ext2fs/ext2_inode_cnv.c optional ext2fs gnu/fs/ext2fs/ext2_linux_balloc.c optional ext2fs gnu/fs/ext2fs/ext2_linux_ialloc.c optional ext2fs gnu/fs/ext2fs/ext2_lookup.c optional ext2fs gnu/fs/ext2fs/ext2_subr.c optional ext2fs gnu/fs/ext2fs/ext2_vfsops.c optional ext2fs gnu/fs/ext2fs/ext2_vnops.c optional ext2fs gnu/fs/reiserfs/reiserfs_hashes.c optional reiserfs \ warning "kernel contains GPL contaminated ReiserFS filesystem" gnu/fs/reiserfs/reiserfs_inode.c optional reiserfs gnu/fs/reiserfs/reiserfs_item_ops.c optional reiserfs gnu/fs/reiserfs/reiserfs_namei.c optional reiserfs gnu/fs/reiserfs/reiserfs_prints.c optional reiserfs gnu/fs/reiserfs/reiserfs_stree.c optional reiserfs gnu/fs/reiserfs/reiserfs_vfsops.c optional reiserfs gnu/fs/reiserfs/reiserfs_vnops.c optional reiserfs # isa/isa_if.m standard isa/isa_common.c optional isa isa/isahint.c optional isa isa/orm.c optional isa isa/pnp.c optional isa isapnp isa/pnpparse.c optional isa isapnp fs/cd9660/cd9660_bmap.c optional cd9660 fs/cd9660/cd9660_lookup.c optional cd9660 fs/cd9660/cd9660_node.c optional cd9660 fs/cd9660/cd9660_rrip.c optional cd9660 fs/cd9660/cd9660_util.c optional cd9660 fs/cd9660/cd9660_vfsops.c optional cd9660 fs/cd9660/cd9660_vnops.c optional cd9660 fs/cd9660/cd9660_iconv.c optional cd9660_iconv kern/bus_if.m standard kern/clock_if.m standard kern/cpufreq_if.m standard kern/device_if.m standard kern/imgact_elf.c standard kern/imgact_shell.c standard kern/inflate.c optional gzip kern/init_main.c standard kern/init_sysent.c standard kern/ksched.c optional _kposix_priority_scheduling kern/kern_acct.c standard kern/kern_alq.c optional alq kern/kern_clock.c standard kern/kern_condvar.c standard kern/kern_conf.c standard kern/kern_cons.c standard kern/kern_cpu.c standard kern/kern_cpuset.c standard kern/kern_context.c standard kern/kern_descrip.c standard kern/kern_dtrace.c optional kdtrace_hooks kern/kern_environment.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fork.c standard kern/kern_idle.c standard kern/kern_intr.c standard kern/kern_jail.c standard kern/kern_kthread.c standard kern/kern_ktr.c optional ktr kern/kern_ktrace.c standard kern/kern_linker.c standard kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_malloc.c standard kern/kern_mbuf.c standard kern/kern_mib.c standard kern/kern_module.c standard kern/kern_mtxpool.c standard kern/kern_mutex.c standard kern/kern_ntptime.c standard kern/kern_osd.c standard kern/kern_physio.c standard kern/kern_pmc.c standard kern/kern_poll.c optional device_polling kern/kern_priv.c standard kern/kern_proc.c standard kern/kern_prot.c standard kern/kern_resource.c standard kern/kern_rmlock.c standard kern/kern_rwlock.c standard kern/kern_sdt.c optional kdtrace_hooks kern/kern_sema.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_subr.c standard kern/kern_switch.c standard kern/kern_sx.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_tc.c standard kern/kern_thr.c standard kern/kern_thread.c standard kern/kern_time.c standard kern/kern_timeout.c standard kern/kern_umtx.c standard kern/kern_uuid.c standard kern/kern_xxx.c standard kern/kern_vimage.c standard kern/link_elf.c standard kern/linker_if.m standard kern/md4c.c optional netsmb kern/md5c.c standard kern/p1003_1b.c standard kern/posix4_mib.c standard kern/sched_4bsd.c optional sched_4bsd kern/sched_ule.c optional sched_ule kern/serdev_if.m standard kern/stack_protector.c standard \ compile-with "${NORMAL_C:N-fstack-protector*}" kern/subr_acl_posix1e.c standard kern/subr_autoconf.c standard kern/subr_blist.c standard kern/subr_bus.c standard kern/subr_bufring.c standard kern/subr_clist.c standard kern/subr_clock.c standard kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_eventhandler.c standard kern/subr_fattime.c standard kern/subr_firmware.c optional firmware kern/subr_hints.c standard kern/subr_kdb.c standard kern/subr_kobj.c standard kern/subr_lock.c standard kern/subr_log.c standard kern/subr_mbpool.c optional libmbpool kern/subr_mchain.c optional libmchain kern/subr_module.c standard kern/subr_msgbuf.c standard kern/subr_param.c standard kern/subr_pcpu.c standard kern/subr_power.c standard kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_rman.c standard kern/subr_rtc.c standard kern/subr_sbuf.c standard kern/subr_scanf.c standard kern/subr_sleepqueue.c standard kern/subr_smp.c standard kern/subr_stack.c optional ddb | stack | ktr kern/subr_taskqueue.c standard kern/subr_trap.c standard kern/subr_turnstile.c standard kern/subr_unit.c standard kern/subr_witness.c optional witness kern/sys_generic.c standard kern/sys_pipe.c standard kern/sys_process.c standard kern/sys_socket.c standard kern/syscalls.c optional witness | invariants | kdtrace_hooks kern/sysv_ipc.c standard kern/sysv_msg.c optional sysvmsg kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c optional compat_43tty kern/tty_info.c standard kern/tty_inq.c standard kern/tty_outq.c standard kern/tty_pts.c standard kern/tty_pty.c optional pty kern/tty_tty.c standard kern/tty_ttydisc.c standard kern/uipc_accf.c optional inet kern/uipc_cow.c optional zero_copy_sockets kern/uipc_debug.c optional ddb kern/uipc_domain.c standard kern/uipc_mbuf.c standard kern/uipc_mbuf2.c standard kern/uipc_mqueue.c optional p1003_1b_mqueue kern/uipc_sem.c optional p1003_1b_semaphores kern/uipc_shm.c standard kern/uipc_sockbuf.c standard kern/uipc_socket.c standard kern/uipc_syscalls.c standard kern/uipc_usrreq.c standard kern/vfs_acl.c standard kern/vfs_aio.c optional vfs_aio kern/vfs_bio.c standard kern/vfs_cache.c standard kern/vfs_cluster.c standard kern/vfs_default.c standard kern/vfs_export.c standard kern/vfs_extattr.c standard kern/vfs_hash.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard kern/vfs_mount.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard # # Kernel GSS-API # gssd.h optional kgssapi \ dependency "$S/kgssapi/gssd.x" \ compile-with "rpcgen -hM $S/kgssapi/gssd.x | grep -v pthread.h > gssd.h" \ no-obj no-implicit-rule before-depend local \ clean "gssd.h" gssd_xdr.c optional kgssapi \ dependency "$S/kgssapi/gssd.x gssd.h" \ compile-with "rpcgen -c $S/kgssapi/gssd.x -o gssd_xdr.c" \ no-implicit-rule before-depend local \ clean "gssd_xdr.c" gssd_clnt.c optional kgssapi \ dependency "$S/kgssapi/gssd.x gssd.h" \ compile-with "rpcgen -lM $S/kgssapi/gssd.x | grep -v string.h > gssd_clnt.c" \ no-implicit-rule before-depend local \ clean "gssd_clnt.c" kgssapi/gss_accept_sec_context.c optional kgssapi kgssapi/gss_add_oid_set_member.c optional kgssapi kgssapi/gss_acquire_cred.c optional kgssapi kgssapi/gss_canonicalize_name.c optional kgssapi kgssapi/gss_create_empty_oid_set.c optional kgssapi kgssapi/gss_delete_sec_context.c optional kgssapi kgssapi/gss_display_status.c optional kgssapi kgssapi/gss_export_name.c optional kgssapi kgssapi/gss_get_mic.c optional kgssapi kgssapi/gss_init_sec_context.c optional kgssapi kgssapi/gss_impl.c optional kgssapi kgssapi/gss_import_name.c optional kgssapi kgssapi/gss_names.c optional kgssapi kgssapi/gss_pname_to_uid.c optional kgssapi kgssapi/gss_release_buffer.c optional kgssapi kgssapi/gss_release_cred.c optional kgssapi kgssapi/gss_release_name.c optional kgssapi kgssapi/gss_release_oid_set.c optional kgssapi kgssapi/gss_set_cred_option.c optional kgssapi kgssapi/gss_test_oid_set_member.c optional kgssapi kgssapi/gss_unwrap.c optional kgssapi kgssapi/gss_verify_mic.c optional kgssapi kgssapi/gss_wrap.c optional kgssapi kgssapi/gss_wrap_size_limit.c optional kgssapi kgssapi/gssd_prot.c optional kgssapi kgssapi/krb5/krb5_mech.c optional kgssapi kgssapi/krb5/kcrypto.c optional kgssapi kgssapi/krb5/kcrypto_aes.c optional kgssapi kgssapi/krb5/kcrypto_arcfour.c optional kgssapi kgssapi/krb5/kcrypto_des.c optional kgssapi kgssapi/krb5/kcrypto_des3.c optional kgssapi kgssapi/kgss_if.m optional kgssapi kgssapi/gsstest.c optional kgssapi_debug # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures, e.g., # libkern/divdi3.c is needed by i386 but not alpha. Also, some of these # routines may be optimized for a particular platform. In either case, # the file should be moved to conf/files. from here. # libkern/arc4random.c standard libkern/bcd.c standard libkern/bsearch.c standard libkern/crc32.c standard libkern/fnmatch.c standard libkern/gets.c standard libkern/iconv.c optional libiconv libkern/iconv_converter_if.m optional libiconv libkern/iconv_xlat.c optional libiconv libkern/iconv_xlat16.c optional libiconv libkern/index.c standard libkern/inet_ntoa.c standard libkern/mcount.c optional profiling-routine libkern/memcmp.c standard libkern/qsort.c standard libkern/qsort_r.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcasecmp.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strcspn.c standard libkern/strdup.c standard libkern/strlcat.c standard libkern/strlcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/strsep.c standard libkern/strspn.c standard libkern/strstr.c standard libkern/strtol.c standard libkern/strtoq.c standard libkern/strtoul.c standard libkern/strtouq.c standard libkern/strvalid.c standard net/bpf.c standard net/bpf_buffer.c optional bpf net/bpf_jitter.c optional bpf_jitter net/bpf_filter.c optional bpf | netgraph_bpf net/bpf_zerocopy.c optional bpf net/bridgestp.c optional bridge | if_bridge net/bsd_comp.c optional ppp_bsdcomp net/ieee8023ad_lacp.c optional lagg net/if.c standard net/if_arcsubr.c optional arcnet net/if_atmsubr.c optional atm net/if_bridge.c optional bridge | if_bridge net/if_clone.c standard net/if_disc.c optional disc net/if_edsc.c optional edsc net/if_ef.c optional ef net/if_enc.c optional enc net/if_ethersubr.c optional ether \ compile-with "${NORMAL_C} -I$S/contrib/pf" net/if_faith.c optional faith net/if_fddisubr.c optional fddi net/if_fwsubr.c optional fwip net/if_gif.c optional gif net/if_gre.c optional gre net/if_iso88025subr.c optional token net/if_lagg.c optional lagg net/if_loop.c optional loop net/if_llatbl.c standard net/if_media.c standard net/if_mib.c standard net/if_ppp.c optional ppp net/if_sl.c optional sl net/if_spppfr.c optional sppp | netgraph_sppp net/if_spppsubr.c optional sppp | netgraph_sppp net/if_stf.c optional stf net/if_tun.c optional tun net/if_tap.c optional tap net/if_vlan.c optional vlan net/mppcc.c optional netgraph_mppc_compression net/mppcd.c optional netgraph_mppc_compression net/netisr.c standard net/ppp_deflate.c optional ppp_deflate net/ppp_tty.c optional ppp net/pfil.c optional ether | inet net/radix.c standard net/radix_mpath.c standard net/raw_cb.c standard net/raw_usrreq.c standard net/route.c standard net/rtsock.c standard net/slcompress.c optional netgraph_vjc | ppp | sl | sppp | \ netgraph_sppp net/zlib.c optional crypto | geom_uzip | ipsec | \ mxge | ppp_deflate | netgraph_deflate | \ ddb_ctf net80211/ieee80211.c optional wlan net80211/ieee80211_acl.c optional wlan_acl net80211/ieee80211_adhoc.c optional wlan net80211/ieee80211_amrr.c optional wlan_amrr net80211/ieee80211_crypto.c optional wlan net80211/ieee80211_crypto_ccmp.c optional wlan_ccmp net80211/ieee80211_crypto_none.c optional wlan net80211/ieee80211_crypto_tkip.c optional wlan_tkip net80211/ieee80211_crypto_wep.c optional wlan_wep net80211/ieee80211_ddb.c optional wlan ddb net80211/ieee80211_dfs.c optional wlan net80211/ieee80211_freebsd.c optional wlan net80211/ieee80211_hostap.c optional wlan net80211/ieee80211_ht.c optional wlan net80211/ieee80211_input.c optional wlan net80211/ieee80211_ioctl.c optional wlan net80211/ieee80211_monitor.c optional wlan net80211/ieee80211_node.c optional wlan net80211/ieee80211_output.c optional wlan net80211/ieee80211_phy.c optional wlan net80211/ieee80211_power.c optional wlan net80211/ieee80211_proto.c optional wlan net80211/ieee80211_regdomain.c optional wlan net80211/ieee80211_rssadapt.c optional wlan_rssadapt net80211/ieee80211_scan.c optional wlan net80211/ieee80211_scan_sta.c optional wlan net80211/ieee80211_sta.c optional wlan net80211/ieee80211_wds.c optional wlan net80211/ieee80211_xauth.c optional wlan_xauth netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk netatalk/at_rmx.c optional netatalk netatalk/ddp_input.c optional netatalk netatalk/ddp_output.c optional netatalk netatalk/ddp_pcb.c optional netatalk netatalk/ddp_usrreq.c optional netatalk netgraph/atm/ccatm/ng_ccatm.c optional ngatm_ccatm \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/ng_atm.c optional ngatm_atm netgraph/atm/ngatmbase.c optional ngatm_atmbase \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/sscfu/ng_sscfu.c optional ngatm_sscfu \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/sscop/ng_sscop.c optional ngatm_sscop \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/atm/uni/ng_uni.c optional ngatm_uni \ compile-with "${NORMAL_C} -I$S/contrib/ngatm" netgraph/bluetooth/common/ng_bluetooth.c optional netgraph_bluetooth netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c optional netgraph_bluetooth_bt3c netgraph/bluetooth/drivers/h4/ng_h4.c optional netgraph_bluetooth_h4 netgraph/bluetooth/drivers/ubt/ng_ubt.c optional netgraph_bluetooth_ubt netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c optional netgraph_bluetooth_ubtbcmfw netgraph/bluetooth/hci/ng_hci_cmds.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_evnt.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_main.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_misc.c optional netgraph_bluetooth_hci netgraph/bluetooth/hci/ng_hci_ulpi.c optional netgraph_bluetooth_hci netgraph/bluetooth/l2cap/ng_l2cap_cmds.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_evnt.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_llpi.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_main.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_misc.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c optional netgraph_bluetooth_l2cap netgraph/bluetooth/socket/ng_btsocket.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_hci_raw.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_l2cap.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_l2cap_raw.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_rfcomm.c optional netgraph_bluetooth_socket netgraph/bluetooth/socket/ng_btsocket_sco.c optional netgraph_bluetooth_socket netgraph/netflow/netflow.c optional netgraph_netflow netgraph/netflow/ng_netflow.c optional netgraph_netflow netgraph/ng_UI.c optional netgraph_UI netgraph/ng_async.c optional netgraph_async netgraph/ng_atmllc.c optional netgraph_atmllc netgraph/ng_base.c optional netgraph netgraph/ng_bpf.c optional netgraph_bpf netgraph/ng_bridge.c optional netgraph_bridge netgraph/ng_car.c optional netgraph_car netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_deflate.c optional netgraph_deflate netgraph/ng_device.c optional netgraph_device netgraph/ng_echo.c optional netgraph_echo netgraph/ng_eiface.c optional netgraph_eiface netgraph/ng_ether.c optional netgraph_ether netgraph/ng_fec.c optional netgraph_fec netgraph/ng_frame_relay.c optional netgraph_frame_relay netgraph/ng_gif.c optional netgraph_gif netgraph/ng_gif_demux.c optional netgraph_gif_demux netgraph/ng_hole.c optional netgraph_hole netgraph/ng_iface.c optional netgraph_iface netgraph/ng_ip_input.c optional netgraph_ip_input netgraph/ng_ipfw.c optional netgraph_ipfw netgraph/ng_ksocket.c optional netgraph_ksocket netgraph/ng_l2tp.c optional netgraph_l2tp netgraph/ng_lmi.c optional netgraph_lmi netgraph/ng_mppc.c optional netgraph_mppc_compression | \ netgraph_mppc_encryption netgraph/ng_nat.c optional netgraph_nat netgraph/ng_one2many.c optional netgraph_one2many netgraph/ng_parse.c optional netgraph netgraph/ng_ppp.c optional netgraph_ppp netgraph/ng_pppoe.c optional netgraph_pppoe netgraph/ng_pptpgre.c optional netgraph_pptpgre netgraph/ng_pred1.c optional netgraph_pred1 netgraph/ng_rfc1490.c optional netgraph_rfc1490 netgraph/ng_socket.c optional netgraph_socket netgraph/ng_split.c optional netgraph_split netgraph/ng_sppp.c optional netgraph_sppp netgraph/ng_tag.c optional netgraph_tag netgraph/ng_tcpmss.c optional netgraph_tcpmss netgraph/ng_tee.c optional netgraph_tee netgraph/ng_tty.c optional netgraph_tty netgraph/ng_vjc.c optional netgraph_vjc netinet/accf_data.c optional accept_filter_data netinet/accf_dns.c optional accept_filter_dns netinet/accf_http.c optional accept_filter_http netinet/if_atm.c optional atm netinet/if_ether.c optional ether netinet/igmp.c optional inet netinet/in.c optional inet netinet/ip_carp.c optional carp netinet/in_gif.c optional gif inet netinet/ip_gre.c optional gre inet netinet/ip_id.c optional inet netinet/in_mcast.c optional inet netinet/in_pcb.c optional inet netinet/in_proto.c optional inet \ compile-with "${NORMAL_C} -I$S/contrib/pf" netinet/in_rmx.c optional inet netinet/ip_divert.c optional ipdivert netinet/ip_dummynet.c optional dummynet netinet/ip_ecn.c optional inet | inet6 netinet/ip_encap.c optional inet | inet6 netinet/ip_fastfwd.c optional inet netinet/ip_fw2.c optional ipfirewall \ compile-with "${NORMAL_C} -I$S/contrib/pf" netinet/ip_fw_pfil.c optional ipfirewall netinet/ip_fw_nat.c optional ipfirewall_nat netinet/ip_icmp.c optional inet netinet/ip_input.c optional inet netinet/ip_ipsec.c optional ipsec netinet/ip_mroute.c optional mrouting inet | mrouting inet6 netinet/ip_options.c optional inet netinet/ip_output.c optional inet netinet/raw_ip.c optional inet netinet/sctp_asconf.c optional inet sctp netinet/sctp_auth.c optional inet sctp netinet/sctp_bsd_addr.c optional inet sctp netinet/sctp_cc_functions.c optional inet sctp netinet/sctp_crc32.c optional inet sctp netinet/sctp_indata.c optional inet sctp netinet/sctp_input.c optional inet sctp netinet/sctp_output.c optional inet sctp netinet/sctp_pcb.c optional inet sctp netinet/sctp_peeloff.c optional inet sctp netinet/sctp_sysctl.c optional inet sctp netinet/sctp_timer.c optional inet sctp netinet/sctp_usrreq.c optional inet sctp netinet/sctputil.c optional inet sctp netinet/tcp_debug.c optional tcpdebug netinet/tcp_hostcache.c optional inet netinet/tcp_input.c optional inet netinet/tcp_lro.c optional inet netinet/tcp_output.c optional inet netinet/tcp_offload.c optional inet netinet/tcp_reass.c optional inet netinet/tcp_sack.c optional inet netinet/tcp_subr.c optional inet netinet/tcp_syncache.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_timewait.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet netinet/libalias/alias.c optional libalias | netgraph_nat netinet/libalias/alias_db.c optional libalias | netgraph_nat netinet/libalias/alias_mod.c optional libalias | netgraph_nat netinet/libalias/alias_proxy.c optional libalias | netgraph_nat netinet/libalias/alias_util.c optional libalias | netgraph_nat netinet6/dest6.c optional inet6 netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 netinet6/in6.c optional inet6 netinet6/in6_cksum.c optional inet6 netinet6/in6_gif.c optional gif inet6 netinet6/in6_ifattach.c optional inet6 netinet6/in6_pcb.c optional inet6 netinet6/in6_proto.c optional inet6 netinet6/in6_rmx.c optional inet6 netinet6/in6_src.c optional inet6 netinet6/ip6_forward.c optional inet6 netinet6/ip6_id.c optional inet6 netinet6/ip6_input.c optional inet6 netinet6/ip6_mroute.c optional mrouting inet6 netinet6/ip6_output.c optional inet6 netinet6/ip6_ipsec.c optional inet6 ipsec netinet6/mld6.c optional inet6 netinet6/nd6.c optional inet6 netinet6/nd6_nbr.c optional inet6 netinet6/nd6_rtr.c optional inet6 netinet6/raw_ip6.c optional inet6 netinet6/route6.c optional inet6 netinet6/scope6.c optional inet6 netinet6/sctp6_usrreq.c optional inet6 sctp netinet6/udp6_usrreq.c optional inet6 netipsec/ipsec.c optional ipsec netipsec/ipsec_input.c optional ipsec netipsec/ipsec_mbuf.c optional ipsec netipsec/ipsec_output.c optional ipsec netipsec/key.c optional ipsec netipsec/key_debug.c optional ipsec netipsec/keysock.c optional ipsec netipsec/xform_ah.c optional ipsec netipsec/xform_esp.c optional ipsec netipsec/xform_ipcomp.c optional ipsec netipsec/xform_ipip.c optional ipsec netipsec/xform_tcp.c optional ipsec tcp_signature netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx netipx/ipx_outputfl.c optional ipx netipx/ipx_pcb.c optional ipx netipx/ipx_proto.c optional ipx netipx/ipx_usrreq.c optional ipx netipx/spx_debug.c optional ipx netipx/spx_usrreq.c optional ipx netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm netnatm/natm_proto.c optional natm netncp/ncp_conn.c optional ncp netncp/ncp_crypt.c optional ncp netncp/ncp_login.c optional ncp netncp/ncp_mod.c optional ncp netncp/ncp_ncp.c optional ncp netncp/ncp_nls.c optional ncp netncp/ncp_rq.c optional ncp netncp/ncp_sock.c optional ncp netncp/ncp_subr.c optional ncp netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb netsmb/smb_iod.c optional netsmb netsmb/smb_rq.c optional netsmb netsmb/smb_smb.c optional netsmb netsmb/smb_subr.c optional netsmb netsmb/smb_trantcp.c optional netsmb netsmb/smb_usr.c optional netsmb nfs/nfs_common.c optional nfsclient | nfsserver nfs4client/nfs4_dev.c optional nfsclient nfs4client/nfs4_idmap.c optional nfsclient nfs4client/nfs4_socket.c optional nfsclient nfs4client/nfs4_subs.c optional nfsclient nfs4client/nfs4_vfs_subs.c optional nfsclient nfs4client/nfs4_vfsops.c optional nfsclient nfs4client/nfs4_vn_subs.c optional nfsclient nfs4client/nfs4_vnops.c optional nfsclient nfsclient/bootp_subr.c optional bootp nfsclient nfsclient/krpc_subr.c optional bootp nfsclient nfsclient/nfs_bio.c optional nfsclient nfsclient/nfs_diskless.c optional nfsclient nfs_root nfsclient/nfs_node.c optional nfsclient nfsclient/nfs_socket.c optional nfsclient nfsclient/nfs_krpc.c optional nfsclient nfsclient/nfs_subs.c optional nfsclient nfsclient/nfs_nfsiod.c optional nfsclient nfsclient/nfs_vfsops.c optional nfsclient nfsclient/nfs_vnops.c optional nfsclient nfsclient/nfs_lock.c optional nfsclient nfsserver/nfs_fha.c optional nfsserver nfsserver/nfs_serv.c optional nfsserver nfsserver/nfs_srvkrpc.c optional nfsserver nfsserver/nfs_srvsock.c optional nfsserver nfsserver/nfs_srvcache.c optional nfsserver nfsserver/nfs_srvsubs.c optional nfsserver nfsserver/nfs_syscalls.c optional nfsserver nlm/nlm_advlock.c optional nfslockd nfsclient nlm/nlm_prot_clnt.c optional nfslockd nlm/nlm_prot_impl.c optional nfslockd nlm/nlm_prot_server.c optional nfslockd nlm/nlm_prot_svc.c optional nfslockd nlm/nlm_prot_xdr.c optional nfslockd nlm/sm_inter_xdr.c optional nfslockd # crypto support opencrypto/cast.c optional crypto | ipsec opencrypto/criov.c optional crypto opencrypto/crypto.c optional crypto opencrypto/cryptodev.c optional cryptodev opencrypto/cryptodev_if.m optional crypto opencrypto/cryptosoft.c optional crypto opencrypto/deflate.c optional crypto opencrypto/rmd160.c optional crypto | ipsec opencrypto/skipjack.c optional crypto opencrypto/xform.c optional crypto pci/alpm.c optional alpm pci pci/amdpm.c optional amdpm pci | nfpm pci pci/amdsmb.c optional amdsmb pci pci/if_rl.c optional rl pci pci/intpm.c optional intpm pci pci/ncr.c optional ncr pci pci/nfsmb.c optional nfsmb pci pci/viapm.c optional viapm pci rpc/auth_none.c optional krpc | nfslockd | nfsclient | nfsserver rpc/auth_unix.c optional krpc | nfslockd | nfsclient rpc/authunix_prot.c optional krpc | nfslockd | nfsclient | nfsserver rpc/clnt_dg.c optional krpc | nfslockd | nfsclient rpc/clnt_rc.c optional krpc | nfslockd | nfsclient rpc/clnt_vc.c optional krpc | nfslockd | nfsclient | nfsserver rpc/getnetconfig.c optional krpc | nfslockd | nfsclient | nfsserver rpc/inet_ntop.c optional krpc | nfslockd | nfsclient | nfsserver rpc/inet_pton.c optional krpc | nfslockd | nfsclient | nfsserver rpc/replay.c optional krpc | nfslockd | nfsserver rpc/rpc_callmsg.c optional krpc | nfslockd | nfsclient | nfsserver rpc/rpc_generic.c optional krpc | nfslockd | nfsclient | nfsserver rpc/rpc_prot.c optional krpc | nfslockd | nfsclient | nfsserver rpc/rpcb_clnt.c optional krpc | nfslockd | nfsclient | nfsserver rpc/rpcb_prot.c optional krpc | nfslockd | nfsclient | nfsserver rpc/rpcclnt.c optional nfsclient rpc/svc.c optional krpc | nfslockd | nfsserver rpc/svc_auth.c optional krpc | nfslockd | nfsserver rpc/svc_auth_unix.c optional krpc | nfslockd | nfsserver rpc/svc_dg.c optional krpc | nfslockd | nfsserver rpc/svc_generic.c optional krpc | nfslockd | nfsserver rpc/svc_vc.c optional krpc | nfslockd | nfsserver rpc/rpcsec_gss/rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi rpc/rpcsec_gss/rpcsec_gss_conf.c optional krpc kgssapi | nfslockd kgssapi rpc/rpcsec_gss/rpcsec_gss_misc.c optional krpc kgssapi | nfslockd kgssapi rpc/rpcsec_gss/rpcsec_gss_prot.c optional krpc kgssapi | nfslockd kgssapi rpc/rpcsec_gss/svc_rpcsec_gss.c optional krpc kgssapi | nfslockd kgssapi security/audit/audit.c optional audit security/audit/audit_arg.c optional audit security/audit/audit_bsm.c optional audit security/audit/audit_bsm_klib.c optional audit security/audit/audit_bsm_token.c optional audit security/audit/audit_pipe.c optional audit security/audit/audit_syscalls.c standard security/audit/audit_trigger.c optional audit security/audit/audit_worker.c optional audit security/mac/mac_atalk.c optional mac netatalk security/mac/mac_audit.c optional mac audit security/mac/mac_cred.c optional mac security/mac/mac_framework.c optional mac security/mac/mac_inet.c optional mac inet security/mac/mac_inet6.c optional mac inet6 security/mac/mac_label.c optional mac security/mac/mac_net.c optional mac security/mac/mac_pipe.c optional mac security/mac/mac_posix_sem.c optional mac security/mac/mac_posix_shm.c optional mac security/mac/mac_priv.c optional mac security/mac/mac_process.c optional mac security/mac/mac_socket.c optional mac security/mac/mac_syscalls.c standard security/mac/mac_system.c optional mac security/mac/mac_sysv_msg.c optional mac security/mac/mac_sysv_sem.c optional mac security/mac/mac_sysv_shm.c optional mac security/mac/mac_vfs.c optional mac security/mac_biba/mac_biba.c optional mac_biba security/mac_bsdextended/mac_bsdextended.c optional mac_bsdextended security/mac_bsdextended/ugidfw_system.c optional mac_bsdextended security/mac_bsdextended/ugidfw_vnode.c optional mac_bsdextended security/mac_ifoff/mac_ifoff.c optional mac_ifoff security/mac_lomac/mac_lomac.c optional mac_lomac security/mac_mls/mac_mls.c optional mac_mls security/mac_none/mac_none.c optional mac_none security/mac_partition/mac_partition.c optional mac_partition security/mac_portacl/mac_portacl.c optional mac_portacl security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids security/mac_stub/mac_stub.c optional mac_stub security/mac_test/mac_test.c optional mac_test ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_balloc.c optional ffs ufs/ffs/ffs_inode.c optional ffs ufs/ffs/ffs_snapshot.c optional ffs ufs/ffs/ffs_softdep.c optional ffs ufs/ffs/ffs_subr.c optional ffs ufs/ffs/ffs_tables.c optional ffs ufs/ffs/ffs_vfsops.c optional ffs ufs/ffs/ffs_vnops.c optional ffs ufs/ffs/ffs_rawread.c optional directio ufs/ufs/ufs_acl.c optional ffs ufs/ufs/ufs_bmap.c optional ffs ufs/ufs/ufs_dirhash.c optional ffs ufs/ufs/ufs_extattr.c optional ffs ufs/ufs/ufs_gjournal.c optional ffs ufs/ufs/ufs_inode.c optional ffs ufs/ufs/ufs_lookup.c optional ffs ufs/ufs/ufs_quota.c optional ffs ufs/ufs/ufs_vfsops.c optional ffs ufs/ufs/ufs_vnops.c optional ffs vm/default_pager.c standard vm/device_pager.c standard vm/phys_pager.c standard vm/redzone.c optional DEBUG_REDZONE vm/swap_pager.c standard vm/uma_core.c standard vm/uma_dbg.c standard vm/vm_contig.c standard vm/memguard.c optional DEBUG_MEMGUARD vm/vm_fault.c standard vm/vm_glue.c standard vm/vm_init.c standard vm/vm_kern.c standard vm/vm_map.c standard vm/vm_meter.c standard vm/vm_mmap.c standard vm/vm_object.c standard vm/vm_page.c standard vm/vm_pageout.c standard vm/vm_pager.c standard vm/vm_phys.c standard vm/vm_reserv.c standard vm/vm_unix.c standard vm/vm_zeroidle.c standard vm/vnode_pager.c standard xdr/xdr.c optional krpc | nfslockd | nfsclient | nfsserver xdr/xdr_array.c optional krpc | nfslockd | nfsclient | nfsserver xdr/xdr_mbuf.c optional krpc | nfslockd | nfsclient | nfsserver xdr/xdr_mem.c optional krpc | nfslockd | nfsclient | nfsserver xdr/xdr_reference.c optional krpc | nfslockd | nfsclient | nfsserver xdr/xdr_sizeof.c optional krpc | nfslockd | nfsclient | nfsserver # gnu/fs/xfs/xfs_alloc.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" \ warning "kernel contains GPL contaminated xfs filesystem" gnu/fs/xfs/xfs_alloc_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_bit.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_bmap.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_bmap_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_buf_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_da_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_block.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_data.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_leaf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_node.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_sf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir2_trace.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dir_leaf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_error.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_extfree_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_fsops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_ialloc.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_ialloc_btree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_inode.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_inode_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_iocore.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_itable.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dfrag.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_log.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_log_recover.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_mount.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_rename.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_ail.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_buf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_extfree.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_inode.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_trans_item.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_utils.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_vfsops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_vnodeops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_rw.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_attr_leaf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_attr.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_dmops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_qmops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_iget.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_freebsd_iget.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_mountops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_vnops.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_frw.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_buf.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_globals.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_dmistubs.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_super.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_stats.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_vfs.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_vnode.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_sysctl.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_fs_subr.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/xfs_ioctl.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/debug.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/ktrace.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/mrlock.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/uuid.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/FreeBSD/support/kmem.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_iomap.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" gnu/fs/xfs/xfs_behavior.c optional xfs \ compile-with "${NORMAL_C} -I$S/gnu/fs/xfs/FreeBSD -I$S/gnu/fs/xfs/FreeBSD/support -I$S/gnu/fs/xfs" xen/gnttab.c optional xen xen/features.c optional xen xen/evtchn/evtchn.c optional xen xen/evtchn/evtchn_dev.c optional xen xen/xenbus/xenbus_client.c optional xen xen/xenbus/xenbus_comms.c optional xen xen/xenbus/xenbus_dev.c optional xen xen/xenbus/xenbus_if.m optional xen xen/xenbus/xenbus_probe.c optional xen #xen/xenbus/xenbus_probe_backend.c optional xen xen/xenbus/xenbus_xs.c optional xen dev/xen/console/console.c optional xen dev/xen/console/xencons_ring.c optional xen dev/xen/blkfront/blkfront.c optional xen dev/xen/netfront/netfront.c optional xen #dev/xen/xenpci/xenpci.c optional xen #xen/xenbus/xenbus_newbus.c optional xenhvm Index: projects/cambria/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c =================================================================== --- projects/cambria/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c (revision 186459) +++ projects/cambria/sys/contrib/ipfilter/netinet/ip_fil_freebsd.c (revision 186460) @@ -1,1659 +1,1661 @@ /* $FreeBSD$ */ /* * Copyright (C) 1993-2003 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. */ #if !defined(lint) static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; static const char rcsid[] = "@(#)$Id: ip_fil_freebsd.c,v 2.53.2.50 2007/09/20 12:51:50 darrenr Exp $"; #endif #if defined(KERNEL) || defined(_KERNEL) # undef KERNEL # undef _KERNEL # define KERNEL 1 # define _KERNEL 1 #endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 400000) && \ !defined(KLD_MODULE) && !defined(IPFILTER_LKM) # include "opt_inet6.h" #endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 440000) && \ !defined(KLD_MODULE) && !defined(IPFILTER_LKM) # include "opt_random_ip_id.h" #endif #include #if defined(__FreeBSD__) && !defined(__FreeBSD_version) # if defined(IPFILTER_LKM) # ifndef __FreeBSD_cc_version # include # else # if __FreeBSD_cc_version < 430000 # include # endif # endif # endif #endif #include #include #include #if __FreeBSD_version >= 220000 # include # include #else # include #endif #include #include #if (__FreeBSD_version >= 300000) # include #else # include #endif #if !defined(__hpux) # include #endif #include #include #if __FreeBSD_version >= 500043 # include #else # include #endif #if __FreeBSD_version >= 800044 # include #else #define V_path_mtu_discovery path_mtu_discovery #define V_ipforwarding ipforwarding #endif #include #if __FreeBSD_version >= 300000 # include # if __FreeBSD_version >= 500043 # include # endif # if !defined(IPFILTER_LKM) # include "opt_ipfilter.h" # endif #endif #include #include #include #include #include #include #include #if defined(__osf__) # include #endif #include #include #include #ifndef _KERNEL # include "netinet/ipf.h" #endif #include "netinet/ip_compat.h" #ifdef USE_INET6 # include #endif #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" #ifdef IPFILTER_SYNC #include "netinet/ip_sync.h" #endif #ifdef IPFILTER_SCAN #include "netinet/ip_scan.h" #endif #include "netinet/ip_pool.h" #if defined(__FreeBSD_version) && (__FreeBSD_version >= 800056) # include #endif #if defined(__FreeBSD_version) && (__FreeBSD_version >= 300000) # include #endif #include #ifdef CSUM_DATA_VALID #include #endif extern int ip_optcopy __P((struct ip *, struct ip *)); #if (__FreeBSD_version > 460000) && (__FreeBSD_version < 800055) extern int path_mtu_discovery; #endif # ifdef IPFILTER_M_IPFILTER MALLOC_DEFINE(M_IPFILTER, "ipfilter", "IP Filter packet filter data structures"); # endif #if !defined(__osf__) extern struct protosw inetsw[]; #endif static int (*fr_savep) __P((ip_t *, int, void *, int, struct mbuf **)); static int fr_send_ip __P((fr_info_t *, mb_t *, mb_t **)); # ifdef USE_MUTEXES ipfmutex_t ipl_mutex, ipf_authmx, ipf_rw, ipf_stinsert; ipfmutex_t ipf_nat_new, ipf_natio, ipf_timeoutlock; ipfrwlock_t ipf_mutex, ipf_global, ipf_ipidfrag, ipf_frcache, ipf_tokens; ipfrwlock_t ipf_frag, ipf_state, ipf_nat, ipf_natfrag, ipf_auth; # endif int ipf_locks_done = 0; #if (__FreeBSD_version >= 300000) struct callout_handle fr_slowtimer_ch; #endif struct selinfo ipfselwait[IPL_LOGSIZE]; #if (__FreeBSD_version >= 500011) # include # if defined(NETBSD_PF) # include -# include +# if (__FreeBSD_version < 501108) +# include +# endif /* * We provide the fr_checkp name just to minimize changes later. */ int (*fr_checkp) __P((ip_t *ip, int hlen, void *ifp, int out, mb_t **mp)); # endif /* NETBSD_PF */ #endif /* __FreeBSD_version >= 500011 */ #if (__FreeBSD_version >= 502103) static eventhandler_tag ipf_arrivetag, ipf_departtag, ipf_clonetag; static void ipf_ifevent(void *arg); static void ipf_ifevent(arg) void *arg; { frsync(NULL); } #endif #if (__FreeBSD_version >= 501108) && defined(_KERNEL) static int fr_check_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct ip *ip = mtod(*mp, struct ip *); return fr_check(ip, ip->ip_hl << 2, ifp, (dir == PFIL_OUT), mp); } # ifdef USE_INET6 # include static int fr_check_wrapper6(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { return (fr_check(mtod(*mp, struct ip *), sizeof(struct ip6_hdr), ifp, (dir == PFIL_OUT), mp)); } # endif #endif /* __FreeBSD_version >= 501108 */ #if defined(IPFILTER_LKM) int iplidentify(s) char *s; { if (strcmp(s, "ipl") == 0) return 1; return 0; } #endif /* IPFILTER_LKM */ int ipfattach() { #ifdef USE_SPL int s; #endif SPL_NET(s); if (fr_running > 0) { SPL_X(s); return EBUSY; } MUTEX_INIT(&ipf_rw, "ipf rw mutex"); MUTEX_INIT(&ipf_timeoutlock, "ipf timeout queue mutex"); RWLOCK_INIT(&ipf_ipidfrag, "ipf IP NAT-Frag rwlock"); RWLOCK_INIT(&ipf_tokens, "ipf token rwlock"); ipf_locks_done = 1; if (fr_initialise() < 0) { SPL_X(s); return EIO; } if (fr_checkp != fr_check) { fr_savep = fr_checkp; fr_checkp = fr_check; } bzero((char *)ipfselwait, sizeof(ipfselwait)); bzero((char *)frcache, sizeof(frcache)); fr_running = 1; if (fr_control_forwarding & 1) V_ipforwarding = 1; SPL_X(s); #if (__FreeBSD_version >= 300000) fr_slowtimer_ch = timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); #else timeout(fr_slowtimer, NULL, (hz / IPF_HZ_DIVIDE) * IPF_HZ_MULT); #endif return 0; } /* * Disable the filter by removing the hooks from the IP input/output * stream. */ int ipfdetach() { #ifdef USE_SPL int s; #endif if (fr_control_forwarding & 2) V_ipforwarding = 0; SPL_NET(s); #if (__FreeBSD_version >= 300000) if (fr_slowtimer_ch.callout != NULL) untimeout(fr_slowtimer, NULL, fr_slowtimer_ch); bzero(&fr_slowtimer_ch, sizeof(fr_slowtimer_ch)); #else untimeout(fr_slowtimer, NULL); #endif /* FreeBSD */ #ifndef NETBSD_PF if (fr_checkp != NULL) fr_checkp = fr_savep; fr_savep = NULL; #endif fr_deinitialise(); fr_running = -2; (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE|FR_INACTIVE); (void) frflush(IPL_LOGIPF, 0, FR_INQUE|FR_OUTQUE); if (ipf_locks_done == 1) { MUTEX_DESTROY(&ipf_timeoutlock); MUTEX_DESTROY(&ipf_rw); RW_DESTROY(&ipf_ipidfrag); RW_DESTROY(&ipf_tokens); ipf_locks_done = 0; } SPL_X(s); return 0; } /* * Filter ioctl interface. */ int iplioctl(dev, cmd, data, mode # if defined(_KERNEL) && ((BSD >= 199506) || (__FreeBSD_version >= 220000)) , p) # if (__FreeBSD_version >= 500024) struct thread *p; # if (__FreeBSD_version >= 500043) # define p_uid td_ucred->cr_ruid # else # define p_uid t_proc->p_cred->p_ruid # endif # else struct proc *p; # define p_uid p_cred->p_ruid # endif /* __FreeBSD_version >= 500024 */ # else ) # endif #if defined(_KERNEL) && (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif ioctlcmd_t cmd; caddr_t data; int mode; { int error = 0, unit = 0; SPL_INT(s); #if (BSD >= 199306) && defined(_KERNEL) if ((securelevel >= 3) && (mode & FWRITE)) return EPERM; #endif unit = GET_MINOR(dev); if ((IPL_LOGMAX < unit) || (unit < 0)) return ENXIO; if (fr_running <= 0) { if (unit != IPL_LOGIPF) return EIO; if (cmd != SIOCIPFGETNEXT && cmd != SIOCIPFGET && cmd != SIOCIPFSET && cmd != SIOCFRENB && cmd != SIOCGETFS && cmd != SIOCGETFF) return EIO; } SPL_NET(s); error = fr_ioctlswitch(unit, data, cmd, mode, p->p_uid, p); if (error != -1) { SPL_X(s); return error; } SPL_X(s); return error; } #if 0 void fr_forgetifp(ifp) void *ifp; { register frentry_t *f; WRITE_ENTER(&ipf_mutex); for (f = ipacct[0][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = ipacct[1][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = ipfilter[0][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = ipfilter[1][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; #ifdef USE_INET6 for (f = ipacct6[0][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = ipacct6[1][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = ipfilter6[0][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; for (f = ipfilter6[1][fr_active]; (f != NULL); f = f->fr_next) if (f->fr_ifa == ifp) f->fr_ifa = (void *)-1; #endif RWLOCK_EXIT(&ipf_mutex); fr_natsync(ifp); } #endif /* * routines below for saving IP headers to buffer */ int iplopen(dev, flags #if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) , devtype, p) int devtype; # if (__FreeBSD_version >= 500024) struct thread *p; # else struct proc *p; # endif /* __FreeBSD_version >= 500024 */ #else ) #endif #if defined(_KERNEL) && (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif int flags; { u_int min = GET_MINOR(dev); if (IPL_LOGMAX < min) min = ENXIO; else min = 0; return min; } int iplclose(dev, flags #if ((BSD >= 199506) || (__FreeBSD_version >= 220000)) && defined(_KERNEL) , devtype, p) int devtype; # if (__FreeBSD_version >= 500024) struct thread *p; # else struct proc *p; # endif /* __FreeBSD_version >= 500024 */ #else ) #endif #if defined(_KERNEL) && (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif int flags; { u_int min = GET_MINOR(dev); if (IPL_LOGMAX < min) min = ENXIO; else min = 0; return min; } /* * iplread/ipllog * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ #if (BSD >= 199306) int iplread(dev, uio, ioflag) int ioflag; #else int iplread(dev, uio) #endif #if defined(_KERNEL) && (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif register struct uio *uio; { u_int xmin = GET_MINOR(dev); if (fr_running < 1) return EIO; if (xmin < 0) return ENXIO; # ifdef IPFILTER_SYNC if (xmin == IPL_LOGSYNC) return ipfsync_read(uio); # endif #ifdef IPFILTER_LOG return ipflog_read(xmin, uio); #else return ENXIO; #endif } /* * iplwrite * both of these must operate with at least splnet() lest they be * called during packet processing and cause an inconsistancy to appear in * the filter lists. */ #if (BSD >= 199306) int iplwrite(dev, uio, ioflag) int ioflag; #else int iplwrite(dev, uio) #endif #if defined(_KERNEL) && (__FreeBSD_version >= 502116) struct cdev *dev; #else dev_t dev; #endif register struct uio *uio; { if (fr_running < 1) return EIO; #ifdef IPFILTER_SYNC if (GET_MINOR(dev) == IPL_LOGSYNC) return ipfsync_write(uio); #endif return ENXIO; } /* * fr_send_reset - this could conceivably be a call to tcp_respond(), but that * requires a large amount of setting up and isn't any more efficient. */ int fr_send_reset(fin) fr_info_t *fin; { struct tcphdr *tcp, *tcp2; int tlen = 0, hlen; struct mbuf *m; #ifdef USE_INET6 ip6_t *ip6; #endif ip_t *ip; tcp = fin->fin_dp; if (tcp->th_flags & TH_RST) return -1; /* feedback loop */ if (fr_checkl4sum(fin) == -1) return -1; tlen = fin->fin_dlen - (TCP_OFF(tcp) << 2) + ((tcp->th_flags & TH_SYN) ? 1 : 0) + ((tcp->th_flags & TH_FIN) ? 1 : 0); #ifdef USE_INET6 hlen = (fin->fin_v == 6) ? sizeof(ip6_t) : sizeof(ip_t); #else hlen = sizeof(ip_t); #endif #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); #else MGET(m, M_DONTWAIT, MT_HEADER); #endif if (m == NULL) return -1; if (sizeof(*tcp2) + hlen > MLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { FREE_MB_T(m); return -1; } } m->m_len = sizeof(*tcp2) + hlen; #if (BSD >= 199103) m->m_data += max_linkhdr; m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = (struct ifnet *)0; #endif ip = mtod(m, struct ip *); bzero((char *)ip, hlen); #ifdef USE_INET6 ip6 = (ip6_t *)ip; #endif tcp2 = (struct tcphdr *)((char *)ip + hlen); tcp2->th_sport = tcp->th_dport; tcp2->th_dport = tcp->th_sport; if (tcp->th_flags & TH_ACK) { tcp2->th_seq = tcp->th_ack; tcp2->th_flags = TH_RST; tcp2->th_ack = 0; } else { tcp2->th_seq = 0; tcp2->th_ack = ntohl(tcp->th_seq); tcp2->th_ack += tlen; tcp2->th_ack = htonl(tcp2->th_ack); tcp2->th_flags = TH_RST|TH_ACK; } TCP_X2_A(tcp2, 0); TCP_OFF_A(tcp2, sizeof(*tcp2) >> 2); tcp2->th_win = tcp->th_win; tcp2->th_sum = 0; tcp2->th_urp = 0; #ifdef USE_INET6 if (fin->fin_v == 6) { ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; ip6->ip6_plen = htons(sizeof(struct tcphdr)); ip6->ip6_nxt = IPPROTO_TCP; ip6->ip6_hlim = 0; ip6->ip6_src = fin->fin_dst6; ip6->ip6_dst = fin->fin_src6; tcp2->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*ip6), sizeof(*tcp2)); return fr_send_ip(fin, m, &m); } #endif ip->ip_p = IPPROTO_TCP; ip->ip_len = htons(sizeof(struct tcphdr)); ip->ip_src.s_addr = fin->fin_daddr; ip->ip_dst.s_addr = fin->fin_saddr; tcp2->th_sum = in_cksum(m, hlen + sizeof(*tcp2)); ip->ip_len = hlen + sizeof(*tcp2); return fr_send_ip(fin, m, &m); } static int fr_send_ip(fin, m, mpp) fr_info_t *fin; mb_t *m, **mpp; { fr_info_t fnew; ip_t *ip, *oip; int hlen; ip = mtod(m, ip_t *); bzero((char *)&fnew, sizeof(fnew)); IP_V_A(ip, fin->fin_v); switch (fin->fin_v) { case 4 : fnew.fin_v = 4; oip = fin->fin_ip; IP_HL_A(ip, sizeof(*oip) >> 2); ip->ip_tos = oip->ip_tos; ip->ip_id = fin->fin_ip->ip_id; #if (__FreeBSD_version > 460000) ip->ip_off = V_path_mtu_discovery ? IP_DF : 0; #else ip->ip_off = 0; #endif ip->ip_ttl = V_ip_defttl; ip->ip_sum = 0; hlen = sizeof(*oip); break; #ifdef USE_INET6 case 6 : { ip6_t *ip6 = (ip6_t *)ip; ip6->ip6_vfc = 0x60; ip6->ip6_hlim = IPDEFTTL; fnew.fin_v = 6; hlen = sizeof(*ip6); break; } #endif default : return EINVAL; } #ifdef IPSEC m->m_pkthdr.rcvif = NULL; #endif fnew.fin_ifp = fin->fin_ifp; fnew.fin_flx = FI_NOCKSUM; fnew.fin_m = m; fnew.fin_ip = ip; fnew.fin_mp = mpp; fnew.fin_hlen = hlen; fnew.fin_dp = (char *)ip + hlen; (void) fr_makefrip(hlen, ip, &fnew); return fr_fastroute(m, mpp, &fnew, NULL); } int fr_send_icmp_err(type, fin, dst) int type; fr_info_t *fin; int dst; { int err, hlen, xtra, iclen, ohlen, avail, code; struct in_addr dst4; struct icmp *icmp; struct mbuf *m; void *ifp; #ifdef USE_INET6 ip6_t *ip6; struct in6_addr dst6; #endif ip_t *ip, *ip2; if ((type < 0) || (type >= ICMP_MAXTYPE)) return -1; code = fin->fin_icode; #ifdef USE_INET6 if ((code < 0) || (code > sizeof(icmptoicmp6unreach)/sizeof(int))) return -1; #endif if (fr_checkl4sum(fin) == -1) return -1; #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); #else MGET(m, M_DONTWAIT, MT_HEADER); #endif if (m == NULL) return -1; avail = MHLEN; xtra = 0; hlen = 0; ohlen = 0; ifp = fin->fin_ifp; if (fin->fin_v == 4) { if ((fin->fin_p == IPPROTO_ICMP) && !(fin->fin_flx & FI_SHORT)) switch (ntohs(fin->fin_data[0]) >> 8) { case ICMP_ECHO : case ICMP_TSTAMP : case ICMP_IREQ : case ICMP_MASKREQ : break; default : FREE_MB_T(m); return 0; } if (dst == 0) { if (fr_ifpaddr(4, FRI_NORMAL, ifp, &dst4, NULL) == -1) { FREE_MB_T(m); return -1; } } else dst4.s_addr = fin->fin_daddr; hlen = sizeof(ip_t); ohlen = fin->fin_hlen; if (fin->fin_hlen < fin->fin_plen) xtra = MIN(fin->fin_dlen, 8); else xtra = 0; } #ifdef USE_INET6 else if (fin->fin_v == 6) { hlen = sizeof(ip6_t); ohlen = sizeof(ip6_t); type = icmptoicmp6types[type]; if (type == ICMP6_DST_UNREACH) code = icmptoicmp6unreach[code]; if (hlen + sizeof(*icmp) + max_linkhdr + fin->fin_plen > avail) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { FREE_MB_T(m); return -1; } avail = MCLBYTES; } xtra = MIN(fin->fin_plen, avail - hlen - sizeof(*icmp) - max_linkhdr); if (dst == 0) { if (fr_ifpaddr(6, FRI_NORMAL, ifp, (struct in_addr *)&dst6, NULL) == -1) { FREE_MB_T(m); return -1; } } else dst6 = fin->fin_dst6; } #endif else { FREE_MB_T(m); return -1; } iclen = hlen + sizeof(*icmp); avail -= (max_linkhdr + iclen); if (avail < 0) { FREE_MB_T(m); return -1; } if (xtra > avail) xtra = avail; iclen += xtra; m->m_data += max_linkhdr; m->m_pkthdr.rcvif = (struct ifnet *)0; m->m_pkthdr.len = iclen; m->m_len = iclen; ip = mtod(m, ip_t *); icmp = (struct icmp *)((char *)ip + hlen); ip2 = (ip_t *)&icmp->icmp_ip; icmp->icmp_type = type; icmp->icmp_code = fin->fin_icode; icmp->icmp_cksum = 0; #ifdef icmp_nextmtu if (type == ICMP_UNREACH && fin->fin_icode == ICMP_UNREACH_NEEDFRAG && ifp) icmp->icmp_nextmtu = htons(((struct ifnet *)ifp)->if_mtu); #endif bcopy((char *)fin->fin_ip, (char *)ip2, ohlen); #ifdef USE_INET6 ip6 = (ip6_t *)ip; if (fin->fin_v == 6) { ip6->ip6_flow = ((ip6_t *)fin->fin_ip)->ip6_flow; ip6->ip6_plen = htons(iclen - hlen); ip6->ip6_nxt = IPPROTO_ICMPV6; ip6->ip6_hlim = 0; ip6->ip6_src = dst6; ip6->ip6_dst = fin->fin_src6; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), iclen - hlen); } else #endif { ip2->ip_len = htons(ip2->ip_len); ip2->ip_off = htons(ip2->ip_off); ip->ip_p = IPPROTO_ICMP; ip->ip_src.s_addr = dst4.s_addr; ip->ip_dst.s_addr = fin->fin_saddr; if (xtra > 0) bcopy((char *)fin->fin_ip + ohlen, (char *)&icmp->icmp_ip + ohlen, xtra); icmp->icmp_cksum = ipf_cksum((u_short *)icmp, sizeof(*icmp) + 8); ip->ip_len = iclen; ip->ip_p = IPPROTO_ICMP; } err = fr_send_ip(fin, m, &m); return err; } #if !defined(IPFILTER_LKM) && (__FreeBSD_version < 300000) # if (BSD < 199306) int iplinit __P((void)); int # else void iplinit __P((void)); void # endif iplinit() { if (ipfattach() != 0) printf("IP Filter failed to attach\n"); ip_init(); } #endif /* __FreeBSD_version < 300000 */ /* * m0 - pointer to mbuf where the IP packet starts * mpp - pointer to the mbuf pointer that is the start of the mbuf chain */ int fr_fastroute(m0, mpp, fin, fdp) mb_t *m0, **mpp; fr_info_t *fin; frdest_t *fdp; { register struct ip *ip, *mhip; register struct mbuf *m = *mpp; register struct route *ro; int len, off, error = 0, hlen, code; struct ifnet *ifp, *sifp; struct sockaddr_in *dst; struct route iproute; u_short ip_off; frentry_t *fr; ro = NULL; #ifdef M_WRITABLE /* * HOT FIX/KLUDGE: * * If the mbuf we're about to send is not writable (because of * a cluster reference, for example) we'll need to make a copy * of it since this routine modifies the contents. * * If you have non-crappy network hardware that can transmit data * from the mbuf, rather than making a copy, this is gonna be a * problem. */ if (M_WRITABLE(m) == 0) { m0 = m_dup(m, M_DONTWAIT); if (m0 != 0) { FREE_MB_T(m); m = m0; *mpp = m; } else { error = ENOBUFS; FREE_MB_T(m); goto done; } } #endif #ifdef USE_INET6 if (fin->fin_v == 6) { /* * currently "to " and "to :ip#" are not supported * for IPv6 */ #if (__FreeBSD_version >= 490000) return ip6_output(m0, NULL, NULL, 0, NULL, NULL, NULL); #else return ip6_output(m0, NULL, NULL, 0, NULL, NULL); #endif } #endif hlen = fin->fin_hlen; ip = mtod(m0, struct ip *); /* * Route packet. */ ro = &iproute; bzero((caddr_t)ro, sizeof (*ro)); dst = (struct sockaddr_in *)&ro->ro_dst; dst->sin_family = AF_INET; dst->sin_addr = ip->ip_dst; fr = fin->fin_fr; if (fdp != NULL) ifp = fdp->fd_ifp; else ifp = fin->fin_ifp; if ((ifp == NULL) && (!fr || !(fr->fr_flags & FR_FASTROUTE))) { error = -2; goto bad; } if ((fdp != NULL) && (fdp->fd_ip.s_addr != 0)) dst->sin_addr = fdp->fd_ip; dst->sin_len = sizeof(*dst); in_rtalloc(ro, 0); if ((ifp == NULL) && (ro->ro_rt != NULL)) ifp = ro->ro_rt->rt_ifp; if ((ro->ro_rt == NULL) || (ifp == NULL)) { if (in_localaddr(ip->ip_dst)) error = EHOSTUNREACH; else error = ENETUNREACH; goto bad; } if (ro->ro_rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in *)ro->ro_rt->rt_gateway; if (ro->ro_rt) ro->ro_rt->rt_use++; /* * For input packets which are being "fastrouted", they won't * go back through output filtering and miss their chance to get * NAT'd and counted. Duplicated packets aren't considered to be * part of the normal packet stream, so do not NAT them or pass * them through stateful checking, etc. */ if ((fdp != &fr->fr_dif) && (fin->fin_out == 0)) { sifp = fin->fin_ifp; fin->fin_ifp = ifp; fin->fin_out = 1; (void) fr_acctpkt(fin, NULL); fin->fin_fr = NULL; if (!fr || !(fr->fr_flags & FR_RETMASK)) { u_32_t pass; if (fr_checkstate(fin, &pass) != NULL) fr_statederef((ipstate_t **)&fin->fin_state); } switch (fr_checknatout(fin, NULL)) { case 0 : break; case 1 : fr_natderef((nat_t **)&fin->fin_nat); ip->ip_sum = 0; break; case -1 : error = -1; goto bad; break; } fin->fin_ifp = sifp; fin->fin_out = 0; } else ip->ip_sum = 0; /* * If small enough for interface, can just send directly. */ if (ip->ip_len <= ifp->if_mtu) { ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); goto done; } /* * Too large for interface; fragment if possible. * Must be able to put at least 8 bytes per fragment. */ ip_off = ntohs(ip->ip_off); if (ip_off & IP_DF) { error = EMSGSIZE; goto bad; } len = (ifp->if_mtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; } { int mhlen, firstlen = len; struct mbuf **mnext = &m->m_act; /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < ip->ip_len; off += len) { #ifdef MGETHDR MGETHDR(m, M_DONTWAIT, MT_HEADER); #else MGET(m, M_DONTWAIT, MT_HEADER); #endif if (m == 0) { m = m0; error = ENOBUFS; goto bad; } m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); bcopy((char *)ip, (char *)mhip, sizeof(*ip)); if (hlen > sizeof (struct ip)) { mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); IP_HL_A(mhip, mhlen >> 2); } m->m_len = mhlen; mhip->ip_off = ((off - hlen) >> 3) + ip_off; if (off + len >= ip->ip_len) len = ip->ip_len - off; else mhip->ip_off |= IP_MF; mhip->ip_len = htons((u_short)(len + mhlen)); *mnext = m; m->m_next = m_copy(m0, off, len); if (m->m_next == 0) { error = ENOBUFS; /* ??? */ goto sendorfree; } m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = NULL; mhip->ip_off = htons((u_short)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); mnext = &m->m_act; } /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m_adj(m0, hlen + firstlen - ip->ip_len); ip->ip_len = htons((u_short)(hlen + firstlen)); ip->ip_off = htons((u_short)IP_MF); ip->ip_sum = 0; ip->ip_sum = in_cksum(m0, hlen); sendorfree: for (m = m0; m; m = m0) { m0 = m->m_act; m->m_act = 0; if (error == 0) error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); else FREE_MB_T(m); } } done: if (!error) fr_frouteok[0]++; else fr_frouteok[1]++; if ((ro != NULL) && (ro->ro_rt != NULL)) { RTFREE(ro->ro_rt); } *mpp = NULL; return 0; bad: if (error == EMSGSIZE) { sifp = fin->fin_ifp; code = fin->fin_icode; fin->fin_icode = ICMP_UNREACH_NEEDFRAG; fin->fin_ifp = ifp; (void) fr_send_icmp_err(ICMP_UNREACH, fin, 1); fin->fin_ifp = sifp; fin->fin_icode = code; } FREE_MB_T(m); goto done; } int fr_verifysrc(fin) fr_info_t *fin; { struct sockaddr_in *dst; struct route iproute; bzero((char *)&iproute, sizeof(iproute)); dst = (struct sockaddr_in *)&iproute.ro_dst; dst->sin_len = sizeof(*dst); dst->sin_family = AF_INET; dst->sin_addr = fin->fin_src; in_rtalloc(&iproute, 0); if (iproute.ro_rt == NULL) return 0; return (fin->fin_ifp == iproute.ro_rt->rt_ifp); } /* * return the first IP Address associated with an interface */ int fr_ifpaddr(v, atype, ifptr, inp, inpmask) int v, atype; void *ifptr; struct in_addr *inp, *inpmask; { #ifdef USE_INET6 struct in6_addr *inp6 = NULL; #endif struct sockaddr *sock, *mask; struct sockaddr_in *sin; struct ifaddr *ifa; struct ifnet *ifp; if ((ifptr == NULL) || (ifptr == (void *)-1)) return -1; sin = NULL; ifp = ifptr; if (v == 4) inp->s_addr = 0; #ifdef USE_INET6 else if (v == 6) bzero((char *)inp, sizeof(struct in6_addr)); #endif #if (__FreeBSD_version >= 300000) ifa = TAILQ_FIRST(&ifp->if_addrhead); #else ifa = ifp->if_addrlist; #endif /* __FreeBSD_version >= 300000 */ sock = ifa->ifa_addr; while (sock != NULL && ifa != NULL) { sin = (struct sockaddr_in *)sock; if ((v == 4) && (sin->sin_family == AF_INET)) break; #ifdef USE_INET6 if ((v == 6) && (sin->sin_family == AF_INET6)) { inp6 = &((struct sockaddr_in6 *)sin)->sin6_addr; if (!IN6_IS_ADDR_LINKLOCAL(inp6) && !IN6_IS_ADDR_LOOPBACK(inp6)) break; } #endif #if (__FreeBSD_version >= 300000) ifa = TAILQ_NEXT(ifa, ifa_link); #else ifa = ifa->ifa_next; #endif /* __FreeBSD_version >= 300000 */ if (ifa != NULL) sock = ifa->ifa_addr; } if (ifa == NULL || sin == NULL) return -1; mask = ifa->ifa_netmask; if (atype == FRI_BROADCAST) sock = ifa->ifa_broadaddr; else if (atype == FRI_PEERADDR) sock = ifa->ifa_dstaddr; if (sock == NULL) return -1; #ifdef USE_INET6 if (v == 6) { return fr_ifpfillv6addr(atype, (struct sockaddr_in6 *)sock, (struct sockaddr_in6 *)mask, inp, inpmask); } #endif return fr_ifpfillv4addr(atype, (struct sockaddr_in *)sock, (struct sockaddr_in *)mask, inp, inpmask); } u_32_t fr_newisn(fin) fr_info_t *fin; { u_32_t newiss; #if (__FreeBSD_version >= 400000) newiss = arc4random(); #else static iss_seq_off = 0; u_char hash[16]; MD5_CTX ctx; /* * Compute the base value of the ISS. It is a hash * of (saddr, sport, daddr, dport, secret). */ MD5Init(&ctx); MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src, sizeof(fin->fin_fi.fi_src)); MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst, sizeof(fin->fin_fi.fi_dst)); MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat)); MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret)); MD5Final(hash, &ctx); memcpy(&newiss, hash, sizeof(newiss)); /* * Now increment our "timer", and add it in to * the computed value. * * XXX Use `addin'? * XXX TCP_ISSINCR too large to use? */ iss_seq_off += 0x00010000; newiss += iss_seq_off; #endif return newiss; } /* ------------------------------------------------------------------------ */ /* Function: fr_nextipid */ /* Returns: int - 0 == success, -1 == error (packet should be droppped) */ /* Parameters: fin(I) - pointer to packet information */ /* */ /* Returns the next IPv4 ID to use for this packet. */ /* ------------------------------------------------------------------------ */ u_short fr_nextipid(fin) fr_info_t *fin; { #ifndef RANDOM_IP_ID static u_short ipid = 0; u_short id; MUTEX_ENTER(&ipf_rw); id = ipid++; MUTEX_EXIT(&ipf_rw); #else u_short id; id = ip_randomid(); #endif return id; } INLINE void fr_checkv4sum(fin) fr_info_t *fin; { #ifdef CSUM_DATA_VALID int manual = 0; u_short sum; ip_t *ip; mb_t *m; if ((fin->fin_flx & FI_NOCKSUM) != 0) return; if (fin->fin_cksum != 0) return; m = fin->fin_m; if (m == NULL) { manual = 1; goto skipauto; } ip = fin->fin_ip; if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) { if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) sum = m->m_pkthdr.csum_data; else sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htonl(m->m_pkthdr.csum_data + fin->fin_ip->ip_len + fin->fin_p)); sum ^= 0xffff; if (sum != 0) { fin->fin_flx |= FI_BAD; fin->fin_cksum = -1; } else { fin->fin_cksum = 1; } } else manual = 1; skipauto: # ifdef IPFILTER_CKSUM if (manual != 0) if (fr_checkl4sum(fin) == -1) fin->fin_flx |= FI_BAD; # else ; # endif #else # ifdef IPFILTER_CKSUM if (fr_checkl4sum(fin) == -1) fin->fin_flx |= FI_BAD; # endif #endif } #ifdef USE_INET6 INLINE void fr_checkv6sum(fin) fr_info_t *fin; { # ifdef IPFILTER_CKSUM if (fr_checkl4sum(fin) == -1) fin->fin_flx |= FI_BAD; # endif } #endif /* USE_INET6 */ size_t mbufchainlen(m0) struct mbuf *m0; { size_t len; if ((m0->m_flags & M_PKTHDR) != 0) { len = m0->m_pkthdr.len; } else { struct mbuf *m; for (m = m0, len = 0; m != NULL; m = m->m_next) len += m->m_len; } return len; } /* ------------------------------------------------------------------------ */ /* Function: fr_pullup */ /* Returns: NULL == pullup failed, else pointer to protocol header */ /* Parameters: m(I) - pointer to buffer where data packet starts */ /* fin(I) - pointer to packet information */ /* len(I) - number of bytes to pullup */ /* */ /* Attempt to move at least len bytes (from the start of the buffer) into a */ /* single buffer for ease of access. Operating system native functions are */ /* used to manage buffers - if necessary. If the entire packet ends up in */ /* a single buffer, set the FI_COALESCE flag even though fr_coalesce() has */ /* not been called. Both fin_ip and fin_dp are updated before exiting _IF_ */ /* and ONLY if the pullup succeeds. */ /* */ /* We assume that 'min' is a pointer to a buffer that is part of the chain */ /* of buffers that starts at *fin->fin_mp. */ /* ------------------------------------------------------------------------ */ void *fr_pullup(min, fin, len) mb_t *min; fr_info_t *fin; int len; { int out = fin->fin_out, dpoff, ipoff; mb_t *m = min; char *ip; if (m == NULL) return NULL; ip = (char *)fin->fin_ip; if ((fin->fin_flx & FI_COALESCE) != 0) return ip; ipoff = fin->fin_ipoff; if (fin->fin_dp != NULL) dpoff = (char *)fin->fin_dp - (char *)ip; else dpoff = 0; if (M_LEN(m) < len) { #ifdef MHLEN /* * Assume that M_PKTHDR is set and just work with what is left * rather than check.. * Should not make any real difference, anyway. */ if (len > MHLEN) #else if (len > MLEN) #endif { #ifdef HAVE_M_PULLDOWN if (m_pulldown(m, 0, len, NULL) == NULL) m = NULL; #else FREE_MB_T(*fin->fin_mp); m = NULL; #endif } else { m = m_pullup(m, len); } *fin->fin_mp = m; if (m == NULL) { fin->fin_m = NULL; ATOMIC_INCL(frstats[out].fr_pull[1]); return NULL; } while (M_LEN(m) == 0) { m = m->m_next; } fin->fin_m = m; ip = MTOD(m, char *) + ipoff; } ATOMIC_INCL(frstats[out].fr_pull[0]); fin->fin_ip = (ip_t *)ip; if (fin->fin_dp != NULL) fin->fin_dp = (char *)fin->fin_ip + dpoff; if (len == fin->fin_plen) fin->fin_flx |= FI_COALESCE; return ip; } int ipf_inject(fin, m) fr_info_t *fin; mb_t *m; { int error = 0; if (fin->fin_out == 0) { #if (__FreeBSD_version >= 501000) netisr_dispatch(NETISR_IP, m); #else struct ifqueue *ifq; ifq = &ipintrq; # ifdef _IF_QFULL if (_IF_QFULL(ifq)) # else if (IF_QFULL(ifq)) # endif { # ifdef _IF_DROP _IF_DROP(ifq); # else IF_DROP(ifq); # endif FREE_MB_T(m); error = ENOBUFS; } else { IF_ENQUEUE(ifq, m); } #endif } else { fin->fin_ip->ip_len = ntohs(fin->fin_ip->ip_len); fin->fin_ip->ip_off = ntohs(fin->fin_ip->ip_off); #if (__FreeBSD_version >= 470102) error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL); #else error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL); #endif } return error; } int ipf_pfil_unhook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) # if __FreeBSD_version >= 501108 struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif # endif #endif #ifdef NETBSD_PF # if (__FreeBSD_version >= 500011) # if (__FreeBSD_version >= 501108) ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); if (ph_inet != NULL) pfil_remove_hook((void *)fr_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # else pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); # endif # else pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK); # endif # ifdef USE_INET6 # if (__FreeBSD_version >= 501108) ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); if (ph_inet6 != NULL) pfil_remove_hook((void *)fr_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # else pfil_remove_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); # endif # endif #endif return (0); } int ipf_pfil_hook(void) { #if defined(NETBSD_PF) && (__FreeBSD_version >= 500011) # if __FreeBSD_version >= 501108 struct pfil_head *ph_inet; # ifdef USE_INET6 struct pfil_head *ph_inet6; # endif # endif #endif # ifdef NETBSD_PF # if __FreeBSD_version >= 500011 # if __FreeBSD_version >= 501108 ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); # ifdef USE_INET6 ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); # endif if (ph_inet == NULL # ifdef USE_INET6 && ph_inet6 == NULL # endif ) return ENODEV; if (ph_inet != NULL) pfil_add_hook((void *)fr_check_wrapper, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet); # else pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); # endif # else pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK); # endif # ifdef USE_INET6 # if __FreeBSD_version >= 501108 if (ph_inet6 != NULL) pfil_add_hook((void *)fr_check_wrapper6, NULL, PFIL_IN|PFIL_OUT|PFIL_WAITOK, ph_inet6); # else pfil_add_hook((void *)fr_check, PFIL_IN|PFIL_OUT|PFIL_WAITOK, &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); # endif # endif # endif return (0); } void ipf_event_reg(void) { #if (__FreeBSD_version >= 502103) ipf_arrivetag = EVENTHANDLER_REGISTER(ifnet_arrival_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); ipf_departtag = EVENTHANDLER_REGISTER(ifnet_departure_event, \ ipf_ifevent, NULL, \ EVENTHANDLER_PRI_ANY); ipf_clonetag = EVENTHANDLER_REGISTER(if_clone_event, ipf_ifevent, \ NULL, EVENTHANDLER_PRI_ANY); #endif } void ipf_event_dereg(void) { #if (__FreeBSD_version >= 502103) if (ipf_arrivetag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ipf_arrivetag); } if (ipf_departtag != NULL) { EVENTHANDLER_DEREGISTER(ifnet_departure_event, ipf_departtag); } if (ipf_clonetag != NULL) { EVENTHANDLER_DEREGISTER(if_clone_event, ipf_clonetag); } #endif } Index: projects/cambria/sys/dev/agp/agp.c =================================================================== --- projects/cambria/sys/dev/agp/agp.c (revision 186459) +++ projects/cambria/sys/dev/agp/agp.c (revision 186460) @@ -1,976 +1,978 @@ /*- * Copyright (c) 2000 Doug Rabson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_VERSION(agp, 1); MALLOC_DEFINE(M_AGP, "agp", "AGP data structures"); /* agp_drv.c */ static d_open_t agp_open; static d_close_t agp_close; static d_ioctl_t agp_ioctl; static d_mmap_t agp_mmap; static struct cdevsw agp_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = agp_open, .d_close = agp_close, .d_ioctl = agp_ioctl, .d_mmap = agp_mmap, .d_name = "agp", }; static devclass_t agp_devclass; #define KDEV2DEV(kdev) devclass_get_device(agp_devclass, dev2unit(kdev)) /* Helper functions for implementing chipset mini drivers. */ void agp_flush_cache() { #if defined(__i386__) || defined(__amd64__) wbinvd(); #endif } u_int8_t agp_find_caps(device_t dev) { int capreg; if (pci_find_extcap(dev, PCIY_AGP, &capreg) != 0) capreg = 0; return (capreg); } /* * Find an AGP display device (if any). */ static device_t agp_find_display(void) { devclass_t pci = devclass_find("pci"); device_t bus, dev = 0; device_t *kids; int busnum, numkids, i; for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) { bus = devclass_get_device(pci, busnum); if (!bus) continue; if (device_get_children(bus, &kids, &numkids) != 0) continue; for (i = 0; i < numkids; i++) { dev = kids[i]; if (pci_get_class(dev) == PCIC_DISPLAY && pci_get_subclass(dev) == PCIS_DISPLAY_VGA) if (agp_find_caps(dev)) { free(kids, M_TEMP); return dev; } } free(kids, M_TEMP); } return 0; } struct agp_gatt * agp_alloc_gatt(device_t dev) { u_int32_t apsize = AGP_GET_APERTURE(dev); u_int32_t entries = apsize >> AGP_PAGE_SHIFT; struct agp_gatt *gatt; if (bootverbose) device_printf(dev, "allocating GATT for aperture of size %dM\n", apsize / (1024*1024)); if (entries == 0) { device_printf(dev, "bad aperture size\n"); return NULL; } gatt = malloc(sizeof(struct agp_gatt), M_AGP, M_NOWAIT); if (!gatt) return 0; gatt->ag_entries = entries; gatt->ag_virtual = contigmalloc(entries * sizeof(u_int32_t), M_AGP, 0, 0, ~0, PAGE_SIZE, 0); if (!gatt->ag_virtual) { if (bootverbose) device_printf(dev, "contiguous allocation failed\n"); free(gatt, M_AGP); return 0; } bzero(gatt->ag_virtual, entries * sizeof(u_int32_t)); gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); agp_flush_cache(); return gatt; } void agp_free_gatt(struct agp_gatt *gatt) { contigfree(gatt->ag_virtual, gatt->ag_entries * sizeof(u_int32_t), M_AGP); free(gatt, M_AGP); } static u_int agp_max[][2] = { {0, 0}, {32, 4}, {64, 28}, {128, 96}, {256, 204}, {512, 440}, {1024, 942}, {2048, 1920}, {4096, 3932} }; #define agp_max_size (sizeof(agp_max) / sizeof(agp_max[0])) /** * Sets the PCI resource which represents the AGP aperture. * * If not called, the default AGP aperture resource of AGP_APBASE will * be used. Must be called before agp_generic_attach(). */ void agp_set_aperture_resource(device_t dev, int rid) { struct agp_softc *sc = device_get_softc(dev); sc->as_aperture_rid = rid; } int agp_generic_attach(device_t dev) { struct agp_softc *sc = device_get_softc(dev); int i; u_int memsize; /* * Find and map the aperture, RF_SHAREABLE for DRM but not RF_ACTIVE * because the kernel doesn't need to map it. */ if (sc->as_aperture_rid == 0) sc->as_aperture_rid = AGP_APBASE; sc->as_aperture = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->as_aperture_rid, RF_SHAREABLE); if (!sc->as_aperture) return ENOMEM; /* * Work out an upper bound for agp memory allocation. This * uses a heurisitc table from the Linux driver. */ memsize = ptoa(Maxmem) >> 20; for (i = 0; i < agp_max_size; i++) { if (memsize <= agp_max[i][0]) break; } if (i == agp_max_size) i = agp_max_size - 1; sc->as_maxmem = agp_max[i][1] << 20U; /* * The lock is used to prevent re-entry to * agp_generic_bind_memory() since that function can sleep. */ mtx_init(&sc->as_lock, "agp lock", NULL, MTX_DEF); /* * Initialise stuff for the userland device. */ agp_devclass = devclass_find("agp"); TAILQ_INIT(&sc->as_memory); sc->as_nextid = 1; sc->as_devnode = make_dev(&agp_cdevsw, device_get_unit(dev), UID_ROOT, GID_WHEEL, 0600, "agpgart"); return 0; } void agp_free_cdev(device_t dev) { struct agp_softc *sc = device_get_softc(dev); destroy_dev(sc->as_devnode); } void agp_free_res(device_t dev) { struct agp_softc *sc = device_get_softc(dev); bus_release_resource(dev, SYS_RES_MEMORY, sc->as_aperture_rid, sc->as_aperture); mtx_destroy(&sc->as_lock); agp_flush_cache(); } int agp_generic_detach(device_t dev) { agp_free_cdev(dev); agp_free_res(dev); return 0; } /** * Default AGP aperture size detection which simply returns the size of * the aperture's PCI resource. */ int agp_generic_get_aperture(device_t dev) { struct agp_softc *sc = device_get_softc(dev); return rman_get_size(sc->as_aperture); } /** * Default AGP aperture size setting function, which simply doesn't allow * changes to resource size. */ int agp_generic_set_aperture(device_t dev, u_int32_t aperture) { u_int32_t current_aperture; current_aperture = AGP_GET_APERTURE(dev); if (current_aperture != aperture) return EINVAL; else return 0; } /* * This does the enable logic for v3, with the same topology * restrictions as in place for v2 -- one bus, one device on the bus. */ static int agp_v3_enable(device_t dev, device_t mdev, u_int32_t mode) { u_int32_t tstatus, mstatus; u_int32_t command; int rq, sba, fw, rate, arqsz, cal; tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); /* Set RQ to the min of mode, tstatus and mstatus */ rq = AGP_MODE_GET_RQ(mode); if (AGP_MODE_GET_RQ(tstatus) < rq) rq = AGP_MODE_GET_RQ(tstatus); if (AGP_MODE_GET_RQ(mstatus) < rq) rq = AGP_MODE_GET_RQ(mstatus); /* * ARQSZ - Set the value to the maximum one. * Don't allow the mode register to override values. */ arqsz = AGP_MODE_GET_ARQSZ(mode); if (AGP_MODE_GET_ARQSZ(tstatus) > rq) rq = AGP_MODE_GET_ARQSZ(tstatus); if (AGP_MODE_GET_ARQSZ(mstatus) > rq) rq = AGP_MODE_GET_ARQSZ(mstatus); /* Calibration cycle - don't allow override by mode register */ cal = AGP_MODE_GET_CAL(tstatus); if (AGP_MODE_GET_CAL(mstatus) < cal) cal = AGP_MODE_GET_CAL(mstatus); /* SBA must be supported for AGP v3. */ sba = 1; /* Set FW if all three support it. */ fw = (AGP_MODE_GET_FW(tstatus) & AGP_MODE_GET_FW(mstatus) & AGP_MODE_GET_FW(mode)); /* Figure out the max rate */ rate = (AGP_MODE_GET_RATE(tstatus) & AGP_MODE_GET_RATE(mstatus) & AGP_MODE_GET_RATE(mode)); if (rate & AGP_MODE_V3_RATE_8x) rate = AGP_MODE_V3_RATE_8x; else rate = AGP_MODE_V3_RATE_4x; if (bootverbose) device_printf(dev, "Setting AGP v3 mode %d\n", rate * 4); pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, 0, 4); /* Construct the new mode word and tell the hardware */ command = 0; command = AGP_MODE_SET_RQ(0, rq); command = AGP_MODE_SET_ARQSZ(command, arqsz); command = AGP_MODE_SET_CAL(command, cal); command = AGP_MODE_SET_SBA(command, sba); command = AGP_MODE_SET_FW(command, fw); command = AGP_MODE_SET_RATE(command, rate); command = AGP_MODE_SET_MODE_3(command, 1); command = AGP_MODE_SET_AGP(command, 1); pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); return 0; } static int agp_v2_enable(device_t dev, device_t mdev, u_int32_t mode) { u_int32_t tstatus, mstatus; u_int32_t command; int rq, sba, fw, rate; tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); /* Set RQ to the min of mode, tstatus and mstatus */ rq = AGP_MODE_GET_RQ(mode); if (AGP_MODE_GET_RQ(tstatus) < rq) rq = AGP_MODE_GET_RQ(tstatus); if (AGP_MODE_GET_RQ(mstatus) < rq) rq = AGP_MODE_GET_RQ(mstatus); /* Set SBA if all three can deal with SBA */ sba = (AGP_MODE_GET_SBA(tstatus) & AGP_MODE_GET_SBA(mstatus) & AGP_MODE_GET_SBA(mode)); /* Similar for FW */ fw = (AGP_MODE_GET_FW(tstatus) & AGP_MODE_GET_FW(mstatus) & AGP_MODE_GET_FW(mode)); /* Figure out the max rate */ rate = (AGP_MODE_GET_RATE(tstatus) & AGP_MODE_GET_RATE(mstatus) & AGP_MODE_GET_RATE(mode)); if (rate & AGP_MODE_V2_RATE_4x) rate = AGP_MODE_V2_RATE_4x; else if (rate & AGP_MODE_V2_RATE_2x) rate = AGP_MODE_V2_RATE_2x; else rate = AGP_MODE_V2_RATE_1x; if (bootverbose) device_printf(dev, "Setting AGP v2 mode %d\n", rate); /* Construct the new mode word and tell the hardware */ command = 0; command = AGP_MODE_SET_RQ(0, rq); command = AGP_MODE_SET_SBA(command, sba); command = AGP_MODE_SET_FW(command, fw); command = AGP_MODE_SET_RATE(command, rate); command = AGP_MODE_SET_AGP(command, 1); pci_write_config(dev, agp_find_caps(dev) + AGP_COMMAND, command, 4); pci_write_config(mdev, agp_find_caps(mdev) + AGP_COMMAND, command, 4); return 0; } int agp_generic_enable(device_t dev, u_int32_t mode) { device_t mdev = agp_find_display(); u_int32_t tstatus, mstatus; if (!mdev) { AGP_DPF("can't find display\n"); return ENXIO; } tstatus = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); mstatus = pci_read_config(mdev, agp_find_caps(mdev) + AGP_STATUS, 4); /* * Check display and bridge for AGP v3 support. AGP v3 allows * more variety in topology than v2, e.g. multiple AGP devices * attached to one bridge, or multiple AGP bridges in one * system. This doesn't attempt to address those situations, * but should work fine for a classic single AGP slot system * with AGP v3. */ if (AGP_MODE_GET_MODE_3(mode) && AGP_MODE_GET_MODE_3(tstatus) && AGP_MODE_GET_MODE_3(mstatus)) return (agp_v3_enable(dev, mdev, mode)); else return (agp_v2_enable(dev, mdev, mode)); } struct agp_memory * agp_generic_alloc_memory(device_t dev, int type, vm_size_t size) { struct agp_softc *sc = device_get_softc(dev); struct agp_memory *mem; if ((size & (AGP_PAGE_SIZE - 1)) != 0) return 0; if (sc->as_allocated + size > sc->as_maxmem) return 0; if (type != 0) { printf("agp_generic_alloc_memory: unsupported type %d\n", type); return 0; } mem = malloc(sizeof *mem, M_AGP, M_WAITOK); mem->am_id = sc->as_nextid++; mem->am_size = size; mem->am_type = 0; mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size))); mem->am_physical = 0; mem->am_offset = 0; mem->am_is_bound = 0; TAILQ_INSERT_TAIL(&sc->as_memory, mem, am_link); sc->as_allocated += size; return mem; } int agp_generic_free_memory(device_t dev, struct agp_memory *mem) { struct agp_softc *sc = device_get_softc(dev); if (mem->am_is_bound) return EBUSY; sc->as_allocated -= mem->am_size; TAILQ_REMOVE(&sc->as_memory, mem, am_link); vm_object_deallocate(mem->am_obj); free(mem, M_AGP); return 0; } int agp_generic_bind_memory(device_t dev, struct agp_memory *mem, vm_offset_t offset) { struct agp_softc *sc = device_get_softc(dev); vm_offset_t i, j, k; vm_page_t m; int error; /* Do some sanity checks first. */ if (offset < 0 || (offset & (AGP_PAGE_SIZE - 1)) != 0 || offset + mem->am_size > AGP_GET_APERTURE(dev)) { device_printf(dev, "binding memory at bad offset %#x\n", (int)offset); return EINVAL; } /* * Allocate the pages early, before acquiring the lock, * because vm_page_grab() used with VM_ALLOC_RETRY may * block and we can't hold a mutex while blocking. */ VM_OBJECT_LOCK(mem->am_obj); for (i = 0; i < mem->am_size; i += PAGE_SIZE) { /* * Find a page from the object and wire it * down. This page will be mapped using one or more * entries in the GATT (assuming that PAGE_SIZE >= * AGP_PAGE_SIZE. If this is the first call to bind, * the pages will be allocated and zeroed. */ m = vm_page_grab(mem->am_obj, OFF_TO_IDX(i), VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY); AGP_DPF("found page pa=%#x\n", VM_PAGE_TO_PHYS(m)); } VM_OBJECT_UNLOCK(mem->am_obj); mtx_lock(&sc->as_lock); if (mem->am_is_bound) { device_printf(dev, "memory already bound\n"); error = EINVAL; VM_OBJECT_LOCK(mem->am_obj); + i = 0; goto bad; } /* * Bind the individual pages and flush the chipset's * TLB. */ VM_OBJECT_LOCK(mem->am_obj); for (i = 0; i < mem->am_size; i += PAGE_SIZE) { m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(i)); /* * Install entries in the GATT, making sure that if * AGP_PAGE_SIZE < PAGE_SIZE and mem->am_size is not * aligned to PAGE_SIZE, we don't modify too many GATT * entries. */ for (j = 0; j < PAGE_SIZE && i + j < mem->am_size; j += AGP_PAGE_SIZE) { vm_offset_t pa = VM_PAGE_TO_PHYS(m) + j; AGP_DPF("binding offset %#x to pa %#x\n", offset + i + j, pa); error = AGP_BIND_PAGE(dev, offset + i + j, pa); if (error) { /* * Bail out. Reverse all the mappings * and unwire the pages. */ - vm_page_wakeup(m); for (k = 0; k < i + j; k += AGP_PAGE_SIZE) AGP_UNBIND_PAGE(dev, offset + k); goto bad; } } vm_page_wakeup(m); } VM_OBJECT_UNLOCK(mem->am_obj); /* * Flush the cpu cache since we are providing a new mapping * for these pages. */ agp_flush_cache(); /* * Make sure the chipset gets the new mappings. */ AGP_FLUSH_TLB(dev); mem->am_offset = offset; mem->am_is_bound = 1; mtx_unlock(&sc->as_lock); return 0; bad: mtx_unlock(&sc->as_lock); VM_OBJECT_LOCK_ASSERT(mem->am_obj, MA_OWNED); - for (i = 0; i < mem->am_size; i += PAGE_SIZE) { - m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(i)); + for (k = 0; k < mem->am_size; k += PAGE_SIZE) { + m = vm_page_lookup(mem->am_obj, OFF_TO_IDX(k)); + if (k >= i) + vm_page_wakeup(m); vm_page_lock_queues(); vm_page_unwire(m, 0); vm_page_unlock_queues(); } VM_OBJECT_UNLOCK(mem->am_obj); return error; } int agp_generic_unbind_memory(device_t dev, struct agp_memory *mem) { struct agp_softc *sc = device_get_softc(dev); vm_page_t m; int i; mtx_lock(&sc->as_lock); if (!mem->am_is_bound) { device_printf(dev, "memory is not bound\n"); mtx_unlock(&sc->as_lock); return EINVAL; } /* * Unbind the individual pages and flush the chipset's * TLB. Unwire the pages so they can be swapped. */ for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) AGP_UNBIND_PAGE(dev, mem->am_offset + i); VM_OBJECT_LOCK(mem->am_obj); for (i = 0; i < mem->am_size; i += PAGE_SIZE) { m = vm_page_lookup(mem->am_obj, atop(i)); vm_page_lock_queues(); vm_page_unwire(m, 0); vm_page_unlock_queues(); } VM_OBJECT_UNLOCK(mem->am_obj); agp_flush_cache(); AGP_FLUSH_TLB(dev); mem->am_offset = 0; mem->am_is_bound = 0; mtx_unlock(&sc->as_lock); return 0; } /* Helper functions for implementing user/kernel api */ static int agp_acquire_helper(device_t dev, enum agp_acquire_state state) { struct agp_softc *sc = device_get_softc(dev); if (sc->as_state != AGP_ACQUIRE_FREE) return EBUSY; sc->as_state = state; return 0; } static int agp_release_helper(device_t dev, enum agp_acquire_state state) { struct agp_softc *sc = device_get_softc(dev); if (sc->as_state == AGP_ACQUIRE_FREE) return 0; if (sc->as_state != state) return EBUSY; sc->as_state = AGP_ACQUIRE_FREE; return 0; } static struct agp_memory * agp_find_memory(device_t dev, int id) { struct agp_softc *sc = device_get_softc(dev); struct agp_memory *mem; AGP_DPF("searching for memory block %d\n", id); TAILQ_FOREACH(mem, &sc->as_memory, am_link) { AGP_DPF("considering memory block %d\n", mem->am_id); if (mem->am_id == id) return mem; } return 0; } /* Implementation of the userland ioctl api */ static int agp_info_user(device_t dev, agp_info *info) { struct agp_softc *sc = device_get_softc(dev); bzero(info, sizeof *info); info->bridge_id = pci_get_devid(dev); info->agp_mode = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); info->aper_base = rman_get_start(sc->as_aperture); info->aper_size = AGP_GET_APERTURE(dev) >> 20; info->pg_total = info->pg_system = sc->as_maxmem >> AGP_PAGE_SHIFT; info->pg_used = sc->as_allocated >> AGP_PAGE_SHIFT; return 0; } static int agp_setup_user(device_t dev, agp_setup *setup) { return AGP_ENABLE(dev, setup->agp_mode); } static int agp_allocate_user(device_t dev, agp_allocate *alloc) { struct agp_memory *mem; mem = AGP_ALLOC_MEMORY(dev, alloc->type, alloc->pg_count << AGP_PAGE_SHIFT); if (mem) { alloc->key = mem->am_id; alloc->physical = mem->am_physical; return 0; } else { return ENOMEM; } } static int agp_deallocate_user(device_t dev, int id) { struct agp_memory *mem = agp_find_memory(dev, id);; if (mem) { AGP_FREE_MEMORY(dev, mem); return 0; } else { return ENOENT; } } static int agp_bind_user(device_t dev, agp_bind *bind) { struct agp_memory *mem = agp_find_memory(dev, bind->key); if (!mem) return ENOENT; return AGP_BIND_MEMORY(dev, mem, bind->pg_start << AGP_PAGE_SHIFT); } static int agp_unbind_user(device_t dev, agp_unbind *unbind) { struct agp_memory *mem = agp_find_memory(dev, unbind->key); if (!mem) return ENOENT; return AGP_UNBIND_MEMORY(dev, mem); } static int agp_open(struct cdev *kdev, int oflags, int devtype, struct thread *td) { device_t dev = KDEV2DEV(kdev); struct agp_softc *sc = device_get_softc(dev); if (!sc->as_isopen) { sc->as_isopen = 1; device_busy(dev); } return 0; } static int agp_close(struct cdev *kdev, int fflag, int devtype, struct thread *td) { device_t dev = KDEV2DEV(kdev); struct agp_softc *sc = device_get_softc(dev); struct agp_memory *mem; /* * Clear the GATT and force release on last close */ while ((mem = TAILQ_FIRST(&sc->as_memory)) != 0) { if (mem->am_is_bound) AGP_UNBIND_MEMORY(dev, mem); AGP_FREE_MEMORY(dev, mem); } if (sc->as_state == AGP_ACQUIRE_USER) agp_release_helper(dev, AGP_ACQUIRE_USER); sc->as_isopen = 0; device_unbusy(dev); return 0; } static int agp_ioctl(struct cdev *kdev, u_long cmd, caddr_t data, int fflag, struct thread *td) { device_t dev = KDEV2DEV(kdev); switch (cmd) { case AGPIOC_INFO: return agp_info_user(dev, (agp_info *) data); case AGPIOC_ACQUIRE: return agp_acquire_helper(dev, AGP_ACQUIRE_USER); case AGPIOC_RELEASE: return agp_release_helper(dev, AGP_ACQUIRE_USER); case AGPIOC_SETUP: return agp_setup_user(dev, (agp_setup *)data); case AGPIOC_ALLOCATE: return agp_allocate_user(dev, (agp_allocate *)data); case AGPIOC_DEALLOCATE: return agp_deallocate_user(dev, *(int *) data); case AGPIOC_BIND: return agp_bind_user(dev, (agp_bind *)data); case AGPIOC_UNBIND: return agp_unbind_user(dev, (agp_unbind *)data); } return EINVAL; } static int agp_mmap(struct cdev *kdev, vm_offset_t offset, vm_paddr_t *paddr, int prot) { device_t dev = KDEV2DEV(kdev); struct agp_softc *sc = device_get_softc(dev); if (offset > AGP_GET_APERTURE(dev)) return -1; *paddr = rman_get_start(sc->as_aperture) + offset; return 0; } /* Implementation of the kernel api */ device_t agp_find_device() { device_t *children, child; int i, count; if (!agp_devclass) return NULL; if (devclass_get_devices(agp_devclass, &children, &count) != 0) return NULL; child = NULL; for (i = 0; i < count; i++) { if (device_is_attached(children[i])) { child = children[i]; break; } } free(children, M_TEMP); return child; } enum agp_acquire_state agp_state(device_t dev) { struct agp_softc *sc = device_get_softc(dev); return sc->as_state; } void agp_get_info(device_t dev, struct agp_info *info) { struct agp_softc *sc = device_get_softc(dev); info->ai_mode = pci_read_config(dev, agp_find_caps(dev) + AGP_STATUS, 4); info->ai_aperture_base = rman_get_start(sc->as_aperture); info->ai_aperture_size = rman_get_size(sc->as_aperture); info->ai_memory_allowed = sc->as_maxmem; info->ai_memory_used = sc->as_allocated; } int agp_acquire(device_t dev) { return agp_acquire_helper(dev, AGP_ACQUIRE_KERNEL); } int agp_release(device_t dev) { return agp_release_helper(dev, AGP_ACQUIRE_KERNEL); } int agp_enable(device_t dev, u_int32_t mode) { return AGP_ENABLE(dev, mode); } void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes) { return (void *) AGP_ALLOC_MEMORY(dev, type, bytes); } void agp_free_memory(device_t dev, void *handle) { struct agp_memory *mem = (struct agp_memory *) handle; AGP_FREE_MEMORY(dev, mem); } int agp_bind_memory(device_t dev, void *handle, vm_offset_t offset) { struct agp_memory *mem = (struct agp_memory *) handle; return AGP_BIND_MEMORY(dev, mem, offset); } int agp_unbind_memory(device_t dev, void *handle) { struct agp_memory *mem = (struct agp_memory *) handle; return AGP_UNBIND_MEMORY(dev, mem); } void agp_memory_info(device_t dev, void *handle, struct agp_memory_info *mi) { struct agp_memory *mem = (struct agp_memory *) handle; mi->ami_size = mem->am_size; mi->ami_physical = mem->am_physical; mi->ami_offset = mem->am_offset; mi->ami_is_bound = mem->am_is_bound; } Index: projects/cambria/sys/dev/agp/agp_i810.c =================================================================== --- projects/cambria/sys/dev/agp/agp_i810.c (revision 186459) +++ projects/cambria/sys/dev/agp/agp_i810.c (revision 186460) @@ -1,1122 +1,1129 @@ /*- * Copyright (c) 2000 Doug Rabson * Copyright (c) 2000 Ruslan Ermilov * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * Fixes for 830/845G support: David Dawes * 852GM/855GM/865G support added by David Dawes */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DECLARE(M_AGP); enum { CHIP_I810, /* i810/i815 */ CHIP_I830, /* 830M/845G */ CHIP_I855, /* 852GM/855GM/865G */ CHIP_I915, /* 915G/915GM */ CHIP_I965, /* G965 */ CHIP_G33, /* G33/Q33/Q35 */ CHIP_G4X, /* G45/Q45 */ }; /* The i810 through i855 have the registers at BAR 1, and the GATT gets * allocated by us. The i915 has registers in BAR 0 and the GATT is at the * start of the stolen memory, and should only be accessed by the OS through * BAR 3. The G965 has registers and GATT in the same BAR (0) -- first 512KB * is registers, second 512KB is GATT. */ static struct resource_spec agp_i810_res_spec[] = { { SYS_RES_MEMORY, AGP_I810_MMADR, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static struct resource_spec agp_i915_res_spec[] = { { SYS_RES_MEMORY, AGP_I915_MMADR, RF_ACTIVE | RF_SHAREABLE }, { SYS_RES_MEMORY, AGP_I915_GTTADR, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static struct resource_spec agp_i965_res_spec[] = { { SYS_RES_MEMORY, AGP_I965_GTTMMADR, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct agp_i810_softc { struct agp_softc agp; u_int32_t initial_aperture; /* aperture size at startup */ struct agp_gatt *gatt; int chiptype; /* i810-like or i830 */ u_int32_t dcache_size; /* i810 only */ u_int32_t stolen; /* number of i830/845 gtt entries for stolen memory */ device_t bdev; /* bridge device */ void *argb_cursor; /* contigmalloc area for ARGB cursor */ struct resource_spec * sc_res_spec; struct resource *sc_res[2]; }; /* For adding new devices, devid is the id of the graphics controller * (pci:0:2:0, for example). The placeholder (usually at pci:0:2:1) for the * second head should never be added. The bridge_offset is the offset to * subtract from devid to get the id of the hostb that the device is on. */ static const struct agp_i810_match { int devid; int chiptype; int bridge_offset; char *name; } agp_i810_matches[] = { {0x71218086, CHIP_I810, 0x00010000, "Intel 82810 (i810 GMCH) SVGA controller"}, {0x71238086, CHIP_I810, 0x00010000, "Intel 82810-DC100 (i810-DC100 GMCH) SVGA controller"}, {0x71258086, CHIP_I810, 0x00010000, "Intel 82810E (i810E GMCH) SVGA controller"}, {0x11328086, CHIP_I810, 0x00020000, "Intel 82815 (i815 GMCH) SVGA controller"}, {0x35778086, CHIP_I830, 0x00020000, "Intel 82830M (830M GMCH) SVGA controller"}, {0x25628086, CHIP_I830, 0x00020000, "Intel 82845M (845M GMCH) SVGA controller"}, {0x35828086, CHIP_I855, 0x00020000, "Intel 82852/855GM SVGA controller"}, {0x25728086, CHIP_I855, 0x00020000, "Intel 82865G (865G GMCH) SVGA controller"}, {0x25828086, CHIP_I915, 0x00020000, "Intel 82915G (915G GMCH) SVGA controller"}, {0x258A8086, CHIP_I915, 0x00020000, "Intel E7221 SVGA controller"}, {0x25928086, CHIP_I915, 0x00020000, "Intel 82915GM (915GM GMCH) SVGA controller"}, {0x27728086, CHIP_I915, 0x00020000, "Intel 82945G (945G GMCH) SVGA controller"}, {0x27A28086, CHIP_I915, 0x00020000, "Intel 82945GM (945GM GMCH) SVGA controller"}, {0x27AE8086, CHIP_I915, 0x00020000, "Intel 945GME SVGA controller"}, {0x29728086, CHIP_I965, 0x00020000, "Intel 946GZ SVGA controller"}, {0x29828086, CHIP_I965, 0x00020000, "Intel G965 SVGA controller"}, {0x29928086, CHIP_I965, 0x00020000, "Intel Q965 SVGA controller"}, {0x29A28086, CHIP_I965, 0x00020000, "Intel G965 SVGA controller"}, {0x29B28086, CHIP_G33, 0x00020000, "Intel Q35 SVGA controller"}, {0x29C28086, CHIP_G33, 0x00020000, "Intel G33 SVGA controller"}, {0x29D28086, CHIP_G33, 0x00020000, "Intel Q33 SVGA controller"}, {0x2A028086, CHIP_I965, 0x00020000, "Intel GM965 SVGA controller"}, {0x2A128086, CHIP_I965, 0x00020000, "Intel GME965 SVGA controller"}, - {0x2A428086, CHIP_I965, 0x00020000, + {0x2A428086, CHIP_G4X, 0x00020000, "Intel GM45 SVGA controller"}, {0x2E028086, CHIP_G4X, 0x00020000, "Intel 4 Series SVGA controller"}, {0x2E128086, CHIP_G4X, 0x00020000, "Intel Q45 SVGA controller"}, {0x2E228086, CHIP_G4X, 0x00020000, "Intel G45 SVGA controller"}, {0, 0, 0, NULL} }; static const struct agp_i810_match* agp_i810_match(device_t dev) { int i, devid; if (pci_get_class(dev) != PCIC_DISPLAY || pci_get_subclass(dev) != PCIS_DISPLAY_VGA) return NULL; devid = pci_get_devid(dev); for (i = 0; agp_i810_matches[i].devid != 0; i++) { if (agp_i810_matches[i].devid == devid) break; } if (agp_i810_matches[i].devid == 0) return NULL; else return &agp_i810_matches[i]; } /* * Find bridge device. */ static device_t agp_i810_find_bridge(device_t dev) { device_t *children, child; int nchildren, i; u_int32_t devid; const struct agp_i810_match *match; match = agp_i810_match(dev); devid = match->devid - match->bridge_offset; if (device_get_children(device_get_parent(device_get_parent(dev)), &children, &nchildren)) return 0; for (i = 0; i < nchildren; i++) { child = children[i]; if (pci_get_devid(child) == devid) { free(children, M_TEMP); return child; } } free(children, M_TEMP); return 0; } static void agp_i810_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "agp", -1) == NULL && agp_i810_match(parent)) device_add_child(parent, "agp", -1); } static int agp_i810_probe(device_t dev) { device_t bdev; const struct agp_i810_match *match; u_int8_t smram; int gcc1, deven; if (resource_disabled("agp", device_get_unit(dev))) return (ENXIO); match = agp_i810_match(dev); if (match == NULL) return ENXIO; bdev = agp_i810_find_bridge(dev); if (!bdev) { if (bootverbose) printf("I810: can't find bridge device\n"); return ENXIO; } /* * checking whether internal graphics device has been activated. */ switch (match->chiptype) { case CHIP_I810: smram = pci_read_config(bdev, AGP_I810_SMRAM, 1); if ((smram & AGP_I810_SMRAM_GMS) == AGP_I810_SMRAM_GMS_DISABLED) { if (bootverbose) printf("I810: disabled, not probing\n"); return ENXIO; } break; case CHIP_I830: case CHIP_I855: gcc1 = pci_read_config(bdev, AGP_I830_GCC1, 1); if ((gcc1 & AGP_I830_GCC1_DEV2) == AGP_I830_GCC1_DEV2_DISABLED) { if (bootverbose) printf("I830: disabled, not probing\n"); return ENXIO; } break; case CHIP_I915: case CHIP_I965: case CHIP_G33: + case CHIP_G4X: deven = pci_read_config(bdev, AGP_I915_DEVEN, 4); if ((deven & AGP_I915_DEVEN_D2F0) == AGP_I915_DEVEN_D2F0_DISABLED) { if (bootverbose) printf("I915: disabled, not probing\n"); return ENXIO; } break; } if (match->devid == 0x35828086) { switch (pci_read_config(dev, AGP_I85X_CAPID, 1)) { case AGP_I855_GME: device_set_desc(dev, "Intel 82855GME (855GME GMCH) SVGA controller"); break; case AGP_I855_GM: device_set_desc(dev, "Intel 82855GM (855GM GMCH) SVGA controller"); break; case AGP_I852_GME: device_set_desc(dev, "Intel 82852GME (852GME GMCH) SVGA controller"); break; case AGP_I852_GM: device_set_desc(dev, "Intel 82852GM (852GM GMCH) SVGA controller"); break; default: device_set_desc(dev, "Intel 8285xM (85xGM GMCH) SVGA controller"); break; } } else { device_set_desc(dev, match->name); } return BUS_PROBE_DEFAULT; } static void agp_i810_dump_regs(device_t dev) { struct agp_i810_softc *sc = device_get_softc(dev); device_printf(dev, "AGP_I810_PGTBL_CTL: %08x\n", bus_read_4(sc->sc_res[0], AGP_I810_PGTBL_CTL)); switch (sc->chiptype) { case CHIP_I810: device_printf(dev, "AGP_I810_MISCC: 0x%04x\n", pci_read_config(sc->bdev, AGP_I810_MISCC, 2)); break; case CHIP_I830: device_printf(dev, "AGP_I830_GCC1: 0x%02x\n", pci_read_config(sc->bdev, AGP_I830_GCC1, 1)); break; case CHIP_I855: device_printf(dev, "AGP_I855_GCC1: 0x%02x\n", pci_read_config(sc->bdev, AGP_I855_GCC1, 1)); break; case CHIP_I915: case CHIP_I965: case CHIP_G33: + case CHIP_G4X: device_printf(dev, "AGP_I855_GCC1: 0x%02x\n", pci_read_config(sc->bdev, AGP_I855_GCC1, 1)); device_printf(dev, "AGP_I915_MSAC: 0x%02x\n", pci_read_config(sc->bdev, AGP_I915_MSAC, 1)); break; } device_printf(dev, "Aperture resource size: %d bytes\n", AGP_GET_APERTURE(dev)); } static int agp_i810_attach(device_t dev) { struct agp_i810_softc *sc = device_get_softc(dev); struct agp_gatt *gatt; const struct agp_i810_match *match; int error; sc->bdev = agp_i810_find_bridge(dev); if (!sc->bdev) return ENOENT; match = agp_i810_match(dev); sc->chiptype = match->chiptype; switch (sc->chiptype) { case CHIP_I810: case CHIP_I830: case CHIP_I855: sc->sc_res_spec = agp_i810_res_spec; agp_set_aperture_resource(dev, AGP_APBASE); break; case CHIP_I915: case CHIP_G33: sc->sc_res_spec = agp_i915_res_spec; agp_set_aperture_resource(dev, AGP_I915_GMADR); break; case CHIP_I965: case CHIP_G4X: sc->sc_res_spec = agp_i965_res_spec; agp_set_aperture_resource(dev, AGP_I915_GMADR); break; } error = agp_generic_attach(dev); if (error) return error; if (sc->chiptype != CHIP_I965 && sc->chiptype != CHIP_G33 && - ptoa((vm_paddr_t)Maxmem) > 0xfffffffful) + sc->chiptype != CHIP_G4X && ptoa((vm_paddr_t)Maxmem) > 0xfffffffful) { device_printf(dev, "agp_i810.c does not support physical " "memory above 4GB.\n"); return ENOENT; } if (bus_alloc_resources(dev, sc->sc_res_spec, sc->sc_res)) { agp_generic_detach(dev); return ENODEV; } sc->initial_aperture = AGP_GET_APERTURE(dev); gatt = malloc( sizeof(struct agp_gatt), M_AGP, M_NOWAIT); if (!gatt) { bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); agp_generic_detach(dev); return ENOMEM; } sc->gatt = gatt; gatt->ag_entries = AGP_GET_APERTURE(dev) >> AGP_PAGE_SHIFT; if ( sc->chiptype == CHIP_I810 ) { /* Some i810s have on-chip memory called dcache */ if (bus_read_1(sc->sc_res[0], AGP_I810_DRT) & AGP_I810_DRT_POPULATED) sc->dcache_size = 4 * 1024 * 1024; else sc->dcache_size = 0; /* According to the specs the gatt on the i810 must be 64k */ gatt->ag_virtual = contigmalloc( 64 * 1024, M_AGP, 0, 0, ~0, PAGE_SIZE, 0); if (!gatt->ag_virtual) { if (bootverbose) device_printf(dev, "contiguous allocation failed\n"); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); free(gatt, M_AGP); agp_generic_detach(dev); return ENOMEM; } bzero(gatt->ag_virtual, gatt->ag_entries * sizeof(u_int32_t)); gatt->ag_physical = vtophys((vm_offset_t) gatt->ag_virtual); agp_flush_cache(); /* Install the GATT. */ bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, gatt->ag_physical | 1); } else if ( sc->chiptype == CHIP_I830 ) { /* The i830 automatically initializes the 128k gatt on boot. */ unsigned int gcc1, pgtblctl; gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 1); switch (gcc1 & AGP_I830_GCC1_GMS) { case AGP_I830_GCC1_GMS_STOLEN_512: sc->stolen = (512 - 132) * 1024 / 4096; break; case AGP_I830_GCC1_GMS_STOLEN_1024: sc->stolen = (1024 - 132) * 1024 / 4096; break; case AGP_I830_GCC1_GMS_STOLEN_8192: sc->stolen = (8192 - 132) * 1024 / 4096; break; default: sc->stolen = 0; device_printf(dev, "unknown memory configuration, disabling\n"); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); free(gatt, M_AGP); agp_generic_detach(dev); return EINVAL; } if (sc->stolen > 0) { device_printf(dev, "detected %dk stolen memory\n", sc->stolen * 4); } device_printf(dev, "aperture size is %dM\n", sc->initial_aperture / 1024 / 1024); /* GATT address is already in there, make sure it's enabled */ pgtblctl = bus_read_4(sc->sc_res[0], AGP_I810_PGTBL_CTL); pgtblctl |= 1; bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, pgtblctl); gatt->ag_physical = pgtblctl & ~1; } else if (sc->chiptype == CHIP_I855 || sc->chiptype == CHIP_I915 || sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33 || sc->chiptype == CHIP_G4X) { unsigned int gcc1, pgtblctl, stolen, gtt_size; /* Stolen memory is set up at the beginning of the aperture by * the BIOS, consisting of the GATT followed by 4kb for the * BIOS display. */ switch (sc->chiptype) { case CHIP_I855: gtt_size = 128; break; case CHIP_I915: gtt_size = 256; break; case CHIP_I965: switch (bus_read_4(sc->sc_res[0], AGP_I810_PGTBL_CTL) & AGP_I810_PGTBL_SIZE_MASK) { case AGP_I810_PGTBL_SIZE_128KB: gtt_size = 128; break; case AGP_I810_PGTBL_SIZE_256KB: gtt_size = 256; break; case AGP_I810_PGTBL_SIZE_512KB: gtt_size = 512; break; case AGP_I965_PGTBL_SIZE_1MB: gtt_size = 1024; break; case AGP_I965_PGTBL_SIZE_2MB: gtt_size = 2048; break; case AGP_I965_PGTBL_SIZE_1_5MB: gtt_size = 1024 + 512; break; default: device_printf(dev, "Bad PGTBL size\n"); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); free(gatt, M_AGP); agp_generic_detach(dev); return EINVAL; } break; case CHIP_G33: gcc1 = pci_read_config(sc->bdev, AGP_I855_GCC1, 2); switch (gcc1 & AGP_G33_MGGC_GGMS_MASK) { case AGP_G33_MGGC_GGMS_SIZE_1M: gtt_size = 1024; break; case AGP_G33_MGGC_GGMS_SIZE_2M: gtt_size = 2048; break; default: device_printf(dev, "Bad PGTBL size\n"); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); free(gatt, M_AGP); agp_generic_detach(dev); return EINVAL; } break; case CHIP_G4X: gtt_size = 0; break; default: device_printf(dev, "Bad chiptype\n"); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); free(gatt, M_AGP); agp_generic_detach(dev); return EINVAL; } /* GCC1 is called MGGC on i915+ */ gcc1 = pci_read_config(sc->bdev, AGP_I855_GCC1, 1); switch (gcc1 & AGP_I855_GCC1_GMS) { case AGP_I855_GCC1_GMS_STOLEN_1M: stolen = 1024; break; case AGP_I855_GCC1_GMS_STOLEN_4M: stolen = 4 * 1024; break; case AGP_I855_GCC1_GMS_STOLEN_8M: stolen = 8 * 1024; break; case AGP_I855_GCC1_GMS_STOLEN_16M: stolen = 16 * 1024; break; case AGP_I855_GCC1_GMS_STOLEN_32M: stolen = 32 * 1024; break; case AGP_I915_GCC1_GMS_STOLEN_48M: if (sc->chiptype == CHIP_I915 || sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33 || sc->chiptype == CHIP_G4X) { stolen = 48 * 1024; } else { stolen = 0; } break; case AGP_I915_GCC1_GMS_STOLEN_64M: if (sc->chiptype == CHIP_I915 || sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33 || sc->chiptype == CHIP_G4X) { stolen = 64 * 1024; } else { stolen = 0; } break; case AGP_G33_GCC1_GMS_STOLEN_128M: if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33 || sc->chiptype == CHIP_G4X) { stolen = 128 * 1024; } else { stolen = 0; } break; case AGP_G33_GCC1_GMS_STOLEN_256M: if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33 || sc->chiptype == CHIP_G4X) { stolen = 256 * 1024; } else { stolen = 0; } break; case AGP_G4X_GCC1_GMS_STOLEN_96M: if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G4X) { stolen = 96 * 1024; } else { stolen = 0; } break; case AGP_G4X_GCC1_GMS_STOLEN_160M: if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G4X) { stolen = 160 * 1024; } else { stolen = 0; } break; case AGP_G4X_GCC1_GMS_STOLEN_224M: if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G4X) { stolen = 224 * 1024; } else { stolen = 0; } break; case AGP_G4X_GCC1_GMS_STOLEN_352M: if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G4X) { stolen = 352 * 1024; } else { stolen = 0; } break; default: device_printf(dev, "unknown memory configuration, " "disabling\n"); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); free(gatt, M_AGP); agp_generic_detach(dev); return EINVAL; } - if (sc->chiptype != CHIP_G4X) - gtt_size += 4; + gtt_size += 4; sc->stolen = (stolen - gtt_size) * 1024 / 4096; if (sc->stolen > 0) device_printf(dev, "detected %dk stolen memory\n", sc->stolen * 4); device_printf(dev, "aperture size is %dM\n", sc->initial_aperture / 1024 / 1024); /* GATT address is already in there, make sure it's enabled */ pgtblctl = bus_read_4(sc->sc_res[0], AGP_I810_PGTBL_CTL); pgtblctl |= 1; bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, pgtblctl); gatt->ag_physical = pgtblctl & ~1; } if (0) agp_i810_dump_regs(dev); return 0; } static int agp_i810_detach(device_t dev) { struct agp_i810_softc *sc = device_get_softc(dev); agp_free_cdev(dev); /* Clear the GATT base. */ if ( sc->chiptype == CHIP_I810 ) { bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, 0); } else { unsigned int pgtblctl; pgtblctl = bus_read_4(sc->sc_res[0], AGP_I810_PGTBL_CTL); pgtblctl &= ~1; bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, pgtblctl); } /* Put the aperture back the way it started. */ AGP_SET_APERTURE(dev, sc->initial_aperture); if ( sc->chiptype == CHIP_I810 ) { contigfree(sc->gatt->ag_virtual, 64 * 1024, M_AGP); } free(sc->gatt, M_AGP); bus_release_resources(dev, sc->sc_res_spec, sc->sc_res); agp_free_res(dev); return 0; } static int agp_i810_resume(device_t dev) { struct agp_i810_softc *sc; sc = device_get_softc(dev); AGP_SET_APERTURE(dev, sc->initial_aperture); /* Install the GATT. */ bus_write_4(sc->sc_res[0], AGP_I810_PGTBL_CTL, sc->gatt->ag_physical | 1); return (bus_generic_resume(dev)); } /** * Sets the PCI resource size of the aperture on i830-class and below chipsets, * while returning failure on later chipsets when an actual change is * requested. * * This whole function is likely bogus, as the kernel would probably need to * reconfigure the placement of the AGP aperture if a larger size is requested, * which doesn't happen currently. */ static int agp_i810_set_aperture(device_t dev, u_int32_t aperture) { struct agp_i810_softc *sc = device_get_softc(dev); u_int16_t miscc, gcc1; switch (sc->chiptype) { case CHIP_I810: /* * Double check for sanity. */ if (aperture != 32 * 1024 * 1024 && aperture != 64 * 1024 * 1024) { device_printf(dev, "bad aperture size %d\n", aperture); return EINVAL; } miscc = pci_read_config(sc->bdev, AGP_I810_MISCC, 2); miscc &= ~AGP_I810_MISCC_WINSIZE; if (aperture == 32 * 1024 * 1024) miscc |= AGP_I810_MISCC_WINSIZE_32; else miscc |= AGP_I810_MISCC_WINSIZE_64; pci_write_config(sc->bdev, AGP_I810_MISCC, miscc, 2); break; case CHIP_I830: if (aperture != 64 * 1024 * 1024 && aperture != 128 * 1024 * 1024) { device_printf(dev, "bad aperture size %d\n", aperture); return EINVAL; } gcc1 = pci_read_config(sc->bdev, AGP_I830_GCC1, 2); gcc1 &= ~AGP_I830_GCC1_GMASIZE; if (aperture == 64 * 1024 * 1024) gcc1 |= AGP_I830_GCC1_GMASIZE_64; else gcc1 |= AGP_I830_GCC1_GMASIZE_128; pci_write_config(sc->bdev, AGP_I830_GCC1, gcc1, 2); break; case CHIP_I855: case CHIP_I915: case CHIP_I965: case CHIP_G33: + case CHIP_G4X: return agp_generic_set_aperture(dev, aperture); } return 0; } /** * Writes a GTT entry mapping the page at the given offset from the beginning * of the aperture to the given physical address. */ static void agp_i810_write_gtt_entry(device_t dev, int offset, vm_offset_t physical, int enabled) { struct agp_i810_softc *sc = device_get_softc(dev); u_int32_t pte; pte = (u_int32_t)physical | 1; - if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33) { + if (sc->chiptype == CHIP_I965 || sc->chiptype == CHIP_G33 || + sc->chiptype == CHIP_G4X) { pte |= (physical & 0x0000000f00000000ull) >> 28; } else { /* If we do actually have memory above 4GB on an older system, * crash cleanly rather than scribble on system memory, * so we know we need to fix it. */ KASSERT((pte & 0x0000000f00000000ull) == 0, (">4GB physical address in agp")); } switch (sc->chiptype) { case CHIP_I810: case CHIP_I830: case CHIP_I855: bus_write_4(sc->sc_res[0], AGP_I810_GTT + (offset >> AGP_PAGE_SHIFT) * 4, pte); break; case CHIP_I915: case CHIP_G33: bus_write_4(sc->sc_res[1], (offset >> AGP_PAGE_SHIFT) * 4, pte); break; case CHIP_I965: bus_write_4(sc->sc_res[0], (offset >> AGP_PAGE_SHIFT) * 4 + (512 * 1024), pte); + break; + case CHIP_G4X: + bus_write_4(sc->sc_res[0], + (offset >> AGP_PAGE_SHIFT) * 4 + (2 * 1024 * 1024), pte); break; } } static int agp_i810_bind_page(device_t dev, int offset, vm_offset_t physical) { struct agp_i810_softc *sc = device_get_softc(dev); if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) { device_printf(dev, "failed: offset is 0x%08x, shift is %d, entries is %d\n", offset, AGP_PAGE_SHIFT, sc->gatt->ag_entries); return EINVAL; } if ( sc->chiptype != CHIP_I810 ) { if ( (offset >> AGP_PAGE_SHIFT) < sc->stolen ) { device_printf(dev, "trying to bind into stolen memory"); return EINVAL; } } agp_i810_write_gtt_entry(dev, offset, physical, 1); return 0; } static int agp_i810_unbind_page(device_t dev, int offset) { struct agp_i810_softc *sc = device_get_softc(dev); if (offset < 0 || offset >= (sc->gatt->ag_entries << AGP_PAGE_SHIFT)) return EINVAL; if ( sc->chiptype != CHIP_I810 ) { if ( (offset >> AGP_PAGE_SHIFT) < sc->stolen ) { device_printf(dev, "trying to unbind from stolen memory"); return EINVAL; } } agp_i810_write_gtt_entry(dev, offset, 0, 0); return 0; } /* * Writing via memory mapped registers already flushes all TLBs. */ static void agp_i810_flush_tlb(device_t dev) { } static int agp_i810_enable(device_t dev, u_int32_t mode) { return 0; } static struct agp_memory * agp_i810_alloc_memory(device_t dev, int type, vm_size_t size) { struct agp_i810_softc *sc = device_get_softc(dev); struct agp_memory *mem; if ((size & (AGP_PAGE_SIZE - 1)) != 0) return 0; if (sc->agp.as_allocated + size > sc->agp.as_maxmem) return 0; if (type == 1) { /* * Mapping local DRAM into GATT. */ if ( sc->chiptype != CHIP_I810 ) return 0; if (size != sc->dcache_size) return 0; } else if (type == 2) { /* * Type 2 is the contiguous physical memory type, that hands * back a physical address. This is used for cursors on i810. * Hand back as many single pages with physical as the user * wants, but only allow one larger allocation (ARGB cursor) * for simplicity. */ if (size != AGP_PAGE_SIZE) { if (sc->argb_cursor != NULL) return 0; /* Allocate memory for ARGB cursor, if we can. */ sc->argb_cursor = contigmalloc(size, M_AGP, 0, 0, ~0, PAGE_SIZE, 0); if (sc->argb_cursor == NULL) return 0; } } mem = malloc(sizeof *mem, M_AGP, M_WAITOK); mem->am_id = sc->agp.as_nextid++; mem->am_size = size; mem->am_type = type; if (type != 1 && (type != 2 || size == AGP_PAGE_SIZE)) mem->am_obj = vm_object_allocate(OBJT_DEFAULT, atop(round_page(size))); else mem->am_obj = 0; if (type == 2) { if (size == AGP_PAGE_SIZE) { /* * Allocate and wire down the page now so that we can * get its physical address. */ vm_page_t m; VM_OBJECT_LOCK(mem->am_obj); m = vm_page_grab(mem->am_obj, 0, VM_ALLOC_NOBUSY | VM_ALLOC_WIRED | VM_ALLOC_ZERO | VM_ALLOC_RETRY); VM_OBJECT_UNLOCK(mem->am_obj); mem->am_physical = VM_PAGE_TO_PHYS(m); } else { /* Our allocation is already nicely wired down for us. * Just grab the physical address. */ mem->am_physical = vtophys(sc->argb_cursor); } } else { mem->am_physical = 0; } mem->am_offset = 0; mem->am_is_bound = 0; TAILQ_INSERT_TAIL(&sc->agp.as_memory, mem, am_link); sc->agp.as_allocated += size; return mem; } static int agp_i810_free_memory(device_t dev, struct agp_memory *mem) { struct agp_i810_softc *sc = device_get_softc(dev); if (mem->am_is_bound) return EBUSY; if (mem->am_type == 2) { if (mem->am_size == AGP_PAGE_SIZE) { /* * Unwire the page which we wired in alloc_memory. */ vm_page_t m; VM_OBJECT_LOCK(mem->am_obj); m = vm_page_lookup(mem->am_obj, 0); VM_OBJECT_UNLOCK(mem->am_obj); vm_page_lock_queues(); vm_page_unwire(m, 0); vm_page_unlock_queues(); } else { contigfree(sc->argb_cursor, mem->am_size, M_AGP); sc->argb_cursor = NULL; } } sc->agp.as_allocated -= mem->am_size; TAILQ_REMOVE(&sc->agp.as_memory, mem, am_link); if (mem->am_obj) vm_object_deallocate(mem->am_obj); free(mem, M_AGP); return 0; } static int agp_i810_bind_memory(device_t dev, struct agp_memory *mem, vm_offset_t offset) { struct agp_i810_softc *sc = device_get_softc(dev); vm_offset_t i; /* Do some sanity checks first. */ if (offset < 0 || (offset & (AGP_PAGE_SIZE - 1)) != 0 || offset + mem->am_size > AGP_GET_APERTURE(dev)) { device_printf(dev, "binding memory at bad offset %#x\n", (int)offset); return EINVAL; } if (mem->am_type == 2 && mem->am_size != AGP_PAGE_SIZE) { mtx_lock(&sc->agp.as_lock); if (mem->am_is_bound) { mtx_unlock(&sc->agp.as_lock); return EINVAL; } /* The memory's already wired down, just stick it in the GTT. */ for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { agp_i810_write_gtt_entry(dev, offset + i, mem->am_physical + i, 1); } agp_flush_cache(); mem->am_offset = offset; mem->am_is_bound = 1; mtx_unlock(&sc->agp.as_lock); return 0; } if (mem->am_type != 1) return agp_generic_bind_memory(dev, mem, offset); if ( sc->chiptype != CHIP_I810 ) return EINVAL; for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { bus_write_4(sc->sc_res[0], AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, i | 3); } return 0; } static int agp_i810_unbind_memory(device_t dev, struct agp_memory *mem) { struct agp_i810_softc *sc = device_get_softc(dev); vm_offset_t i; if (mem->am_type == 2 && mem->am_size != AGP_PAGE_SIZE) { mtx_lock(&sc->agp.as_lock); if (!mem->am_is_bound) { mtx_unlock(&sc->agp.as_lock); return EINVAL; } for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { agp_i810_write_gtt_entry(dev, mem->am_offset + i, 0, 0); } agp_flush_cache(); mem->am_is_bound = 0; mtx_unlock(&sc->agp.as_lock); return 0; } if (mem->am_type != 1) return agp_generic_unbind_memory(dev, mem); if ( sc->chiptype != CHIP_I810 ) return EINVAL; for (i = 0; i < mem->am_size; i += AGP_PAGE_SIZE) { bus_write_4(sc->sc_res[0], AGP_I810_GTT + (i >> AGP_PAGE_SHIFT) * 4, 0); } return 0; } static device_method_t agp_i810_methods[] = { /* Device interface */ DEVMETHOD(device_identify, agp_i810_identify), DEVMETHOD(device_probe, agp_i810_probe), DEVMETHOD(device_attach, agp_i810_attach), DEVMETHOD(device_detach, agp_i810_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, agp_i810_resume), /* AGP interface */ DEVMETHOD(agp_get_aperture, agp_generic_get_aperture), DEVMETHOD(agp_set_aperture, agp_i810_set_aperture), DEVMETHOD(agp_bind_page, agp_i810_bind_page), DEVMETHOD(agp_unbind_page, agp_i810_unbind_page), DEVMETHOD(agp_flush_tlb, agp_i810_flush_tlb), DEVMETHOD(agp_enable, agp_i810_enable), DEVMETHOD(agp_alloc_memory, agp_i810_alloc_memory), DEVMETHOD(agp_free_memory, agp_i810_free_memory), DEVMETHOD(agp_bind_memory, agp_i810_bind_memory), DEVMETHOD(agp_unbind_memory, agp_i810_unbind_memory), { 0, 0 } }; static driver_t agp_i810_driver = { "agp", agp_i810_methods, sizeof(struct agp_i810_softc), }; static devclass_t agp_devclass; DRIVER_MODULE(agp_i810, vgapci, agp_i810_driver, agp_devclass, 0, 0); MODULE_DEPEND(agp_i810, agp, 1, 1, 1); MODULE_DEPEND(agp_i810, pci, 1, 1, 1); Index: projects/cambria/sys/dev/agp/agppriv.h =================================================================== --- projects/cambria/sys/dev/agp/agppriv.h (revision 186459) +++ projects/cambria/sys/dev/agp/agppriv.h (revision 186460) @@ -1,112 +1,109 @@ /*- * Copyright (c) 2000 Doug Rabson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _PCI_AGPPRIV_H_ #define _PCI_AGPPRIV_H_ /* * This file *must not* be included by code outside the agp driver itself. */ #include #include -#define AGP_DEBUGxx - #ifdef AGP_DEBUG -#define AGP_DPF(x...) do { \ - printf("agp: "); \ - printf(##x); \ +#define AGP_DPF(fmt, ...) do { \ + printf("agp: " fmt, ##__VA_ARGS__); \ } while (0) #else -#define AGP_DPF(x...) do {} while (0) +#define AGP_DPF(fmt, ...) do {} while (0) #endif #include "agp_if.h" /* * Data structure to describe an AGP memory allocation. */ TAILQ_HEAD(agp_memory_list, agp_memory); struct agp_memory { TAILQ_ENTRY(agp_memory) am_link; /* wiring for the tailq */ int am_id; /* unique id for block */ vm_size_t am_size; /* number of bytes allocated */ int am_type; /* chipset specific type */ struct vm_object *am_obj; /* VM object owning pages */ vm_offset_t am_physical; /* bogus hack for i810 */ vm_offset_t am_offset; /* page offset if bound */ int am_is_bound; /* non-zero if bound */ }; /* * All chipset drivers must have this at the start of their softc. */ struct agp_softc { struct resource *as_aperture; /* location of aperture */ int as_aperture_rid; u_int32_t as_maxmem; /* allocation upper bound */ u_int32_t as_allocated; /* amount allocated */ enum agp_acquire_state as_state; struct agp_memory_list as_memory; /* list of allocated memory */ int as_nextid; /* next memory block id */ int as_isopen; /* user device is open */ struct cdev *as_devnode; /* from make_dev */ struct mtx as_lock; /* lock for access to GATT */ }; struct agp_gatt { u_int32_t ag_entries; u_int32_t *ag_virtual; vm_offset_t ag_physical; }; void agp_flush_cache(void); u_int8_t agp_find_caps(device_t dev); struct agp_gatt *agp_alloc_gatt(device_t dev); void agp_set_aperture_resource(device_t dev, int rid); void agp_free_cdev(device_t dev); void agp_free_gatt(struct agp_gatt *gatt); void agp_free_res(device_t dev); int agp_generic_attach(device_t dev); int agp_generic_detach(device_t dev); int agp_generic_get_aperture(device_t dev); int agp_generic_set_aperture(device_t dev, u_int32_t aperture); int agp_generic_enable(device_t dev, u_int32_t mode); struct agp_memory *agp_generic_alloc_memory(device_t dev, int type, vm_size_t size); int agp_generic_free_memory(device_t dev, struct agp_memory *mem); int agp_generic_bind_memory(device_t dev, struct agp_memory *mem, vm_offset_t offset); int agp_generic_unbind_memory(device_t dev, struct agp_memory *mem); #endif /* !_PCI_AGPPRIV_H_ */ Index: projects/cambria/sys/dev/drm/drmP.h =================================================================== --- projects/cambria/sys/dev/drm/drmP.h (revision 186459) +++ projects/cambria/sys/dev/drm/drmP.h (revision 186460) @@ -1,1015 +1,1015 @@ /* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com */ /*- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: * Rickard E. (Rik) Faith * Gareth Hughes * */ #include __FBSDID("$FreeBSD$"); #ifndef _DRM_P_H_ #define _DRM_P_H_ #if defined(_KERNEL) || defined(__KERNEL__) struct drm_device; struct drm_file; #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 700000 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 800004 #include #else /* __FreeBSD_version >= 800004 */ #include #endif /* __FreeBSD_version >= 800004 */ #include #include #include #include #include #include #include "dev/drm/drm.h" #include "dev/drm/drm_linux_list.h" #include "dev/drm/drm_atomic.h" #include "dev/drm/drm_internal.h" #include #ifdef DRM_DEBUG #undef DRM_DEBUG #define DRM_DEBUG_DEFAULT_ON 1 #endif /* DRM_DEBUG */ #if defined(DRM_LINUX) && DRM_LINUX && !defined(__amd64__) #include #include #include #include #else /* Either it was defined when it shouldn't be (FreeBSD amd64) or it isn't * supported on this OS yet. */ #undef DRM_LINUX #define DRM_LINUX 0 #endif /* driver capabilities and requirements mask */ #define DRIVER_USE_AGP 0x1 #define DRIVER_REQUIRE_AGP 0x2 #define DRIVER_USE_MTRR 0x4 #define DRIVER_PCI_DMA 0x8 #define DRIVER_SG 0x10 #define DRIVER_HAVE_DMA 0x20 #define DRIVER_HAVE_IRQ 0x40 #define DRIVER_DMA_QUEUE 0x100 #define DRM_HASH_SIZE 16 /* Size of key hash table */ #define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ #define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ MALLOC_DECLARE(DRM_MEM_DMA); MALLOC_DECLARE(DRM_MEM_SAREA); MALLOC_DECLARE(DRM_MEM_DRIVER); MALLOC_DECLARE(DRM_MEM_MAGIC); MALLOC_DECLARE(DRM_MEM_IOCTLS); MALLOC_DECLARE(DRM_MEM_MAPS); MALLOC_DECLARE(DRM_MEM_BUFS); MALLOC_DECLARE(DRM_MEM_SEGS); MALLOC_DECLARE(DRM_MEM_PAGES); MALLOC_DECLARE(DRM_MEM_FILES); MALLOC_DECLARE(DRM_MEM_QUEUES); MALLOC_DECLARE(DRM_MEM_CMDS); MALLOC_DECLARE(DRM_MEM_MAPPINGS); MALLOC_DECLARE(DRM_MEM_BUFLISTS); MALLOC_DECLARE(DRM_MEM_AGPLISTS); MALLOC_DECLARE(DRM_MEM_CTXBITMAP); MALLOC_DECLARE(DRM_MEM_SGLISTS); MALLOC_DECLARE(DRM_MEM_DRAWABLE); #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) /* Internal types and structures */ #define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define DRM_MIN(a,b) ((a)<(b)?(a):(b)) #define DRM_MAX(a,b) ((a)>(b)?(a):(b)) #define DRM_IF_VERSION(maj, min) (maj << 16 | min) #define __OS_HAS_AGP 1 #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) #define DRM_DEV_UID 0 #define DRM_DEV_GID 0 #define wait_queue_head_t atomic_t #define DRM_WAKEUP(w) wakeup((void *)w) #define DRM_WAKEUP_INT(w) wakeup(w) #define DRM_INIT_WAITQUEUE(queue) do {(void)(queue);} while (0) #define DRM_CURPROC curthread #define DRM_STRUCTPROC struct thread #define DRM_SPINTYPE struct mtx #define DRM_SPININIT(l,name) mtx_init(l, name, NULL, MTX_DEF) #define DRM_SPINUNINIT(l) mtx_destroy(l) #define DRM_SPINLOCK(l) mtx_lock(l) #define DRM_SPINUNLOCK(u) mtx_unlock(u) #define DRM_SPINLOCK_IRQSAVE(l, irqflags) do { \ mtx_lock(l); \ (void)irqflags; \ } while (0) #define DRM_SPINUNLOCK_IRQRESTORE(u, irqflags) mtx_unlock(u) #define DRM_SPINLOCK_ASSERT(l) mtx_assert(l, MA_OWNED) #define DRM_CURRENTPID curthread->td_proc->p_pid #define DRM_LOCK() mtx_lock(&dev->dev_lock) #define DRM_UNLOCK() mtx_unlock(&dev->dev_lock) #define DRM_SYSCTL_HANDLER_ARGS (SYSCTL_HANDLER_ARGS) #define DRM_IRQ_ARGS void *arg typedef void irqreturn_t; #define IRQ_HANDLED /* nothing */ #define IRQ_NONE /* nothing */ enum { DRM_IS_NOT_AGP, DRM_IS_AGP, DRM_MIGHT_BE_AGP }; #define DRM_AGP_MEM struct agp_memory_info #define drm_get_device_from_kdev(_kdev) (_kdev->si_drv1) #define PAGE_ALIGN(addr) round_page(addr) /* DRM_SUSER returns true if the user is superuser */ #if __FreeBSD_version >= 700000 #define DRM_SUSER(p) (priv_check(p, PRIV_DRIVER) == 0) #else #define DRM_SUSER(p) (suser(p) == 0) #endif #define DRM_AGP_FIND_DEVICE() agp_find_device() #define DRM_MTRR_WC MDF_WRITECOMBINE #define jiffies ticks typedef unsigned long dma_addr_t; typedef u_int64_t u64; typedef u_int32_t u32; typedef u_int16_t u16; typedef u_int8_t u8; /* DRM_READMEMORYBARRIER() prevents reordering of reads. * DRM_WRITEMEMORYBARRIER() prevents reordering of writes. * DRM_MEMORYBARRIER() prevents reordering of reads and writes. */ #if defined(__i386__) #define DRM_READMEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%esp)" : : : "memory"); #define DRM_WRITEMEMORYBARRIER() __asm __volatile("" : : : "memory"); #define DRM_MEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%esp)" : : : "memory"); #elif defined(__alpha__) #define DRM_READMEMORYBARRIER() alpha_mb(); #define DRM_WRITEMEMORYBARRIER() alpha_wmb(); #define DRM_MEMORYBARRIER() alpha_mb(); #elif defined(__amd64__) #define DRM_READMEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%rsp)" : : : "memory"); #define DRM_WRITEMEMORYBARRIER() __asm __volatile("" : : : "memory"); #define DRM_MEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%rsp)" : : : "memory"); #endif #define DRM_READ8(map, offset) \ *(volatile u_int8_t *) (((unsigned long)(map)->handle) + (offset)) #define DRM_READ16(map, offset) \ *(volatile u_int16_t *) (((unsigned long)(map)->handle) + (offset)) #define DRM_READ32(map, offset) \ *(volatile u_int32_t *)(((unsigned long)(map)->handle) + (offset)) #define DRM_WRITE8(map, offset, val) \ *(volatile u_int8_t *) (((unsigned long)(map)->handle) + (offset)) = val #define DRM_WRITE16(map, offset, val) \ *(volatile u_int16_t *) (((unsigned long)(map)->handle) + (offset)) = val #define DRM_WRITE32(map, offset, val) \ *(volatile u_int32_t *)(((unsigned long)(map)->handle) + (offset)) = val #define DRM_VERIFYAREA_READ( uaddr, size ) \ (!useracc(__DECONST(caddr_t, uaddr), size, VM_PROT_READ)) #define DRM_COPY_TO_USER(user, kern, size) \ copyout(kern, user, size) #define DRM_COPY_FROM_USER(kern, user, size) \ copyin(user, kern, size) #define DRM_COPY_FROM_USER_UNCHECKED(arg1, arg2, arg3) \ copyin(arg2, arg1, arg3) #define DRM_COPY_TO_USER_UNCHECKED(arg1, arg2, arg3) \ copyout(arg2, arg1, arg3) #define DRM_GET_USER_UNCHECKED(val, uaddr) \ ((val) = fuword32(uaddr), 0) #define cpu_to_le32(x) htole32(x) #define le32_to_cpu(x) le32toh(x) #define DRM_HZ hz #define DRM_UDELAY(udelay) DELAY(udelay) #define DRM_TIME_SLICE (hz/20) /* Time slice for GLXContexts */ #define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ (_map) = (_dev)->context_sareas[_ctx]; \ } while(0) #define LOCK_TEST_WITH_RETURN(dev, file_priv) \ do { \ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || \ dev->lock.file_priv != file_priv) { \ DRM_ERROR("%s called without lock held\n", \ __FUNCTION__); \ return EINVAL; \ } \ } while (0) /* Returns -errno to shared code */ #define DRM_WAIT_ON( ret, queue, timeout, condition ) \ for ( ret = 0 ; !ret && !(condition) ; ) { \ DRM_UNLOCK(); \ mtx_lock(&dev->irq_lock); \ if (!(condition)) \ ret = -mtx_sleep(&(queue), &dev->irq_lock, \ PZERO | PCATCH, "drmwtq", (timeout)); \ mtx_unlock(&dev->irq_lock); \ DRM_LOCK(); \ } -#define DRM_ERROR(fmt, arg...) \ +#define DRM_ERROR(fmt, ...) \ printf("error: [" DRM_NAME ":pid%d:%s] *ERROR* " fmt, \ - DRM_CURRENTPID, __func__ , ## arg) + DRM_CURRENTPID, __func__ , ##__VA_ARGS__) -#define DRM_INFO(fmt, arg...) printf("info: [" DRM_NAME "] " fmt , ## arg) +#define DRM_INFO(fmt, ...) printf("info: [" DRM_NAME "] " fmt , ##__VA_ARGS__) -#define DRM_DEBUG(fmt, arg...) do { \ +#define DRM_DEBUG(fmt, ...) do { \ if (drm_debug_flag) \ printf("[" DRM_NAME ":pid%d:%s] " fmt, DRM_CURRENTPID, \ - __func__ , ## arg); \ + __func__ , ##__VA_ARGS__); \ } while (0) typedef struct drm_pci_id_list { int vendor; int device; long driver_private; char *name; } drm_pci_id_list_t; #define DRM_AUTH 0x1 #define DRM_MASTER 0x2 #define DRM_ROOT_ONLY 0x4 typedef struct drm_ioctl_desc { unsigned long cmd; int (*func)(struct drm_device *dev, void *data, struct drm_file *file_priv); int flags; } drm_ioctl_desc_t; /** * Creates a driver or general drm_ioctl_desc array entry for the given * ioctl, for use by drm_ioctl(). */ #define DRM_IOCTL_DEF(ioctl, func, flags) \ [DRM_IOCTL_NR(ioctl)] = {ioctl, func, flags} typedef struct drm_magic_entry { drm_magic_t magic; struct drm_file *priv; struct drm_magic_entry *next; } drm_magic_entry_t; typedef struct drm_magic_head { struct drm_magic_entry *head; struct drm_magic_entry *tail; } drm_magic_head_t; typedef struct drm_buf { int idx; /* Index into master buflist */ int total; /* Buffer size */ int order; /* log-base-2(total) */ int used; /* Amount of buffer in use (for DMA) */ unsigned long offset; /* Byte offset (used internally) */ void *address; /* Address of buffer */ unsigned long bus_address; /* Bus address of buffer */ struct drm_buf *next; /* Kernel-only: used for free list */ __volatile__ int pending; /* On hardware DMA queue */ struct drm_file *file_priv; /* Unique identifier of holding process */ int context; /* Kernel queue for this buffer */ enum { DRM_LIST_NONE = 0, DRM_LIST_FREE = 1, DRM_LIST_WAIT = 2, DRM_LIST_PEND = 3, DRM_LIST_PRIO = 4, DRM_LIST_RECLAIM = 5 } list; /* Which list we're on */ int dev_priv_size; /* Size of buffer private stoarge */ void *dev_private; /* Per-buffer private storage */ } drm_buf_t; typedef struct drm_freelist { int initialized; /* Freelist in use */ atomic_t count; /* Number of free buffers */ drm_buf_t *next; /* End pointer */ int low_mark; /* Low water mark */ int high_mark; /* High water mark */ } drm_freelist_t; typedef struct drm_dma_handle { void *vaddr; bus_addr_t busaddr; bus_dma_tag_t tag; bus_dmamap_t map; } drm_dma_handle_t; typedef struct drm_buf_entry { int buf_size; int buf_count; drm_buf_t *buflist; int seg_count; drm_dma_handle_t **seglist; int page_order; drm_freelist_t freelist; } drm_buf_entry_t; typedef TAILQ_HEAD(drm_file_list, drm_file) drm_file_list_t; struct drm_file { TAILQ_ENTRY(drm_file) link; struct drm_device *dev; int authenticated; int master; int minor; pid_t pid; uid_t uid; drm_magic_t magic; unsigned long ioctl_count; void *driver_priv; }; typedef struct drm_lock_data { struct drm_hw_lock *hw_lock; /* Hardware lock */ struct drm_file *file_priv; /* Unique identifier of holding process (NULL is kernel)*/ int lock_queue; /* Queue of blocked processes */ unsigned long lock_time; /* Time of last lock in jiffies */ } drm_lock_data_t; /* This structure, in the struct drm_device, is always initialized while the * device * is open. dev->dma_lock protects the incrementing of dev->buf_use, which * when set marks that no further bufs may be allocated until device teardown * occurs (when the last open of the device has closed). The high/low * watermarks of bufs are only touched by the X Server, and thus not * concurrently accessed, so no locking is needed. */ typedef struct drm_device_dma { drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; int buf_count; drm_buf_t **buflist; /* Vector of pointers info bufs */ int seg_count; int page_count; unsigned long *pagelist; unsigned long byte_count; enum { _DRM_DMA_USE_AGP = 0x01, _DRM_DMA_USE_SG = 0x02 } flags; } drm_device_dma_t; typedef struct drm_agp_mem { void *handle; unsigned long bound; /* address */ int pages; struct drm_agp_mem *prev; struct drm_agp_mem *next; } drm_agp_mem_t; typedef struct drm_agp_head { device_t agpdev; struct agp_info info; const char *chipset; drm_agp_mem_t *memory; unsigned long mode; int enabled; int acquired; unsigned long base; int mtrr; int cant_use_aperture; unsigned long page_mask; } drm_agp_head_t; typedef struct drm_sg_mem { unsigned long handle; void *virtual; int pages; dma_addr_t *busaddr; struct drm_dma_handle *sg_dmah; /* Handle for sg_pages */ struct drm_dma_handle *dmah; /* Handle to PCI memory */ /* for ATI PCIGART table */ } drm_sg_mem_t; typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t; typedef struct drm_local_map { unsigned long offset; /* Physical address (0 for SAREA)*/ unsigned long size; /* Physical size (bytes) */ enum drm_map_type type; /* Type of memory mapped */ enum drm_map_flags flags; /* Flags */ void *handle; /* User-space: "Handle" to pass to mmap */ /* Kernel-space: kernel-virtual address */ int mtrr; /* Boolean: MTRR used */ /* Private data */ int rid; /* PCI resource ID for bus_space */ struct resource *bsr; bus_space_tag_t bst; bus_space_handle_t bsh; drm_dma_handle_t *dmah; TAILQ_ENTRY(drm_local_map) link; } drm_local_map_t; TAILQ_HEAD(drm_vbl_sig_list, drm_vbl_sig); typedef struct drm_vbl_sig { TAILQ_ENTRY(drm_vbl_sig) link; unsigned int sequence; int signo; int pid; } drm_vbl_sig_t; struct drm_vblank_info { wait_queue_head_t queue; /* vblank wait queue */ atomic_t count; /* number of VBLANK interrupts */ /* (driver must alloc the right number of counters) */ struct drm_vbl_sig_list sigs; /* signal list to send on VBLANK */ atomic_t refcount; /* number of users of vblank interrupts */ u32 last; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ int enabled; /* so we don't call enable more than */ /* once per disable */ int inmodeset; /* Display driver is setting mode */ }; /* location of GART table */ #define DRM_ATI_GART_MAIN 1 #define DRM_ATI_GART_FB 2 #define DRM_ATI_GART_PCI 1 #define DRM_ATI_GART_PCIE 2 #define DRM_ATI_GART_IGP 3 struct drm_ati_pcigart_info { int gart_table_location; int gart_reg_if; void *addr; dma_addr_t bus_addr; dma_addr_t table_mask; dma_addr_t member_mask; struct drm_dma_handle *table_handle; drm_local_map_t mapping; int table_size; }; #ifndef DMA_BIT_MASK #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : (1ULL<<(n)) - 1) #endif #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) struct drm_driver_info { int (*load)(struct drm_device *, unsigned long flags); int (*firstopen)(struct drm_device *); int (*open)(struct drm_device *, struct drm_file *); void (*preclose)(struct drm_device *, struct drm_file *file_priv); void (*postclose)(struct drm_device *, struct drm_file *); void (*lastclose)(struct drm_device *); int (*unload)(struct drm_device *); void (*reclaim_buffers_locked)(struct drm_device *, struct drm_file *file_priv); int (*dma_ioctl)(struct drm_device *dev, void *data, struct drm_file *file_priv); void (*dma_ready)(struct drm_device *); int (*dma_quiescent)(struct drm_device *); int (*dma_flush_block_and_flush)(struct drm_device *, int context, enum drm_lock_flags flags); int (*dma_flush_unblock)(struct drm_device *, int context, enum drm_lock_flags flags); int (*context_ctor)(struct drm_device *dev, int context); int (*context_dtor)(struct drm_device *dev, int context); int (*kernel_context_switch)(struct drm_device *dev, int old, int new); int (*kernel_context_switch_unlock)(struct drm_device *dev); void (*irq_preinstall)(struct drm_device *dev); int (*irq_postinstall)(struct drm_device *dev); void (*irq_uninstall)(struct drm_device *dev); void (*irq_handler)(DRM_IRQ_ARGS); u32 (*get_vblank_counter)(struct drm_device *dev, int crtc); int (*enable_vblank)(struct drm_device *dev, int crtc); void (*disable_vblank)(struct drm_device *dev, int crtc); drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ /** * Called by \c drm_device_is_agp. Typically used to determine if a * card is really attached to AGP or not. * * \param dev DRM device handle * * \returns * One of three values is returned depending on whether or not the * card is absolutely \b not AGP (return of 0), absolutely \b is AGP * (return of 1), or may or may not be AGP (return of 2). */ int (*device_is_agp) (struct drm_device * dev); drm_ioctl_desc_t *ioctls; int max_ioctl; int buf_priv_size; int major; int minor; int patchlevel; const char *name; /* Simple driver name */ const char *desc; /* Longer driver name */ const char *date; /* Date of last major changes. */ u32 driver_features; }; /* Length for the array of resource pointers for drm_get_resource_*. */ #define DRM_MAX_PCI_RESOURCE 3 /** * DRM device functions structure */ struct drm_device { struct drm_driver_info *driver; drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ u_int16_t pci_device; /* PCI device id */ u_int16_t pci_vendor; /* PCI vendor id */ char *unique; /* Unique identifier: e.g., busid */ int unique_len; /* Length of unique field */ device_t device; /* Device instance from newbus */ struct cdev *devnode; /* Device number for mknod */ int if_version; /* Highest interface version set */ int flags; /* Flags to open(2) */ /* Locks */ struct mtx vbl_lock; /* protects vblank operations */ struct mtx dma_lock; /* protects dev->dma */ struct mtx irq_lock; /* protects irq condition checks */ struct mtx dev_lock; /* protects everything else */ DRM_SPINTYPE drw_lock; DRM_SPINTYPE tsk_lock; /* Usage Counters */ int open_count; /* Outstanding files open */ int buf_use; /* Buffers in use -- cannot alloc */ /* Performance counters */ unsigned long counters; enum drm_stat_type types[15]; atomic_t counts[15]; /* Authentication */ drm_file_list_t files; drm_magic_head_t magiclist[DRM_HASH_SIZE]; /* Linked list of mappable regions. Protected by dev_lock */ drm_map_list_t maplist; drm_local_map_t **context_sareas; int max_context; drm_lock_data_t lock; /* Information on hardware lock */ /* DMA queues (contexts) */ drm_device_dma_t *dma; /* Optional pointer for DMA support */ /* Context support */ int irq; /* Interrupt used by board */ int irq_enabled; /* True if the irq handler is enabled */ int irqrid; /* Interrupt used by board */ struct resource *irqr; /* Resource for interrupt used by board */ void *irqh; /* Handle from bus_setup_intr */ /* Storage of resource pointers for drm_get_resource_* */ struct resource *pcir[DRM_MAX_PCI_RESOURCE]; int pcirid[DRM_MAX_PCI_RESOURCE]; int pci_domain; int pci_bus; int pci_slot; int pci_func; atomic_t context_flag; /* Context swapping flag */ int last_context; /* Last current context */ int vblank_disable_allowed; atomic_t vbl_signal_pending; /* number of signals pending on all crtcs */ struct callout vblank_disable_timer; u32 max_vblank_count; /* size of vblank counter register */ struct drm_vblank_info *vblank; /* per crtc vblank info */ int num_crtcs; struct sigio *buf_sigio; /* Processes waiting for SIGIO */ /* Sysctl support */ struct drm_sysctl_info *sysctl; drm_agp_head_t *agp; drm_sg_mem_t *sg; /* Scatter gather memory */ atomic_t *ctx_bitmap; void *dev_private; unsigned int agp_buffer_token; drm_local_map_t *agp_buffer_map; struct unrhdr *drw_unrhdr; /* RB tree of drawable infos */ RB_HEAD(drawable_tree, bsd_drm_drawable_info) drw_head; struct task locked_task; void (*locked_task_call)(struct drm_device *dev); }; static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) { return ((dev->driver->driver_features & feature) ? 1 : 0); } #if __OS_HAS_AGP static inline int drm_core_has_AGP(struct drm_device *dev) { return drm_core_check_feature(dev, DRIVER_USE_AGP); } #else #define drm_core_has_AGP(dev) (0) #endif extern int drm_debug_flag; /* Device setup support (drm_drv.c) */ int drm_probe(device_t nbdev, drm_pci_id_list_t *idlist); int drm_attach(device_t nbdev, drm_pci_id_list_t *idlist); void drm_close(void *data); int drm_detach(device_t nbdev); d_ioctl_t drm_ioctl; d_open_t drm_open; d_read_t drm_read; d_poll_t drm_poll; d_mmap_t drm_mmap; extern drm_local_map_t *drm_getsarea(struct drm_device *dev); /* File operations helpers (drm_fops.c) */ extern int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p, struct drm_device *dev); /* Memory management support (drm_memory.c) */ void drm_mem_init(void); void drm_mem_uninit(void); void *drm_ioremap_wc(struct drm_device *dev, drm_local_map_t *map); void *drm_ioremap(struct drm_device *dev, drm_local_map_t *map); void drm_ioremapfree(drm_local_map_t *map); int drm_mtrr_add(unsigned long offset, size_t size, int flags); int drm_mtrr_del(int handle, unsigned long offset, size_t size, int flags); int drm_context_switch(struct drm_device *dev, int old, int new); int drm_context_switch_complete(struct drm_device *dev, int new); int drm_ctxbitmap_init(struct drm_device *dev); void drm_ctxbitmap_cleanup(struct drm_device *dev); void drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle); int drm_ctxbitmap_next(struct drm_device *dev); /* Locking IOCTL support (drm_lock.c) */ int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context); int drm_lock_transfer(struct drm_lock_data *lock_data, unsigned int context); int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context); /* Buffer management support (drm_bufs.c) */ unsigned long drm_get_resource_start(struct drm_device *dev, unsigned int resource); unsigned long drm_get_resource_len(struct drm_device *dev, unsigned int resource); void drm_rmmap(struct drm_device *dev, drm_local_map_t *map); int drm_order(unsigned long size); int drm_addmap(struct drm_device *dev, unsigned long offset, unsigned long size, enum drm_map_type type, enum drm_map_flags flags, drm_local_map_t **map_ptr); int drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc *request); int drm_addbufs_sg(struct drm_device *dev, struct drm_buf_desc *request); int drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc *request); /* DMA support (drm_dma.c) */ int drm_dma_setup(struct drm_device *dev); void drm_dma_takedown(struct drm_device *dev); void drm_free_buffer(struct drm_device *dev, drm_buf_t *buf); void drm_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv); #define drm_core_reclaim_buffers drm_reclaim_buffers /* IRQ support (drm_irq.c) */ int drm_irq_install(struct drm_device *dev); int drm_irq_uninstall(struct drm_device *dev); irqreturn_t drm_irq_handler(DRM_IRQ_ARGS); void drm_driver_irq_preinstall(struct drm_device *dev); void drm_driver_irq_postinstall(struct drm_device *dev); void drm_driver_irq_uninstall(struct drm_device *dev); void drm_handle_vblank(struct drm_device *dev, int crtc); u32 drm_vblank_count(struct drm_device *dev, int crtc); int drm_vblank_get(struct drm_device *dev, int crtc); void drm_vblank_put(struct drm_device *dev, int crtc); int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); int drm_vblank_init(struct drm_device *dev, int num_crtcs); void drm_vbl_send_signals(struct drm_device *dev, int crtc); int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* AGP/PCI Express/GART support (drm_agpsupport.c) */ int drm_device_is_agp(struct drm_device *dev); int drm_device_is_pcie(struct drm_device *dev); drm_agp_head_t *drm_agp_init(void); int drm_agp_acquire(struct drm_device *dev); int drm_agp_release(struct drm_device *dev); int drm_agp_info(struct drm_device * dev, struct drm_agp_info *info); int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode); void *drm_agp_allocate_memory(size_t pages, u32 type); int drm_agp_free_memory(void *handle); int drm_agp_bind_memory(void *handle, off_t start); int drm_agp_unbind_memory(void *handle); int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request); int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request); int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request); int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request); /* Scatter Gather Support (drm_scatter.c) */ void drm_sg_cleanup(drm_sg_mem_t *entry); int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request); /* sysctl support (drm_sysctl.h) */ extern int drm_sysctl_init(struct drm_device *dev); extern int drm_sysctl_cleanup(struct drm_device *dev); /* ATI PCIGART support (ati_pcigart.c) */ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info); int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info); /* Locking IOCTL support (drm_drv.c) */ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Misc. IOCTL support (drm_ioctl.c) */ int drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_setunique(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_getmap(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_getclient(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_getstats(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_noop(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Context IOCTL support (drm_context.c) */ int drm_resctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_addctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_modctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_switchctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_newctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_rmctx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_setsareactx(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_getsareactx(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Drawable IOCTL support (drm_drawable.c) */ int drm_adddraw(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_rmdraw(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_update_draw(struct drm_device *dev, void *data, struct drm_file *file_priv); struct drm_drawable_info *drm_get_drawable_info(struct drm_device *dev, int handle); /* Drawable support (drm_drawable.c) */ void drm_drawable_free_all(struct drm_device *dev); /* Authentication IOCTL support (drm_auth.c) */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Buffer management support (drm_bufs.c) */ int drm_addmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_rmmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_addbufs_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); /* DMA support (drm_dma.c) */ int drm_dma(struct drm_device *dev, void *data, struct drm_file *file_priv); /* IRQ support (drm_irq.c) */ int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv); void drm_locked_tasklet(struct drm_device *dev, void (*tasklet)(struct drm_device *dev)); /* AGP/GART support (drm_agpsupport.c) */ int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_release_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_enable_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_free_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_agp_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Scatter Gather Support (drm_scatter.c) */ int drm_sg_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv); /* consistent PCI memory functions (drm_pci.c) */ drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size, size_t align, dma_addr_t maxaddr); void drm_pci_free(struct drm_device *dev, drm_dma_handle_t *dmah); /* Inline replacements for drm_alloc and friends */ static __inline__ void * drm_alloc(size_t size, struct malloc_type *area) { return malloc(size, area, M_NOWAIT); } static __inline__ void * drm_calloc(size_t nmemb, size_t size, struct malloc_type *area) { return malloc(size * nmemb, area, M_NOWAIT | M_ZERO); } static __inline__ void * drm_realloc(void *oldpt, size_t oldsize, size_t size, struct malloc_type *area) { return reallocf(oldpt, size, area, M_NOWAIT); } static __inline__ void drm_free(void *pt, size_t size, struct malloc_type *area) { free(pt, area); } /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap_wc(struct drm_local_map *map, struct drm_device *dev) { map->handle = drm_ioremap_wc(dev, map); } static __inline__ void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev) { map->handle = drm_ioremap(dev, map); } static __inline__ void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev) { if ( map->handle && map->size ) drm_ioremapfree(map); } static __inline__ struct drm_local_map * drm_core_findmap(struct drm_device *dev, unsigned long offset) { drm_local_map_t *map; DRM_SPINLOCK_ASSERT(&dev->dev_lock); TAILQ_FOREACH(map, &dev->maplist, link) { if (map->offset == offset) return map; } return NULL; } static __inline__ void drm_core_dropmap(struct drm_map *map) { } #endif /* __KERNEL__ */ #endif /* _DRM_P_H_ */ Index: projects/cambria/sys/dev/re/if_re.c =================================================================== --- projects/cambria/sys/dev/re/if_re.c (revision 186459) +++ projects/cambria/sys/dev/re/if_re.c (revision 186460) @@ -1,3125 +1,3127 @@ /*- * Copyright (c) 1997, 1998-2003 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * RealTek 8139C+/8169/8169S/8110S/8168/8111/8101E PCI NIC driver * * Written by Bill Paul * Senior Networking Software Engineer * Wind River Systems */ /* * This driver is designed to support RealTek's next generation of * 10/100 and 10/100/1000 PCI ethernet controllers. There are currently * seven devices in this family: the RTL8139C+, the RTL8169, the RTL8169S, * RTL8110S, the RTL8168, the RTL8111 and the RTL8101E. * * The 8139C+ is a 10/100 ethernet chip. It is backwards compatible * with the older 8139 family, however it also supports a special * C+ mode of operation that provides several new performance enhancing * features. These include: * * o Descriptor based DMA mechanism. Each descriptor represents * a single packet fragment. Data buffers may be aligned on * any byte boundary. * * o 64-bit DMA * * o TCP/IP checksum offload for both RX and TX * * o High and normal priority transmit DMA rings * * o VLAN tag insertion and extraction * * o TCP large send (segmentation offload) * * Like the 8139, the 8139C+ also has a built-in 10/100 PHY. The C+ * programming API is fairly straightforward. The RX filtering, EEPROM * access and PHY access is the same as it is on the older 8139 series * chips. * * The 8169 is a 64-bit 10/100/1000 gigabit ethernet MAC. It has almost the * same programming API and feature set as the 8139C+ with the following * differences and additions: * * o 1000Mbps mode * * o Jumbo frames * * o GMII and TBI ports/registers for interfacing with copper * or fiber PHYs * * o RX and TX DMA rings can have up to 1024 descriptors * (the 8139C+ allows a maximum of 64) * * o Slight differences in register layout from the 8139C+ * * The TX start and timer interrupt registers are at different locations * on the 8169 than they are on the 8139C+. Also, the status word in the * RX descriptor has a slightly different bit layout. The 8169 does not * have a built-in PHY. Most reference boards use a Marvell 88E1000 'Alaska' * copper gigE PHY. * * The 8169S/8110S 10/100/1000 devices have built-in copper gigE PHYs * (the 'S' stands for 'single-chip'). These devices have the same * programming API as the older 8169, but also have some vendor-specific * registers for the on-board PHY. The 8110S is a LAN-on-motherboard * part designed to be pin-compatible with the RealTek 8100 10/100 chip. * * This driver takes advantage of the RX and TX checksum offload and * VLAN tag insertion/extraction features. It also implements TX * interrupt moderation using the timer interrupt registers, which * significantly reduces TX interrupt load. There is also support * for jumbo frames, however the 8169/8169S/8110S can not transmit * jumbo frames larger than 7440, so the max MTU possible with this * driver is 7422 bytes. */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(re, pci, 1, 1, 1); MODULE_DEPEND(re, ether, 1, 1, 1); MODULE_DEPEND(re, miibus, 1, 1, 1); /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* Tunables. */ static int msi_disable = 1; TUNABLE_INT("hw.re.msi_disable", &msi_disable); #define RE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) /* * Various supported device vendors/types and their names. */ static struct rl_type re_devs[] = { { DLINK_VENDORID, DLINK_DEVICEID_528T, 0, "D-Link DGE-528(T) Gigabit Ethernet Adapter" }, { RT_VENDORID, RT_DEVICEID_8139, 0, "RealTek 8139C+ 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8101E, 0, "RealTek 8101E/8102E/8102EL PCIe 10/100baseTX" }, { RT_VENDORID, RT_DEVICEID_8168, 0, "RealTek 8168/8168B/8168C/8168CP/8168D/8111B/8111C/8111CP PCIe " "Gigabit Ethernet" }, { RT_VENDORID, RT_DEVICEID_8169, 0, "RealTek 8169/8169S/8169SB(L)/8110S/8110SB(L) Gigabit Ethernet" }, { RT_VENDORID, RT_DEVICEID_8169SC, 0, "RealTek 8169SC/8110SC Single-chip Gigabit Ethernet" }, { COREGA_VENDORID, COREGA_DEVICEID_CGLAPCIGT, 0, "Corega CG-LAPCIGT (RTL8169S) Gigabit Ethernet" }, { LINKSYS_VENDORID, LINKSYS_DEVICEID_EG1032, 0, "Linksys EG1032 (RTL8169S) Gigabit Ethernet" }, { USR_VENDORID, USR_DEVICEID_997902, 0, "US Robotics 997902 (RTL8169S) Gigabit Ethernet" } }; static struct rl_hwrev re_hwrevs[] = { { RL_HWREV_8139, RL_8139, "" }, { RL_HWREV_8139A, RL_8139, "A" }, { RL_HWREV_8139AG, RL_8139, "A-G" }, { RL_HWREV_8139B, RL_8139, "B" }, { RL_HWREV_8130, RL_8139, "8130" }, { RL_HWREV_8139C, RL_8139, "C" }, { RL_HWREV_8139D, RL_8139, "8139D/8100B/8100C" }, { RL_HWREV_8139CPLUS, RL_8139CPLUS, "C+"}, { RL_HWREV_8168_SPIN1, RL_8169, "8168"}, { RL_HWREV_8169, RL_8169, "8169"}, { RL_HWREV_8169S, RL_8169, "8169S"}, { RL_HWREV_8110S, RL_8169, "8110S"}, { RL_HWREV_8169_8110SB, RL_8169, "8169SB"}, { RL_HWREV_8169_8110SC, RL_8169, "8169SC"}, { RL_HWREV_8169_8110SBL, RL_8169, "8169SBL"}, { RL_HWREV_8100, RL_8139, "8100"}, { RL_HWREV_8101, RL_8139, "8101"}, { RL_HWREV_8100E, RL_8169, "8100E"}, { RL_HWREV_8101E, RL_8169, "8101E"}, { RL_HWREV_8102E, RL_8169, "8102E"}, { RL_HWREV_8102EL, RL_8169, "8102EL"}, { RL_HWREV_8168_SPIN2, RL_8169, "8168"}, { RL_HWREV_8168_SPIN3, RL_8169, "8168"}, { RL_HWREV_8168C, RL_8169, "8168C/8111C"}, { RL_HWREV_8168C_SPIN2, RL_8169, "8168C/8111C"}, { RL_HWREV_8168CP, RL_8169, "8168CP/8111CP"}, { RL_HWREV_8168D, RL_8169, "8168D"}, { 0, 0, NULL } }; static int re_probe (device_t); static int re_attach (device_t); static int re_detach (device_t); static int re_encap (struct rl_softc *, struct mbuf **); static void re_dma_map_addr (void *, bus_dma_segment_t *, int, int); static int re_allocmem (device_t, struct rl_softc *); static __inline void re_discard_rxbuf (struct rl_softc *, int); static int re_newbuf (struct rl_softc *, int); static int re_rx_list_init (struct rl_softc *); static int re_tx_list_init (struct rl_softc *); #ifdef RE_FIXUP_RX static __inline void re_fixup_rx (struct mbuf *); #endif static int re_rxeof (struct rl_softc *); static void re_txeof (struct rl_softc *); #ifdef DEVICE_POLLING static void re_poll (struct ifnet *, enum poll_cmd, int); static void re_poll_locked (struct ifnet *, enum poll_cmd, int); #endif static int re_intr (void *); static void re_tick (void *); static void re_tx_task (void *, int); static void re_int_task (void *, int); static void re_start (struct ifnet *); static int re_ioctl (struct ifnet *, u_long, caddr_t); static void re_init (void *); static void re_init_locked (struct rl_softc *); static void re_stop (struct rl_softc *); static void re_watchdog (struct rl_softc *); static int re_suspend (device_t); static int re_resume (device_t); static int re_shutdown (device_t); static int re_ifmedia_upd (struct ifnet *); static void re_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void re_eeprom_putbyte (struct rl_softc *, int); static void re_eeprom_getword (struct rl_softc *, int, u_int16_t *); static void re_read_eeprom (struct rl_softc *, caddr_t, int, int); static int re_gmii_readreg (device_t, int, int); static int re_gmii_writereg (device_t, int, int, int); static int re_miibus_readreg (device_t, int, int); static int re_miibus_writereg (device_t, int, int, int); static void re_miibus_statchg (device_t); static void re_setmulti (struct rl_softc *); static void re_reset (struct rl_softc *); static void re_setwol (struct rl_softc *); static void re_clrwol (struct rl_softc *); #ifdef RE_DIAG static int re_diag (struct rl_softc *); #endif static device_method_t re_methods[] = { /* Device interface */ DEVMETHOD(device_probe, re_probe), DEVMETHOD(device_attach, re_attach), DEVMETHOD(device_detach, re_detach), DEVMETHOD(device_suspend, re_suspend), DEVMETHOD(device_resume, re_resume), DEVMETHOD(device_shutdown, re_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, re_miibus_readreg), DEVMETHOD(miibus_writereg, re_miibus_writereg), DEVMETHOD(miibus_statchg, re_miibus_statchg), { 0, 0 } }; static driver_t re_driver = { "re", re_methods, sizeof(struct rl_softc) }; static devclass_t re_devclass; DRIVER_MODULE(re, pci, re_driver, re_devclass, 0, 0); DRIVER_MODULE(re, cardbus, re_driver, re_devclass, 0, 0); DRIVER_MODULE(miibus, re, miibus_driver, miibus_devclass, 0, 0); #define EE_SET(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) | x) #define EE_CLR(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) & ~x) /* * Send a read command and address to the EEPROM, check for ACK. */ static void re_eeprom_putbyte(struct rl_softc *sc, int addr) { int d, i; d = addr | (RL_9346_READ << sc->rl_eewidth); /* * Feed in each bit and strobe the clock. */ for (i = 1 << (sc->rl_eewidth + 3); i; i >>= 1) { if (d & i) { EE_SET(RL_EE_DATAIN); } else { EE_CLR(RL_EE_DATAIN); } DELAY(100); EE_SET(RL_EE_CLK); DELAY(150); EE_CLR(RL_EE_CLK); DELAY(100); } } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void re_eeprom_getword(struct rl_softc *sc, int addr, u_int16_t *dest) { int i; u_int16_t word = 0; /* * Send address of word we want to read. */ re_eeprom_putbyte(sc, addr); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { EE_SET(RL_EE_CLK); DELAY(100); if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) word |= i; EE_CLR(RL_EE_CLK); DELAY(100); } *dest = word; } /* * Read a sequence of words from the EEPROM. */ static void re_read_eeprom(struct rl_softc *sc, caddr_t dest, int off, int cnt) { int i; u_int16_t word = 0, *ptr; CSR_SETBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM); DELAY(100); for (i = 0; i < cnt; i++) { CSR_SETBIT_1(sc, RL_EECMD, RL_EE_SEL); re_eeprom_getword(sc, off + i, &word); CSR_CLRBIT_1(sc, RL_EECMD, RL_EE_SEL); ptr = (u_int16_t *)(dest + (i * 2)); *ptr = word; } CSR_CLRBIT_1(sc, RL_EECMD, RL_EEMODE_PROGRAM); } static int re_gmii_readreg(device_t dev, int phy, int reg) { struct rl_softc *sc; u_int32_t rval; int i; if (phy != 1) return (0); sc = device_get_softc(dev); /* Let the rgephy driver read the GMEDIASTAT register */ if (reg == RL_GMEDIASTAT) { rval = CSR_READ_1(sc, RL_GMEDIASTAT); return (rval); } CSR_WRITE_4(sc, RL_PHYAR, reg << 16); for (i = 0; i < RL_TIMEOUT; i++) { rval = CSR_READ_4(sc, RL_PHYAR); if (rval & RL_PHYAR_BUSY) break; DELAY(100); } if (i == RL_TIMEOUT) { device_printf(sc->rl_dev, "PHY read failed\n"); return (0); } return (rval & RL_PHYAR_PHYDATA); } static int re_gmii_writereg(device_t dev, int phy, int reg, int data) { struct rl_softc *sc; u_int32_t rval; int i; sc = device_get_softc(dev); CSR_WRITE_4(sc, RL_PHYAR, (reg << 16) | (data & RL_PHYAR_PHYDATA) | RL_PHYAR_BUSY); for (i = 0; i < RL_TIMEOUT; i++) { rval = CSR_READ_4(sc, RL_PHYAR); if (!(rval & RL_PHYAR_BUSY)) break; DELAY(100); } if (i == RL_TIMEOUT) { device_printf(sc->rl_dev, "PHY write failed\n"); return (0); } return (0); } static int re_miibus_readreg(device_t dev, int phy, int reg) { struct rl_softc *sc; u_int16_t rval = 0; u_int16_t re8139_reg = 0; sc = device_get_softc(dev); if (sc->rl_type == RL_8169) { rval = re_gmii_readreg(dev, phy, reg); return (rval); } /* Pretend the internal PHY is only at address 0 */ if (phy) { return (0); } switch (reg) { case MII_BMCR: re8139_reg = RL_BMCR; break; case MII_BMSR: re8139_reg = RL_BMSR; break; case MII_ANAR: re8139_reg = RL_ANAR; break; case MII_ANER: re8139_reg = RL_ANER; break; case MII_ANLPAR: re8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); /* * Allow the rlphy driver to read the media status * register. If we have a link partner which does not * support NWAY, this is the register which will tell * us the results of parallel detection. */ case RL_MEDIASTAT: rval = CSR_READ_1(sc, RL_MEDIASTAT); return (rval); default: device_printf(sc->rl_dev, "bad phy register\n"); return (0); } rval = CSR_READ_2(sc, re8139_reg); if (sc->rl_type == RL_8139CPLUS && re8139_reg == RL_BMCR) { /* 8139C+ has different bit layout. */ rval &= ~(BMCR_LOOP | BMCR_ISO); } return (rval); } static int re_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rl_softc *sc; u_int16_t re8139_reg = 0; int rval = 0; sc = device_get_softc(dev); if (sc->rl_type == RL_8169) { rval = re_gmii_writereg(dev, phy, reg, data); return (rval); } /* Pretend the internal PHY is only at address 0 */ if (phy) return (0); switch (reg) { case MII_BMCR: re8139_reg = RL_BMCR; if (sc->rl_type == RL_8139CPLUS) { /* 8139C+ has different bit layout. */ data &= ~(BMCR_LOOP | BMCR_ISO); } break; case MII_BMSR: re8139_reg = RL_BMSR; break; case MII_ANAR: re8139_reg = RL_ANAR; break; case MII_ANER: re8139_reg = RL_ANER; break; case MII_ANLPAR: re8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); break; default: device_printf(sc->rl_dev, "bad phy register\n"); return (0); } CSR_WRITE_2(sc, re8139_reg, data); return (0); } static void re_miibus_statchg(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->rl_miibus); ifp = sc->rl_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; sc->rl_flags &= ~RL_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->rl_flags |= RL_FLAG_LINK; break; case IFM_1000_T: if ((sc->rl_flags & RL_FLAG_FASTETHER) != 0) break; sc->rl_flags |= RL_FLAG_LINK; break; default: break; } } /* * RealTek controllers does not provide any interface to * Tx/Rx MACs for resolved speed, duplex and flow-control * parameters. */ } /* * Program the 64-bit multicast hash filter. */ static void re_setmulti(struct rl_softc *sc) { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; u_int32_t rxfilt; int mcnt = 0; RL_LOCK_ASSERT(sc); ifp = sc->rl_ifp; rxfilt = CSR_READ_4(sc, RL_RXCFG); rxfilt &= ~(RL_RXCFG_RX_ALLPHYS | RL_RXCFG_RX_MULTI); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { if (ifp->if_flags & IFF_PROMISC) rxfilt |= RL_RXCFG_RX_ALLPHYS; /* * Unlike other hardwares, we have to explicitly set * RL_RXCFG_RX_MULTI to receive multicast frames in * promiscuous mode. */ rxfilt |= RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, RL_MAR0, 0); CSR_WRITE_4(sc, RL_MAR4, 0); /* now program new ones */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } IF_ADDR_UNLOCK(ifp); if (mcnt) rxfilt |= RL_RXCFG_RX_MULTI; else rxfilt &= ~RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); /* * For some unfathomable reason, RealTek decided to reverse * the order of the multicast hash registers in the PCI Express * parts. This means we have to write the hash pattern in reverse * order for those devices. */ if ((sc->rl_flags & RL_FLAG_INVMAR) != 0) { CSR_WRITE_4(sc, RL_MAR0, bswap32(hashes[1])); CSR_WRITE_4(sc, RL_MAR4, bswap32(hashes[0])); } else { CSR_WRITE_4(sc, RL_MAR0, hashes[0]); CSR_WRITE_4(sc, RL_MAR4, hashes[1]); } } static void re_reset(struct rl_softc *sc) { int i; RL_LOCK_ASSERT(sc); CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); for (i = 0; i < RL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) break; } if (i == RL_TIMEOUT) device_printf(sc->rl_dev, "reset never completed!\n"); if ((sc->rl_flags & RL_FLAG_PHY8169) != 0) CSR_WRITE_1(sc, 0x82, 1); if ((sc->rl_flags & RL_FLAG_PHY8110S) != 0) { CSR_WRITE_1(sc, 0x82, 1); re_gmii_writereg(sc->rl_dev, 1, 0x0B, 0); } } #ifdef RE_DIAG /* * The following routine is designed to test for a defect on some * 32-bit 8169 cards. Some of these NICs have the REQ64# and ACK64# * lines connected to the bus, however for a 32-bit only card, they * should be pulled high. The result of this defect is that the * NIC will not work right if you plug it into a 64-bit slot: DMA * operations will be done with 64-bit transfers, which will fail * because the 64-bit data lines aren't connected. * * There's no way to work around this (short of talking a soldering * iron to the board), however we can detect it. The method we use * here is to put the NIC into digital loopback mode, set the receiver * to promiscuous mode, and then try to send a frame. We then compare * the frame data we sent to what was received. If the data matches, * then the NIC is working correctly, otherwise we know the user has * a defective NIC which has been mistakenly plugged into a 64-bit PCI * slot. In the latter case, there's no way the NIC can work correctly, * so we print out a message on the console and abort the device attach. */ static int re_diag(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; struct mbuf *m0; struct ether_header *eh; struct rl_desc *cur_rx; u_int16_t status; u_int32_t rxstat; int total_len, i, error = 0, phyaddr; u_int8_t dst[] = { 0x00, 'h', 'e', 'l', 'l', 'o' }; u_int8_t src[] = { 0x00, 'w', 'o', 'r', 'l', 'd' }; /* Allocate a single mbuf */ MGETHDR(m0, M_DONTWAIT, MT_DATA); if (m0 == NULL) return (ENOBUFS); RL_LOCK(sc); /* * Initialize the NIC in test mode. This sets the chip up * so that it can send and receive frames, but performs the * following special functions: * - Puts receiver in promiscuous mode * - Enables digital loopback mode * - Leaves interrupts turned off */ ifp->if_flags |= IFF_PROMISC; sc->rl_testmode = 1; re_init_locked(sc); sc->rl_flags |= RL_FLAG_LINK; if (sc->rl_type == RL_8169) phyaddr = 1; else phyaddr = 0; re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_RESET); for (i = 0; i < RL_TIMEOUT; i++) { status = re_miibus_readreg(sc->rl_dev, phyaddr, MII_BMCR); if (!(status & BMCR_RESET)) break; } re_miibus_writereg(sc->rl_dev, phyaddr, MII_BMCR, BMCR_LOOP); CSR_WRITE_2(sc, RL_ISR, RL_INTRS); DELAY(100000); /* Put some data in the mbuf */ eh = mtod(m0, struct ether_header *); bcopy ((char *)&dst, eh->ether_dhost, ETHER_ADDR_LEN); bcopy ((char *)&src, eh->ether_shost, ETHER_ADDR_LEN); eh->ether_type = htons(ETHERTYPE_IP); m0->m_pkthdr.len = m0->m_len = ETHER_MIN_LEN - ETHER_CRC_LEN; /* * Queue the packet, start transmission. * Note: IF_HANDOFF() ultimately calls re_start() for us. */ CSR_WRITE_2(sc, RL_ISR, 0xFFFF); RL_UNLOCK(sc); /* XXX: re_diag must not be called when in ALTQ mode */ IF_HANDOFF(&ifp->if_snd, m0, ifp); RL_LOCK(sc); m0 = NULL; /* Wait for it to propagate through the chip */ DELAY(100000); for (i = 0; i < RL_TIMEOUT; i++) { status = CSR_READ_2(sc, RL_ISR); CSR_WRITE_2(sc, RL_ISR, status); if ((status & (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) == (RL_ISR_TIMEOUT_EXPIRED|RL_ISR_RX_OK)) break; DELAY(10); } if (i == RL_TIMEOUT) { device_printf(sc->rl_dev, "diagnostic failed, failed to receive packet in" " loopback mode\n"); error = EIO; goto done; } /* * The packet should have been dumped into the first * entry in the RX DMA ring. Grab it from there. */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_POSTREAD); bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_desc[0].rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_desc[0].rx_dmamap); m0 = sc->rl_ldata.rl_rx_desc[0].rx_m; sc->rl_ldata.rl_rx_desc[0].rx_m = NULL; eh = mtod(m0, struct ether_header *); cur_rx = &sc->rl_ldata.rl_rx_list[0]; total_len = RL_RXBYTES(cur_rx); rxstat = le32toh(cur_rx->rl_cmdstat); if (total_len != ETHER_MIN_LEN) { device_printf(sc->rl_dev, "diagnostic failed, received short packet\n"); error = EIO; goto done; } /* Test that the received packet data matches what we sent. */ if (bcmp((char *)&eh->ether_dhost, (char *)&dst, ETHER_ADDR_LEN) || bcmp((char *)&eh->ether_shost, (char *)&src, ETHER_ADDR_LEN) || ntohs(eh->ether_type) != ETHERTYPE_IP) { device_printf(sc->rl_dev, "WARNING, DMA FAILURE!\n"); device_printf(sc->rl_dev, "expected TX data: %6D/%6D/0x%x\n", dst, ":", src, ":", ETHERTYPE_IP); device_printf(sc->rl_dev, "received RX data: %6D/%6D/0x%x\n", eh->ether_dhost, ":", eh->ether_shost, ":", ntohs(eh->ether_type)); device_printf(sc->rl_dev, "You may have a defective 32-bit " "NIC plugged into a 64-bit PCI slot.\n"); device_printf(sc->rl_dev, "Please re-install the NIC in a " "32-bit slot for proper operation.\n"); device_printf(sc->rl_dev, "Read the re(4) man page for more " "details.\n"); error = EIO; } done: /* Turn interface off, release resources */ sc->rl_testmode = 0; sc->rl_flags &= ~RL_FLAG_LINK; ifp->if_flags &= ~IFF_PROMISC; re_stop(sc); if (m0 != NULL) m_freem(m0); RL_UNLOCK(sc); return (error); } #endif /* * Probe for a RealTek 8139C+/8169/8110 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int re_probe(device_t dev) { struct rl_type *t; uint16_t devid, vendor; uint16_t revid, sdevid; int i; vendor = pci_get_vendor(dev); devid = pci_get_device(dev); revid = pci_get_revid(dev); sdevid = pci_get_subdevice(dev); if (vendor == LINKSYS_VENDORID && devid == LINKSYS_DEVICEID_EG1032) { if (sdevid != LINKSYS_SUBDEVICE_EG1032_REV3) { /* * Only attach to rev. 3 of the Linksys EG1032 adapter. * Rev. 2 is supported by sk(4). */ return (ENXIO); } } if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) { if (revid != 0x20) { /* 8139, let rl(4) take care of this device. */ return (ENXIO); } } t = re_devs; for (i = 0; i < sizeof(re_devs) / sizeof(re_devs[0]); i++, t++) { if (vendor == t->rl_vid && devid == t->rl_did) { device_set_desc(dev, t->rl_name); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } /* * Map a single buffer address. */ static void re_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *addr; if (error) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); addr = arg; *addr = segs->ds_addr; } static int re_allocmem(device_t dev, struct rl_softc *sc) { bus_size_t rx_list_size, tx_list_size; int error; int i; rx_list_size = sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc); tx_list_size = sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc); /* * Allocate the parent bus DMA tag appropriate for PCI. * In order to use DAC, RL_CPLUSCMD_PCI_DAC bit of RL_CPLUS_CMD * register should be set. However some RealTek chips are known * to be buggy on DAC handling, therefore disable DAC by limiting * DMA address space to 32bit. PCIe variants of RealTek chips * may not have the limitation but I took safer path. */ error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->rl_parent_tag); if (error) { device_printf(dev, "could not allocate parent DMA tag\n"); return (error); } /* * Allocate map for TX mbufs. */ error = bus_dma_tag_create(sc->rl_parent_tag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * RL_NTXSEGS, RL_NTXSEGS, 4096, 0, NULL, NULL, &sc->rl_ldata.rl_tx_mtag); if (error) { device_printf(dev, "could not allocate TX DMA tag\n"); return (error); } /* * Allocate map for RX mbufs. */ error = bus_dma_tag_create(sc->rl_parent_tag, sizeof(uint64_t), 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->rl_ldata.rl_rx_mtag); if (error) { device_printf(dev, "could not allocate RX DMA tag\n"); return (error); } /* * Allocate map for TX descriptor list. */ error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, tx_list_size, 1, tx_list_size, 0, NULL, NULL, &sc->rl_ldata.rl_tx_list_tag); if (error) { device_printf(dev, "could not allocate TX DMA ring tag\n"); return (error); } /* Allocate DMA'able memory for the TX ring */ error = bus_dmamem_alloc(sc->rl_ldata.rl_tx_list_tag, (void **)&sc->rl_ldata.rl_tx_list, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_ldata.rl_tx_list_map); if (error) { device_printf(dev, "could not allocate TX DMA ring\n"); return (error); } /* Load the map for the TX ring. */ sc->rl_ldata.rl_tx_list_addr = 0; error = bus_dmamap_load(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, sc->rl_ldata.rl_tx_list, tx_list_size, re_dma_map_addr, &sc->rl_ldata.rl_tx_list_addr, BUS_DMA_NOWAIT); if (error != 0 || sc->rl_ldata.rl_tx_list_addr == 0) { device_printf(dev, "could not load TX DMA ring\n"); return (ENOMEM); } /* Create DMA maps for TX buffers */ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) { error = bus_dmamap_create(sc->rl_ldata.rl_tx_mtag, 0, &sc->rl_ldata.rl_tx_desc[i].tx_dmamap); if (error) { device_printf(dev, "could not create DMA map for TX\n"); return (error); } } /* * Allocate map for RX descriptor list. */ error = bus_dma_tag_create(sc->rl_parent_tag, RL_RING_ALIGN, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, rx_list_size, 1, rx_list_size, 0, NULL, NULL, &sc->rl_ldata.rl_rx_list_tag); if (error) { device_printf(dev, "could not create RX DMA ring tag\n"); return (error); } /* Allocate DMA'able memory for the RX ring */ error = bus_dmamem_alloc(sc->rl_ldata.rl_rx_list_tag, (void **)&sc->rl_ldata.rl_rx_list, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_ldata.rl_rx_list_map); if (error) { device_printf(dev, "could not allocate RX DMA ring\n"); return (error); } /* Load the map for the RX ring. */ sc->rl_ldata.rl_rx_list_addr = 0; error = bus_dmamap_load(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, sc->rl_ldata.rl_rx_list, rx_list_size, re_dma_map_addr, &sc->rl_ldata.rl_rx_list_addr, BUS_DMA_NOWAIT); if (error != 0 || sc->rl_ldata.rl_rx_list_addr == 0) { device_printf(dev, "could not load RX DMA ring\n"); return (ENOMEM); } /* Create DMA maps for RX buffers */ error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0, &sc->rl_ldata.rl_rx_sparemap); if (error) { device_printf(dev, "could not create spare DMA map for RX\n"); return (error); } for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) { error = bus_dmamap_create(sc->rl_ldata.rl_rx_mtag, 0, &sc->rl_ldata.rl_rx_desc[i].rx_dmamap); if (error) { device_printf(dev, "could not create DMA map for RX\n"); return (error); } } return (0); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int re_attach(device_t dev) { u_char eaddr[ETHER_ADDR_LEN]; u_int16_t as[ETHER_ADDR_LEN / 2]; struct rl_softc *sc; struct ifnet *ifp; struct rl_hwrev *hw_rev; int hwrev; u_int16_t devid, re_did = 0; int error = 0, rid, i; int msic, reg; uint8_t cfg; sc = device_get_softc(dev); sc->rl_dev = dev; mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0); /* * Map control/status registers. */ pci_enable_busmaster(dev); devid = pci_get_device(dev); /* Prefer memory space register mapping over IO space. */ sc->rl_res_id = PCIR_BAR(1); sc->rl_res_type = SYS_RES_MEMORY; /* RTL8168/8101E seems to use different BARs. */ if (devid == RT_DEVICEID_8168 || devid == RT_DEVICEID_8101E) sc->rl_res_id = PCIR_BAR(2); sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type, &sc->rl_res_id, RF_ACTIVE); if (sc->rl_res == NULL) { sc->rl_res_id = PCIR_BAR(0); sc->rl_res_type = SYS_RES_IOPORT; sc->rl_res = bus_alloc_resource_any(dev, sc->rl_res_type, &sc->rl_res_id, RF_ACTIVE); if (sc->rl_res == NULL) { device_printf(dev, "couldn't map ports/memory\n"); error = ENXIO; goto fail; } } sc->rl_btag = rman_get_bustag(sc->rl_res); sc->rl_bhandle = rman_get_bushandle(sc->rl_res); msic = 0; if (pci_find_extcap(dev, PCIY_EXPRESS, ®) == 0) { sc->rl_flags |= RL_FLAG_PCIE; msic = pci_msi_count(dev); if (bootverbose) device_printf(dev, "MSI count : %d\n", msic); } if (msic == RL_MSI_MESSAGES && msi_disable == 0) { if (pci_alloc_msi(dev, &msic) == 0) { if (msic == RL_MSI_MESSAGES) { device_printf(dev, "Using %d MSI messages\n", msic); sc->rl_flags |= RL_FLAG_MSI; /* Explicitly set MSI enable bit. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); cfg = CSR_READ_1(sc, RL_CFG2); cfg |= RL_CFG2_MSI; CSR_WRITE_1(sc, RL_CFG2, cfg); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); } else pci_release_msi(dev); } } /* Allocate interrupt */ if ((sc->rl_flags & RL_FLAG_MSI) == 0) { rid = 0; sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->rl_irq[0] == NULL) { device_printf(dev, "couldn't allocate IRQ resources\n"); error = ENXIO; goto fail; } } else { for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) { sc->rl_irq[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->rl_irq[i] == NULL) { device_printf(dev, "couldn't llocate IRQ resources for " "message %d\n", rid); error = ENXIO; goto fail; } } } if ((sc->rl_flags & RL_FLAG_MSI) == 0) { CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); cfg = CSR_READ_1(sc, RL_CFG2); if ((cfg & RL_CFG2_MSI) != 0) { device_printf(dev, "turning off MSI enable bit.\n"); cfg &= ~RL_CFG2_MSI; CSR_WRITE_1(sc, RL_CFG2, cfg); } CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); } /* Reset the adapter. */ RL_LOCK(sc); re_reset(sc); RL_UNLOCK(sc); hw_rev = re_hwrevs; hwrev = CSR_READ_4(sc, RL_TXCFG); device_printf(dev, "Chip rev. 0x%08x\n", hwrev & 0x7c800000); device_printf(dev, "MAC rev. 0x%08x\n", hwrev & 0x00700000); hwrev &= RL_TXCFG_HWREV; while (hw_rev->rl_desc != NULL) { if (hw_rev->rl_rev == hwrev) { sc->rl_type = hw_rev->rl_type; break; } hw_rev++; } if (hw_rev->rl_desc == NULL) { device_printf(dev, "Unknown H/W revision: 0x%08x\n", hwrev); error = ENXIO; goto fail; } switch (hw_rev->rl_rev) { case RL_HWREV_8139CPLUS: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_FASTETHER; break; case RL_HWREV_8110S: sc->rl_flags |= RL_FLAG_PHY8110S; break; case RL_HWREV_8100E: case RL_HWREV_8101E: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_INVMAR | RL_FLAG_PHYWAKE | RL_FLAG_FASTETHER; break; case RL_HWREV_8102E: case RL_HWREV_8102EL: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_INVMAR | RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_FASTETHER | RL_FLAG_CMDSTOP; break; case RL_HWREV_8168_SPIN1: case RL_HWREV_8168_SPIN2: sc->rl_flags |= RL_FLAG_WOLRXENB; /* FALLTHROUGH */ case RL_HWREV_8168_SPIN3: sc->rl_flags |= RL_FLAG_INVMAR | RL_FLAG_PHYWAKE | RL_FLAG_MACSTAT; break; case RL_HWREV_8168C_SPIN2: sc->rl_flags |= RL_FLAG_MACSLEEP; /* FALLTHROUGH */ case RL_HWREV_8168C: if ((hwrev & 0x00700000) == 0x00200000) sc->rl_flags |= RL_FLAG_MACSLEEP; /* FALLTHROUGH */ case RL_HWREV_8168CP: case RL_HWREV_8168D: sc->rl_flags |= RL_FLAG_INVMAR | RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | RL_FLAG_CMDSTOP; /* * These controllers support jumbo frame but it seems * that enabling it requires touching additional magic * registers. Depending on MAC revisions some * controllers need to disable checksum offload. So * disable jumbo frame until I have better idea what * it really requires to make it support. * RTL8168C/CP : supports up to 6KB jumbo frame. * RTL8111C/CP : supports up to 9KB jumbo frame. */ sc->rl_flags |= RL_FLAG_NOJUMBO; break; case RL_HWREV_8169: case RL_HWREV_8169S: sc->rl_flags |= RL_FLAG_PHY8169; break; case RL_HWREV_8169_8110SB: case RL_HWREV_8169_8110SC: case RL_HWREV_8169_8110SBL: sc->rl_flags |= RL_FLAG_PHYWAKE | RL_FLAG_PHY8169; break; default: break; } /* Enable PME. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); cfg = CSR_READ_1(sc, RL_CFG1); cfg |= RL_CFG1_PME; CSR_WRITE_1(sc, RL_CFG1, cfg); cfg = CSR_READ_1(sc, RL_CFG5); cfg &= RL_CFG5_PME_STS; CSR_WRITE_1(sc, RL_CFG5, cfg); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); if ((sc->rl_flags & RL_FLAG_PAR) != 0) { /* * XXX Should have a better way to extract station * address from EEPROM. */ for (i = 0; i < ETHER_ADDR_LEN; i++) eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i); } else { sc->rl_eewidth = RL_9356_ADDR_LEN; re_read_eeprom(sc, (caddr_t)&re_did, 0, 1); if (re_did != 0x8129) sc->rl_eewidth = RL_9346_ADDR_LEN; /* * Get station address from the EEPROM. */ re_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3); for (i = 0; i < ETHER_ADDR_LEN / 2; i++) as[i] = le16toh(as[i]); bcopy(as, eaddr, sizeof(eaddr)); } if (sc->rl_type == RL_8169) { /* Set RX length mask and number of descriptors. */ sc->rl_rxlenmask = RL_RDESC_STAT_GFRAGLEN; sc->rl_txstart = RL_GTXSTART; sc->rl_ldata.rl_tx_desc_cnt = RL_8169_TX_DESC_CNT; sc->rl_ldata.rl_rx_desc_cnt = RL_8169_RX_DESC_CNT; } else { /* Set RX length mask and number of descriptors. */ sc->rl_rxlenmask = RL_RDESC_STAT_FRAGLEN; sc->rl_txstart = RL_TXSTART; sc->rl_ldata.rl_tx_desc_cnt = RL_8139_TX_DESC_CNT; sc->rl_ldata.rl_rx_desc_cnt = RL_8139_RX_DESC_CNT; } error = re_allocmem(dev, sc); if (error) goto fail; ifp = sc->rl_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } /* Take controller out of deep sleep mode. */ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80) CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) | 0x01); else CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) & ~0x01); } /* Take PHY out of power down mode. */ if ((sc->rl_flags & RL_FLAG_PHYWAKE) != 0) { re_gmii_writereg(dev, 1, 0x1f, 0); re_gmii_writereg(dev, 1, 0x0e, 0); } /* Do MII setup */ if (mii_phy_probe(dev, &sc->rl_miibus, re_ifmedia_upd, re_ifmedia_sts)) { device_printf(dev, "MII without any phy!\n"); error = ENXIO; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = re_ioctl; ifp->if_start = re_start; ifp->if_hwassist = RE_CSUM_FEATURES; ifp->if_capabilities = IFCAP_HWCSUM; ifp->if_capenable = ifp->if_capabilities; ifp->if_init = re_init; IFQ_SET_MAXLEN(&ifp->if_snd, RL_IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = RL_IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); TASK_INIT(&sc->rl_txtask, 1, re_tx_task, ifp); TASK_INIT(&sc->rl_inttask, 0, re_int_task, sc); /* * XXX * Still have no idea how to make TSO work on 8168C, 8168CP, * 8111C and 8111CP. */ if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) { ifp->if_hwassist |= CSUM_TSO; ifp->if_capabilities |= IFCAP_TSO4; } /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* VLAN capability setup */ ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING; if (ifp->if_capabilities & IFCAP_HWCSUM) ifp->if_capabilities |= IFCAP_VLAN_HWCSUM; /* Enable WOL if PM is supported. */ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, ®) == 0) ifp->if_capabilities |= IFCAP_WOL; ifp->if_capenable = ifp->if_capabilities; /* * Don't enable TSO by default. Under certain * circumtances the controller generated corrupted * packets in TSO size. */ ifp->if_hwassist &= ~CSUM_TSO; ifp->if_capenable &= ~IFCAP_TSO4; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif /* * Tell the upper layer(s) we support long frames. * Must appear after the call to ether_ifattach() because * ether_ifattach() sets ifi_hdrlen to the default value. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); #ifdef RE_DIAG /* * Perform hardware diagnostic on the original RTL8169. * Some 32-bit cards were incorrectly wired and would * malfunction if plugged into a 64-bit slot. */ if (hwrev == RL_HWREV_8169) { error = re_diag(sc); if (error) { device_printf(dev, "attach aborted due to hardware diag failure\n"); ether_ifdetach(ifp); goto fail; } } #endif /* Hook interrupt last to avoid having to lock softc */ if ((sc->rl_flags & RL_FLAG_MSI) == 0) error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, &sc->rl_intrhand[0]); else { for (i = 0; i < RL_MSI_MESSAGES; i++) { error = bus_setup_intr(dev, sc->rl_irq[i], INTR_TYPE_NET | INTR_MPSAFE, re_intr, NULL, sc, &sc->rl_intrhand[i]); if (error != 0) break; } } if (error) { device_printf(dev, "couldn't set up irq\n"); ether_ifdetach(ifp); } fail: if (error) re_detach(dev); return (error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int re_detach(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; int i, rid; sc = device_get_softc(dev); ifp = sc->rl_ifp; KASSERT(mtx_initialized(&sc->rl_mtx), ("re mutex not initialized")); /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif RL_LOCK(sc); #if 0 sc->suspended = 1; #endif re_stop(sc); RL_UNLOCK(sc); callout_drain(&sc->rl_stat_callout); taskqueue_drain(taskqueue_fast, &sc->rl_inttask); taskqueue_drain(taskqueue_fast, &sc->rl_txtask); /* * Force off the IFF_UP flag here, in case someone * still had a BPF descriptor attached to this * interface. If they do, ether_ifdetach() will cause * the BPF code to try and clear the promisc mode * flag, which will bubble down to re_ioctl(), * which will try to call re_init() again. This will * turn the NIC back on and restart the MII ticker, * which will panic the system when the kernel tries * to invoke the re_tick() function that isn't there * anymore. */ ifp->if_flags &= ~IFF_UP; ether_ifdetach(ifp); } if (sc->rl_miibus) device_delete_child(dev, sc->rl_miibus); bus_generic_detach(dev); /* * The rest is resource deallocation, so we should already be * stopped here. */ for (i = 0; i < RL_MSI_MESSAGES; i++) { if (sc->rl_intrhand[i] != NULL) { bus_teardown_intr(dev, sc->rl_irq[i], sc->rl_intrhand[i]); sc->rl_intrhand[i] = NULL; } } if (ifp != NULL) if_free(ifp); if ((sc->rl_flags & RL_FLAG_MSI) == 0) { if (sc->rl_irq[0] != NULL) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq[0]); sc->rl_irq[0] = NULL; } } else { for (i = 0, rid = 1; i < RL_MSI_MESSAGES; i++, rid++) { if (sc->rl_irq[i] != NULL) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->rl_irq[i]); sc->rl_irq[i] = NULL; } } pci_release_msi(dev); } if (sc->rl_res) bus_release_resource(dev, sc->rl_res_type, sc->rl_res_id, sc->rl_res); /* Unload and free the RX DMA ring memory and map */ if (sc->rl_ldata.rl_rx_list_tag) { bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map); bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list, sc->rl_ldata.rl_rx_list_map); bus_dma_tag_destroy(sc->rl_ldata.rl_rx_list_tag); } /* Unload and free the TX DMA ring memory and map */ if (sc->rl_ldata.rl_tx_list_tag) { bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map); bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list, sc->rl_ldata.rl_tx_list_map); bus_dma_tag_destroy(sc->rl_ldata.rl_tx_list_tag); } /* Destroy all the RX and TX buffer maps */ if (sc->rl_ldata.rl_tx_mtag) { for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) bus_dmamap_destroy(sc->rl_ldata.rl_tx_mtag, sc->rl_ldata.rl_tx_desc[i].tx_dmamap); bus_dma_tag_destroy(sc->rl_ldata.rl_tx_mtag); } if (sc->rl_ldata.rl_rx_mtag) { for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_desc[i].rx_dmamap); if (sc->rl_ldata.rl_rx_sparemap) bus_dmamap_destroy(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_sparemap); bus_dma_tag_destroy(sc->rl_ldata.rl_rx_mtag); } /* Unload and free the stats buffer and map */ if (sc->rl_ldata.rl_stag) { bus_dmamap_unload(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_rx_list_map); bus_dmamem_free(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_stats, sc->rl_ldata.rl_smap); bus_dma_tag_destroy(sc->rl_ldata.rl_stag); } if (sc->rl_parent_tag) bus_dma_tag_destroy(sc->rl_parent_tag); mtx_destroy(&sc->rl_mtx); return (0); } static __inline void re_discard_rxbuf(struct rl_softc *sc, int idx) { struct rl_desc *desc; struct rl_rxdesc *rxd; uint32_t cmdstat; rxd = &sc->rl_ldata.rl_rx_desc[idx]; desc = &sc->rl_ldata.rl_rx_list[idx]; desc->rl_vlanctl = 0; cmdstat = rxd->rx_size; if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1) cmdstat |= RL_RDESC_CMD_EOR; desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN); } static int re_newbuf(struct rl_softc *sc, int idx) { struct mbuf *m; struct rl_rxdesc *rxd; bus_dma_segment_t segs[1]; bus_dmamap_t map; struct rl_desc *desc; uint32_t cmdstat; int error, nsegs; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; #ifdef RE_FIXUP_RX /* * This is part of an evil trick to deal with non-x86 platforms. * The RealTek chip requires RX buffers to be aligned on 64-bit * boundaries, but that will hose non-x86 machines. To get around * this, we leave some empty space at the start of each buffer * and for non-x86 hosts, we copy the buffer back six bytes * to achieve word alignment. This is slightly more efficient * than allocating a new buffer, copying the contents, and * discarding the old buffer. */ m_adj(m, RE_ETHER_ALIGN); #endif error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_rx_mtag, sc->rl_ldata.rl_rx_sparemap, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { m_freem(m); return (ENOBUFS); } KASSERT(nsegs == 1, ("%s: %d segment returned!", __func__, nsegs)); rxd = &sc->rl_ldata.rl_rx_desc[idx]; if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap); } rxd->rx_m = m; map = rxd->rx_dmamap; rxd->rx_dmamap = sc->rl_ldata.rl_rx_sparemap; rxd->rx_size = segs[0].ds_len; sc->rl_ldata.rl_rx_sparemap = map; bus_dmamap_sync(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap, BUS_DMASYNC_PREREAD); desc = &sc->rl_ldata.rl_rx_list[idx]; desc->rl_vlanctl = 0; desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[0].ds_addr)); desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[0].ds_addr)); cmdstat = segs[0].ds_len; if (idx == sc->rl_ldata.rl_rx_desc_cnt - 1) cmdstat |= RL_RDESC_CMD_EOR; desc->rl_cmdstat = htole32(cmdstat | RL_RDESC_CMD_OWN); return (0); } #ifdef RE_FIXUP_RX static __inline void re_fixup_rx(struct mbuf *m) { int i; uint16_t *src, *dst; src = mtod(m, uint16_t *); dst = src - (RE_ETHER_ALIGN - ETHER_ALIGN) / sizeof *src; for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) *dst++ = *src++; m->m_data -= RE_ETHER_ALIGN - ETHER_ALIGN; } #endif static int re_tx_list_init(struct rl_softc *sc) { struct rl_desc *desc; int i; RL_LOCK_ASSERT(sc); bzero(sc->rl_ldata.rl_tx_list, sc->rl_ldata.rl_tx_desc_cnt * sizeof(struct rl_desc)); for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) sc->rl_ldata.rl_tx_desc[i].tx_m = NULL; /* Set EOR. */ desc = &sc->rl_ldata.rl_tx_list[sc->rl_ldata.rl_tx_desc_cnt - 1]; desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOR); bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->rl_ldata.rl_tx_prodidx = 0; sc->rl_ldata.rl_tx_considx = 0; sc->rl_ldata.rl_tx_free = sc->rl_ldata.rl_tx_desc_cnt; return (0); } static int re_rx_list_init(struct rl_softc *sc) { int error, i; bzero(sc->rl_ldata.rl_rx_list, sc->rl_ldata.rl_rx_desc_cnt * sizeof(struct rl_desc)); for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) { sc->rl_ldata.rl_rx_desc[i].rx_m = NULL; if ((error = re_newbuf(sc, i)) != 0) return (error); } /* Flush the RX descriptors */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); sc->rl_ldata.rl_rx_prodidx = 0; sc->rl_head = sc->rl_tail = NULL; return (0); } /* * RX handler for C+ and 8169. For the gigE chips, we support * the reception of jumbo frames that have been fragmented * across multiple 2K mbuf cluster buffers. */ static int re_rxeof(struct rl_softc *sc) { struct mbuf *m; struct ifnet *ifp; int i, total_len; struct rl_desc *cur_rx; u_int32_t rxstat, rxvlan; int maxpkt = 16; RL_LOCK_ASSERT(sc); ifp = sc->rl_ifp; /* Invalidate the descriptor memory */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (i = sc->rl_ldata.rl_rx_prodidx; maxpkt > 0; i = RL_RX_DESC_NXT(sc, i)) { cur_rx = &sc->rl_ldata.rl_rx_list[i]; rxstat = le32toh(cur_rx->rl_cmdstat); if ((rxstat & RL_RDESC_STAT_OWN) != 0) break; total_len = rxstat & sc->rl_rxlenmask; rxvlan = le32toh(cur_rx->rl_vlanctl); m = sc->rl_ldata.rl_rx_desc[i].rx_m; if (!(rxstat & RL_RDESC_STAT_EOF)) { if (re_newbuf(sc, i) != 0) { /* * If this is part of a multi-fragment packet, * discard all the pieces. */ if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } re_discard_rxbuf(sc, i); continue; } m->m_len = RE_RX_DESC_BUFLEN; if (sc->rl_head == NULL) sc->rl_head = sc->rl_tail = m; else { m->m_flags &= ~M_PKTHDR; sc->rl_tail->m_next = m; sc->rl_tail = m; } continue; } /* * NOTE: for the 8139C+, the frame length field * is always 12 bits in size, but for the gigE chips, * it is 13 bits (since the max RX frame length is 16K). * Unfortunately, all 32 bits in the status word * were already used, so to make room for the extra * length bit, RealTek took out the 'frame alignment * error' bit and shifted the other status bits * over one slot. The OWN, EOR, FS and LS bits are * still in the same places. We have already extracted * the frame length and checked the OWN bit, so rather * than using an alternate bit mapping, we shift the * status bits one space to the right so we can evaluate * them using the 8169 status as though it was in the * same format as that of the 8139C+. */ if (sc->rl_type == RL_8169) rxstat >>= 1; /* * if total_len > 2^13-1, both _RXERRSUM and _GIANT will be * set, but if CRC is clear, it will still be a valid frame. */ if (rxstat & RL_RDESC_STAT_RXERRSUM && !(total_len > 8191 && (rxstat & RL_RDESC_STAT_ERRS) == RL_RDESC_STAT_GIANT)) { ifp->if_ierrors++; /* * If this is part of a multi-fragment packet, * discard all the pieces. */ if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } re_discard_rxbuf(sc, i); continue; } /* * If allocating a replacement mbuf fails, * reload the current one. */ if (re_newbuf(sc, i) != 0) { ifp->if_iqdrops++; if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } re_discard_rxbuf(sc, i); continue; } if (sc->rl_head != NULL) { m->m_len = total_len % RE_RX_DESC_BUFLEN; if (m->m_len == 0) m->m_len = RE_RX_DESC_BUFLEN; /* * Special case: if there's 4 bytes or less * in this buffer, the mbuf can be discarded: * the last 4 bytes is the CRC, which we don't * care about anyway. */ if (m->m_len <= ETHER_CRC_LEN) { sc->rl_tail->m_len -= (ETHER_CRC_LEN - m->m_len); m_freem(m); } else { m->m_len -= ETHER_CRC_LEN; m->m_flags &= ~M_PKTHDR; sc->rl_tail->m_next = m; } m = sc->rl_head; sc->rl_head = sc->rl_tail = NULL; m->m_pkthdr.len = total_len - ETHER_CRC_LEN; } else m->m_pkthdr.len = m->m_len = (total_len - ETHER_CRC_LEN); #ifdef RE_FIXUP_RX re_fixup_rx(m); #endif ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; /* Do RX checksumming if enabled */ if (ifp->if_capenable & IFCAP_RXCSUM) { if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) { /* Check IP header checksum */ if (rxstat & RL_RDESC_STAT_PROTOID) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (!(rxstat & RL_RDESC_STAT_IPSUMBAD)) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; /* Check TCP/UDP checksum */ if ((RL_TCPPKT(rxstat) && !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) || (RL_UDPPKT(rxstat) && !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID|CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } else { /* * RTL8168C/RTL816CP/RTL8111C/RTL8111CP */ if ((rxstat & RL_RDESC_STAT_PROTOID) && (rxvlan & RL_RDESC_IPV4)) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (!(rxstat & RL_RDESC_STAT_IPSUMBAD) && (rxvlan & RL_RDESC_IPV4)) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if (((rxstat & RL_RDESC_STAT_TCP) && !(rxstat & RL_RDESC_STAT_TCPSUMBAD)) || ((rxstat & RL_RDESC_STAT_UDP) && !(rxstat & RL_RDESC_STAT_UDPSUMBAD))) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID|CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } } maxpkt--; if (rxvlan & RL_RDESC_VLANCTL_TAG) { m->m_pkthdr.ether_vtag = bswap16((rxvlan & RL_RDESC_VLANCTL_DATA)); m->m_flags |= M_VLANTAG; } RL_UNLOCK(sc); (*ifp->if_input)(ifp, m); RL_LOCK(sc); } /* Flush the RX DMA ring */ bus_dmamap_sync(sc->rl_ldata.rl_rx_list_tag, sc->rl_ldata.rl_rx_list_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); sc->rl_ldata.rl_rx_prodidx = i; if (maxpkt) return(EAGAIN); return(0); } static void re_txeof(struct rl_softc *sc) { struct ifnet *ifp; struct rl_txdesc *txd; u_int32_t txstat; int cons; cons = sc->rl_ldata.rl_tx_considx; if (cons == sc->rl_ldata.rl_tx_prodidx) return; ifp = sc->rl_ifp; /* Invalidate the TX descriptor list */ bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (; cons != sc->rl_ldata.rl_tx_prodidx; cons = RL_TX_DESC_NXT(sc, cons)) { txstat = le32toh(sc->rl_ldata.rl_tx_list[cons].rl_cmdstat); if (txstat & RL_TDESC_STAT_OWN) break; /* * We only stash mbufs in the last descriptor * in a fragment chain, which also happens to * be the only place where the TX status bits * are valid. */ if (txstat & RL_TDESC_CMD_EOF) { txd = &sc->rl_ldata.rl_tx_desc[cons]; bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap); KASSERT(txd->tx_m != NULL, ("%s: freeing NULL mbufs!", __func__)); m_freem(txd->tx_m); txd->tx_m = NULL; if (txstat & (RL_TDESC_STAT_EXCESSCOL| RL_TDESC_STAT_COLCNT)) ifp->if_collisions++; if (txstat & RL_TDESC_STAT_TXERRSUM) ifp->if_oerrors++; else ifp->if_opackets++; } sc->rl_ldata.rl_tx_free++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } sc->rl_ldata.rl_tx_considx = cons; /* No changes made to the TX ring, so no flush needed */ if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) { #ifdef RE_TX_MODERATION /* * If not all descriptors have been reaped yet, reload * the timer so that we will eventually get another * interrupt that will cause us to re-enter this routine. * This is done in case the transmitter has gone idle. */ CSR_WRITE_4(sc, RL_TIMERCNT, 1); #endif } else sc->rl_watchdog_timer = 0; } static void re_tick(void *xsc) { struct rl_softc *sc; struct mii_data *mii; sc = xsc; RL_LOCK_ASSERT(sc); mii = device_get_softc(sc->rl_miibus); mii_tick(mii); + if ((sc->rl_flags & RL_FLAG_LINK) == 0) + re_miibus_statchg(sc->rl_dev); re_watchdog(sc); callout_reset(&sc->rl_stat_callout, hz, re_tick, sc); } #ifdef DEVICE_POLLING static void re_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; RL_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) re_poll_locked(ifp, cmd, count); RL_UNLOCK(sc); } static void re_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; RL_LOCK_ASSERT(sc); sc->rxcycles = count; re_rxeof(sc); re_txeof(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int16_t status; status = CSR_READ_2(sc, RL_ISR); if (status == 0xffff) return; if (status) CSR_WRITE_2(sc, RL_ISR, status); if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) && (sc->rl_flags & RL_FLAG_PCIE)) CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); /* * XXX check behaviour on receiver stalls. */ if (status & RL_ISR_SYSTEM_ERR) re_init_locked(sc); } } #endif /* DEVICE_POLLING */ static int re_intr(void *arg) { struct rl_softc *sc; uint16_t status; sc = arg; status = CSR_READ_2(sc, RL_ISR); if (status == 0xFFFF || (status & RL_INTRS_CPLUS) == 0) return (FILTER_STRAY); CSR_WRITE_2(sc, RL_IMR, 0); taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); return (FILTER_HANDLED); } static void re_int_task(void *arg, int npending) { struct rl_softc *sc; struct ifnet *ifp; u_int16_t status; int rval = 0; sc = arg; ifp = sc->rl_ifp; RL_LOCK(sc); status = CSR_READ_2(sc, RL_ISR); CSR_WRITE_2(sc, RL_ISR, status); if (sc->suspended || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { RL_UNLOCK(sc); return; } #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { RL_UNLOCK(sc); return; } #endif if (status & (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_FIFO_OFLOW)) rval = re_rxeof(sc); /* * Some chips will ignore a second TX request issued * while an existing transmission is in progress. If * the transmitter goes idle but there are still * packets waiting to be sent, we need to restart the * channel here to flush them out. This only seems to * be required with the PCIe devices. */ if ((status & (RL_ISR_TX_OK | RL_ISR_TX_DESC_UNAVAIL)) && (sc->rl_flags & RL_FLAG_PCIE)) CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); if (status & ( #ifdef RE_TX_MODERATION RL_ISR_TIMEOUT_EXPIRED| #else RL_ISR_TX_OK| #endif RL_ISR_TX_ERR|RL_ISR_TX_DESC_UNAVAIL)) re_txeof(sc); if (status & RL_ISR_SYSTEM_ERR) re_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); RL_UNLOCK(sc); if ((CSR_READ_2(sc, RL_ISR) & RL_INTRS_CPLUS) || rval) { taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_inttask); return; } CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); } static int re_encap(struct rl_softc *sc, struct mbuf **m_head) { struct rl_txdesc *txd, *txd_last; bus_dma_segment_t segs[RL_NTXSEGS]; bus_dmamap_t map; struct mbuf *m_new; struct rl_desc *desc; int nsegs, prod; int i, error, ei, si; int padlen; uint32_t cmdstat, csum_flags, vlanctl; RL_LOCK_ASSERT(sc); M_ASSERTPKTHDR((*m_head)); /* * With some of the RealTek chips, using the checksum offload * support in conjunction with the autopadding feature results * in the transmission of corrupt frames. For example, if we * need to send a really small IP fragment that's less than 60 * bytes in size, and IP header checksumming is enabled, the * resulting ethernet frame that appears on the wire will * have garbled payload. To work around this, if TX IP checksum * offload is enabled, we always manually pad short frames out * to the minimum ethernet frame size. */ if ((sc->rl_flags & RL_FLAG_DESCV2) == 0 && (*m_head)->m_pkthdr.len < RL_IP4CSUMTX_PADLEN && ((*m_head)->m_pkthdr.csum_flags & CSUM_IP) != 0) { padlen = RL_MIN_FRAMELEN - (*m_head)->m_pkthdr.len; if (M_WRITABLE(*m_head) == 0) { /* Get a writable copy. */ m_new = m_dup(*m_head, M_DONTWAIT); m_freem(*m_head); if (m_new == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m_new; } if ((*m_head)->m_next != NULL || M_TRAILINGSPACE(*m_head) < padlen) { m_new = m_defrag(*m_head, M_DONTWAIT); if (m_new == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } } else m_new = *m_head; /* * Manually pad short frames, and zero the pad space * to avoid leaking data. */ bzero(mtod(m_new, char *) + m_new->m_pkthdr.len, padlen); m_new->m_pkthdr.len += padlen; m_new->m_len = m_new->m_pkthdr.len; *m_head = m_new; } prod = sc->rl_ldata.rl_tx_prodidx; txd = &sc->rl_ldata.rl_tx_desc[prod]; error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { m_new = m_collapse(*m_head, M_DONTWAIT, RL_NTXSEGS); if (m_new == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } *m_head = m_new; error = bus_dmamap_load_mbuf_sg(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (error); } } else if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } /* Check for number of available descriptors. */ if (sc->rl_ldata.rl_tx_free - nsegs <= 1) { bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap); return (ENOBUFS); } bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, BUS_DMASYNC_PREWRITE); /* * Set up checksum offload. Note: checksum offload bits must * appear in all descriptors of a multi-descriptor transmit * attempt. This is according to testing done with an 8169 * chip. This is a requirement. */ vlanctl = 0; csum_flags = 0; if (((*m_head)->m_pkthdr.csum_flags & CSUM_TSO) != 0) csum_flags = RL_TDESC_CMD_LGSEND | ((uint32_t)(*m_head)->m_pkthdr.tso_segsz << RL_TDESC_CMD_MSSVAL_SHIFT); else { /* * Unconditionally enable IP checksum if TCP or UDP * checksum is required. Otherwise, TCP/UDP checksum * does't make effects. */ if (((*m_head)->m_pkthdr.csum_flags & RE_CSUM_FEATURES) != 0) { if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) { csum_flags |= RL_TDESC_CMD_IPCSUM; if (((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) != 0) csum_flags |= RL_TDESC_CMD_TCPCSUM; if (((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) != 0) csum_flags |= RL_TDESC_CMD_UDPCSUM; } else { vlanctl |= RL_TDESC_CMD_IPCSUMV2; if (((*m_head)->m_pkthdr.csum_flags & CSUM_TCP) != 0) vlanctl |= RL_TDESC_CMD_TCPCSUMV2; if (((*m_head)->m_pkthdr.csum_flags & CSUM_UDP) != 0) vlanctl |= RL_TDESC_CMD_UDPCSUMV2; } } } /* * Set up hardware VLAN tagging. Note: vlan tag info must * appear in all descriptors of a multi-descriptor * transmission attempt. */ if ((*m_head)->m_flags & M_VLANTAG) vlanctl |= bswap16((*m_head)->m_pkthdr.ether_vtag) | RL_TDESC_VLANCTL_TAG; si = prod; for (i = 0; i < nsegs; i++, prod = RL_TX_DESC_NXT(sc, prod)) { desc = &sc->rl_ldata.rl_tx_list[prod]; desc->rl_vlanctl = htole32(vlanctl); desc->rl_bufaddr_lo = htole32(RL_ADDR_LO(segs[i].ds_addr)); desc->rl_bufaddr_hi = htole32(RL_ADDR_HI(segs[i].ds_addr)); cmdstat = segs[i].ds_len; if (i != 0) cmdstat |= RL_TDESC_CMD_OWN; if (prod == sc->rl_ldata.rl_tx_desc_cnt - 1) cmdstat |= RL_TDESC_CMD_EOR; desc->rl_cmdstat = htole32(cmdstat | csum_flags); sc->rl_ldata.rl_tx_free--; } /* Update producer index. */ sc->rl_ldata.rl_tx_prodidx = prod; /* Set EOF on the last descriptor. */ ei = RL_TX_DESC_PRV(sc, prod); desc = &sc->rl_ldata.rl_tx_list[ei]; desc->rl_cmdstat |= htole32(RL_TDESC_CMD_EOF); desc = &sc->rl_ldata.rl_tx_list[si]; /* Set SOF and transfer ownership of packet to the chip. */ desc->rl_cmdstat |= htole32(RL_TDESC_CMD_OWN | RL_TDESC_CMD_SOF); /* * Insure that the map for this transmission * is placed at the array index of the last descriptor * in this chain. (Swap last and first dmamaps.) */ txd_last = &sc->rl_ldata.rl_tx_desc[ei]; map = txd->tx_dmamap; txd->tx_dmamap = txd_last->tx_dmamap; txd_last->tx_dmamap = map; txd_last->tx_m = *m_head; return (0); } static void re_tx_task(void *arg, int npending) { struct ifnet *ifp; ifp = arg; re_start(ifp); } /* * Main transmit routine for C+ and gigE NICs. */ static void re_start(struct ifnet *ifp) { struct rl_softc *sc; struct mbuf *m_head; int queued; sc = ifp->if_softc; RL_LOCK(sc); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) { RL_UNLOCK(sc); return; } for (queued = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->rl_ldata.rl_tx_free > 1;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (re_encap(sc, &m_head) != 0) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ ETHER_BPF_MTAP(ifp, m_head); queued++; } if (queued == 0) { #ifdef RE_TX_MODERATION if (sc->rl_ldata.rl_tx_free != sc->rl_ldata.rl_tx_desc_cnt) CSR_WRITE_4(sc, RL_TIMERCNT, 1); #endif RL_UNLOCK(sc); return; } /* Flush the TX descriptors */ bus_dmamap_sync(sc->rl_ldata.rl_tx_list_tag, sc->rl_ldata.rl_tx_list_map, BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD); CSR_WRITE_1(sc, sc->rl_txstart, RL_TXSTART_START); #ifdef RE_TX_MODERATION /* * Use the countdown timer for interrupt moderation. * 'TX done' interrupts are disabled. Instead, we reset the * countdown timer, which will begin counting until it hits * the value in the TIMERINT register, and then trigger an * interrupt. Each time we write to the TIMERCNT register, * the timer count is reset to 0. */ CSR_WRITE_4(sc, RL_TIMERCNT, 1); #endif /* * Set a timeout in case the chip goes out to lunch. */ sc->rl_watchdog_timer = 5; RL_UNLOCK(sc); } static void re_init(void *xsc) { struct rl_softc *sc = xsc; RL_LOCK(sc); re_init_locked(sc); RL_UNLOCK(sc); } static void re_init_locked(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; struct mii_data *mii; u_int32_t rxcfg = 0; uint16_t cfg; union { uint32_t align_dummy; u_char eaddr[ETHER_ADDR_LEN]; } eaddr; RL_LOCK_ASSERT(sc); mii = device_get_softc(sc->rl_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ re_stop(sc); /* Put controller into known state. */ re_reset(sc); /* * Enable C+ RX and TX mode, as well as VLAN stripping and * RX checksum offload. We must configure the C+ register * before all others. */ cfg = RL_CPLUSCMD_PCI_MRW; if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) cfg |= RL_CPLUSCMD_RXCSUM_ENB; if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) cfg |= RL_CPLUSCMD_VLANSTRIP; if ((sc->rl_flags & RL_FLAG_MACSTAT) != 0) { cfg |= RL_CPLUSCMD_MACSTAT_DIS; /* XXX magic. */ cfg |= 0x0001; } else cfg |= RL_CPLUSCMD_RXENB | RL_CPLUSCMD_TXENB; CSR_WRITE_2(sc, RL_CPLUS_CMD, cfg); /* * Disable TSO if interface MTU size is greater than MSS * allowed in controller. */ if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; } /* * Init our MAC address. Even though the chipset * documentation doesn't mention it, we need to enter "Config * register write enable" mode to modify the ID registers. */ /* Copy MAC address on stack to align. */ bcopy(IF_LLADDR(ifp), eaddr.eaddr, ETHER_ADDR_LEN); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG); CSR_WRITE_4(sc, RL_IDR0, htole32(*(u_int32_t *)(&eaddr.eaddr[0]))); CSR_WRITE_4(sc, RL_IDR4, htole32(*(u_int32_t *)(&eaddr.eaddr[4]))); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); /* * For C+ mode, initialize the RX descriptors and mbufs. */ re_rx_list_init(sc); re_tx_list_init(sc); /* * Load the addresses of the RX and TX lists into the chip. */ CSR_WRITE_4(sc, RL_RXLIST_ADDR_HI, RL_ADDR_HI(sc->rl_ldata.rl_rx_list_addr)); CSR_WRITE_4(sc, RL_RXLIST_ADDR_LO, RL_ADDR_LO(sc->rl_ldata.rl_rx_list_addr)); CSR_WRITE_4(sc, RL_TXLIST_ADDR_HI, RL_ADDR_HI(sc->rl_ldata.rl_tx_list_addr)); CSR_WRITE_4(sc, RL_TXLIST_ADDR_LO, RL_ADDR_LO(sc->rl_ldata.rl_tx_list_addr)); /* * Enable transmit and receive. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); /* * Set the initial TX and RX configuration. */ if (sc->rl_testmode) { if (sc->rl_type == RL_8169) CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG|RL_LOOPTEST_ON); else CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG|RL_LOOPTEST_ON_CPLUS); } else CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); CSR_WRITE_1(sc, RL_EARLY_TX_THRESH, 16); CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG); /* Set the individual bit to receive frames for this host only. */ rxcfg = CSR_READ_4(sc, RL_RXCFG); rxcfg |= RL_RXCFG_RX_INDIV; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) rxcfg |= RL_RXCFG_RX_ALLPHYS; else rxcfg &= ~RL_RXCFG_RX_ALLPHYS; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); /* * Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) rxcfg |= RL_RXCFG_RX_BROAD; else rxcfg &= ~RL_RXCFG_RX_BROAD; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); /* * Program the multicast filter, if necessary. */ re_setmulti(sc); #ifdef DEVICE_POLLING /* * Disable interrupts if we are polling. */ if (ifp->if_capenable & IFCAP_POLLING) CSR_WRITE_2(sc, RL_IMR, 0); else /* otherwise ... */ #endif /* * Enable interrupts. */ if (sc->rl_testmode) CSR_WRITE_2(sc, RL_IMR, 0); else CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); CSR_WRITE_2(sc, RL_ISR, RL_INTRS_CPLUS); /* Set initial TX threshold */ sc->rl_txthresh = RL_TX_THRESH_INIT; /* Start RX/TX process. */ CSR_WRITE_4(sc, RL_MISSEDPKT, 0); #ifdef notdef /* Enable receiver and transmitter. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); #endif #ifdef RE_TX_MODERATION /* * Initialize the timer interrupt register so that * a timer interrupt will be generated once the timer * reaches a certain number of ticks. The timer is * reloaded on each transmit. This gives us TX interrupt * moderation, which dramatically improves TX frame rate. */ if (sc->rl_type == RL_8169) CSR_WRITE_4(sc, RL_TIMERINT_8169, 0x800); else CSR_WRITE_4(sc, RL_TIMERINT, 0x400); #endif /* * For 8169 gigE NICs, set the max allowed RX packet * size so we can receive jumbo frames. */ if (sc->rl_type == RL_8169) CSR_WRITE_2(sc, RL_MAXRXPKTLEN, 16383); if (sc->rl_testmode) return; mii_mediachg(mii); CSR_WRITE_1(sc, RL_CFG1, CSR_READ_1(sc, RL_CFG1) | RL_CFG1_DRVLOAD); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; sc->rl_flags &= ~RL_FLAG_LINK; sc->rl_watchdog_timer = 0; callout_reset(&sc->rl_stat_callout, hz, re_tick, sc); } /* * Set media options. */ static int re_ifmedia_upd(struct ifnet *ifp) { struct rl_softc *sc; struct mii_data *mii; int error; sc = ifp->if_softc; mii = device_get_softc(sc->rl_miibus); RL_LOCK(sc); error = mii_mediachg(mii); RL_UNLOCK(sc); return (error); } /* * Report current media status. */ static void re_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rl_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->rl_miibus); RL_LOCK(sc); mii_pollstat(mii); RL_UNLOCK(sc); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int re_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct rl_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; switch (command) { case SIOCSIFMTU: if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > RL_JUMBO_MTU) { error = EINVAL; break; } if ((sc->rl_flags & RL_FLAG_NOJUMBO) != 0 && ifr->ifr_mtu > RL_MAX_FRAMELEN) { error = EINVAL; break; } RL_LOCK(sc); if (ifp->if_mtu != ifr->ifr_mtu) ifp->if_mtu = ifr->ifr_mtu; if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; } RL_UNLOCK(sc); break; case SIOCSIFFLAGS: RL_LOCK(sc); if ((ifp->if_flags & IFF_UP) != 0) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { if (((ifp->if_flags ^ sc->rl_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) re_setmulti(sc); } else re_init_locked(sc); } else { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) re_stop(sc); } sc->rl_if_flags = ifp->if_flags; RL_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: RL_LOCK(sc); re_setmulti(sc); RL_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->rl_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: { int mask, reinit; mask = ifr->ifr_reqcap ^ ifp->if_capenable; reinit = 0; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(re_poll, ifp); if (error) return(error); RL_LOCK(sc); /* Disable interrupts */ CSR_WRITE_2(sc, RL_IMR, 0x0000); ifp->if_capenable |= IFCAP_POLLING; RL_UNLOCK(sc); } else { error = ether_poll_deregister(ifp); /* Enable interrupts. */ RL_LOCK(sc); CSR_WRITE_2(sc, RL_IMR, RL_INTRS_CPLUS); ifp->if_capenable &= ~IFCAP_POLLING; RL_UNLOCK(sc); } } #endif /* DEVICE_POLLING */ if (mask & IFCAP_HWCSUM) { ifp->if_capenable ^= IFCAP_HWCSUM; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist |= RE_CSUM_FEATURES; else ifp->if_hwassist &= ~RE_CSUM_FEATURES; reinit = 1; } if (mask & IFCAP_VLAN_HWTAGGING) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; reinit = 1; } if (mask & IFCAP_TSO4) { ifp->if_capenable ^= IFCAP_TSO4; if ((IFCAP_TSO4 & ifp->if_capenable) && (IFCAP_TSO4 & ifp->if_capabilities)) ifp->if_hwassist |= CSUM_TSO; else ifp->if_hwassist &= ~CSUM_TSO; if (ifp->if_mtu > RL_TSO_MTU && (ifp->if_capenable & IFCAP_TSO4) != 0) { ifp->if_capenable &= ~IFCAP_TSO4; ifp->if_hwassist &= ~CSUM_TSO; } } if ((mask & IFCAP_WOL) != 0 && (ifp->if_capabilities & IFCAP_WOL) != 0) { if ((mask & IFCAP_WOL_UCAST) != 0) ifp->if_capenable ^= IFCAP_WOL_UCAST; if ((mask & IFCAP_WOL_MCAST) != 0) ifp->if_capenable ^= IFCAP_WOL_MCAST; if ((mask & IFCAP_WOL_MAGIC) != 0) ifp->if_capenable ^= IFCAP_WOL_MAGIC; } if (reinit && ifp->if_drv_flags & IFF_DRV_RUNNING) re_init(sc); VLAN_CAPABILITIES(ifp); } break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void re_watchdog(struct rl_softc *sc) { struct ifnet *ifp; RL_LOCK_ASSERT(sc); if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer != 0) return; ifp = sc->rl_ifp; re_txeof(sc); if (sc->rl_ldata.rl_tx_free == sc->rl_ldata.rl_tx_desc_cnt) { if_printf(ifp, "watchdog timeout (missed Tx interrupts) " "-- recovering\n"); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); return; } if_printf(ifp, "watchdog timeout\n"); ifp->if_oerrors++; re_rxeof(sc); re_init_locked(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) taskqueue_enqueue_fast(taskqueue_fast, &sc->rl_txtask); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void re_stop(struct rl_softc *sc) { int i; struct ifnet *ifp; struct rl_txdesc *txd; struct rl_rxdesc *rxd; RL_LOCK_ASSERT(sc); ifp = sc->rl_ifp; sc->rl_watchdog_timer = 0; callout_stop(&sc->rl_stat_callout); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); if ((sc->rl_flags & RL_FLAG_CMDSTOP) != 0) CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_STOPREQ | RL_CMD_TX_ENB | RL_CMD_RX_ENB); else CSR_WRITE_1(sc, RL_COMMAND, 0x00); DELAY(1000); CSR_WRITE_2(sc, RL_IMR, 0x0000); CSR_WRITE_2(sc, RL_ISR, 0xFFFF); if (sc->rl_head != NULL) { m_freem(sc->rl_head); sc->rl_head = sc->rl_tail = NULL; } /* Free the TX list buffers. */ for (i = 0; i < sc->rl_ldata.rl_tx_desc_cnt; i++) { txd = &sc->rl_ldata.rl_tx_desc[i]; if (txd->tx_m != NULL) { bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->rl_ldata.rl_tx_mtag, txd->tx_dmamap); m_freem(txd->tx_m); txd->tx_m = NULL; } } /* Free the RX list buffers. */ for (i = 0; i < sc->rl_ldata.rl_rx_desc_cnt; i++) { rxd = &sc->rl_ldata.rl_rx_desc[i]; if (rxd->rx_m != NULL) { bus_dmamap_sync(sc->rl_ldata.rl_tx_mtag, rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rl_ldata.rl_rx_mtag, rxd->rx_dmamap); m_freem(rxd->rx_m); rxd->rx_m = NULL; } } } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int re_suspend(device_t dev) { struct rl_softc *sc; sc = device_get_softc(dev); RL_LOCK(sc); re_stop(sc); re_setwol(sc); sc->suspended = 1; RL_UNLOCK(sc); return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int re_resume(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); RL_LOCK(sc); ifp = sc->rl_ifp; /* Take controller out of sleep mode. */ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80) CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) | 0x01); } /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) re_init_locked(sc); /* * Clear WOL matching such that normal Rx filtering * wouldn't interfere with WOL patterns. */ re_clrwol(sc); sc->suspended = 0; RL_UNLOCK(sc); return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int re_shutdown(device_t dev) { struct rl_softc *sc; sc = device_get_softc(dev); RL_LOCK(sc); re_stop(sc); /* * Mark interface as down since otherwise we will panic if * interrupt comes in later on, which can happen in some * cases. */ sc->rl_ifp->if_flags &= ~IFF_UP; re_setwol(sc); RL_UNLOCK(sc); return (0); } static void re_setwol(struct rl_softc *sc) { struct ifnet *ifp; int pmc; uint16_t pmstat; uint8_t v; RL_LOCK_ASSERT(sc); if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0) return; ifp = sc->rl_ifp; /* Put controller into sleep mode. */ if ((sc->rl_flags & RL_FLAG_MACSLEEP) != 0) { if ((CSR_READ_1(sc, RL_MACDBG) & 0x80) == 0x80) CSR_WRITE_1(sc, RL_GPIO, CSR_READ_1(sc, RL_GPIO) & ~0x01); } if ((ifp->if_capenable & IFCAP_WOL) != 0 && (sc->rl_flags & RL_FLAG_WOLRXENB) != 0) CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RX_ENB); /* Enable config register write. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); /* Enable PME. */ v = CSR_READ_1(sc, RL_CFG1); v &= ~RL_CFG1_PME; if ((ifp->if_capenable & IFCAP_WOL) != 0) v |= RL_CFG1_PME; CSR_WRITE_1(sc, RL_CFG1, v); v = CSR_READ_1(sc, RL_CFG3); v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC); if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) v |= RL_CFG3_WOL_MAGIC; CSR_WRITE_1(sc, RL_CFG3, v); /* Config register write done. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); v = CSR_READ_1(sc, RL_CFG5); v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST); v &= ~RL_CFG5_WOL_LANWAKE; if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0) v |= RL_CFG5_WOL_UCAST; if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0) v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST; if ((ifp->if_capenable & IFCAP_WOL) != 0) v |= RL_CFG5_WOL_LANWAKE; CSR_WRITE_1(sc, RL_CFG5, v); /* * It seems that hardware resets its link speed to 100Mbps in * power down mode so switching to 100Mbps in driver is not * needed. */ /* Request PME if WOL is requested. */ pmstat = pci_read_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, 2); pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); if ((ifp->if_capenable & IFCAP_WOL) != 0) pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; pci_write_config(sc->rl_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); } static void re_clrwol(struct rl_softc *sc) { int pmc; uint8_t v; RL_LOCK_ASSERT(sc); if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0) return; /* Enable config register write. */ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE); v = CSR_READ_1(sc, RL_CFG3); v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC); CSR_WRITE_1(sc, RL_CFG3, v); /* Config register write done. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); v = CSR_READ_1(sc, RL_CFG5); v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST); v &= ~RL_CFG5_WOL_LANWAKE; CSR_WRITE_1(sc, RL_CFG5, v); } Index: projects/cambria/sys/dev/sound/pci/hda/hdac.c =================================================================== --- projects/cambria/sys/dev/sound/pci/hda/hdac.c (revision 186459) +++ projects/cambria/sys/dev/sound/pci/hda/hdac.c (revision 186460) @@ -1,7837 +1,7848 @@ /*- * Copyright (c) 2006 Stephane E. Potvin * Copyright (c) 2006 Ariff Abdullah * Copyright (c) 2008 Alexander Motin * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * Intel High Definition Audio (Controller) driver for FreeBSD. Be advised * that this driver still in its early stage, and possible of rewrite are * pretty much guaranteed. There are supposedly several distinct parent/child * busses to make this "perfect", but as for now and for the sake of * simplicity, everything is gobble up within single source. * * List of subsys: * 1) HDA Controller support * 2) HDA Codecs support, which may include * - HDA * - Modem * - HDMI * 3) Widget parser - the real magic of why this driver works on so * many hardwares with minimal vendor specific quirk. The original * parser was written using Ruby and can be found at * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude * ruby parser take the verbose dmesg dump as its input. Refer to * http://www.microsoft.com/whdc/device/audio/default.mspx for various * interesting documents, especially UAA (Universal Audio Architecture). * 4) Possible vendor specific support. * (snd_hda_intel, snd_hda_ati, etc..) * * Thanks to Ahmad Ubaidah Omar @ Defenxis Sdn. Bhd. for the * Compaq V3000 with Conexant HDA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This driver is a collaborative effort made by: * * * * * * Stephane E. Potvin * * * Andrea Bittau * * * Wesley Morgan * * * Daniel Eischen * * * Maxime Guillaud * * * Ariff Abdullah * * * Alexander Motin * * * * * * ....and various people from freebsd-multimedia@FreeBSD.org * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include #include #include #include #include #include #include "mixer_if.h" -#define HDA_DRV_TEST_REV "20081219_0119" +#define HDA_DRV_TEST_REV "20081223_0121" SND_DECLARE_FILE("$FreeBSD$"); #define HDA_BOOTVERBOSE(stmt) do { \ if (bootverbose != 0 || snd_verbose > 3) { \ stmt \ } \ } while(0) #define HDA_BOOTHVERBOSE(stmt) do { \ if (snd_verbose > 3) { \ stmt \ } \ } while(0) #if 1 #undef HDAC_INTR_EXTRA #define HDAC_INTR_EXTRA 1 #endif #define hdac_lock(sc) snd_mtxlock((sc)->lock) #define hdac_unlock(sc) snd_mtxunlock((sc)->lock) #define hdac_lockassert(sc) snd_mtxassert((sc)->lock) #define hdac_lockowned(sc) mtx_owned((sc)->lock) #undef HDAC_MSI_ENABLED #if __FreeBSD_version >= 700026 || \ (__FreeBSD_version < 700000 && __FreeBSD_version >= 602106) #define HDAC_MSI_ENABLED 1 #endif #define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) #define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ (fl) == 0xffffffff || \ (((fl) & 0xffff0000) == 0xffff0000 && \ ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ (((fl) & 0x0000ffff) == 0x0000ffff && \ ((fl) & 0xffff0000) == ((v) & 0xffff0000))) #define HDA_MATCH_ALL 0xffffffff #define HDAC_INVALID 0xffffffff /* Default controller / jack sense poll: 250ms */ #define HDAC_POLL_INTERVAL max(hz >> 2, 1) /* * Make room for possible 4096 playback/record channels, in 100 years to come. */ #define HDAC_TRIGGER_NONE 0x00000000 #define HDAC_TRIGGER_PLAY 0x00000fff #define HDAC_TRIGGER_REC 0x00fff000 #define HDAC_TRIGGER_UNSOL 0x80000000 #define HDA_MODEL_CONSTRUCT(vendor, model) \ (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) /* Controller models */ /* Intel */ #define INTEL_VENDORID 0x8086 #define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) #define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) #define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) #define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) #define HDA_INTEL_82801I HDA_MODEL_CONSTRUCT(INTEL, 0x293e) #define HDA_INTEL_82801J HDA_MODEL_CONSTRUCT(INTEL, 0x3a3e) #define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b) #define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) /* Nvidia */ #define NVIDIA_VENDORID 0x10de #define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) #define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) #define HDA_NVIDIA_MCP61_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) #define HDA_NVIDIA_MCP61_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) #define HDA_NVIDIA_MCP65_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) #define HDA_NVIDIA_MCP65_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) #define HDA_NVIDIA_MCP67_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055c) #define HDA_NVIDIA_MCP67_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055d) #define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) /* ATI */ #define ATI_VENDORID 0x1002 #define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) #define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) #define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) /* VIA */ #define VIA_VENDORID 0x1106 #define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) #define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) /* SiS */ #define SIS_VENDORID 0x1039 #define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) #define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) /* ULI */ #define ULI_VENDORID 0x10b9 #define HDA_ULI_M5461 HDA_MODEL_CONSTRUCT(ULI, 0x5461) #define HDA_ULI_ALL HDA_MODEL_CONSTRUCT(ULI, 0xffff) /* OEM/subvendors */ /* Intel */ #define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) /* HP/Compaq */ #define HP_VENDORID 0x103c #define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) #define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) #define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) #define HP_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) #define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) #define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) #define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) #define HP_DC7700S_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2801) #define HP_DC7700_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2802) #define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) /* What is wrong with XN 2563 anyway? (Got the picture ?) */ #define HP_NX6325_SUBVENDORX 0x103c30b0 /* Dell */ #define DELL_VENDORID 0x1028 #define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) #define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) #define DELL_V1400_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0227) #define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) #define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) #define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) #define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) #define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) /* Clevo */ #define CLEVO_VENDORID 0x1558 #define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) #define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) /* Acer */ #define ACER_VENDORID 0x1025 #define ACER_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) #define ACER_A4520_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0127) #define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) #define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) #define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) #define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) #define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) /* Asus */ #define ASUS_VENDORID 0x1043 #define ASUS_A8X_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) #define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) #define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) #define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) #define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) #define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339) #define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) #define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) #define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) #define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) #define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) #define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) #define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) #define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) #define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84) #define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) /* IBM / Lenovo */ #define IBM_VENDORID 0x1014 #define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) #define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) /* Lenovo */ #define LENOVO_VENDORID 0x17aa #define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) #define LENOVO_3KN200_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e) #define LENOVO_TCA55_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x1015) #define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) /* Samsung */ #define SAMSUNG_VENDORID 0x144d #define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) #define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) /* Medion ? */ #define MEDION_VENDORID 0x161f #define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) #define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) /* Apple Computer Inc. */ #define APPLE_VENDORID 0x106b #define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) /* Sony */ #define SONY_VENDORID 0x104d #define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) #define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) /* * Apple Intel MacXXXX seems using Sigmatel codec/vendor id * instead of their own, which is beyond my comprehension * (see HDA_CODEC_STAC9221 below). */ #define APPLE_INTEL_MAC 0x76808384 /* LG Electronics */ #define LG_VENDORID 0x1854 #define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) #define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) /* Fujitsu Siemens */ #define FS_VENDORID 0x1734 #define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) #define FS_SI1848_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10cd) #define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) /* Fujitsu Limited */ #define FL_VENDORID 0x10cf #define FL_S7020D_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x1326) #define FL_U1010_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x142d) #define FL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0xffff) /* Toshiba */ #define TOSHIBA_VENDORID 0x1179 #define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) #define TOSHIBA_A135_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xff01) #define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) /* Micro-Star International (MSI) */ #define MSI_VENDORID 0x1462 #define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) #define MSI_MS034A_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x034a) #define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) /* Giga-Byte Technology */ #define GB_VENDORID 0x1458 #define GB_G33S2H_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xa022) #define GP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xffff) /* Uniwill ? */ #define UNIWILL_VENDORID 0x1584 #define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) #define UNIWILL_9080_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9080) /* Misc constants.. */ #define HDA_AMP_VOL_DEFAULT (-1) #define HDA_AMP_MUTE_DEFAULT (0xffffffff) #define HDA_AMP_MUTE_NONE (0) #define HDA_AMP_MUTE_LEFT (1 << 0) #define HDA_AMP_MUTE_RIGHT (1 << 1) #define HDA_AMP_MUTE_ALL (HDA_AMP_MUTE_LEFT | HDA_AMP_MUTE_RIGHT) #define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) #define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) #define HDA_ADC_MONITOR (1 << 0) #define HDA_CTL_OUT 1 #define HDA_CTL_IN 2 #define HDA_GPIO_MAX 8 /* 0 - 7 = GPIO , 8 = Flush */ #define HDA_QUIRK_GPIO0 (1 << 0) #define HDA_QUIRK_GPIO1 (1 << 1) #define HDA_QUIRK_GPIO2 (1 << 2) #define HDA_QUIRK_GPIO3 (1 << 3) #define HDA_QUIRK_GPIO4 (1 << 4) #define HDA_QUIRK_GPIO5 (1 << 5) #define HDA_QUIRK_GPIO6 (1 << 6) #define HDA_QUIRK_GPIO7 (1 << 7) #define HDA_QUIRK_GPIOFLUSH (1 << 8) /* 9 - 25 = anything else */ #define HDA_QUIRK_SOFTPCMVOL (1 << 9) #define HDA_QUIRK_FIXEDRATE (1 << 10) #define HDA_QUIRK_FORCESTEREO (1 << 11) #define HDA_QUIRK_EAPDINV (1 << 12) #define HDA_QUIRK_DMAPOS (1 << 13) #define HDA_QUIRK_SENSEINV (1 << 14) /* 26 - 31 = vrefs */ #define HDA_QUIRK_IVREF50 (1 << 26) #define HDA_QUIRK_IVREF80 (1 << 27) #define HDA_QUIRK_IVREF100 (1 << 28) #define HDA_QUIRK_OVREF50 (1 << 29) #define HDA_QUIRK_OVREF80 (1 << 30) #define HDA_QUIRK_OVREF100 (1 << 31) #define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \ HDA_QUIRK_IVREF100) #define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \ HDA_QUIRK_OVREF100) #define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) #if __FreeBSD_version < 600000 #define taskqueue_drain(...) #endif static const struct { char *key; uint32_t value; } hdac_quirks_tab[] = { { "gpio0", HDA_QUIRK_GPIO0 }, { "gpio1", HDA_QUIRK_GPIO1 }, { "gpio2", HDA_QUIRK_GPIO2 }, { "gpio3", HDA_QUIRK_GPIO3 }, { "gpio4", HDA_QUIRK_GPIO4 }, { "gpio5", HDA_QUIRK_GPIO5 }, { "gpio6", HDA_QUIRK_GPIO6 }, { "gpio7", HDA_QUIRK_GPIO7 }, { "gpioflush", HDA_QUIRK_GPIOFLUSH }, { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, { "fixedrate", HDA_QUIRK_FIXEDRATE }, { "forcestereo", HDA_QUIRK_FORCESTEREO }, { "eapdinv", HDA_QUIRK_EAPDINV }, { "dmapos", HDA_QUIRK_DMAPOS }, { "senseinv", HDA_QUIRK_SENSEINV }, { "ivref50", HDA_QUIRK_IVREF50 }, { "ivref80", HDA_QUIRK_IVREF80 }, { "ivref100", HDA_QUIRK_IVREF100 }, { "ovref50", HDA_QUIRK_OVREF50 }, { "ovref80", HDA_QUIRK_OVREF80 }, { "ovref100", HDA_QUIRK_OVREF100 }, { "ivref", HDA_QUIRK_IVREF }, { "ovref", HDA_QUIRK_OVREF }, { "vref", HDA_QUIRK_VREF }, }; #define HDAC_QUIRKS_TAB_LEN \ (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) #define HDA_BDL_MIN 2 #define HDA_BDL_MAX 256 #define HDA_BDL_DEFAULT HDA_BDL_MIN #define HDA_BLK_MIN HDAC_DMA_ALIGNMENT #define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) #define HDA_BUFSZ_MIN 4096 #define HDA_BUFSZ_MAX 65536 #define HDA_BUFSZ_DEFAULT 16384 #define HDA_PARSE_MAXDEPTH 10 #define HDAC_UNSOLTAG_EVENT_HP 0x00 MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", "White", "Other"}; const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; /* Default */ static uint32_t hdac_fmt[] = { AFMT_STEREO | AFMT_S16_LE, 0 }; static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; static const struct { uint32_t model; char *desc; } hdac_devices[] = { { HDA_INTEL_82801F, "Intel 82801F" }, { HDA_INTEL_63XXESB, "Intel 631x/632xESB" }, { HDA_INTEL_82801G, "Intel 82801G" }, { HDA_INTEL_82801H, "Intel 82801H" }, { HDA_INTEL_82801I, "Intel 82801I" }, { HDA_INTEL_82801J, "Intel 82801J" }, { HDA_INTEL_SCH, "Intel SCH" }, { HDA_NVIDIA_MCP51, "NVidia MCP51" }, { HDA_NVIDIA_MCP55, "NVidia MCP55" }, { HDA_NVIDIA_MCP61_1, "NVidia MCP61" }, { HDA_NVIDIA_MCP61_2, "NVidia MCP61" }, { HDA_NVIDIA_MCP65_1, "NVidia MCP65" }, { HDA_NVIDIA_MCP65_2, "NVidia MCP65" }, { HDA_NVIDIA_MCP67_1, "NVidia MCP67" }, { HDA_NVIDIA_MCP67_2, "NVidia MCP67" }, { HDA_ATI_SB450, "ATI SB450" }, { HDA_ATI_SB600, "ATI SB600" }, { HDA_VIA_VT82XX, "VIA VT8251/8237A" }, { HDA_SIS_966, "SiS 966" }, { HDA_ULI_M5461, "ULI M5461" }, /* Unknown */ { HDA_INTEL_ALL, "Intel (Unknown)" }, { HDA_NVIDIA_ALL, "NVidia (Unknown)" }, { HDA_ATI_ALL, "ATI (Unknown)" }, { HDA_VIA_ALL, "VIA (Unknown)" }, { HDA_SIS_ALL, "SiS (Unknown)" }, { HDA_ULI_ALL, "ULI (Unknown)" }, }; #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) static const struct { uint16_t vendor; uint8_t reg; uint8_t mask; uint8_t enable; } hdac_pcie_snoop[] = { { INTEL_VENDORID, 0x00, 0x00, 0x00 }, { ATI_VENDORID, 0x42, 0xf8, 0x02 }, { NVIDIA_VENDORID, 0x4e, 0xf0, 0x0f }, }; #define HDAC_PCIESNOOP_LEN \ (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[0])) static const struct { uint32_t rate; int valid; uint16_t base; uint16_t mul; uint16_t div; } hda_rate_tab[] = { { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ }; #define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) /* All codecs you can eat... */ #define HDA_CODEC_CONSTRUCT(vendor, id) \ (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) /* Realtek */ #define REALTEK_VENDORID 0x10ec #define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) #define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) #define HDA_CODEC_ALC267 HDA_CODEC_CONSTRUCT(REALTEK, 0x0267) #define HDA_CODEC_ALC268 HDA_CODEC_CONSTRUCT(REALTEK, 0x0268) #define HDA_CODEC_ALC269 HDA_CODEC_CONSTRUCT(REALTEK, 0x0269) #define HDA_CODEC_ALC272 HDA_CODEC_CONSTRUCT(REALTEK, 0x0272) #define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) #define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) #define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) #define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) #define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) #define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) #define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) #define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) #define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) #define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) #define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) #define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) /* Analog Devices */ #define ANALOGDEVICES_VENDORID 0x11d4 +#define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) +#define HDA_CODEC_AD1882 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1882) +#define HDA_CODEC_AD1883 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1883) +#define HDA_CODEC_AD1884 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1884) +#define HDA_CODEC_AD1984A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194a) +#define HDA_CODEC_AD1984B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194b) #define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) #define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) #define HDA_CODEC_AD1984 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1984) #define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) +#define HDA_CODEC_AD1987 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1987) #define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) #define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) +#define HDA_CODEC_AD1882A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x882a) +#define HDA_CODEC_AD1989B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989b) #define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) /* CMedia */ #define CMEDIA_VENDORID 0x434d #define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) #define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) /* Sigmatel */ #define SIGMATEL_VENDORID 0x8384 #define HDA_CODEC_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) #define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) #define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) #define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) #define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) #define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) #define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) #define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) #define HDA_CODEC_STAC9274 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7620) #define HDA_CODEC_STAC9274D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7621) #define HDA_CODEC_STAC9273X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7622) #define HDA_CODEC_STAC9273D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7623) #define HDA_CODEC_STAC9272X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7624) #define HDA_CODEC_STAC9272D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7625) #define HDA_CODEC_STAC9271X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7626) #define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) #define HDA_CODEC_STAC9274X5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7628) #define HDA_CODEC_STAC9274D5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7629) #define HDA_CODEC_STAC9250 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7634) #define HDA_CODEC_STAC9251 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7636) #define HDA_CODEC_IDT92HD700X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7638) #define HDA_CODEC_IDT92HD700D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7639) #define HDA_CODEC_IDT92HD206X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7645) #define HDA_CODEC_IDT92HD206D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7646) #define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) #define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) #define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) #define HDA_CODEC_STAC9221_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7682) #define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) #define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) #define HDA_CODEC_STAC9200D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7691) #define HDA_CODEC_IDT92HD005 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7698) #define HDA_CODEC_IDT92HD005D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7699) #define HDA_CODEC_STAC9205X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) #define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) #define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) #define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) #define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) #define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) #define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) /* IDT */ #define IDT_VENDORID 0x111d #define HDA_CODEC_IDT92HD75BX HDA_CODEC_CONSTRUCT(IDT, 0x7603) #define HDA_CODEC_IDT92HD83C1X HDA_CODEC_CONSTRUCT(IDT, 0x7604) #define HDA_CODEC_IDT92HD81B1X HDA_CODEC_CONSTRUCT(IDT, 0x7605) #define HDA_CODEC_IDT92HD75B3 HDA_CODEC_CONSTRUCT(IDT, 0x7608) #define HDA_CODEC_IDT92HD73D1 HDA_CODEC_CONSTRUCT(IDT, 0x7674) #define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) #define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) #define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) #define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) #define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) #define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) #define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) #define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) /* Silicon Image */ #define SII_VENDORID 0x1095 #define HDA_CODEC_SII1392 HDA_CODEC_CONSTRUCT(SII, 0x1392) #define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) /* Lucent/Agere */ #define AGERE_VENDORID 0x11c1 #define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) -/* - * Conexant - * - * Ok, the truth is, I don't have any idea at all whether - * it is "Venice" or "Waikiki" or other unnamed CXyadayada. The only - * place that tell me it is "Venice" is from its Windows driver INF. - * - * Venice - CX????? - * Waikiki - CX20551-22 - */ +/* Conexant */ #define CONEXANT_VENDORID 0x14f1 -#define HDA_CODEC_CXVENICE HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) -#define HDA_CODEC_CXWAIKIKI HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CX20549 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) +#define HDA_CODEC_CX20551 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CX20561 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5051) #define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) /* VIA */ #define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) #define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) #define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) #define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) #define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) #define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) #define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) #define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) #define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) #define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) #define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) #define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) #define HDA_CODEC_VT1708B_0 HDA_CODEC_CONSTRUCT(VIA, 0xe720) #define HDA_CODEC_VT1708B_1 HDA_CODEC_CONSTRUCT(VIA, 0xe721) #define HDA_CODEC_VT1708B_2 HDA_CODEC_CONSTRUCT(VIA, 0xe722) #define HDA_CODEC_VT1708B_3 HDA_CODEC_CONSTRUCT(VIA, 0xe723) #define HDA_CODEC_VT1708B_4 HDA_CODEC_CONSTRUCT(VIA, 0xe724) #define HDA_CODEC_VT1708B_5 HDA_CODEC_CONSTRUCT(VIA, 0xe725) #define HDA_CODEC_VT1708B_6 HDA_CODEC_CONSTRUCT(VIA, 0xe726) #define HDA_CODEC_VT1708B_7 HDA_CODEC_CONSTRUCT(VIA, 0xe727) #define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) /* ATI */ #define HDA_CODEC_ATIRS600_1 HDA_CODEC_CONSTRUCT(ATI, 0x793c) #define HDA_CODEC_ATIRS600_2 HDA_CODEC_CONSTRUCT(ATI, 0x7919) #define HDA_CODEC_ATIRS690 HDA_CODEC_CONSTRUCT(ATI, 0x791a) #define HDA_CODEC_ATIR6XX HDA_CODEC_CONSTRUCT(ATI, 0xaa01) #define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) /* NVIDIA */ #define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) /* INTEL */ #define HDA_CODEC_INTELXXXX HDA_CODEC_CONSTRUCT(INTEL, 0xffff) /* Codecs */ static const struct { uint32_t id; char *name; } hdac_codecs[] = { { HDA_CODEC_ALC260, "Realtek ALC260" }, { HDA_CODEC_ALC262, "Realtek ALC262" }, { HDA_CODEC_ALC267, "Realtek ALC267" }, { HDA_CODEC_ALC268, "Realtek ALC268" }, { HDA_CODEC_ALC269, "Realtek ALC269" }, { HDA_CODEC_ALC272, "Realtek ALC272" }, { HDA_CODEC_ALC660, "Realtek ALC660" }, { HDA_CODEC_ALC662, "Realtek ALC662" }, { HDA_CODEC_ALC663, "Realtek ALC663" }, { HDA_CODEC_ALC861, "Realtek ALC861" }, { HDA_CODEC_ALC861VD, "Realtek ALC861-VD" }, { HDA_CODEC_ALC880, "Realtek ALC880" }, { HDA_CODEC_ALC882, "Realtek ALC882" }, { HDA_CODEC_ALC883, "Realtek ALC883" }, { HDA_CODEC_ALC885, "Realtek ALC885" }, { HDA_CODEC_ALC888, "Realtek ALC888" }, { HDA_CODEC_ALC889, "Realtek ALC889" }, + { HDA_CODEC_AD1882, "Analog Devices AD1882" }, + { HDA_CODEC_AD1882A, "Analog Devices AD1882A" }, + { HDA_CODEC_AD1883, "Analog Devices AD1883" }, + { HDA_CODEC_AD1884, "Analog Devices AD1884" }, + { HDA_CODEC_AD1884A, "Analog Devices AD1884A" }, { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, { HDA_CODEC_AD1983, "Analog Devices AD1983" }, { HDA_CODEC_AD1984, "Analog Devices AD1984" }, + { HDA_CODEC_AD1984A, "Analog Devices AD1984A" }, + { HDA_CODEC_AD1984B, "Analog Devices AD1984B" }, { HDA_CODEC_AD1986A, "Analog Devices AD1986A" }, - { HDA_CODEC_AD1988, "Analog Devices AD1988" }, + { HDA_CODEC_AD1987, "Analog Devices AD1987" }, + { HDA_CODEC_AD1988, "Analog Devices AD1988A" }, { HDA_CODEC_AD1988B, "Analog Devices AD1988B" }, + { HDA_CODEC_AD1989B, "Analog Devices AD1989B" }, { HDA_CODEC_CMI9880, "CMedia CMI9880" }, { HDA_CODEC_STAC9200D, "Sigmatel STAC9200D" }, { HDA_CODEC_STAC9204X, "Sigmatel STAC9204X" }, { HDA_CODEC_STAC9204D, "Sigmatel STAC9204D" }, { HDA_CODEC_STAC9205X, "Sigmatel STAC9205X" }, { HDA_CODEC_STAC9205D, "Sigmatel STAC9205D" }, { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, { HDA_CODEC_STAC9220_A1, "Sigmatel STAC9220_A1" }, { HDA_CODEC_STAC9220_A2, "Sigmatel STAC9220_A2" }, { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, { HDA_CODEC_STAC9221_A2, "Sigmatel STAC9221_A2" }, { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, { HDA_CODEC_STAC9227X, "Sigmatel STAC9227X" }, { HDA_CODEC_STAC9227D, "Sigmatel STAC9227D" }, { HDA_CODEC_STAC9228X, "Sigmatel STAC9228X" }, { HDA_CODEC_STAC9228D, "Sigmatel STAC9228D" }, { HDA_CODEC_STAC9229X, "Sigmatel STAC9229X" }, { HDA_CODEC_STAC9229D, "Sigmatel STAC9229D" }, { HDA_CODEC_STAC9230X, "Sigmatel STAC9230X" }, { HDA_CODEC_STAC9230D, "Sigmatel STAC9230D" }, { HDA_CODEC_STAC9250, "Sigmatel STAC9250" }, { HDA_CODEC_STAC9251, "Sigmatel STAC9251" }, { HDA_CODEC_STAC9271X, "Sigmatel STAC9271X" }, { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, { HDA_CODEC_STAC9272X, "Sigmatel STAC9272X" }, { HDA_CODEC_STAC9272D, "Sigmatel STAC9272D" }, { HDA_CODEC_STAC9273X, "Sigmatel STAC9273X" }, { HDA_CODEC_STAC9273D, "Sigmatel STAC9273D" }, { HDA_CODEC_STAC9274, "Sigmatel STAC9274" }, { HDA_CODEC_STAC9274D, "Sigmatel STAC9274D" }, { HDA_CODEC_STAC9274X5NH, "Sigmatel STAC9274X5NH" }, { HDA_CODEC_STAC9274D5NH, "Sigmatel STAC9274D5NH" }, { HDA_CODEC_STAC9872AK, "Sigmatel STAC9872AK" }, { HDA_CODEC_IDT92HD005, "IDT 92HD005" }, { HDA_CODEC_IDT92HD005D, "IDT 92HD005D" }, { HDA_CODEC_IDT92HD206X, "IDT 92HD206X" }, { HDA_CODEC_IDT92HD206D, "IDT 92HD206D" }, { HDA_CODEC_IDT92HD700X, "IDT 92HD700X" }, { HDA_CODEC_IDT92HD700D, "IDT 92HD700D" }, { HDA_CODEC_IDT92HD71B5, "IDT 92HD71B5" }, { HDA_CODEC_IDT92HD71B7, "IDT 92HD71B7" }, { HDA_CODEC_IDT92HD71B8, "IDT 92HD71B8" }, { HDA_CODEC_IDT92HD73C1, "IDT 92HD73C1" }, { HDA_CODEC_IDT92HD73D1, "IDT 92HD73D1" }, { HDA_CODEC_IDT92HD73E1, "IDT 92HD73E1" }, { HDA_CODEC_IDT92HD75B3, "IDT 92HD75B3" }, { HDA_CODEC_IDT92HD75BX, "IDT 92HD75BX" }, { HDA_CODEC_IDT92HD81B1C, "IDT 92HD81B1C" }, { HDA_CODEC_IDT92HD81B1X, "IDT 92HD81B1X" }, { HDA_CODEC_IDT92HD83C1C, "IDT 92HD83C1C" }, { HDA_CODEC_IDT92HD83C1X, "IDT 92HD83C1X" }, - { HDA_CODEC_CXVENICE, "Conexant Venice" }, - { HDA_CODEC_CXWAIKIKI, "Conexant Waikiki" }, + { HDA_CODEC_CX20549, "Conexant CX20549 (Venice)" }, + { HDA_CODEC_CX20551, "Conexant CX20551 (Waikiki)" }, + { HDA_CODEC_CX20561, "Conexant CX20561 (Hermosa)" }, { HDA_CODEC_VT1708_8, "VIA VT1708_8" }, { HDA_CODEC_VT1708_9, "VIA VT1708_9" }, { HDA_CODEC_VT1708_A, "VIA VT1708_A" }, { HDA_CODEC_VT1708_B, "VIA VT1708_B" }, { HDA_CODEC_VT1709_0, "VIA VT1709_0" }, { HDA_CODEC_VT1709_1, "VIA VT1709_1" }, { HDA_CODEC_VT1709_2, "VIA VT1709_2" }, { HDA_CODEC_VT1709_3, "VIA VT1709_3" }, { HDA_CODEC_VT1709_4, "VIA VT1709_4" }, { HDA_CODEC_VT1709_5, "VIA VT1709_5" }, { HDA_CODEC_VT1709_6, "VIA VT1709_6" }, { HDA_CODEC_VT1709_7, "VIA VT1709_7" }, { HDA_CODEC_VT1708B_0, "VIA VT1708B_0" }, { HDA_CODEC_VT1708B_1, "VIA VT1708B_1" }, { HDA_CODEC_VT1708B_2, "VIA VT1708B_2" }, { HDA_CODEC_VT1708B_3, "VIA VT1708B_3" }, { HDA_CODEC_VT1708B_4, "VIA VT1708B_4" }, { HDA_CODEC_VT1708B_5, "VIA VT1708B_5" }, { HDA_CODEC_VT1708B_6, "VIA VT1708B_6" }, { HDA_CODEC_VT1708B_7, "VIA VT1708B_7" }, { HDA_CODEC_ATIRS600_1,"ATI RS600 HDMI" }, { HDA_CODEC_ATIRS600_2,"ATI RS600 HDMI" }, { HDA_CODEC_ATIRS690, "ATI RS690/780 HDMI" }, { HDA_CODEC_ATIR6XX, "ATI R6xx HDMI" }, { HDA_CODEC_SII1392, "Silicon Image SiI1392 HDMI" }, /* Unknown codec */ { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, { HDA_CODEC_SIIXXXX, "Silicon Image (Unknown)" }, { HDA_CODEC_AGEREXXXX, "Lucent/Agere Systems (Unknown)" }, { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, { HDA_CODEC_ATIXXXX, "ATI (Unknown)" }, { HDA_CODEC_NVIDIAXXXX,"NVidia (Unknown)" }, { HDA_CODEC_INTELXXXX, "Intel (Unknown)" }, { HDA_CODEC_IDTXXXX, "IDT (Unknown)" }, }; #define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) /**************************************************************************** * Function prototypes ****************************************************************************/ static void hdac_intr_handler(void *); static int hdac_reset(struct hdac_softc *, int); static int hdac_get_capabilities(struct hdac_softc *); static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); static int hdac_dma_alloc(struct hdac_softc *, struct hdac_dma *, bus_size_t); static void hdac_dma_free(struct hdac_softc *, struct hdac_dma *); static int hdac_mem_alloc(struct hdac_softc *); static void hdac_mem_free(struct hdac_softc *); static int hdac_irq_alloc(struct hdac_softc *); static void hdac_irq_free(struct hdac_softc *); static void hdac_corb_init(struct hdac_softc *); static void hdac_rirb_init(struct hdac_softc *); static void hdac_corb_start(struct hdac_softc *); static void hdac_rirb_start(struct hdac_softc *); static void hdac_scan_codecs(struct hdac_softc *); static void hdac_probe_codec(struct hdac_codec *); static void hdac_probe_function(struct hdac_codec *, nid_t); static int hdac_pcmchannel_setup(struct hdac_chan *); static void hdac_attach2(void *); static uint32_t hdac_command_sendone_internal(struct hdac_softc *, uint32_t, int); static void hdac_command_send_internal(struct hdac_softc *, struct hdac_command_list *, int); static int hdac_probe(device_t); static int hdac_attach(device_t); static int hdac_detach(device_t); static int hdac_suspend(device_t); static int hdac_resume(device_t); static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, uint32_t, int, int); static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, nid_t, int, int, int); static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, nid_t, nid_t, int, int, int, int, int, int); static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); static int hdac_rirb_flush(struct hdac_softc *sc); static int hdac_unsolq_flush(struct hdac_softc *sc); static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf); #define hdac_command(a1, a2, a3) \ hdac_command_sendone_internal(a1, a2, a3) #define hdac_codec_id(c) \ ((uint32_t)((c == NULL) ? 0x00000000 : \ ((((uint32_t)(c)->vendor_id & 0x0000ffff) << 16) | \ ((uint32_t)(c)->device_id & 0x0000ffff)))) static char * hdac_codec_name(struct hdac_codec *codec) { uint32_t id; int i; id = hdac_codec_id(codec); for (i = 0; i < HDAC_CODECS_LEN; i++) { if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) return (hdac_codecs[i].name); } return ((id == 0x00000000) ? "NULL Codec" : "Unknown Codec"); } static char * hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) { static char *ossname[] = SOUND_DEVICE_NAMES; int i, first = 1; bzero(buf, len); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (mask & (1 << i)) { if (first == 0) strlcat(buf, ", ", len); strlcat(buf, ossname[i], len); first = 0; } } return (buf); } static struct hdac_audio_ctl * hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) { if (devinfo == NULL || devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO || index == NULL || devinfo->function.audio.ctl == NULL || devinfo->function.audio.ctlcnt < 1 || *index < 0 || *index >= devinfo->function.audio.ctlcnt) return (NULL); return (&devinfo->function.audio.ctl[(*index)++]); } static struct hdac_audio_ctl * hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, int dir, int index, int cnt) { struct hdac_audio_ctl *ctl; int i, found = 0; if (devinfo == NULL || devinfo->function.audio.ctl == NULL) return (NULL); i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if (ctl->widget->nid != nid) continue; if (dir && ctl->ndir != dir) continue; if (index >= 0 && ctl->ndir == HDA_CTL_IN && ctl->dir == ctl->ndir && ctl->index != index) continue; found++; if (found == cnt || cnt <= 0) return (ctl); } return (NULL); } /* * Jack detection (Speaker/HP redirection) event handler. */ static void hdac_hp_switch_handler(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as; struct hdac_softc *sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; uint32_t val, res; int i, j; nid_t cad; if (devinfo == NULL || devinfo->codec == NULL || devinfo->codec->sc == NULL) return; sc = devinfo->codec->sc; cad = devinfo->codec->cad; as = devinfo->function.audio.as; for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].hpredir < 0) continue; w = hdac_widget_get(devinfo, as[i].pins[15]); if (w == NULL || w->enable == 0 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, as[i].pins[15]), cad); HDA_BOOTVERBOSE( device_printf(sc->dev, "Pin sense: nid=%d res=0x%08x\n", as[i].pins[15], res); ); res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); if (devinfo->function.audio.quirks & HDA_QUIRK_SENSEINV) res ^= 1; /* (Un)Mute headphone pin. */ ctl = hdac_audio_ctl_amp_get(devinfo, as[i].pins[15], HDA_CTL_IN, -1, 1); if (ctl != NULL && ctl->mute) { /* If pin has muter - use it. */ val = (res != 0) ? 0 : 1; if (val != ctl->forcemute) { ctl->forcemute = val; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); } } else { /* If there is no muter - disable pin output. */ w = hdac_widget_get(devinfo, as[i].pins[15]); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if (res != 0) val = w->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else val = w->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (val != w->wclass.pin.ctrl) { w->wclass.pin.ctrl = val; hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } } } /* (Un)Mute other pins. */ for (j = 0; j < 15; j++) { if (as[i].pins[j] <= 0) continue; ctl = hdac_audio_ctl_amp_get(devinfo, as[i].pins[j], HDA_CTL_IN, -1, 1); if (ctl != NULL && ctl->mute) { /* If pin has muter - use it. */ val = (res != 0) ? 1 : 0; if (val == ctl->forcemute) continue; ctl->forcemute = val; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); continue; } /* If there is no muter - disable pin output. */ w = hdac_widget_get(devinfo, as[i].pins[j]); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if (res != 0) val = w->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else val = w->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (val != w->wclass.pin.ctrl) { w->wclass.pin.ctrl = val; hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } } } } } /* * Callback for poll based jack detection. */ static void hdac_jack_poll_callback(void *arg) { struct hdac_devinfo *devinfo = arg; struct hdac_softc *sc; if (devinfo == NULL || devinfo->codec == NULL || devinfo->codec->sc == NULL) return; sc = devinfo->codec->sc; hdac_lock(sc); if (sc->poll_ival == 0) { hdac_unlock(sc); return; } hdac_hp_switch_handler(devinfo); callout_reset(&sc->poll_jack, sc->poll_ival, hdac_jack_poll_callback, devinfo); hdac_unlock(sc); } /* * Jack detection initializer. */ static void hdac_hp_switch_init(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; uint32_t id; int i, enable = 0, poll = 0; nid_t cad; id = hdac_codec_id(devinfo->codec); cad = devinfo->codec->cad; for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].hpredir < 0) continue; w = hdac_widget_get(devinfo, as[i].pins[15]); if (w == NULL || w->enable == 0 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { device_printf(sc->dev, "No jack detection support at pin %d\n", as[i].pins[15]); continue; } enable = 1; if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { hdac_command(sc, HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | HDAC_UNSOLTAG_EVENT_HP), cad); } else poll = 1; HDA_BOOTVERBOSE( device_printf(sc->dev, "Enabling headphone/speaker " "audio routing switching:\n"); device_printf(sc->dev, "\tas=%d sense nid=%d [%s]\n", i, w->nid, (poll != 0) ? "POLL" : "UNSOL"); ); } if (enable) { hdac_hp_switch_handler(devinfo); if (poll) { callout_reset(&sc->poll_jack, 1, hdac_jack_poll_callback, devinfo); } } } /* * Unsolicited messages handler. */ static void hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) { struct hdac_softc *sc; struct hdac_devinfo *devinfo = NULL; int i; if (codec == NULL || codec->sc == NULL) return; sc = codec->sc; HDA_BOOTVERBOSE( device_printf(sc->dev, "Unsol Tag: 0x%08x\n", tag); ); for (i = 0; i < codec->num_fgs; i++) { if (codec->fgs[i].node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { devinfo = &codec->fgs[i]; break; } } if (devinfo == NULL) return; switch (tag) { case HDAC_UNSOLTAG_EVENT_HP: hdac_hp_switch_handler(devinfo); break; default: device_printf(sc->dev, "Unknown unsol tag: 0x%08x!\n", tag); break; } } static int hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) { /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA uint32_t res; #endif if (!(ch->flags & HDAC_CHN_RUNNING)) return (0); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA res = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDSTS); #endif /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA HDA_BOOTVERBOSE( if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) device_printf(ch->pdevinfo->dev, "PCMDIR_%s intr triggered beyond stream boundary:" "%08x\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); ); #endif HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDSTS, HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA if (res & HDAC_SDSTS_BCIS) { #endif return (1); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA } #endif return (0); } /**************************************************************************** * void hdac_intr_handler(void *) * * Interrupt handler. Processes interrupts received from the hdac. ****************************************************************************/ static void hdac_intr_handler(void *context) { struct hdac_softc *sc; uint32_t intsts; uint8_t rirbsts; struct hdac_rirb *rirb_base; uint32_t trigger; int i; sc = (struct hdac_softc *)context; hdac_lock(sc); if (sc->polling != 0) { hdac_unlock(sc); return; } /* Do we have anything to do? */ intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { hdac_unlock(sc); return; } trigger = 0; /* Was this a controller interrupt? */ if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); /* Get as many responses that we can */ while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); if (hdac_rirb_flush(sc) != 0) trigger |= HDAC_TRIGGER_UNSOL; rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); } /* XXX to be removed */ /* Clear interrupt and exit */ #ifdef HDAC_INTR_EXTRA HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); #endif } if (intsts & HDAC_INTSTS_SIS_MASK) { for (i = 0; i < sc->num_chans; i++) { if ((intsts & (1 << (sc->chans[i].off >> 5))) && hdac_stream_intr(sc, &sc->chans[i]) != 0) trigger |= (1 << i); } /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK); #endif } hdac_unlock(sc); for (i = 0; i < sc->num_chans; i++) { if (trigger & (1 << i)) chn_intr(sc->chans[i].c); } if (trigger & HDAC_TRIGGER_UNSOL) taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); } /**************************************************************************** * int hdac_reset(hdac_softc *, int) * * Reset the hdac to a quiescent and known state. ****************************************************************************/ static int hdac_reset(struct hdac_softc *sc, int wakeup) { uint32_t gctl; int count, i; /* * Stop all Streams DMA engine */ for (i = 0; i < sc->num_iss; i++) HDAC_WRITE_4(&sc->mem, HDAC_ISDCTL(sc, i), 0x0); for (i = 0; i < sc->num_oss; i++) HDAC_WRITE_4(&sc->mem, HDAC_OSDCTL(sc, i), 0x0); for (i = 0; i < sc->num_bss; i++) HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0); /* * Stop Control DMA engines. */ HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0); HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0); /* * Reset DMA position buffer. */ HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, 0x0); HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, 0x0); /* * Reset the controller. The reset must remain asserted for * a minimum of 100us. */ gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl & ~HDAC_GCTL_CRST); count = 10000; do { gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); if (!(gctl & HDAC_GCTL_CRST)) break; DELAY(10); } while (--count); if (gctl & HDAC_GCTL_CRST) { device_printf(sc->dev, "Unable to put hdac in reset\n"); return (ENXIO); } /* If wakeup is not requested - leave the controller in reset state. */ if (!wakeup) return (0); DELAY(100); gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); count = 10000; do { gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); if (gctl & HDAC_GCTL_CRST) break; DELAY(10); } while (--count); if (!(gctl & HDAC_GCTL_CRST)) { device_printf(sc->dev, "Device stuck in reset\n"); return (ENXIO); } /* * Wait for codecs to finish their own reset sequence. The delay here * should be of 250us but for some reasons, on it's not enough on my * computer. Let's use twice as much as necessary to make sure that * it's reset properly. */ DELAY(1000); return (0); } /**************************************************************************** * int hdac_get_capabilities(struct hdac_softc *); * * Retreive the general capabilities of the hdac; * Number of Input Streams * Number of Output Streams * Number of bidirectional Streams * 64bit ready * CORB and RIRB sizes ****************************************************************************/ static int hdac_get_capabilities(struct hdac_softc *sc) { uint16_t gcap; uint8_t corbsize, rirbsize; gcap = HDAC_READ_2(&sc->mem, HDAC_GCAP); sc->num_iss = HDAC_GCAP_ISS(gcap); sc->num_oss = HDAC_GCAP_OSS(gcap); sc->num_bss = HDAC_GCAP_BSS(gcap); sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == HDAC_CORBSIZE_CORBSZCAP_256) sc->corb_size = 256; else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_16) == HDAC_CORBSIZE_CORBSZCAP_16) sc->corb_size = 16; else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_2) == HDAC_CORBSIZE_CORBSZCAP_2) sc->corb_size = 2; else { device_printf(sc->dev, "%s: Invalid corb size (%x)\n", __func__, corbsize); return (ENXIO); } rirbsize = HDAC_READ_1(&sc->mem, HDAC_RIRBSIZE); if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_256) == HDAC_RIRBSIZE_RIRBSZCAP_256) sc->rirb_size = 256; else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_16) == HDAC_RIRBSIZE_RIRBSZCAP_16) sc->rirb_size = 16; else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_2) == HDAC_RIRBSIZE_RIRBSZCAP_2) sc->rirb_size = 2; else { device_printf(sc->dev, "%s: Invalid rirb size (%x)\n", __func__, rirbsize); return (ENXIO); } HDA_BOOTHVERBOSE( device_printf(sc->dev, " CORB size: %d\n", sc->corb_size); device_printf(sc->dev, " RIRB size: %d\n", sc->rirb_size); device_printf(sc->dev, " Streams: ISS=%d OSS=%d BSS=%d\n", sc->num_iss, sc->num_oss, sc->num_bss); ); return (0); } /**************************************************************************** * void hdac_dma_cb * * This function is called by bus_dmamap_load when the mapping has been * established. We just record the physical address of the mapping into * the struct hdac_dma passed in. ****************************************************************************/ static void hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error) { struct hdac_dma *dma; if (error == 0) { dma = (struct hdac_dma *)callback_arg; dma->dma_paddr = segs[0].ds_addr; } } /**************************************************************************** * int hdac_dma_alloc * * This function allocate and setup a dma region (struct hdac_dma). * It must be freed by a corresponding hdac_dma_free. ****************************************************************************/ static int hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) { bus_size_t roundsz; int result; int lowaddr; roundsz = roundup2(size, HDAC_DMA_ALIGNMENT); lowaddr = (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT; bzero(dma, sizeof(*dma)); /* * Create a DMA tag */ result = bus_dma_tag_create(NULL, /* parent */ HDAC_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ roundsz, /* maxsize */ 1, /* nsegments */ roundsz, /* maxsegsz */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &dma->dma_tag); /* dmat */ if (result != 0) { device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", __func__, result); goto hdac_dma_alloc_fail; } /* * Allocate DMA memory */ result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, BUS_DMA_NOWAIT | BUS_DMA_ZERO | ((sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), &dma->dma_map); if (result != 0) { device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", __func__, result); goto hdac_dma_alloc_fail; } dma->dma_size = roundsz; /* * Map the memory */ result = bus_dmamap_load(dma->dma_tag, dma->dma_map, (void *)dma->dma_vaddr, roundsz, hdac_dma_cb, (void *)dma, 0); if (result != 0 || dma->dma_paddr == 0) { if (result == 0) result = ENOMEM; device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n", __func__, result); goto hdac_dma_alloc_fail; } HDA_BOOTHVERBOSE( device_printf(sc->dev, "%s: size=%ju -> roundsz=%ju\n", __func__, (uintmax_t)size, (uintmax_t)roundsz); ); return (0); hdac_dma_alloc_fail: hdac_dma_free(sc, dma); return (result); } /**************************************************************************** * void hdac_dma_free(struct hdac_softc *, struct hdac_dma *) * * Free a struct dhac_dma that has been previously allocated via the * hdac_dma_alloc function. ****************************************************************************/ static void hdac_dma_free(struct hdac_softc *sc, struct hdac_dma *dma) { if (dma->dma_map != NULL) { #if 0 /* Flush caches */ bus_dmamap_sync(dma->dma_tag, dma->dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); #endif bus_dmamap_unload(dma->dma_tag, dma->dma_map); } if (dma->dma_vaddr != NULL) { bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); dma->dma_vaddr = NULL; } dma->dma_map = NULL; if (dma->dma_tag != NULL) { bus_dma_tag_destroy(dma->dma_tag); dma->dma_tag = NULL; } dma->dma_size = 0; } /**************************************************************************** * int hdac_mem_alloc(struct hdac_softc *) * * Allocate all the bus resources necessary to speak with the physical * controller. ****************************************************************************/ static int hdac_mem_alloc(struct hdac_softc *sc) { struct hdac_mem *mem; mem = &sc->mem; mem->mem_rid = PCIR_BAR(0); mem->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &mem->mem_rid, RF_ACTIVE); if (mem->mem_res == NULL) { device_printf(sc->dev, "%s: Unable to allocate memory resource\n", __func__); return (ENOMEM); } mem->mem_tag = rman_get_bustag(mem->mem_res); mem->mem_handle = rman_get_bushandle(mem->mem_res); return (0); } /**************************************************************************** * void hdac_mem_free(struct hdac_softc *) * * Free up resources previously allocated by hdac_mem_alloc. ****************************************************************************/ static void hdac_mem_free(struct hdac_softc *sc) { struct hdac_mem *mem; mem = &sc->mem; if (mem->mem_res != NULL) bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid, mem->mem_res); mem->mem_res = NULL; } /**************************************************************************** * int hdac_irq_alloc(struct hdac_softc *) * * Allocate and setup the resources necessary for interrupt handling. ****************************************************************************/ static int hdac_irq_alloc(struct hdac_softc *sc) { struct hdac_irq *irq; int result; irq = &sc->irq; irq->irq_rid = 0x0; #ifdef HDAC_MSI_ENABLED if ((sc->flags & HDAC_F_MSI) && (result = pci_msi_count(sc->dev)) == 1 && pci_alloc_msi(sc->dev, &result) == 0) irq->irq_rid = 0x1; else #endif sc->flags &= ~HDAC_F_MSI; irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); if (irq->irq_res == NULL) { device_printf(sc->dev, "%s: Unable to allocate irq\n", __func__); goto hdac_irq_alloc_fail; } result = bus_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE | INTR_TYPE_AV, NULL, hdac_intr_handler, sc, &irq->irq_handle); if (result != 0) { device_printf(sc->dev, "%s: Unable to setup interrupt handler (%x)\n", __func__, result); goto hdac_irq_alloc_fail; } return (0); hdac_irq_alloc_fail: hdac_irq_free(sc); return (ENXIO); } /**************************************************************************** * void hdac_irq_free(struct hdac_softc *) * * Free up resources previously allocated by hdac_irq_alloc. ****************************************************************************/ static void hdac_irq_free(struct hdac_softc *sc) { struct hdac_irq *irq; irq = &sc->irq; if (irq->irq_res != NULL && irq->irq_handle != NULL) bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle); if (irq->irq_res != NULL) bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, irq->irq_res); #ifdef HDAC_MSI_ENABLED if ((sc->flags & HDAC_F_MSI) && irq->irq_rid == 0x1) pci_release_msi(sc->dev); #endif irq->irq_handle = NULL; irq->irq_res = NULL; irq->irq_rid = 0x0; } /**************************************************************************** * void hdac_corb_init(struct hdac_softc *) * * Initialize the corb registers for operations but do not start it up yet. * The CORB engine must not be running when this function is called. ****************************************************************************/ static void hdac_corb_init(struct hdac_softc *sc) { uint8_t corbsize; uint64_t corbpaddr; /* Setup the CORB size. */ switch (sc->corb_size) { case 256: corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_256); break; case 16: corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_16); break; case 2: corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_2); break; default: panic("%s: Invalid CORB size (%x)\n", __func__, sc->corb_size); } HDAC_WRITE_1(&sc->mem, HDAC_CORBSIZE, corbsize); /* Setup the CORB Address in the hdac */ corbpaddr = (uint64_t)sc->corb_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, HDAC_CORBLBASE, (uint32_t)corbpaddr); HDAC_WRITE_4(&sc->mem, HDAC_CORBUBASE, (uint32_t)(corbpaddr >> 32)); /* Set the WP and RP */ sc->corb_wp = 0; HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, HDAC_CORBRP_CORBRPRST); /* * The HDA specification indicates that the CORBRPRST bit will always * read as zero. Unfortunately, it seems that at least the 82801G * doesn't reset the bit to zero, which stalls the corb engine. * manually reset the bit to zero before continuing. */ HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, 0x0); /* Enable CORB error reporting */ #if 0 HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, HDAC_CORBCTL_CMEIE); #endif } /**************************************************************************** * void hdac_rirb_init(struct hdac_softc *) * * Initialize the rirb registers for operations but do not start it up yet. * The RIRB engine must not be running when this function is called. ****************************************************************************/ static void hdac_rirb_init(struct hdac_softc *sc) { uint8_t rirbsize; uint64_t rirbpaddr; /* Setup the RIRB size. */ switch (sc->rirb_size) { case 256: rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_256); break; case 16: rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_16); break; case 2: rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_2); break; default: panic("%s: Invalid RIRB size (%x)\n", __func__, sc->rirb_size); } HDAC_WRITE_1(&sc->mem, HDAC_RIRBSIZE, rirbsize); /* Setup the RIRB Address in the hdac */ rirbpaddr = (uint64_t)sc->rirb_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, HDAC_RIRBLBASE, (uint32_t)rirbpaddr); HDAC_WRITE_4(&sc->mem, HDAC_RIRBUBASE, (uint32_t)(rirbpaddr >> 32)); /* Setup the WP and RP */ sc->rirb_rp = 0; HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); /* Setup the interrupt threshold */ HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); /* Enable Overrun and response received reporting */ #if 0 HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); #else HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); #endif #if 0 /* * Make sure that the Host CPU cache doesn't contain any dirty * cache lines that falls in the rirb. If I understood correctly, it * should be sufficient to do this only once as the rirb is purely * read-only from now on. */ bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, BUS_DMASYNC_PREREAD); #endif } /**************************************************************************** * void hdac_corb_start(hdac_softc *) * * Startup the corb DMA engine ****************************************************************************/ static void hdac_corb_start(struct hdac_softc *sc) { uint32_t corbctl; corbctl = HDAC_READ_1(&sc->mem, HDAC_CORBCTL); corbctl |= HDAC_CORBCTL_CORBRUN; HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, corbctl); } /**************************************************************************** * void hdac_rirb_start(hdac_softc *) * * Startup the rirb DMA engine ****************************************************************************/ static void hdac_rirb_start(struct hdac_softc *sc) { uint32_t rirbctl; rirbctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); rirbctl |= HDAC_RIRBCTL_RIRBDMAEN; HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); } /**************************************************************************** * void hdac_scan_codecs(struct hdac_softc *, int) * * Scan the bus for available codecs, starting with num. ****************************************************************************/ static void hdac_scan_codecs(struct hdac_softc *sc) { struct hdac_codec *codec; int i; uint16_t statests; statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); for (i = 0; i < HDAC_CODEC_MAX; i++) { if (HDAC_STATESTS_SDIWAKE(statests, i)) { /* We have found a codec. */ codec = (struct hdac_codec *)malloc(sizeof(*codec), M_HDAC, M_ZERO | M_NOWAIT); if (codec == NULL) { device_printf(sc->dev, "Unable to allocate memory for codec\n"); continue; } codec->commands = NULL; codec->responses_received = 0; codec->verbs_sent = 0; codec->sc = sc; codec->cad = i; sc->codecs[i] = codec; hdac_probe_codec(codec); } } /* All codecs have been probed, now try to attach drivers to them */ /* bus_generic_attach(sc->dev); */ } /**************************************************************************** * void hdac_probe_codec(struct hdac_softc *, int) * * Probe a the given codec_id for available function groups. ****************************************************************************/ static void hdac_probe_codec(struct hdac_codec *codec) { struct hdac_softc *sc = codec->sc; uint32_t vendorid, revisionid, subnode; int startnode; int endnode; int i; nid_t cad = codec->cad; HDA_BOOTVERBOSE( device_printf(sc->dev, "Probing codec #%d...\n", cad); ); vendorid = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), cad); revisionid = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), cad); codec->vendor_id = HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); codec->device_id = HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); codec->revision_id = HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); codec->stepping_id = HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); if (vendorid == HDAC_INVALID && revisionid == HDAC_INVALID) { device_printf(sc->dev, "Codec #%d is not responding!" " Probing aborted.\n", cad); return; } device_printf(sc->dev, "HDA Codec #%d: %s\n", cad, hdac_codec_name(codec)); HDA_BOOTVERBOSE( device_printf(sc->dev, " HDA Codec ID: 0x%08x\n", hdac_codec_id(codec)); device_printf(sc->dev, " Vendor: 0x%04x\n", codec->vendor_id); device_printf(sc->dev, " Device: 0x%04x\n", codec->device_id); device_printf(sc->dev, " Revision: 0x%02x\n", codec->revision_id); device_printf(sc->dev, " Stepping: 0x%02x\n", codec->stepping_id); device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", sc->pci_subvendor); ); subnode = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), cad); startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); HDA_BOOTHVERBOSE( device_printf(sc->dev, "\tstartnode=%d endnode=%d\n", startnode, endnode); ); codec->fgs = (struct hdac_devinfo *)malloc(sizeof(struct hdac_devinfo) * (endnode - startnode), M_HDAC, M_NOWAIT | M_ZERO); if (codec->fgs == NULL) { device_printf(sc->dev, "%s: Unable to allocate function groups\n", __func__); return; } for (i = startnode; i < endnode; i++) hdac_probe_function(codec, i); return; } /* * Probe codec function and add it to the list. */ static void hdac_probe_function(struct hdac_codec *codec, nid_t nid) { struct hdac_softc *sc = codec->sc; struct hdac_devinfo *devinfo = &codec->fgs[codec->num_fgs]; uint32_t fctgrptype; uint32_t res; nid_t cad = codec->cad; fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); devinfo->nid = nid; devinfo->node_type = fctgrptype; devinfo->codec = codec; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); devinfo->endnode = devinfo->startnode + devinfo->nodecnt; HDA_BOOTVERBOSE( device_printf(sc->dev, "\tFound %s FG nid=%d startnode=%d endnode=%d total=%d\n", (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": "unknown", nid, devinfo->startnode, devinfo->endnode, devinfo->nodecnt); ); if (devinfo->nodecnt > 0) devinfo->widget = (struct hdac_widget *)malloc( sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, M_NOWAIT | M_ZERO); else devinfo->widget = NULL; if (devinfo->widget == NULL) { device_printf(sc->dev, "unable to allocate widgets!\n"); devinfo->endnode = devinfo->startnode; devinfo->nodecnt = 0; return; } codec->num_fgs++; } static void hdac_widget_connection_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t res; int i, j, max, ents, entnum; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; nid_t cnid, addcnid, prevcnid; w->nconns = 0; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); if (ents < 1) return; entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; prevcnid = 0; #define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) #define CONN_NMASK(e) (CONN_RMASK(e) - 1) #define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) #define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) #define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) for (i = 0; i < ents; i += entnum) { res = hdac_command(sc, HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); for (j = 0; j < entnum; j++) { cnid = CONN_CNID(res, entnum, j); if (cnid == 0) { if (w->nconns < ents) device_printf(sc->dev, "%s: nid=%d WARNING: zero cnid " "entnum=%d j=%d index=%d " "entries=%d found=%d res=0x%08x\n", __func__, nid, entnum, j, i, ents, w->nconns, res); else goto getconns_out; } if (cnid < w->devinfo->startnode || cnid >= w->devinfo->endnode) { HDA_BOOTVERBOSE( device_printf(sc->dev, "GHOST: nid=%d j=%d " "entnum=%d index=%d res=0x%08x\n", nid, j, entnum, i, res); ); } if (CONN_RANGE(res, entnum, j) == 0) addcnid = cnid; else if (prevcnid == 0 || prevcnid >= cnid) { device_printf(sc->dev, "%s: WARNING: Invalid child range " "nid=%d index=%d j=%d entnum=%d " "prevcnid=%d cnid=%d res=0x%08x\n", __func__, nid, i, j, entnum, prevcnid, cnid, res); addcnid = cnid; } else addcnid = prevcnid + 1; while (addcnid <= cnid) { if (w->nconns > max) { device_printf(sc->dev, "Adding %d (nid=%d): " "Max connection reached! max=%d\n", addcnid, nid, max + 1); goto getconns_out; } w->connsenable[w->nconns] = 1; w->conns[w->nconns++] = addcnid++; } prevcnid = cnid; } } getconns_out: return; } static uint32_t hdac_widget_pin_patch(uint32_t config, const char *str) { char buf[256]; char *key, *value, *rest, *bad; int ival, i; strlcpy(buf, str, sizeof(buf)); rest = buf; while ((key = strsep(&rest, "=")) != NULL) { value = strsep(&rest, " \t"); if (value == NULL) break; ival = strtol(value, &bad, 10); if (strcmp(key, "seq") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); } else if (strcmp(key, "as") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); } else if (strcmp(key, "misc") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & HDA_CONFIG_DEFAULTCONF_MISC_MASK); } else if (strcmp(key, "color") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; if (bad[0] == 0) { config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & HDA_CONFIG_DEFAULTCONF_COLOR_MASK); }; for (i = 0; i < 16; i++) { if (strcasecmp(HDA_COLORS[i], value) == 0) { config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); break; } } } else if (strcmp(key, "ctype") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); } else if (strcmp(key, "device") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; if (bad[0] == 0) { config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); continue; }; for (i = 0; i < 16; i++) { if (strcasecmp(HDA_DEVS[i], value) == 0) { config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); break; } } } else if (strcmp(key, "loc") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); } else if (strcmp(key, "conn") == 0) { config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; if (bad[0] == 0) { config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); continue; }; for (i = 0; i < 4; i++) { if (strcasecmp(HDA_CONNS[i], value) == 0) { config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); break; } } } } return (config); } static uint32_t hdac_widget_pin_getconfig(struct hdac_widget *w) { struct hdac_softc *sc; uint32_t config, orig, id; nid_t cad, nid; char buf[32]; const char *res = NULL, *patch = NULL; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; id = hdac_codec_id(w->devinfo->codec); config = hdac_command(sc, HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), cad); orig = config; HDA_BOOTVERBOSE( hdac_dump_pin_config(w, orig); ); /* XXX: Old patches require complete review. * Now they may create more problem then solve due to * incorrect associations. */ if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { switch (nid) { case 26: config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; break; case 27: config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; break; default: break; } } else if (id == HDA_CODEC_ALC880 && (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { /* * Super broken BIOS */ switch (nid) { case 24: /* MIC1 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; break; case 25: /* XXX MIC2 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; break; case 26: /* LINE1 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; break; case 27: /* XXX LINE2 */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; break; case 28: /* CD */ config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; break; } } else if (id == HDA_CODEC_ALC883 && (sc->pci_subvendor == MSI_MS034A_SUBVENDOR || HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor))) { switch (nid) { case 25: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 28: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; } - } else if (id == HDA_CODEC_CXVENICE && sc->pci_subvendor == + } else if (id == HDA_CODEC_CX20549 && sc->pci_subvendor == HP_V3000_SUBVENDOR) { switch (nid) { case 18: config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; break; case 20: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 21: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; } - } else if (id == HDA_CODEC_CXWAIKIKI && sc->pci_subvendor == + } else if (id == HDA_CODEC_CX20551 && sc->pci_subvendor == HP_DV5000_SUBVENDOR) { switch (nid) { case 20: case 21: config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; break; } } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == ASUS_W6F_SUBVENDOR) { switch (nid) { case 11: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 12: case 14: case 16: case 31: case 32: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); break; case 15: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); break; } } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == UNIWILL_9075_SUBVENDOR) { switch (nid) { case 15: config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); break; } } /* New patches */ if (id == HDA_CODEC_AD1986A && (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR || sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR)) { switch (nid) { case 28: /* 5.1 out => 2.0 out + 2 inputs */ patch = "device=Line-in as=8 seq=1"; break; case 29: patch = "device=Mic as=8 seq=2"; break; case 31: /* Lot of inputs configured with as=15 and unusable */ patch = "as=8 seq=3"; break; case 32: patch = "as=8 seq=4"; break; case 34: patch = "as=8 seq=5"; break; case 36: patch = "as=8 seq=6"; break; } } else if (id == HDA_CODEC_ALC260 && HDA_DEV_MATCH(SONY_S5_SUBVENDOR, sc->pci_subvendor)) { switch (nid) { case 16: patch = "seq=15 device=Headphones"; break; } } else if (id == HDA_CODEC_ALC268 && HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor)) { switch (nid) { case 28: patch = "device=CD conn=fixed"; break; } } if (patch != NULL) config = hdac_widget_pin_patch(config, patch); snprintf(buf, sizeof(buf), "cad%u.nid%u.config", cad, nid); if (resource_string_value(device_get_name(sc->dev), device_get_unit(sc->dev), buf, &res) == 0) { if (strncmp(res, "0x", 2) == 0) { config = strtol(res + 2, NULL, 16); } else { config = hdac_widget_pin_patch(config, res); } } HDA_BOOTVERBOSE( if (config != orig) device_printf(sc->dev, "Patching pin config nid=%u 0x%08x -> 0x%08x\n", nid, orig, config); ); return (config); } static uint32_t hdac_widget_pin_getcaps(struct hdac_widget *w) { struct hdac_softc *sc; uint32_t caps, orig, id; nid_t cad, nid; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; id = hdac_codec_id(w->devinfo->codec); caps = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); orig = caps; HDA_BOOTVERBOSE( if (caps != orig) device_printf(sc->dev, "Patching pin caps nid=%u 0x%08x -> 0x%08x\n", nid, orig, caps); ); return (caps); } static void hdac_widget_pin_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t config, pincap; const char *devstr, *connstr; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; config = hdac_widget_pin_getconfig(w); w->wclass.pin.config = config; pincap = hdac_widget_pin_getcaps(w); w->wclass.pin.cap = pincap; w->wclass.pin.ctrl = hdac_command(sc, HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad); if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { w->param.eapdbtl = hdac_command(sc, HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); w->param.eapdbtl &= 0x7; w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; } else w->param.eapdbtl = HDAC_INVALID; devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; connstr = HDA_CONNS[(config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT]; strlcat(w->name, ": ", sizeof(w->name)); strlcat(w->name, devstr, sizeof(w->name)); strlcat(w->name, " (", sizeof(w->name)); strlcat(w->name, connstr, sizeof(w->name)); strlcat(w->name, ")", sizeof(w->name)); } static uint32_t hdac_widget_getcaps(struct hdac_widget *w, int *waspin) { struct hdac_softc *sc; uint32_t caps, orig, id; nid_t cad, nid, beeper = -1; sc = w->devinfo->codec->sc; cad = w->devinfo->codec->cad; nid = w->nid; id = hdac_codec_id(w->devinfo->codec); caps = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), cad); orig = caps; /* On some codecs beeper is an input pin, but it is not recordable alone. Also most of BIOSes does not declare beeper pin. Change beeper pin node type to beeper to help parser. */ *waspin = 0; switch (id) { case HDA_CODEC_AD1988: case HDA_CODEC_AD1988B: beeper = 26; break; case HDA_CODEC_ALC260: beeper = 23; break; case HDA_CODEC_ALC262: case HDA_CODEC_ALC268: case HDA_CODEC_ALC880: case HDA_CODEC_ALC882: case HDA_CODEC_ALC883: case HDA_CODEC_ALC885: case HDA_CODEC_ALC888: case HDA_CODEC_ALC889: beeper = 29; break; } if (nid == beeper) { caps &= ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; caps |= HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; *waspin = 1; } HDA_BOOTVERBOSE( if (caps != orig) { device_printf(sc->dev, "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", nid, orig, caps); } ); return (caps); } static void hdac_widget_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t wcap, cap; char *typestr; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; wcap = hdac_widget_getcaps(w, &w->waspin); w->param.widget_cap = wcap; w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: typestr = "audio output"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: typestr = "audio input"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: typestr = "audio mixer"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: typestr = "audio selector"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: typestr = "pin"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: typestr = "power widget"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: typestr = "volume widget"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: typestr = "beep widget"; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: typestr = "vendor widget"; break; default: typestr = "unknown type"; break; } strlcpy(w->name, typestr, sizeof(w->name)); hdac_widget_connection_parse(w); if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) w->param.outamp_cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), cad); else w->param.outamp_cap = w->devinfo->function.audio.outamp_cap; } else w->param.outamp_cap = 0; if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) w->param.inamp_cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), cad); else w->param.inamp_cap = w->devinfo->function.audio.inamp_cap; } else w->param.inamp_cap = 0; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), cad); w->param.supp_stream_formats = (cap != 0) ? cap : w->devinfo->function.audio.supp_stream_formats; cap = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); w->param.supp_pcm_size_rate = (cap != 0) ? cap : w->devinfo->function.audio.supp_pcm_size_rate; } else { w->param.supp_stream_formats = w->devinfo->function.audio.supp_stream_formats; w->param.supp_pcm_size_rate = w->devinfo->function.audio.supp_pcm_size_rate; } } else { w->param.supp_stream_formats = 0; w->param.supp_pcm_size_rate = 0; } if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) hdac_widget_pin_parse(w); } static struct hdac_widget * hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) { if (devinfo == NULL || devinfo->widget == NULL || nid < devinfo->startnode || nid >= devinfo->endnode) return (NULL); return (&devinfo->widget[nid - devinfo->startnode]); } static __inline int hda_poll_channel(struct hdac_chan *ch) { uint32_t sz, delta; volatile uint32_t ptr; if (!(ch->flags & HDAC_CHN_RUNNING)) return (0); sz = ch->blksz * ch->blkcnt; if (ch->dmapos != NULL) ptr = *(ch->dmapos); else ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, ch->off + HDAC_SDLPIB); ch->ptr = ptr; ptr %= sz; ptr &= ~(ch->blksz - 1); delta = (sz + ptr - ch->prevptr) % sz; if (delta < ch->blksz) return (0); ch->prevptr = ptr; return (1); } static void hda_poll_callback(void *arg) { struct hdac_softc *sc = arg; uint32_t trigger; int i, active = 0; if (sc == NULL) return; hdac_lock(sc); if (sc->polling == 0) { hdac_unlock(sc); return; } trigger = 0; for (i = 0; i < sc->num_chans; i++) { if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) continue; active = 1; if (hda_poll_channel(&sc->chans[i])) trigger |= (1 << i); } /* XXX */ if (active) callout_reset(&sc->poll_hda, sc->poll_ticks, hda_poll_callback, sc); hdac_unlock(sc); for (i = 0; i < sc->num_chans; i++) { if (trigger & (1 << i)) chn_intr(sc->chans[i].c); } } static int hdac_rirb_flush(struct hdac_softc *sc) { struct hdac_rirb *rirb_base, *rirb; struct hdac_codec *codec; struct hdac_command_list *commands; nid_t cad; uint32_t resp; uint8_t rirbwp; int ret; rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); #if 0 bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, BUS_DMASYNC_POSTREAD); #endif ret = 0; while (sc->rirb_rp != rirbwp) { sc->rirb_rp++; sc->rirb_rp %= sc->rirb_size; rirb = &rirb_base[sc->rirb_rp]; cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); if (cad < 0 || cad >= HDAC_CODEC_MAX || sc->codecs[cad] == NULL) continue; resp = rirb->response; codec = sc->codecs[cad]; commands = codec->commands; if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { sc->unsolq[sc->unsolq_wp++] = (cad << 16) | ((resp >> 26) & 0xffff); sc->unsolq_wp %= HDAC_UNSOLQ_MAX; } else if (commands != NULL && commands->num_commands > 0 && codec->responses_received < commands->num_commands) commands->responses[codec->responses_received++] = resp; ret++; } return (ret); } static int hdac_unsolq_flush(struct hdac_softc *sc) { nid_t cad; uint32_t tag; int ret = 0; if (sc->unsolq_st == HDAC_UNSOLQ_READY) { sc->unsolq_st = HDAC_UNSOLQ_BUSY; while (sc->unsolq_rp != sc->unsolq_wp) { cad = sc->unsolq[sc->unsolq_rp] >> 16; tag = sc->unsolq[sc->unsolq_rp++] & 0xffff; sc->unsolq_rp %= HDAC_UNSOLQ_MAX; hdac_unsolicited_handler(sc->codecs[cad], tag); ret++; } sc->unsolq_st = HDAC_UNSOLQ_READY; } return (ret); } static void hdac_poll_callback(void *arg) { struct hdac_softc *sc = arg; if (sc == NULL) return; hdac_lock(sc); if (sc->polling == 0 || sc->poll_ival == 0) { hdac_unlock(sc); return; } if (hdac_rirb_flush(sc) != 0) hdac_unsolq_flush(sc); callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc); hdac_unlock(sc); } static void hdac_poll_reinit(struct hdac_softc *sc) { int i, pollticks, min = 1000000; struct hdac_chan *ch; for (i = 0; i < sc->num_chans; i++) { if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) continue; ch = &sc->chans[i]; pollticks = ((uint64_t)hz * ch->blksz) / ((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b)); pollticks >>= 1; if (pollticks > hz) pollticks = hz; if (pollticks < 1) { HDA_BOOTVERBOSE( device_printf(sc->dev, "%s: pollticks=%d < 1 !\n", __func__, pollticks); ); pollticks = 1; } if (min > pollticks) min = pollticks; } HDA_BOOTVERBOSE( device_printf(sc->dev, "%s: pollticks %d -> %d\n", __func__, sc->poll_ticks, min); ); sc->poll_ticks = min; if (min == 1000000) callout_stop(&sc->poll_hda); else callout_reset(&sc->poll_hda, 1, hda_poll_callback, sc); } static void hdac_stream_stop(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | HDAC_SDCTL_RUN); HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); ch->flags &= ~HDAC_CHN_RUNNING; if (sc->polling != 0) hdac_poll_reinit(sc); ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl &= ~(1 << (ch->off >> 5)); HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); } static void hdac_stream_start(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; ch->flags |= HDAC_CHN_RUNNING; if (sc->polling != 0) hdac_poll_reinit(sc); ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl |= 1 << (ch->off >> 5); HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | HDAC_SDCTL_RUN; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); } static void hdac_stream_reset(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; int timeout = 1000; int to = timeout; uint32_t ctl; ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); ctl |= HDAC_SDCTL_SRST; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); do { ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); if (ctl & HDAC_SDCTL_SRST) break; DELAY(10); } while (--to); if (!(ctl & HDAC_SDCTL_SRST)) { device_printf(sc->dev, "timeout in reset\n"); } ctl &= ~HDAC_SDCTL_SRST; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); to = timeout; do { ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); if (!(ctl & HDAC_SDCTL_SRST)) break; DELAY(10); } while (--to); if (ctl & HDAC_SDCTL_SRST) device_printf(sc->dev, "can't reset!\n"); } static void hdac_stream_setid(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL2); ctl &= ~HDAC_SDCTL2_STRM_MASK; ctl |= ch->sid << HDAC_SDCTL2_STRM_SHIFT; HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL2, ctl); } static void hdac_bdl_setup(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; struct hdac_bdle *bdle; uint64_t addr; uint32_t blksz, blkcnt; int i; addr = (uint64_t)sndbuf_getbufaddr(ch->b); bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; blksz = ch->blksz; blkcnt = ch->blkcnt; for (i = 0; i < blkcnt; i++, bdle++) { bdle->addrl = (uint32_t)addr; bdle->addrh = (uint32_t)(addr >> 32); bdle->len = blksz; bdle->ioc = 1; addr += blksz; } HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt); HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1); addr = ch->bdl_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); if (ch->dmapos != NULL && !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) { addr = sc->pos_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001); HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (uint32_t)(addr >> 32)); } } static int hdac_bdl_alloc(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; int rc; rc = hdac_dma_alloc(sc, &ch->bdl_dma, sizeof(struct hdac_bdle) * HDA_BDL_MAX); if (rc) { device_printf(sc->dev, "can't alloc bdl\n"); return (rc); } return (0); } static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, int index, int lmute, int rmute, int left, int right, int dir) { uint16_t v = 0; if (sc == NULL) return; if (left != right || lmute != rmute) { v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | (lmute << 7) | left; hdac_command(sc, HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | (rmute << 7) | right; } else v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | (lmute << 7) | left; hdac_command(sc, HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); } static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, int left, int right) { struct hdac_softc *sc; nid_t nid, cad; int lmute, rmute; sc = ctl->widget->devinfo->codec->sc; cad = ctl->widget->devinfo->codec->cad; nid = ctl->widget->nid; /* Save new values if valid. */ if (mute != HDA_AMP_MUTE_DEFAULT) ctl->muted = mute; if (left != HDA_AMP_VOL_DEFAULT) ctl->left = left; if (right != HDA_AMP_VOL_DEFAULT) ctl->right = right; /* Prepare effective values */ if (ctl->forcemute) { lmute = 1; rmute = 1; left = 0; right = 0; } else { lmute = HDA_AMP_LEFT_MUTED(ctl->muted); rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); left = ctl->left; right = ctl->right; } /* Apply effective values */ if (ctl->dir & HDA_CTL_OUT) hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, lmute, rmute, left, right, 0); if (ctl->dir & HDA_CTL_IN) hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, lmute, rmute, left, right, 1); } static void hdac_widget_connection_select(struct hdac_widget *w, uint8_t index) { if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) return; hdac_command(w->devinfo->codec->sc, HDA_CMD_SET_CONNECTION_SELECT_CONTROL(w->devinfo->codec->cad, w->nid, index), w->devinfo->codec->cad); w->selconn = index; } /**************************************************************************** * uint32_t hdac_command_sendone_internal * * Wrapper function that sends only one command to a given codec ****************************************************************************/ static uint32_t hdac_command_sendone_internal(struct hdac_softc *sc, uint32_t verb, nid_t cad) { struct hdac_command_list cl; uint32_t response = HDAC_INVALID; if (!hdac_lockowned(sc)) device_printf(sc->dev, "WARNING!!!! mtx not owned!!!!\n"); cl.num_commands = 1; cl.verbs = &verb; cl.responses = &response; hdac_command_send_internal(sc, &cl, cad); return (response); } /**************************************************************************** * hdac_command_send_internal * * Send a command list to the codec via the corb. We queue as much verbs as * we can and msleep on the codec. When the interrupt get the responses * back from the rirb, it will wake us up so we can queue the remaining verbs * if any. ****************************************************************************/ static void hdac_command_send_internal(struct hdac_softc *sc, struct hdac_command_list *commands, nid_t cad) { struct hdac_codec *codec; int corbrp; uint32_t *corb; int timeout; int retry = 10; struct hdac_rirb *rirb_base; if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL || commands->num_commands < 1) return; codec = sc->codecs[cad]; codec->commands = commands; codec->responses_received = 0; codec->verbs_sent = 0; corb = (uint32_t *)sc->corb_dma.dma_vaddr; rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; do { if (codec->verbs_sent != commands->num_commands) { /* Queue as many verbs as possible */ corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); #if 0 bus_dmamap_sync(sc->corb_dma.dma_tag, sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); #endif while (codec->verbs_sent != commands->num_commands && ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { sc->corb_wp++; sc->corb_wp %= sc->corb_size; corb[sc->corb_wp] = commands->verbs[codec->verbs_sent++]; } /* Send the verbs to the codecs */ #if 0 bus_dmamap_sync(sc->corb_dma.dma_tag, sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); #endif HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); } timeout = 1000; while (hdac_rirb_flush(sc) == 0 && --timeout) DELAY(10); } while ((codec->verbs_sent != commands->num_commands || codec->responses_received != commands->num_commands) && --retry); if (retry == 0) device_printf(sc->dev, "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", __func__, commands->num_commands, codec->verbs_sent, codec->responses_received); codec->commands = NULL; codec->responses_received = 0; codec->verbs_sent = 0; hdac_unsolq_flush(sc); } /**************************************************************************** * Device Methods ****************************************************************************/ /**************************************************************************** * int hdac_probe(device_t) * * Probe for the presence of an hdac. If none is found, check for a generic * match using the subclass of the device. ****************************************************************************/ static int hdac_probe(device_t dev) { int i, result; uint32_t model; uint16_t class, subclass; char desc[64]; model = (uint32_t)pci_get_device(dev) << 16; model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; class = pci_get_class(dev); subclass = pci_get_subclass(dev); bzero(desc, sizeof(desc)); result = ENXIO; for (i = 0; i < HDAC_DEVICES_LEN; i++) { if (hdac_devices[i].model == model) { strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); result = BUS_PROBE_DEFAULT; break; } if (HDA_DEV_MATCH(hdac_devices[i].model, model) && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); result = BUS_PROBE_GENERIC; break; } } if (result == ENXIO && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { strlcpy(desc, "Generic", sizeof(desc)); result = BUS_PROBE_GENERIC; } if (result != ENXIO) { strlcat(desc, " High Definition Audio Controller", sizeof(desc)); device_set_desc_copy(dev, desc); } return (result); } static void * hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct hdac_pcm_devinfo *pdevinfo = data; struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_chan *ch; int i, ord = 0, chid; hdac_lock(sc); chid = (dir == PCMDIR_PLAY)?pdevinfo->play:pdevinfo->rec; ch = &sc->chans[chid]; for (i = 0; i < sc->num_chans && i < chid; i++) { if (ch->dir == sc->chans[i].dir) ord++; } if (dir == PCMDIR_PLAY) { ch->off = (sc->num_iss + ord) << 5; } else { ch->off = ord << 5; } if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { ch->caps.minspeed = ch->caps.maxspeed = 48000; ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; } if (sc->pos_dma.dma_vaddr != NULL) ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + (sc->streamcnt * 8)); else ch->dmapos = NULL; ch->sid = ++sc->streamcnt; ch->dir = dir; ch->b = b; ch->c = c; ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; ch->blkcnt = pdevinfo->chan_blkcnt; hdac_unlock(sc); if (hdac_bdl_alloc(ch) != 0) { ch->blkcnt = 0; return (NULL); } if (sndbuf_alloc(ch->b, sc->chan_dmat, (sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0, pdevinfo->chan_size) != 0) return (NULL); return (ch); } static int hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) { struct hdac_chan *ch = data; int i; for (i = 0; ch->caps.fmtlist[i] != 0; i++) { if (format == ch->caps.fmtlist[i]) { ch->fmt = format; return (0); } } return (EINVAL); } static int hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) { struct hdac_chan *ch = data; uint32_t spd = 0, threshold; int i; for (i = 0; ch->pcmrates[i] != 0; i++) { spd = ch->pcmrates[i]; threshold = spd + ((ch->pcmrates[i + 1] != 0) ? ((ch->pcmrates[i + 1] - spd) >> 1) : 0); if (speed < threshold) break; } if (spd == 0) /* impossible */ ch->spd = 48000; else ch->spd = spd; return (ch->spd); } static void hdac_stream_setup(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; struct hdac_audio_as *as = &ch->devinfo->function.audio.as[ch->as]; struct hdac_widget *w; int i, chn, totalchn, c; nid_t cad = ch->devinfo->codec->cad; uint16_t fmt, dfmt; HDA_BOOTHVERBOSE( device_printf(ch->pdevinfo->dev, "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ch->fmt, ch->spd); ); fmt = 0; if (ch->fmt & AFMT_S16_LE) fmt |= ch->bit16 << 4; else if (ch->fmt & AFMT_S32_LE) fmt |= ch->bit32 << 4; else fmt |= 1 << 4; for (i = 0; i < HDA_RATE_TAB_LEN; i++) { if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { fmt |= hda_rate_tab[i].base; fmt |= hda_rate_tab[i].mul; fmt |= hda_rate_tab[i].div; break; } } if (ch->fmt & (AFMT_STEREO | AFMT_AC3)) { fmt |= 1; totalchn = 2; } else totalchn = 1; HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; if (ch->fmt & AFMT_AC3) dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; chn = 0; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(ch->devinfo, ch->io[i]); if (w == NULL) continue; if (as->hpredir >= 0 && i == as->pincnt) chn = 0; HDA_BOOTHVERBOSE( device_printf(ch->pdevinfo->dev, "PCMDIR_%s: Stream setup nid=%d: " "fmt=0x%04x, dfmt=0x%04x\n", (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ch->io[i], fmt, dfmt); ); hdac_command(sc, HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { hdac_command(sc, HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], dfmt), cad); } /* If HP redirection is enabled, but failed to use same DAC make last DAC one to duplicate first one. */ if (as->hpredir >= 0 && i == as->pincnt) { c = (ch->sid << 4); } else if (chn >= totalchn) { /* This is until OSS will support multichannel. Should be: c = 0; to disable unused DAC */ c = (ch->sid << 4); }else { c = (ch->sid << 4) | chn; } hdac_command(sc, HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], c), cad); chn += HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(w->param.widget_cap) ? 2 : 1; } } static int hdac_channel_setfragments(kobj_t obj, void *data, uint32_t blksz, uint32_t blkcnt) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; blksz &= HDA_BLK_ALIGN; if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; if (blksz < HDA_BLK_MIN) blksz = HDA_BLK_MIN; if (blkcnt > HDA_BDL_MAX) blkcnt = HDA_BDL_MAX; if (blkcnt < HDA_BDL_MIN) blkcnt = HDA_BDL_MIN; while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { if ((blkcnt >> 1) >= HDA_BDL_MIN) blkcnt >>= 1; else if ((blksz >> 1) >= HDA_BLK_MIN) blksz >>= 1; else break; } if ((sndbuf_getblksz(ch->b) != blksz || sndbuf_getblkcnt(ch->b) != blkcnt) && sndbuf_resize(ch->b, blkcnt, blksz) != 0) device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", __func__, blksz, blkcnt); ch->blksz = sndbuf_getblksz(ch->b); ch->blkcnt = sndbuf_getblkcnt(ch->b); return (1); } static int hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct hdac_chan *ch = data; hdac_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); return (ch->blksz); } static void hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) { struct hdac_devinfo *devinfo = ch->devinfo; struct hdac_widget *w; nid_t cad = devinfo->codec->cad; int i; hdac_stream_stop(ch); for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(ch->devinfo, ch->io[i]); if (w == NULL) continue; if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { hdac_command(sc, HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], 0), cad); } hdac_command(sc, HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], 0), cad); } } static void hdac_channel_start(struct hdac_softc *sc, struct hdac_chan *ch) { ch->ptr = 0; ch->prevptr = 0; hdac_stream_stop(ch); hdac_stream_reset(ch); hdac_bdl_setup(ch); hdac_stream_setid(ch); hdac_stream_setup(ch); hdac_stream_start(ch); } static int hdac_channel_trigger(kobj_t obj, void *data, int go) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; if (!PCMTRIG_COMMON(go)) return (0); hdac_lock(sc); switch (go) { case PCMTRIG_START: hdac_channel_start(sc, ch); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: hdac_channel_stop(sc, ch); break; default: break; } hdac_unlock(sc); return (0); } static int hdac_channel_getptr(kobj_t obj, void *data) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ptr; hdac_lock(sc); if (sc->polling != 0) ptr = ch->ptr; else if (ch->dmapos != NULL) ptr = *(ch->dmapos); else ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); hdac_unlock(sc); /* * Round to available space and force 128 bytes aligment. */ ptr %= ch->blksz * ch->blkcnt; ptr &= HDA_BLK_ALIGN; return (ptr); } static struct pcmchan_caps * hdac_channel_getcaps(kobj_t obj, void *data) { return (&((struct hdac_chan *)data)->caps); } static kobj_method_t hdac_channel_methods[] = { KOBJMETHOD(channel_init, hdac_channel_init), KOBJMETHOD(channel_setformat, hdac_channel_setformat), KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), KOBJMETHOD(channel_setfragments, hdac_channel_setfragments), KOBJMETHOD(channel_trigger, hdac_channel_trigger), KOBJMETHOD(channel_getptr, hdac_channel_getptr), KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), { 0, 0 } }; CHANNEL_DECLARE(hdac_channel); static int hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) { struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; uint32_t mask, recmask, id; int i, j, softpcmvol; hdac_lock(sc); /* Make sure that in case of soft volume it won't stay muted. */ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { pdevinfo->left[i] = 100; pdevinfo->right[i] = 100; } mask = 0; recmask = 0; id = hdac_codec_id(devinfo->codec); /* Declate EAPD as ogain control. */ if (pdevinfo->play >= 0) { for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->param.eapdbtl == HDAC_INVALID || w->bindas != sc->chans[pdevinfo->play].as) continue; mask |= SOUND_MASK_OGAIN; break; } } /* Declare volume controls assigned to this association. */ i = 0; ctl = NULL; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if ((pdevinfo->play >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->play].as) || (pdevinfo->rec >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || (ctl->widget->bindas == -2 && pdevinfo->index == 0)) mask |= ctl->ossmask; } /* Declare record sources available to this association. */ if (pdevinfo->rec >= 0) { struct hdac_chan *ch = &sc->chans[pdevinfo->rec]; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(devinfo, ch->io[i]); if (w == NULL || w->enable == 0) continue; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; if (cw->bindas != sc->chans[pdevinfo->rec].as && cw->bindas != -2) continue; recmask |= cw->ossmask; } } } /* Declare soft PCM volume if needed. */ if (pdevinfo->play >= 0 && !pdevinfo->digital) { ctl = NULL; if ((mask & SOUND_MASK_PCM) == 0 || (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { softpcmvol = 1; mask |= SOUND_MASK_PCM; } else { softpcmvol = 0; i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if (ctl->widget->bindas != sc->chans[pdevinfo->play].as && (ctl->widget->bindas != -2 || pdevinfo->index != 0)) continue; if (!(ctl->ossmask & SOUND_MASK_PCM)) continue; if (ctl->step > 0) break; } } if (softpcmvol == 1 || ctl == NULL) { pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); HDA_BOOTVERBOSE( device_printf(pdevinfo->dev, "%s Soft PCM volume\n", (softpcmvol == 1) ? "Forcing" : "Enabling"); ); } } /* Declare master volume if needed. */ if (pdevinfo->play >= 0) { if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == SOUND_MASK_PCM) { mask |= SOUND_MASK_VOLUME; mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM); mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); HDA_BOOTVERBOSE( device_printf(pdevinfo->dev, "Forcing master volume with PCM\n"); ); } } recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; mix_setrecdevs(m, recmask); mix_setdevs(m, mask); hdac_unlock(sc); return (0); } static int hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; uint32_t mute; int lvol, rvol; int i, j; hdac_lock(sc); /* Save new values. */ pdevinfo->left[dev] = left; pdevinfo->right[dev] = right; /* 'ogain' is the special case implemented with EAPD. */ if (dev == SOUND_MIXER_OGAIN) { uint32_t orig; w = NULL; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->param.eapdbtl == HDAC_INVALID) continue; break; } if (i >= devinfo->endnode) { hdac_unlock(sc); return (-1); } orig = w->param.eapdbtl; if (left == 0) w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; else w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; if (orig != w->param.eapdbtl) { uint32_t val; val = w->param.eapdbtl; if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; hdac_command(sc, HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, w->nid, val), devinfo->codec->cad); } hdac_unlock(sc); return (left | (left << 8)); } /* Recalculate all controls related to this OSS device. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || !(ctl->ossmask & (1 << dev))) continue; if (!((pdevinfo->play >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->play].as) || (pdevinfo->rec >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || ctl->widget->bindas == -2)) continue; lvol = 100; rvol = 100; for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { if (ctl->ossmask & (1 << j)) { lvol = lvol * pdevinfo->left[j] / 100; rvol = rvol * pdevinfo->right[j] / 100; } } mute = (left == 0) ? HDA_AMP_MUTE_LEFT : 0; mute |= (right == 0) ? HDA_AMP_MUTE_RIGHT : 0; lvol = (lvol * ctl->step + 50) / 100; rvol = (rvol * ctl->step + 50) / 100; hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); } hdac_unlock(sc); return (left | (right << 8)); } /* * Commutate specified record source. */ static uint32_t hdac_audio_ctl_recsel_comm(struct hdac_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; char buf[64]; int i, muted; uint32_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); for (i = 0; i < w->nconns; i++) { if (w->connsenable[i] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[i]); if (cw == NULL || cw->enable == 0 || cw->bindas == -1) continue; /* Call recursively to trace signal to it's source if needed. */ if ((src & cw->ossmask) != 0) { if (cw->ossdev < 0) { res |= hdac_audio_ctl_recsel_comm(pdevinfo, src, w->conns[i], depth + 1); } else { res |= cw->ossmask; } } /* We have two special cases: mixers and others (selectors). */ if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, i, 1); if (ctl == NULL) continue; /* If we have input control on this node mute them * according to requested sources. */ muted = (src & cw->ossmask) ? 0 : 1; if (muted != ctl->forcemute) { ctl->forcemute = muted; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); } HDA_BOOTHVERBOSE( device_printf(pdevinfo->dev, "Recsel (%s): nid %d source %d %s\n", hdac_audio_ctl_ossmixer_mask2allname( src, buf, sizeof(buf)), nid, i, muted?"mute":"unmute"); ); } else { if (w->nconns == 1) break; if ((src & cw->ossmask) == 0) continue; /* If we found requested source - select it and exit. */ hdac_widget_connection_select(w, i); HDA_BOOTHVERBOSE( device_printf(pdevinfo->dev, "Recsel (%s): nid %d source %d select\n", hdac_audio_ctl_ossmixer_mask2allname( src, buf, sizeof(buf)), nid, i); ); break; } } return (res); } static uint32_t hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) { struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_chan *ch; int i; uint32_t ret = 0xffffffff; hdac_lock(sc); /* Commutate requested recsrc for each ADC. */ ch = &sc->chans[pdevinfo->rec]; for (i = 0; ch->io[i] != -1; i++) { w = hdac_widget_get(devinfo, ch->io[i]); if (w == NULL || w->enable == 0) continue; ret &= hdac_audio_ctl_recsel_comm(pdevinfo, src, ch->io[i], 0); } hdac_unlock(sc); return ((ret == 0xffffffff)? 0 : ret); } static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), { 0, 0 } }; MIXER_DECLARE(hdac_audio_ctl_ossmixer); static void hdac_unsolq_task(void *context, int pending) { struct hdac_softc *sc; sc = (struct hdac_softc *)context; hdac_lock(sc); hdac_unsolq_flush(sc); hdac_unlock(sc); } /**************************************************************************** * int hdac_attach(device_t) * * Attach the device into the kernel. Interrupts usually won't be enabled * when this function is called. Setup everything that doesn't require * interrupts and defer probing of codecs until interrupts are enabled. ****************************************************************************/ static int hdac_attach(device_t dev) { struct hdac_softc *sc; int result; int i; uint16_t vendor; uint8_t v; device_printf(dev, "HDA Driver Revision: %s\n", HDA_DRV_TEST_REV); sc = device_get_softc(dev); sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); sc->dev = dev; sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; vendor = pci_get_vendor(dev); if (sc->pci_subvendor == HP_NX6325_SUBVENDORX) { /* Screw nx6325 - subdevice/subvendor swapped */ sc->pci_subvendor = HP_NX6325_SUBVENDOR; } callout_init(&sc->poll_hda, CALLOUT_MPSAFE); callout_init(&sc->poll_hdac, CALLOUT_MPSAFE); callout_init(&sc->poll_jack, CALLOUT_MPSAFE); TASK_INIT(&sc->unsolq_task, 0, hdac_unsolq_task, sc); sc->poll_ticks = 1000000; sc->poll_ival = HDAC_POLL_INTERVAL; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "polling", &i) == 0 && i != 0) sc->polling = 1; else sc->polling = 0; result = bus_dma_tag_create(NULL, /* parent */ HDAC_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ HDA_BUFSZ_MAX, /* maxsize */ 1, /* nsegments */ HDA_BUFSZ_MAX, /* maxsegsz */ 0, /* flags */ NULL, /* lockfunc */ NULL, /* lockfuncarg */ &sc->chan_dmat); /* dmat */ if (result != 0) { device_printf(dev, "%s: bus_dma_tag_create failed (%x)\n", __func__, result); snd_mtxfree(sc->lock); free(sc, M_DEVBUF); return (ENXIO); } sc->hdabus = NULL; for (i = 0; i < HDAC_CODEC_MAX; i++) sc->codecs[i] = NULL; pci_enable_busmaster(dev); if (vendor == INTEL_VENDORID) { /* TCSEL -> TC0 */ v = pci_read_config(dev, 0x44, 1); pci_write_config(dev, 0x44, v & 0xf8, 1); HDA_BOOTHVERBOSE( device_printf(dev, "TCSEL: 0x%02d -> 0x%02d\n", v, pci_read_config(dev, 0x44, 1)); ); } #ifdef HDAC_MSI_ENABLED if (resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &i) == 0 && i != 0 && pci_msi_count(dev) == 1) sc->flags |= HDAC_F_MSI; else #endif sc->flags &= ~HDAC_F_MSI; #if defined(__i386__) || defined(__amd64__) sc->flags |= HDAC_F_DMA_NOCACHE; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "snoop", &i) == 0 && i != 0) { #else sc->flags &= ~HDAC_F_DMA_NOCACHE; #endif /* * Try to enable PCIe snoop to avoid messing around with * uncacheable DMA attribute. Since PCIe snoop register * config is pretty much vendor specific, there are no * general solutions on how to enable it, forcing us (even * Microsoft) to enable uncacheable or write combined DMA * by default. * * http://msdn2.microsoft.com/en-us/library/ms790324.aspx */ for (i = 0; i < HDAC_PCIESNOOP_LEN; i++) { if (hdac_pcie_snoop[i].vendor != vendor) continue; sc->flags &= ~HDAC_F_DMA_NOCACHE; if (hdac_pcie_snoop[i].reg == 0x00) break; v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); if ((v & hdac_pcie_snoop[i].enable) == hdac_pcie_snoop[i].enable) break; v &= hdac_pcie_snoop[i].mask; v |= hdac_pcie_snoop[i].enable; pci_write_config(dev, hdac_pcie_snoop[i].reg, v, 1); v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); if ((v & hdac_pcie_snoop[i].enable) != hdac_pcie_snoop[i].enable) { HDA_BOOTVERBOSE( device_printf(dev, "WARNING: Failed to enable PCIe " "snoop!\n"); ); #if defined(__i386__) || defined(__amd64__) sc->flags |= HDAC_F_DMA_NOCACHE; #endif } break; } #if defined(__i386__) || defined(__amd64__) } #endif HDA_BOOTHVERBOSE( device_printf(dev, "DMA Coherency: %s / vendor=0x%04x\n", (sc->flags & HDAC_F_DMA_NOCACHE) ? "Uncacheable" : "PCIe snoop", vendor); ); /* Allocate resources */ result = hdac_mem_alloc(sc); if (result != 0) goto hdac_attach_fail; result = hdac_irq_alloc(sc); if (result != 0) goto hdac_attach_fail; /* Get Capabilities */ result = hdac_get_capabilities(sc); if (result != 0) goto hdac_attach_fail; /* Allocate CORB and RIRB dma memory */ result = hdac_dma_alloc(sc, &sc->corb_dma, sc->corb_size * sizeof(uint32_t)); if (result != 0) goto hdac_attach_fail; result = hdac_dma_alloc(sc, &sc->rirb_dma, sc->rirb_size * sizeof(struct hdac_rirb)); if (result != 0) goto hdac_attach_fail; /* Quiesce everything */ HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); hdac_reset(sc, 1); /* Initialize the CORB and RIRB */ hdac_corb_init(sc); hdac_rirb_init(sc); /* Defer remaining of initialization until interrupts are enabled */ sc->intrhook.ich_func = hdac_attach2; sc->intrhook.ich_arg = (void *)sc; if (cold == 0 || config_intrhook_establish(&sc->intrhook) != 0) { sc->intrhook.ich_func = NULL; hdac_attach2((void *)sc); } return (0); hdac_attach_fail: hdac_irq_free(sc); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); hdac_mem_free(sc); snd_mtxfree(sc->lock); free(sc, M_DEVBUF); return (ENXIO); } static void hdac_audio_parse(struct hdac_devinfo *devinfo) { struct hdac_codec *codec = devinfo->codec; struct hdac_softc *sc = codec->sc; struct hdac_widget *w; uint32_t res; int i; nid_t cad, nid; cad = devinfo->codec->cad; nid = devinfo->nid; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); devinfo->function.audio.gpio = res; HDA_BOOTVERBOSE( device_printf(sc->dev, "GPIO: 0x%08x " "NumGPIO=%d NumGPO=%d " "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", devinfo->function.audio.gpio, HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); ); res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), cad); devinfo->function.audio.supp_stream_formats = res; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); devinfo->function.audio.supp_pcm_size_rate = res; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), cad); devinfo->function.audio.outamp_cap = res; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), cad); devinfo->function.audio.inamp_cap = res; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) device_printf(sc->dev, "Ghost widget! nid=%d!\n", i); else { w->devinfo = devinfo; w->nid = i; w->enable = 1; w->selconn = -1; w->pflags = 0; w->ossdev = -1; w->bindas = -1; w->param.eapdbtl = HDAC_INVALID; hdac_widget_parse(w); } } } static void hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_ctl *ctls; struct hdac_widget *w, *cw; int i, j, cnt, max, ocap, icap; int mute, offset, step, size; /* XXX This is redundant */ max = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->param.outamp_cap != 0) max++; if (w->param.inamp_cap != 0) { switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: for (j = 0; j < w->nconns; j++) { cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; max++; } break; default: max++; break; } } } devinfo->function.audio.ctlcnt = max; if (max < 1) return; ctls = (struct hdac_audio_ctl *)malloc( sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); if (ctls == NULL) { /* Blekh! */ device_printf(sc->dev, "unable to allocate ctls!\n"); devinfo->function.audio.ctlcnt = 0; return; } cnt = 0; for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { if (cnt >= max) { device_printf(sc->dev, "%s: Ctl overflow!\n", __func__); break; } w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; ocap = w->param.outamp_cap; icap = w->param.inamp_cap; if (ocap != 0) { mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); /*if (offset > step) { HDA_BOOTVERBOSE( device_printf(sc->dev, "BUGGY outamp: nid=%d " "[offset=%d > step=%d]\n", w->nid, offset, step); ); offset = step; }*/ ctls[cnt].enable = 1; ctls[cnt].widget = w; ctls[cnt].mute = mute; ctls[cnt].step = step; ctls[cnt].size = size; ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin) ctls[cnt].ndir = HDA_CTL_IN; else ctls[cnt].ndir = HDA_CTL_OUT; ctls[cnt++].dir = HDA_CTL_OUT; } if (icap != 0) { mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); /*if (offset > step) { HDA_BOOTVERBOSE( device_printf(sc->dev, "BUGGY inamp: nid=%d " "[offset=%d > step=%d]\n", w->nid, offset, step); ); offset = step; }*/ switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: for (j = 0; j < w->nconns; j++) { if (cnt >= max) { device_printf(sc->dev, "%s: Ctl overflow!\n", __func__); break; } cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) continue; ctls[cnt].enable = 1; ctls[cnt].widget = w; ctls[cnt].childwidget = cw; ctls[cnt].index = j; ctls[cnt].mute = mute; ctls[cnt].step = step; ctls[cnt].size = size; ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; ctls[cnt].ndir = HDA_CTL_IN; ctls[cnt++].dir = HDA_CTL_IN; } break; default: if (cnt >= max) { device_printf(sc->dev, "%s: Ctl overflow!\n", __func__); break; } ctls[cnt].enable = 1; ctls[cnt].widget = w; ctls[cnt].mute = mute; ctls[cnt].step = step; ctls[cnt].size = size; ctls[cnt].offset = offset; ctls[cnt].left = offset; ctls[cnt].right = offset; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) ctls[cnt].ndir = HDA_CTL_OUT; else ctls[cnt].ndir = HDA_CTL_IN; ctls[cnt++].dir = HDA_CTL_IN; break; } } } devinfo->function.audio.ctl = ctls; } static void hdac_audio_as_parse(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as; struct hdac_widget *w; int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; /* Count present associations */ max = 0; for (j = 1; j < 16; j++) { for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) != j) continue; max++; if (j != 15) /* There could be many 1-pin assocs #15 */ break; } } devinfo->function.audio.ascnt = max; if (max < 1) return; as = (struct hdac_audio_as *)malloc( sizeof(*as) * max, M_HDAC, M_ZERO | M_NOWAIT); if (as == NULL) { /* Blekh! */ device_printf(sc->dev, "unable to allocate assocs!\n"); devinfo->function.audio.ascnt = 0; return; } for (i = 0; i < max; i++) { as[i].hpredir = -1; as[i].chan = -1; as[i].digital = 1; } /* Scan associations skipping as=0. */ cnt = 0; for (j = 1; j < 16; j++) { first = 16; hpredir = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); if (assoc != j) { continue; } KASSERT(cnt < max, ("%s: Associations owerflow (%d of %d)", __func__, cnt, max)); type = w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; /* Get pin direction. */ if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) dir = HDA_CTL_OUT; else dir = HDA_CTL_IN; /* If this is a first pin - create new association. */ if (as[cnt].pincnt == 0) { as[cnt].enable = 1; as[cnt].index = j; as[cnt].dir = dir; } if (seq < first) first = seq; /* Check association correctness. */ if (as[cnt].pins[seq] != 0) { device_printf(sc->dev, "%s: Duplicate pin %d (%d) " "in association %d! Disabling association.\n", __func__, seq, w->nid, j); as[cnt].enable = 0; } if (dir != as[cnt].dir) { device_printf(sc->dev, "%s: Pin %d has wrong " "direction for association %d! Disabling " "association.\n", __func__, w->nid, j); as[cnt].enable = 0; } if (!HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) as[cnt].digital = 0; /* Headphones with seq=15 may mean redirection. */ if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && seq == 15) hpredir = 1; as[cnt].pins[seq] = w->nid; as[cnt].pincnt++; /* Association 15 is a multiple unassociated pins. */ if (j == 15) cnt++; } if (j != 15 && as[cnt].pincnt > 0) { if (hpredir && as[cnt].pincnt > 1) as[cnt].hpredir = first; cnt++; } } HDA_BOOTVERBOSE( device_printf(sc->dev, "%d associations found:\n", max); for (i = 0; i < max; i++) { device_printf(sc->dev, "Association %d (%d) %s%s:\n", i, as[i].index, (as[i].dir == HDA_CTL_IN)?"in":"out", as[i].enable?"":" (disabled)"); for (j = 0; j < 16; j++) { if (as[i].pins[j] == 0) continue; device_printf(sc->dev, " Pin nid=%d seq=%d\n", as[i].pins[j], j); } } ); devinfo->function.audio.as = as; } static const struct { uint32_t model; uint32_t id; uint32_t set, unset; } hdac_quirks[] = { /* * XXX Force stereo quirk. Monoural recording / playback * on few codecs (especially ALC880) seems broken or * perhaps unsupported. */ { HDA_MATCH_ALL, HDA_MATCH_ALL, HDA_QUIRK_FORCESTEREO | HDA_QUIRK_IVREF, 0 }, { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, HDA_QUIRK_GPIO0, 0 }, { ASUS_G2K_SUBVENDOR, HDA_CODEC_ALC660, HDA_QUIRK_GPIO0, 0 }, { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO0, 0 }, { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO0, 0 }, { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, HDA_QUIRK_GPIO0, 0 }, { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, HDA_QUIRK_GPIO0, 0 }, { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { ASUS_A8X_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, HDA_QUIRK_OVREF, 0 }, { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, HDA_QUIRK_OVREF, 0 }, /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 },*/ { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO1, 0 }, { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV | HDA_QUIRK_SENSEINV, 0 }, { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, HDA_QUIRK_EAPDINV, 0 }, { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, HDA_QUIRK_GPIO0 | HDA_QUIRK_OVREF50, 0}, { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205X, HDA_QUIRK_GPIO0, 0 }, { DELL_V1400_SUBVENDOR, HDA_CODEC_STAC9228X, HDA_QUIRK_GPIO2, 0 }, { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205X, HDA_QUIRK_GPIO0, 0 }, { HDA_MATCH_ALL, HDA_CODEC_AD1988, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, { HDA_MATCH_ALL, HDA_CODEC_AD1988B, HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, - { HDA_MATCH_ALL, HDA_CODEC_CXVENICE, + { HDA_MATCH_ALL, HDA_CODEC_CX20549, 0, HDA_QUIRK_FORCESTEREO } }; #define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) static void hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) { struct hdac_widget *w; uint32_t id, subvendor; int i; id = hdac_codec_id(devinfo->codec); subvendor = devinfo->codec->sc->pci_subvendor; /* * Quirks */ for (i = 0; i < HDAC_QUIRKS_LEN; i++) { if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subvendor) && HDA_DEV_MATCH(hdac_quirks[i].id, id))) continue; if (hdac_quirks[i].set != 0) devinfo->function.audio.quirks |= hdac_quirks[i].set; if (hdac_quirks[i].unset != 0) devinfo->function.audio.quirks &= ~(hdac_quirks[i].unset); } switch (id) { case HDA_CODEC_ALC883: /* * nid: 24/25 = External (jack) or Internal (fixed) Mic. * Clear vref cap for jack connectivity. */ w = hdac_widget_get(devinfo, 24); if (w != NULL && w->enable != 0 && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) w->wclass.pin.cap &= ~( HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK | HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK | HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK); w = hdac_widget_get(devinfo, 25); if (w != NULL && w->enable != 0 && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) w->wclass.pin.cap &= ~( HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK | HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK | HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK); /* * nid: 26 = Line-in, leave it alone. */ break; case HDA_CODEC_AD1986A: if (subvendor == ASUS_A8X_SUBVENDOR) { /* * This is just plain ridiculous.. There * are several A8 series that share the same * pci id but works differently (EAPD). */ w = hdac_widget_get(devinfo, 26); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) != HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) devinfo->function.audio.quirks &= ~HDA_QUIRK_EAPDINV; } break; case HDA_CODEC_AD1981HD: /* * This codec has very unusual design with several * points inappropriate for the present parser. */ /* Disable recording from mono playback mix. */ w = hdac_widget_get(devinfo, 21); if (w != NULL) w->connsenable[3] = 0; /* Disable rear to front mic mixer, use separately. */ w = hdac_widget_get(devinfo, 31); if (w != NULL) w->enable = 0; /* Disable playback mixer, use direct bypass. */ w = hdac_widget_get(devinfo, 14); if (w != NULL) w->enable = 0; break; } } /* * Trace path from DAC to pin. */ static nid_t hdac_audio_trace_dac(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, int dupseq, int min, int only, int depth) { struct hdac_widget *w; int i, im = -1; nid_t m = 0, ret; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*stracing via nid %d\n", depth + 1, "", w->nid); } ); /* Use only unused widgets */ if (w->bindas >= 0 && w->bindas != as) { HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*snid %d busy by association %d\n", depth + 1, "", w->nid, w->bindas); } ); return (0); } if (dupseq < 0) { if (w->bindseqmask != 0) { HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*snid %d busy by seqmask %x\n", depth + 1, "", w->nid, w->bindseqmask); } ); return (0); } } else { /* If this is headphones - allow duplicate first pin. */ if (w->bindseqmask != 0 && (w->bindseqmask & (1 << dupseq)) == 0) { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d busy by seqmask %x\n", depth + 1, "", w->nid, w->bindseqmask); ); return (0); } } switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: /* Do not traverse input. AD1988 has digital monitor for which we are not ready. */ break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: /* If we are tracing HP take only dac of first pin. */ if ((only == 0 || only == w->nid) && (w->nid >= min) && (dupseq < 0 || w->nid == devinfo->function.audio.as[as].dacs[dupseq])) m = w->nid; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (depth > 0) break; /* Fall */ default: /* Find reachable DACs with smallest nid respecting constraints. */ for (i = 0; i < w->nconns; i++) { if (w->connsenable[i] == 0) continue; if (w->selconn != -1 && w->selconn != i) continue; if ((ret = hdac_audio_trace_dac(devinfo, as, seq, w->conns[i], dupseq, min, only, depth + 1)) != 0) { if (m == 0 || ret < m) { m = ret; im = i; } if (only || dupseq >= 0) break; } } if (m && only && ((w->nconns > 1 && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) w->selconn = im; break; } if (m && only) { w->bindas = as; w->bindseqmask |= (1 << seq); } HDA_BOOTHVERBOSE( if (!only) { device_printf(devinfo->codec->sc->dev, " %*snid %d returned %d\n", depth + 1, "", w->nid, m); } ); return (m); } /* * Trace path from widget to ADC. */ static nid_t hdac_audio_trace_adc(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, int only, int depth) { struct hdac_widget *w, *wc; int i, j; nid_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*stracing via nid %d\n", depth + 1, "", w->nid); ); /* Use only unused widgets */ if (w->bindas >= 0 && w->bindas != as) { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d busy by association %d\n", depth + 1, "", w->nid, w->bindas); ); return (0); } switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: /* If we are tracing HP take only dac of first pin. */ if (only == w->nid) res = 1; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (depth > 0) break; /* Fall */ default: /* Try to find reachable ADCs with specified nid. */ for (j = devinfo->startnode; j < devinfo->endnode; j++) { wc = hdac_widget_get(devinfo, j); if (wc == NULL || wc->enable == 0) continue; for (i = 0; i < wc->nconns; i++) { if (wc->connsenable[i] == 0) continue; if (wc->conns[i] != nid) continue; if (hdac_audio_trace_adc(devinfo, as, seq, j, only, depth + 1) != 0) { res = 1; if (((wc->nconns > 1 && wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) && wc->selconn == -1) wc->selconn = i; } } } break; } if (res) { w->bindas = as; w->bindseqmask |= (1 << seq); } HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d returned %d\n", depth + 1, "", w->nid, res); ); return (res); } /* * Erase trace path of the specified association. */ static void hdac_audio_undo_trace(struct hdac_devinfo *devinfo, int as, int seq) { struct hdac_widget *w; int i; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->bindas == as) { if (seq >= 0) { w->bindseqmask &= ~(1 << seq); if (w->bindseqmask == 0) { w->bindas = -1; w->selconn = -1; } } else { w->bindas = -1; w->bindseqmask = 0; w->selconn = -1; } } } } /* * Trace association path from DAC to output */ static int hdac_audio_trace_as_out(struct hdac_devinfo *devinfo, int as, int seq) { struct hdac_audio_as *ases = devinfo->function.audio.as; int i, hpredir; nid_t min, res; /* Find next pin */ for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) ; /* Check if there is no any left. If so - we succeded. */ if (i == 16) return (1); hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; min = 0; res = 0; do { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing pin %d with min nid %d", ases[as].pins[i], min); if (hpredir >= 0) printf(" and hpredir %d", hpredir); printf("\n"); ); /* Trace this pin taking min nid into account. */ res = hdac_audio_trace_dac(devinfo, as, i, ases[as].pins[i], hpredir, min, 0, 0); if (res == 0) { /* If we failed - return to previous and redo it. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Unable to trace pin %d seq %d with min " "nid %d", ases[as].pins[i], i, min); if (hpredir >= 0) printf(" and hpredir %d", hpredir); printf("\n"); ); return (0); } HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Pin %d traced to DAC %d", ases[as].pins[i], res); if (hpredir >= 0) printf(" and hpredir %d", hpredir); if (ases[as].fakeredir) printf(" with fake redirection"); printf("\n"); ); /* Trace again to mark the path */ hdac_audio_trace_dac(devinfo, as, i, ases[as].pins[i], hpredir, min, res, 0); ases[as].dacs[i] = res; /* We succeded, so call next. */ if (hdac_audio_trace_as_out(devinfo, as, i + 1)) return (1); /* If next failed, we should retry with next min */ hdac_audio_undo_trace(devinfo, as, i); ases[as].dacs[i] = 0; min = res + 1; } while (1); } /* * Trace association path from input to ADC */ static int hdac_audio_trace_as_in(struct hdac_devinfo *devinfo, int as) { struct hdac_audio_as *ases = devinfo->function.audio.as; struct hdac_widget *w; int i, j, k; for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) continue; if (w->bindas >= 0 && w->bindas != as) continue; /* Find next pin */ for (i = 0; i < 16; i++) { if (ases[as].pins[i] == 0) continue; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing pin %d to ADC %d\n", ases[as].pins[i], j); ); /* Trace this pin taking goal into account. */ if (hdac_audio_trace_adc(devinfo, as, i, ases[as].pins[i], j, 0) == 0) { /* If we failed - return to previous and redo it. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Unable to trace pin %d to ADC %d, undo traces\n", ases[as].pins[i], j); ); hdac_audio_undo_trace(devinfo, as, -1); for (k = 0; k < 16; k++) ases[as].dacs[k] = 0; break; } HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Pin %d traced to ADC %d\n", ases[as].pins[i], j); ); ases[as].dacs[i] = j; } if (i == 16) return (1); } return (0); } /* * Trace input monitor path from mixer to output association. */ static int hdac_audio_trace_to_out(struct hdac_devinfo *devinfo, nid_t nid, int depth) { struct hdac_audio_as *ases = devinfo->function.audio.as; struct hdac_widget *w, *wc; int i, j; nid_t res = 0; if (depth > HDA_PARSE_MAXDEPTH) return (0); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (0); HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*stracing via nid %d\n", depth + 1, "", w->nid); ); /* Use only unused widgets */ if (depth > 0 && w->bindas != -1) { if (w->bindas < 0 || ases[w->bindas].dir == HDA_CTL_OUT) { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d found output association %d\n", depth + 1, "", w->nid, w->bindas); ); return (1); } else { HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d busy by input association %d\n", depth + 1, "", w->nid, w->bindas); ); return (0); } } switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: /* Do not traverse input. AD1988 has digital monitor for which we are not ready. */ break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (depth > 0) break; /* Fall */ default: /* Try to find reachable ADCs with specified nid. */ for (j = devinfo->startnode; j < devinfo->endnode; j++) { wc = hdac_widget_get(devinfo, j); if (wc == NULL || wc->enable == 0) continue; for (i = 0; i < wc->nconns; i++) { if (wc->connsenable[i] == 0) continue; if (wc->conns[i] != nid) continue; if (hdac_audio_trace_to_out(devinfo, j, depth + 1) != 0) { res = 1; if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && wc->selconn == -1) wc->selconn = i; } } } break; } if (res) w->bindas = -2; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " %*snid %d returned %d\n", depth + 1, "", w->nid, res); ); return (res); } /* * Trace extra associations (beeper, monitor) */ static void hdac_audio_trace_as_extra(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; int j; /* Input monitor */ /* Find mixer associated with input, but supplying signal for output associations. Hope it will be input monitor. */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing input monitor\n"); ); for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) continue; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing nid %d to out\n", j); ); if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " nid %d is input monitor\n", w->nid); ); w->pflags |= HDA_ADC_MONITOR; w->ossdev = SOUND_MIXER_IMIX; } } /* Beeper */ HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing beeper\n"); ); for (j = devinfo->startnode; j < devinfo->endnode; j++) { w = hdac_widget_get(devinfo, j); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) continue; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Tracing nid %d to out\n", j); ); if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, " nid %d traced to out\n", j); ); } w->bindas = -2; } } /* * Bind assotiations to PCM channels */ static void hdac_audio_bind_as(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as = devinfo->function.audio.as; int j, cnt = 0, free; for (j = 0; j < devinfo->function.audio.ascnt; j++) { if (as[j].enable) cnt++; } if (sc->num_chans == 0) { sc->chans = (struct hdac_chan *)malloc( sizeof(struct hdac_chan) * cnt, M_HDAC, M_ZERO | M_NOWAIT); if (sc->chans == NULL) { device_printf(devinfo->codec->sc->dev, "Channels memory allocation failed!\n"); return; } } else { sc->chans = (struct hdac_chan *)realloc(sc->chans, sizeof(struct hdac_chan) * (sc->num_chans + cnt), M_HDAC, M_ZERO | M_NOWAIT); if (sc->chans == NULL) { sc->num_chans = 0; device_printf(devinfo->codec->sc->dev, "Channels memory allocation failed!\n"); return; } } free = sc->num_chans; sc->num_chans += cnt; for (j = free; j < free + cnt; j++) { devinfo->codec->sc->chans[j].devinfo = devinfo; devinfo->codec->sc->chans[j].as = -1; } /* Assign associations in order of their numbers, */ for (j = 0; j < devinfo->function.audio.ascnt; j++) { if (as[j].enable == 0) continue; as[j].chan = free; devinfo->codec->sc->chans[free].as = j; devinfo->codec->sc->chans[free].dir = (as[j].dir == HDA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY; hdac_pcmchannel_setup(&devinfo->codec->sc->chans[free]); free++; } } static void hdac_audio_disable_nonaudio(struct hdac_devinfo *devinfo) { struct hdac_widget *w; int i; /* Disable power and volume widgets. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d due to it's" " non-audio type.\n", w->nid); ); } } } static void hdac_audio_disable_useless(struct hdac_devinfo *devinfo) { struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; int done, found, i, j, k; /* Disable useless pins. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling pin nid %d due" " to None connectivity.\n", w->nid); ); } else if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling unassociated" " pin nid %d.\n", w->nid); ); } } } do { done = 1; /* Disable and mute controls for disabled widgets. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) continue; if (ctl->widget->enable == 0 || (ctl->childwidget != NULL && ctl->childwidget->enable == 0)) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; if (ctl->ndir == HDA_CTL_IN) ctl->widget->connsenable[ctl->index] = 0; done = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling ctl %d nid %d cnid %d due" " to disabled widget.\n", i, ctl->widget->nid, (ctl->childwidget != NULL)? ctl->childwidget->nid:-1); ); } } /* Disable useless widgets. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; /* Disable inputs with disabled child widgets. */ for (j = 0; j < w->nconns; j++) { if (w->connsenable[j]) { cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || cw->enable == 0) { w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d connection %d due" " to disabled child widget.\n", i, j); ); } } } if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; /* Disable mixers and selectors without inputs. */ found = 0; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j]) { found = 1; break; } } if (found == 0) { w->enable = 0; done = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d due to all it's" " inputs disabled.\n", w->nid); ); } /* Disable nodes without consumers. */ if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; found = 0; for (k = devinfo->startnode; k < devinfo->endnode; k++) { cw = hdac_widget_get(devinfo, k); if (cw == NULL || cw->enable == 0) continue; for (j = 0; j < cw->nconns; j++) { if (cw->connsenable[j] && cw->conns[j] == i) { found = 1; break; } } } if (found == 0) { w->enable = 0; done = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling nid %d due to all it's" " consumers disabled.\n", w->nid); ); } } } while (done == 0); } static void hdac_audio_disable_unas(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; int i, j, k; /* Disable unassosiated widgets. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->bindas == -1) { w->enable = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling unassociated nid %d.\n", w->nid); ); } } /* Disable input connections on input pin and * output on output. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (w->bindas < 0) continue; if (as[w->bindas].dir == HDA_CTL_IN) { for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling connection to input pin " "nid %d conn %d.\n", i, j); ); } ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, -1, 1); if (ctl && ctl->enable) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; } } else { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); if (ctl && ctl->enable) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; } for (k = devinfo->startnode; k < devinfo->endnode; k++) { cw = hdac_widget_get(devinfo, k); if (cw == NULL || cw->enable == 0) continue; for (j = 0; j < cw->nconns; j++) { if (cw->connsenable[j] && cw->conns[j] == i) { cw->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling connection from output pin " "nid %d conn %d cnid %d.\n", k, j, i); ); if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && cw->nconns > 1) continue; ctl = hdac_audio_ctl_amp_get(devinfo, k, HDA_CTL_IN, j, 1); if (ctl && ctl->enable) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; } } } } } } } static void hdac_audio_disable_notselected(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; int i, j; /* On playback path we can safely disable all unseleted inputs. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->nconns <= 1) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; if (w->bindas < 0 || as[w->bindas].dir == HDA_CTL_IN) continue; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; if (w->selconn < 0 || w->selconn == j) continue; w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling unselected connection " "nid %d conn %d.\n", i, j); ); } } } static void hdac_audio_disable_crossas(struct hdac_devinfo *devinfo) { struct hdac_widget *w, *cw; struct hdac_audio_ctl *ctl; int i, j; /* Disable crossassociatement connections. */ /* ... using selectors */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->nconns <= 1) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) continue; if (w->bindas == -2) continue; for (j = 0; j < w->nconns; j++) { if (w->connsenable[j] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[j]); if (cw == NULL || w->enable == 0) continue; if (w->bindas == cw->bindas || cw->bindas == -2) continue; w->connsenable[j] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling crossassociatement connection " "nid %d conn %d cnid %d.\n", i, j, cw->nid); ); } } /* ... using controls */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || ctl->childwidget == NULL) continue; if (ctl->widget->bindas == -2 || ctl->childwidget->bindas == -2) continue; if (ctl->widget->bindas != ctl->childwidget->bindas) { ctl->forcemute = 1; ctl->muted = HDA_AMP_MUTE_ALL; ctl->left = 0; ctl->right = 0; ctl->enable = 0; if (ctl->ndir == HDA_CTL_IN) ctl->widget->connsenable[ctl->index] = 0; HDA_BOOTHVERBOSE( device_printf(devinfo->codec->sc->dev, " Disabling crossassociatement connection " "ctl %d nid %d cnid %d.\n", i, ctl->widget->nid, ctl->childwidget->nid); ); } } } #define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) /* * Find controls to control amplification for source. */ static int hdac_audio_ctl_source_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, int ossdev, int ctlable, int depth, int need) { struct hdac_widget *w, *wc; struct hdac_audio_ctl *ctl; int i, j, conns = 0, rneed; if (depth > HDA_PARSE_MAXDEPTH) return (need); w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return (need); /* Count number of active inputs. */ if (depth > 0) { for (j = 0; j < w->nconns; j++) { if (w->connsenable[j]) conns++; } } /* If this is not a first step - use input mixer. Pins have common input ctl so care must be taken. */ if (depth > 0 && ctlable && (conns == 1 || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, index, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & need) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); need &= ~HDA_CTL_GIVE(ctl); } } /* If widget has own ossdev - not traverse it. It will be traversed on it's own. */ if (w->ossdev >= 0 && depth > 0) return (need); /* We must not traverse pin */ if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && depth > 0) return (need); /* record that this widget exports such signal, */ w->ossmask |= (1 << ossdev); /* If signals mixed, we can't assign controls farther. * Ignore this on depth zero. Caller must knows why. * Ignore this for static selectors if this input selected. */ if (conns > 1) ctlable = 0; if (ctlable) { ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & need) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); need &= ~HDA_CTL_GIVE(ctl); } } rneed = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { wc = hdac_widget_get(devinfo, i); if (wc == NULL || wc->enable == 0) continue; for (j = 0; j < wc->nconns; j++) { if (wc->connsenable[j] && wc->conns[j] == nid) { rneed |= hdac_audio_ctl_source_amp(devinfo, wc->nid, j, ossdev, ctlable, depth + 1, need); } } } rneed &= need; return (rneed); } /* * Find controls to control amplification for destination. */ static void hdac_audio_ctl_dest_amp(struct hdac_devinfo *devinfo, nid_t nid, int ossdev, int depth, int need) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w, *wc; struct hdac_audio_ctl *ctl; int i, j, consumers; if (depth > HDA_PARSE_MAXDEPTH) return; w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return; if (depth > 0) { /* If this node produce output for several consumers, we can't touch it. */ consumers = 0; for (i = devinfo->startnode; i < devinfo->endnode; i++) { wc = hdac_widget_get(devinfo, i); if (wc == NULL || wc->enable == 0) continue; for (j = 0; j < wc->nconns; j++) { if (wc->connsenable[j] && wc->conns[j] == nid) consumers++; } } /* The only exception is if real HP redirection is configured and this is a duplication point. XXX: Actually exception is not completely correct. XXX: Duplication point check is not perfect. */ if ((consumers == 2 && (w->bindas < 0 || as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || (w->bindseqmask & (1 << 15)) == 0)) || consumers > 2) return; /* Else use it's output mixer. */ ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & need) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); need &= ~HDA_CTL_GIVE(ctl); } } /* We must not traverse pin */ if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && depth > 0) return; for (i = 0; i < w->nconns; i++) { int tneed = need; if (w->connsenable[i] == 0) continue; ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, i, 1); if (ctl) { if (HDA_CTL_GIVE(ctl) & tneed) ctl->ossmask |= (1 << ossdev); else ctl->possmask |= (1 << ossdev); tneed &= ~HDA_CTL_GIVE(ctl); } hdac_audio_ctl_dest_amp(devinfo, w->conns[i], ossdev, depth + 1, tneed); } } /* * Assign OSS names to sound sources */ static void hdac_audio_assign_names(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; int i, j; int type = -1, use, used = 0; static const int types[7][13] = { { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, SOUND_MIXER_LINE3, -1 }, /* line */ { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ { SOUND_MIXER_CD, -1 }, /* cd */ { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, -1 }, /* digital */ { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, -1 } /* others */ }; /* Surely known names */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->bindas == -1) continue; use = -1; switch (w->type) { case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: if (as[w->bindas].dir == HDA_CTL_OUT) break; type = -1; switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: type = 0; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) break; type = 1; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: type = 3; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: type = 4; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: type = 5; break; } if (type == -1) break; j = 0; while (types[type][j] >= 0 && (used & (1 << types[type][j])) != 0) { j++; } if (types[type][j] >= 0) use = types[type][j]; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: use = SOUND_MIXER_PCM; break; case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: use = SOUND_MIXER_SPEAKER; break; default: break; } if (use >= 0) { w->ossdev = use; used |= (1 << use); } } /* Semi-known names */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->ossdev >= 0) continue; if (w->bindas == -1) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (as[w->bindas].dir == HDA_CTL_OUT) continue; type = -1; switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: type = 0; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: type = 2; break; case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: type = 5; break; } if (type == -1) break; j = 0; while (types[type][j] >= 0 && (used & (1 << types[type][j])) != 0) { j++; } if (types[type][j] >= 0) { w->ossdev = types[type][j]; used |= (1 << types[type][j]); } } /* Others */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->ossdev >= 0) continue; if (w->bindas == -1) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (as[w->bindas].dir == HDA_CTL_OUT) continue; j = 0; while (types[6][j] >= 0 && (used & (1 << types[6][j])) != 0) { j++; } if (types[6][j] >= 0) { w->ossdev = types[6][j]; used |= (1 << types[6][j]); } } } static void hdac_audio_build_tree(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; int j, res; /* Trace all associations in order of their numbers, */ for (j = 0; j < devinfo->function.audio.ascnt; j++) { if (as[j].enable == 0) continue; HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Tracing association %d (%d)\n", j, as[j].index); ); if (as[j].dir == HDA_CTL_OUT) { retry: res = hdac_audio_trace_as_out(devinfo, j, 0); if (res == 0 && as[j].hpredir >= 0 && as[j].fakeredir == 0) { /* If codec can't do analog HP redirection try to make it using one more DAC. */ as[j].fakeredir = 1; goto retry; } } else { res = hdac_audio_trace_as_in(devinfo, j); } if (res) { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Association %d (%d) trace succeded\n", j, as[j].index); ); } else { HDA_BOOTVERBOSE( device_printf(devinfo->codec->sc->dev, "Association %d (%d) trace failed\n", j, as[j].index); ); as[j].enable = 0; } } /* Trace mixer and beeper pseudo associations. */ hdac_audio_trace_as_extra(devinfo); } static void hdac_audio_assign_mixers(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_audio_ctl *ctl; struct hdac_widget *w; int i; /* Assign mixers to the tree. */ for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && as[w->bindas].dir == HDA_CTL_IN)) { if (w->ossdev < 0) continue; hdac_audio_ctl_source_amp(devinfo, w->nid, -1, w->ossdev, 1, 0, 1); } else if ((w->pflags & HDA_ADC_MONITOR) != 0) { if (w->ossdev < 0) continue; if (hdac_audio_ctl_source_amp(devinfo, w->nid, -1, w->ossdev, 1, 0, 1)) { /* If we are unable to control input monitor as source - try to control it as destination. */ hdac_audio_ctl_dest_amp(devinfo, w->nid, w->ossdev, 0, 1); } } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { hdac_audio_ctl_dest_amp(devinfo, w->nid, SOUND_MIXER_RECLEV, 0, 1); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && as[w->bindas].dir == HDA_CTL_OUT) { hdac_audio_ctl_dest_amp(devinfo, w->nid, SOUND_MIXER_VOLUME, 0, 1); } } /* Treat unrequired as possible. */ i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->ossmask == 0) ctl->ossmask = ctl->possmask; } } static void hdac_audio_prepare_pin_ctrl(struct hdac_devinfo *devinfo) { struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; uint32_t pincap; int i; for (i = 0; i < devinfo->nodecnt; i++) { w = &devinfo->widget[i]; if (w == NULL) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; pincap = w->wclass.pin.cap; /* Disable everything. */ w->wclass.pin.ctrl &= ~( HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); if (w->enable == 0 || w->bindas < 0 || as[w->bindas].enable == 0) { /* Pin is unused so left it disabled. */ continue; } else if (as[w->bindas].dir == HDA_CTL_IN) { /* Input pin, configure for input. */ if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); } else { /* Output pin, configure for output. */ if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) && (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); } } } static void hdac_audio_commit(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; nid_t cad; uint32_t gdata, gmask, gdir; int commitgpio, numgpio; int i; cad = devinfo->codec->cad; if (sc->pci_subvendor == APPLE_INTEL_MAC) hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, 0x7e7, 0), cad); gdata = 0; gmask = 0; gdir = 0; commitgpio = 0; numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( devinfo->function.audio.gpio); if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) commitgpio = (numgpio > 0) ? 1 : 0; else { for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { if (!(devinfo->function.audio.quirks & (1 << i))) continue; if (commitgpio == 0) { commitgpio = 1; HDA_BOOTVERBOSE( gdata = hdac_command(sc, HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); gmask = hdac_command(sc, HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); gdir = hdac_command(sc, HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); device_printf(sc->dev, "GPIO init: data=0x%08x " "mask=0x%08x dir=0x%08x\n", gdata, gmask, gdir); gdata = 0; gmask = 0; gdir = 0; ); } gdata |= 1 << i; gmask |= 1 << i; gdir |= 1 << i; } } if (commitgpio != 0) { HDA_BOOTVERBOSE( device_printf(sc->dev, "GPIO commit: data=0x%08x mask=0x%08x " "dir=0x%08x\n", gdata, gmask, gdir); ); hdac_command(sc, HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, gmask), cad); hdac_command(sc, HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, gdir), cad); hdac_command(sc, HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, gdata), cad); } for (i = 0; i < devinfo->nodecnt; i++) { w = &devinfo->widget[i]; if (w == NULL) continue; if (w->selconn == -1) w->selconn = 0; if (w->nconns > 0) hdac_widget_connection_select(w, w->selconn); if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } if (w->param.eapdbtl != HDAC_INVALID) { uint32_t val; val = w->param.eapdbtl; if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; hdac_command(sc, HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, val), cad); } } } static void hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) { struct hdac_audio_ctl *ctl; int i, z; i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0) { /* Mute disabled controls. */ hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_ALL, 0, 0); continue; } /* Init controls to 0dB amplification. */ z = ctl->offset; if (z > ctl->step) z = ctl->step; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_NONE, z, z); } } static void hdac_powerup(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; nid_t cad = devinfo->codec->cad; int i; hdac_command(sc, HDA_CMD_SET_POWER_STATE(cad, devinfo->nid, HDA_CMD_POWER_STATE_D0), cad); DELAY(100); for (i = devinfo->startnode; i < devinfo->endnode; i++) { hdac_command(sc, HDA_CMD_SET_POWER_STATE(cad, i, HDA_CMD_POWER_STATE_D0), cad); } DELAY(1000); } static int hdac_pcmchannel_setup(struct hdac_chan *ch) { struct hdac_devinfo *devinfo = ch->devinfo; struct hdac_audio_as *as = devinfo->function.audio.as; struct hdac_widget *w; uint32_t cap, fmtcap, pcmcap; int i, j, ret, max; ch->caps = hdac_caps; ch->caps.fmtlist = ch->fmtlist; ch->bit16 = 1; ch->bit32 = 0; ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; ret = 0; fmtcap = devinfo->function.audio.supp_stream_formats; pcmcap = devinfo->function.audio.supp_pcm_size_rate; max = (sizeof(ch->io) / sizeof(ch->io[0])) - 1; for (i = 0; i < 16 && ret < max; i++) { /* Check as is correct */ if (ch->as < 0) break; /* Cound only present DACs */ if (as[ch->as].dacs[i] <= 0) continue; /* Ignore duplicates */ for (j = 0; j < ret; j++) { if (ch->io[j] == as[ch->as].dacs[i]) break; } if (j < ret) continue; w = hdac_widget_get(devinfo, as[ch->as].dacs[i]); if (w == NULL || w->enable == 0) continue; if (!HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(w->param.widget_cap)) continue; cap = w->param.supp_stream_formats; /*if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) { }*/ if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) continue; /* Many codec does not declare AC3 support on SPDIF. I don't beleave that they doesn't support it! */ if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; if (ret == 0) { fmtcap = cap; pcmcap = w->param.supp_pcm_size_rate; } else { fmtcap &= cap; pcmcap &= w->param.supp_pcm_size_rate; } ch->io[ret++] = as[ch->as].dacs[i]; } ch->io[ret] = -1; ch->supp_stream_formats = fmtcap; ch->supp_pcm_size_rate = pcmcap; /* * 8bit = 0 * 16bit = 1 * 20bit = 2 * 24bit = 3 * 32bit = 4 */ if (ret > 0) { i = 0; if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) ch->bit16 = 1; else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) ch->bit16 = 0; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) ch->bit32 = 4; else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) ch->bit32 = 3; else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) ch->bit32 = 2; if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) ch->fmtlist[i++] = AFMT_S16_LE; ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; if (ch->bit32 > 0) { if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) ch->fmtlist[i++] = AFMT_S32_LE; ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; } } if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { ch->fmtlist[i++] = AFMT_AC3; } ch->fmtlist[i] = 0; i = 0; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) ch->pcmrates[i++] = 8000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) ch->pcmrates[i++] = 11025; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) ch->pcmrates[i++] = 16000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) ch->pcmrates[i++] = 22050; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) ch->pcmrates[i++] = 32000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) ch->pcmrates[i++] = 44100; /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ ch->pcmrates[i++] = 48000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) ch->pcmrates[i++] = 88200; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) ch->pcmrates[i++] = 96000; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) ch->pcmrates[i++] = 176400; if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) ch->pcmrates[i++] = 192000; /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ ch->pcmrates[i] = 0; if (i > 0) { ch->caps.minspeed = ch->pcmrates[0]; ch->caps.maxspeed = ch->pcmrates[i - 1]; } } return (ret); } static void hdac_create_pcms(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_audio_as *as = devinfo->function.audio.as; int i, j, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].enable == 0) continue; if (as[i].dir == HDA_CTL_IN) { if (as[i].digital) drdev++; else ardev++; } else { if (as[i].digital) dpdev++; else apdev++; } } devinfo->function.audio.num_devs = max(ardev, apdev) + max(drdev, dpdev); devinfo->function.audio.devs = (struct hdac_pcm_devinfo *)malloc( devinfo->function.audio.num_devs * sizeof(struct hdac_pcm_devinfo), M_HDAC, M_ZERO | M_NOWAIT); if (devinfo->function.audio.devs == NULL) { device_printf(sc->dev, "Unable to allocate memory for devices\n"); return; } for (i = 0; i < devinfo->function.audio.num_devs; i++) { devinfo->function.audio.devs[i].index = i; devinfo->function.audio.devs[i].devinfo = devinfo; devinfo->function.audio.devs[i].play = -1; devinfo->function.audio.devs[i].rec = -1; devinfo->function.audio.devs[i].digital = 2; } for (i = 0; i < devinfo->function.audio.ascnt; i++) { if (as[i].enable == 0) continue; for (j = 0; j < devinfo->function.audio.num_devs; j++) { if (devinfo->function.audio.devs[j].digital != 2 && devinfo->function.audio.devs[j].digital != as[i].digital) continue; if (as[i].dir == HDA_CTL_IN) { if (devinfo->function.audio.devs[j].rec >= 0) continue; devinfo->function.audio.devs[j].rec = as[i].chan; } else { if (devinfo->function.audio.devs[j].play >= 0) continue; devinfo->function.audio.devs[j].play = as[i].chan; } sc->chans[as[i].chan].pdevinfo = &devinfo->function.audio.devs[j]; devinfo->function.audio.devs[j].digital = as[i].digital; break; } } for (i = 0; i < devinfo->function.audio.num_devs; i++) { struct hdac_pcm_devinfo *pdevinfo = &devinfo->function.audio.devs[i]; pdevinfo->dev = device_add_child(sc->dev, "pcm", -1); device_set_ivars(pdevinfo->dev, (void *)pdevinfo); } } static void hdac_dump_ctls(struct hdac_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_audio_ctl *ctl; struct hdac_softc *sc = devinfo->codec->sc; char buf[64]; int i, j, printed; if (flag == 0) { flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR); } for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { if ((flag & (1 << j)) == 0) continue; i = 0; printed = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || ctl->widget->enable == 0) continue; if (!((pdevinfo->play >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->play].as) || (pdevinfo->rec >= 0 && ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || (ctl->widget->bindas == -2 && pdevinfo->index == 0))) continue; if ((ctl->ossmask & (1 << j)) == 0) continue; if (printed == 0) { device_printf(pdevinfo->dev, "\n"); if (banner != NULL) { device_printf(pdevinfo->dev, "%s", banner); } else { device_printf(pdevinfo->dev, "Unknown Ctl"); } printf(" (OSS: %s)\n", hdac_audio_ctl_ossmixer_mask2allname(1 << j, buf, sizeof(buf))); device_printf(pdevinfo->dev, " |\n"); printed = 1; } device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, ctl->widget->nid, (ctl->ndir == HDA_CTL_IN)?"in ":"out"); if (ctl->ndir == HDA_CTL_IN && ctl->ndir == ctl->dir) printf(" %2d): ", ctl->index); else printf("): "); if (ctl->step > 0) { printf("%+d/%+ddB (%d steps)%s\n", (0 - ctl->offset) * (ctl->size + 1) / 4, (ctl->step - ctl->offset) * (ctl->size + 1) / 4, ctl->step + 1, ctl->mute?" + mute":""); } else printf("%s\n", ctl->mute?"mute":""); } } } static void hdac_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) { uint32_t cap; cap = fcap; if (cap != 0) { device_printf(dev, " Stream cap: 0x%08x\n", cap); device_printf(dev, " "); if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) printf(" AC3"); if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) printf(" FLOAT32"); if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) printf(" PCM"); printf("\n"); } cap = pcmcap; if (cap != 0) { device_printf(dev, " PCM cap: 0x%08x\n", cap); device_printf(dev, " "); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) printf(" 8"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) printf(" 16"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) printf(" 20"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) printf(" 24"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) printf(" 32"); printf(" bits,"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) printf(" 8"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) printf(" 11"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) printf(" 16"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) printf(" 22"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) printf(" 32"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) printf(" 44"); printf(" 48"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) printf(" 88"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) printf(" 96"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) printf(" 176"); if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) printf(" 192"); printf(" KHz\n"); } } static void hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) { uint32_t pincap; pincap = w->wclass.pin.cap; device_printf(sc->dev, " Pin cap: 0x%08x\n", pincap); device_printf(sc->dev, " "); if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) printf(" ISC"); if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) printf(" TRQD"); if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) printf(" PDC"); if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) printf(" HP"); if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) printf(" OUT"); if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) printf(" IN"); if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) printf(" BAL"); if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { printf(" VREF["); if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) printf(" 50"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) printf(" 80"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) printf(" 100"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) printf(" GROUND"); if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) printf(" HIZ"); printf(" ]"); } if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) printf(" EAPD"); printf("\n"); device_printf(sc->dev, " Pin config: 0x%08x\n", w->wclass.pin.config); device_printf(sc->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) printf(" HP"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) printf(" IN"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) printf(" OUT"); if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) printf(" VREFs"); printf("\n"); } static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf) { struct hdac_softc *sc = w->devinfo->codec->sc; device_printf(sc->dev, " nid %d 0x%08x as %2d seq %2d %13s %5s " "jack %2d loc %2d color %7s misc %d%s\n", w->nid, conf, HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf), HDA_CONFIG_DEFAULTCONF_LOCATION(conf), HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], HDA_CONFIG_DEFAULTCONF_MISC(conf), (w->enable == 0)?" [DISABLED]":""); } static void hdac_dump_pin_configs(struct hdac_devinfo *devinfo) { struct hdac_widget *w; int i; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; hdac_dump_pin_config(w, w->wclass.pin.config); } } static void hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) { device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); device_printf(sc->dev, " " "mute=%d step=%d size=%d offset=%d\n", HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); } static void hdac_dump_nodes(struct hdac_devinfo *devinfo) { struct hdac_softc *sc = devinfo->codec->sc; static char *ossname[] = SOUND_DEVICE_NAMES; struct hdac_widget *w, *cw; char buf[64]; int i, j; device_printf(sc->dev, "\n"); device_printf(sc->dev, "Default Parameter\n"); device_printf(sc->dev, "-----------------\n"); hdac_dump_audio_formats(sc->dev, devinfo->function.audio.supp_stream_formats, devinfo->function.audio.supp_pcm_size_rate); device_printf(sc->dev, " IN amp: 0x%08x\n", devinfo->function.audio.inamp_cap); device_printf(sc->dev, " OUT amp: 0x%08x\n", devinfo->function.audio.outamp_cap); for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL) { device_printf(sc->dev, "Ghost widget nid=%d\n", i); continue; } device_printf(sc->dev, "\n"); device_printf(sc->dev, " nid: %d%s\n", w->nid, (w->enable == 0) ? " [DISABLED]" : ""); device_printf(sc->dev, " Name: %s\n", w->name); device_printf(sc->dev, " Widget cap: 0x%08x\n", w->param.widget_cap); if (w->param.widget_cap & 0x0ee1) { device_printf(sc->dev, " "); if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap)) printf(" LRSWAP"); if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap)) printf(" PWR"); if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) printf(" DIGITAL"); if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) printf(" UNSOL"); if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) printf(" PROC"); if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) printf(" STRIPE"); if (HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(w->param.widget_cap)) printf(" STEREO"); printf("\n"); } if (w->bindas != -1) { device_printf(sc->dev, " Association: %d (0x%08x)\n", w->bindas, w->bindseqmask); } if (w->ossmask != 0 || w->ossdev >= 0) { device_printf(sc->dev, " OSS: %s", hdac_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); if (w->ossdev >= 0) printf(" (%s)", ossname[w->ossdev]); printf("\n"); } if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { hdac_dump_audio_formats(sc->dev, w->param.supp_stream_formats, w->param.supp_pcm_size_rate); } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) hdac_dump_pin(sc, w); if (w->param.eapdbtl != HDAC_INVALID) device_printf(sc->dev, " EAPD: 0x%08x\n", w->param.eapdbtl); if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && w->param.outamp_cap != 0) hdac_dump_amp(sc, w->param.outamp_cap, "Output"); if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && w->param.inamp_cap != 0) hdac_dump_amp(sc, w->param.inamp_cap, " Input"); if (w->nconns > 0) { device_printf(sc->dev, " connections: %d\n", w->nconns); device_printf(sc->dev, " |\n"); } for (j = 0; j < w->nconns; j++) { cw = hdac_widget_get(devinfo, w->conns[j]); device_printf(sc->dev, " + %s<- nid=%d [%s]", (w->connsenable[j] == 0)?"[DISABLED] ":"", w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); if (cw == NULL) printf(" [UNKNOWN]"); else if (cw->enable == 0) printf(" [DISABLED]"); if (w->nconns > 1 && w->selconn == j && w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) printf(" (selected)"); printf("\n"); } } } static void hdac_dump_dst_nid(struct hdac_pcm_devinfo *pdevinfo, nid_t nid, int depth) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w, *cw; char buf[64]; int i, printed = 0; if (depth > HDA_PARSE_MAXDEPTH) return; w = hdac_widget_get(devinfo, nid); if (w == NULL || w->enable == 0) return; if (depth == 0) device_printf(pdevinfo->dev, "%*s", 4, ""); else device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); printf("nid=%d [%s]", w->nid, w->name); if (depth > 0) { if (w->ossmask == 0) { printf("\n"); return; } printf(" [src: %s]", hdac_audio_ctl_ossmixer_mask2allname( w->ossmask, buf, sizeof(buf))); if (w->ossdev >= 0) { printf("\n"); return; } } printf("\n"); for (i = 0; i < w->nconns; i++) { if (w->connsenable[i] == 0) continue; cw = hdac_widget_get(devinfo, w->conns[i]); if (cw == NULL || cw->enable == 0 || cw->bindas == -1) continue; if (printed == 0) { device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); printed = 1; } hdac_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); } } static void hdac_dump_dac(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; int i, printed = 0; if (pdevinfo->play < 0) return; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; if (w->bindas != sc->chans[pdevinfo->play].as) continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Playback:\n"); } device_printf(pdevinfo->dev, "\n"); hdac_dump_dst_nid(pdevinfo, i, 0); } } static void hdac_dump_adc(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; int i; int printed = 0; if (pdevinfo->rec < 0) return; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) continue; if (w->bindas != sc->chans[pdevinfo->rec].as) continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Record:\n"); } device_printf(pdevinfo->dev, "\n"); hdac_dump_dst_nid(pdevinfo, i, 0); } } static void hdac_dump_mix(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_devinfo *devinfo = pdevinfo->devinfo; struct hdac_widget *w; int i; int printed = 0; if (pdevinfo->index != 0) return; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) continue; if ((w->pflags & HDA_ADC_MONITOR) == 0) continue; if (printed == 0) { printed = 1; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Input Mix:\n"); } device_printf(pdevinfo->dev, "\n"); hdac_dump_dst_nid(pdevinfo, i, 0); } } static void hdac_dump_pcmchannels(struct hdac_pcm_devinfo *pdevinfo) { struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; nid_t *nids; int i; if (pdevinfo->play >= 0) { i = pdevinfo->play; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Playback:\n"); device_printf(pdevinfo->dev, "\n"); hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, sc->chans[i].supp_pcm_size_rate); device_printf(pdevinfo->dev, " DAC:"); for (nids = sc->chans[i].io; *nids != -1; nids++) printf(" %d", *nids); printf("\n"); } if (pdevinfo->rec >= 0) { i = pdevinfo->rec; device_printf(pdevinfo->dev, "\n"); device_printf(pdevinfo->dev, "Record:\n"); device_printf(pdevinfo->dev, "\n"); hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, sc->chans[i].supp_pcm_size_rate); device_printf(pdevinfo->dev, " ADC:"); for (nids = sc->chans[i].io; *nids != -1; nids++) printf(" %d", *nids); printf("\n"); } } static void hdac_release_resources(struct hdac_softc *sc) { int i, j; if (sc == NULL) return; hdac_lock(sc); sc->polling = 0; sc->poll_ival = 0; callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); callout_stop(&sc->poll_jack); hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); callout_drain(&sc->poll_jack); hdac_irq_free(sc); for (i = 0; i < HDAC_CODEC_MAX; i++) { if (sc->codecs[i] == NULL) continue; for (j = 0; j < sc->codecs[i]->num_fgs; j++) { free(sc->codecs[i]->fgs[j].widget, M_HDAC); if (sc->codecs[i]->fgs[j].node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { free(sc->codecs[i]->fgs[j].function.audio.ctl, M_HDAC); free(sc->codecs[i]->fgs[j].function.audio.as, M_HDAC); free(sc->codecs[i]->fgs[j].function.audio.devs, M_HDAC); } } free(sc->codecs[i]->fgs, M_HDAC); free(sc->codecs[i], M_HDAC); sc->codecs[i] = NULL; } hdac_dma_free(sc, &sc->pos_dma); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); for (i = 0; i < sc->num_chans; i++) { if (sc->chans[i].blkcnt > 0) hdac_dma_free(sc, &sc->chans[i].bdl_dma); } free(sc->chans, M_HDAC); if (sc->chan_dmat != NULL) { bus_dma_tag_destroy(sc->chan_dmat); sc->chan_dmat = NULL; } hdac_mem_free(sc); snd_mtxfree(sc->lock); } /* This function surely going to make its way into upper level someday. */ static void hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) { const char *res = NULL; int i = 0, j, k, len, inv; if (on != NULL) *on = 0; if (off != NULL) *off = 0; if (sc == NULL) return; if (resource_string_value(device_get_name(sc->dev), device_get_unit(sc->dev), "config", &res) != 0) return; if (!(res != NULL && strlen(res) > 0)) return; HDA_BOOTVERBOSE( device_printf(sc->dev, "HDA Config:"); ); for (;;) { while (res[i] != '\0' && (res[i] == ',' || isspace(res[i]) != 0)) i++; if (res[i] == '\0') { HDA_BOOTVERBOSE( printf("\n"); ); return; } j = i; while (res[j] != '\0' && !(res[j] == ',' || isspace(res[j]) != 0)) j++; len = j - i; if (len > 2 && strncmp(res + i, "no", 2) == 0) inv = 2; else inv = 0; for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { if (strncmp(res + i + inv, hdac_quirks_tab[k].key, len - inv) != 0) continue; if (len - inv != strlen(hdac_quirks_tab[k].key)) break; HDA_BOOTVERBOSE( printf(" %s%s", (inv != 0) ? "no" : "", hdac_quirks_tab[k].key); ); if (inv == 0 && on != NULL) *on |= hdac_quirks_tab[k].value; else if (inv != 0 && off != NULL) *off |= hdac_quirks_tab[k].value; break; } i = j; } } #ifdef SND_DYNSYSCTL static int sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; device_t dev; uint32_t ctl; int err, val; dev = oidp->oid_arg1; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); hdac_lock(sc); val = sc->polling; hdac_unlock(sc); err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < 0 || val > 1) return (EINVAL); hdac_lock(sc); if (val != sc->polling) { if (val == 0) { callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); hdac_unlock(sc); callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); hdac_lock(sc); sc->polling = 0; ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl |= HDAC_INTCTL_GIE; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); } else { ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl &= ~HDAC_INTCTL_GIE; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); hdac_lock(sc); sc->polling = 1; hdac_poll_reinit(sc); callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } } hdac_unlock(sc); return (err); } static int sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; device_t dev; int err, val; dev = oidp->oid_arg1; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); hdac_lock(sc); val = ((uint64_t)sc->poll_ival * 1000) / hz; hdac_unlock(sc); err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL) return (err); if (val < 1) val = 1; if (val > 5000) val = 5000; val = ((uint64_t)val * hz) / 1000; if (val < 1) val = 1; if (val > (hz * 5)) val = hz * 5; hdac_lock(sc); sc->poll_ival = val; hdac_unlock(sc); return (err); } static int sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) { struct hdac_softc *sc; struct hdac_codec *codec; struct hdac_devinfo *devinfo; struct hdac_widget *w; device_t dev; uint32_t res, pincap, delay; int codec_index, fg_index; int i, err, val; nid_t cad; dev = oidp->oid_arg1; sc = device_get_softc(dev); if (sc == NULL) return (EINVAL); val = 0; err = sysctl_handle_int(oidp, &val, 0, req); if (err != 0 || req->newptr == NULL || val == 0) return (err); /* XXX: Temporary. For debugging. */ if (val == 100) { hdac_suspend(dev); return (0); } else if (val == 101) { hdac_resume(dev); return (0); } hdac_lock(sc); for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; cad = codec->cad; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; if (devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) continue; device_printf(dev, "Dumping AFG cad=%d nid=%d pins:\n", codec_index, devinfo->nid); for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) continue; hdac_dump_pin_config(w, w->wclass.pin.config); pincap = w->wclass.pin.cap; device_printf(dev, " Caps: %2s %3s %2s %4s %4s", HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { delay = 0; hdac_command(sc, HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), cad); do { res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); if (res != 0x7fffffff && res != 0xffffffff) break; DELAY(10); } while (++delay < 10000); } else { delay = 0; res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); } printf(" Sense: 0x%08x", res); if (delay > 0) printf(" delay %dus", delay * 10); } printf("\n"); } device_printf(dev, "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { device_printf(dev, " GPI:"); res = hdac_command(sc, HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); printf(" data=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); printf(" wake=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), cad); printf(" unsol=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); printf(" sticky=0x%08x\n", res); } if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { device_printf(dev, " GPO:"); res = hdac_command(sc, HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); printf(" data=0x%08x\n", res); } if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { device_printf(dev, "GPIO:"); res = hdac_command(sc, HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); printf(" data=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); printf(" enable=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); printf(" direction=0x%08x\n", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); device_printf(dev, " wake=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), cad); printf(" unsol=0x%08x", res); res = hdac_command(sc, HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); printf(" sticky=0x%08x\n", res); } } } hdac_unlock(sc); return (0); } #endif static void hdac_attach2(void *arg) { struct hdac_codec *codec; struct hdac_softc *sc; struct hdac_audio_ctl *ctl; uint32_t quirks_on, quirks_off; int codec_index, fg_index; int i, dmaalloc = 0; struct hdac_devinfo *devinfo; sc = (struct hdac_softc *)arg; hdac_config_fetch(sc, &quirks_on, &quirks_off); HDA_BOOTHVERBOSE( device_printf(sc->dev, "HDA Config: on=0x%08x off=0x%08x\n", quirks_on, quirks_off); ); hdac_lock(sc); /* Remove ourselves from the config hooks */ if (sc->intrhook.ich_func != NULL) { config_intrhook_disestablish(&sc->intrhook); sc->intrhook.ich_func = NULL; } /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(sc->dev, "Starting CORB Engine...\n"); ); hdac_corb_start(sc); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Enabling controller interrupt...\n"); ); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); if (sc->polling == 0) { HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); } else { callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } DELAY(1000); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Scanning HDA codecs ...\n"); ); hdac_scan_codecs(sc); for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; HDA_BOOTVERBOSE( device_printf(sc->dev, "\n"); device_printf(sc->dev, "Processing %s FG cad=%d nid=%d...\n", (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": "unknown", devinfo->codec->cad, devinfo->nid); ); if (devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { HDA_BOOTHVERBOSE( device_printf(sc->dev, "Powering down...\n"); ); hdac_command(sc, HDA_CMD_SET_POWER_STATE(codec->cad, devinfo->nid, HDA_CMD_POWER_STATE_D3), codec->cad); continue; } HDA_BOOTHVERBOSE( device_printf(sc->dev, "Powering up...\n"); ); hdac_powerup(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing audio FG...\n"); ); hdac_audio_parse(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing Ctls...\n"); ); hdac_audio_ctl_parse(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing vendor patch...\n"); ); hdac_vendor_patch_parse(devinfo); devinfo->function.audio.quirks |= quirks_on; devinfo->function.audio.quirks &= ~quirks_off; HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling nonaudio...\n"); ); hdac_audio_disable_nonaudio(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling useless...\n"); ); hdac_audio_disable_useless(devinfo); HDA_BOOTVERBOSE( device_printf(sc->dev, "Patched pins configuration:\n"); hdac_dump_pin_configs(devinfo); ); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Parsing pin associations...\n"); ); hdac_audio_as_parse(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Building AFG tree...\n"); ); hdac_audio_build_tree(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling unassociated " "widgets...\n"); ); hdac_audio_disable_unas(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling nonselected " "inputs...\n"); ); hdac_audio_disable_notselected(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling useless...\n"); ); hdac_audio_disable_useless(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling " "crossassociatement connections...\n"); ); hdac_audio_disable_crossas(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Disabling useless...\n"); ); hdac_audio_disable_useless(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Binding associations to channels...\n"); ); hdac_audio_bind_as(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Assigning names to signal sources...\n"); ); hdac_audio_assign_names(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Assigning mixers to the tree...\n"); ); hdac_audio_assign_mixers(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Preparing pin controls...\n"); ); hdac_audio_prepare_pin_ctrl(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "AFG commit...\n"); ); hdac_audio_commit(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "Ctls commit...\n"); ); hdac_audio_ctl_commit(devinfo); HDA_BOOTHVERBOSE( device_printf(sc->dev, "HP switch init...\n"); ); hdac_hp_switch_init(devinfo); if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && dmaalloc == 0) { if (hdac_dma_alloc(sc, &sc->pos_dma, (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { HDA_BOOTVERBOSE( device_printf(sc->dev, "Failed to " "allocate DMA pos buffer " "(non-fatal)\n"); ); } else dmaalloc = 1; } HDA_BOOTHVERBOSE( device_printf(sc->dev, "Creating PCM devices...\n"); ); hdac_create_pcms(devinfo); HDA_BOOTVERBOSE( if (devinfo->function.audio.quirks != 0) { device_printf(sc->dev, "FG config/quirks:"); for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { if ((devinfo->function.audio.quirks & hdac_quirks_tab[i].value) == hdac_quirks_tab[i].value) printf(" %s", hdac_quirks_tab[i].key); } printf("\n"); } device_printf(sc->dev, "\n"); device_printf(sc->dev, "+-------------------+\n"); device_printf(sc->dev, "| DUMPING HDA NODES |\n"); device_printf(sc->dev, "+-------------------+\n"); hdac_dump_nodes(devinfo); ); HDA_BOOTHVERBOSE( device_printf(sc->dev, "\n"); device_printf(sc->dev, "+------------------------+\n"); device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); device_printf(sc->dev, "+------------------------+\n"); device_printf(sc->dev, "\n"); i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { device_printf(sc->dev, "%3d: nid %3d %s (%s) index %d", i, (ctl->widget != NULL) ? ctl->widget->nid : -1, (ctl->ndir == HDA_CTL_IN)?"in ":"out", (ctl->dir == HDA_CTL_IN)?"in ":"out", ctl->index); if (ctl->childwidget != NULL) printf(" cnid %3d", ctl->childwidget->nid); else printf(" "); printf(" ossmask=0x%08x\n", ctl->ossmask); device_printf(sc->dev, " mute: %d step: %3d size: %3d off: %3d%s\n", ctl->mute, ctl->step, ctl->size, ctl->offset, (ctl->enable == 0) ? " [DISABLED]" : ((ctl->ossmask == 0) ? " [UNUSED]" : "")); } ); } } hdac_unlock(sc); HDA_BOOTVERBOSE( device_printf(sc->dev, "\n"); ); bus_generic_attach(sc->dev); #ifdef SND_DYNSYSCTL SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_polling, "I", "Enable polling mode"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_polling_interval, "I", "Controller/Jack Sense polling interval (1-1000 ms)"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_pindump, "I", "Dump pin states/data"); #endif } /**************************************************************************** * int hdac_suspend(device_t) * * Suspend and power down HDA bus and codecs. ****************************************************************************/ static int hdac_suspend(device_t dev) { struct hdac_softc *sc; struct hdac_codec *codec; struct hdac_devinfo *devinfo; int codec_index, fg_index, i; HDA_BOOTHVERBOSE( device_printf(dev, "Suspend...\n"); ); sc = device_get_softc(dev); hdac_lock(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Stop streams...\n"); ); for (i = 0; i < sc->num_chans; i++) { if (sc->chans[i].flags & HDAC_CHN_RUNNING) { sc->chans[i].flags |= HDAC_CHN_SUSPEND; hdac_channel_stop(sc, &sc->chans[i]); } } for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; HDA_BOOTHVERBOSE( device_printf(dev, "Power down FG" " cad=%d nid=%d to the D3 state...\n", codec->cad, devinfo->nid); ); hdac_command(sc, HDA_CMD_SET_POWER_STATE(codec->cad, devinfo->nid, HDA_CMD_POWER_STATE_D3), codec->cad); } } HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); callout_stop(&sc->poll_hda); callout_stop(&sc->poll_hdac); callout_stop(&sc->poll_jack); hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); callout_drain(&sc->poll_hda); callout_drain(&sc->poll_hdac); callout_drain(&sc->poll_jack); HDA_BOOTHVERBOSE( device_printf(dev, "Suspend done\n"); ); return (0); } /**************************************************************************** * int hdac_resume(device_t) * * Powerup and restore HDA bus and codecs state. ****************************************************************************/ static int hdac_resume(device_t dev) { struct hdac_softc *sc; struct hdac_codec *codec; struct hdac_devinfo *devinfo; int codec_index, fg_index, i; HDA_BOOTHVERBOSE( device_printf(dev, "Resume...\n"); ); sc = device_get_softc(dev); hdac_lock(sc); /* Quiesce everything */ HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); hdac_reset(sc, 1); /* Initialize the CORB and RIRB */ hdac_corb_init(sc); hdac_rirb_init(sc); /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(dev, "Starting CORB Engine...\n"); ); hdac_corb_start(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Enabling controller interrupt...\n"); ); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); if (sc->polling == 0) { HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); } else { callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } DELAY(1000); for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { codec = sc->codecs[codec_index]; if (codec == NULL) continue; for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { devinfo = &codec->fgs[fg_index]; if (devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { HDA_BOOTHVERBOSE( device_printf(dev, "Power down unsupported non-audio FG" " cad=%d nid=%d to the D3 state...\n", codec->cad, devinfo->nid); ); hdac_command(sc, HDA_CMD_SET_POWER_STATE(codec->cad, devinfo->nid, HDA_CMD_POWER_STATE_D3), codec->cad); continue; } HDA_BOOTHVERBOSE( device_printf(dev, "Power up audio FG cad=%d nid=%d...\n", devinfo->codec->cad, devinfo->nid); ); hdac_powerup(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "AFG commit...\n"); ); hdac_audio_commit(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "Ctls commit...\n"); ); hdac_audio_ctl_commit(devinfo); HDA_BOOTHVERBOSE( device_printf(dev, "HP switch init...\n"); ); hdac_hp_switch_init(devinfo); hdac_unlock(sc); for (i = 0; i < devinfo->function.audio.num_devs; i++) { struct hdac_pcm_devinfo *pdevinfo = &devinfo->function.audio.devs[i]; HDA_BOOTHVERBOSE( device_printf(pdevinfo->dev, "OSS mixer reinitialization...\n"); ); if (mixer_reinit(pdevinfo->dev) == -1) device_printf(pdevinfo->dev, "unable to reinitialize the mixer\n"); } hdac_lock(sc); } } HDA_BOOTHVERBOSE( device_printf(dev, "Start streams...\n"); ); for (i = 0; i < sc->num_chans; i++) { if (sc->chans[i].flags & HDAC_CHN_SUSPEND) { sc->chans[i].flags &= ~HDAC_CHN_SUSPEND; hdac_channel_start(sc, &sc->chans[i]); } } hdac_unlock(sc); HDA_BOOTHVERBOSE( device_printf(dev, "Resume done\n"); ); return (0); } /**************************************************************************** * int hdac_detach(device_t) * * Detach and free up resources utilized by the hdac device. ****************************************************************************/ static int hdac_detach(device_t dev) { struct hdac_softc *sc; device_t *devlist; int i, devcount, error; if ((error = device_get_children(dev, &devlist, &devcount)) != 0) return (error); for (i = 0; i < devcount; i++) { if ((error = device_delete_child(dev, devlist[i])) != 0) { free(devlist, M_TEMP); return (error); } } free(devlist, M_TEMP); sc = device_get_softc(dev); hdac_release_resources(sc); return (0); } static int hdac_print_child(device_t dev, device_t child) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(child); int retval; retval = bus_print_child_header(dev, child); retval += printf(" at cad %d nid %d", pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid); retval += bus_print_child_footer(dev, child); return (retval); } static device_method_t hdac_methods[] = { /* device interface */ DEVMETHOD(device_probe, hdac_probe), DEVMETHOD(device_attach, hdac_attach), DEVMETHOD(device_detach, hdac_detach), DEVMETHOD(device_suspend, hdac_suspend), DEVMETHOD(device_resume, hdac_resume), /* Bus interface */ DEVMETHOD(bus_print_child, hdac_print_child), { 0, 0 } }; static driver_t hdac_driver = { "hdac", hdac_methods, sizeof(struct hdac_softc), }; static devclass_t hdac_devclass; DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_hda, 1); static int hdac_pcm_probe(device_t dev) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(dev); char buf[128]; snprintf(buf, sizeof(buf), "HDA %s PCM #%d %s", hdac_codec_name(pdevinfo->devinfo->codec), pdevinfo->index, pdevinfo->digital?"Digital":"Analog"); device_set_desc_copy(dev, buf); return (0); } static int hdac_pcm_attach(device_t dev) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(dev); struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; char status[SND_STATUSLEN]; int i; pdevinfo->chan_size = pcm_getbuffersize(dev, HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); HDA_BOOTVERBOSE( device_printf(dev, "+--------------------------------------+\n"); device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); device_printf(dev, "+--------------------------------------+\n"); hdac_dump_pcmchannels(pdevinfo); device_printf(dev, "\n"); device_printf(dev, "+--------------------------------+\n"); device_printf(dev, "| DUMPING Playback/Record Pathes |\n"); device_printf(dev, "+--------------------------------+\n"); hdac_dump_dac(pdevinfo); hdac_dump_adc(pdevinfo); hdac_dump_mix(pdevinfo); device_printf(dev, "\n"); device_printf(dev, "+-------------------------+\n"); device_printf(dev, "| DUMPING Volume Controls |\n"); device_printf(dev, "+-------------------------+\n"); hdac_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); hdac_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); hdac_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); hdac_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); hdac_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); hdac_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); hdac_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); hdac_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); hdac_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); hdac_dump_ctls(pdevinfo, NULL, 0); device_printf(dev, "\n"); ); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { i &= HDA_BLK_ALIGN; if (i < HDA_BLK_MIN) i = HDA_BLK_MIN; pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; i = 0; while (pdevinfo->chan_blkcnt >> i) i++; pdevinfo->chan_blkcnt = 1 << (i - 1); if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) pdevinfo->chan_blkcnt = HDA_BDL_MIN; else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) pdevinfo->chan_blkcnt = HDA_BDL_MAX; } else pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; /* * We don't register interrupt handler with snd_setup_intr * in pcm device. Mark pcm device as MPSAFE manually. */ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); HDA_BOOTHVERBOSE( device_printf(dev, "OSS mixer initialization...\n"); ); if (mixer_init(dev, &hdac_audio_ctl_ossmixer_class, pdevinfo) != 0) device_printf(dev, "Can't register mixer\n"); HDA_BOOTHVERBOSE( device_printf(dev, "Registering PCM channels...\n"); ); if (pcm_register(dev, pdevinfo, (pdevinfo->play >= 0)?1:0, (pdevinfo->rec >= 0)?1:0) != 0) device_printf(dev, "Can't register PCM\n"); pdevinfo->registered++; if (pdevinfo->play >= 0) pcm_addchan(dev, PCMDIR_PLAY, &hdac_channel_class, pdevinfo); if (pdevinfo->rec >= 0) pcm_addchan(dev, PCMDIR_REC, &hdac_channel_class, pdevinfo); snprintf(status, SND_STATUSLEN, "at cad %d nid %d on %s %s", pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid, device_get_nameunit(sc->dev), PCM_KLDSTRING(snd_hda)); pcm_setstatus(dev, status); return (0); } static int hdac_pcm_detach(device_t dev) { struct hdac_pcm_devinfo *pdevinfo = (struct hdac_pcm_devinfo *)device_get_ivars(dev); int err; if (pdevinfo->registered > 0) { err = pcm_unregister(dev); if (err != 0) return (err); } return (0); } static device_method_t hdac_pcm_methods[] = { /* device interface */ DEVMETHOD(device_probe, hdac_pcm_probe), DEVMETHOD(device_attach, hdac_pcm_attach), DEVMETHOD(device_detach, hdac_pcm_detach), { 0, 0 } }; static driver_t hdac_pcm_driver = { "pcm", hdac_pcm_methods, PCM_SOFTC_SIZE, }; DRIVER_MODULE(snd_hda_pcm, hdac, hdac_pcm_driver, pcm_devclass, 0, 0); Index: projects/cambria/sys/dev/usb/ehci_ixp4xx.c =================================================================== --- projects/cambria/sys/dev/usb/ehci_ixp4xx.c (nonexistent) +++ projects/cambria/sys/dev/usb/ehci_ixp4xx.c (revision 186460) @@ -0,0 +1,360 @@ +/*- + * Copyright (c) 2008 Sam Leffler. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * IXP435 attachment driver for the USB Enhanced Host Controller. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define EHCI_VENDORID_IXP4XX 0x42fa05 +#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller" + +struct ixp_ehci_softc { + ehci_softc_t base; /* storage for EHCI code */ + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct bus_space tag; /* tag for private bus space ops */ +}; + +static int ehci_ixp_detach(device_t self); + +static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t); +static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t); +static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t); +static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t); +static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t); +static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t); + +static int +ehci_ixp_suspend(device_t self) +{ + ehci_softc_t *sc; + int err; + + err = bus_generic_suspend(self); + if (err == 0) { + sc = device_get_softc(self); + ehci_power(PWR_SUSPEND, sc); + } + return err; +} + +static int +ehci_ixp_resume(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + + ehci_power(PWR_RESUME, sc); + bus_generic_resume(self); + return 0; +} + +static int +ehci_ixp_shutdown(device_t self) +{ + ehci_softc_t *sc; + int err; + + err = bus_generic_shutdown(self); + if (err == 0) { + sc = device_get_softc(self); + ehci_shutdown(sc); + } + return err; +} + +static int +ehci_ixp_probe(device_t self) +{ + device_set_desc(self, EHCI_HC_DEVSTR); + return BUS_PROBE_DEFAULT; +} + +static int +ehci_ixp_attach(device_t self) +{ + struct ixp_ehci_softc *isc = device_get_softc(self); + ehci_softc_t *sc = &isc->base; + int err, rid; + + sc->sc_bus.usbrev = USBREV_2_0; + + /* NB: hints fix the memory location and irq */ + + rid = 0; + sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->io_res == NULL) { + device_printf(self, "Could not map memory\n"); + return ENXIO; + } + + /* + * Craft special resource for bus space ops that handle + * byte-alignment of non-word addresses. Also, since + * we're already intercepting bus space ops we handle + * the register window offset that could otherwise be + * done with bus_space_subregion. + */ + isc->iot = rman_get_bustag(sc->io_res); + isc->tag.bs_cookie = isc->iot; + /* read single */ + isc->tag.bs_r_1 = ehci_bs_r_1, + isc->tag.bs_r_2 = ehci_bs_r_2, + isc->tag.bs_r_4 = ehci_bs_r_4, + /* write (single) */ + isc->tag.bs_w_1 = ehci_bs_w_1, + isc->tag.bs_w_2 = ehci_bs_w_2, + isc->tag.bs_w_4 = ehci_bs_w_4, + + sc->iot = &isc->tag; + sc->ioh = rman_get_bushandle(sc->io_res); + sc->sc_size = IXP435_USB1_SIZE - 0x100; + + rid = 0; + sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, + &rid, RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + ehci_ixp_detach(self); + return ENXIO; + } + sc->sc_bus.bdev = device_add_child(self, "usb", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + ehci_ixp_detach(self); + return ENOMEM; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + + sprintf(sc->sc_vendor, "Intel"); + sc->sc_id_vendor = EHCI_VENDORID_IXP4XX; + + err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO, + NULL, (driver_intr_t*)ehci_intr, sc, &sc->ih); + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->ih = NULL; + ehci_ixp_detach(self); + return ENXIO; + } + + /* There are no companion USB controllers */ + sc->sc_ncomp = 0; + + /* Allocate a parent dma tag for DMA maps */ + err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + NULL, NULL, &sc->sc_bus.parent_dmatag); + if (err) { + device_printf(self, "Could not allocate parent DMA tag (%d)\n", + err); + ehci_ixp_detach(self); + return ENXIO; + } + + /* Allocate a dma tag for transfer buffers */ + err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, + busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag); + if (err) { + device_printf(self, "Could not allocate buffer DMA tag (%d)\n", + err); + ehci_ixp_detach(self); + return ENXIO; + } + + /* + * Arrange to force Host mode, select big-endian byte alignment, + * and arrange to not terminate reset operations (the adapter + * will ignore it if we do but might as well save a reg write). + * Also, the controller has an embedded Transaction Translator + * which means port speed must be read from the Port Status + * register following a port enable. + */ + sc->sc_flags |= EHCI_SCFLG_TT + | EHCI_SCFLG_SETMODE + | EHCI_SCFLG_BIGEDESC + | EHCI_SCFLG_BIGEMMIO + | EHCI_SCFLG_NORESTERM + ; + (void) ehci_reset(sc); + + err = ehci_init(sc); + if (!err) { + sc->sc_flags |= EHCI_SCFLG_DONEINIT; + err = device_probe_and_attach(sc->sc_bus.bdev); + } + + if (err) { + device_printf(self, "USB init failed err=%d\n", err); + ehci_ixp_detach(self); + return EIO; + } + return 0; +} + +static int +ehci_ixp_detach(device_t self) +{ + struct ixp_ehci_softc *isc = device_get_softc(self); + ehci_softc_t *sc = &isc->base; + int err; + + if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { + ehci_detach(sc, 0); + sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; + } + + /* + * Disable interrupts that might have been switched on in ehci_init() + */ + if (sc->iot && sc->ioh) + bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0); + if (sc->sc_bus.parent_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.parent_dmatag); + if (sc->sc_bus.buffer_dmatag != NULL) + bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag); + + if (sc->irq_res && sc->ih) { + err = bus_teardown_intr(self, sc->irq_res, sc->ih); + + if (err) + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->ih = NULL; + } + if (sc->sc_bus.bdev != NULL) { + device_delete_child(self, sc->sc_bus.bdev); + sc->sc_bus.bdev = NULL; + } + if (sc->irq_res != NULL) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); + sc->irq_res = NULL; + } + if (sc->io_res != NULL) { + bus_release_resource(self, SYS_RES_MEMORY, 0, sc->io_res); + sc->io_res = NULL; + } + sc->iot = 0; + sc->ioh = 0; + return 0; +} + +/* + * Bus space accessors for PIO operations. + */ + +static uint8_t +ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o) +{ + return bus_space_read_1((bus_space_tag_t) t, h, + 0x100 + (o &~ 3) + (3 - (o & 3))); +} + +static void +ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v) +{ + panic("%s", __func__); +} + +static uint16_t +ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o) +{ + return bus_space_read_2((bus_space_tag_t) t, h, + 0x100 + (o &~ 3) + (2 - (o & 3))); +} + +static void +ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v) +{ + panic("%s", __func__); +} + +static uint32_t +ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o) +{ + return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o); +} + +static void +ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v) +{ + bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ehci_ixp_probe), + DEVMETHOD(device_attach, ehci_ixp_attach), + DEVMETHOD(device_detach, ehci_ixp_detach), + DEVMETHOD(device_suspend, ehci_ixp_suspend), + DEVMETHOD(device_resume, ehci_ixp_resume), + DEVMETHOD(device_shutdown, ehci_ixp_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct ixp_ehci_softc), +}; +static devclass_t ehci_devclass; +DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0); Property changes on: projects/cambria/sys/dev/usb/ehci_ixp4xx.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mergeinfo ## -0,0 +0,2 ## Merged /projects/cambria/sys/arm/xscale/ixp425/ixp435_ehci.c:r186008-186414 Merged /user/peter/kinfo/sys/arm/xscale/ixp425/ixp435_ehci.c:r185413-185547 Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/cambria/sys/dev/usb/ehci_mbus.c =================================================================== --- projects/cambria/sys/dev/usb/ehci_mbus.c (revision 186459) +++ projects/cambria/sys/dev/usb/ehci_mbus.c (revision 186460) @@ -1,318 +1,320 @@ /*- * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. * All rights reserved. * * Developed by Semihalf. * * 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. Neither the name of MARVELL nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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. */ /* * MBus attachment driver for the USB Enhanced Host Controller. */ #include __FBSDID("$FreeBSD$"); #include "opt_bus.h" #include #include #include #include #include #include #include #include #include -#include #include +#include + +#include #include #include #include #include #include #include #include #include #include #define EHCI_VENDORID_MRVL 0x1286 #define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller" static device_attach_t ehci_mbus_attach; static device_detach_t ehci_mbus_detach; static device_shutdown_t ehci_mbus_shutdown; static device_suspend_t ehci_mbus_suspend; static device_resume_t ehci_mbus_resume; static int ehci_mbus_suspend(device_t self) { ehci_softc_t *sc; int err; err = bus_generic_suspend(self); if (err) return (err); sc = device_get_softc(self); ehci_power(PWR_SUSPEND, sc); return (0); } static int ehci_mbus_resume(device_t self) { ehci_softc_t *sc; sc = device_get_softc(self); ehci_power(PWR_RESUME, sc); bus_generic_resume(self); return (0); } static int ehci_mbus_shutdown(device_t self) { ehci_softc_t *sc; int err; err = bus_generic_shutdown(self); if (err) return (err); sc = device_get_softc(self); ehci_shutdown(sc); return (0); } static int ehci_mbus_probe(device_t self) { device_set_desc(self, EHCI_HC_DEVSTR); return (BUS_PROBE_DEFAULT); } static int ehci_mbus_attach(device_t self) { ehci_softc_t *sc; bus_space_handle_t bsh; int err, rid; sc = device_get_softc(self); sc->sc_bus.usbrev = USBREV_2_0; rid = 0; sc->io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->io_res) { device_printf(self, "Could not map memory\n"); return (ENXIO); } sc->iot = rman_get_bustag(sc->io_res); bsh = rman_get_bushandle(sc->io_res); /* * Marvell EHCI host controller registers start at certain offset within * the whole USB registers range, so create a subregion for the host * mode configuration purposes. */ if (bus_space_subregion(sc->iot, bsh, MV_USB_HOST_OFST, MV_USB_SIZE - MV_USB_HOST_OFST, &sc->ioh) != 0) panic("%s: unable to subregion USB host registers", device_get_name(self)); sc->sc_size = MV_USB_SIZE - MV_USB_HOST_OFST; /* * Notice: Marvell EHCI controller has TWO interrupt lines, so make sure to * use the correct rid for the main one (controller interrupt) -- * refer to obio_devices[] for the right resource number to use here. */ rid = 1; sc->irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); ehci_mbus_detach(self); return (ENXIO); } sc->sc_bus.bdev = device_add_child(self, "usb", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); ehci_mbus_detach(self); return (ENOMEM); } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); sprintf(sc->sc_vendor, "Marvell"); sc->sc_id_vendor = EHCI_VENDORID_MRVL; err = bus_setup_intr(self, sc->irq_res, INTR_TYPE_BIO, NULL, (driver_intr_t*)ehci_intr, sc, &sc->ih); if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->ih = NULL; ehci_mbus_detach(self); return (ENXIO); } /* There are no companion USB controllers */ sc->sc_ncomp = 0; /* Allocate a parent dma tag for DMA maps */ err = bus_dma_tag_create(bus_get_dma_tag(self), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &sc->sc_bus.parent_dmatag); if (err) { device_printf(self, "Could not allocate parent DMA tag (%d)\n", err); ehci_mbus_detach(self); return (ENXIO); } /* Allocate a dma tag for transfer buffers */ err = bus_dma_tag_create(sc->sc_bus.parent_dmatag, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, USB_DMA_NSEG, BUS_SPACE_MAXSIZE_32BIT, 0, busdma_lock_mutex, &Giant, &sc->sc_bus.buffer_dmatag); if (err) { device_printf(self, "Could not allocate buffer DMA tag (%d)\n", err); ehci_mbus_detach(self); return (ENXIO); } /* * Workaround for Marvell integrated EHCI controller: reset of * the EHCI core clears the USBMODE register, which sets the core in * an undefined state (neither host nor agent), so it needs to be set * again for proper operation. * * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for * details. */ sc->sc_flags |= EHCI_SCFLG_SETMODE; if (bootverbose) device_printf(self, "5.24 GL USB-2 workaround enabled\n"); /* XXX all MV chips need it? */ sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM; err = ehci_init(sc); if (!err) { sc->sc_flags |= EHCI_SCFLG_DONEINIT; err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); ehci_mbus_detach(self); return (EIO); } return (0); } static int ehci_mbus_detach(device_t self) { ehci_softc_t *sc; int err; sc = device_get_softc(self); if (sc->sc_flags & EHCI_SCFLG_DONEINIT) { ehci_detach(sc, 0); sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; } /* * Disable interrupts that might have been switched on in ehci_init() */ if (sc->iot && sc->ioh) bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0); if (sc->sc_bus.parent_dmatag != NULL) bus_dma_tag_destroy(sc->sc_bus.parent_dmatag); if (sc->sc_bus.buffer_dmatag != NULL) bus_dma_tag_destroy(sc->sc_bus.buffer_dmatag); if (sc->irq_res && sc->ih) { err = bus_teardown_intr(self, sc->irq_res, sc->ih); if (err) device_printf(self, "Could not tear down irq, %d\n", err); sc->ih = NULL; } if (sc->sc_bus.bdev) { device_delete_child(self, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; } if (sc->irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->irq_res); sc->irq_res = NULL; } if (sc->io_res) { bus_release_resource(self, SYS_RES_MEMORY, 0, sc->io_res); sc->io_res = NULL; sc->iot = 0; sc->ioh = 0; } return (0); } static device_method_t ehci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ehci_mbus_probe), DEVMETHOD(device_attach, ehci_mbus_attach), DEVMETHOD(device_detach, ehci_mbus_detach), DEVMETHOD(device_suspend, ehci_mbus_suspend), DEVMETHOD(device_resume, ehci_mbus_resume), DEVMETHOD(device_shutdown, ehci_mbus_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t ehci_driver = { "ehci", ehci_methods, sizeof(ehci_softc_t), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, mbus, ehci_driver, ehci_devclass, 0, 0); Index: projects/cambria/sys/dev/usb/uftdi.c =================================================================== --- projects/cambria/sys/dev/usb/uftdi.c (revision 186459) +++ projects/cambria/sys/dev/usb/uftdi.c (revision 186460) @@ -1,776 +1,778 @@ /* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net). * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include __FBSDID("$FreeBSD$"); /* * FTDI FT8U100AX serial adapter driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #ifdef USB_DEBUG static int uftdidebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi"); SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW, &uftdidebug, 0, "uftdi debug level"); #define DPRINTF(x) do { \ if (uftdidebug) \ printf x; \ } while (0) #define DPRINTFN(n, x) do { \ if (uftdidebug > (n)) \ printf x; \ } while (0) #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UFTDI_CONFIG_INDEX 0 #define UFTDI_IFACE_INDEX 0 /* * These are the maximum number of bytes transferred per frame. * The output buffer size cannot be increased due to the size encoding. */ #define UFTDIIBUFSIZE 256 #define UFTDIOBUFSIZE 64 struct uftdi_softc { struct ucom_softc sc_ucom; usbd_interface_handle sc_iface; /* interface */ enum uftdi_type sc_type; u_int sc_hdrlen; u_char sc_msr; u_char sc_lsr; u_int last_lcr; }; static void uftdi_get_status(void *, int portno, u_char *lsr, u_char *msr); static void uftdi_set(void *, int, int, int); static int uftdi_param(void *, int, struct termios *); static int uftdi_open(void *sc, int portno); static void uftdi_read(void *sc, int portno, u_char **ptr,u_int32_t *count); static size_t uftdi_write(void *sc, int portno, struct tty *, u_char *to, u_int32_t count); static void uftdi_break(void *sc, int portno, int onoff); static int uftdi_8u232am_getrate(speed_t speed, int *rate); struct ucom_callback uftdi_callback = { uftdi_get_status, uftdi_set, uftdi_param, NULL, uftdi_open, NULL, uftdi_read, uftdi_write, }; static int uftdi_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->iface != NULL) { if (uaa->vendor == USB_VENDOR_FTDI && (uaa->product == USB_PRODUCT_FTDI_SERIAL_2232C)) return (UMATCH_VENDOR_IFACESUBCLASS); return (UMATCH_NONE); } DPRINTFN(20,("uftdi: vendor=0x%x, product=0x%x\n", uaa->vendor, uaa->product)); if (uaa->vendor == USB_VENDOR_FTDI && (uaa->product == USB_PRODUCT_FTDI_SERIAL_8U100AX || uaa->product == USB_PRODUCT_FTDI_SERIAL_8U232AM || uaa->product == USB_PRODUCT_FTDI_SEMC_DSS20 || uaa->product == USB_PRODUCT_FTDI_CFA_631 || uaa->product == USB_PRODUCT_FTDI_CFA_632 || uaa->product == USB_PRODUCT_FTDI_CFA_633 || uaa->product == USB_PRODUCT_FTDI_CFA_634 || uaa->product == USB_PRODUCT_FTDI_CFA_635 || uaa->product == USB_PRODUCT_FTDI_USBSERIAL || uaa->product == USB_PRODUCT_FTDI_MX2_3 || uaa->product == USB_PRODUCT_FTDI_MX4_5 || uaa->product == USB_PRODUCT_FTDI_LK202 || uaa->product == USB_PRODUCT_FTDI_LK204 || uaa->product == USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M || uaa->product == USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S || uaa->product == USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U || uaa->product == USB_PRODUCT_FTDI_EISCOU || uaa->product == USB_PRODUCT_FTDI_UOPTBR || uaa->product == USB_PRODUCT_FTDI_EMCU2D || uaa->product == USB_PRODUCT_FTDI_PCMSFU || - uaa->product == USB_PRODUCT_FTDI_EMCU2H )) + uaa->product == USB_PRODUCT_FTDI_EMCU2H || + uaa->product == USB_PRODUCT_FTDI_MAXSTREAM )) return (UMATCH_VENDOR_PRODUCT); if (uaa->vendor == USB_VENDOR_SIIG2 && (uaa->product == USB_PRODUCT_SIIG2_US2308)) return (UMATCH_VENDOR_PRODUCT); if (uaa->vendor == USB_VENDOR_INTREPIDCS && (uaa->product == USB_PRODUCT_INTREPIDCS_VALUECAN || uaa->product == USB_PRODUCT_INTREPIDCS_NEOVI)) return (UMATCH_VENDOR_PRODUCT); if (uaa->vendor == USB_VENDOR_BBELECTRONICS && (uaa->product == USB_PRODUCT_BBELECTRONICS_USOTL4)) return (UMATCH_VENDOR_PRODUCT); if (uaa->vendor == USB_VENDOR_MELCO && (uaa->product == USB_PRODUCT_MELCO_PCOPRS1)) return (UMATCH_VENDOR_PRODUCT); return (UMATCH_NONE); } static int uftdi_attach(device_t self) { struct uftdi_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); usbd_device_handle dev = uaa->device; usbd_interface_handle iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int i; usbd_status err; struct ucom_softc *ucom = &sc->sc_ucom; DPRINTFN(10,("\nuftdi_attach: sc=%p\n", sc)); ucom->sc_dev = self; ucom->sc_udev = dev; if (uaa->iface == NULL) { /* Move the device into the configured state. */ err = usbd_set_config_index(dev, UFTDI_CONFIG_INDEX, 1); if (err) { device_printf(ucom->sc_dev, "failed to set configuration, err=%s\n", usbd_errstr(err)); goto bad; } err = usbd_device2interface_handle(dev, UFTDI_IFACE_INDEX, &iface); if (err) { device_printf(ucom->sc_dev, "failed to get interface, err=%s\n", usbd_errstr(err)); goto bad; } } else { iface = uaa->iface; } id = usbd_get_interface_descriptor(iface); ucom->sc_iface = iface; switch( uaa->vendor ){ case USB_VENDOR_FTDI: switch( uaa->product ){ case USB_PRODUCT_FTDI_SERIAL_8U100AX: sc->sc_type = UFTDI_TYPE_SIO; sc->sc_hdrlen = 1; break; case USB_PRODUCT_FTDI_SEMC_DSS20: case USB_PRODUCT_FTDI_SERIAL_8U232AM: case USB_PRODUCT_FTDI_SERIAL_2232C: case USB_PRODUCT_FTDI_CFA_631: case USB_PRODUCT_FTDI_CFA_632: case USB_PRODUCT_FTDI_CFA_633: case USB_PRODUCT_FTDI_CFA_634: case USB_PRODUCT_FTDI_CFA_635: case USB_PRODUCT_FTDI_USBSERIAL: case USB_PRODUCT_FTDI_MX2_3: case USB_PRODUCT_FTDI_MX4_5: case USB_PRODUCT_FTDI_LK202: case USB_PRODUCT_FTDI_LK204: case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M: case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S: case USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U: case USB_PRODUCT_FTDI_EISCOU: case USB_PRODUCT_FTDI_UOPTBR: case USB_PRODUCT_FTDI_EMCU2D: case USB_PRODUCT_FTDI_PCMSFU: case USB_PRODUCT_FTDI_EMCU2H: + case USB_PRODUCT_FTDI_MAXSTREAM: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto bad; } break; case USB_VENDOR_INTREPIDCS: switch( uaa->product ){ case USB_PRODUCT_INTREPIDCS_VALUECAN: case USB_PRODUCT_INTREPIDCS_NEOVI: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto bad; } break; case USB_VENDOR_SIIG2: switch( uaa->product ){ case USB_PRODUCT_SIIG2_US2308: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto bad; } break; case USB_VENDOR_BBELECTRONICS: switch( uaa->product ){ case USB_PRODUCT_BBELECTRONICS_USOTL4: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto bad; } break; case USB_VENDOR_MELCO: switch( uaa->product ){ case USB_PRODUCT_MELCO_PCOPRS1: sc->sc_type = UFTDI_TYPE_8U232AM; sc->sc_hdrlen = 0; break; default: /* Can't happen */ goto bad; } break; default: /* Can't happen */ goto bad; } ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1; for (i = 0; i < id->bNumEndpoints; i++) { int addr, dir, attr; ed = usbd_interface2endpoint_descriptor(iface, i); if (ed == NULL) { device_printf(ucom->sc_dev, "could not read endpoint descriptor\n"); goto bad; } addr = ed->bEndpointAddress; dir = UE_GET_DIR(ed->bEndpointAddress); attr = ed->bmAttributes & UE_XFERTYPE; if (dir == UE_DIR_IN && attr == UE_BULK) ucom->sc_bulkin_no = addr; else if (dir == UE_DIR_OUT && attr == UE_BULK) ucom->sc_bulkout_no = addr; else { device_printf(ucom->sc_dev, "unexpected endpoint\n"); goto bad; } } if (ucom->sc_bulkin_no == -1) { device_printf(ucom->sc_dev, "Could not find data bulk in\n"); goto bad; } if (ucom->sc_bulkout_no == -1) { device_printf(ucom->sc_dev, "Could not find data bulk out\n"); goto bad; } ucom->sc_parent = sc; if (uaa->iface == NULL) ucom->sc_portno = FTDI_PIT_SIOA; else ucom->sc_portno = FTDI_PIT_SIOA + id->bInterfaceNumber; /* bulkin, bulkout set above */ ucom->sc_ibufsize = UFTDIIBUFSIZE; ucom->sc_obufsize = UFTDIOBUFSIZE - sc->sc_hdrlen; ucom->sc_ibufsizepad = UFTDIIBUFSIZE; ucom->sc_opkthdrlen = sc->sc_hdrlen; ucom->sc_callback = &uftdi_callback; #if 0 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, ucom->sc_udev, ucom->sc_dev); #endif DPRINTF(("uftdi: in=0x%x out=0x%x\n", ucom->sc_bulkin_no, ucom->sc_bulkout_no)); ucom_attach(&sc->sc_ucom); return 0; bad: DPRINTF(("uftdi_attach: ATTACH ERROR\n")); ucom->sc_dying = 1; return ENXIO; } #if 0 int uftdi_activate(device_t self, enum devact act) { struct uftdi_softc *sc = (struct uftdi_softc *)self; int rv = 0; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); case DVACT_DEACTIVATE: if (sc->sc_subdev != NULL) rv = config_deactivate(sc->sc_subdev); sc->sc_ucom.sc_dying = 1; break; } return (rv); } #endif static int uftdi_detach(device_t self) { struct uftdi_softc *sc = device_get_softc(self); int rv = 0; DPRINTF(("uftdi_detach: sc=%p\n", sc)); sc->sc_ucom.sc_dying = 1; rv = ucom_detach(&sc->sc_ucom); return rv; } static int uftdi_open(void *vsc, int portno) { struct uftdi_softc *sc = vsc; struct ucom_softc *ucom = &sc->sc_ucom; usb_device_request_t req; usbd_status err; struct termios t; DPRINTF(("uftdi_open: sc=%p\n", sc)); if (ucom->sc_dying) return (EIO); /* Perform a full reset on the device */ req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_RESET; USETW(req.wValue, FTDI_SIO_RESET_SIO); USETW(req.wIndex, portno); USETW(req.wLength, 0); err = usbd_do_request(ucom->sc_udev, &req, NULL); if (err) return (EIO); /* Set 9600 baud, 2 stop bits, no parity, 8 bits */ t.c_ospeed = 9600; t.c_cflag = CSTOPB | CS8; (void)uftdi_param(sc, portno, &t); /* Turn on RTS/CTS flow control */ req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_FLOW_CTRL; USETW(req.wValue, 0); USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, portno); USETW(req.wLength, 0); err = usbd_do_request(ucom->sc_udev, &req, NULL); if (err) return (EIO); return (0); } static void uftdi_read(void *vsc, int portno, u_char **ptr, u_int32_t *count) { struct uftdi_softc *sc = vsc; u_char msr, lsr; unsigned l; DPRINTFN(15,("uftdi_read: sc=%p, port=%d count=%d\n", sc, portno, *count)); while (*count > 0) { l = *count; if (l > 64) l = 64; msr = FTDI_GET_MSR(*ptr); lsr = FTDI_GET_LSR(*ptr); if (sc->sc_msr != msr || (sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK)) { DPRINTF(("uftdi_read: status change msr=0x%02x(0x%02x) " "lsr=0x%02x(0x%02x)\n", msr, sc->sc_msr, lsr, sc->sc_lsr)); sc->sc_msr = msr; sc->sc_lsr = lsr; ucom_status_change(&sc->sc_ucom); } if (l > 2) ucomrxchars(&sc->sc_ucom, (*ptr) + 2, l - 2); *ptr += l; *count -= l; } } static size_t uftdi_write(void *vsc, int portno, struct tty *tp, u_char *to, u_int32_t count) { struct uftdi_softc *sc = vsc; size_t l; DPRINTFN(10,("uftdi_write: sc=%p, port=%d tp=%p, count=%u\n", vsc, portno, tp, count)); /* Leave space for the length tag. */ l = ttydisc_getc(tp, to + sc->sc_hdrlen, count - sc->sc_hdrlen); if (l == 0) return (0); /* Make length tag. */ if (sc->sc_hdrlen > 0) *to = FTDI_OUT_TAG(l, portno); return (l + sc->sc_hdrlen); } static void uftdi_set(void *vsc, int portno, int reg, int onoff) { struct uftdi_softc *sc = vsc; struct ucom_softc *ucom = vsc; usb_device_request_t req; int ctl; DPRINTF(("uftdi_set: sc=%p, port=%d reg=%d onoff=%d\n", vsc, portno, reg, onoff)); switch (reg) { case UCOM_SET_DTR: ctl = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; break; case UCOM_SET_RTS: ctl = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; break; case UCOM_SET_BREAK: uftdi_break(sc, portno, onoff); return; default: return; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_MODEM_CTRL; USETW(req.wValue, ctl); USETW(req.wIndex, portno); USETW(req.wLength, 0); DPRINTFN(2,("uftdi_set: reqtype=0x%02x req=0x%02x value=0x%04x " "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest, UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength))); (void)usbd_do_request(ucom->sc_udev, &req, NULL); } static int uftdi_param(void *vsc, int portno, struct termios *t) { struct uftdi_softc *sc = vsc; struct ucom_softc *ucom = &sc->sc_ucom; usb_device_request_t req; usbd_status err; int rate=0, data, flow; DPRINTF(("uftdi_param: sc=%p\n", sc)); if (ucom->sc_dying) return (EIO); switch (sc->sc_type) { case UFTDI_TYPE_SIO: switch (t->c_ospeed) { case 300: rate = ftdi_sio_b300; break; case 600: rate = ftdi_sio_b600; break; case 1200: rate = ftdi_sio_b1200; break; case 2400: rate = ftdi_sio_b2400; break; case 4800: rate = ftdi_sio_b4800; break; case 9600: rate = ftdi_sio_b9600; break; case 19200: rate = ftdi_sio_b19200; break; case 38400: rate = ftdi_sio_b38400; break; case 57600: rate = ftdi_sio_b57600; break; case 115200: rate = ftdi_sio_b115200; break; default: return (EINVAL); } break; case UFTDI_TYPE_8U232AM: if (uftdi_8u232am_getrate(t->c_ospeed, &rate) == -1) return (EINVAL); break; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_BAUD_RATE; USETW(req.wValue, rate); USETW(req.wIndex, portno); USETW(req.wLength, 0); DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x " "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest, UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength))); err = usbd_do_request(ucom->sc_udev, &req, NULL); if (err) return (EIO); if (ISSET(t->c_cflag, CSTOPB)) data = FTDI_SIO_SET_DATA_STOP_BITS_2; else data = FTDI_SIO_SET_DATA_STOP_BITS_1; if (ISSET(t->c_cflag, PARENB)) { if (ISSET(t->c_cflag, PARODD)) data |= FTDI_SIO_SET_DATA_PARITY_ODD; else data |= FTDI_SIO_SET_DATA_PARITY_EVEN; } else data |= FTDI_SIO_SET_DATA_PARITY_NONE; switch (ISSET(t->c_cflag, CSIZE)) { case CS5: data |= FTDI_SIO_SET_DATA_BITS(5); break; case CS6: data |= FTDI_SIO_SET_DATA_BITS(6); break; case CS7: data |= FTDI_SIO_SET_DATA_BITS(7); break; case CS8: data |= FTDI_SIO_SET_DATA_BITS(8); break; } sc->last_lcr = data; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, data); USETW(req.wIndex, portno); USETW(req.wLength, 0); DPRINTFN(2,("uftdi_param: reqtype=0x%02x req=0x%02x value=0x%04x " "index=0x%04x len=%d\n", req.bmRequestType, req.bRequest, UGETW(req.wValue), UGETW(req.wIndex), UGETW(req.wLength))); err = usbd_do_request(ucom->sc_udev, &req, NULL); if (err) return (EIO); if (ISSET(t->c_cflag, CRTSCTS)) { flow = FTDI_SIO_RTS_CTS_HS; USETW(req.wValue, 0); } else if (ISSET(t->c_iflag, IXON|IXOFF)) { flow = FTDI_SIO_XON_XOFF_HS; USETW2(req.wValue, t->c_cc[VSTOP], t->c_cc[VSTART]); } else { flow = FTDI_SIO_DISABLE_FLOW_CTRL; USETW(req.wValue, 0); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_FLOW_CTRL; USETW2(req.wIndex, flow, portno); USETW(req.wLength, 0); err = usbd_do_request(ucom->sc_udev, &req, NULL); if (err) return (EIO); return (0); } void uftdi_get_status(void *vsc, int portno, u_char *lsr, u_char *msr) { struct uftdi_softc *sc = vsc; DPRINTF(("uftdi_status: msr=0x%02x lsr=0x%02x\n", sc->sc_msr, sc->sc_lsr)); if (msr != NULL) *msr = sc->sc_msr; if (lsr != NULL) *lsr = sc->sc_lsr; } void uftdi_break(void *vsc, int portno, int onoff) { struct uftdi_softc *sc = vsc; struct ucom_softc *ucom = vsc; usb_device_request_t req; int data; DPRINTF(("uftdi_break: sc=%p, port=%d onoff=%d\n", vsc, portno, onoff)); if (onoff) { data = sc->last_lcr | FTDI_SIO_SET_BREAK; } else { data = sc->last_lcr; } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, data); USETW(req.wIndex, portno); USETW(req.wLength, 0); (void)usbd_do_request(ucom->sc_udev, &req, NULL); } static int uftdi_8u232am_getrate(speed_t speed, int *rate) { /* Table of the nearest even powers-of-2 for values 0..15. */ static const unsigned char roundoff[16] = { 0, 2, 2, 4, 4, 4, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, }; unsigned int d, freq; int result; if (speed <= 0) return (-1); /* Special cases for 2M and 3M. */ if (speed >= 3000000 * 100 / 103 && speed <= 3000000 * 100 / 97) { result = 0; goto done; } if (speed >= 2000000 * 100 / 103 && speed <= 2000000 * 100 / 97) { result = 1; goto done; } d = (FTDI_8U232AM_FREQ << 4) / speed; d = (d & ~15) + roundoff[d & 15]; if (d < FTDI_8U232AM_MIN_DIV) d = FTDI_8U232AM_MIN_DIV; else if (d > FTDI_8U232AM_MAX_DIV) d = FTDI_8U232AM_MAX_DIV; /* * Calculate the frequency needed for d to exactly divide down * to our target speed, and check that the actual frequency is * within 3% of this. */ freq = speed * d; if (freq < (quad_t)(FTDI_8U232AM_FREQ << 4) * 100 / 103 || freq > (quad_t)(FTDI_8U232AM_FREQ << 4) * 100 / 97) return (-1); /* * Pack the divisor into the resultant value. The lower * 14-bits hold the integral part, while the upper 2 bits * encode the fractional component: either 0, 0.5, 0.25, or * 0.125. */ result = d >> 4; if (d & 8) result |= 0x4000; else if (d & 4) result |= 0x8000; else if (d & 2) result |= 0xc000; done: *rate = result; return (0); } static device_method_t uftdi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uftdi_match), DEVMETHOD(device_attach, uftdi_attach), DEVMETHOD(device_detach, uftdi_detach), { 0, 0 } }; static driver_t uftdi_driver = { "ucom", uftdi_methods, sizeof (struct uftdi_softc) }; DRIVER_MODULE(uftdi, uhub, uftdi_driver, ucom_devclass, usbd_driver_load, 0); MODULE_DEPEND(uftdi, usb, 1, 1, 1); MODULE_DEPEND(uftdi, ucom,UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER); Index: projects/cambria/sys/dev/usb/usbdevs =================================================================== --- projects/cambria/sys/dev/usb/usbdevs (revision 186459) +++ projects/cambria/sys/dev/usb/usbdevs (revision 186460) @@ -1,2496 +1,2503 @@ $FreeBSD$ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ /*- * Copyright (c) 1998-2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * List of known USB vendors * * USB.org publishes a VID list of USB-IF member companies at * http://www.usb.org/developers/tools * Note that it does not show companies that have obtained a Vendor ID * without becoming full members. * * Please note that these IDs do not do anything. Adding an ID here and * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name * available to the source code and does not change any functionality, nor * does it make your device available to a specific driver. * It will however make the descriptive string available if a device does not * provide the string itself. * * After adding a vendor ID VNDR and a product ID PRDCT you will have the * following extra defines: * #define USB_VENDOR_VNDR 0x???? * #define USB_PRODUCT_VNDR_PRDCT 0x???? * * You may have to add these defines to the respective probe routines to * make the device recognised by the appropriate device driver. */ vendor UNKNOWN1 0x0053 Unknown vendor vendor UNKNOWN2 0x0105 Unknown vendor vendor EGALAX2 0x0123 eGalax, Inc. vendor HUMAX 0x02ad HUMAX vendor LTS 0x0386 LTS vendor BWCT 0x03da Bernd Walter Computer Technology vendor AOX 0x03e8 AOX vendor THESYS 0x03e9 Thesys vendor DATABROADCAST 0x03ea Data Broadcasting vendor ATMEL 0x03eb Atmel vendor IWATSU 0x03ec Iwatsu America vendor MITSUMI 0x03ee Mitsumi vendor HP 0x03f0 Hewlett Packard vendor GENOA 0x03f1 Genoa vendor OAK 0x03f2 Oak vendor ADAPTEC 0x03f3 Adaptec vendor DIEBOLD 0x03f4 Diebold vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical vendor EPSONIMAGING 0x03f8 Epson Imaging vendor KEYTRONIC 0x03f9 KeyTronic vendor OPTI 0x03fb OPTi vendor ELITEGROUP 0x03fc Elitegroup vendor XILINX 0x03fd Xilinx vendor FARALLON 0x03fe Farallon Communications vendor NATIONAL 0x0400 National Semiconductor vendor NATIONALREG 0x0401 National Registry vendor ACERLABS 0x0402 Acer Labs vendor FTDI 0x0403 Future Technology Devices vendor NCR 0x0404 NCR vendor SYNOPSYS2 0x0405 Synopsys vendor FUJITSUICL 0x0406 Fujitsu-ICL vendor FUJITSU2 0x0407 Fujitsu Personal Systems vendor QUANTA 0x0408 Quanta vendor NEC 0x0409 NEC vendor KODAK 0x040a Eastman Kodak vendor WELTREND 0x040b Weltrend vendor VIA 0x040d VIA vendor MCCI 0x040e MCCI vendor MELCO 0x0411 Melco vendor LEADTEK 0x0413 Leadtek vendor WINBOND 0x0416 Winbond vendor PHOENIX 0x041a Phoenix vendor CREATIVE 0x041e Creative Labs vendor NOKIA 0x0421 Nokia vendor ADI 0x0422 ADI Systems vendor CATC 0x0423 Computer Access Technology vendor SMC2 0x0424 Standard Microsystems vendor MOTOROLA_HK 0x0425 Motorola HK vendor GRAVIS 0x0428 Advanced Gravis Computer vendor CIRRUSLOGIC 0x0429 Cirrus Logic vendor INNOVATIVE 0x042c Innovative Semiconductors vendor MOLEX 0x042f Molex vendor SUN 0x0430 Sun Microsystems vendor UNISYS 0x0432 Unisys vendor TAUGA 0x0436 Taugagreining HF vendor AMD 0x0438 Advanced Micro Devices vendor LEXMARK 0x043d Lexmark International vendor LG 0x043e LG Electronics vendor NANAO 0x0440 NANAO vendor GATEWAY 0x0443 Gateway 2000 vendor NMB 0x0446 NMB vendor ALPS 0x044e Alps Electric vendor THRUST 0x044f Thrustmaster vendor TI 0x0451 Texas Instruments vendor ANALOGDEVICES 0x0456 Analog Devices vendor SIS 0x0457 Silicon Integrated Systems Corp. vendor KYE 0x0458 KYE Systems vendor DIAMOND2 0x045a Diamond (Supra) vendor RENESAS 0x045b Renesas vendor MICROSOFT 0x045e Microsoft vendor PRIMAX 0x0461 Primax Electronics vendor MGE 0x0463 MGE UPS Systems vendor AMP 0x0464 AMP vendor CHERRY 0x046a Cherry Mikroschalter vendor MEGATRENDS 0x046b American Megatrends vendor LOGITECH 0x046d Logitech vendor BTC 0x046e Behavior Tech. Computer vendor PHILIPS 0x0471 Philips vendor SUN2 0x0472 Sun Microsystems (offical) vendor SANYO 0x0474 Sanyo Electric vendor SEAGATE 0x0477 Seagate vendor CONNECTIX 0x0478 Connectix vendor SEMTECH 0x047a Semtech vendor KENSINGTON 0x047d Kensington vendor LUCENT 0x047e Lucent vendor PLANTRONICS 0x047f Plantronics vendor KYOCERA 0x0482 Kyocera Wireless Corp. vendor STMICRO 0x0483 STMicroelectronics vendor FOXCONN 0x0489 Foxconn vendor YAMAHA 0x0499 YAMAHA vendor COMPAQ 0x049f Compaq vendor HITACHI 0x04a4 Hitachi vendor ACERP 0x04a5 Acer Peripherals vendor DAVICOM 0x04a6 Davicom vendor VISIONEER 0x04a7 Visioneer vendor CANON 0x04a9 Canon vendor NIKON 0x04b0 Nikon vendor PAN 0x04b1 Pan International vendor IBM 0x04b3 IBM vendor CYPRESS 0x04b4 Cypress Semiconductor vendor ROHM 0x04b5 ROHM vendor COMPAL 0x04b7 Compal vendor EPSON 0x04b8 Seiko Epson vendor RAINBOW 0x04b9 Rainbow Technologies vendor IODATA 0x04bb I-O Data vendor TDK 0x04bf TDK vendor 3COMUSR 0x04c1 U.S. Robotics vendor METHODE 0x04c2 Methode Electronics Far East vendor MAXISWITCH 0x04c3 Maxi Switch vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research vendor FUJITSU 0x04c5 Fujitsu vendor TOSHIBAAM 0x04c6 Toshiba America vendor MICROMACRO 0x04c7 Micro Macro Technologies vendor KONICA 0x04c8 Konica vendor LITEON 0x04ca Lite-On Technology vendor FUJIPHOTO 0x04cb Fuji Photo Film vendor PHILIPSSEMI 0x04cc Philips Semiconductors vendor TATUNG 0x04cd Tatung Co. Of America vendor SCANLOGIC 0x04ce ScanLogic vendor MYSON 0x04cf Myson Technology vendor DIGI2 0x04d0 Digi vendor ITTCANON 0x04d1 ITT Canon vendor ALTEC 0x04d2 Altec Lansing vendor LSI 0x04d4 LSI vendor MENTORGRAPHICS 0x04d6 Mentor Graphics vendor ITUNERNET 0x04d8 I-Tuner Networks vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc. vendor PANASONIC 0x04da Panasonic (Matsushita) vendor HUANHSIN 0x04dc Huan Hsin vendor SHARP 0x04dd Sharp vendor IIYAMA 0x04e1 Iiyama vendor SHUTTLE 0x04e6 Shuttle Technology vendor ELO 0x04e7 Elo TouchSystems vendor SAMSUNG 0x04e8 Samsung Electronics vendor NORTHSTAR 0x04eb Northstar vendor TOKYOELECTRON 0x04ec Tokyo Electron vendor ANNABOOKS 0x04ed Annabooks vendor JVC 0x04f1 JVC vendor CHICONY 0x04f2 Chicony Electronics vendor ELAN 0x04f3 Elan vendor NEWNEX 0x04f7 Newnex vendor BROTHER 0x04f9 Brother Industries vendor DALLAS 0x04fa Dallas Semiconductor vendor AIPTEK2 0x04fc AIPTEK International vendor PFU 0x04fe PFU vendor FUJIKURA 0x0501 Fujikura/DDK vendor ACER 0x0502 Acer vendor 3COM 0x0506 3Com vendor HOSIDEN 0x0507 Hosiden Corporation vendor AZTECH 0x0509 Aztech Systems vendor BELKIN 0x050d Belkin Components vendor KAWATSU 0x050f Kawatsu Semiconductor vendor FCI 0x0514 FCI vendor LONGWELL 0x0516 Longwell vendor COMPOSITE 0x0518 Composite vendor STAR 0x0519 Star Micronics vendor APC 0x051d American Power Conversion vendor SCIATLANTA 0x051e Scientific Atlanta vendor TSM 0x0520 TSM vendor CONNECTEK 0x0522 Advanced Connectek USA vendor NETCHIP 0x0525 NetChip Technology vendor ALTRA 0x0527 ALTRA vendor ATI 0x0528 ATI Technologies vendor AKS 0x0529 Aladdin Knowledge Systems vendor TEKOM 0x052b Tekom vendor CANONDEV 0x052c Canon vendor WACOMTECH 0x0531 Wacom vendor INVENTEC 0x0537 Inventec vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG vendor SYNOPSYS 0x053f Synopsys vendor UNIACCESS 0x0540 Universal Access vendor VIEWSONIC 0x0543 ViewSonic vendor XIRLINK 0x0545 Xirlink vendor ANCHOR 0x0547 Anchor Chips vendor SONY 0x054c Sony vendor FUJIXEROX 0x0550 Fuji Xerox vendor VISION 0x0553 VLSI Vision vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems vendor ATEN 0x0557 ATEN International vendor SAMSUNG2 0x055d Samsung Electronics vendor MUSTEK 0x055f Mustek Systems vendor TELEX 0x0562 Telex Communications vendor CHINON 0x0564 Chinon vendor PERACOM 0x0565 Peracom Networks vendor ALCOR2 0x0566 Alcor Micro vendor XYRATEX 0x0567 Xyratex vendor WACOM 0x056a WACOM vendor ETEK 0x056c e-TEK Labs vendor EIZO 0x056d EIZO vendor ELECOM 0x056e Elecom vendor CONEXANT 0x0572 Conexant vendor HAUPPAUGE 0x0573 Hauppauge Computer Works vendor BAFO 0x0576 BAFO/Quality Computer Accessories vendor YEDATA 0x057b Y-E Data vendor AVM 0x057c AVM vendor QUICKSHOT 0x057f Quickshot vendor ROLAND 0x0582 Roland vendor ROCKFIRE 0x0583 Rockfire vendor RATOC 0x0584 RATOC Systems vendor ZYXEL 0x0586 ZyXEL Communication vendor INFINEON 0x058b Infineon vendor MICREL 0x058d Micrel vendor ALCOR 0x058f Alcor Micro vendor OMRON 0x0590 OMRON vendor ZORAN 0x0595 Zoran Microelectronics vendor NIIGATA 0x0598 Niigata vendor IOMEGA 0x059b Iomega vendor ATREND 0x059c A-Trend Technology vendor AID 0x059d Advanced Input Devices vendor LACIE 0x059f LaCie vendor FUJIFILM 0x05a2 Fuji Film vendor ARC 0x05a3 ARC vendor ORTEK 0x05a4 Ortek vendor BOSE 0x05a7 Bose vendor OMNIVISION 0x05a9 OmniVision vendor INSYSTEM 0x05ab In-System Design vendor APPLE 0x05ac Apple Computer vendor YCCABLE 0x05ad Y.C. Cable vendor DIGITALPERSONA 0x05ba DigitalPersona vendor 3G 0x05bc 3G Green Green Globe vendor RAFI 0x05bd RAFI vendor TYCO 0x05be Tyco vendor KAWASAKI 0x05c1 Kawasaki vendor DIGI 0x05c5 Digi International vendor QUALCOMM2 0x05c6 Qualcomm vendor QTRONIX 0x05c7 Qtronix vendor FOXLINK 0x05c8 Foxlink vendor RICOH 0x05ca Ricoh vendor ELSA 0x05cc ELSA vendor SCIWORX 0x05ce sci-worx vendor BRAINBOXES 0x05d1 Brainboxes Limited vendor ULTIMA 0x05d8 Ultima vendor AXIOHM 0x05d9 Axiohm Transaction Solutions vendor MICROTEK 0x05da Microtek vendor SUNTAC 0x05db SUN Corporation vendor LEXAR 0x05dc Lexar Media vendor ADDTRON 0x05dd Addtron vendor SYMBOL 0x05e0 Symbol Technologies vendor SYNTEK 0x05e1 Syntek vendor GENESYS 0x05e3 Genesys Logic vendor FUJI 0x05e5 Fuji Electric vendor KEITHLEY 0x05e6 Keithley Instruments vendor EIZONANAO 0x05e7 EIZO Nanao vendor KLSI 0x05e9 Kawasaki LSI vendor FFC 0x05eb FFC vendor ANKO 0x05ef Anko Electronic vendor PIENGINEERING 0x05f3 P.I. Engineering vendor AOC 0x05f6 AOC International vendor CHIC 0x05fe Chic Technology vendor BARCO 0x0600 Barco Display Systems vendor BRIDGE 0x0607 Bridge Information vendor SOLIDYEAR 0x060b Solid Year vendor BIORAD 0x0614 Bio-Rad Laboratories vendor MACALLY 0x0618 Macally vendor ACTLABS 0x061c Act Labs vendor ALARIS 0x0620 Alaris vendor APEX 0x0624 Apex vendor CREATIVE3 0x062a Creative Labs vendor VIVITAR 0x0636 Vivitar vendor GUNZE 0x0637 Gunze Electronics USA vendor AVISION 0x0638 Avision vendor TEAC 0x0644 TEAC vendor SGI 0x065e Silicon Graphics vendor SANWASUPPLY 0x0663 Sanwa Supply vendor LINKSYS 0x066b Linksys vendor ACERSA 0x066e Acer Semiconductor America vendor SIGMATEL 0x066f Sigmatel vendor DRAYTEK 0x0675 DrayTek vendor AIWA 0x0677 Aiwa vendor ACARD 0x0678 ACARD Technology vendor PROLIFIC 0x067b Prolific Technology vendor SIEMENS 0x067c Siemens vendor AVANCELOGIC 0x0680 Avance Logic vendor SIEMENS2 0x0681 Siemens vendor MINOLTA 0x0686 Minolta vendor CHPRODUCTS 0x068e CH Products vendor HAGIWARA 0x0693 Hagiwara Sys-Com vendor CTX 0x0698 Chuntex vendor ASKEY 0x069a Askey Computer vendor SAITEK 0x06a3 Saitek vendor ALCATELT 0x06b9 Alcatel Telecom vendor AGFA 0x06bd AGFA-Gevaert vendor ASIAMD 0x06be Asia Microelectronic Development vendor BIZLINK 0x06c4 Bizlink International vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc. vendor AASHIMA 0x06d6 Aashima Technology vendor MULTITECH 0x06e0 MultiTech vendor ADS 0x06e1 ADS Technologies vendor ALCATELM 0x06e4 Alcatel Microelectronics vendor SIRIUS 0x06ea Sirius Technologies vendor GUILLEMOT 0x06f8 Guillemot vendor BOSTON 0x06fd Boston Acoustics vendor SMC 0x0707 Standard Microsystems vendor PUTERCOM 0x0708 Putercom vendor MCT 0x0711 MCT vendor IMATION 0x0718 Imation vendor SONYERICSSON 0x0731 Sony Ericsson vendor EICON 0x0734 Eicon Networks vendor SYNTECH 0x0745 Syntech Information vendor DIGITALSTREAM 0x074e Digital Stream vendor AUREAL 0x0755 Aureal Semiconductor vendor MIDIMAN 0x0763 Midiman vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc. vendor SURECOM 0x0769 Surecom Technology vendor LINKSYS2 0x077b Linksys vendor GRIFFIN 0x077d Griffin Technology vendor SANDISK 0x0781 SanDisk vendor JENOPTIK 0x0784 Jenoptik vendor LOGITEC 0x0789 Logitec vendor BRIMAX 0x078e Brimax vendor AXIS 0x0792 Axis Communications vendor ABL 0x0794 ABL Electronics vendor SAGEM 0x079b Sagem vendor SUNCOMM 0x079c Sun Communications, Inc. vendor ALFADATA 0x079d Alfadata Computer vendor NATIONALTECH 0x07a2 National Technical Systems vendor ONNTO 0x07a3 Onnto vendor BE 0x07a4 Be vendor ADMTEK 0x07a6 ADMtek vendor COREGA 0x07aa Corega vendor FREECOM 0x07ab Freecom vendor MICROTECH 0x07af Microtech vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola) vendor OLYMPUS 0x07b4 Olympus vendor ABOCOM 0x07b8 AboCom Systems vendor KEISOKUGIKEN 0x07c1 Keisokugiken vendor ONSPEC 0x07c4 OnSpec vendor APG 0x07c5 APG Cash Drawer vendor BUG 0x07c8 B.U.G. vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International vendor AVERMEDIA 0x07ca AVerMedia Technologies vendor SIIG 0x07cc SIIG vendor CASIO 0x07cf CASIO vendor DLINK2 0x07d1 D-Link vendor APTIO 0x07d2 Aptio Products vendor ARASAN 0x07da Arasan Chip Systems vendor ALLIEDCABLE 0x07e6 Allied Cable vendor STSN 0x07ef STSN vendor CENTURY 0x07f7 Century Corp vendor ZOOM 0x0803 Zoom Telephonics vendor PCS 0x0810 Personal Communication Systems vendor BROADLOGIC 0x0827 BroadLogic vendor HANDSPRING 0x082d Handspring vendor PALM 0x0830 Palm Computing vendor SOURCENEXT 0x0833 SOURCENEXT vendor ACTIONSTAR 0x0835 Action Star Enterprise vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin vendor ACCTON 0x083a Accton Technology vendor DIAMOND 0x0841 Diamond vendor NETGEAR 0x0846 BayNETGEAR vendor TOPRE 0x0853 Topre Corporation vendor ACTIVEWIRE 0x0854 ActiveWire vendor BBELECTRONICS 0x0856 B&B Electronics vendor PORTGEAR 0x085a PortGear vendor NETGEAR2 0x0864 Netgear vendor SYSTEMTALKS 0x086e System Talks vendor METRICOM 0x0870 Metricom vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America vendor JATON 0x087d Jaton vendor APT 0x0880 APT Technologies vendor BOCARESEARCH 0x0885 Boca Research vendor ANDREA 0x08a8 Andrea Electronics vendor BURRBROWN 0x08bb Burr-Brown Japan vendor 2WIRE 0x08c8 2Wire vendor AIPTEK 0x08ca AIPTEK International vendor SMARTBRIDGES 0x08d1 SmartBridges vendor BILLIONTON 0x08dd Billionton Systems vendor EXTENDED 0x08e9 Extended Systems vendor MSYSTEMS 0x08ec M-Systems vendor AUTHENTEC 0x08ff AuthenTec vendor AUDIOTECHNICA 0x0909 Audio-Technica vendor TRUMPION 0x090a Trumpion Microelectronics vendor FEIYA 0x090c Feiya vendor ALATION 0x0910 Alation Systems vendor GLOBESPAN 0x0915 Globespan vendor CONCORDCAMERA 0x0919 Concord Camera vendor GARMIN 0x091e Garmin International vendor GOHUBS 0x0921 GoHubs vendor XEROX 0x0924 Xerox vendor BIOMETRIC 0x0929 American Biometric Company vendor TOSHIBA 0x0930 Toshiba vendor PLEXTOR 0x093b Plextor vendor INTREPIDCS 0x093c Intrepid vendor YANO 0x094f Yano vendor KINGSTON 0x0951 Kingston Technology vendor BLUEWATER 0x0956 BlueWater Systems vendor AGILENT 0x0957 Agilent Technologies vendor GUDE 0x0959 Gude ADS vendor PORTSMITH 0x095a Portsmith vendor ACERW 0x0967 Acer vendor ADIRONDACK 0x0976 Adirondack Wire & Cable vendor BECKHOFF 0x0978 Beckhoff vendor MINDSATWORK 0x097a Minds At Work vendor POINTCHIPS 0x09a6 PointChips vendor INTERSIL 0x09aa Intersil vendor ALTIUS 0x09b3 Altius Solutions vendor ARRIS 0x09c1 Arris Interactive vendor ACTIVCARD 0x09c3 ACTIVCARD vendor ACTISYS 0x09c4 ACTiSYS vendor NOVATEL2 0x09d7 Novatel Wireless vendor AFOURTECH 0x09da A-FOUR TECH vendor AIMEX 0x09dc AIMEX vendor ADDONICS 0x09df Addonics Technologies vendor AKAI 0x09e8 AKAI professional M.I. vendor ARESCOM 0x09f5 ARESCOM vendor BAY 0x09f9 Bay Associates vendor ALTERA 0x09fb Altera vendor CSR 0x0a12 Cambridge Silicon Radio vendor TREK 0x0a16 Trek Technology vendor ASAHIOPTICAL 0x0a17 Asahi Optical vendor BOCASYSTEMS 0x0a43 Boca Systems vendor SHANTOU 0x0a46 ShanTou vendor MEDIAGEAR 0x0a48 MediaGear vendor BROADCOM 0x0a5c Broadcom vendor GREENHOUSE 0x0a6b GREENHOUSE vendor GEOCAST 0x0a79 Geocast Network Systems vendor IDQUANTIQUE 0x0aba id Quantique vendor ZYDAS 0x0ace Zydas Technology Corporation vendor NEODIO 0x0aec Neodio vendor OPTION 0x0af0 Option N.V: vendor ASUS 0x0b05 ASUSTeK Computer vendor TODOS 0x0b0c Todos Data System vendor SIIG2 0x0b39 SIIG vendor TEKRAM 0x0b3b Tekram Technology vendor HAL 0x0b41 HAL Corporation vendor EMS 0x0b43 EMS Production vendor NEC2 0x0b62 NEC vendor ATI2 0x0b6f ATI vendor ZEEVO 0x0b7a Zeevo, Inc. vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc. vendor ASIX 0x0b95 ASIX Electronics vendor O2MICRO 0x0b97 O2 Micro, Inc. vendor USR 0x0baf U.S. Robotics vendor AMBIT 0x0bb2 Ambit Microsystems vendor HTC 0x0bb4 HTC vendor REALTEK 0x0bda Realtek vendor ADDONICS2 0x0bf6 Addonics Technology vendor FSC 0x0bf8 Fujitsu Siemens Computers vendor AGATE 0x0c08 Agate Technologies vendor DMI 0x0c0b DMI vendor CHICONY2 0x0c45 Chicony vendor SEALEVEL 0x0c52 Sealevel System vendor LUWEN 0x0c76 Luwen vendor KYOCERA2 0x0c88 Kyocera Wireless Corp. vendor ZCOM 0x0cde Z-Com vendor ATHEROS2 0x0cf3 Atheros Communications vendor TANGTOP 0x0d3d Tangtop vendor SMC3 0x0d5c Standard Microsystems vendor ADDON 0x0d7d Add-on Technology vendor ACDC 0x0d7e American Computer & Digital Components vendor ABC 0x0d8c ABC vendor CONCEPTRONIC 0x0d8e Conceptronic vendor SKANHEX 0x0d96 Skanhex Technology, Inc. vendor MSI 0x0db0 Micro Star International vendor ELCON 0x0db7 ELCON Systemtechnik vendor NETAC 0x0dd8 Netac vendor SITECOMEU 0x0df6 Sitecom Europe vendor MOBILEACTION 0x0df7 Mobile Action vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia vendor HAWKING 0x0e66 Hawking vendor FOSSIL 0x0e67 Fossil, Inc vendor GMATE 0x0e7e G.Mate, Inc vendor OTI 0x0ea0 Ours Technology +vendor YISO 0x0eab Yiso Wireless Co. vendor PILOTECH 0x0eaf Pilotech vendor NOVATECH 0x0eb0 NovaTech vendor ITEGNO 0x0eba iTegno vendor WINMAXGROUP 0x0ed1 WinMaxGroup vendor TOD 0x0ede TOD vendor EGALAX 0x0eef eGalax, Inc. vendor AIRPRIME 0x0f3d AirPrime, Inc. vendor MICROTUNE 0x0f4d Microtune vendor VTECH 0x0f88 VTech vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH vendor RIM 0x0fca Research In Motion vendor DYNASTREAM 0x0fcf Dynastream Innovations vendor QUALCOMM 0x1004 Qualcomm vendor DESKNOTE 0x1019 Desknote vendor GIGABYTE 0x1044 GIGABYTE vendor WESTERN 0x1058 Western Digital vendor MOTOROLA 0x1063 Motorola vendor CCYU 0x1065 CCYU Technology vendor CURITEL 0x106c Curitel Communications Inc vendor SILABS2 0x10a6 SILABS2 vendor USI 0x10ab USI vendor PLX 0x10b5 PLX vendor ASANTE 0x10bd Asante vendor SILABS 0x10c4 Silicon Labs vendor ANALOG 0x1110 Analog Devices vendor TENX 0x1130 Ten X Technology, Inc. vendor ISSC 0x1131 Integrated System Solution Corp. vendor JRC 0x1145 Japan Radio Company vendor SPHAIRON 0x114b Sphairon Access Systems GmbH vendor DELORME 0x1163 DeLorme vendor SERVERWORKS 0x1166 ServerWorks vendor ACERCM 0x1189 Acer Communications & Multimedia vendor SIERRA 0x1199 Sierra Wireless vendor TOPFIELD 0x11db Topfield Co., Ltd vendor SIEMENS3 0x11f5 Siemens vendor PROLIFIC2 0x11f6 Prolific vendor ALCATEL 0x11f7 Alcatel vendor UNKNOWN3 0x1233 Unknown vendor vendor TSUNAMI 0x1241 Tsunami vendor PHEENET 0x124a Pheenet vendor TARGUS 0x1267 Targus vendor TWINMOS 0x126f TwinMOS vendor TENDA 0x1286 Tenda vendor CREATIVE2 0x1292 Creative Labs vendor BELKIN2 0x1293 Belkin Components vendor CYBERTAN 0x129b CyberTAN Technology vendor HUAWEI 0x12d1 Huawei Technologies vendor ARANEUS 0x12d8 Araneus Information Systems vendor TAPWAVE 0x12ef Tapwave vendor AINCOMM 0x12fd Aincomm vendor MOBILITY 0x1342 Mobility vendor DICKSMITH 0x1371 Dick Smith Electronics vendor NETGEAR3 0x1385 Netgear vendor BALTECH 0x13ad Baltech vendor CISCOLINKSYS 0x13b1 Cisco-Linksys vendor SHARK 0x13d2 Shark vendor NOVATEL 0x1410 Novatel Wireless vendor MERLIN 0x1416 Merlin vendor WISTRONNEWEB 0x1435 Wistron NeWeb vendor RADIOSHACK 0x1453 Radio Shack vendor HUAWEI3COM 0x1472 Huawei-3Com vendor SILICOM 0x1485 Silicom vendor RALINK 0x148f Ralink Technology vendor IMAGINATION 0x149a Imagination Technologies vendor CONCEPTRONIC2 0x14b2 Conceptronic vendor PLANEX3 0x14ea Planex Communications vendor SILICONPORTALS 0x1527 Silicon Portals vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd. vendor UBLOX 0x1546 U-blox vendor PNY 0x154b PNY vendor OQO 0x1557 OQO vendor UMEDIA 0x157e U-MEDIA Communications vendor FIBERLINE 0x1582 Fiberline vendor SPARKLAN 0x15a9 SparkLAN vendor SOHOWARE 0x15e8 SOHOware vendor UMAX 0x1606 UMAX Data Systems vendor INSIDEOUT 0x1608 Inside Out Networks vendor GOODWAY 0x1631 Good Way Technology vendor ENTREGA 0x1645 Entrega vendor ACTIONTEC 0x1668 Actiontec Electronics vendor ATHEROS 0x168c Atheros Communications vendor GIGASET 0x1690 Gigaset vendor GLOBALSUN 0x16ab Global Sun Technology vendor ANYDATA 0x16d5 AnyDATA Corporation vendor JABLOTRON 0x16d6 Jablotron vendor CMOTECH 0x16d8 CMOTECH Co., Ltd. vendor AXESSTEL 0x1726 Axesstel Co., Ltd. vendor LINKSYS4 0x1737 Linksys vendor SENAO 0x1740 Senao vendor METAGEEK 0x1781 MetaGeek vendor AMIT 0x18c5 AMIT vendor QCOM 0x18e8 Qcom vendor LINKSYS3 0x1915 Linksys vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated vendor STELERA 0x1a8d Stelera Wireless vendor DLINK 0x2001 D-Link vendor PLANEX2 0x2019 Planex Communications vendor ERICSSON 0x2282 Ericsson vendor MOTOROLA2 0x22b8 Motorola vendor TRIPPLITE 0x2478 Tripp-Lite vendor HIROSE 0x2631 Hirose Electric vendor NHJ 0x2770 NHJ vendor PLANEX 0x2c02 Planex Communications vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd vendor AEI 0x3334 AEI vendor HANK 0x3353 Hank Connection vendor PQI 0x3538 PQI vendor DAISY 0x3579 Daisy Technology vendor NI 0x3923 National Instruments vendor MICRONET 0x3980 Micronet Communications vendor IODATA2 0x40bb I-O Data vendor IRIVER 0x4102 iRiver vendor DELL 0x413c Dell vendor WCH 0x4348 QinHeng Electronics vendor ACEECA 0x4766 Aceeca vendor AVERATEC 0x50c2 Averatec vendor SWEEX 0x5173 Sweex vendor ONSPEC2 0x55aa OnSpec Electronic Inc. vendor ZINWELL 0x5a57 Zinwell vendor SITECOM 0x6189 Sitecom vendor ARKMICRO 0x6547 Arkmicro Technologies Inc. vendor 3COM2 0x6891 3Com vendor INTEL 0x8086 Intel vendor SITECOM2 0x9016 Sitecom vendor MOSCHIP 0x9710 MosChip Semiconductor vendor 3COM3 0xa727 3Com vendor HP2 0xf003 Hewlett Packard vendor USRP 0xfffe GNU Radio USRP /* * List of known products. Grouped by vendor. */ /* 3Com products */ product 3COM HOMECONN 0x009d HomeConnect Camera product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter product 3COM 3C460 0x11f8 HomeConnect 3C460 product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro product 3COM 3C460B 0x4601 HomeConnect 3C460B product 3COM2 3CRUSB10075 0xa727 3CRUSB10075 product 3COM3 AR5523_1 0x6893 AR5523 product 3COM3 AR5523_2 0x6895 AR5523 product 3COM3 AR5523_3 0x6897 AR5523 product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro /* AboCom products */ product ABOCOM XX1 0x110c XX1 product ABOCOM XX2 0x200c XX2 product ABOCOM URE450 0x4000 URE450 Ethernet Adapter product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter product ABOCOM XX4 0x4004 XX4 product ABOCOM XX5 0x4007 XX5 product ABOCOM XX6 0x400b XX6 product ABOCOM XX7 0x400c XX7 product ABOCOM RTL8151 0x401a RTL8151 product ABOCOM XX8 0x4102 XX8 product ABOCOM XX9 0x4104 XX9 product ABOCOM UF200 0x420a UF200 Ethernet product ABOCOM WL54 0x6001 WL54 product ABOCOM XX10 0xabc1 XX10 product ABOCOM BWU613 0xb000 BWU613 product ABOCOM HWU54DM 0xb21b HWU54DM product ABOCOM RT2573_2 0xb21c RT2573 product ABOCOM RT2573_3 0xb21d RT2573 product ABOCOM RT2573_4 0xb21e RT2573 product ABOCOM WUG2700 0xb21f WUG2700 /* Accton products */ product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter product ACCTON 2664W 0x3501 2664W product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter product ACCTON SMCWUSBG 0x4505 SMCWUSB-G product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter product ACCTON ZD1211B 0xe501 ZD1211B /* Aceeca products */ product ACEECA MEZ1000 0x0001 MEZ1000 RDA /* Acer Communications & Multimedia (oemd by Surecom) */ product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter /* Acer Labs products */ product ACERLABS M5632 0x5632 USB 2.0 Data Link /* Acer Peripherals, Inc. products */ product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U product ACERP ACERSCAN_320U 0x2022 Acerscan 320U product ACERP ACERSCAN_640U 0x2040 Acerscan 640U product ACERP ACERSCAN_620U 0x2060 Acerscan 620U product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U product ACERP ATAPI 0x6003 ATA/ATAPI Adapter product ACERP AWL300 0x9000 AWL300 Wireless Adapter product ACERP AWL400 0x9001 AWL400 Wireless Adapter /* Acer Warp products */ product ACERW WARPLINK 0x0204 Warplink /* Actiontec, Inc. products */ product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter /* ACTiSYS products */ product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR /* ActiveWire, Inc. products */ product ACTIVEWIRE IOBOARD 0x0100 I/O Board product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware /* Adaptec products */ product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN /* Addtron products */ product ADDTRON AWU120 0xff31 AWU-120 /* ADMtek products */ product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet product ADMTEK PEGASUS 0x0986 AN986 Ethernet product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet /* ADDON products */ /* PNY OEMs these */ product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive) /* Addonics products */ product ADDONICS2 CABLE_205 0xa001 Cable 205 /* ADS products */ product ADS UBS10BT 0x0008 UBS-10BT Ethernet product ADS UBS10BTX 0x0009 UBS-10BT Ethernet /* AEI products */ product AEI FASTETHERNET 0x1701 Fast Ethernet /* Agate Technologies products */ product AGATE QDRIVE 0x0378 Q-Drive /* AGFA products */ product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U product AGFA SNAPSCANE40 0x208d SnapScan e40 product AGFA SNAPSCANE50 0x208f SnapScan e50 product AGFA SNAPSCANE20 0x2091 SnapScan e20 product AGFA SNAPSCANE25 0x2095 SnapScan e25 product AGFA SNAPSCANE26 0x2097 SnapScan e26 product AGFA SNAPSCANE52 0x20fd SnapScan e52 /* Ain Communication Technology products */ product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter /* AIPTEK products */ product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega product AIPTEK2 PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3 /* AirPrime products */ product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card /* AKS products */ product AKS USBHASP 0x0001 USB-HASP 0.06 /* Alcor Micro, Inc. products */ product ALCOR2 KBD_HUB 0x2802 Kbd Hub product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub product ALCOR AU9814 0x9215 AU9814 Hub product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub /* Altec Lansing products */ product ALTEC ADA70 0x0070 ADA70 Speakers product ALTEC ASC495 0xff05 ASC495 Speakers /* Allied Telesyn International products */ product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100 /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Ambit Microsystems products */ product AMBIT WLAN 0x0302 WLAN product AMBIT NTL_250 0x6098 NTL 250 cable modem /* AMIT products */ product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO /* Anchor products */ product ANCHOR EZUSB 0x2131 EZUSB product ANCHOR EZLINK 0x2720 EZLINK /* AnyData products */ product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem /* AOX, Inc. products */ product AOX USB101 0x0008 Ethernet /* American Power Conversion products */ product APC UPS 0x0002 Uninterruptible Power Supply /* Apple Computer products */ product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard product APPLE OPTMOUSE 0x0302 Optical mouse product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard product APPLE SPEAKERS 0x1101 Speakers product APPLE IPOD 0x1201 iPod product APPLE IPOD2G 0x1202 iPod 2G product APPLE IPOD3G 0x1203 iPod 3G product APPLE IPOD_04 0x1204 iPod '04' product APPLE IPODMINI 0x1205 iPod Mini product APPLE IPOD_06 0x1206 iPod '06' product APPLE IPOD_07 0x1207 iPod '07' product APPLE IPOD_08 0x1208 iPod '08' product APPLE IPODVIDEO 0x1209 iPod Video product APPLE IPODNANO 0x120a iPod Nano product APPLE IPHONE 0x1290 iPhone product APPLE IPHONE_3G 0x1292 iPhone 3G product APPLE ETHERNET 0x1402 Ethernet A1277 /* Arkmicro Technologies */ product ARKMICRO ARK3116 0x0232 ARK3116 Serial /* Asahi Optical products */ product ASAHIOPTICAL OPTIO230 0x0004 Digital camera product ASAHIOPTICAL OPTIO330 0x0006 Digital camera /* Asante products */ product ASANTE EA 0x1427 Ethernet /* ASIX Electronics products */ product ASIX AX88172 0x1720 10/100 Ethernet product ASIX AX88178 0x1780 AX88178 product ASIX AX88772 0x7720 AX88772 /* ASUS products */ product ASUS WL167G 0x1707 WL-167g Wireless Adapter product ASUS WL159G 0x170c WL-159g product ASUS A9T_WIFI 0x171b A9T wireless product ASUS RT2573_1 0x1723 RT2573 product ASUS RT2573_2 0x1724 RT2573 product ASUS LCM 0x1726 LCM display product ASUS P535 0x420f ASUS P535 PDA /* ATen products */ product ATEN UC1284 0x2001 Parallel printer product ATEN UC10T 0x2002 10Mbps Ethernet product ATEN UC110T 0x2007 UC-110T Ethernet product ATEN UC232A 0x2008 Serial product ATEN UC210T 0x2009 UC-210T Ethernet product ATEN DSB650C 0x4000 DSB-650C /* Atheros Communications products */ product ATHEROS AR5523 0x0001 AR5523 product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_1 0x0001 AR5523 product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware) product ATHEROS2 AR5523_2 0x0003 AR5523 product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware) product ATHEROS2 AR5523_3 0x0005 AR5523 product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware) /* Atmel Comp. products */ product ATMEL UHB124 0x3301 UHB124 hub product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter product ATMEL BW002 0x7605 BW002 Wireless Adapter product ATMEL WL1130USB 0x7613 WL-1130 USB product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter /* Avision products */ product AVISION 1200U 0x0268 1200U scanner /* Axesstel products */ product AXESSTEL DATAMODEM 0x1000 Data Modem /* Baltech products */ product BALTECH CARDREADER 0x9999 Card reader /* B&B Electronics products */ product BBELECTRONICS USOTL4 0xAC01 RS-422/485 /* Belkin products */ /*product BELKIN F5U111 0x???? F5U111 Ethernet*/ product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth product BELKIN F5U103 0x0103 F5U103 Serial product BELKIN F5U109 0x0109 F5U109 Serial product BELKIN USB2SCSI 0x0115 USB to SCSI product BELKIN F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter product BELKIN USB2LAN 0x0121 USB to LAN product BELKIN F5U208 0x0208 F5U208 VideoBus II product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub product BELKIN F5U257 0x0257 F5U257 Serial product BELKIN F5U409 0x0409 F5U409 Serial product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS product BELKIN F5U120 0x1203 F5U120-PC Hub product BELKIN ZD1211B 0x4050 ZD1211B product BELKIN F5D5055 0x5055 F5D5055 product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter /* Also sold as 'Ativa 802.11g wireless card' */ product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter product BELKIN2 F5U002 0x0002 F5U002 Parallel printer /* Billionton products */ product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet product BILLIONTON USBLP100 0x0987 USB100LP product BILLIONTON USBEL100 0x0988 USB100EL product BILLIONTON USBE100 0x8511 USBE100 product BILLIONTON USB2AR 0x90ff USB2AR Ethernet /* Broadcom products */ product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle /* Brother Industries products */ product BROTHER HL1050 0x0002 HL-1050 laser printer /* Behavior Technology Computer products */ product BTC BTC7932 0x6782 Keyboard with mouse port /* Canon, Inc. products */ product CANON N656U 0x2206 CanoScan N656U product CANON N1220U 0x2207 CanoScan N1220U product CANON D660U 0x2208 CanoScan D660U product CANON N676U 0x220d CanoScan N676U product CANON N1240U 0x220e CanoScan N1240U product CANON LIDE25 0x2220 CanoScan LIDE 25 product CANON S10 0x3041 PowerShot S10 product CANON S100 0x3045 PowerShot S100 product CANON S200 0x3065 PowerShot S200 product CANON REBELXT 0x30ef Digital Rebel XT /* CATC products */ product CATC NETMATE 0x000a Netmate Ethernet product CATC NETMATE2 0x000c Netmate2 Ethernet product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer product CATC ANDROMEDA 0x1237 Andromeda hub /* CASIO products */ product CASIO QV_DIGICAM 0x1001 QV DigiCam product CASIO EXS880 0x1105 Exilim EX-S880 product CASIO BE300 0x2002 BE-300 PDA product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB /* CCYU products */ product CCYU ED1064 0x2136 EasyDisk ED1064 /* Century products */ product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure /* Cherry products */ product CHERRY MY3000KBD 0x0001 My3000 keyboard product CHERRY MY3000HUB 0x0003 My3000 hub product CHERRY CYBOARD 0x0004 CyBoard Keyboard /* Chic Technology products */ product CHIC MOUSE1 0x0001 mouse product CHIC CYPRESS 0x0003 Cypress USB Mouse /* Chicony products */ product CHICONY KB8933 0x0001 KB-8933 keyboard product CHICONY2 TWINKLECAM 0x600d TwinkleCam USB camera /* CH Products */ product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke /* Cisco-Linksys products */ product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter product CISCOLINKSYS USB200MV2 0x0018 USB200M v2 product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G /* CMOTECH products */ product CMOTECH CNU510 0x5141 CMOTECH CDMA Technologies USB modem product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem product CMOTECH CDMA_MODEM1 0x6280 CMOTECH CDMA Technologies USB modem /* Compaq products */ product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC product COMPAQ PJB100 0x504a Personal Jukebox PJB100 product COMPAQ IPAQLINUX 0x505a iPAQ Linux /* Composite Corp products looks the same as "TANGTOP" */ product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor /* Conceptronic products */ product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN product CONCEPTRONIC C11U 0x7100 C11U product CONCEPTRONIC WL210 0x7110 WL-210 product CONCEPTRONIC AR5523_1 0x7801 AR5523 product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware) product CONCEPTRONIC AR5523_2 0x7811 AR5523 product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware) product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN product CONCEPTRONIC2 C54RU2 0x3c22 C54RU /* Connectix products */ product CONNECTIX QUICKCAM 0x0001 QuickCam /* Corega products */ product COREGA ETHER_USB_T 0x0001 Ether USB-T product COREGA FETHER_USB_TX 0x0004 FEther USB-TX product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11 product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS product COREGA WLANUSB 0x0012 Wireless LAN Stick-11 product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11 product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC /* Creative products */ product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG product CREATIVE NOMAD 0x4106 Nomad product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse /* Cambridge Silicon Radio Ltd. products */ product CSR BT_DONGLE 0x0001 Bluetooth USB dongle product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State /* CTX products */ product CTX EX1300 0x9999 Ex1300 hub /* Curitel products */ product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C) product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600) product CURITEL PC5740 0x3701 Broadband Wireless modem /* CyberPower products */ product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD /* CyberTAN Technology products */ product CYBERTAN TG54USB 0x1666 TG54USB /* Cypress Semiconductor products */ product CYPRESS MOUSE 0x0001 mouse product CYPRESS THERMO 0x0002 thermometer product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy product CYPRESS KBDHUB 0x0101 Keyboard/Hub product CYPRESS FMRADIO 0x1002 FM Radio product CYPRESS USBRS232 0x5500 USB-RS232 Interface product CYPRESS SLIM_HUB 0x6560 Slim Hub /* Daisy Technology products */ product DAISY DMC 0x6901 USB MultiMedia Reader /* Dallas Semiconductor products */ product DALLAS J6502 0x4201 J-6502 speakers /* Dell products */ product DELL PORT 0x0058 Port Replicator product DELL AIO926 0x5115 Photo AIO Printer 926 product DELL BC02 0x8000 BC02 Bluetooth USB Adapter product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN product DELL U740 0x8135 Dell U740 CDMA /* Delorme Paublishing products */ product DELORME EARTHMATE 0x0100 Earthmate GPS /* Desknote products */ product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B /* Diamond products */ product DIAMOND RIO500USB 0x0001 Rio 500 USB /* Dick Smith Electronics (really C-Net) products */ product DICKSMITH RT2573 0x9022 RT2573 product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F /* Digi International products */ product DIGI ACCELEPORT2 0x0002 AccelePort USB 2 product DIGI ACCELEPORT4 0x0004 AccelePort USB 4 product DIGI ACCELEPORT8 0x0008 AccelePort USB 8 /* D-Link products */ /*product DLINK DSBS25 0x0100 DSB-S25 serial*/ product DLINK DUBE100 0x1a00 10/100 Ethernet product DLINK DSB650TX4 0x200c 10/100 Ethernet product DLINK DWL120E 0x3200 DWL-120 rev E product DLINK DWL122 0x3700 DWL-122 product DLINK DWLG120 0x3701 DWL-G120 product DLINK DWL120F 0x3702 DWL-120 rev F product DLINK DWLAG132 0x3a00 DWL-AG132 product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware) product DLINK DWLG132 0x3a02 DWL-G132 product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware) product DLINK DWLAG122 0x3a04 DWL-AG122 product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware) product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1 product DLINK DSB650C 0x4000 10Mbps Ethernet product DLINK DSB650TX1 0x4001 10/100 Ethernet product DLINK DSB650TX 0x4002 10/100 Ethernet product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet product DLINK DSB650TX3 0x400b 10/100 Ethernet product DLINK DSB650TX2 0x4102 10/100 Ethernet product DLINK DSB650 0xabc1 10/100 Ethernet product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1 product DLINK2 WUA1340 0x3c04 WUA-1340 product DLINK2 DWA111 0x3c06 DWA-111 product DLINK2 DWA110 0x3c07 DWA-110 /* DMI products */ product DMI CFSM_RW 0xa109 CF/SM Reader/Writer /* DrayTek products */ product DRAYTEK VIGOR550 0x0550 Vigor550 /* Dynastream Innovations */ product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board /* EIZO products */ product EIZO HUB 0x0000 hub product EIZO MONITOR 0x0001 monitor /* ELCON Systemtechnik products */ product ELCON PLAN 0x0002 Goldpfeil P-LAN /* Elecom products */ product ELECOM MOUSE29UO 0x0002 mouse 29UO product ELECOM LDUSBTX0 0x200c LD-USB/TX product ELECOM LDUSBTX1 0x4002 LD-USB/TX product ELECOM LDUSBLTX 0x4005 LD-USBL/TX product ELECOM LDUSBTX2 0x400b LD-USB/TX product ELECOM LDUSB20 0x4010 LD-USB20 product ELECOM UCSGT 0x5003 UC-SGT product ELECOM UCSGT0 0x5004 UC-SGT product ELECOM LDUSBTX3 0xabc1 LD-USB/TX /* Elsa products */ product ELSA MODEM1 0x2265 ELSA Modem Board product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet /* EMS products */ product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter /* Entrega products */ product ENTREGA 1S 0x0001 1S serial product ENTREGA 2S 0x0002 2S serial product ENTREGA 1S25 0x0003 1S25 serial product ENTREGA 4S 0x0004 4S serial product ENTREGA E45 0x0005 E45 Ethernet product ENTREGA CENTRONICS 0x0006 Parallel Port product ENTREGA XX1 0x0008 Ethernet product ENTREGA 1S9 0x0093 1S9 serial product ENTREGA EZUSB 0x8000 EZ-USB /*product ENTREGA SERIAL 0x8001 DB25 Serial*/ product ENTREGA 2U4S 0x8004 2U4S serial/usb hub product ENTREGA XX2 0x8005 Ethernet /*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/ /* Epson products */ product EPSON PRINTER1 0x0001 USB Printer product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac product EPSON PRINTER3 0x0003 ISD USB Smart Cable product EPSON PRINTER5 0x0005 USB Printer product EPSON 636 0x0101 Perfection 636U / 636Photo scanner product EPSON 610 0x0103 Perfection 610 scanner product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner product EPSON 1600 0x0107 Expression 1600 scanner product EPSON 1640 0x010a Perfection 1640SU scanner product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner product EPSON 640U 0x010c Perfection 640U scanner product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner product EPSON 1650 0x0110 Perfection 1650 scanner product EPSON GT9700F 0x0112 GT-9700F scanner product EPSON GT9300UF 0x011b GT-9300UF scanner product EPSON 3200 0x011c Perfection 3200 scanner product EPSON 1260 0x011d Perfection 1260 scanner product EPSON 1660 0x011e Perfection 1660 scanner product EPSON 1670 0x011f Perfection 1670 scanner product EPSON 1270 0x0120 Perfection 1270 scanner product EPSON 2480 0x0121 Perfection 2480 scanner product EPSON 3590 0x0122 Perfection 3590 scanner product EPSON 4990 0x012a Perfection 4990 Photo scanner product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader product EPSON CX5400 0x0808 CX5400 scanner product EPSON 3500 0x080e CX-3500/3600/3650 MFP product EPSON RX425 0x080f Stylus Photo RX425 scanner product EPSON 4800 0x0819 CX4800 MP scanner product EPSON 4200 0x0820 CX4200 MP scanner product EPSON 5000 0x082b DX-50x0 MFP scanner product EPSON 6000 0x082e DX-60x0 MFP scanner product EPSON DX7400 0x0838 DX7400/CX7300 scanner product EPSON DX8400 0x0839 DX8400 scanner /* e-TEK Labs products */ product ETEK 1COM 0x8007 Serial /* Extended Systems products */ product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA /* FEIYA products */ product FEIYA 5IN1 0x1132 5-in-1 Card Reader /* Fiberline */ product FIBERLINE WL430U 0x6003 WL-430U /* Fossil, Inc products */ product FOSSIL WRISTPDA 0x0002 Wrist PDA /* Freecom products */ product FREECOM DVD 0xfc01 DVD drive /* Fujitsu Siemens Computers products */ product FSC E5400 0x1009 PrismGT USB 2.0 WLAN /* Future Technology Devices products */ product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial /* Gude Analog- und Digitalsysteme products also uses FTDI's id: */ product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal product FTDI EISCOU 0xe888 Expert ISDN Control USB product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II product FTDI PCMSFU 0xe88b Precision Clock MSF USB product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG +product FTDI MAXSTREAM 0xee18 Maxstream PKG-U product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3 product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5 product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation /* Fuji photo products */ product FUJIPHOTO MASS0100 0x0100 Mass Storage /* Fujitsu protducts */ product FUJITSU AH_F401U 0x105b AH-F401U Air H device /* Garmin products */ product GARMIN IQUE_3600 0x0004 iQue 3600 /* General Instruments (Motorola) products */ product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem /* Genesys Logic products */ product GENESYS GL620USB 0x0501 GL620USB Host-Host interface product GENESYS GL650 0x0604 GL650 Hub product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2 product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader /* GIGABYTE products */ product GIGABYTE GN54G 0x8001 GN-54G product GIGABYTE GNBR402W 0x8002 GN-BR402W product GIGABYTE GNWLBM101 0x8003 GN-WLBM101 product GIGABYTE GNWBKG 0x8007 GN-WBKG product GIGABYTE GNWB01GS 0x8008 GN-WB01GS product GIGABYTE GNWI05GS 0x800a GN-WI05GS /* Gigaset products */ product GIGASET WLAN 0x0701 WLAN product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware) product GIGASET AR5523 0x0712 AR5523 product GIGASET AR5523_NF 0x0713 AR5523 (no firmware) product GIGASET RT2573 0x0722 RT2573 /* Global Sun Technology product */ product GLOBALSUN AR5523_1 0x7801 AR5523 product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware) product GLOBALSUN AR5523_2 0x7811 AR5523 product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware) /* Globespan products */ product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* G.Mate, Inc products */ product GMATE YP3X00 0x1001 YP3X00 PDA /* GoHubs products */ product GOHUBS GOCOM232 0x1001 GoCOM232 Serial /* Good Way Technology products */ product GOODWAY GWUSB2E 0x6200 GWUSB2E product GOODWAY RT2573 0xc019 RT2573 /* Gravis products */ product GRAVIS GAMEPADPRO 0x4001 GamePad Pro /* GREENHOUSE products */ product GREENHOUSE KANA21 0x0001 CF-writer with MP3 /* Griffin Technology */ product GRIFFIN IMATE 0x0405 iMate, ADB Adapter /* Guillemot Corporation */ product GUILLEMOT DALEADER 0xa300 DA Leader product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP /* Hagiwara products */ product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader product HAGIWARA FG 0x0005 FlashGate /* HAL Corporation products */ product HAL IMR001 0x0011 Crossam2+USB IR commander /* Handspring, Inc. */ product HANDSPRING VISOR 0x0100 Handspring Visor product HANDSPRING TREO 0x0200 Handspring Treo product HANDSPRING TREO600 0x0300 Handspring Treo 600 /* Hauppauge Computer Works */ product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM /* Hawking Technologies products */ product HAWKING UF100 0x400c 10/100 USB Ethernet /* Hitachi, Ltd. products */ product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface /* HP products */ product HP 895C 0x0004 DeskJet 895C product HP 4100C 0x0101 Scanjet 4100C product HP S20 0x0102 Photosmart S20 product HP 880C 0x0104 DeskJet 880C product HP 4200C 0x0105 ScanJet 4200C product HP CDWRITERPLUS 0x0107 CD-Writer Plus product HP KBDHUB 0x010c Multimedia Keyboard Hub product HP G55XI 0x0111 OfficeJet G55xi product HP HN210W 0x011c HN210W 802.11b WLAN product HP 49GPLUS 0x0121 49g+ graphing calculator product HP 6200C 0x0201 ScanJet 6200C product HP S20b 0x0202 PhotoSmart S20 product HP 815C 0x0204 DeskJet 815C product HP 3300C 0x0205 ScanJet 3300C product HP CDW8200 0x0207 CD-Writer Plus 8200e product HP MMKEYB 0x020c Multimedia keyboard product HP 1220C 0x0212 DeskJet 1220C product HP 810C 0x0304 DeskJet 810C/812C product HP 4300C 0x0305 Scanjet 4300C product HP CDW4E 0x0307 CD-Writer+ CD-4e product HP G85XI 0x0311 OfficeJet G85xi product HP 1200 0x0317 LaserJet 1200 product HP 5200C 0x0401 Scanjet 5200C product HP 830C 0x0404 DeskJet 830C product HP 3400CSE 0x0405 ScanJet 3400cse product HP 6300C 0x0601 Scanjet 6300C product HP 840C 0x0604 DeskJet 840c product HP 2200C 0x0605 ScanJet 2200C product HP 5300C 0x0701 Scanjet 5300C product HP 4400C 0x0705 Scanjet 4400C +product HP 4470C 0x0805 Scanjet 4470C product HP 82x0C 0x0b01 Scanjet 82x0C product HP 2300D 0x0b17 Laserjet 2300d product HP 970CSE 0x1004 Deskjet 970Cse product HP 5400C 0x1005 Scanjet 5400C product HP 2215 0x1016 iPAQ 22xx/Jornada 548 product HP 568J 0x1116 Jornada 568 product HP 930C 0x1204 DeskJet 930c product HP P2000U 0x1801 Inkjet P-2000U product HP 640C 0x2004 DeskJet 640c product HP 4670V 0x3005 ScanJet 4670v product HP P1100 0x3102 Photosmart P1100 product HP OJ4215 0x3d11 OfficeJet 4215 product HP HN210E 0x811c Ethernet HN210E product HP2 C500 0x6002 PhotoSmart C500 product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775) /* HTC products */ product HTC WINMOBILE 0x00ce HTC USB Sync product HTC PPC6700MODEM 0x00cf PPC6700 Modem product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync /* HUAWEI products */ product HUAWEI MOBILE 0x1001 Huawei Mobile product HUAWEI E220 0x1003 Huawei HSDPA modem /* HUAWEI 3com products */ product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g /* IBM Corporation */ product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive /* Imagination Technologies products */ product IMAGINATION DBX1 0x2107 DBX1 DSP core /* Inside Out Networks products */ product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports /* In-System products */ product INSYSTEM F5U002 0x0002 Parallel printer product INSYSTEM ATAPI 0x0031 ATAPI Adapter product INSYSTEM ISD110 0x0200 IDE Adapter ISD110 product INSYSTEM ISD105 0x0202 IDE Adapter ISD105 product INSYSTEM USBCABLE 0x081a USB cable product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2 /* Intel products */ product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera product INTEL TESTBOARD 0x9890 82930 test board /* Intersil products */ product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN /* Interpid Control Systems products */ product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface /* I/O DATA products */ product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2 product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8 product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card product IODATA USBSDRW 0x031e USB-SDRW SD-card product IODATA USBETT 0x0901 USB ETT product IODATA USBETTX 0x0904 USB ETTX product IODATA USBETTXS 0x0913 USB ETTX product IODATA USBWNB11A 0x0919 USB WN-B11 product IODATA USBWNB11 0x0922 USB Airport WN-B11 product IODATA ETGUS2 0x0930 ETG-US2 product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1 product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC /* Iomega products */ product IOMEGA ZIP100 0x0001 Zip 100 product IOMEGA ZIP250 0x0030 Zip 250 /* Ituner networks products */ product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20 /* Jablotron products */ product JABLOTRON PC60B 0x0001 PC-60B /* Jaton products */ product JATON EDA 0x5704 Ethernet /* JVC products */ product JVC GR_DX95 0x000a GR-DX95 product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet /* JRC products */ product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V /* Kawatsu products */ product KAWATSU MH4000P 0x0003 MiniHub 4000P /* Keisokugiken Corp. products */ product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ /* Kensington products */ product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball product KENSINGTON TURBOBALL 0x1005 TurboBall /* Keyspan products */ product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware) product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware) product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware) product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware) product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware) product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware) product KEYSPAN USA19 0x0107 USA-19 serial Adapter product KEYSPAN USA19W 0x0108 USA-19W serial Adapter product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware) product KEYSPAN USA49W 0x010a USA-49W serial Adapter product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware) product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware) product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter product KEYSPAN USA28 0x010f USA-28 serial Adapter product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter product KEYSPAN USA18 0x0111 USA-18 serial Adapter product KEYSPAN USA18X 0x0112 USA-18X serial Adapter product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware) product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware) product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware) product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter product KEYSPAN UIA10 0x0201 UIA-10 remote control product KEYSPAN UIA11 0x0202 UIA-11 remote control /* Kingston products */ product KINGSTON XX1 0x0008 Ethernet product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet /* Kawasaki products */ product KLSI DUH3E10BT 0x0008 USB Ethernet product KLSI DUH3E10BTN 0x0009 USB Ethernet /* Kodak products */ product KODAK DC220 0x0100 Digital Science DC220 product KODAK DC260 0x0110 Digital Science DC260 product KODAK DC265 0x0111 Digital Science DC265 product KODAK DC290 0x0112 Digital Science DC290 product KODAK DC240 0x0120 Digital Science DC240 product KODAK DC280 0x0130 Digital Science DC280 /* Konica Corp. Products */ product KONICA CAMERA 0x0720 Digital Color Camera /* KYE products */ product KYE NICHE 0x0001 Niche mouse product KYE NETSCROLL 0x0003 Genius NetScroll mouse product KYE FLIGHT2000 0x1004 Flight 2000 joystick product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner /* Kyocera products */ product KYOCERA FINECAM_S3X 0x0100 Finecam S3x product KYOCERA FINECAM_S4 0x0101 Finecam S4 product KYOCERA FINECAM_S5 0x0103 Finecam S5 product KYOCERA FINECAM_L3 0x0105 Finecam L3 product KYOCERA AHK3001V 0x0203 AH-K3001V product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM /* LaCie products */ product LACIE HD 0xa601 Hard Disk product LACIE CDRW 0xa602 CD R/W /* Lexar products */ product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader product LEXAR CF_READER 0xb002 USB CF Reader /* Lexmark products */ product LEXMARK S2450 0x0009 Optra S 2450 /* Linksys products */ product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2 product LINKSYS USB10TX1 0x200c USB10TX product LINKSYS USB10T 0x2202 USB10T Ethernet product LINKSYS USB100TX 0x2203 USB100TX Ethernet product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA product LINKSYS USB10TA 0x2206 USB10TA Ethernet product LINKSYS USB10TX2 0x400b USB10TX product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter product LINKSYS4 USB1000 0x0039 USB1000 /* Logitech products */ product LOGITECH M2452 0x0203 M2452 keyboard product LOGITECH M4848 0x0301 M4848 mouse product LOGITECH PAGESCAN 0x040f PageScan product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express product LOGITECH QUICKCAM 0x0850 QuickCam product LOGITECH N43 0xc000 N43 product LOGITECH N48 0xc001 N48 mouse product LOGITECH MBA47 0xc002 M-BA47 mouse product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse product LOGITECH BD58 0xc00c BD58 mouse product LOGITECH UN58A 0xc030 iFeel Mouse product LOGITECH UN53B 0xc032 iFeel MouseMan product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme product LOGITECH WMRPAD 0xc20a WingMan RumblePad product LOGITECH WMJOY 0xc281 WingMan Force joystick product LOGITECH BB13 0xc401 USB-PS/2 Trackball product LOGITECH RK53 0xc501 Cordless mouse product LOGITECH RB6 0xc503 Cordless keyboard product LOGITECH MX700 0xc506 Cordless optical mouse product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro /* Logitec Corp. products */ product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2 product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2 /* Lucent products */ product LUCENT EVALKIT 0x1001 USS-720 evaluation kit /* Luwen products */ product LUWEN EASYDISK 0x0005 EasyDisc /* Macally products */ product MACALLY MOUSE1 0x0101 mouse /* MCT Corp. */ product MCT HUB0100 0x0100 Hub product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub product MCT USB232 0x0210 USB-232 Interface product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products /* Melco, Inc products */ product MELCO LUATX1 0x0001 LUA-TX Ethernet product MELCO LUATX5 0x0005 LUA-TX Ethernet product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet product MELCO LUAKTX 0x0012 LUA-KTX Ethernet product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN product MELCO KG54 0x0066 WLI-U2-KG54 WLAN product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN product MELCO NINWIFI 0x008b Nintendo Wi-Fi product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation product MELCO SG54HP 0x00d8 WLI-U2-SG54HP product MELCO G54HP 0x00d9 WLI-U2-G54HP product MELCO KG54L 0x00da WLI-U2-KG54L /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* MetaGeek products */ product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x /* Metricom products */ product METRICOM RICOCHET_GS 0x0001 Ricochet GS /* MGE UPS Systems */ product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1 product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2 /* Micro Star International products */ product MSI BT_DONGLE 0x1967 Bluetooth USB dongle product MSI UB11B 0x6823 UB11B product MSI RT2570 0x6861 RT2570 product MSI RT2570_2 0x6865 RT2570 product MSI RT2570_3 0x6869 RT2570 product MSI RT2573_1 0x6874 RT2573 product MSI RT2573_2 0x6877 RT2573 product MSI RT2573_3 0xa861 RT2573 product MSI RT2573_4 0xa874 RT2573 /* Microsoft products */ product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite product MICROSOFT DDS80 0x0014 Digital Sound System 80 product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel product MICROSOFT INETPRO 0x001c Internet Keyboard Pro product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro product MICROSOFT MN510 0x006e MN510 Wireless product MICROSOFT MN110 0x007a 10/100 USB NIC product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023) product MICROSOFT COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043) product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056) product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049) product MICROSOFT WLUSBMOUSE 0x00b9 Wireless USB Mouse product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN /* Microtech products */ product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25 product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50 product MICROTECH DPCM 0x0006 USB CameraMate product MICROTECH FREECOM 0xfc01 Freecom USB-IDE /* Microtek products */ product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner product MICROTEK X6U 0x0099 ScanMaker X6 - X6U product MICROTEK C6 0x009a Phantom C6 scanner product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner product MICROTEK V6USL 0x00a3 ScanMaker V6USL product MICROTEK V6USL2 0x80a3 ScanMaker V6USL product MICROTEK V6UL 0x80ac ScanMaker V6UL /* Microtune, Inc. products */ product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle /* Midiman products */ product MIDIMAN MIDISPORT2X2 0x1001 Midisport 2x2 /* MindsAtWork products */ product MINDSATWORK WALLET 0x0001 Digital Wallet /* Minolta Co., Ltd. */ product MINOLTA 2300 0x4001 Dimage 2300 product MINOLTA S304 0x4007 Dimage S304 product MINOLTA X 0x4009 Dimage X product MINOLTA 5400 0x400e Dimage 5400 product MINOLTA F300 0x4011 Dimage F300 product MINOLTA E223 0x4017 Dimage E223 /* Mitsumi products */ product MITSUMI CDRRW 0x0000 CD-R/RW Drive product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle product MITSUMI FDD 0x6901 USB FDD /* Mobility products */ product MOBILITY EA 0x0204 Ethernet product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet /* MosChip products */ product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet /* Motorola products */ product MOTOROLA MC141555 0x1555 MC141555 hub controller product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones product MOTOROLA2 E398 0x4810 E398 Mobile Phone product MOTOROLA2 USBLAN 0x600c USBLAN product MOTOROLA2 USBLAN2 0x6027 USBLAN /* MultiTech products */ product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem /* Mustek products */ product MUSTEK 1200CU 0x0001 1200 CU scanner product MUSTEK 600CU 0x0002 600 CU scanner product MUSTEK 1200USB 0x0003 1200 USB scanner product MUSTEK 1200UB 0x0006 1200 UB scanner product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner product MUSTEK 600USB 0x0873 600 USB scanner product MUSTEK MDC800 0xa800 MDC-800 digital camera /* M-Systems products */ product MSYSTEMS DISKONKEY 0x0010 DiskOnKey product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey /* Myson products */ product MYSON HEDEN 0x8818 USB-IDE /* National Semiconductor */ product NATIONAL BEARPAW1200 0x1000 BearPaw 1200 product NATIONAL BEARPAW2400 0x1001 BearPaw 2400 /* NEC products */ product NEC HUB 0x55aa hub product NEC HUB_B 0x55ab hub /* NEODIO products */ product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller product NEODIO ND5010 0x5010 Multi-format Flash Controller /* Netac products */ product NETAC CF_CARD 0x1060 USB-CF-Card product NETAC ONLYDISK 0x0003 OnlyDisk /* NetChip Technology Products */ product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect product NETCHIP CLIK_40 0xa140 USB Clik! 40 product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x /* Netgear products */ product NETGEAR EA101 0x1001 Ethernet product NETGEAR EA101X 0x1002 Ethernet product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1 product NETGEAR FA120 0x1040 USB 2.0 Ethernet product NETGEAR WG111V2_2 0x4240 PrismGT USB 2.0 WLAN product NETGEAR WG111U 0x4300 WG111U product NETGEAR WG111U_NF 0x4301 WG111U (no firmware) product NETGEAR2 MA101 0x4100 MA101 product NETGEAR2 MA101B 0x4102 MA101 Rev B product NETGEAR3 WG111T 0x4250 WG111T product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware) product NETGEAR3 WPN111 0x5f00 WPN111 product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware) /* Nikon products */ product NIKON E990 0x0102 Digital Camera E990 product NIKON LS40 0x4000 CoolScan LS40 ED product NIKON D300 0x041a Digital Camera D300 /* NovaTech Products */ product NOVATECH NV902 0x9020 NovaTech NV-902W product NOVATECH RT2573 0x9021 RT2573 /* Novatel Wireless products */ product NOVATEL V640 0x1100 Merlin V620 product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA product NOVATEL V620 0x1110 Merlin V620 product NOVATEL V740 0x1120 Merlin V740 product NOVATEL V720 0x1130 Merlin V720 product NOVATEL U740 0x1400 Merlin U740 product NOVATEL U740_2 0x1410 Merlin U740 product NOVATEL U870 0x1420 Merlin U870 product NOVATEL XU870 0x1430 Merlin XU870 product NOVATEL X950D 0x1450 Merlin X950D product NOVATEL ES620 0x2100 ES620 CDMA product NOVATEL U720 0x2110 Merlin U720 product NOVATEL U727 0x4100 Merlin U727 CDMA product NOVATEL MC950D 0x4400 Novatel MC950D HSUPA product NOVATEL ZEROCD 0x5010 Novatel ZeroCD product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver /* Merlin products */ product MERLIN V620 0x1110 Merlin V620 /* Olympus products */ product OLYMPUS C1 0x0102 C-1 Digital Camera product OLYMPUS C700 0x0105 C-700 Ultra Zoom /* OmniVision Technologies, Inc. products */ product OMNIVISION OV511 0x0511 OV511 Camera product OMNIVISION OV511PLUS 0xa511 OV511+ Camera /* OnSpec Electronic, Inc. */ product ONSPEC SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer product ONSPEC READER 0xa003 Datafab-based Reader product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1) product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55 /* Option products */ product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard product OPTION GT3G 0x6000 GlobeTrotter 3G datacard product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard product OPTION GTICON322 0xd033 GlobeTrotter Icon322 storage product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA /* OQO */ product OQO WIFI01 0x0002 model 01 WiFi interface product OQO BT01 0x0003 model 01 Bluetooth interface product OQO ETHER01PLUS 0x7720 model 01+ Ethernet product OQO ETHER01 0x8150 model 01 Ethernet interface /* Palm Computing, Inc. product */ product PALM SERIAL 0x0080 USB Serial product PALM M500 0x0001 Palm m500 product PALM M505 0x0002 Palm m505 product PALM M515 0x0003 Palm m515 product PALM I705 0x0020 Palm i705 product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z product PALM M125 0x0040 Palm m125 product PALM M130 0x0050 Palm m130 product PALM TUNGSTEN_T 0x0060 Palm Tungsten T product PALM ZIRE31 0x0061 Palm Zire 31 product PALM ZIRE 0x0070 Palm Zire /* Panasonic products */ product PANASONIC LS120CAM 0x0901 LS-120 Camera product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW product PANASONIC SDCAAE 0x1b00 MultiMediaCard /* Peracom products */ product PERACOM SERIAL1 0x0001 Serial product PERACOM ENET 0x0002 Ethernet product PERACOM ENET3 0x0003 At Home Ethernet product PERACOM ENET2 0x0005 Ethernet /* Philips products */ product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System product PHILIPS HUB 0x0201 hub product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System product PHILIPS SNU5600 0x1236 SNU5600 product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player /* Philips Semiconductor products */ product PHILIPSSEMI HUB1122 0x1122 hub /* P.I. Engineering products */ product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter /* Planex Communications products */ product PLANEX GW_US11H 0x14ea GW-US11H WLAN product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN product PLANEX2 GWUS54HP 0xab01 GW-US54HP product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2 product PLANEX2 GWUS54SG 0xc002 GW-US54SG product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL product PLANEX2 GWUS54GD 0xed01 GW-US54GD product PLANEX2 GWUSMM 0xed02 GW-USMM product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ product PLANEX3 GU1000T 0xab11 GU-1000T product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini /* Plextor Corp. */ product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U /* PLX products */ product PLX TESTBOARD 0x9060 test board /* PNY products */ product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive /* PortGear products */ product PORTGEAR EA8 0x0008 Ethernet product PORTGEAR EA9 0x0009 Ethernet /* Portsmith products */ product PORTSMITH EEA 0x3003 Express Ethernet /* Primax products */ product PRIMAX G2X300 0x0300 G2-200 scanner product PRIMAX G2E300 0x0301 G2E-300 scanner product PRIMAX G2300 0x0302 G2-300 scanner product PRIMAX G2E3002 0x0303 G2E-300 scanner product PRIMAX 9600 0x0340 Colorado USB 9600 scanner product PRIMAX 600U 0x0341 Colorado 600u scanner product PRIMAX 6200 0x0345 Visioneer 6200 scanner product PRIMAX 19200 0x0360 Colorado USB 19200 scanner product PRIMAX 1200U 0x0361 Colorado 1200u scanner product PRIMAX G600 0x0380 G2-600 scanner product PRIMAX 636I 0x0381 ReadyScan 636i product PRIMAX G2600 0x0382 G2-600 scanner product PRIMAX G2E600 0x0383 G2E-600 scanner product PRIMAX COMFORT 0x4d01 Comfort product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1 /* Prolific products */ product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2) product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A) product PROLIFIC PL2305 0x2305 Parallel printer product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface product PROLIFIC PHAROS 0xaaa0 Prolific Pharos product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3) product PROLIFIC2 WSIM 0x2001 Willcom WSIM /* Putercom products */ product PUTERCOM UPA100 0x047e USB-1284 BRIDGE /* Qcom products */ product QCOM RT2573 0x6196 RT2573 product QCOM RT2573_2 0x6229 RT2573 /* Qualcomm products */ product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage +product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB modem /* Qtronix products */ product QTRONIX 980N 0x2011 Scorpion-980N keyboard /* Quickshot products */ product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad /* Radio Shack */ product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable /* Rainbow Technologies products */ product RAINBOW IKEY2000 0x1200 i-Key 2000 /* Ralink Technology products */ product RALINK RT2570 0x1706 RT2500USB Wireless Adapter product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter product RALINK RT2573 0x2573 RT2501USB Wireless Adapter product RALINK RT2671 0x2671 RT2601USB Wireless Adapter product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter /* ReakTek products */ /* Green House and CompUSA OEM this part */ product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet /* Ricoh products */ product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera product RICOH VGPVCC7 0x183a VGP-VCC7 Camera product RICOH VGPVCC8 0x183b VGP-VCC8 Camera /* Roland products */ product ROLAND UM1 0x0009 UM-1 MIDI I/F product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native) product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic) /* Rockfire products */ product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB /* RATOC Systems products */ product RATOC REXUSB60 0xb000 REX-USB60 /* Sagem products */ product SAGEM USBSERIAL 0x0027 USB-Serial Controller product SAGEM XG760A 0x004a XG-760A product SAGEM XG76NA 0x0062 XG-76NA /* Samsung products */ product SAMSUNG ML6060 0x3008 ML-6060 laser printer product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player product SAMSUNG I500 0x6601 I500 Palm USB Phone /* Samsung Techwin products */ product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410 /* SanDisk products */ product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a product SANDISK SDDR31 0x0002 ImageMate SDDR-31 product SANDISK SDDR05 0x0005 ImageMate SDDR-05 product SANDISK SDDR12 0x0100 ImageMate SDDR-12 product SANDISK SDDR09 0x0200 ImageMate SDDR-09 product SANDISK SDDR75 0x0810 ImageMate SDDR-75 product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB /* Sanyo Electric products */ product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone /* ScanLogic products */ product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner /* Senao products */ product SENAO NUB8301 0x2000 NUB-8301 /* ShanTou products */ product SHANTOU ST268 0x0268 ST268 product SHANTOU DM9601 0x9601 DM 9601 /* Shark products */ product SHARK PA 0x0400 Pocket Adapter /* Sharp products */ product SHARP SL5500 0x8004 Zaurus SL-5500 PDA product SHARP SLA300 0x8005 Zaurus SL-A300 PDA product SHARP SL5600 0x8006 Zaurus SL-5600 PDA product SHARP SLC700 0x8007 Zaurus SL-C700 PDA product SHARP SLC750 0x9031 Zaurus SL-C750 PDA product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone /* Shuttle Technology products */ product SHUTTLE EUSB 0x0001 E-USB Bridge product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge product SHUTTLE SDDR09 0x0003 ImageMate SDDR09 product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter product SHUTTLE HIFD 0x0007 Sony Hifd product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter product SHUTTLE CF 0x000a eUSB CompactFlash Adapter product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge product SHUTTLE CDRW 0x0101 CD-RW Device product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader /* Siemens products */ product SIEMENS SPEEDSTREAM 0x1001 SpeedStream product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022 product SIEMENS2 WLL013 0x001b WLL013 product SIEMENS2 ES75 0x0034 GSM module MC35 product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter product SIEMENS3 SX1 0x0001 SX1 product SIEMENS3 X65 0x0003 X65 product SIEMENS3 X75 0x0004 X75 /* Sierra Wireless products */ product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580 product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595 product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E product SIERRA C597 0x0023 Sierra Wireless Compass 597 product SIERRA AC875 0x6820 Sierra Wireless AirCard 875 product SIERRA AC880 0x6850 Sierra Wireless AirCard 880 product SIERRA AC881 0x6851 Sierra Wireless AirCard 881 product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U product SIERRA AC885U 0x6880 Sierra Wireless AirCard 885U product SIERRA EM5625 0x0017 EM5625 product SIERRA MC5720 0x0218 MC5720 Wireless Modem product SIERRA MC5720_2 0x0018 MC5720 product SIERRA MC5725 0x0020 MC5725 product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275 product SIERRA MC8755_2 0x6802 MC8755 product SIERRA MC8765 0x6803 MC8765 product SIERRA MC8755 0x6804 MC8755 product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem product SIERRA MC8755_3 0x6813 MC8755 HSDPA product SIERRA MC8775_2 0x6815 MC8775 product SIERRA AIRCARD875 0x6820 Aircard 875 HSDPA product SIERRA MC8780 0x6832 MC8780 product SIERRA MC8781 0x6833 MC8781 product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer /* Sigmatel products */ product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player /* SIIG products */ /* Also: Omnidirectional Control Technology products */ product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader product SIIG WINTERREADER 0x0330 WINTERREADER Reader product SIIG2 USBTOETHER 0x0109 USB TO Ethernet product SIIG2 US2308 0x0421 Serial /* Silicom products */ product SILICOM U2E 0x0001 U2E product SILICOM GPE 0x0002 Psion Gold Port Ethernet /* SI Labs */ product SILABS POLOLU 0x803b Pololu Serial product SILABS ARGUSISP 0x8066 Argussoft ISP product SILABS CRUMB128 0x807a Crumb128 board product SILABS DEGREE 0x80ca Degree Controls Inc product SILABS TRAQMATE 0x80ed Track Systems Traqmate product SILABS SUUNTO 0x80f6 Suunto Sports Instrument product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM product SILABS CP2102 0xea60 SILABS USB UART product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1 product SILABS CP2102 0xea60 SILABS USB UARTa product SILABS CP210X_2 0xea61 CP210x Serial product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone /* Silicon Portals Inc. */ product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware) product SILICONPORTALS YAPPHONE 0x0201 YAP Phone /* Sirius Technologies products */ product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB /* Sitecom products */ product SITECOM LN029 0x182d USB 2.0 Ethernet product SITECOM SERIAL 0x2068 USB to serial cable (v2) product SITECOM2 WL022 0x182d WL-022 /* Sitecom Europe products */ product SITECOMEU LN028 0x061c LN-028 product SITECOMEU WL113 0x9071 WL-113 product SITECOMEU ZD1211B 0x9075 ZD1211B product SITECOMEU WL172 0x90ac WL-172 product SITECOMEU WL113R2 0x9712 WL-113 rev 2 /* Skanhex Technology products */ product SKANHEX MD_7425 0x410a MD 7425 Camera product SKANHEX SX_520Z 0x5200 SX 520z Camera /* SmartBridges products */ product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet /* SMC products */ product SMC 2102USB 0x0100 10Mbps Ethernet product SMC 2202USB 0x0200 10/100 Ethernet product SMC 2206USB 0x0201 EZ Connect USB Ethernet product SMC 2862WG 0xee13 EZ Connect Wireless Adapter product SMC2 2020HUB 0x2020 USB Hub product SMC3 2662WUSB 0xa002 2662W-AR Wireless /* SOHOware products */ product SOHOWARE NUB100 0x9100 10/100 USB Ethernet product SOHOWARE NUB110 0x9110 10/100 USB Ethernet /* SOLID YEAR products */ product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard /* SONY products */ product SONY DSC 0x0010 DSC cameras product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7 product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2 product SONY MSACUS1 0x002d Memorystick MSAC-US1 product SONY HANDYCAM 0x002e Handycam product SONY MSC 0x0032 MSC memory stick slot product SONY CLIE_35 0x0038 Sony Clie v3.5 product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick product SONY CLIE_40 0x0066 Sony Clie v4.0 product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03 product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot product SONY CLIE_S360 0x0095 Sony Clie s360 product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot product SONY CLIE_41 0x009a Sony Clie v4.1 product SONY CLIE_NX60 0x00da Sony Clie nx60 product SONY CLIE_TH55 0x0144 Sony Clie th55 product SONY CLIE_TJ37 0x0169 Sony Clie tj37 product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1 /* Sony Ericsson products */ product SONYERICSSON DCU10 0x0528 USB Cable /* SOURCENEXT products */ product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8 product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger /* SparkLAN products */ product SPARKLAN RT2573 0x0004 RT2573 /* Sphairon Access Systems GmbH products */ product SPHAIRON UB801R 0x0110 UB801R /* Stelera Wireless products */ product STELERA ZEROCD 0x1000 Zerocd Installer product STELERA C105 0x1002 Stelera/Bandrish C105 USB /* STMicroelectronics products */ product STMICRO BIOCPU 0x2016 Biometric Coprocessor product STMICRO COMMUNICATOR 0x7554 USB Communicator /* STSN products */ product STSN STSN0001 0x0001 Internet Access Device /* SUN Corporation products */ product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2 product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1 product SUNTAC VS10U 0x0009 SUNTAC Slipper U product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3 product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4 /* Sun Microsystems products */ product SUN KEYBOARD 0x0005 Type 6 USB keyboard /* XXX The above is a North American PC style keyboard possibly */ product SUN MOUSE 0x0100 Type 6 USB mouse /* Supra products */ product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem product DIAMOND2 RIO600USB 0x5001 Rio 600 USB product DIAMOND2 RIO800USB 0x5002 Rio 800 USB /* Surecom Technology products */ product SURECOM RT2570 0x11f3 RT2570 product SURECOM RT2573 0x31f3 RT2573 /* Sweex products */ product SWEEX ZD1211 0x1809 ZD1211 /* System TALKS, Inc. */ product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL /* Tapwave products */ product TAPWAVE ZODIAC 0x0100 Zodiac /* Taugagreining products */ product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB) /* TDK products */ product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664 product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464 product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400 product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400 product TDK BT_DONGLE 0x0309 Bluetooth USB dongle /* TEAC products */ product TEAC FD05PUB 0x0000 FD-05PUB floppy /* Tekram Technology products */ product TEKRAM QUICKWLAN 0x1630 QuickWLAN product TEKRAM ZD1211_1 0x5630 ZD1211 product TEKRAM ZD1211_2 0x6630 ZD1211 /* Telex Communications products */ product TELEX MIC1 0x0001 Enhanced USB Microphone /* Ten X Technology, Inc. */ product TENX UAUDIO0 0xf211 USB audio headset /* Texas Intel products */ product TI UTUSB41 0x1446 UT-USB41 hub product TI TUSB2046 0x2046 TUSB2046 hub /* Thrustmaster products */ product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad /* Topre Corporation products */ product TOPRE HHKB 0x0100 HHKB Professional /* Toshiba Corporation products */ product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740 /* Trek Technology products */ product TREK THUMBDRIVE 0x1111 ThumbDrive product TREK MEMKEY 0x8888 IBM USB Memory Key product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB /* Tripp-Lite products */ product TRIPPLITE U209 0x2008 Serial /* Trumpion products */ product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller product TRUMPION C3310 0x1100 Comotron C3310 MP3 player product TRUMPION MP3 0x1200 MP3 player /* TwinMOS */ product TWINMOS G240 0xa006 G240 product TWINMOS MDIV 0x1325 Memory Disk IV /* Ubiquam products */ product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520) /* Ultima products */ product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner /* UMAX products */ product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner product UMAX ASTRA3400 0x0060 Astra 3400 Scanner /* U-MEDIA Communications products */ product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware) product UMEDIA TEW429UB_A 0x300a TEW-429UB_A product UMEDIA TEW429UB 0x300b TEW-429UB product UMEDIA TEW429UBC1 0x300d TEW-429UB C1 product UMEDIA ALL0298V2 0x3204 ALL0298 v2 product UMEDIA AR5523_2 0x3205 AR5523 product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware) /* Universal Access products */ product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter /* U.S. Robotics products */ product USR USR5423 0x0121 USR5423 WLAN /* VIA Technologies products */ product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge /* USI products */ product USI MC60 0x10c5 MC60 Serial /* VidzMedia products */ product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H /* Vision products */ product VISION VC6452V002 0x0002 CPiA Camera /* Visioneer products */ product VISIONEER 7600 0x0211 OneTouch 7600 product VISIONEER 5300 0x0221 OneTouch 5300 product VISIONEER 3000 0x0224 Scanport 3000 product VISIONEER 6100 0x0231 OneTouch 6100 product VISIONEER 6200 0x0311 OneTouch 6200 product VISIONEER 8100 0x0321 OneTouch 8100 product VISIONEER 8600 0x0331 OneTouch 8600 /* Vivitar products */ product VIVITAR 35XX 0x0003 Vivicam 35Xx /* VTech products */ product VTECH RT2570 0x3012 RT2570 product VTECH ZD1211B 0x3014 ZD1211B /* Wacom products */ product WACOM CT0405U 0x0000 CT-0405-U Tablet product WACOM GRAPHIRE 0x0010 Graphire product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5 product WACOM INTUOSA5 0x0021 Intuos A5 product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet /* WCH products*/ product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge /* Western Digital products */ product WESTERN COMBO 0x0200 Firewire USB Combo product WESTERN EXTHDD 0x0400 External HDD product WESTERN HUB 0x0500 USB HUB product WESTERN MYBOOK 0x0901 MyBook External HDD /* Windbond Electronics */ product WINBOND UH104 0x5518 4-port USB Hub /* WinMaxGroup products */ product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C /* Wistron NeWeb products */ product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN product WISTRONNEWEB UR055G 0x0711 UR055G product WISTRONNEWEB AR5523_1 0x0826 AR5523 product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware) product WISTRONNEWEB AR5523_2 0x082a AR5523 product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware) /* Xerox products */ product XEROX WCM15 0xffef WorkCenter M15 /* Xirlink products */ product XIRLINK PCCAM 0x8080 IBM PC Camera /* Xyratex products */ product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN /* Y-E Data products */ product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U /* Yamaha products */ product YAMAHA UX256 0x1000 UX256 MIDI I/F product YAMAHA UX96 0x1008 UX96 MIDI I/F product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router /* Yano products */ product YANO U640MO 0x0101 U640MO-03 product YANO FW800HD 0x05fc METALWEAR-HDD + +/* Yiso Wireless Co. products */ +product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card /* Z-Com products */ product ZCOM M4Y750 0x0001 M4Y-750 product ZCOM XI725 0x0002 XI-725/726 product ZCOM XI735 0x0005 XI-735 product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN product ZCOM ZD1211 0x0011 ZD1211 product ZCOM AR5523 0x0012 AR5523 product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware) product ZCOM ZD1211B 0x001a ZD1211B /* Zinwell products */ product ZINWELL RT2570 0x0260 RT2570 /* Zoom Telephonics, Inc. products */ product ZOOM 2986L 0x9700 2986L Fax modem /* Zoran Microelectronics products */ product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC /* Zydas Technology Corporation products */ product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg product ZYDAS ZD1211B 0x1215 ZD1211B /* ZyXEL Communication Co. products */ product ZYXEL OMNI56K 0x1500 Omni 56K Plus product ZYXEL 980N 0x2011 Scorpion-980N keyboard product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220 product ZYXEL G200V2 0x3407 G-200 v2 product ZYXEL AG225H 0x3409 AG-225H product ZYXEL M202 0x340a M-202 product ZYXEL G220V2 0x340f G-220 v2 product ZYXEL G202 0x3410 G-202 Index: projects/cambria/sys/dev/usb/uscanner.c =================================================================== --- projects/cambria/sys/dev/usb/uscanner.c (revision 186459) +++ projects/cambria/sys/dev/usb/uscanner.c (revision 186460) @@ -1,707 +1,708 @@ /* $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp$ */ /* Also already merged from NetBSD: * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology * and Nick Hibma (n_hibma@qubesoft.com). * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #ifdef USB_DEBUG #define DPRINTF(x) if (uscannerdebug) printf x #define DPRINTFN(n,x) if (uscannerdebug>(n)) printf x int uscannerdebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner"); SYSCTL_INT(_hw_usb_uscanner, OID_AUTO, debug, CTLFLAG_RW, &uscannerdebug, 0, "uscanner debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct uscan_info { struct usb_devno devno; u_int flags; #define USC_KEEP_OPEN 1 }; /* Table of scanners that may work with this driver. */ static const struct uscan_info uscanner_devs[] = { /* Acer Peripherals */ {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U }, 0 }, {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_4300U }, 0 }, {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U }, 0 }, {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT }, 0 }, {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U }, 0 }, {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U }, 0 }, {{ USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U }, 0 }, /* AGFA */ {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2 }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40 }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50 }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20 }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25 }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26 }, 0 }, {{ USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52 }, 0 }, /* Avision */ {{ USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U }, 0 }, /* Canon */ {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U }, 0 }, {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U }, 0 }, {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U }, 0 }, {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U }, 0 }, {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U }, 0 }, {{ USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25 }, 0 }, /* Kye */ {{ USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO }, 0 }, /* HP */ {{ USB_VENDOR_HP, USB_PRODUCT_HP_2200C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_3300C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_4100C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_4200C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_4300C }, 0 }, + {{ USB_VENDOR_HP, USB_PRODUCT_HP_4470C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_4670V }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_S20 }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_5200C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_5300C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_5400C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_6200C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_6300C }, 0 }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_82x0C }, 0 }, /* Microtek */ {{ USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2 }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6 }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2 }, 0 }, {{ USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL }, 0 }, /* Minolta */ {{ USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400 }, 0 }, /* Mustek */ {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS }, 0 }, {{ USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS }, 0 }, /* National */ {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200 }, 0 }, {{ USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400 }, 0 }, /* Nikon */ {{ USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40 }, 0 }, /* Primax */ {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600 }, 0 }, {{ USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600 }, 0 }, /* Epson */ {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1270 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200 }, USC_KEEP_OPEN }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F }, USC_KEEP_OPEN }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3500 }, USC_KEEP_OPEN }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3590 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4200 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4800 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4990 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_5000 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX7400 }, 0 }, {{ USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX8400 }, 0 }, /* UMAX */ {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U }, 0 }, {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U }, 0 }, {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U }, 0 }, {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U }, 0 }, {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U }, 0 }, {{ USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400 }, 0 }, /* Visioneer */ {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000 }, 0 }, {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300 }, 0 }, {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600 }, 0 }, {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100 }, 0 }, {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200 }, 0 }, {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100 }, 0 }, {{ USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600 }, 0 }, /* Ultima */ {{ USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS }, 0 }, }; #define uscanner_lookup(v, p) ((const struct uscan_info *)usb_lookup(uscanner_devs, v, p)) #define USCANNER_BUFFERSIZE 1024 struct uscanner_softc { device_t sc_dev; /* base device */ usbd_device_handle sc_udev; usbd_interface_handle sc_iface; struct cdev *dev; u_int sc_dev_flags; usbd_pipe_handle sc_bulkin_pipe; int sc_bulkin; usbd_xfer_handle sc_bulkin_xfer; void *sc_bulkin_buffer; int sc_bulkin_bufferlen; int sc_bulkin_datalen; usbd_pipe_handle sc_bulkout_pipe; int sc_bulkout; usbd_xfer_handle sc_bulkout_xfer; void *sc_bulkout_buffer; int sc_bulkout_bufferlen; int sc_bulkout_datalen; u_char sc_state; #define USCANNER_OPEN 0x01 /* opened */ int sc_refcnt; u_char sc_dying; }; d_open_t uscanneropen; d_close_t uscannerclose; d_read_t uscannerread; d_write_t uscannerwrite; d_poll_t uscannerpoll; static struct cdevsw uscanner_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = uscanneropen, .d_close = uscannerclose, .d_read = uscannerread, .d_write = uscannerwrite, .d_poll = uscannerpoll, .d_name = "uscanner", }; static int uscanner_do_read(struct uscanner_softc *, struct uio *, int); static int uscanner_do_write(struct uscanner_softc *, struct uio *, int); static void uscanner_do_close(struct uscanner_softc *); #define USCANNERUNIT(n) (dev2unit(n)) static device_probe_t uscanner_match; static device_attach_t uscanner_attach; static device_detach_t uscanner_detach; static device_method_t uscanner_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uscanner_match), DEVMETHOD(device_attach, uscanner_attach), DEVMETHOD(device_detach, uscanner_detach), { 0, 0 } }; static driver_t uscanner_driver = { "uscanner", uscanner_methods, sizeof(struct uscanner_softc) }; static devclass_t uscanner_devclass; static int uscanner_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); usb_interface_descriptor_t *id; if (uaa->iface == NULL) return UMATCH_NONE; /* do not grab the entire device */ if (uscanner_lookup(uaa->vendor, uaa->product) == NULL) return UMATCH_NONE; /* not in the list of known devices */ id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL) return UMATCH_NONE; /* * There isn't a specific UICLASS for scanners, many vendors use * UICLASS_VENDOR, so detecting the right interface is not so easy. * But certainly we can exclude PRINTER and MASS - which some * multifunction devices implement. */ if (id->bInterfaceClass == UICLASS_PRINTER || id->bInterfaceClass == UICLASS_MASS) return UMATCH_NONE; return UMATCH_VENDOR_PRODUCT; /* ok we found it */ } static int uscanner_attach(device_t self) { struct uscanner_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); usb_interface_descriptor_t *id = 0; usb_endpoint_descriptor_t *ed, *ed_bulkin = NULL, *ed_bulkout = NULL; int i; usbd_status err; int ifnum; sc->sc_dev = self; sc->sc_dev_flags = uscanner_lookup(uaa->vendor, uaa->product)->flags; sc->sc_udev = uaa->device; id = usbd_get_interface_descriptor(uaa->iface); ifnum = id->bInterfaceNumber; #if 0 /* * This was in the original driver, but we cannot change the * configuration of the whole device while attaching only to * one of its interfaces. This can kill other already-attached * driver, and/or possibly prevent this driver from attaching * if an error occurs in set_config_no. * If a device need setting the configuration, this must be done * before attaching drivers to the various interfaces. */ err = usbd_set_config_no(uaa->device, 1, 1); /* XXX */ if (err) { printf("%s: setting config no failed\n", device_get_nameunit(sc->sc_dev)); return ENXIO; } #endif err = usbd_device2interface_handle(sc->sc_udev, ifnum, &sc->sc_iface); if (!err && sc->sc_iface) id = usbd_get_interface_descriptor(sc->sc_iface); if (err || id == 0) { printf("%s: could not get interface descriptor, err=%d,id=%p\n", device_get_nameunit(sc->sc_dev), err, id); return ENXIO; } /* Find the two first bulk endpoints */ for (i = 0 ; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); if (ed == 0) { printf("%s: could not read endpoint descriptor\n", device_get_nameunit(sc->sc_dev)); return ENXIO; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { ed_bulkin = ed; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { ed_bulkout = ed; } if (ed_bulkin && ed_bulkout) /* found all we need */ break; } /* Verify that we goething sensible */ if (ed_bulkin == NULL || ed_bulkout == NULL) { printf("%s: bulk-in and/or bulk-out endpoint not found\n", device_get_nameunit(sc->sc_dev)); return ENXIO; } sc->sc_bulkin = ed_bulkin->bEndpointAddress; sc->sc_bulkout = ed_bulkout->bEndpointAddress; /* the main device, ctrl endpoint */ sc->dev = make_dev(&uscanner_cdevsw, device_get_unit(sc->sc_dev), UID_ROOT, GID_OPERATOR, 0644, "%s", device_get_nameunit(sc->sc_dev)); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,sc->sc_dev); return 0; } int uscanneropen(struct cdev *dev, int flag, int mode, struct thread *p) { struct uscanner_softc *sc; int unit = USCANNERUNIT(dev); usbd_status err; sc = devclass_get_softc(uscanner_devclass, unit); if (sc == NULL) return (ENXIO); DPRINTFN(5, ("uscanneropen: flag=%d, mode=%d, unit=%d\n", flag, mode, unit)); if (sc->sc_dying) return (ENXIO); if (sc->sc_state & USCANNER_OPEN) return (EBUSY); sc->sc_state |= USCANNER_OPEN; sc->sc_bulkin_buffer = malloc(USCANNER_BUFFERSIZE, M_USBDEV, M_WAITOK); sc->sc_bulkout_buffer = malloc(USCANNER_BUFFERSIZE, M_USBDEV, M_WAITOK); /* No need to check buffers for NULL since we have WAITOK */ sc->sc_bulkin_bufferlen = USCANNER_BUFFERSIZE; sc->sc_bulkout_bufferlen = USCANNER_BUFFERSIZE; /* We have decided on which endpoints to use, now open the pipes */ if (sc->sc_bulkin_pipe == NULL) { err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin, USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); if (err) { printf("%s: cannot open bulk-in pipe (addr %d)\n", device_get_nameunit(sc->sc_dev), sc->sc_bulkin); uscanner_do_close(sc); return (EIO); } } if (sc->sc_bulkout_pipe == NULL) { err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout, USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); if (err) { printf("%s: cannot open bulk-out pipe (addr %d)\n", device_get_nameunit(sc->sc_dev), sc->sc_bulkout); uscanner_do_close(sc); return (EIO); } } sc->sc_bulkin_xfer = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_bulkin_xfer == NULL) { uscanner_do_close(sc); return (ENOMEM); } sc->sc_bulkout_xfer = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_bulkout_xfer == NULL) { uscanner_do_close(sc); return (ENOMEM); } return (0); /* success */ } int uscannerclose(struct cdev *dev, int flag, int mode, struct thread *p) { struct uscanner_softc *sc; sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev)); DPRINTFN(5, ("uscannerclose: flag=%d, mode=%d, unit=%d\n", flag, mode, USCANNERUNIT(dev))); #ifdef DIAGNOSTIC if (!(sc->sc_state & USCANNER_OPEN)) { printf("uscannerclose: not open\n"); return (EINVAL); } #endif uscanner_do_close(sc); return (0); } void uscanner_do_close(struct uscanner_softc *sc) { if (sc->sc_bulkin_xfer) { usbd_free_xfer(sc->sc_bulkin_xfer); sc->sc_bulkin_xfer = NULL; } if (sc->sc_bulkout_xfer) { usbd_free_xfer(sc->sc_bulkout_xfer); sc->sc_bulkout_xfer = NULL; } if (!(sc->sc_dev_flags & USC_KEEP_OPEN)) { if (sc->sc_bulkin_pipe != NULL) { usbd_abort_pipe(sc->sc_bulkin_pipe); usbd_close_pipe(sc->sc_bulkin_pipe); sc->sc_bulkin_pipe = NULL; } if (sc->sc_bulkout_pipe != NULL) { usbd_abort_pipe(sc->sc_bulkout_pipe); usbd_close_pipe(sc->sc_bulkout_pipe); sc->sc_bulkout_pipe = NULL; } } if (sc->sc_bulkin_buffer) { free(sc->sc_bulkin_buffer, M_USBDEV); sc->sc_bulkin_buffer = NULL; } if (sc->sc_bulkout_buffer) { free(sc->sc_bulkout_buffer, M_USBDEV); sc->sc_bulkout_buffer = NULL; } sc->sc_state &= ~USCANNER_OPEN; } static int uscanner_do_read(struct uscanner_softc *sc, struct uio *uio, int flag) { u_int32_t n, tn; usbd_status err; int error = 0; DPRINTFN(5, ("%s: uscannerread\n", device_get_nameunit(sc->sc_dev))); if (sc->sc_dying) return (EIO); while ((n = min(sc->sc_bulkin_bufferlen, uio->uio_resid)) != 0) { DPRINTFN(1, ("uscannerread: start transfer %d bytes\n",n)); tn = n; err = usbd_bulk_transfer( sc->sc_bulkin_xfer, sc->sc_bulkin_pipe, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, sc->sc_bulkin_buffer, &tn, "uscnrb"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; else if (err == USBD_TIMEOUT) error = ETIMEDOUT; else error = EIO; break; } DPRINTFN(1, ("uscannerread: got %d bytes\n", tn)); error = uiomove(sc->sc_bulkin_buffer, tn, uio); if (error || tn < n) break; } return (error); } int uscannerread(struct cdev *dev, struct uio *uio, int flag) { struct uscanner_softc *sc; int error; sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev)); sc->sc_refcnt++; error = uscanner_do_read(sc, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(sc->sc_dev); return (error); } static int uscanner_do_write(struct uscanner_softc *sc, struct uio *uio, int flag) { u_int32_t n; int error = 0; usbd_status err; DPRINTFN(5, ("%s: uscanner_do_write\n", device_get_nameunit(sc->sc_dev))); if (sc->sc_dying) return (EIO); while ((n = min(sc->sc_bulkout_bufferlen, uio->uio_resid)) != 0) { error = uiomove(sc->sc_bulkout_buffer, n, uio); if (error) break; DPRINTFN(1, ("uscanner_do_write: transfer %d bytes\n", n)); err = usbd_bulk_transfer( sc->sc_bulkout_xfer, sc->sc_bulkout_pipe, 0, USBD_NO_TIMEOUT, sc->sc_bulkout_buffer, &n, "uscnwb"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; else error = EIO; break; } } return (error); } int uscannerwrite(struct cdev *dev, struct uio *uio, int flag) { struct uscanner_softc *sc; int error; sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev)); sc->sc_refcnt++; error = uscanner_do_write(sc, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(sc->sc_dev); return (error); } static int uscanner_detach(device_t self) { struct uscanner_softc *sc = device_get_softc(self); int s; DPRINTF(("uscanner_detach: sc=%p\n", sc)); sc->sc_dying = 1; sc->sc_dev_flags = 0; /* make close really close device */ /* Abort all pipes. Causes processes waiting for transfer to wake. */ if (sc->sc_bulkin_pipe != NULL) usbd_abort_pipe(sc->sc_bulkin_pipe); if (sc->sc_bulkout_pipe != NULL) usbd_abort_pipe(sc->sc_bulkout_pipe); s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wait for processes to go away. */ usb_detach_wait(sc->sc_dev); } splx(s); /* destroy the device for the control endpoint */ destroy_dev(sc->dev); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); return (0); } int uscannerpoll(struct cdev *dev, int events, struct thread *p) { struct uscanner_softc *sc; int revents = 0; sc = devclass_get_softc(uscanner_devclass, USCANNERUNIT(dev)); if (sc->sc_dying) return (EIO); /* * We have no easy way of determining if a read will * yield any data or a write will happen. * Pretend they will. */ revents |= events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); return (revents); } MODULE_DEPEND(uscanner, usb, 1, 1, 1); DRIVER_MODULE(uscanner, uhub, uscanner_driver, uscanner_devclass, usbd_driver_load, 0); Index: projects/cambria/sys/dev/usb2/controller/at91dci.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/at91dci.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/at91dci.c (revision 186460) @@ -1,2499 +1,2496 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2007-2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * This file contains the driver for the AT91 series USB Device * Controller */ /* * Thanks to "David Brownell" for helping out regarding the hardware * endpoint profiles. */ /* * NOTE: The "fifo_bank" is not reset in hardware when the endpoint is * reset ! * * NOTE: When the chip detects BUS-reset it will also reset the * endpoints, Function-address and more. */ #include #include #include #include #define USB_DEBUG_VAR at91dcidebug #define usb2_config_td_cc at91dci_config_copy #define usb2_config_td_softc at91dci_softc #include #include #include #include #include #include #include #include #include #include #include #include #include #define AT9100_DCI_BUS2SC(bus) \ ((struct at91dci_softc *)(((uint8_t *)(bus)) - \ USB_P2U(&(((struct at91dci_softc *)0)->sc_bus)))) #define AT9100_DCI_PC2SC(pc) \ AT9100_DCI_BUS2SC((pc)->tag_parent->info->bus) #if USB_DEBUG static int at91dcidebug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, at91dci, CTLFLAG_RW, 0, "USB at91dci"); SYSCTL_INT(_hw_usb2_at91dci, OID_AUTO, debug, CTLFLAG_RW, &at91dcidebug, 0, "at91dci debug level"); #endif #define AT9100_DCI_INTR_ENDPT 1 /* prototypes */ struct usb2_bus_methods at91dci_bus_methods; struct usb2_pipe_methods at91dci_device_bulk_methods; struct usb2_pipe_methods at91dci_device_ctrl_methods; struct usb2_pipe_methods at91dci_device_intr_methods; struct usb2_pipe_methods at91dci_device_isoc_fs_methods; struct usb2_pipe_methods at91dci_root_ctrl_methods; struct usb2_pipe_methods at91dci_root_intr_methods; static at91dci_cmd_t at91dci_setup_rx; static at91dci_cmd_t at91dci_data_rx; static at91dci_cmd_t at91dci_data_tx; static at91dci_cmd_t at91dci_data_tx_sync; static void at91dci_device_done(struct usb2_xfer *, usb2_error_t); static void at91dci_do_poll(struct usb2_bus *); static void at91dci_root_ctrl_poll(struct at91dci_softc *); static void at91dci_standard_done(struct usb2_xfer *); static usb2_sw_transfer_func_t at91dci_root_intr_done; static usb2_sw_transfer_func_t at91dci_root_ctrl_done; static usb2_config_td_command_t at91dci_root_ctrl_task; /* * NOTE: Some of the bits in the CSR register have inverse meaning so * we need a helper macro when acknowledging events: */ #define AT91_CSR_ACK(csr, what) do { \ (csr) &= ~((AT91_UDP_CSR_FORCESTALL| \ AT91_UDP_CSR_TXPKTRDY| \ AT91_UDP_CSR_RXBYTECNT) ^ (what));\ (csr) |= ((AT91_UDP_CSR_RX_DATA_BK0| \ AT91_UDP_CSR_RX_DATA_BK1| \ AT91_UDP_CSR_TXCOMP| \ AT91_UDP_CSR_RXSETUP| \ AT91_UDP_CSR_STALLSENT) ^ (what)); \ } while (0) /* * Here is a list of what the chip supports. * Probably it supports more than listed here! */ static const struct usb2_hw_ep_profile at91dci_ep_profile[AT91_UDP_EP_MAX] = { [0] = { .max_in_frame_size = 8, .max_out_frame_size = 8, .is_simplex = 1, .support_control = 1, }, [1] = { .max_in_frame_size = 64, .max_out_frame_size = 64, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, [2] = { .max_in_frame_size = 64, .max_out_frame_size = 64, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, [3] = { /* can also do BULK */ .max_in_frame_size = 8, .max_out_frame_size = 8, .is_simplex = 1, .support_interrupt = 1, .support_in = 1, .support_out = 1, }, [4] = { .max_in_frame_size = 256, .max_out_frame_size = 256, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, [5] = { .max_in_frame_size = 256, .max_out_frame_size = 256, .is_simplex = 1, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, }; static void at91dci_get_hw_ep_profile(struct usb2_device *udev, const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) { if (ep_addr < AT91_UDP_EP_MAX) { *ppf = (at91dci_ep_profile + ep_addr); } else { *ppf = NULL; } } static void at91dci_clocks_on(struct at91dci_softc *sc) { if (sc->sc_flags.clocks_off && sc->sc_flags.port_powered) { DPRINTFN(5, "\n"); if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } sc->sc_flags.clocks_off = 0; /* enable Transceiver */ AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, 0); } } static void at91dci_clocks_off(struct at91dci_softc *sc) { if (!sc->sc_flags.clocks_off) { DPRINTFN(5, "\n"); /* disable Transceiver */ AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); if (sc->sc_clocks_off) { (sc->sc_clocks_off) (sc->sc_clocks_arg); } sc->sc_flags.clocks_off = 1; } } static void at91dci_pull_up(struct at91dci_softc *sc) { /* pullup D+, if possible */ if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) { sc->sc_flags.d_pulled_up = 1; (sc->sc_pull_up) (sc->sc_pull_arg); } } static void at91dci_pull_down(struct at91dci_softc *sc) { /* pulldown D+, if possible */ if (sc->sc_flags.d_pulled_up) { sc->sc_flags.d_pulled_up = 0; (sc->sc_pull_down) (sc->sc_pull_arg); } } static void at91dci_wakeup_peer(struct at91dci_softc *sc) { uint32_t temp; if (!(sc->sc_flags.status_suspend)) { return; } temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE); if (!(temp & AT91_UDP_GSTATE_ESR)) { return; } AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp); } static void at91dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) { struct at91dci_softc *sc; uint32_t temp; DPRINTFN(5, "is_on=%u\n", is_on); USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); sc = AT9100_DCI_BUS2SC(udev->bus); temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE); if (is_on) { temp |= AT91_UDP_GSTATE_ESR; } else { temp &= ~AT91_UDP_GSTATE_ESR; } AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp); } static void at91dci_set_address(struct at91dci_softc *sc, uint8_t addr) { DPRINTFN(5, "addr=%d\n", addr); AT91_UDP_WRITE_4(sc, AT91_UDP_FADDR, addr | AT91_UDP_FADDR_EN); } static uint8_t at91dci_setup_rx(struct at91dci_td *td) { struct at91dci_softc *sc; struct usb2_device_request req; uint32_t csr; uint32_t temp; uint16_t count; /* read out FIFO status */ csr = bus_space_read_4(td->io_tag, td->io_hdl, td->status_reg); DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); temp = csr; temp &= (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1 | AT91_UDP_CSR_STALLSENT | AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_TXCOMP); if (!(csr & AT91_UDP_CSR_RXSETUP)) { /* abort any ongoing transfer */ if (!td->did_stall) { DPRINTFN(5, "stalling\n"); temp |= AT91_UDP_CSR_FORCESTALL; td->did_stall = 1; } goto not_complete; } /* get the packet byte count */ count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16; /* verify data length */ if (count != td->remainder) { DPRINTFN(0, "Invalid SETUP packet " "length, %d bytes\n", count); goto not_complete; } if (count != sizeof(req)) { DPRINTFN(0, "Unsupported SETUP packet " "length, %d bytes\n", count); goto not_complete; } /* receive data */ bus_space_read_multi_1(td->io_tag, td->io_hdl, td->fifo_reg, (void *)&req, sizeof(req)); /* copy data into real buffer */ usb2_copy_in(td->pc, 0, &req, sizeof(req)); td->offset = sizeof(req); td->remainder = 0; /* get pointer to softc */ sc = AT9100_DCI_PC2SC(td->pc); /* sneak peek the set address */ if ((req.bmRequestType == UT_WRITE_DEVICE) && (req.bRequest == UR_SET_ADDRESS)) { sc->sc_dv_addr = req.wValue[0] & 0x7F; } else { sc->sc_dv_addr = 0xFF; } /* sneak peek the endpoint direction */ if (req.bmRequestType & UE_DIR_IN) { csr |= AT91_UDP_CSR_DIR; } else { csr &= ~AT91_UDP_CSR_DIR; } /* write the direction of the control transfer */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); return (0); /* complete */ not_complete: /* clear interrupts, if any */ if (temp) { DPRINTFN(5, "clearing 0x%08x\n", temp); AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); } return (1); /* not complete */ } static uint8_t at91dci_data_rx(struct at91dci_td *td) { struct usb2_page_search buf_res; uint32_t csr; uint32_t temp; uint16_t count; uint8_t to; uint8_t got_short; to = 2; /* don't loop forever! */ got_short = 0; /* check if any of the FIFO banks have data */ repeat: /* read out FIFO status */ csr = bus_space_read_4(td->io_tag, td->io_hdl, td->status_reg); DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); if (csr & AT91_UDP_CSR_RXSETUP) { if (td->remainder == 0) { /* * We are actually complete and have * received the next SETUP */ DPRINTFN(5, "faking complete\n"); return (0); /* complete */ } /* * USB Host Aborted the transfer. */ td->error = 1; return (0); /* complete */ } /* Make sure that "STALLSENT" gets cleared */ temp = csr; temp &= AT91_UDP_CSR_STALLSENT; /* check status */ if (!(csr & (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1))) { if (temp) { /* write command */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); } return (1); /* not complete */ } /* get the packet byte count */ count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16; /* verify the packet byte count */ if (count != td->max_packet_size) { if (count < td->max_packet_size) { /* we have a short packet */ td->short_pkt = 1; got_short = 1; } else { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } while (count > 0) { usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* receive data */ bus_space_read_multi_1(td->io_tag, td->io_hdl, td->fifo_reg, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* clear status bits */ if (td->support_multi_buffer) { if (td->fifo_bank) { td->fifo_bank = 0; temp |= AT91_UDP_CSR_RX_DATA_BK1; } else { td->fifo_bank = 1; temp |= AT91_UDP_CSR_RX_DATA_BK0; } } else { temp |= (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1); } /* write command */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); /* * NOTE: We may have to delay a little bit before * proceeding after clearing the DATA_BK bits. */ /* check if we are complete */ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ return (0); } /* else need to receive a zero length packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t at91dci_data_tx(struct at91dci_td *td) { struct usb2_page_search buf_res; uint32_t csr; uint32_t temp; uint16_t count; uint8_t to; to = 2; /* don't loop forever! */ repeat: /* read out FIFO status */ csr = bus_space_read_4(td->io_tag, td->io_hdl, td->status_reg); DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder); if (csr & AT91_UDP_CSR_RXSETUP) { /* * The current transfer was aborted * by the USB Host */ td->error = 1; return (0); /* complete */ } /* Make sure that "STALLSENT" gets cleared */ temp = csr; temp &= AT91_UDP_CSR_STALLSENT; if (csr & AT91_UDP_CSR_TXPKTRDY) { if (temp) { /* write command */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); } return (1); /* not complete */ } else { /* clear TXCOMP and set TXPKTRDY */ temp |= (AT91_UDP_CSR_TXCOMP | AT91_UDP_CSR_TXPKTRDY); } count = td->max_packet_size; if (td->remainder < count) { /* we have a short packet */ td->short_pkt = 1; count = td->remainder; } while (count > 0) { usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* transmit data */ bus_space_write_multi_1(td->io_tag, td->io_hdl, td->fifo_reg, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* write command */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) { return (0); /* complete */ } /* else we need to transmit a short packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t at91dci_data_tx_sync(struct at91dci_td *td) { struct at91dci_softc *sc; uint32_t csr; uint32_t temp; #if 0 repeat: #endif /* read out FIFO status */ csr = bus_space_read_4(td->io_tag, td->io_hdl, td->status_reg); DPRINTFN(5, "csr=0x%08x\n", csr); if (csr & AT91_UDP_CSR_RXSETUP) { DPRINTFN(5, "faking complete\n"); /* Race condition */ return (0); /* complete */ } temp = csr; temp &= (AT91_UDP_CSR_STALLSENT | AT91_UDP_CSR_TXCOMP); /* check status */ if (csr & AT91_UDP_CSR_TXPKTRDY) { goto not_complete; } if (!(csr & AT91_UDP_CSR_TXCOMP)) { goto not_complete; } sc = AT9100_DCI_PC2SC(td->pc); if (sc->sc_dv_addr != 0xFF) { /* * The AT91 has a special requirement with regard to * setting the address and that is to write the new * address before clearing TXCOMP: */ at91dci_set_address(sc, sc->sc_dv_addr); } /* write command */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); return (0); /* complete */ not_complete: if (temp) { /* write command */ AT91_CSR_ACK(csr, temp); bus_space_write_4(td->io_tag, td->io_hdl, td->status_reg, csr); } return (1); /* not complete */ } static uint8_t at91dci_xfer_do_fifo(struct usb2_xfer *xfer) { struct at91dci_softc *sc; struct at91dci_td *td; uint8_t temp; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; while (1) { if ((td->func) (td)) { /* operation in progress */ break; } if (((void *)td) == xfer->td_transfer_last) { goto done; } if (td->error) { goto done; } else if (td->remainder > 0) { /* * We had a short transfer. If there is no alternate * next, stop processing ! */ if (!td->alt_next) { goto done; } } /* * Fetch the next transfer descriptor and transfer * some flags to the next transfer descriptor */ temp = 0; if (td->fifo_bank) temp |= 1; td = td->obj_next; xfer->td_transfer_cache = td; if (temp & 1) td->fifo_bank = 1; } return (1); /* not complete */ done: sc = xfer->usb2_sc; temp = (xfer->endpoint & UE_ADDR); /* update FIFO bank flag and multi buffer */ if (td->fifo_bank) { sc->sc_ep_flags[temp].fifo_bank = 1; } else { sc->sc_ep_flags[temp].fifo_bank = 0; } /* compute all actual lengths */ at91dci_standard_done(xfer); return (0); /* complete */ } static void at91dci_interrupt_poll(struct at91dci_softc *sc) { struct usb2_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (!at91dci_xfer_do_fifo(xfer)) { /* queue has been modified */ goto repeat; } } } static void at91dci_vbus_interrupt(struct usb2_bus *bus, uint8_t is_on) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus); DPRINTFN(5, "vbus = %u\n", is_on); USB_BUS_LOCK(&sc->sc_bus); if (is_on) { if (!sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 1; /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &at91dci_root_intr_done); } } else { if (sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &at91dci_root_intr_done); } } USB_BUS_UNLOCK(&sc->sc_bus); } void at91dci_interrupt(struct at91dci_softc *sc) { uint32_t status; USB_BUS_LOCK(&sc->sc_bus); status = AT91_UDP_READ_4(sc, AT91_UDP_ISR); status &= AT91_UDP_INT_DEFAULT; if (!status) { USB_BUS_UNLOCK(&sc->sc_bus); return; } /* acknowledge interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, status); /* check for any bus state change interrupts */ if (status & AT91_UDP_INT_BUS) { DPRINTFN(5, "real bus interrupt 0x%08x\n", status); if (status & AT91_UDP_INT_END_BR) { /* set correct state */ sc->sc_flags.status_bus_reset = 1; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* disable resume interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_RXRSM); /* enable suspend interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_RXSUSP); } /* * If RXRSM and RXSUSP is set at the same time we interpret * that like RESUME. Resume is set when there is at least 3 * milliseconds of inactivity on the USB BUS. */ if (status & AT91_UDP_INT_RXRSM) { if (sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 1; /* disable resume interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_RXRSM); /* enable suspend interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_RXSUSP); } } else if (status & AT91_UDP_INT_RXSUSP) { if (!sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 1; sc->sc_flags.change_suspend = 1; /* disable suspend interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_RXSUSP); /* enable resume interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_RXRSM); } } /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &at91dci_root_intr_done); } /* check for any endpoint interrupts */ if (status & AT91_UDP_INT_EPS) { DPRINTFN(5, "real endpoint interrupt 0x%08x\n", status); at91dci_interrupt_poll(sc); } USB_BUS_UNLOCK(&sc->sc_bus); } static void at91dci_setup_standard_chain_sub(struct at91dci_std_temp *temp) { struct at91dci_td *td; /* get current Transfer Descriptor */ td = temp->td_next; temp->td = td; /* prepare for next TD */ temp->td_next = td->obj_next; /* fill out the Transfer Descriptor */ td->func = temp->func; td->pc = temp->pc; td->offset = temp->offset; td->remainder = temp->len; td->fifo_bank = 0; td->error = 0; td->did_stall = 0; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; } static void at91dci_setup_standard_chain(struct usb2_xfer *xfer) { struct at91dci_std_temp temp; struct at91dci_softc *sc; struct at91dci_td *td; uint32_t x; uint8_t ep_no; uint8_t need_sync; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->sumlen, usb2_get_speed(xfer->udev)); temp.max_frame_size = xfer->max_frame_size; td = xfer->td_start[0]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; /* setup temp */ temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.offset = 0; sc = xfer->usb2_sc; ep_no = (xfer->endpoint & UE_ADDR); /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.func = &at91dci_setup_rx; temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; at91dci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } if (x != xfer->nframes) { if (xfer->endpoint & UE_DIR_IN) { temp.func = &at91dci_data_tx; need_sync = 1; } else { temp.func = &at91dci_data_rx; need_sync = 0; } /* setup "pc" pointer */ temp.pc = xfer->frbuffers + x; } else { need_sync = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; x++; if (x == xfer->nframes) { temp.setup_alt_next = 0; } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.short_pkt = 0; } else { /* regular data transfer */ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; } at91dci_setup_standard_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += temp.len; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } /* always setup a valid "pc" pointer for status and sync */ temp.pc = xfer->frbuffers + 0; /* check if we need to sync */ if (need_sync && xfer->flags_int.control_xfr) { /* we need a SYNC point after TX */ temp.func = &at91dci_data_tx_sync; temp.len = 0; temp.short_pkt = 0; at91dci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current * endpoint direction. */ if (xfer->endpoint & UE_DIR_IN) { temp.func = &at91dci_data_rx; need_sync = 0; } else { temp.func = &at91dci_data_tx; need_sync = 1; } temp.len = 0; temp.short_pkt = 0; at91dci_setup_standard_chain_sub(&temp); if (need_sync) { /* we need a SYNC point after TX */ temp.func = &at91dci_data_tx_sync; temp.len = 0; temp.short_pkt = 0; at91dci_setup_standard_chain_sub(&temp); } } /* must have at least one frame! */ td = temp.td; xfer->td_transfer_last = td; /* setup the correct fifo bank */ if (sc->sc_ep_flags[ep_no].fifo_bank) { td = xfer->td_transfer_first; td->fifo_bank = 1; } } static void at91dci_timeout(void *arg) { struct usb2_xfer *xfer = arg; - struct at91dci_softc *sc = xfer->usb2_sc; DPRINTF("xfer=%p\n", xfer); - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* transfer is transferred */ at91dci_device_done(xfer, USB_ERR_TIMEOUT); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void at91dci_start_standard_chain(struct usb2_xfer *xfer) { DPRINTFN(9, "\n"); /* poll one time */ if (at91dci_xfer_do_fifo(xfer)) { struct at91dci_softc *sc = xfer->usb2_sc; uint8_t ep_no = xfer->endpoint & UE_ADDR; /* * Only enable the endpoint interrupt when we are actually * waiting for data, hence we are dealing with level * triggered interrupts ! */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_EP(ep_no)); DPRINTFN(15, "enable interrupts on endpoint %d\n", ep_no); /* put transfer on interrupt queue */ usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usb2_transfer_timeout_ms(xfer, &at91dci_timeout, xfer->timeout); } } } static void at91dci_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct at91dci_softc *sc = xfer->usb2_sc; DPRINTFN(9, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_PRE_DATA) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ at91dci_device_done(xfer, std->err); } goto done; } /* setup buffer */ std->ptr = sc->sc_hub_idata; std->len = sizeof(sc->sc_hub_idata); /* set port bit */ sc->sc_hub_idata[0] = 0x02; /* we only have one port */ done: return; } static usb2_error_t at91dci_standard_done_sub(struct usb2_xfer *xfer) { struct at91dci_td *td; uint32_t len; uint8_t error; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; do { len = td->remainder; if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract * the remainder from "frlengths[]": */ if (len > xfer->frlengths[xfer->aframes]) { td->error = 1; } else { xfer->frlengths[xfer->aframes] -= len; } } /* Check for transfer error */ if (td->error) { /* the transfer is finished */ error = 1; td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { td = td->obj_next; } else { td = NULL; } } else { /* the transfer is finished */ td = NULL; } error = 0; break; } td = td->obj_next; /* this USB frame is complete */ error = 0; break; } while (0); /* update transfer cache */ xfer->td_transfer_cache = td; return (error ? USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); } static void at91dci_standard_done(struct usb2_xfer *xfer) { usb2_error_t err = 0; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = at91dci_standard_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = at91dci_standard_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = at91dci_standard_done_sub(xfer); } done: at91dci_device_done(xfer, err); } /*------------------------------------------------------------------------* * at91dci_device_done * * NOTE: this function can be called more than one time on the * same USB transfer! *------------------------------------------------------------------------*/ static void at91dci_device_done(struct usb2_xfer *xfer, usb2_error_t error) { struct at91dci_softc *sc = xfer->usb2_sc; uint8_t ep_no; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error); if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { ep_no = (xfer->endpoint & UE_ADDR); /* disable endpoint interrupt */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_EP(ep_no)); DPRINTFN(15, "disable interrupts on endpoint %d\n", ep_no); } /* dequeue transfer and start next transfer */ usb2_transfer_done(xfer, error); } static void at91dci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, struct usb2_pipe *pipe) { struct at91dci_softc *sc; uint32_t csr_val; uint8_t csr_reg; USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); DPRINTFN(5, "pipe=%p\n", pipe); if (xfer) { /* cancel any ongoing transfers */ at91dci_device_done(xfer, USB_ERR_STALLED); } /* set FORCESTALL */ sc = AT9100_DCI_BUS2SC(udev->bus); csr_reg = (pipe->edesc->bEndpointAddress & UE_ADDR); csr_reg = AT91_UDP_CSR(csr_reg); csr_val = AT91_UDP_READ_4(sc, csr_reg); AT91_CSR_ACK(csr_val, AT91_UDP_CSR_FORCESTALL); AT91_UDP_WRITE_4(sc, csr_reg, csr_val); } static void at91dci_clear_stall_sub(struct at91dci_softc *sc, uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) { const struct usb2_hw_ep_profile *pf; uint32_t csr_val; uint32_t temp; uint8_t csr_reg; uint8_t to; if (ep_type == UE_CONTROL) { /* clearing stall is not needed */ return; } /* compute CSR register offset */ csr_reg = AT91_UDP_CSR(ep_no); /* compute default CSR value */ csr_val = 0; AT91_CSR_ACK(csr_val, 0); /* disable endpoint */ AT91_UDP_WRITE_4(sc, csr_reg, csr_val); /* get endpoint profile */ at91dci_get_hw_ep_profile(NULL, &pf, ep_no); /* reset FIFO */ AT91_UDP_WRITE_4(sc, AT91_UDP_RST, AT91_UDP_RST_EP(ep_no)); AT91_UDP_WRITE_4(sc, AT91_UDP_RST, 0); /* * NOTE: One would assume that a FIFO reset would release the * FIFO banks aswell, but it doesn't! We have to do this * manually! */ /* release FIFO banks, if any */ for (to = 0; to != 2; to++) { /* get csr value */ csr_val = AT91_UDP_READ_4(sc, csr_reg); if (csr_val & (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1)) { /* clear status bits */ if (pf->support_multi_buffer) { if (sc->sc_ep_flags[ep_no].fifo_bank) { sc->sc_ep_flags[ep_no].fifo_bank = 0; temp = AT91_UDP_CSR_RX_DATA_BK1; } else { sc->sc_ep_flags[ep_no].fifo_bank = 1; temp = AT91_UDP_CSR_RX_DATA_BK0; } } else { temp = (AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1); } } else { temp = 0; } /* clear FORCESTALL */ temp |= AT91_UDP_CSR_STALLSENT; AT91_CSR_ACK(csr_val, temp); AT91_UDP_WRITE_4(sc, csr_reg, csr_val); } /* compute default CSR value */ csr_val = 0; AT91_CSR_ACK(csr_val, 0); /* enable endpoint */ csr_val &= ~AT91_UDP_CSR_ET_MASK; csr_val |= AT91_UDP_CSR_EPEDS; if (ep_type == UE_CONTROL) { csr_val |= AT91_UDP_CSR_ET_CTRL; } else { if (ep_type == UE_BULK) { csr_val |= AT91_UDP_CSR_ET_BULK; } else if (ep_type == UE_INTERRUPT) { csr_val |= AT91_UDP_CSR_ET_INT; } else { csr_val |= AT91_UDP_CSR_ET_ISO; } if (ep_dir & UE_DIR_IN) { csr_val |= AT91_UDP_CSR_ET_DIR_IN; } } /* enable endpoint */ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(ep_no), csr_val); } static void at91dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) { struct at91dci_softc *sc; struct usb2_endpoint_descriptor *ed; DPRINTFN(5, "pipe=%p\n", pipe); USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check mode */ if (udev->flags.usb2_mode != USB_MODE_DEVICE) { /* not supported */ return; } /* get softc */ sc = AT9100_DCI_BUS2SC(udev->bus); /* get endpoint descriptor */ ed = pipe->edesc; /* reset endpoint */ at91dci_clear_stall_sub(sc, (ed->bEndpointAddress & UE_ADDR), (ed->bmAttributes & UE_XFERTYPE), (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); } usb2_error_t at91dci_init(struct at91dci_softc *sc) { uint32_t csr_val; uint8_t n; DPRINTF("start\n"); /* set up the bus structure */ sc->sc_bus.usbrev = USB_REV_1_1; sc->sc_bus.methods = &at91dci_bus_methods; USB_BUS_LOCK(&sc->sc_bus); /* turn on clocks */ if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } /* wait a little for things to stabilise */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF); /* compute default CSR value */ csr_val = 0; AT91_CSR_ACK(csr_val, 0); /* disable all endpoints */ for (n = 0; n != AT91_UDP_EP_MAX; n++) { /* disable endpoint */ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(n), csr_val); } /* enable the control endpoint */ AT91_CSR_ACK(csr_val, AT91_UDP_CSR_ET_CTRL | AT91_UDP_CSR_EPEDS); /* write to FIFO control register */ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(0), csr_val); /* enable the interrupts we want */ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_BUS); /* turn off clocks */ at91dci_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ at91dci_do_poll(&sc->sc_bus); return (0); /* success */ } void at91dci_uninit(struct at91dci_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF); sc->sc_flags.port_powered = 0; sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; at91dci_pull_down(sc); at91dci_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); } void at91dci_suspend(struct at91dci_softc *sc) { return; } void at91dci_resume(struct at91dci_softc *sc) { return; } static void at91dci_do_poll(struct usb2_bus *bus) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); at91dci_interrupt_poll(sc); at91dci_root_ctrl_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * at91dci bulk support *------------------------------------------------------------------------*/ static void at91dci_device_bulk_open(struct usb2_xfer *xfer) { return; } static void at91dci_device_bulk_close(struct usb2_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_bulk_enter(struct usb2_xfer *xfer) { return; } static void at91dci_device_bulk_start(struct usb2_xfer *xfer) { /* setup TDs */ at91dci_setup_standard_chain(xfer); at91dci_start_standard_chain(xfer); } struct usb2_pipe_methods at91dci_device_bulk_methods = { .open = at91dci_device_bulk_open, .close = at91dci_device_bulk_close, .enter = at91dci_device_bulk_enter, .start = at91dci_device_bulk_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci control support *------------------------------------------------------------------------*/ static void at91dci_device_ctrl_open(struct usb2_xfer *xfer) { return; } static void at91dci_device_ctrl_close(struct usb2_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_ctrl_enter(struct usb2_xfer *xfer) { return; } static void at91dci_device_ctrl_start(struct usb2_xfer *xfer) { /* setup TDs */ at91dci_setup_standard_chain(xfer); at91dci_start_standard_chain(xfer); } struct usb2_pipe_methods at91dci_device_ctrl_methods = { .open = at91dci_device_ctrl_open, .close = at91dci_device_ctrl_close, .enter = at91dci_device_ctrl_enter, .start = at91dci_device_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci interrupt support *------------------------------------------------------------------------*/ static void at91dci_device_intr_open(struct usb2_xfer *xfer) { return; } static void at91dci_device_intr_close(struct usb2_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_intr_enter(struct usb2_xfer *xfer) { return; } static void at91dci_device_intr_start(struct usb2_xfer *xfer) { /* setup TDs */ at91dci_setup_standard_chain(xfer); at91dci_start_standard_chain(xfer); } struct usb2_pipe_methods at91dci_device_intr_methods = { .open = at91dci_device_intr_open, .close = at91dci_device_intr_close, .enter = at91dci_device_intr_enter, .start = at91dci_device_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci full speed isochronous support *------------------------------------------------------------------------*/ static void at91dci_device_isoc_fs_open(struct usb2_xfer *xfer) { return; } static void at91dci_device_isoc_fs_close(struct usb2_xfer *xfer) { at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_device_isoc_fs_enter(struct usb2_xfer *xfer) { struct at91dci_softc *sc = xfer->usb2_sc; uint32_t temp; uint32_t nframes; DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes); /* get the current frame index */ nframes = AT91_UDP_READ_4(sc, AT91_UDP_FRM); /* * check if the frame index is within the window where the frames * will be inserted */ temp = (nframes - xfer->pipe->isoc_next) & AT91_UDP_FRM_MASK; if ((xfer->pipe->is_synced == 0) || (temp < xfer->nframes)) { /* * If there is data underflow or the pipe queue is * empty we schedule the transfer a few frames ahead * of the current frame position. Else two isochronous * transfers might overlap. */ xfer->pipe->isoc_next = (nframes + 3) & AT91_UDP_FRM_MASK; xfer->pipe->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->pipe->isoc_next - nframes) & AT91_UDP_FRM_MASK; /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + xfer->nframes; /* compute frame number for next insertion */ xfer->pipe->isoc_next += xfer->nframes; /* setup TDs */ at91dci_setup_standard_chain(xfer); } static void at91dci_device_isoc_fs_start(struct usb2_xfer *xfer) { /* start TD chain */ at91dci_start_standard_chain(xfer); } struct usb2_pipe_methods at91dci_device_isoc_fs_methods = { .open = at91dci_device_isoc_fs_open, .close = at91dci_device_isoc_fs_close, .enter = at91dci_device_isoc_fs_enter, .start = at91dci_device_isoc_fs_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci root control support *------------------------------------------------------------------------* * simulate a hardware HUB by handling * all the necessary requests *------------------------------------------------------------------------*/ static void at91dci_root_ctrl_open(struct usb2_xfer *xfer) { return; } static void at91dci_root_ctrl_close(struct usb2_xfer *xfer) { struct at91dci_softc *sc = xfer->usb2_sc; if (sc->sc_root_ctrl.xfer == xfer) { sc->sc_root_ctrl.xfer = NULL; } at91dci_device_done(xfer, USB_ERR_CANCELLED); } /* * USB descriptors for the virtual Root HUB: */ static const struct usb2_device_descriptor at91dci_devd = { .bLength = sizeof(struct usb2_device_descriptor), .bDescriptorType = UDESC_DEVICE, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_HSHUBSTT, .bMaxPacketSize = 64, .bcdDevice = {0x00, 0x01}, .iManufacturer = 1, .iProduct = 2, .bNumConfigurations = 1, }; static const struct usb2_device_qualifier at91dci_odevd = { .bLength = sizeof(struct usb2_device_qualifier), .bDescriptorType = UDESC_DEVICE_QUALIFIER, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_FSHUB, .bMaxPacketSize0 = 0, .bNumConfigurations = 0, }; static const struct at91dci_config_desc at91dci_confd = { .confd = { .bLength = sizeof(struct usb2_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(at91dci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, }, .ifcd = { .bLength = sizeof(struct usb2_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_HSHUBSTT, }, .endpd = { .bLength = sizeof(struct usb2_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = (UE_DIR_IN | AT9100_DCI_INTR_ENDPT), .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, .bInterval = 255, }, }; static const struct usb2_hub_descriptor_min at91dci_hubd = { .bDescLength = sizeof(at91dci_hubd), .bDescriptorType = UDESC_HUB, .bNbrPorts = 1, .wHubCharacteristics[0] = (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF, .wHubCharacteristics[1] = (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16, .bPwrOn2PwrGood = 50, .bHubContrCurrent = 0, .DeviceRemovable = {0}, /* port is removable */ }; #define STRING_LANG \ 0x09, 0x04, /* American English */ #define STRING_VENDOR \ 'A', 0, 'T', 0, 'M', 0, 'E', 0, 'L', 0 #define STRING_PRODUCT \ 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \ 'U', 0, 'B', 0, USB_MAKE_STRING_DESC(STRING_LANG, at91dci_langtab); USB_MAKE_STRING_DESC(STRING_VENDOR, at91dci_vendor); USB_MAKE_STRING_DESC(STRING_PRODUCT, at91dci_product); static void at91dci_root_ctrl_enter(struct usb2_xfer *xfer) { return; } static void at91dci_root_ctrl_start(struct usb2_xfer *xfer) { struct at91dci_softc *sc = xfer->usb2_sc; sc->sc_root_ctrl.xfer = xfer; usb2_config_td_queue_command( &sc->sc_config_td, NULL, &at91dci_root_ctrl_task, 0, 0); } static void at91dci_root_ctrl_task(struct at91dci_softc *sc, struct at91dci_config_copy *cc, uint16_t refcount) { at91dci_root_ctrl_poll(sc); } static void at91dci_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct at91dci_softc *sc = xfer->usb2_sc; uint16_t value; uint16_t index; uint8_t use_polling; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_SETUP) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ at91dci_device_done(xfer, std->err); } goto done; } /* buffer reset */ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0); std->len = 0; value = UGETW(std->req.wValue); index = UGETW(std->req.wIndex); use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; /* demultiplex the control request */ switch (std->req.bmRequestType) { case UT_READ_DEVICE: switch (std->req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (std->req.bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_DESCRIPTOR: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: switch (UGETW(std->req.wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (UGETW(std->req.wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; case UR_SYNCH_FRAME: goto tr_valid; /* nop */ default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (std->req.bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; case UT_WRITE_INTERFACE: switch (std->req.bRequest) { case UR_SET_INTERFACE: goto tr_handle_set_interface; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (std->req.bRequest) { case UR_GET_INTERFACE: goto tr_handle_get_interface; case UR_GET_STATUS: goto tr_handle_get_iface_status; default: goto tr_stalled; } break; case UT_WRITE_CLASS_INTERFACE: case UT_WRITE_VENDOR_INTERFACE: /* XXX forward */ break; case UT_READ_CLASS_INTERFACE: case UT_READ_VENDOR_INTERFACE: /* XXX forward */ break; case UT_WRITE_CLASS_DEVICE: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: goto tr_valid; case UR_SET_DESCRIPTOR: case UR_SET_FEATURE: break; default: goto tr_stalled; } break; case UT_WRITE_CLASS_OTHER: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: goto tr_handle_clear_port_feature; case UR_SET_FEATURE: goto tr_handle_set_port_feature; case UR_CLEAR_TT_BUFFER: case UR_RESET_TT: case UR_STOP_TT: goto tr_valid; default: goto tr_stalled; } break; case UT_READ_CLASS_OTHER: switch (std->req.bRequest) { case UR_GET_TT_STATE: goto tr_handle_get_tt_state; case UR_GET_STATUS: goto tr_handle_get_port_status; default: goto tr_stalled; } break; case UT_READ_CLASS_DEVICE: switch (std->req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; case UR_GET_STATUS: goto tr_handle_get_class_status; default: goto tr_stalled; } break; default: goto tr_stalled; } goto tr_valid; tr_handle_get_descriptor: switch (value >> 8) { case UDESC_DEVICE: if (value & 0xff) { goto tr_stalled; } std->len = sizeof(at91dci_devd); std->ptr = USB_ADD_BYTES(&at91dci_devd, 0); goto tr_valid; case UDESC_CONFIG: if (value & 0xff) { goto tr_stalled; } std->len = sizeof(at91dci_confd); std->ptr = USB_ADD_BYTES(&at91dci_confd, 0); goto tr_valid; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ std->len = sizeof(at91dci_langtab); std->ptr = USB_ADD_BYTES(&at91dci_langtab, 0); goto tr_valid; case 1: /* Vendor */ std->len = sizeof(at91dci_vendor); std->ptr = USB_ADD_BYTES(&at91dci_vendor, 0); goto tr_valid; case 2: /* Product */ std->len = sizeof(at91dci_product); std->ptr = USB_ADD_BYTES(&at91dci_product, 0); goto tr_valid; default: break; } break; default: goto tr_stalled; } goto tr_stalled; tr_handle_get_config: std->len = 1; sc->sc_hub_temp.wValue[0] = sc->sc_conf; goto tr_valid; tr_handle_get_status: std->len = 2; USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); goto tr_valid; tr_handle_set_address: if (value & 0xFF00) { goto tr_stalled; } sc->sc_rt_addr = value; goto tr_valid; tr_handle_set_config: if (value >= 2) { goto tr_stalled; } sc->sc_conf = value; goto tr_valid; tr_handle_get_interface: std->len = 1; sc->sc_hub_temp.wValue[0] = 0; goto tr_valid; tr_handle_get_tt_state: tr_handle_get_class_status: tr_handle_get_iface_status: tr_handle_get_ep_status: std->len = 2; USETW(sc->sc_hub_temp.wValue, 0); goto tr_valid; tr_handle_set_halt: tr_handle_set_interface: tr_handle_set_wakeup: tr_handle_clear_wakeup: tr_handle_clear_halt: goto tr_valid; tr_handle_clear_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_SUSPEND: at91dci_wakeup_peer(sc); break; case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 0; break; case UHF_PORT_TEST: case UHF_PORT_INDICATOR: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 0; at91dci_pull_down(sc); at91dci_clocks_off(sc); break; case UHF_C_PORT_CONNECTION: sc->sc_flags.change_connect = 0; break; case UHF_C_PORT_SUSPEND: sc->sc_flags.change_suspend = 0; break; default: std->err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_set_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); switch (value) { case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 1; break; case UHF_PORT_SUSPEND: case UHF_PORT_RESET: case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 1; break; default: std->err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_get_port_status: DPRINTFN(9, "UR_GET_PORT_STATUS\n"); if (index != 1) { goto tr_stalled; } if (sc->sc_flags.status_vbus) { at91dci_clocks_on(sc); at91dci_pull_up(sc); } else { at91dci_pull_down(sc); at91dci_clocks_off(sc); } /* Select FULL-speed and Device Side Mode */ value = UPS_PORT_MODE_DEVICE; if (sc->sc_flags.port_powered) { value |= UPS_PORT_POWER; } if (sc->sc_flags.port_enabled) { value |= UPS_PORT_ENABLED; } if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { value |= UPS_CURRENT_CONNECT_STATUS; } if (sc->sc_flags.status_suspend) { value |= UPS_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortStatus, value); value = 0; if (sc->sc_flags.change_connect) { value |= UPS_C_CONNECT_STATUS; if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { /* reset endpoint flags */ bzero(sc->sc_ep_flags, sizeof(sc->sc_ep_flags)); } } if (sc->sc_flags.change_suspend) { value |= UPS_C_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortChange, value); std->len = sizeof(sc->sc_hub_temp.ps); goto tr_valid; tr_handle_get_class_descriptor: if (value & 0xFF) { goto tr_stalled; } std->ptr = USB_ADD_BYTES(&at91dci_hubd, 0); std->len = sizeof(at91dci_hubd); goto tr_valid; tr_stalled: std->err = USB_ERR_STALLED; tr_valid: done: return; } static void at91dci_root_ctrl_poll(struct at91dci_softc *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &at91dci_root_ctrl_done); } struct usb2_pipe_methods at91dci_root_ctrl_methods = { .open = at91dci_root_ctrl_open, .close = at91dci_root_ctrl_close, .enter = at91dci_root_ctrl_enter, .start = at91dci_root_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 0, }; /*------------------------------------------------------------------------* * at91dci root interrupt support *------------------------------------------------------------------------*/ static void at91dci_root_intr_open(struct usb2_xfer *xfer) { return; } static void at91dci_root_intr_close(struct usb2_xfer *xfer) { struct at91dci_softc *sc = xfer->usb2_sc; if (sc->sc_root_intr.xfer == xfer) { sc->sc_root_intr.xfer = NULL; } at91dci_device_done(xfer, USB_ERR_CANCELLED); } static void at91dci_root_intr_enter(struct usb2_xfer *xfer) { return; } static void at91dci_root_intr_start(struct usb2_xfer *xfer) { struct at91dci_softc *sc = xfer->usb2_sc; sc->sc_root_intr.xfer = xfer; } struct usb2_pipe_methods at91dci_root_intr_methods = { .open = at91dci_root_intr_open, .close = at91dci_root_intr_close, .enter = at91dci_root_intr_enter, .start = at91dci_root_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; static void at91dci_xfer_setup(struct usb2_setup_params *parm) { const struct usb2_hw_ep_profile *pf; struct at91dci_softc *sc; struct usb2_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; uint8_t ep_no; sc = AT9100_DCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * setup xfer */ xfer->usb2_sc = sc; /* * NOTE: This driver does not use any of the parameters that * are computed from the following values. Just set some * reasonable dummies: */ parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x500; usb2_transfer_setup_sub(parm); /* * compute maximum number of TDs */ if (parm->methods == &at91dci_device_ctrl_methods) { ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */ + 1 /* SYNC 2 */ ; } else if (parm->methods == &at91dci_device_bulk_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &at91dci_device_intr_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &at91dci_device_isoc_fs_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else { ntd = 0; } /* * check if "usb2_transfer_setup_sub" set an error */ if (parm->err) { return; } /* * allocate transfer descriptors */ last_obj = NULL; /* * get profile stuff */ if (ntd) { ep_no = xfer->endpoint & UE_ADDR; at91dci_get_hw_ep_profile(parm->udev, &pf, ep_no); if (pf == NULL) { /* should not happen */ parm->err = USB_ERR_INVAL; return; } } else { ep_no = 0; pf = NULL; } /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); for (n = 0; n != ntd; n++) { struct at91dci_td *td; if (parm->buf) { td = USB_ADD_BYTES(parm->buf, parm->size[0]); /* init TD */ td->io_tag = sc->sc_io_tag; td->io_hdl = sc->sc_io_hdl; td->max_packet_size = xfer->max_packet_size; td->status_reg = AT91_UDP_CSR(ep_no); td->fifo_reg = AT91_UDP_FDR(ep_no); if (pf->support_multi_buffer) { td->support_multi_buffer = 1; } td->obj_next = last_obj; last_obj = td; } parm->size[0] += sizeof(*td); } xfer->td_start[0] = last_obj; } static void at91dci_xfer_unsetup(struct usb2_xfer *xfer) { return; } static void at91dci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) { struct at91dci_softc *sc = AT9100_DCI_BUS2SC(udev->bus); DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, udev->flags.usb2_mode, sc->sc_rt_addr); if (udev->device_index == sc->sc_rt_addr) { if (udev->flags.usb2_mode != USB_MODE_HOST) { /* not supported */ return; } switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &at91dci_root_ctrl_methods; break; case UE_DIR_IN | AT9100_DCI_INTR_ENDPT: pipe->methods = &at91dci_root_intr_methods; break; default: /* do nothing */ break; } } else { if (udev->flags.usb2_mode != USB_MODE_DEVICE) { /* not supported */ return; } if (udev->speed != USB_SPEED_FULL) { /* not supported */ return; } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &at91dci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &at91dci_device_intr_methods; break; case UE_ISOCHRONOUS: pipe->methods = &at91dci_device_isoc_fs_methods; break; case UE_BULK: pipe->methods = &at91dci_device_bulk_methods; break; default: /* do nothing */ break; } } } struct usb2_bus_methods at91dci_bus_methods = { .pipe_init = &at91dci_pipe_init, .xfer_setup = &at91dci_xfer_setup, .xfer_unsetup = &at91dci_xfer_unsetup, .do_poll = &at91dci_do_poll, .get_hw_ep_profile = &at91dci_get_hw_ep_profile, .set_stall = &at91dci_set_stall, .clear_stall = &at91dci_clear_stall, .vbus_interrupt = &at91dci_vbus_interrupt, .rem_wakeup_set = &at91dci_rem_wakeup_set, }; Index: projects/cambria/sys/dev/usb2/controller/at91dci_atmelarm.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/at91dci_atmelarm.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/at91dci_atmelarm.c (revision 186460) @@ -1,356 +1,357 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2007-2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 /* Pin Definitions - do they belong here or somewhere else ? */ #define VBUS_MASK AT91C_PIO_PB24 #define VBUS_BASE AT91RM92_PIOB_BASE #define PULLUP_MASK AT91C_PIO_PB22 #define PULLUP_BASE AT91RM92_PIOB_BASE static device_probe_t at91_udp_probe; static device_attach_t at91_udp_attach; static device_detach_t at91_udp_detach; static device_shutdown_t at91_udp_shutdown; struct at91_udp_softc { struct at91dci_softc sc_dci; /* must be first */ struct at91_pmc_clock *sc_iclk; struct at91_pmc_clock *sc_fclk; struct resource *sc_vbus_irq_res; void *sc_vbus_intr_hdl; }; static void at91_vbus_interrupt(struct at91_udp_softc *sc) { uint32_t temp; uint8_t vbus_val; /* XXX temporary clear interrupts here */ temp = at91_pio_gpio_clear_interrupt(VBUS_BASE); /* just forward it */ vbus_val = at91_pio_gpio_get(VBUS_BASE, VBUS_MASK); (sc->sc_dci.sc_bus.methods->vbus_interrupt) (&sc->sc_dci.sc_bus, vbus_val); } static void at91_udp_clocks_on(void *arg) { struct at91_udp_softc *sc = arg; at91_pmc_clock_enable(sc->sc_iclk); at91_pmc_clock_enable(sc->sc_fclk); } static void at91_udp_clocks_off(void *arg) { struct at91_udp_softc *sc = arg; at91_pmc_clock_disable(sc->sc_fclk); at91_pmc_clock_disable(sc->sc_iclk); } static void at91_udp_pull_up(void *arg) { at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK); } static void at91_udp_pull_down(void *arg) { at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK); } static int at91_udp_probe(device_t dev) { device_set_desc(dev, "AT91 integrated AT91_UDP controller"); return (0); } static int at91_udp_attach(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); int err; int rid; if (sc == NULL) { return (ENXIO); } /* setup AT9100 USB device controller interface softc */ sc->sc_dci.sc_clocks_on = &at91_udp_clocks_on; sc->sc_dci.sc_clocks_off = &at91_udp_clocks_off; sc->sc_dci.sc_clocks_arg = sc; sc->sc_dci.sc_pull_up = &at91_udp_pull_up; sc->sc_dci.sc_pull_down = &at91_udp_pull_down; sc->sc_dci.sc_pull_arg = sc; /* get all DMA memory */ + sc->sc_dci.sc_bus.parent = dev; if (usb2_bus_mem_alloc_all(&sc->sc_dci.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } /* * configure VBUS input pin, enable deglitch and enable * interrupt : */ at91_pio_use_gpio(VBUS_BASE, VBUS_MASK); at91_pio_gpio_input(VBUS_BASE, VBUS_MASK); at91_pio_gpio_set_deglitch(VBUS_BASE, VBUS_MASK, 1); at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 1); /* * configure PULLUP output pin : */ at91_pio_use_gpio(PULLUP_BASE, PULLUP_MASK); at91_pio_gpio_output(PULLUP_BASE, PULLUP_MASK, 0); at91_udp_pull_down(sc); /* wait 10ms for pulldown to stabilise */ usb2_pause_mtx(NULL, 10); sc->sc_iclk = at91_pmc_clock_ref("udc_clk"); sc->sc_fclk = at91_pmc_clock_ref("udpck"); rid = MEM_RID; sc->sc_dci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_dci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_dci.sc_io_tag = rman_get_bustag(sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_hdl = rman_get_bushandle(sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_size = rman_get_size(sc->sc_dci.sc_io_res); rid = 0; sc->sc_dci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_dci.sc_irq_res)) { goto error; } rid = 1; sc->sc_vbus_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_vbus_irq_res)) { goto error; } sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_dci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus); err = usb2_config_td_setup(&sc->sc_dci.sc_config_td, sc, &sc->sc_dci.sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(dev, "could not setup config thread!\n"); goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl); #endif if (err) { sc->sc_dci.sc_intr_hdl = NULL; goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_vbus_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)at91_vbus_interrupt, sc, &sc->sc_vbus_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_vbus_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)at91_vbus_interrupt, sc, &sc->sc_vbus_intr_hdl); #endif if (err) { sc->sc_vbus_intr_hdl = NULL; goto error; } err = at91dci_init(&sc->sc_dci); if (!err) { err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev); } if (err) { goto error; } else { /* poll VBUS one time */ at91_vbus_interrupt(sc); } return (0); error: at91_udp_detach(dev); return (ENXIO); } static int at91_udp_detach(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_dci.sc_bus.bdev) { bdev = sc->sc_dci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(dev); /* disable Transceiver */ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS); /* disable and clear all interrupts */ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_IDR, 0xFFFFFFFF); AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_ICR, 0xFFFFFFFF); /* disable VBUS interrupt */ at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 0); if (sc->sc_vbus_irq_res && sc->sc_vbus_intr_hdl) { err = bus_teardown_intr(dev, sc->sc_vbus_irq_res, sc->sc_vbus_intr_hdl); sc->sc_vbus_intr_hdl = NULL; } if (sc->sc_vbus_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 1, sc->sc_vbus_irq_res); sc->sc_vbus_irq_res = NULL; } if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) { /* * only call at91_udp_uninit() after at91_udp_init() */ at91dci_uninit(&sc->sc_dci); err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res, sc->sc_dci.sc_intr_hdl); sc->sc_dci.sc_intr_hdl = NULL; } if (sc->sc_dci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_dci.sc_irq_res); sc->sc_dci.sc_irq_res = NULL; } if (sc->sc_dci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_dci.sc_io_res); sc->sc_dci.sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_dci.sc_config_td); usb2_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL); /* disable clocks */ at91_pmc_clock_disable(sc->sc_iclk); at91_pmc_clock_disable(sc->sc_fclk); at91_pmc_clock_deref(sc->sc_fclk); at91_pmc_clock_deref(sc->sc_iclk); return (0); } static int at91_udp_shutdown(device_t dev) { struct at91_udp_softc *sc = device_get_softc(dev); int err; err = bus_generic_shutdown(dev); if (err) return (err); at91dci_uninit(&sc->sc_dci); return (0); } static device_method_t at91_udp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, at91_udp_probe), DEVMETHOD(device_attach, at91_udp_attach), DEVMETHOD(device_detach, at91_udp_detach), DEVMETHOD(device_shutdown, at91_udp_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t at91_udp_driver = { "at91_udp", at91_udp_methods, sizeof(struct at91_udp_softc), }; static devclass_t at91_udp_devclass; DRIVER_MODULE(at91_udp, atmelarm, at91_udp_driver, at91_udp_devclass, 0, 0); Index: projects/cambria/sys/dev/usb2/controller/ehci2.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/ehci2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/ehci2.c (revision 186460) @@ -1,3810 +1,3804 @@ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 2004 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 2004 Lennart Augustsson. All rights reserved. * Copyright (c) 2004 Charles M. Hannum. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * * The EHCI 0.96 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r096.pdf * The EHCI 1.0 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r10.pdf * and the USB 2.0 spec at * http://www.usb.org/developers/docs/usb_20.zip * */ /* * TODO: * 1) command failures are not recovered correctly */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #define USB_DEBUG_VAR ehcidebug #define usb2_config_td_cc ehci_config_copy #define usb2_config_td_softc ehci_softc #include #include #include #include #include #include #include #include #include #include #include #include #include #define EHCI_BUS2SC(bus) ((ehci_softc_t *)(((uint8_t *)(bus)) - \ USB_P2U(&(((ehci_softc_t *)0)->sc_bus)))) #if USB_DEBUG static int ehcidebug = 0; static int ehcinohighspeed = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, debug, CTLFLAG_RW, &ehcidebug, 0, "Debug level"); SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, no_hs, CTLFLAG_RW, &ehcinohighspeed, 0, "Disable High Speed USB"); static void ehci_dump_regs(ehci_softc_t *sc); static void ehci_dump_sqh(ehci_qh_t *sqh); #endif #define EHCI_INTR_ENDPT 1 extern struct usb2_bus_methods ehci_bus_methods; extern struct usb2_pipe_methods ehci_device_bulk_methods; extern struct usb2_pipe_methods ehci_device_ctrl_methods; extern struct usb2_pipe_methods ehci_device_intr_methods; extern struct usb2_pipe_methods ehci_device_isoc_fs_methods; extern struct usb2_pipe_methods ehci_device_isoc_hs_methods; extern struct usb2_pipe_methods ehci_root_ctrl_methods; extern struct usb2_pipe_methods ehci_root_intr_methods; static usb2_config_td_command_t ehci_root_ctrl_task; static void ehci_do_poll(struct usb2_bus *bus); static void ehci_root_ctrl_poll(struct ehci_softc *sc); static void ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error); static uint8_t ehci_check_transfer(struct usb2_xfer *xfer); static void ehci_timeout(void *arg); static usb2_sw_transfer_func_t ehci_root_intr_done; static usb2_sw_transfer_func_t ehci_root_ctrl_done; struct ehci_std_temp { struct usb2_page_cache *pc; ehci_qtd_t *td; ehci_qtd_t *td_next; uint32_t average; uint32_t qtd_status; uint32_t len; uint16_t max_frame_size; uint8_t shortpkt; uint8_t auto_data_toggle; uint8_t setup_alt_next; uint8_t short_frames_ok; }; void ehci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) { struct ehci_softc *sc = EHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, sizeof(uint32_t) * EHCI_FRAMELIST_COUNT, EHCI_FRAMELIST_ALIGN); cb(bus, &sc->sc_hw.async_start_pc, &sc->sc_hw.async_start_pg, sizeof(ehci_qh_t), EHCI_QH_ALIGN); for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, sizeof(ehci_qh_t), EHCI_QH_ALIGN); } for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.isoc_hs_start_pc + i, sc->sc_hw.isoc_hs_start_pg + i, sizeof(ehci_itd_t), EHCI_ITD_ALIGN); } for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.isoc_fs_start_pc + i, sc->sc_hw.isoc_fs_start_pg + i, sizeof(ehci_sitd_t), EHCI_SITD_ALIGN); } } static usb2_error_t ehci_hc_reset(ehci_softc_t *sc) { uint32_t hcr; uint32_t n; EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ for (n = 0; n != 100; n++) { usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); hcr = EOREAD4(sc, EHCI_USBSTS); if (hcr & EHCI_STS_HCH) { hcr = 0; break; } } /* * Fall through and try reset anyway even though * Table 2-9 in the EHCI spec says this will result * in undefined behavior. */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); for (n = 0; n != 100; n++) { usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); hcr = EOREAD4(sc, EHCI_USBCMD); if (!(hcr & EHCI_CMD_HCRESET)) { if (sc->sc_flags & EHCI_SCFLG_SETMODE) EOWRITE4(sc, 0x68, 0x3); hcr = 0; break; } } if (hcr) { return (USB_ERR_IOERROR); } return (0); } usb2_error_t ehci_init(ehci_softc_t *sc) { struct usb2_page_search buf_res; uint32_t version; uint32_t sparams; uint32_t cparams; uint32_t hcr; uint16_t i; uint16_t x; uint16_t y; uint16_t bit; usb2_error_t err = 0; USB_BUS_LOCK(&sc->sc_bus); DPRINTF("start\n"); - usb2_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, - CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0); #if USB_DEBUG if (ehcidebug > 2) { ehci_dump_regs(sc); } #endif sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); version = EREAD2(sc, EHCI_HCIVERSION); device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n", version >> 8, version & 0xff); sparams = EREAD4(sc, EHCI_HCSPARAMS); DPRINTF("sparams=0x%x\n", sparams); sc->sc_noport = EHCI_HCS_N_PORTS(sparams); cparams = EREAD4(sc, EHCI_HCCPARAMS); DPRINTF("cparams=0x%x\n", cparams); if (EHCI_HCC_64BIT(cparams)) { DPRINTF("HCC uses 64-bit structures\n"); /* MUST clear segment register if 64 bit capable */ EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); } sc->sc_bus.usbrev = USB_REV_2_0; /* Reset the controller */ DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)); err = ehci_hc_reset(sc); if (err) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); goto done; } /* * use current frame-list-size selection 0: 1024*4 bytes 1: 512*4 * bytes 2: 256*4 bytes 3: unknown */ if (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD)) == 3) { device_printf(sc->sc_bus.bdev, "invalid frame-list-size\n"); err = USB_ERR_IOERROR; goto done; } /* set up the bus struct */ sc->sc_bus.methods = &ehci_bus_methods; sc->sc_eintrs = EHCI_NORMAL_INTRS; for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { ehci_qh_t *qh; usb2_get_page(sc->sc_hw.intr_start_pc + i, 0, &buf_res); qh = buf_res.buffer; /* initialize page cache pointer */ qh->page_cache = sc->sc_hw.intr_start_pc + i; /* store a pointer to queue head */ sc->sc_intr_p_last[i] = qh; qh->qh_self = htole32(buf_res.physaddr) | htole32(EHCI_LINK_QH); qh->qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); qh->qh_endphub = htole32(EHCI_QH_SET_MULT(1)); qh->qh_curqtd = 0; qh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2; while (bit) { x = bit; while (x & bit) { ehci_qh_t *qh_x; ehci_qh_t *qh_y; y = (x ^ bit) | (bit / 2); qh_x = sc->sc_intr_p_last[x]; qh_y = sc->sc_intr_p_last[y]; /* * the next QH has half the poll interval */ qh_x->qh_link = qh_y->qh_self; x++; } bit >>= 1; } if (1) { ehci_qh_t *qh; qh = sc->sc_intr_p_last[0]; /* the last (1ms) QH terminates */ qh->qh_link = htole32(EHCI_LINK_TERMINATE); } for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { ehci_sitd_t *sitd; ehci_itd_t *itd; usb2_get_page(sc->sc_hw.isoc_fs_start_pc + i, 0, &buf_res); sitd = buf_res.buffer; /* initialize page cache pointer */ sitd->page_cache = sc->sc_hw.isoc_fs_start_pc + i; /* store a pointer to the transfer descriptor */ sc->sc_isoc_fs_p_last[i] = sitd; /* initialize full speed isochronous */ sitd->sitd_self = htole32(buf_res.physaddr) | htole32(EHCI_LINK_SITD); sitd->sitd_back = htole32(EHCI_LINK_TERMINATE); sitd->sitd_next = sc->sc_intr_p_last[i | (EHCI_VIRTUAL_FRAMELIST_COUNT / 2)]->qh_self; usb2_get_page(sc->sc_hw.isoc_hs_start_pc + i, 0, &buf_res); itd = buf_res.buffer; /* initialize page cache pointer */ itd->page_cache = sc->sc_hw.isoc_hs_start_pc + i; /* store a pointer to the transfer descriptor */ sc->sc_isoc_hs_p_last[i] = itd; /* initialize high speed isochronous */ itd->itd_self = htole32(buf_res.physaddr) | htole32(EHCI_LINK_ITD); itd->itd_next = sitd->sitd_self; } usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); if (1) { uint32_t *pframes; pframes = buf_res.buffer; /* * execution order: * pframes -> high speed isochronous -> * full speed isochronous -> interrupt QH's */ for (i = 0; i < EHCI_FRAMELIST_COUNT; i++) { pframes[i] = sc->sc_isoc_hs_p_last [i & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1)]->itd_self; } } /* setup sync list pointer */ EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); usb2_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); if (1) { ehci_qh_t *qh; qh = buf_res.buffer; /* initialize page cache pointer */ qh->page_cache = &sc->sc_hw.async_start_pc; /* store a pointer to the queue head */ sc->sc_async_p_last = qh; /* init dummy QH that starts the async list */ qh->qh_self = htole32(buf_res.physaddr) | htole32(EHCI_LINK_QH); /* fill the QH */ qh->qh_endp = htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); qh->qh_endphub = htole32(EHCI_QH_SET_MULT(1)); qh->qh_link = qh->qh_self; qh->qh_curqtd = 0; /* fill the overlay qTD */ qh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); } /* flush all cache into memory */ usb2_bus_mem_flush_all(&sc->sc_bus, &ehci_iterate_hw_softc); #if USB_DEBUG if (ehcidebug) { ehci_dump_sqh(sc->sc_async_p_last); } #endif /* setup async list pointer */ EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); /* enable interrupts */ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* turn on controller */ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_ITC_1 | /* 1 microframes interrupt delay */ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | EHCI_CMD_ASE | EHCI_CMD_PSE | EHCI_CMD_RS); /* Take over port ownership */ EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); for (i = 0; i < 100; i++) { usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "run timeout\n"); err = USB_ERR_IOERROR; goto done; } done: USB_BUS_UNLOCK(&sc->sc_bus); if (!err) { /* catch any lost interrupts */ ehci_do_poll(&sc->sc_bus); } return (err); } /* * shut down the controller when the system is going down */ void ehci_detach(struct ehci_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); usb2_callout_stop(&sc->sc_tmo_pcd); EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); if (ehci_hc_reset(sc)) { DPRINTF("reset failed!\n"); } /* XXX let stray task complete */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 50); USB_BUS_UNLOCK(&sc->sc_bus); usb2_callout_drain(&sc->sc_tmo_pcd); } void ehci_suspend(struct ehci_softc *sc) { uint32_t cmd; uint32_t hcr; uint8_t i; USB_BUS_LOCK(&sc->sc_bus); for (i = 1; i <= sc->sc_noport; i++) { cmd = EOREAD4(sc, EHCI_PORTSC(i)); if (((cmd & EHCI_PS_PO) == 0) && ((cmd & EHCI_PS_PE) == EHCI_PS_PE)) { EOWRITE4(sc, EHCI_PORTSC(i), cmd | EHCI_PS_SUSP); } } sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD); cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE); EOWRITE4(sc, EHCI_USBCMD, cmd); for (i = 0; i < 100; i++) { hcr = EOREAD4(sc, EHCI_USBSTS) & (EHCI_STS_ASS | EHCI_STS_PSS); if (hcr == 0) { break; } usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); } if (hcr != 0) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); } cmd &= ~EHCI_CMD_RS; EOWRITE4(sc, EHCI_USBCMD, cmd); for (i = 0; i < 100; i++) { hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (hcr == EHCI_STS_HCH) { break; } usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); } if (hcr != EHCI_STS_HCH) { device_printf(sc->sc_bus.bdev, "config timeout\n"); } USB_BUS_UNLOCK(&sc->sc_bus); } void ehci_resume(struct ehci_softc *sc) { struct usb2_page_search buf_res; uint32_t cmd; uint32_t hcr; uint8_t i; USB_BUS_LOCK(&sc->sc_bus); /* restore things in case the bios doesn't */ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); usb2_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); hcr = 0; for (i = 1; i <= sc->sc_noport; i++) { cmd = EOREAD4(sc, EHCI_PORTSC(i)); if (((cmd & EHCI_PS_PO) == 0) && ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) { EOWRITE4(sc, EHCI_PORTSC(i), cmd | EHCI_PS_FPR); hcr = 1; } } if (hcr) { usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_WAIT); for (i = 1; i <= sc->sc_noport; i++) { cmd = EOREAD4(sc, EHCI_PORTSC(i)); if (((cmd & EHCI_PS_PO) == 0) && ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) { EOWRITE4(sc, EHCI_PORTSC(i), cmd & ~EHCI_PS_FPR); } } } EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd); for (i = 0; i < 100; i++) { hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (hcr != EHCI_STS_HCH) { break; } usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); } if (hcr == EHCI_STS_HCH) { device_printf(sc->sc_bus.bdev, "config timeout\n"); } usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_WAIT); USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ ehci_do_poll(&sc->sc_bus); } void ehci_shutdown(ehci_softc_t *sc) { DPRINTF("stopping the HC\n"); USB_BUS_LOCK(&sc->sc_bus); if (ehci_hc_reset(sc)) { DPRINTF("reset failed!\n"); } USB_BUS_UNLOCK(&sc->sc_bus); } #if USB_DEBUG static void ehci_dump_regs(ehci_softc_t *sc) { uint32_t i; i = EOREAD4(sc, EHCI_USBCMD); printf("cmd=0x%08x\n", i); if (i & EHCI_CMD_ITC_1) printf(" EHCI_CMD_ITC_1\n"); if (i & EHCI_CMD_ITC_2) printf(" EHCI_CMD_ITC_2\n"); if (i & EHCI_CMD_ITC_4) printf(" EHCI_CMD_ITC_4\n"); if (i & EHCI_CMD_ITC_8) printf(" EHCI_CMD_ITC_8\n"); if (i & EHCI_CMD_ITC_16) printf(" EHCI_CMD_ITC_16\n"); if (i & EHCI_CMD_ITC_32) printf(" EHCI_CMD_ITC_32\n"); if (i & EHCI_CMD_ITC_64) printf(" EHCI_CMD_ITC_64\n"); if (i & EHCI_CMD_ASPME) printf(" EHCI_CMD_ASPME\n"); if (i & EHCI_CMD_ASPMC) printf(" EHCI_CMD_ASPMC\n"); if (i & EHCI_CMD_LHCR) printf(" EHCI_CMD_LHCR\n"); if (i & EHCI_CMD_IAAD) printf(" EHCI_CMD_IAAD\n"); if (i & EHCI_CMD_ASE) printf(" EHCI_CMD_ASE\n"); if (i & EHCI_CMD_PSE) printf(" EHCI_CMD_PSE\n"); if (i & EHCI_CMD_FLS_M) printf(" EHCI_CMD_FLS_M\n"); if (i & EHCI_CMD_HCRESET) printf(" EHCI_CMD_HCRESET\n"); if (i & EHCI_CMD_RS) printf(" EHCI_CMD_RS\n"); i = EOREAD4(sc, EHCI_USBSTS); printf("sts=0x%08x\n", i); if (i & EHCI_STS_ASS) printf(" EHCI_STS_ASS\n"); if (i & EHCI_STS_PSS) printf(" EHCI_STS_PSS\n"); if (i & EHCI_STS_REC) printf(" EHCI_STS_REC\n"); if (i & EHCI_STS_HCH) printf(" EHCI_STS_HCH\n"); if (i & EHCI_STS_IAA) printf(" EHCI_STS_IAA\n"); if (i & EHCI_STS_HSE) printf(" EHCI_STS_HSE\n"); if (i & EHCI_STS_FLR) printf(" EHCI_STS_FLR\n"); if (i & EHCI_STS_PCD) printf(" EHCI_STS_PCD\n"); if (i & EHCI_STS_ERRINT) printf(" EHCI_STS_ERRINT\n"); if (i & EHCI_STS_INT) printf(" EHCI_STS_INT\n"); printf("ien=0x%08x\n", EOREAD4(sc, EHCI_USBINTR)); printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n", EOREAD4(sc, EHCI_FRINDEX), EOREAD4(sc, EHCI_CTRLDSSEGMENT), EOREAD4(sc, EHCI_PERIODICLISTBASE), EOREAD4(sc, EHCI_ASYNCLISTADDR)); for (i = 1; i <= sc->sc_noport; i++) { printf("port %d status=0x%08x\n", i, EOREAD4(sc, EHCI_PORTSC(i))); } } static void ehci_dump_link(uint32_t link, int type) { link = le32toh(link); printf("0x%08x", link); if (link & EHCI_LINK_TERMINATE) printf(""); else { printf("<"); if (type) { switch (EHCI_LINK_TYPE(link)) { case EHCI_LINK_ITD: printf("ITD"); break; case EHCI_LINK_QH: printf("QH"); break; case EHCI_LINK_SITD: printf("SITD"); break; case EHCI_LINK_FSTN: printf("FSTN"); break; } } printf(">"); } } static void ehci_dump_qtd(ehci_qtd_t *qtd) { uint32_t s; printf(" next="); ehci_dump_link(qtd->qtd_next, 0); printf(" altnext="); ehci_dump_link(qtd->qtd_altnext, 0); printf("\n"); s = le32toh(qtd->qtd_status); printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); printf(" cerr=%d pid=%d stat=%s%s%s%s%s%s%s%s\n", EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s), (s & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE", (s & EHCI_QTD_HALTED) ? "-HALTED" : "", (s & EHCI_QTD_BUFERR) ? "-BUFERR" : "", (s & EHCI_QTD_BABBLE) ? "-BABBLE" : "", (s & EHCI_QTD_XACTERR) ? "-XACTERR" : "", (s & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "", (s & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "", (s & EHCI_QTD_PINGSTATE) ? "-PING" : ""); for (s = 0; s < 5; s++) { printf(" buffer[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer[s])); } for (s = 0; s < 5; s++) { printf(" buffer_hi[%d]=0x%08x\n", s, le32toh(qtd->qtd_buffer_hi[s])); } } static uint8_t ehci_dump_sqtd(ehci_qtd_t *sqtd) { uint8_t temp; usb2_pc_cpu_invalidate(sqtd->page_cache); printf("QTD(%p) at 0x%08x:\n", sqtd, le32toh(sqtd->qtd_self)); ehci_dump_qtd(sqtd); temp = (sqtd->qtd_next & htole32(EHCI_LINK_TERMINATE)) ? 1 : 0; return (temp); } static void ehci_dump_sqtds(ehci_qtd_t *sqtd) { uint16_t i; uint8_t stop; stop = 0; for (i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) { stop = ehci_dump_sqtd(sqtd); } if (sqtd) { printf("dump aborted, too many TDs\n"); } } static void ehci_dump_sqh(ehci_qh_t *qh) { uint32_t endp, endphub; usb2_pc_cpu_invalidate(qh->page_cache); printf("QH(%p) at 0x%08x:\n", qh, le32toh(qh->qh_self) & ~0x1F); printf(" link="); ehci_dump_link(qh->qh_link, 1); printf("\n"); endp = le32toh(qh->qh_endp); printf(" endp=0x%08x\n", endp); printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp), EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp)); printf(" mpl=0x%x ctl=%d nrl=%d\n", EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), EHCI_QH_GET_NRL(endp)); endphub = le32toh(qh->qh_endphub); printf(" endphub=0x%08x\n", endphub); printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), EHCI_QH_GET_MULT(endphub)); printf(" curqtd="); ehci_dump_link(qh->qh_curqtd, 0); printf("\n"); printf("Overlay qTD:\n"); ehci_dump_qtd((void *)&qh->qh_qtd); } static void ehci_dump_sitd(ehci_sitd_t *sitd) { usb2_pc_cpu_invalidate(sitd->page_cache); printf("SITD(%p) at 0x%08x\n", sitd, le32toh(sitd->sitd_self) & ~0x1F); printf(" next=0x%08x\n", le32toh(sitd->sitd_next)); printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", le32toh(sitd->sitd_portaddr), (sitd->sitd_portaddr & htole32(EHCI_SITD_SET_DIR_IN)) ? "in" : "out", EHCI_SITD_GET_ADDR(le32toh(sitd->sitd_portaddr)), EHCI_SITD_GET_ENDPT(le32toh(sitd->sitd_portaddr)), EHCI_SITD_GET_PORT(le32toh(sitd->sitd_portaddr)), EHCI_SITD_GET_HUBA(le32toh(sitd->sitd_portaddr))); printf(" mask=0x%08x\n", le32toh(sitd->sitd_mask)); printf(" status=0x%08x <%s> len=0x%x\n", le32toh(sitd->sitd_status), (sitd->sitd_status & htole32(EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", EHCI_SITD_GET_LEN(le32toh(sitd->sitd_status))); printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", le32toh(sitd->sitd_back), le32toh(sitd->sitd_bp[0]), le32toh(sitd->sitd_bp[1]), le32toh(sitd->sitd_bp_hi[0]), le32toh(sitd->sitd_bp_hi[1])); } static void ehci_dump_itd(ehci_itd_t *itd) { usb2_pc_cpu_invalidate(itd->page_cache); printf("ITD(%p) at 0x%08x\n", itd, le32toh(itd->itd_self) & ~0x1F); printf(" next=0x%08x\n", le32toh(itd->itd_next)); printf(" status[0]=0x%08x; <%s>\n", le32toh(itd->itd_status[0]), (itd->itd_status[0] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[1]=0x%08x; <%s>\n", le32toh(itd->itd_status[1]), (itd->itd_status[1] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[2]=0x%08x; <%s>\n", le32toh(itd->itd_status[2]), (itd->itd_status[2] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[3]=0x%08x; <%s>\n", le32toh(itd->itd_status[3]), (itd->itd_status[3] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[4]=0x%08x; <%s>\n", le32toh(itd->itd_status[4]), (itd->itd_status[4] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[5]=0x%08x; <%s>\n", le32toh(itd->itd_status[5]), (itd->itd_status[5] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[6]=0x%08x; <%s>\n", le32toh(itd->itd_status[6]), (itd->itd_status[6] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" status[7]=0x%08x; <%s>\n", le32toh(itd->itd_status[7]), (itd->itd_status[7] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); printf(" bp[0]=0x%08x\n", le32toh(itd->itd_bp[0])); printf(" addr=0x%02x; endpt=0x%01x\n", EHCI_ITD_GET_ADDR(le32toh(itd->itd_bp[0])), EHCI_ITD_GET_ENDPT(le32toh(itd->itd_bp[0]))); printf(" bp[1]=0x%08x\n", le32toh(itd->itd_bp[1])); printf(" dir=%s; mpl=0x%02x\n", (le32toh(itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", EHCI_ITD_GET_MPL(le32toh(itd->itd_bp[1]))); printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", le32toh(itd->itd_bp[2]), le32toh(itd->itd_bp[3]), le32toh(itd->itd_bp[4]), le32toh(itd->itd_bp[5]), le32toh(itd->itd_bp[6])); printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" " 0x%08x,0x%08x,0x%08x\n", le32toh(itd->itd_bp_hi[0]), le32toh(itd->itd_bp_hi[1]), le32toh(itd->itd_bp_hi[2]), le32toh(itd->itd_bp_hi[3]), le32toh(itd->itd_bp_hi[4]), le32toh(itd->itd_bp_hi[5]), le32toh(itd->itd_bp_hi[6])); } static void ehci_dump_isoc(ehci_softc_t *sc) { ehci_itd_t *itd; ehci_sitd_t *sitd; uint16_t max = 1000; uint16_t pos; pos = (EOREAD4(sc, EHCI_FRINDEX) / 8) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); printf("%s: isochronous dump from frame 0x%03x:\n", __FUNCTION__, pos); itd = sc->sc_isoc_hs_p_last[pos]; sitd = sc->sc_isoc_fs_p_last[pos]; while (itd && max && max--) { ehci_dump_itd(itd); itd = itd->prev; } while (sitd && max && max--) { ehci_dump_sitd(sitd); sitd = sitd->prev; } } #endif static void ehci_transfer_intr_enqueue(struct usb2_xfer *xfer) { /* check for early completion */ if (ehci_check_transfer(xfer)) { return; } /* put transfer on interrupt queue */ usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usb2_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout); } } #define EHCI_APPEND_FS_TD(std,last) (last) = _ehci_append_fs_td(std,last) static ehci_sitd_t * _ehci_append_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) { DPRINTFN(11, "%p to %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->sitd_next = last->sitd_next; std->prev = last; usb2_pc_cpu_flush(std->page_cache); /* * the last->next->prev is never followed: std->next->prev = std; */ last->next = std; last->sitd_next = std->sitd_self; usb2_pc_cpu_flush(last->page_cache); return (std); } #define EHCI_APPEND_HS_TD(std,last) (last) = _ehci_append_hs_td(std,last) static ehci_itd_t * _ehci_append_hs_td(ehci_itd_t *std, ehci_itd_t *last) { DPRINTFN(11, "%p to %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->itd_next = last->itd_next; std->prev = last; usb2_pc_cpu_flush(std->page_cache); /* * the last->next->prev is never followed: std->next->prev = std; */ last->next = std; last->itd_next = std->itd_self; usb2_pc_cpu_flush(last->page_cache); return (std); } #define EHCI_APPEND_QH(sqh,last) (last) = _ehci_append_qh(sqh,last) static ehci_qh_t * _ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(11, "%p to %p\n", sqh, last); /* (sc->sc_bus.mtx) must be locked */ sqh->next = last->next; sqh->qh_link = last->qh_link; sqh->prev = last; usb2_pc_cpu_flush(sqh->page_cache); /* * the last->next->prev is never followed: sqh->next->prev = sqh; */ last->next = sqh; last->qh_link = sqh->qh_self; usb2_pc_cpu_flush(last->page_cache); #if USB_DEBUG if (ehcidebug > 5) { printf("%s:\n", __FUNCTION__); ehci_dump_sqh(sqh); } #endif return (sqh); } #define EHCI_REMOVE_FS_TD(std,last) (last) = _ehci_remove_fs_td(std,last) static ehci_sitd_t * _ehci_remove_fs_td(ehci_sitd_t *std, ehci_sitd_t *last) { DPRINTFN(11, "%p from %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->sitd_next = std->sitd_next; usb2_pc_cpu_flush(std->prev->page_cache); if (std->next) { std->next->prev = std->prev; usb2_pc_cpu_flush(std->next->page_cache); } return ((last == std) ? std->prev : last); } #define EHCI_REMOVE_HS_TD(std,last) (last) = _ehci_remove_hs_td(std,last) static ehci_itd_t * _ehci_remove_hs_td(ehci_itd_t *std, ehci_itd_t *last) { DPRINTFN(11, "%p from %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->itd_next = std->itd_next; usb2_pc_cpu_flush(std->prev->page_cache); if (std->next) { std->next->prev = std->prev; usb2_pc_cpu_flush(std->next->page_cache); } return ((last == std) ? std->prev : last); } #define EHCI_REMOVE_QH(sqh,last) (last) = _ehci_remove_qh(sqh,last) static ehci_qh_t * _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(11, "%p from %p\n", sqh, last); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if (sqh->prev) { sqh->prev->next = sqh->next; sqh->prev->qh_link = sqh->qh_link; usb2_pc_cpu_flush(sqh->prev->page_cache); if (sqh->next) { sqh->next->prev = sqh->prev; usb2_pc_cpu_flush(sqh->next->page_cache); } /* * set the Terminate-bit in the e_next of the QH, in case * the transferred packet was short so that the QH still * points at the last used TD */ sqh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); last = ((last == sqh) ? sqh->prev : last); sqh->prev = 0; usb2_pc_cpu_flush(sqh->page_cache); } return (last); } static usb2_error_t ehci_non_isoc_done_sub(struct usb2_xfer *xfer) { ehci_qtd_t *td; ehci_qtd_t *td_alt_next; uint32_t status; uint16_t len; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; while (1) { usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->qtd_status); len = EHCI_QTD_GET_BYTES(status); /* * Verify the status length and subtract * the remainder from "frlengths[]": */ if (len > td->len) { /* should not happen */ DPRINTF("Invalid status length, " "0x%04x/0x%04x bytes\n", len, td->len); status |= EHCI_QTD_HALTED; } else if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] -= len; } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { if (len == 0) { /* * Halt is ok if descriptor is last, * and complete: */ status &= ~EHCI_QTD_HALTED; } td = NULL; break; } /* Check for transfer error */ if (status & EHCI_QTD_HALTED) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; /* update data toggle */ xfer->pipe->toggle_next = (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0; #if USB_DEBUG if (status & EHCI_QTD_STATERRS) { DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x" "status=%s%s%s%s%s%s%s%s\n", xfer->address, xfer->endpoint, xfer->aframes, (status & EHCI_QTD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]", (status & EHCI_QTD_HALTED) ? "[HALTED]" : "", (status & EHCI_QTD_BUFERR) ? "[BUFERR]" : "", (status & EHCI_QTD_BABBLE) ? "[BABBLE]" : "", (status & EHCI_QTD_XACTERR) ? "[XACTERR]" : "", (status & EHCI_QTD_MISSEDMICRO) ? "[MISSED]" : "", (status & EHCI_QTD_SPLITXSTATE) ? "[SPLIT]" : "", (status & EHCI_QTD_PINGSTATE) ? "[PING]" : ""); } #endif return ((status & EHCI_QTD_HALTED) ? USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); } static void ehci_non_isoc_done(struct usb2_xfer *xfer) { usb2_error_t err = 0; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); #if USB_DEBUG if (ehcidebug > 10) { ehci_dump_sqtds(xfer->td_transfer_first); } #endif /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = ehci_non_isoc_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = ehci_non_isoc_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = ehci_non_isoc_done_sub(xfer); } done: ehci_device_done(xfer, err); } /*------------------------------------------------------------------------* * ehci_check_transfer * * Return values: * 0: USB transfer is not finished * Else: USB transfer is finished *------------------------------------------------------------------------*/ static uint8_t ehci_check_transfer(struct usb2_xfer *xfer) { struct usb2_pipe_methods *methods = xfer->pipe->methods; uint32_t status; DPRINTFN(13, "xfer=%p checking transfer\n", xfer); if (methods == &ehci_device_isoc_fs_methods) { ehci_sitd_t *td; /* isochronous full speed transfer */ td = xfer->td_transfer_last; usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->sitd_status); /* also check if first is complete */ td = xfer->td_transfer_first; usb2_pc_cpu_invalidate(td->page_cache); status |= le32toh(td->sitd_status); if (!(status & EHCI_SITD_ACTIVE)) { ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } } else if (methods == &ehci_device_isoc_hs_methods) { ehci_itd_t *td; /* isochronous high speed transfer */ td = xfer->td_transfer_last; usb2_pc_cpu_invalidate(td->page_cache); status = td->itd_status[0] | td->itd_status[1] | td->itd_status[2] | td->itd_status[3] | td->itd_status[4] | td->itd_status[5] | td->itd_status[6] | td->itd_status[7]; /* also check first transfer */ td = xfer->td_transfer_first; usb2_pc_cpu_invalidate(td->page_cache); status |= td->itd_status[0] | td->itd_status[1] | td->itd_status[2] | td->itd_status[3] | td->itd_status[4] | td->itd_status[5] | td->itd_status[6] | td->itd_status[7]; /* if no transactions are active we continue */ if (!(status & htole32(EHCI_ITD_ACTIVE))) { ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } } else { ehci_qtd_t *td; /* non-isochronous transfer */ /* * check whether there is an error somewhere in the middle, * or whether there was a short packet (SPD and not ACTIVE) */ td = xfer->td_transfer_cache; while (1) { usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->qtd_status); /* * if there is an active TD the transfer isn't done */ if (status & EHCI_QTD_ACTIVE) { /* update cache */ xfer->td_transfer_cache = td; goto done; } /* * last transfer descriptor makes the transfer done */ if (((void *)td) == xfer->td_transfer_last) { break; } /* * any kind of error makes the transfer done */ if (status & EHCI_QTD_HALTED) { break; } /* * if there is no alternate next transfer, a short * packet also makes the transfer done */ if (EHCI_QTD_GET_BYTES(status)) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { td = td->alt_next; continue; } } /* transfer is done */ break; } td = td->obj_next; } ehci_non_isoc_done(xfer); goto transferred; } done: DPRINTFN(13, "xfer=%p is still active\n", xfer); return (0); transferred: return (1); } static void ehci_pcd_enable(ehci_softc_t *sc) { USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); sc->sc_eintrs |= EHCI_STS_PCD; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); /* acknowledge any PCD interrupt */ EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD); usb2_sw_transfer(&sc->sc_root_intr, &ehci_root_intr_done); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void ehci_interrupt_poll(ehci_softc_t *sc) { struct usb2_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* * check if transfer is transferred */ if (ehci_check_transfer(xfer)) { /* queue has been modified */ goto repeat; } } } /*------------------------------------------------------------------------* * ehci_interrupt - EHCI interrupt handler * * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, * hence the interrupt handler will be setup before "sc->sc_bus.bdev" * is present ! *------------------------------------------------------------------------*/ void ehci_interrupt(ehci_softc_t *sc) { uint32_t status; USB_BUS_LOCK(&sc->sc_bus); DPRINTFN(16, "real interrupt\n"); #if USB_DEBUG if (ehcidebug > 15) { ehci_dump_regs(sc); } #endif status = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS)); if (status == 0) { /* the interrupt was not for us */ goto done; } if (!(status & sc->sc_eintrs)) { goto done; } EOWRITE4(sc, EHCI_USBSTS, status); /* acknowledge */ status &= sc->sc_eintrs; if (status & EHCI_STS_HSE) { printf("%s: unrecoverable error, " "controller halted\n", __FUNCTION__); #if USB_DEBUG ehci_dump_regs(sc); ehci_dump_isoc(sc); #endif } if (status & EHCI_STS_PCD) { /* * Disable PCD interrupt for now, because it will be * on until the port has been reset. */ sc->sc_eintrs &= ~EHCI_STS_PCD; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); usb2_sw_transfer(&sc->sc_root_intr, &ehci_root_intr_done); /* do not allow RHSC interrupts > 1 per second */ usb2_callout_reset(&sc->sc_tmo_pcd, hz, (void *)&ehci_pcd_enable, sc); } status &= ~(EHCI_STS_INT | EHCI_STS_ERRINT | EHCI_STS_PCD | EHCI_STS_IAA); if (status != 0) { /* block unprocessed interrupts */ sc->sc_eintrs &= ~status; EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); printf("%s: blocking interrupts 0x%x\n", __FUNCTION__, status); } /* poll all the USB transfers */ ehci_interrupt_poll(sc); done: USB_BUS_UNLOCK(&sc->sc_bus); } /* * called when a request does not complete */ static void ehci_timeout(void *arg) { struct usb2_xfer *xfer = arg; - ehci_softc_t *sc = xfer->usb2_sc; DPRINTF("xfer=%p\n", xfer); - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* transfer is transferred */ ehci_device_done(xfer, USB_ERR_TIMEOUT); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void ehci_do_poll(struct usb2_bus *bus) { struct ehci_softc *sc = EHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); ehci_interrupt_poll(sc); ehci_root_ctrl_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void ehci_setup_standard_chain_sub(struct ehci_std_temp *temp) { struct usb2_page_search buf_res; ehci_qtd_t *td; ehci_qtd_t *td_next; ehci_qtd_t *td_alt_next; uint32_t qtd_altnext; uint32_t buf_offset; uint32_t average; uint32_t len_old; uint8_t shortpkt_old; uint8_t precompute; qtd_altnext = htole32(EHCI_LINK_TERMINATE); td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; len_old = temp->len; precompute = 1; restart: td = temp->td; td_next = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) { break; } /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; average = 0; } else { average = temp->average; if (temp->len < average) { if (temp->len % temp->max_frame_size) { temp->shortpkt = 1; } average = temp->len; } } if (td_next == NULL) { panic("%s: out of EHCI transfer descriptors!", __FUNCTION__); } /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->qtd_status = temp->qtd_status | htole32(EHCI_QTD_SET_BYTES(average)); if (average == 0) { if (temp->auto_data_toggle == 0) { /* update data toggle, ZLP case */ temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); } td->len = 0; td->qtd_buffer[0] = 0; td->qtd_buffer_hi[0] = 0; td->qtd_buffer[1] = 0; td->qtd_buffer_hi[1] = 0; } else { uint8_t x; if (temp->auto_data_toggle == 0) { /* update data toggle */ if (((average + temp->max_frame_size - 1) / temp->max_frame_size) & 1) { temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); } } td->len = average; /* update remaining length */ temp->len -= average; /* fill out buffer pointers */ usb2_get_page(temp->pc, buf_offset, &buf_res); td->qtd_buffer[0] = htole32(buf_res.physaddr); td->qtd_buffer_hi[0] = 0; x = 1; while (average > EHCI_PAGE_SIZE) { average -= EHCI_PAGE_SIZE; buf_offset += EHCI_PAGE_SIZE; usb2_get_page(temp->pc, buf_offset, &buf_res); td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[x] = 0; x++; } /* * NOTE: The "average" variable is never zero after * exiting the loop above ! * * NOTE: We have to subtract one from the offset to * ensure that we are computing the physical address * of a valid page ! */ buf_offset += average; usb2_get_page(temp->pc, buf_offset - 1, &buf_res); td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[x] = 0; } if (td_next) { /* link the current TD with the next one */ td->qtd_next = td_next->qtd_self; } td->qtd_altnext = qtd_altnext; td->alt_next = td_alt_next; usb2_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* setup alt next pointer, if any */ if (temp->short_frames_ok) { if (temp->setup_alt_next) { td_alt_next = td_next; qtd_altnext = td_next->qtd_self; } } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } temp->td = td; temp->td_next = td_next; } static void ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) { struct ehci_std_temp temp; struct usb2_pipe_methods *methods; ehci_qh_t *qh; ehci_qtd_t *td; uint32_t qh_endp; uint32_t qh_endphub; uint32_t x; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->sumlen, usb2_get_speed(xfer->udev)); temp.average = xfer->max_usb2_frame_size; temp.max_frame_size = xfer->max_frame_size; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; temp.td = NULL; temp.td_next = td; temp.qtd_status = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.short_frames_ok = xfer->flags_int.short_frames_ok; if (xfer->flags_int.control_xfr) { if (xfer->pipe->toggle_next) { /* DATA1 is next */ temp.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); } temp.auto_data_toggle = 0; } else { temp.auto_data_toggle = 1; } if (usb2_get_speed(xfer->udev) != USB_SPEED_HIGH) { /* max 3 retries */ temp.qtd_status |= htole32(EHCI_QTD_SET_CERR(3)); } /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3)); temp.qtd_status |= htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | EHCI_QTD_SET_TOGGLE(0)); temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; ehci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.pc = xfer->frbuffers + x; x++; if (x == xfer->nframes) { temp.setup_alt_next = 0; } /* keep previous data toggle and error count */ temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(1)); if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; } else { /* regular data transfer */ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; } /* set endpoint direction */ temp.qtd_status |= (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? htole32(EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : htole32(EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT)); ehci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current endpoint * direction. */ temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(1)); temp.qtd_status |= (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? htole32(EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | EHCI_QTD_SET_TOGGLE(1)) : htole32(EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) | EHCI_QTD_SET_TOGGLE(1)); temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; ehci_setup_standard_chain_sub(&temp); } td = temp.td; /* the last TD terminates the transfer: */ td->qtd_next = htole32(EHCI_LINK_TERMINATE); td->qtd_altnext = htole32(EHCI_LINK_TERMINATE); td->qtd_status |= htole32(EHCI_QTD_IOC); usb2_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ xfer->td_transfer_last = td; #if USB_DEBUG if (ehcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next); ehci_dump_sqtds(xfer->td_transfer_first); } #endif methods = xfer->pipe->methods; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* the "qh_link" field is filled when the QH is added */ qh_endp = (EHCI_QH_SET_ADDR(xfer->address) | EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) | EHCI_QH_SET_MPL(xfer->max_packet_size)); if (usb2_get_speed(xfer->udev) == USB_SPEED_HIGH) { qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_DTC | EHCI_QH_SET_NRL(8)); } else { if (usb2_get_speed(xfer->udev) == USB_SPEED_FULL) { qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL) | EHCI_QH_DTC); } else { qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW) | EHCI_QH_DTC); } if (methods == &ehci_device_ctrl_methods) { qh_endp |= EHCI_QH_CTL; } if (methods != &ehci_device_intr_methods) { /* Only try one time per microframe! */ qh_endp |= EHCI_QH_SET_NRL(1); } } qh->qh_endp = htole32(qh_endp); qh_endphub = (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) | EHCI_QH_SET_CMASK(xfer->usb2_cmask) | EHCI_QH_SET_SMASK(xfer->usb2_smask) | EHCI_QH_SET_HUBA(xfer->udev->hs_hub_addr) | EHCI_QH_SET_PORT(xfer->udev->hs_port_no)); qh->qh_endphub = htole32(qh_endphub); qh->qh_curqtd = htole32(0); /* fill the overlay qTD */ qh->qh_qtd.qtd_status = htole32(0); if (temp.auto_data_toggle) { /* let the hardware compute the data toggle */ qh->qh_endp &= ~htole32(EHCI_QH_DTC); if (xfer->pipe->toggle_next) { /* DATA1 is next */ qh->qh_qtd.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); } } td = xfer->td_transfer_first; qh->qh_qtd.qtd_next = td->qtd_self; qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); usb2_pc_cpu_flush(qh->page_cache); EHCI_APPEND_QH(qh, *qh_last); } static void ehci_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct ehci_softc *sc = xfer->usb2_sc; uint16_t i; uint16_t m; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_PRE_DATA) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ ehci_device_done(xfer, std->err); } goto done; } /* setup buffer */ std->ptr = sc->sc_hub_idata; std->len = sizeof(sc->sc_hub_idata); /* clear any old interrupt data */ bzero(sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); /* set bits */ m = (sc->sc_noport + 1); if (m > (8 * sizeof(sc->sc_hub_idata))) { m = (8 * sizeof(sc->sc_hub_idata)); } for (i = 1; i < m; i++) { /* pick out CHANGE bits from the status register */ if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) { sc->sc_hub_idata[i / 8] |= 1 << (i % 8); DPRINTF("port %d changed\n", i); } } done: return; } static void ehci_isoc_fs_done(ehci_softc_t *sc, struct usb2_xfer *xfer) { uint32_t nframes = xfer->nframes; uint32_t status; uint32_t *plen = xfer->frlengths; uint16_t len = 0; ehci_sitd_t *td = xfer->td_transfer_first; ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos]; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_fs_p_last[0]; } #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("isoc FS-TD\n"); ehci_dump_sitd(td); } #endif usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->sitd_status); len = EHCI_SITD_GET_LEN(status); if (*plen >= len) { len = *plen - len; } else { len = 0; } *plen = len; /* remove FS-TD from schedule */ EHCI_REMOVE_FS_TD(td, *pp_last); pp_last++; plen++; td = td->obj_next; } xfer->aframes = xfer->nframes; } static void ehci_isoc_hs_done(ehci_softc_t *sc, struct usb2_xfer *xfer) { uint32_t nframes = xfer->nframes; uint32_t status; uint32_t *plen = xfer->frlengths; uint16_t len = 0; uint8_t td_no = 0; ehci_itd_t *td = xfer->td_transfer_first; ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos]; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_hs_p_last[0]; } #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("isoc HS-TD\n"); ehci_dump_itd(td); } #endif usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->itd_status[td_no]); len = EHCI_ITD_GET_LEN(status); if (*plen >= len) { /* * The length is valid. NOTE: The complete * length is written back into the status * field, and not the remainder like with * other transfer descriptor types. */ } else { /* Invalid length - truncate */ len = 0; } *plen = len; plen++; td_no++; if ((td_no == 8) || (nframes == 0)) { /* remove HS-TD from schedule */ EHCI_REMOVE_HS_TD(td, *pp_last); pp_last++; td_no = 0; td = td->obj_next; } } xfer->aframes = xfer->nframes; } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error) { struct usb2_pipe_methods *methods = xfer->pipe->methods; ehci_softc_t *sc = xfer->usb2_sc; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error); if ((methods == &ehci_device_bulk_methods) || (methods == &ehci_device_ctrl_methods)) { #if USB_DEBUG if (ehcidebug > 8) { DPRINTF("nexttog=%d; data after transfer:\n", xfer->pipe->toggle_next); ehci_dump_sqtds(xfer->td_transfer_first); } #endif EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_async_p_last); } if (methods == &ehci_device_intr_methods) { EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], sc->sc_intr_p_last[xfer->qh_pos]); } /* * Only finish isochronous transfers once which will update * "xfer->frlengths". */ if (xfer->td_transfer_first && xfer->td_transfer_last) { if (methods == &ehci_device_isoc_fs_methods) { ehci_isoc_fs_done(sc, xfer); } if (methods == &ehci_device_isoc_hs_methods) { ehci_isoc_hs_done(sc, xfer); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } /* dequeue transfer and start next transfer */ usb2_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * ehci bulk support *------------------------------------------------------------------------*/ static void ehci_device_bulk_open(struct usb2_xfer *xfer) { return; } static void ehci_device_bulk_close(struct usb2_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_bulk_enter(struct usb2_xfer *xfer) { return; } static void ehci_device_bulk_start(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ehci_device_bulk_methods = { .open = ehci_device_bulk_open, .close = ehci_device_bulk_close, .enter = ehci_device_bulk_enter, .start = ehci_device_bulk_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ehci control support *------------------------------------------------------------------------*/ static void ehci_device_ctrl_open(struct usb2_xfer *xfer) { return; } static void ehci_device_ctrl_close(struct usb2_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_ctrl_enter(struct usb2_xfer *xfer) { return; } static void ehci_device_ctrl_start(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last); /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ehci_device_ctrl_methods = { .open = ehci_device_ctrl_open, .close = ehci_device_ctrl_close, .enter = ehci_device_ctrl_enter, .start = ehci_device_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ehci interrupt support *------------------------------------------------------------------------*/ static void ehci_device_intr_open(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; uint16_t best; uint16_t bit; uint16_t x; uint8_t slot; /* Allocate a microframe slot first: */ slot = usb2_intr_schedule_adjust (xfer->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX); if (usb2_get_speed(xfer->udev) == USB_SPEED_HIGH) { xfer->usb2_uframe = slot; xfer->usb2_smask = (1 << slot) & 0xFF; xfer->usb2_cmask = 0; } else { xfer->usb2_uframe = slot; xfer->usb2_smask = (1 << slot) & 0x3F; xfer->usb2_cmask = (-(4 << slot)) & 0xFE; } /* * Find the best QH position corresponding to the given interval: */ best = 0; bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2; while (bit) { if (xfer->interval >= bit) { x = bit; best = bit; while (x & bit) { if (sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(3, "best=%d interval=%d\n", best, xfer->interval); } static void ehci_device_intr_close(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; uint8_t slot; slot = usb2_intr_schedule_adjust (xfer->udev, -(xfer->max_frame_size), xfer->usb2_uframe); sc->sc_intr_stat[xfer->qh_pos]--; ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_intr_enter(struct usb2_xfer *xfer) { return; } static void ehci_device_intr_start(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; /* setup TD's and QH */ ehci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ehci_device_intr_methods = { .open = ehci_device_intr_open, .close = ehci_device_intr_close, .enter = ehci_device_intr_enter, .start = ehci_device_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ehci full speed isochronous support *------------------------------------------------------------------------*/ static void ehci_device_isoc_fs_open(struct usb2_xfer *xfer) { ehci_sitd_t *td; uint32_t sitd_portaddr; uint8_t ds; sitd_portaddr = EHCI_SITD_SET_ADDR(xfer->address) | EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) | EHCI_SITD_SET_HUBA(xfer->udev->hs_hub_addr) | EHCI_SITD_SET_PORT(xfer->udev->hs_port_no); if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { sitd_portaddr |= EHCI_SITD_SET_DIR_IN; } sitd_portaddr = htole32(sitd_portaddr); /* initialize all TD's */ for (ds = 0; ds != 2; ds++) { for (td = xfer->td_start[ds]; td; td = td->obj_next) { td->sitd_portaddr = sitd_portaddr; /* * TODO: make some kind of automatic * SMASK/CMASK selection based on micro-frame * usage * * micro-frame usage (8 microframes per 1ms) */ td->sitd_back = htole32(EHCI_LINK_TERMINATE); usb2_pc_cpu_flush(td->page_cache); } } } static void ehci_device_isoc_fs_close(struct usb2_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) { struct usb2_page_search buf_res; ehci_softc_t *sc = xfer->usb2_sc; struct usb2_fs_isoc_schedule *fss_start; struct usb2_fs_isoc_schedule *fss_end; struct usb2_fs_isoc_schedule *fss; ehci_sitd_t *td; ehci_sitd_t *td_last = NULL; ehci_sitd_t **pp_last; uint32_t *plen; uint32_t buf_offset; uint32_t nframes; uint32_t temp; uint32_t sitd_mask; uint16_t tlen; uint8_t sa; uint8_t sb; uint8_t error; #if USB_DEBUG uint8_t once = 1; #endif DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes); /* get the current frame index */ nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; /* * check if the frame index is within the window where the frames * will be inserted */ buf_offset = (nframes - xfer->pipe->isoc_next) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); if ((xfer->pipe->is_synced == 0) || (buf_offset < xfer->nframes)) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->pipe->isoc_next = (nframes + 3) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); xfer->pipe->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ buf_offset = (xfer->pipe->isoc_next - nframes) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb2_fs_isoc_schedule_isoc_time_expand (xfer->udev, &fss_start, &fss_end, nframes) + buf_offset + xfer->nframes; /* get the real number of frames */ nframes = xfer->nframes; buf_offset = 0; plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_fs_p_last[xfer->pipe->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->pipe->isoc_next; fss = fss_start + (xfer->qh_pos % USB_ISOC_TIME_MAX); while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_fs_p_last[0]; } if (fss >= fss_end) { fss = fss_start; } /* reuse sitd_portaddr and sitd_back from last transfer */ if (*plen > xfer->max_frame_size) { #if USB_DEBUG if (once) { once = 0; printf("%s: frame length(%d) exceeds %d " "bytes (frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } /* * We currently don't care if the ISOCHRONOUS schedule is * full! */ error = usb2_fs_isoc_schedule_alloc(fss, &sa, *plen); if (error) { /* * The FULL speed schedule is FULL! Set length * to zero. */ *plen = 0; } if (*plen) { /* * only call "usb2_get_page()" when we have a * non-zero length */ usb2_get_page(xfer->frbuffers, buf_offset, &buf_res); td->sitd_bp[0] = htole32(buf_res.physaddr); buf_offset += *plen; /* * NOTE: We need to subtract one from the offset so * that we are on a valid page! */ usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); temp = buf_res.physaddr & ~0xFFF; } else { td->sitd_bp[0] = 0; temp = 0; } if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) { tlen = *plen; if (tlen <= 188) { temp |= 1; /* T-count = 1, TP = ALL */ tlen = 1; } else { tlen += 187; tlen /= 188; temp |= tlen; /* T-count = [1..6] */ temp |= 8; /* TP = Begin */ } tlen += sa; if (tlen >= 8) { sb = 0; } else { sb = (1 << tlen); } sa = (1 << sa); sa = (sb - sa) & 0x3F; sb = 0; } else { sb = (-(4 << sa)) & 0xFE; sa = (1 << sa) & 0x3F; } sitd_mask = (EHCI_SITD_SET_SMASK(sa) | EHCI_SITD_SET_CMASK(sb)); td->sitd_bp[1] = htole32(temp); td->sitd_mask = htole32(sitd_mask); if (nframes == 0) { td->sitd_status = htole32 (EHCI_SITD_IOC | EHCI_SITD_ACTIVE | EHCI_SITD_SET_LEN(*plen)); } else { td->sitd_status = htole32 (EHCI_SITD_ACTIVE | EHCI_SITD_SET_LEN(*plen)); } usb2_pc_cpu_flush(td->page_cache); #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("FS-TD %d\n", nframes); ehci_dump_sitd(td); } #endif /* insert TD into schedule */ EHCI_APPEND_FS_TD(td, *pp_last); pp_last++; plen++; fss++; td_last = td; td = td->obj_next; } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); } static void ehci_device_isoc_fs_start(struct usb2_xfer *xfer) { /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ehci_device_isoc_fs_methods = { .open = ehci_device_isoc_fs_open, .close = ehci_device_isoc_fs_close, .enter = ehci_device_isoc_fs_enter, .start = ehci_device_isoc_fs_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ehci high speed isochronous support *------------------------------------------------------------------------*/ static void ehci_device_isoc_hs_open(struct usb2_xfer *xfer) { ehci_itd_t *td; uint32_t temp; uint8_t ds; /* initialize all TD's */ for (ds = 0; ds != 2; ds++) { for (td = xfer->td_start[ds]; td; td = td->obj_next) { /* set TD inactive */ td->itd_status[0] = 0; td->itd_status[1] = 0; td->itd_status[2] = 0; td->itd_status[3] = 0; td->itd_status[4] = 0; td->itd_status[5] = 0; td->itd_status[6] = 0; td->itd_status[7] = 0; /* set endpoint and address */ td->itd_bp[0] = htole32 (EHCI_ITD_SET_ADDR(xfer->address) | EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint))); temp = EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF); /* set direction */ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { temp |= EHCI_ITD_SET_DIR_IN; } /* set maximum packet size */ td->itd_bp[1] = htole32(temp); /* set transfer multiplier */ td->itd_bp[2] = htole32(xfer->max_packet_count & 3); usb2_pc_cpu_flush(td->page_cache); } } } static void ehci_device_isoc_hs_close(struct usb2_xfer *xfer) { ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) { struct usb2_page_search buf_res; ehci_softc_t *sc = xfer->usb2_sc; ehci_itd_t *td; ehci_itd_t *td_last = NULL; ehci_itd_t **pp_last; bus_size_t page_addr; uint32_t *plen; uint32_t status; uint32_t buf_offset; uint32_t nframes; uint32_t itd_offset[8 + 1]; uint8_t x; uint8_t td_no; uint8_t page_no; #if USB_DEBUG uint8_t once = 1; #endif DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes); /* get the current frame index */ nframes = EOREAD4(sc, EHCI_FRINDEX) / 8; /* * check if the frame index is within the window where the frames * will be inserted */ buf_offset = (nframes - xfer->pipe->isoc_next) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); if ((xfer->pipe->is_synced == 0) || (buf_offset < ((xfer->nframes + 7) / 8))) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->pipe->isoc_next = (nframes + 3) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); xfer->pipe->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ buf_offset = (xfer->pipe->isoc_next - nframes) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb2_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + ((xfer->nframes + 7) / 8); /* get the real number of frames */ nframes = xfer->nframes; buf_offset = 0; td_no = 0; plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_hs_p_last[xfer->pipe->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->pipe->isoc_next; while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_hs_p_last[0]; } /* range check */ if (*plen > xfer->max_frame_size) { #if USB_DEBUG if (once) { once = 0; printf("%s: frame length(%d) exceeds %d bytes " "(frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } status = (EHCI_ITD_SET_LEN(*plen) | EHCI_ITD_ACTIVE | EHCI_ITD_SET_PG(0)); td->itd_status[td_no] = htole32(status); itd_offset[td_no] = buf_offset; buf_offset += *plen; plen++; td_no++; if ((td_no == 8) || (nframes == 0)) { /* the rest of the transfers are not active, if any */ for (x = td_no; x != 8; x++) { td->itd_status[x] = 0; /* not active */ } /* check if there is any data to be transferred */ if (itd_offset[0] != buf_offset) { page_no = 0; itd_offset[td_no] = buf_offset; /* get first page offset */ usb2_get_page(xfer->frbuffers, itd_offset[0], &buf_res); /* get page address */ page_addr = buf_res.physaddr & ~0xFFF; /* update page address */ td->itd_bp[0] &= htole32(0xFFF); td->itd_bp[0] |= htole32(page_addr); for (x = 0; x != td_no; x++) { /* set page number and page offset */ status = (EHCI_ITD_SET_PG(page_no) | (buf_res.physaddr & 0xFFF)); td->itd_status[x] |= htole32(status); /* get next page offset */ if (itd_offset[x + 1] == buf_offset) { /* * We subtract one so that * we don't go off the last * page! */ usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); } else { usb2_get_page(xfer->frbuffers, itd_offset[x + 1], &buf_res); } /* check if we need a new page */ if ((buf_res.physaddr ^ page_addr) & ~0xFFF) { /* new page needed */ page_addr = buf_res.physaddr & ~0xFFF; if (page_no == 6) { panic("%s: too many pages\n", __FUNCTION__); } page_no++; /* update page address */ td->itd_bp[page_no] &= htole32(0xFFF); td->itd_bp[page_no] |= htole32(page_addr); } } } /* set IOC bit if we are complete */ if (nframes == 0) { td->itd_status[7] |= htole32(EHCI_ITD_IOC); } usb2_pc_cpu_flush(td->page_cache); #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("HS-TD %d\n", nframes); ehci_dump_itd(td); } #endif /* insert TD into schedule */ EHCI_APPEND_HS_TD(td, *pp_last); pp_last++; td_no = 0; td_last = td; td = td->obj_next; } } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); } static void ehci_device_isoc_hs_start(struct usb2_xfer *xfer) { /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ehci_device_isoc_hs_methods = { .open = ehci_device_isoc_hs_open, .close = ehci_device_isoc_hs_close, .enter = ehci_device_isoc_hs_enter, .start = ehci_device_isoc_hs_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ehci root control support *------------------------------------------------------------------------* * simulate a hardware hub by handling * all the necessary requests *------------------------------------------------------------------------*/ static void ehci_root_ctrl_open(struct usb2_xfer *xfer) { return; } static void ehci_root_ctrl_close(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; if (sc->sc_root_ctrl.xfer == xfer) { sc->sc_root_ctrl.xfer = NULL; } ehci_device_done(xfer, USB_ERR_CANCELLED); } /* data structures and routines * to emulate the root hub: */ static const struct usb2_device_descriptor ehci_devd = { sizeof(struct usb2_device_descriptor), UDESC_DEVICE, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_HSHUBSTT, /* protocol */ 64, /* max packet */ {0}, {0}, {0x00, 0x01}, /* device id */ 1, 2, 0, /* string indicies */ 1 /* # of configurations */ }; static const struct usb2_device_qualifier ehci_odevd = { sizeof(struct usb2_device_qualifier), UDESC_DEVICE_QUALIFIER, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 0, /* max packet */ 0, /* # of configurations */ 0 }; static const struct ehci_config_desc ehci_confd = { .confd = { .bLength = sizeof(struct usb2_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(ehci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0 /* max power */ }, .ifcd = { .bLength = sizeof(struct usb2_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_HSHUBSTT, 0 }, .endpd = { .bLength = sizeof(struct usb2_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | EHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, /* max packet (63 ports) */ .bInterval = 255, }, }; static const struct usb2_hub_descriptor ehci_hubd = { 0, /* dynamic length */ UDESC_HUB, 0, {0, 0}, 0, 0, {0}, }; static void ehci_disown(ehci_softc_t *sc, uint16_t index, uint8_t lowspeed) { uint32_t port; uint32_t v; DPRINTF("index=%d lowspeed=%d\n", index, lowspeed); port = EHCI_PORTSC(index); v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; EOWRITE4(sc, port, v | EHCI_PS_PO); } static void ehci_root_ctrl_enter(struct usb2_xfer *xfer) { return; } static void ehci_root_ctrl_start(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; DPRINTF("\n"); sc->sc_root_ctrl.xfer = xfer; usb2_config_td_queue_command (&sc->sc_config_td, NULL, &ehci_root_ctrl_task, 0, 0); } static void ehci_root_ctrl_task(struct ehci_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { ehci_root_ctrl_poll(sc); } static void ehci_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct ehci_softc *sc = xfer->usb2_sc; char *ptr; uint32_t port; uint32_t v; uint16_t i; uint16_t value; uint16_t index; uint8_t l; uint8_t use_polling; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_SETUP) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ ehci_device_done(xfer, std->err); } goto done; } /* buffer reset */ std->ptr = sc->sc_hub_desc.temp; std->len = 0; value = UGETW(std->req.wValue); index = UGETW(std->req.wIndex); use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", std->req.bmRequestType, std->req.bRequest, UGETW(std->req.wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(std->req.bRequest, std->req.bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): std->len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(ehci_devd); sc->sc_hub_desc.devd = ehci_devd; break; /* * We can't really operate at another speed, * but the specification says we need this * descriptor: */ case UDESC_DEVICE_QUALIFIER: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(ehci_odevd); sc->sc_hub_desc.odevd = ehci_odevd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(ehci_confd); std->ptr = USB_ADD_BYTES(&ehci_confd, 0); break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ ptr = "\001"; break; case 1: /* Vendor */ ptr = sc->sc_vendor; break; case 2: /* Product */ ptr = "EHCI root HUB"; break; default: ptr = ""; break; } std->len = usb2_make_str_desc (sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), ptr); break; default: std->err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): std->len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): std->len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): std->len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= USB_MAX_DEVICES) { std->err = USB_ERR_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if ((value != 0) && (value != 1)) { std->err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): std->err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n"); if ((index < 1) || (index > sc->sc_noport)) { std->err = USB_ERR_IOERROR; goto done; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; switch (value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v & ~EHCI_PS_PE); break; case UHF_PORT_SUSPEND: EOWRITE4(sc, port, v & ~EHCI_PS_SUSP); break; case UHF_PORT_POWER: EOWRITE4(sc, port, v & ~EHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(3, "clear port test " "%d\n", index); break; case UHF_PORT_INDICATOR: DPRINTFN(3, "clear port ind " "%d\n", index); EOWRITE4(sc, port, v & ~EHCI_PS_PIC); break; case UHF_C_PORT_CONNECTION: EOWRITE4(sc, port, v | EHCI_PS_CSC); break; case UHF_C_PORT_ENABLE: EOWRITE4(sc, port, v | EHCI_PS_PEC); break; case UHF_C_PORT_SUSPEND: EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_C_PORT_OVER_CURRENT: EOWRITE4(sc, port, v | EHCI_PS_OCC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; break; default: std->err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } v = EOREAD4(sc, EHCI_HCSPARAMS); sc->sc_hub_desc.hubd = ehci_hubd; sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, (EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH) | (EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ? UHD_PORT_IND : 0)); sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ for (l = 0; l < sc->sc_noport; l++) { /* XXX can't find out? */ sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] &= ~(1 << (l % 8)); } sc->sc_hub_desc.hubd.bDescLength = 8 + ((sc->sc_noport + 7) / 8); std->len = sc->sc_hub_desc.hubd.bDescLength; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): std->len = 16; bzero(sc->sc_hub_desc.temp, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "get port status i=%d\n", index); if ((index < 1) || (index > sc->sc_noport)) { std->err = USB_ERR_IOERROR; goto done; } v = EOREAD4(sc, EHCI_PORTSC(index)); DPRINTFN(9, "port status=0x%04x\n", v); if (sc->sc_flags & EHCI_SCFLG_FORCESPEED) { if ((v & 0xc000000) == 0x8000000) i = UPS_HIGH_SPEED; else if ((v & 0xc000000) == 0x4000000) i = UPS_LOW_SPEED; else i = 0; } else { i = UPS_HIGH_SPEED; } if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND; if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; if (v & EHCI_PS_PR) i |= UPS_RESET; if (v & EHCI_PS_PP) i |= UPS_PORT_POWER; USETW(sc->sc_hub_desc.ps.wPortStatus, i); i = 0; if (v & EHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if (v & EHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (sc->sc_isreset) i |= UPS_C_PORT_RESET; USETW(sc->sc_hub_desc.ps.wPortChange, i); std->len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): std->err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if ((index < 1) || (index > sc->sc_noport)) { std->err = USB_ERR_IOERROR; goto done; } port = EHCI_PORTSC(index); v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR; switch (value) { case UHF_PORT_ENABLE: EOWRITE4(sc, port, v | EHCI_PS_PE); break; case UHF_PORT_SUSPEND: EOWRITE4(sc, port, v | EHCI_PS_SUSP); break; case UHF_PORT_RESET: DPRINTFN(6, "reset port %d\n", index); #if USB_DEBUG if (ehcinohighspeed) { /* * Connect USB device to companion * controller. */ ehci_disown(sc, index, 1); break; } #endif if (EHCI_PS_IS_LOWSPEED(v)) { /* Low speed device, give up ownership. */ ehci_disown(sc, index, 1); break; } /* Start reset sequence. */ v &= ~(EHCI_PS_PE | EHCI_PS_PR); EOWRITE4(sc, port, v | EHCI_PS_PR); if (use_polling) { /* polling */ DELAY(USB_PORT_ROOT_RESET_DELAY * 1000); } else { /* Wait for reset to complete. */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_PORT_ROOT_RESET_DELAY); } /* Terminate reset sequence. */ if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM)) EOWRITE4(sc, port, v); if (use_polling) { /* polling */ DELAY(EHCI_PORT_RESET_COMPLETE * 1000); } else { /* Wait for HC to complete reset. */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, EHCI_PORT_RESET_COMPLETE); } v = EOREAD4(sc, port); DPRINTF("ehci after reset, status=0x%08x\n", v); if (v & EHCI_PS_PR) { device_printf(sc->sc_bus.bdev, "port reset timeout\n"); std->err = USB_ERR_TIMEOUT; goto done; } if (!(v & EHCI_PS_PE)) { /* * Not a high speed device, give up * ownership. */ ehci_disown(sc, index, 0); break; } sc->sc_isreset = 1; DPRINTF("ehci port %d reset, status = 0x%08x\n", index, v); break; case UHF_PORT_POWER: DPRINTFN(3, "set port power %d\n", index); EOWRITE4(sc, port, v | EHCI_PS_PP); break; case UHF_PORT_TEST: DPRINTFN(3, "set port test %d\n", index); break; case UHF_PORT_INDICATOR: DPRINTFN(3, "set port ind %d\n", index); EOWRITE4(sc, port, v | EHCI_PS_PIC); break; default: std->err = USB_ERR_IOERROR; goto done; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: std->err = USB_ERR_IOERROR; goto done; } done: return; } static void ehci_root_ctrl_poll(struct ehci_softc *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &ehci_root_ctrl_done); } struct usb2_pipe_methods ehci_root_ctrl_methods = { .open = ehci_root_ctrl_open, .close = ehci_root_ctrl_close, .enter = ehci_root_ctrl_enter, .start = ehci_root_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 0, }; /*------------------------------------------------------------------------* * ehci root interrupt support *------------------------------------------------------------------------*/ static void ehci_root_intr_open(struct usb2_xfer *xfer) { return; } static void ehci_root_intr_close(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; if (sc->sc_root_intr.xfer == xfer) { sc->sc_root_intr.xfer = NULL; } ehci_device_done(xfer, USB_ERR_CANCELLED); } static void ehci_root_intr_enter(struct usb2_xfer *xfer) { return; } static void ehci_root_intr_start(struct usb2_xfer *xfer) { ehci_softc_t *sc = xfer->usb2_sc; sc->sc_root_intr.xfer = xfer; } struct usb2_pipe_methods ehci_root_intr_methods = { .open = ehci_root_intr_open, .close = ehci_root_intr_close, .enter = ehci_root_intr_enter, .start = ehci_root_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; static void ehci_xfer_setup(struct usb2_setup_params *parm) { struct usb2_page_search page_info; struct usb2_page_cache *pc; ehci_softc_t *sc; struct usb2_xfer *xfer; void *last_obj; uint32_t nqtd; uint32_t nqh; uint32_t nsitd; uint32_t nitd; uint32_t n; sc = EHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; nqtd = 0; nqh = 0; nsitd = 0; nitd = 0; /* * setup xfer */ xfer->usb2_sc = sc; /* * compute maximum number of some structures */ if (parm->methods == &ehci_device_ctrl_methods) { /* * The proof for the "nqtd" formula is illustrated like * this: * * +------------------------------------+ * | | * | |remainder -> | * | +-----+---+ | * | | xxx | x | frm 0 | * | +-----+---++ | * | | xxx | xx | frm 1 | * | +-----+----+ | * | ... | * +------------------------------------+ * * "xxx" means a completely full USB transfer descriptor * * "x" and "xx" means a short USB packet * * For the remainder of an USB transfer modulo * "max_data_length" we need two USB transfer descriptors. * One to transfer the remaining data and one to finalise * with a zero length packet in case the "force_short_xfer" * flag is set. We only need two USB transfer descriptors in * the case where the transfer length of the first one is a * factor of "max_frame_size". The rest of the needed USB * transfer descriptors is given by the buffer size divided * by the maximum data payload. */ parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nqh = 1; nqtd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_usb2_frame_size)); } else if (parm->methods == &ehci_device_bulk_methods) { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nqh = 1; nqtd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_usb2_frame_size)); } else if (parm->methods == &ehci_device_intr_methods) { if (parm->speed == USB_SPEED_HIGH) { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 3; } else if (parm->speed == USB_SPEED_FULL) { parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME; parm->hc_max_packet_count = 1; } else { parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME / 8; parm->hc_max_packet_count = 1; } parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX; xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nqh = 1; nqtd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_usb2_frame_size)); } else if (parm->methods == &ehci_device_isoc_fs_methods) { parm->hc_max_packet_size = 0x3FF; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x3FF; xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nsitd = xfer->nframes; } else if (parm->methods == &ehci_device_isoc_hs_methods) { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 3; parm->hc_max_frame_size = 0xC00; xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nitd = (xfer->nframes + 7) / 8; } else { parm->hc_max_packet_size = 0x400; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x400; usb2_transfer_setup_sub(parm); } alloc_dma_set: if (parm->err) { return; } /* * Allocate queue heads and transfer descriptors */ last_obj = NULL; if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_itd_t), EHCI_ITD_ALIGN, nitd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nitd; n++) { ehci_itd_t *td; usb2_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->itd_self = htole32(page_info.physaddr | EHCI_LINK_ITD); td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb2_pc_cpu_flush(pc + n); } } if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_sitd_t), EHCI_SITD_ALIGN, nsitd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nsitd; n++) { ehci_sitd_t *td; usb2_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->sitd_self = htole32(page_info.physaddr | EHCI_LINK_SITD); td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb2_pc_cpu_flush(pc + n); } } if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_qtd_t), EHCI_QTD_ALIGN, nqtd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqtd; n++) { ehci_qtd_t *qtd; usb2_get_page(pc + n, 0, &page_info); qtd = page_info.buffer; /* init TD */ qtd->qtd_self = htole32(page_info.physaddr); qtd->obj_next = last_obj; qtd->page_cache = pc + n; last_obj = qtd; usb2_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ehci_qh_t), EHCI_QH_ALIGN, nqh)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqh; n++) { ehci_qh_t *qh; usb2_get_page(pc + n, 0, &page_info); qh = page_info.buffer; /* init QH */ qh->qh_self = htole32(page_info.physaddr | EHCI_LINK_QH); qh->obj_next = last_obj; qh->page_cache = pc + n; last_obj = qh; usb2_pc_cpu_flush(pc + n); } } xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static void ehci_xfer_unsetup(struct usb2_xfer *xfer) { return; } static void ehci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) { ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, udev->flags.usb2_mode, sc->sc_addr); if (udev->flags.usb2_mode != USB_MODE_HOST) { /* not supported */ return; } if (udev->device_index == sc->sc_addr) { switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &ehci_root_ctrl_methods; break; case UE_DIR_IN | EHCI_INTR_ENDPT: pipe->methods = &ehci_root_intr_methods; break; default: /* do nothing */ break; } } else { if ((udev->speed != USB_SPEED_HIGH) && ((udev->hs_hub_addr == 0) || (udev->hs_port_no == 0) || (udev->bus->devices[udev->hs_hub_addr] == NULL) || (udev->bus->devices[udev->hs_hub_addr]->hub == NULL))) { /* We need a transaction translator */ goto done; } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &ehci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &ehci_device_intr_methods; break; case UE_ISOCHRONOUS: if (udev->speed == USB_SPEED_HIGH) { pipe->methods = &ehci_device_isoc_hs_methods; } else if (udev->speed == USB_SPEED_FULL) { pipe->methods = &ehci_device_isoc_fs_methods; } break; case UE_BULK: if (udev->speed != USB_SPEED_LOW) { pipe->methods = &ehci_device_bulk_methods; } break; default: /* do nothing */ break; } } done: return; } static void ehci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) { /* * Wait until the hardware has finished any possible use of * the transfer descriptor(s) and QH */ *pus = (188); /* microseconds */ } struct usb2_bus_methods ehci_bus_methods = { .pipe_init = ehci_pipe_init, .xfer_setup = ehci_xfer_setup, .xfer_unsetup = ehci_xfer_unsetup, .do_poll = ehci_do_poll, .get_dma_delay = ehci_get_dma_delay, }; Index: projects/cambria/sys/dev/usb2/controller/ehci2_pci.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/ehci2_pci.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/ehci2_pci.c (revision 186460) @@ -1,497 +1,498 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include __FBSDID("$FreeBSD$"); /* * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller. * * The EHCI 1.0 spec can be found at * http://developer.intel.com/technology/usb/download/ehci-r10.pdf * and the USB 2.0 spec at * http://www.usb.org/developers/docs/usb_20.zip */ /* The low level controller code for EHCI has been split into * PCI probes and EHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_EHCI_VENDORID_ACERLABS 0x10b9 #define PCI_EHCI_VENDORID_AMD 0x1022 #define PCI_EHCI_VENDORID_APPLE 0x106b #define PCI_EHCI_VENDORID_ATI 0x1002 #define PCI_EHCI_VENDORID_CMDTECH 0x1095 #define PCI_EHCI_VENDORID_INTEL 0x8086 #define PCI_EHCI_VENDORID_NEC 0x1033 #define PCI_EHCI_VENDORID_OPTI 0x1045 #define PCI_EHCI_VENDORID_PHILIPS 0x1131 #define PCI_EHCI_VENDORID_SIS 0x1039 #define PCI_EHCI_VENDORID_NVIDIA 0x12D2 #define PCI_EHCI_VENDORID_NVIDIA2 0x10DE #define PCI_EHCI_VENDORID_VIA 0x1106 #define PCI_EHCI_BASE_REG 0x10 static void ehci_pci_takecontroller(device_t self); static device_probe_t ehci_pci_probe; static device_attach_t ehci_pci_attach; static device_detach_t ehci_pci_detach; static device_suspend_t ehci_pci_suspend; static device_resume_t ehci_pci_resume; static device_shutdown_t ehci_pci_shutdown; static int ehci_pci_suspend(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_suspend(self); if (err) return (err); ehci_suspend(sc); return (0); } static int ehci_pci_resume(device_t self) { ehci_softc_t *sc = device_get_softc(self); ehci_pci_takecontroller(self); ehci_resume(sc); bus_generic_resume(self); return (0); } static int ehci_pci_shutdown(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_shutdown(self); if (err) return (err); ehci_shutdown(sc); return (0); } static const char * ehci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x268c8086: return ("Intel 63XXESB USB 2.0 controller"); case 0x523910b9: return "ALi M5239 USB 2.0 controller"; case 0x10227463: return "AMD 8111 USB 2.0 controller"; case 0x20951022: return ("AMD CS5536 (Geode) USB 2.0 controller"); case 0x43451002: return "ATI SB200 USB 2.0 controller"; case 0x43731002: return "ATI SB400 USB 2.0 controller"; case 0x25ad8086: return "Intel 6300ESB USB 2.0 controller"; case 0x24cd8086: return "Intel 82801DB/L/M (ICH4) USB 2.0 controller"; case 0x24dd8086: return "Intel 82801EB/R (ICH5) USB 2.0 controller"; case 0x265c8086: return "Intel 82801FB (ICH6) USB 2.0 controller"; case 0x27cc8086: return "Intel 82801GB/R (ICH7) USB 2.0 controller"; case 0x28368086: return "Intel 82801H (ICH8) USB 2.0 controller USB2-A"; case 0x283a8086: return "Intel 82801H (ICH8) USB 2.0 controller USB2-B"; case 0x293a8086: return "Intel 82801I (ICH9) USB 2.0 controller"; case 0x293c8086: return "Intel 82801I (ICH9) USB 2.0 controller"; case 0x00e01033: return ("NEC uPD 720100 USB 2.0 controller"); case 0x006810de: return "NVIDIA nForce2 USB 2.0 controller"; case 0x008810de: return "NVIDIA nForce2 Ultra 400 USB 2.0 controller"; case 0x00d810de: return "NVIDIA nForce3 USB 2.0 controller"; case 0x00e810de: return "NVIDIA nForce3 250 USB 2.0 controller"; case 0x005b10de: return "NVIDIA nForce4 USB 2.0 controller"; case 0x15621131: return "Philips ISP156x USB 2.0 controller"; case 0x31041106: return ("VIA VT6202 USB 2.0 controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) { return ("EHCI (generic) USB 2.0 controller"); } return (NULL); /* dunno */ } static int ehci_pci_probe(device_t self) { const char *desc = ehci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static int ehci_pci_attach(device_t self) { ehci_softc_t *sc = device_get_softc(self); int err; int rid; if (sc == NULL) { device_printf(self, "Could not allocate sc\n"); return (ENXIO); } /* get all DMA memory */ + sc->sc_bus.parent = self; if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { return ENOMEM; } sc->sc_dev = self; pci_enable_busmaster(self); switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) { case PCI_USB_REV_PRE_1_0: case PCI_USB_REV_1_0: case PCI_USB_REV_1_1: /* * NOTE: some EHCI USB controllers have the wrong USB * revision number. It appears those controllers are * fully compliant so we just ignore this value in * some common cases. */ device_printf(self, "pre-2.0 USB revision (ignored)\n"); /* fallthrough */ case PCI_USB_REV_2_0: sc->sc_bus.usbrev = USB_REV_2_0; break; default: sc->sc_bus.usbrev = USB_REV_UNKNOWN; break; } rid = PCI_CBMEM; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); /* * ehci_pci_match will never return NULL if ehci_pci_probe * succeeded */ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_EHCI_VENDORID_ACERLABS: sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_EHCI_VENDORID_AMD: sprintf(sc->sc_vendor, "AMD"); break; case PCI_EHCI_VENDORID_APPLE: sprintf(sc->sc_vendor, "Apple"); break; case PCI_EHCI_VENDORID_ATI: sprintf(sc->sc_vendor, "ATI"); break; case PCI_EHCI_VENDORID_CMDTECH: sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_EHCI_VENDORID_INTEL: sprintf(sc->sc_vendor, "Intel"); break; case PCI_EHCI_VENDORID_NEC: sprintf(sc->sc_vendor, "NEC"); break; case PCI_EHCI_VENDORID_OPTI: sprintf(sc->sc_vendor, "OPTi"); break; case PCI_EHCI_VENDORID_PHILIPS: sprintf(sc->sc_vendor, "Philips"); break; case PCI_EHCI_VENDORID_SIS: sprintf(sc->sc_vendor, "SiS"); break; case PCI_EHCI_VENDORID_NVIDIA: case PCI_EHCI_VENDORID_NVIDIA2: sprintf(sc->sc_vendor, "nVidia"); break; case PCI_EHCI_VENDORID_VIA: sprintf(sc->sc_vendor, "VIA"); break; default: if (bootverbose) device_printf(self, "(New EHCI DeviceId=0x%08x)\n", pci_get_devid(self)); sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } err = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(self, "could not setup config thread!\n"); goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } ehci_pci_takecontroller(self); err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed err=%d\n", err); goto error; } return (0); error: ehci_pci_detach(self); return (ENXIO); } static int ehci_pci_detach(device_t self) { ehci_softc_t *sc = device_get_softc(self); device_t bdev; usb2_config_td_drain(&sc->sc_config_td); if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(self); pci_disable_busmaster(self); /* * disable interrupts that might have been switched on in ehci_init */ if (sc->sc_io_res) { EWRITE4(sc, EHCI_USBINTR, 0); } if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ehci_detach() after ehci_init() */ ehci_detach(sc); int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->sc_io_res); sc->sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_config_td); usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); return (0); } static void ehci_pci_takecontroller(device_t self) { ehci_softc_t *sc = device_get_softc(self); uint32_t cparams; uint32_t eec; uint16_t to; uint8_t eecp; uint8_t bios_sem; cparams = EREAD4(sc, EHCI_HCCPARAMS); /* Synchronise with the BIOS if it owns the controller. */ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0; eecp = EHCI_EECP_NEXT(eec)) { eec = pci_read_config(self, eecp, 4); if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) { continue; } bios_sem = pci_read_config(self, eecp + EHCI_LEGSUP_BIOS_SEM, 1); if (bios_sem == 0) { continue; } device_printf(sc->sc_bus.bdev, "waiting for BIOS " "to give up control\n"); pci_write_config(self, eecp + EHCI_LEGSUP_OS_SEM, 1, 1); to = 500; while (1) { bios_sem = pci_read_config(self, eecp + EHCI_LEGSUP_BIOS_SEM, 1); if (bios_sem == 0) break; if (--to == 0) { device_printf(sc->sc_bus.bdev, "timed out waiting for BIOS\n"); break; } usb2_pause_mtx(NULL, 10); /* wait 10ms */ } } } static driver_t ehci_driver = { .name = "ehci", .methods = (device_method_t[]){ /* device interface */ DEVMETHOD(device_probe, ehci_pci_probe), DEVMETHOD(device_attach, ehci_pci_attach), DEVMETHOD(device_detach, ehci_pci_detach), DEVMETHOD(device_suspend, ehci_pci_suspend), DEVMETHOD(device_resume, ehci_pci_resume), DEVMETHOD(device_shutdown, ehci_pci_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }, .size = sizeof(struct ehci_softc), }; static devclass_t ehci_devclass; DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0); DRIVER_MODULE(ehci, cardbus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb2_controller, 1, 1, 1); MODULE_DEPEND(ehci, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/controller/musb2_otg.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/musb2_otg.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/musb2_otg.c (revision 186460) @@ -1,2898 +1,2895 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * Thanks to Mentor Graphics for providing a reference driver for this * USB chip at their homepage. */ /* * This file contains the driver for the Mentor Graphics Inventra USB * 2.0 High Speed Dual-Role controller. * * NOTE: The current implementation only supports Device Side Mode! */ #include #include #include #include #define USB_DEBUG_VAR musbotgdebug #define usb2_config_td_cc musbotg_config_copy #define usb2_config_td_softc musbotg_softc #include #include #include #include #include #include #include #include #include #include #include #include #include #define MUSBOTG_INTR_ENDPT 1 #define MUSBOTG_BUS2SC(bus) \ ((struct musbotg_softc *)(((uint8_t *)(bus)) - \ USB_P2U(&(((struct musbotg_softc *)0)->sc_bus)))) #define MUSBOTG_PC2SC(pc) \ MUSBOTG_BUS2SC((pc)->tag_parent->info->bus) #if USB_DEBUG static int musbotgdebug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, musbotg, CTLFLAG_RW, 0, "USB musbotg"); SYSCTL_INT(_hw_usb2_musbotg, OID_AUTO, debug, CTLFLAG_RW, &musbotgdebug, 0, "Debug level"); #endif /* prototypes */ struct usb2_bus_methods musbotg_bus_methods; struct usb2_pipe_methods musbotg_device_bulk_methods; struct usb2_pipe_methods musbotg_device_ctrl_methods; struct usb2_pipe_methods musbotg_device_intr_methods; struct usb2_pipe_methods musbotg_device_isoc_methods; struct usb2_pipe_methods musbotg_root_ctrl_methods; struct usb2_pipe_methods musbotg_root_intr_methods; static musbotg_cmd_t musbotg_setup_rx; static musbotg_cmd_t musbotg_setup_data_rx; static musbotg_cmd_t musbotg_setup_data_tx; static musbotg_cmd_t musbotg_setup_status; static musbotg_cmd_t musbotg_data_rx; static musbotg_cmd_t musbotg_data_tx; static void musbotg_device_done(struct usb2_xfer *, usb2_error_t); static void musbotg_do_poll(struct usb2_bus *); static void musbotg_root_ctrl_poll(struct musbotg_softc *); static void musbotg_standard_done(struct usb2_xfer *); static void musbotg_interrupt_poll(struct musbotg_softc *); static usb2_sw_transfer_func_t musbotg_root_intr_done; static usb2_sw_transfer_func_t musbotg_root_ctrl_done; static usb2_config_td_command_t musbotg_root_ctrl_task; /* * Here is a configuration that the chip supports. */ static const struct usb2_hw_ep_profile musbotg_ep_profile[1] = { [0] = { .max_in_frame_size = 64,/* fixed */ .max_out_frame_size = 64, /* fixed */ .is_simplex = 1, .support_control = 1, } }; static void musbotg_get_hw_ep_profile(struct usb2_device *udev, const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) { struct musbotg_softc *sc; sc = MUSBOTG_BUS2SC(udev->bus); if (ep_addr == 0) { /* control endpoint */ *ppf = musbotg_ep_profile; } else if (ep_addr <= sc->sc_ep_max) { /* other endpoints */ *ppf = sc->sc_hw_ep_profile + ep_addr; } else { *ppf = NULL; } } static void musbotg_clocks_on(struct musbotg_softc *sc) { if (sc->sc_flags.clocks_off && sc->sc_flags.port_powered) { DPRINTFN(4, "\n"); if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } sc->sc_flags.clocks_off = 0; /* XXX enable Transceiver */ } } static void musbotg_clocks_off(struct musbotg_softc *sc) { if (!sc->sc_flags.clocks_off) { DPRINTFN(4, "\n"); /* XXX disable Transceiver */ if (sc->sc_clocks_off) { (sc->sc_clocks_off) (sc->sc_clocks_arg); } sc->sc_flags.clocks_off = 1; } } static void musbotg_pull_common(struct musbotg_softc *sc, uint8_t on) { uint8_t temp; temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); if (on) temp |= MUSB2_MASK_SOFTC; else temp &= ~MUSB2_MASK_SOFTC; MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); } static void musbotg_pull_up(struct musbotg_softc *sc) { /* pullup D+, if possible */ if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) { sc->sc_flags.d_pulled_up = 1; musbotg_pull_common(sc, 1); } } static void musbotg_pull_down(struct musbotg_softc *sc) { /* pulldown D+, if possible */ if (sc->sc_flags.d_pulled_up) { sc->sc_flags.d_pulled_up = 0; musbotg_pull_common(sc, 0); } } static void musbotg_wakeup_peer(struct usb2_xfer *xfer) { struct musbotg_softc *sc = xfer->usb2_sc; uint8_t temp; uint8_t use_polling; if (!(sc->sc_flags.status_suspend)) { return; } use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); temp |= MUSB2_MASK_RESUME; MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); /* wait 8 milliseconds */ if (use_polling) { /* polling */ DELAY(8000); } else { /* Wait for reset to complete. */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 8); } temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); temp &= ~MUSB2_MASK_RESUME; MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); } static void musbotg_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) { DPRINTFN(4, "is_on=%u\n", is_on); } static void musbotg_set_address(struct musbotg_softc *sc, uint8_t addr) { DPRINTFN(4, "addr=%d\n", addr); addr &= 0x7F; MUSB2_WRITE_1(sc, MUSB2_REG_FADDR, addr); } static uint8_t musbotg_setup_rx(struct musbotg_td *td) { struct musbotg_softc *sc; struct usb2_device_request req; uint16_t count; uint8_t csr; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); /* * NOTE: If DATAEND is set we should not call the * callback, hence the status stage is not complete. */ if (csr & MUSB2_MASK_CSR0L_DATAEND) { /* wait for interrupt */ goto not_complete; } if (csr & MUSB2_MASK_CSR0L_SENTSTALL) { /* clear SENTSTALL */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); /* get latest status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); /* update EP0 state */ sc->sc_ep0_busy = 0; } if (csr & MUSB2_MASK_CSR0L_SETUPEND) { /* clear SETUPEND */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSR0L_SETUPEND_CLR); /* get latest status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); /* update EP0 state */ sc->sc_ep0_busy = 0; } if (sc->sc_ep0_busy) { /* abort any ongoing transfer */ if (!td->did_stall) { DPRINTFN(4, "stalling\n"); MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSR0L_SENDSTALL); td->did_stall = 1; } goto not_complete; } if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) { goto not_complete; } /* get the packet byte count */ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); /* verify data length */ if (count != td->remainder) { DPRINTFN(0, "Invalid SETUP packet " "length, %d bytes\n", count); goto not_complete; } if (count != sizeof(req)) { DPRINTFN(0, "Unsupported SETUP packet " "length, %d bytes\n", count); goto not_complete; } /* receive data */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req)); /* copy data into real buffer */ usb2_copy_in(td->pc, 0, &req, sizeof(req)); td->offset = sizeof(req); td->remainder = 0; /* set pending command */ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR; /* we need set stall or dataend after this */ sc->sc_ep0_busy = 1; /* sneak peek the set address */ if ((req.bmRequestType == UT_WRITE_DEVICE) && (req.bRequest == UR_SET_ADDRESS)) { sc->sc_dv_addr = req.wValue[0] & 0x7F; } else { sc->sc_dv_addr = 0xFF; } return (0); /* complete */ not_complete: return (1); /* not complete */ } /* Control endpoint only data handling functions (RX/TX/SYNC) */ static uint8_t musbotg_setup_data_rx(struct musbotg_td *td) { struct usb2_page_search buf_res; struct musbotg_softc *sc; uint16_t count; uint8_t csr; uint8_t got_short; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); /* check if a command is pending */ if (sc->sc_ep0_cmd) { MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); sc->sc_ep0_cmd = 0; } /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); got_short = 0; if (csr & (MUSB2_MASK_CSR0L_SETUPEND | MUSB2_MASK_CSR0L_SENTSTALL)) { if (td->remainder == 0) { /* * We are actually complete and have * received the next SETUP */ DPRINTFN(4, "faking complete\n"); return (0); /* complete */ } /* * USB Host Aborted the transfer. */ td->error = 1; return (0); /* complete */ } if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) { return (1); /* not complete */ } /* get the packet byte count */ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); /* verify the packet byte count */ if (count != td->max_frame_size) { if (count < td->max_frame_size) { /* we have a short packet */ td->short_pkt = 1; got_short = 1; } else { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } while (count > 0) { uint32_t temp; usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* check for unaligned memory address */ if (USB_P2U(buf_res.buffer) & 3) { temp = count & ~3; if (temp) { /* receive data 4 bytes at a time */ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf, temp / 4); } temp = count & 3; if (temp) { /* receive data 1 byte at a time */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), (void *)(&sc->sc_bounce_buf[count / 4]), temp); } usb2_copy_in(td->pc, td->offset, sc->sc_bounce_buf, count); /* update offset and remainder */ td->offset += count; td->remainder -= count; break; } /* check if we can optimise */ if (buf_res.length >= 4) { /* receive data 4 bytes at a time */ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length / 4); temp = buf_res.length & ~3; /* update counters */ count -= temp; td->offset += temp; td->remainder -= temp; continue; } /* receive data */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* check if we are complete */ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR; return (0); } /* else need to receive a zero length packet */ } /* write command - need more data */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSR0L_RXPKTRDY_CLR); return (1); /* not complete */ } static uint8_t musbotg_setup_data_tx(struct musbotg_td *td) { struct usb2_page_search buf_res; struct musbotg_softc *sc; uint16_t count; uint8_t csr; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); /* check if a command is pending */ if (sc->sc_ep0_cmd) { MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); sc->sc_ep0_cmd = 0; } /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); if (csr & (MUSB2_MASK_CSR0L_SETUPEND | MUSB2_MASK_CSR0L_SENTSTALL)) { /* * The current transfer was aborted * by the USB Host */ td->error = 1; return (0); /* complete */ } if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) { return (1); /* not complete */ } count = td->max_frame_size; if (td->remainder < count) { /* we have a short packet */ td->short_pkt = 1; count = td->remainder; } while (count > 0) { uint32_t temp; usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* check for unaligned memory address */ if (USB_P2U(buf_res.buffer) & 3) { usb2_copy_out(td->pc, td->offset, sc->sc_bounce_buf, count); temp = count & ~3; if (temp) { /* transmit data 4 bytes at a time */ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf, temp / 4); } temp = count & 3; if (temp) { /* receive data 1 byte at a time */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), ((void *)&sc->sc_bounce_buf[count / 4]), temp); } /* update offset and remainder */ td->offset += count; td->remainder -= count; break; } /* check if we can optimise */ if (buf_res.length >= 4) { /* transmit data 4 bytes at a time */ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length / 4); temp = buf_res.length & ~3; /* update counters */ count -= temp; td->offset += temp; td->remainder -= temp; continue; } /* transmit data */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) { sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_TXPKTRDY; return (0); /* complete */ } /* else we need to transmit a short packet */ } /* write command */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSR0L_TXPKTRDY); return (1); /* not complete */ } static uint8_t musbotg_setup_status(struct musbotg_td *td) { struct musbotg_softc *sc; uint8_t csr; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); if (sc->sc_ep0_busy) { sc->sc_ep0_busy = 0; sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND; MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd); sc->sc_ep0_cmd = 0; } /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); if (csr & MUSB2_MASK_CSR0L_DATAEND) { /* wait for interrupt */ return (1); /* not complete */ } if (sc->sc_dv_addr != 0xFF) { /* write function address */ musbotg_set_address(sc, sc->sc_dv_addr); } return (0); /* complete */ } static uint8_t musbotg_data_rx(struct musbotg_td *td) { struct usb2_page_search buf_res; struct musbotg_softc *sc; uint16_t count; uint8_t csr; uint8_t to; uint8_t got_short; to = 8; /* don't loop forever! */ got_short = 0; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); /* select endpoint */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no); repeat: /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); /* clear overrun */ if (csr & MUSB2_MASK_CSRL_RXOVERRUN) { /* make sure we don't clear "RXPKTRDY" */ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, MUSB2_MASK_CSRL_RXPKTRDY); } /* check status */ if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) { return (1); /* not complete */ } /* get the packet byte count */ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); DPRINTFN(4, "count=0x%04x\n", count); /* * Check for short or invalid packet: */ if (count != td->max_frame_size) { if (count < td->max_frame_size) { /* we have a short packet */ td->short_pkt = 1; got_short = 1; } else { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } while (count > 0) { uint32_t temp; usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* check for unaligned memory address */ if (USB_P2U(buf_res.buffer) & 3) { temp = count & ~3; if (temp) { /* receive data 4 bytes at a time */ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), sc->sc_bounce_buf, temp / 4); } temp = count & 3; if (temp) { /* receive data 1 byte at a time */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), ((void *)&sc->sc_bounce_buf[count / 4]), temp); } usb2_copy_in(td->pc, td->offset, sc->sc_bounce_buf, count); /* update offset and remainder */ td->offset += count; td->remainder -= count; break; } /* check if we can optimise */ if (buf_res.length >= 4) { /* receive data 4 bytes at a time */ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, buf_res.length / 4); temp = buf_res.length & ~3; /* update counters */ count -= temp; td->offset += temp; td->remainder -= temp; continue; } /* receive data */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* clear status bits */ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); /* check if we are complete */ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ return (0); } /* else need to receive a zero length packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t musbotg_data_tx(struct musbotg_td *td) { struct usb2_page_search buf_res; struct musbotg_softc *sc; uint16_t count; uint8_t csr; uint8_t to; to = 8; /* don't loop forever! */ /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); /* select endpoint */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no); repeat: /* read out FIFO status */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); DPRINTFN(4, "csr=0x%02x\n", csr); if (csr & (MUSB2_MASK_CSRL_TXINCOMP | MUSB2_MASK_CSRL_TXUNDERRUN)) { /* clear status bits */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); } if (csr & MUSB2_MASK_CSRL_TXPKTRDY) { return (1); /* not complete */ } /* check for short packet */ count = td->max_frame_size; if (td->remainder < count) { /* we have a short packet */ td->short_pkt = 1; count = td->remainder; } while (count > 0) { uint32_t temp; usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* check for unaligned memory address */ if (USB_P2U(buf_res.buffer) & 3) { usb2_copy_out(td->pc, td->offset, sc->sc_bounce_buf, count); temp = count & ~3; if (temp) { /* transmit data 4 bytes at a time */ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), sc->sc_bounce_buf, temp / 4); } temp = count & 3; if (temp) { /* receive data 1 byte at a time */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), ((void *)&sc->sc_bounce_buf[count / 4]), temp); } /* update offset and remainder */ td->offset += count; td->remainder -= count; break; } /* check if we can optimise */ if (buf_res.length >= 4) { /* transmit data 4 bytes at a time */ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, buf_res.length / 4); temp = buf_res.length & ~3; /* update counters */ count -= temp; td->offset += temp; td->remainder -= temp; continue; } /* transmit data */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* write command */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSRL_TXPKTRDY); /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) { return (0); /* complete */ } /* else we need to transmit a short packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t musbotg_xfer_do_fifo(struct usb2_xfer *xfer) { struct musbotg_softc *sc; struct musbotg_td *td; DPRINTFN(8, "\n"); td = xfer->td_transfer_cache; while (1) { if ((td->func) (td)) { /* operation in progress */ break; } if (((void *)td) == xfer->td_transfer_last) { goto done; } if (td->error) { goto done; } else if (td->remainder > 0) { /* * We had a short transfer. If there is no alternate * next, stop processing ! */ if (!td->alt_next) { goto done; } } /* * Fetch the next transfer descriptor and transfer * some flags to the next transfer descriptor */ td = td->obj_next; xfer->td_transfer_cache = td; } return (1); /* not complete */ done: sc = xfer->usb2_sc; /* compute all actual lengths */ musbotg_standard_done(xfer); return (0); /* complete */ } static void musbotg_interrupt_poll(struct musbotg_softc *sc) { struct usb2_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (!musbotg_xfer_do_fifo(xfer)) { /* queue has been modified */ goto repeat; } } } static void musbotg_vbus_interrupt(struct usb2_bus *bus, uint8_t is_on) { struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus); DPRINTFN(4, "vbus = %u\n", is_on); USB_BUS_LOCK(&sc->sc_bus); if (is_on) { if (!sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 1; /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &musbotg_root_intr_done); } } else { if (sc->sc_flags.status_vbus) { sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &musbotg_root_intr_done); } } USB_BUS_UNLOCK(&sc->sc_bus); } void musbotg_interrupt(struct musbotg_softc *sc) { uint16_t rx_status; uint16_t tx_status; uint8_t usb_status; uint8_t temp; uint8_t to = 2; USB_BUS_LOCK(&sc->sc_bus); repeat: /* read all interrupt registers */ usb_status = MUSB2_READ_1(sc, MUSB2_REG_INTUSB); /* read all FIFO interrupts */ rx_status = MUSB2_READ_2(sc, MUSB2_REG_INTRX); tx_status = MUSB2_READ_2(sc, MUSB2_REG_INTTX); /* check for any bus state change interrupts */ if (usb_status & (MUSB2_MASK_IRESET | MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP)) { DPRINTFN(4, "real bus interrupt 0x%08x\n", usb_status); if (usb_status & MUSB2_MASK_IRESET) { /* set correct state */ sc->sc_flags.status_bus_reset = 1; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* determine line speed */ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); if (temp & MUSB2_MASK_HSMODE) sc->sc_flags.status_high_speed = 1; else sc->sc_flags.status_high_speed = 0; /* * After reset all interrupts are on and we need to * turn them off! */ temp = MUSB2_MASK_IRESET; /* disable resume interrupt */ temp &= ~MUSB2_MASK_IRESUME; /* enable suspend interrupt */ temp |= MUSB2_MASK_ISUSP; MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); /* disable TX and RX interrupts */ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); } /* * If RXRSM and RXSUSP is set at the same time we interpret * that like RESUME. Resume is set when there is at least 3 * milliseconds of inactivity on the USB BUS. */ if (usb_status & MUSB2_MASK_IRESUME) { if (sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 1; temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE); /* disable resume interrupt */ temp &= ~MUSB2_MASK_IRESUME; /* enable suspend interrupt */ temp |= MUSB2_MASK_ISUSP; MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); } } else if (usb_status & MUSB2_MASK_ISUSP) { if (!sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 1; sc->sc_flags.change_suspend = 1; temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE); /* disable suspend interrupt */ temp &= ~MUSB2_MASK_ISUSP; /* enable resume interrupt */ temp |= MUSB2_MASK_IRESUME; MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); } } /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &musbotg_root_intr_done); } /* check for any endpoint interrupts */ if (rx_status || tx_status) { DPRINTFN(4, "real endpoint interrupt " "rx=0x%04x, tx=0x%04x\n", rx_status, tx_status); } /* poll one time regardless of FIFO status */ musbotg_interrupt_poll(sc); if (--to) goto repeat; USB_BUS_UNLOCK(&sc->sc_bus); } static void musbotg_setup_standard_chain_sub(struct musbotg_std_temp *temp) { struct musbotg_td *td; /* get current Transfer Descriptor */ td = temp->td_next; temp->td = td; /* prepare for next TD */ temp->td_next = td->obj_next; /* fill out the Transfer Descriptor */ td->func = temp->func; td->pc = temp->pc; td->offset = temp->offset; td->remainder = temp->len; td->error = 0; td->did_stall = 0; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; } static void musbotg_setup_standard_chain(struct usb2_xfer *xfer) { struct musbotg_std_temp temp; struct musbotg_softc *sc; struct musbotg_td *td; uint32_t x; uint8_t ep_no; DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->sumlen, usb2_get_speed(xfer->udev)); temp.max_frame_size = xfer->max_frame_size; td = xfer->td_start[0]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; /* setup temp */ temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.offset = 0; sc = xfer->usb2_sc; ep_no = (xfer->endpoint & UE_ADDR); /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.func = &musbotg_setup_rx; temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; musbotg_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } if (x != xfer->nframes) { if (xfer->endpoint & UE_DIR_IN) { if (xfer->flags_int.control_xfr) temp.func = &musbotg_setup_data_tx; else temp.func = &musbotg_data_tx; } else { if (xfer->flags_int.control_xfr) temp.func = &musbotg_setup_data_rx; else temp.func = &musbotg_data_rx; } /* setup "pc" pointer */ temp.pc = xfer->frbuffers + x; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; x++; if (x == xfer->nframes) { temp.setup_alt_next = 0; } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.short_pkt = 0; } else { /* regular data transfer */ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; } musbotg_setup_standard_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += temp.len; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } /* always setup a valid "pc" pointer for status and sync */ temp.pc = xfer->frbuffers + 0; /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current * endpoint direction. */ temp.func = &musbotg_setup_status; temp.len = 0; temp.short_pkt = 0; musbotg_setup_standard_chain_sub(&temp); } /* must have at least one frame! */ td = temp.td; xfer->td_transfer_last = td; } static void musbotg_timeout(void *arg) { struct usb2_xfer *xfer = arg; - struct musbotg_softc *sc = xfer->usb2_sc; DPRINTFN(1, "xfer=%p\n", xfer); - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* transfer is transferred */ musbotg_device_done(xfer, USB_ERR_TIMEOUT); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void musbotg_ep_int_set(struct usb2_xfer *xfer, uint8_t on) { struct musbotg_softc *sc = xfer->usb2_sc; uint16_t temp; uint8_t ep_no = xfer->endpoint & UE_ADDR; /* * Only enable the endpoint interrupt when we are * actually waiting for data, hence we are dealing * with level triggered interrupts ! */ if (ep_no == 0) { temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); if (on) temp |= MUSB2_MASK_EPINT(0); else temp &= ~MUSB2_MASK_EPINT(0); MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); } else { if (USB_GET_DATA_ISREAD(xfer)) { temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE); if (on) temp |= MUSB2_MASK_EPINT(ep_no); else temp &= ~MUSB2_MASK_EPINT(ep_no); MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp); } else { temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); if (on) temp |= MUSB2_MASK_EPINT(ep_no); else temp &= ~MUSB2_MASK_EPINT(ep_no); MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); } } } static void musbotg_start_standard_chain(struct usb2_xfer *xfer) { DPRINTFN(8, "\n"); /* poll one time */ if (musbotg_xfer_do_fifo(xfer)) { musbotg_ep_int_set(xfer, 1); DPRINTFN(14, "enabled interrupts on endpoint\n"); /* put transfer on interrupt queue */ usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usb2_transfer_timeout_ms(xfer, &musbotg_timeout, xfer->timeout); } } } static void musbotg_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct musbotg_softc *sc = xfer->usb2_sc; DPRINTFN(8, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_PRE_DATA) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ musbotg_device_done(xfer, std->err); } goto done; } /* setup buffer */ std->ptr = sc->sc_hub_idata; std->len = sizeof(sc->sc_hub_idata); /* set port bit */ sc->sc_hub_idata[0] = 0x02; /* we only have one port */ done: return; } static usb2_error_t musbotg_standard_done_sub(struct usb2_xfer *xfer) { struct musbotg_td *td; uint32_t len; uint8_t error; DPRINTFN(8, "\n"); td = xfer->td_transfer_cache; do { len = td->remainder; if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract * the remainder from "frlengths[]": */ if (len > xfer->frlengths[xfer->aframes]) { td->error = 1; } else { xfer->frlengths[xfer->aframes] -= len; } } /* Check for transfer error */ if (td->error) { /* the transfer is finished */ error = 1; td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { td = td->obj_next; } else { td = NULL; } } else { /* the transfer is finished */ td = NULL; } error = 0; break; } td = td->obj_next; /* this USB frame is complete */ error = 0; break; } while (0); /* update transfer cache */ xfer->td_transfer_cache = td; return (error ? USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); } static void musbotg_standard_done(struct usb2_xfer *xfer) { usb2_error_t err = 0; DPRINTFN(12, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = musbotg_standard_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = musbotg_standard_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = musbotg_standard_done_sub(xfer); } done: musbotg_device_done(xfer, err); } /*------------------------------------------------------------------------* * musbotg_device_done * * NOTE: this function can be called more than one time on the * same USB transfer! *------------------------------------------------------------------------*/ static void musbotg_device_done(struct usb2_xfer *xfer, usb2_error_t error) { USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error); if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { musbotg_ep_int_set(xfer, 0); DPRINTFN(14, "disabled interrupts on endpoint\n"); } /* dequeue transfer and start next transfer */ usb2_transfer_done(xfer, error); } static void musbotg_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, struct usb2_pipe *pipe) { struct musbotg_softc *sc; uint8_t ep_no; USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); DPRINTFN(4, "pipe=%p\n", pipe); if (xfer) { /* cancel any ongoing transfers */ musbotg_device_done(xfer, USB_ERR_STALLED); } /* set FORCESTALL */ sc = MUSBOTG_BUS2SC(udev->bus); ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR); /* select endpoint */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no); if (pipe->edesc->bEndpointAddress & UE_DIR_IN) { MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSRL_TXSENDSTALL); } else { MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, MUSB2_MASK_CSRL_RXSENDSTALL); } } static void musbotg_clear_stall_sub(struct musbotg_softc *sc, uint16_t wMaxPacket, uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) { uint16_t mps; uint16_t temp; uint8_t csr; if (ep_type == UE_CONTROL) { /* clearing stall is not needed */ return; } /* select endpoint */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no); /* compute max frame size */ mps = wMaxPacket & 0x7FF; switch ((wMaxPacket >> 11) & 3) { case 1: mps *= 2; break; case 2: mps *= 3; break; default: break; } if (ep_dir == UE_DIR_IN) { temp = 0; /* Configure endpoint */ switch (ep_type) { case UE_INTERRUPT: MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket); MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, MUSB2_MASK_CSRH_TXMODE | temp); break; case UE_ISOCHRONOUS: MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket); MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, MUSB2_MASK_CSRH_TXMODE | MUSB2_MASK_CSRH_TXISO | temp); break; case UE_BULK: MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket); MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, MUSB2_MASK_CSRH_TXMODE | temp); break; default: break; } /* Need to flush twice in case of double bufring */ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) { MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSRL_TXFFLUSH); csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) { MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSRL_TXFFLUSH); csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); } } /* reset data toggle */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSRL_TXDT_CLR); MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); /* set double/single buffering */ temp = MUSB2_READ_2(sc, MUSB2_REG_TXDBDIS); if (mps <= (sc->sc_hw_ep_profile[ep_no]. max_in_frame_size / 2)) { /* double buffer */ temp &= ~(1 << ep_no); } else { /* single buffer */ temp |= (1 << ep_no); } MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, temp); /* clear sent stall */ if (csr & MUSB2_MASK_CSRL_TXSENTSTALL) { MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); } } else { temp = 0; /* Configure endpoint */ switch (ep_type) { case UE_INTERRUPT: MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket); MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, MUSB2_MASK_CSRH_RXNYET | temp); break; case UE_ISOCHRONOUS: MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket); MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, MUSB2_MASK_CSRH_RXNYET | MUSB2_MASK_CSRH_RXISO | temp); break; case UE_BULK: MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket); MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, temp); break; default: break; } /* Need to flush twice in case of double bufring */ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); if (csr & MUSB2_MASK_CSRL_RXPKTRDY) { MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, MUSB2_MASK_CSRL_RXFFLUSH); csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); if (csr & MUSB2_MASK_CSRL_RXPKTRDY) { MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, MUSB2_MASK_CSRL_RXFFLUSH); csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); } } /* reset data toggle */ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, MUSB2_MASK_CSRL_RXDT_CLR); MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); /* set double/single buffering */ temp = MUSB2_READ_2(sc, MUSB2_REG_RXDBDIS); if (mps <= (sc->sc_hw_ep_profile[ep_no]. max_out_frame_size / 2)) { /* double buffer */ temp &= ~(1 << ep_no); } else { /* single buffer */ temp |= (1 << ep_no); } MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, temp); /* clear sent stall */ if (csr & MUSB2_MASK_CSRL_RXSENTSTALL) { MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); } } } static void musbotg_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) { struct musbotg_softc *sc; struct usb2_endpoint_descriptor *ed; DPRINTFN(4, "pipe=%p\n", pipe); USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check mode */ if (udev->flags.usb2_mode != USB_MODE_DEVICE) { /* not supported */ return; } /* get softc */ sc = MUSBOTG_BUS2SC(udev->bus); /* get endpoint descriptor */ ed = pipe->edesc; /* reset endpoint */ musbotg_clear_stall_sub(sc, UGETW(ed->wMaxPacketSize), (ed->bEndpointAddress & UE_ADDR), (ed->bmAttributes & UE_XFERTYPE), (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); } usb2_error_t musbotg_init(struct musbotg_softc *sc) { struct usb2_hw_ep_profile *pf; uint8_t nrx; uint8_t ntx; uint8_t temp; uint8_t fsize; uint8_t frx; uint8_t ftx; DPRINTFN(1, "start\n"); /* set up the bus structure */ sc->sc_bus.usbrev = USB_REV_2_0; sc->sc_bus.methods = &musbotg_bus_methods; USB_BUS_LOCK(&sc->sc_bus); /* turn on clocks */ if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } /* wait a little for things to stabilise */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); /* disable all interrupts */ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); /* disable pullup */ musbotg_pull_common(sc, 0); /* wait a little bit (10ms) */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 10); /* disable double packet buffering */ MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, 0xFFFF); MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, 0xFFFF); /* enable HighSpeed and ISO Update flags */ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD); /* clear Session bit, if set */ temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL); temp &= ~MUSB2_MASK_SESS; MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp); DPRINTF("DEVCTL=0x%02x\n", temp); /* disable testmode */ MUSB2_WRITE_1(sc, MUSB2_REG_TESTMODE, 0); /* set default value */ MUSB2_WRITE_1(sc, MUSB2_REG_MISC, 0); /* select endpoint index 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); /* read out number of endpoints */ nrx = (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) / 16); ntx = (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) % 16); /* these numbers exclude the control endpoint */ DPRINTFN(2, "RX/TX endpoints: %u/%u\n", nrx, ntx); sc->sc_ep_max = (nrx > ntx) ? nrx : ntx; if (sc->sc_ep_max == 0) { DPRINTFN(2, "ERROR: Looks like the clocks are off!\n"); } /* read out configuration data */ sc->sc_conf_data = MUSB2_READ_1(sc, MUSB2_REG_CONFDATA); DPRINTFN(2, "Config Data: 0x%02x\n", sc->sc_conf_data); DPRINTFN(2, "HW version: 0x%04x\n", MUSB2_READ_1(sc, MUSB2_REG_HWVERS)); /* initialise endpoint profiles */ for (temp = 1; temp <= sc->sc_ep_max; temp++) { pf = sc->sc_hw_ep_profile + temp; /* select endpoint */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, temp); fsize = MUSB2_READ_1(sc, MUSB2_REG_FSIZE); frx = (fsize & MUSB2_MASK_RX_FSIZE) / 16;; ftx = (fsize & MUSB2_MASK_TX_FSIZE); DPRINTF("Endpoint %u FIFO size: IN=%u, OUT=%u\n", temp, pf->max_in_frame_size, pf->max_out_frame_size); if (frx && ftx && (temp <= nrx) && (temp <= ntx)) { pf->max_in_frame_size = 1 << ftx; pf->max_out_frame_size = 1 << frx; pf->is_simplex = 0; /* duplex */ pf->support_multi_buffer = 1; pf->support_bulk = 1; pf->support_interrupt = 1; pf->support_isochronous = 1; pf->support_in = 1; pf->support_out = 1; } else if (frx && (temp <= nrx)) { pf->max_out_frame_size = 1 << frx; pf->is_simplex = 1; /* simplex */ pf->support_multi_buffer = 1; pf->support_bulk = 1; pf->support_interrupt = 1; pf->support_isochronous = 1; pf->support_out = 1; } else if (ftx && (temp <= ntx)) { pf->max_in_frame_size = 1 << ftx; pf->is_simplex = 1; /* simplex */ pf->support_multi_buffer = 1; pf->support_bulk = 1; pf->support_interrupt = 1; pf->support_isochronous = 1; pf->support_in = 1; } } /* turn on default interrupts */ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, MUSB2_MASK_IRESET); musbotg_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ musbotg_do_poll(&sc->sc_bus); return (0); /* success */ } void musbotg_uninit(struct musbotg_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); /* disable all interrupts */ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); sc->sc_flags.port_powered = 0; sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; musbotg_pull_down(sc); musbotg_clocks_off(sc); USB_BUS_UNLOCK(&sc->sc_bus); } void musbotg_suspend(struct musbotg_softc *sc) { return; } void musbotg_resume(struct musbotg_softc *sc) { return; } static void musbotg_do_poll(struct usb2_bus *bus) { struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); musbotg_interrupt_poll(sc); musbotg_root_ctrl_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * musbotg bulk support *------------------------------------------------------------------------*/ static void musbotg_device_bulk_open(struct usb2_xfer *xfer) { return; } static void musbotg_device_bulk_close(struct usb2_xfer *xfer) { musbotg_device_done(xfer, USB_ERR_CANCELLED); } static void musbotg_device_bulk_enter(struct usb2_xfer *xfer) { return; } static void musbotg_device_bulk_start(struct usb2_xfer *xfer) { /* setup TDs */ musbotg_setup_standard_chain(xfer); musbotg_start_standard_chain(xfer); } struct usb2_pipe_methods musbotg_device_bulk_methods = { .open = musbotg_device_bulk_open, .close = musbotg_device_bulk_close, .enter = musbotg_device_bulk_enter, .start = musbotg_device_bulk_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * musbotg control support *------------------------------------------------------------------------*/ static void musbotg_device_ctrl_open(struct usb2_xfer *xfer) { return; } static void musbotg_device_ctrl_close(struct usb2_xfer *xfer) { musbotg_device_done(xfer, USB_ERR_CANCELLED); } static void musbotg_device_ctrl_enter(struct usb2_xfer *xfer) { return; } static void musbotg_device_ctrl_start(struct usb2_xfer *xfer) { /* setup TDs */ musbotg_setup_standard_chain(xfer); musbotg_start_standard_chain(xfer); } struct usb2_pipe_methods musbotg_device_ctrl_methods = { .open = musbotg_device_ctrl_open, .close = musbotg_device_ctrl_close, .enter = musbotg_device_ctrl_enter, .start = musbotg_device_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * musbotg interrupt support *------------------------------------------------------------------------*/ static void musbotg_device_intr_open(struct usb2_xfer *xfer) { return; } static void musbotg_device_intr_close(struct usb2_xfer *xfer) { musbotg_device_done(xfer, USB_ERR_CANCELLED); } static void musbotg_device_intr_enter(struct usb2_xfer *xfer) { return; } static void musbotg_device_intr_start(struct usb2_xfer *xfer) { /* setup TDs */ musbotg_setup_standard_chain(xfer); musbotg_start_standard_chain(xfer); } struct usb2_pipe_methods musbotg_device_intr_methods = { .open = musbotg_device_intr_open, .close = musbotg_device_intr_close, .enter = musbotg_device_intr_enter, .start = musbotg_device_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * musbotg full speed isochronous support *------------------------------------------------------------------------*/ static void musbotg_device_isoc_open(struct usb2_xfer *xfer) { return; } static void musbotg_device_isoc_close(struct usb2_xfer *xfer) { musbotg_device_done(xfer, USB_ERR_CANCELLED); } static void musbotg_device_isoc_enter(struct usb2_xfer *xfer) { struct musbotg_softc *sc = xfer->usb2_sc; uint32_t temp; uint32_t nframes; uint32_t fs_frames; DPRINTFN(5, "xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes); /* get the current frame index */ nframes = MUSB2_READ_2(sc, MUSB2_REG_FRAME); /* * check if the frame index is within the window where the frames * will be inserted */ temp = (nframes - xfer->pipe->isoc_next) & MUSB2_MASK_FRAME; if (usb2_get_speed(xfer->udev) == USB_SPEED_HIGH) { fs_frames = (xfer->nframes + 7) / 8; } else { fs_frames = xfer->nframes; } if ((xfer->pipe->is_synced == 0) || (temp < fs_frames)) { /* * If there is data underflow or the pipe queue is * empty we schedule the transfer a few frames ahead * of the current frame position. Else two isochronous * transfers might overlap. */ xfer->pipe->isoc_next = (nframes + 3) & MUSB2_MASK_FRAME; xfer->pipe->is_synced = 1; DPRINTFN(2, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->pipe->isoc_next - nframes) & MUSB2_MASK_FRAME; /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + fs_frames; /* compute frame number for next insertion */ xfer->pipe->isoc_next += fs_frames; /* setup TDs */ musbotg_setup_standard_chain(xfer); } static void musbotg_device_isoc_start(struct usb2_xfer *xfer) { /* start TD chain */ musbotg_start_standard_chain(xfer); } struct usb2_pipe_methods musbotg_device_isoc_methods = { .open = musbotg_device_isoc_open, .close = musbotg_device_isoc_close, .enter = musbotg_device_isoc_enter, .start = musbotg_device_isoc_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * musbotg root control support *------------------------------------------------------------------------* * simulate a hardware HUB by handling * all the necessary requests *------------------------------------------------------------------------*/ static void musbotg_root_ctrl_open(struct usb2_xfer *xfer) { return; } static void musbotg_root_ctrl_close(struct usb2_xfer *xfer) { struct musbotg_softc *sc = xfer->usb2_sc; if (sc->sc_root_ctrl.xfer == xfer) { sc->sc_root_ctrl.xfer = NULL; } musbotg_device_done(xfer, USB_ERR_CANCELLED); } /* * USB descriptors for the virtual Root HUB: */ static const struct usb2_device_descriptor musbotg_devd = { .bLength = sizeof(struct usb2_device_descriptor), .bDescriptorType = UDESC_DEVICE, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_HSHUBSTT, .bMaxPacketSize = 64, .bcdDevice = {0x00, 0x01}, .iManufacturer = 1, .iProduct = 2, .bNumConfigurations = 1, }; static const struct usb2_device_qualifier musbotg_odevd = { .bLength = sizeof(struct usb2_device_qualifier), .bDescriptorType = UDESC_DEVICE_QUALIFIER, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_FSHUB, .bMaxPacketSize0 = 0, .bNumConfigurations = 0, }; static const struct musbotg_config_desc musbotg_confd = { .confd = { .bLength = sizeof(struct usb2_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(musbotg_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, }, .ifcd = { .bLength = sizeof(struct usb2_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_HSHUBSTT, }, .endpd = { .bLength = sizeof(struct usb2_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = (UE_DIR_IN | MUSBOTG_INTR_ENDPT), .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, .bInterval = 255, }, }; static const struct usb2_hub_descriptor_min musbotg_hubd = { .bDescLength = sizeof(musbotg_hubd), .bDescriptorType = UDESC_HUB, .bNbrPorts = 1, .wHubCharacteristics[0] = (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF, .wHubCharacteristics[1] = (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16, .bPwrOn2PwrGood = 50, .bHubContrCurrent = 0, .DeviceRemovable = {0}, /* port is removable */ }; #define STRING_LANG \ 0x09, 0x04, /* American English */ #define STRING_VENDOR \ 'M', 0, 'e', 0, 'n', 0, 't', 0, 'o', 0, 'r', 0, ' ', 0, \ 'G', 0, 'r', 0, 'a', 0, 'p', 0, 'h', 0, 'i', 0, 'c', 0, 's', 0 #define STRING_PRODUCT \ 'O', 0, 'T', 0, 'G', 0, ' ', 0, 'R', 0, \ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \ 'U', 0, 'B', 0, USB_MAKE_STRING_DESC(STRING_LANG, musbotg_langtab); USB_MAKE_STRING_DESC(STRING_VENDOR, musbotg_vendor); USB_MAKE_STRING_DESC(STRING_PRODUCT, musbotg_product); static void musbotg_root_ctrl_enter(struct usb2_xfer *xfer) { return; } static void musbotg_root_ctrl_start(struct usb2_xfer *xfer) { struct musbotg_softc *sc = xfer->usb2_sc; sc->sc_root_ctrl.xfer = xfer; usb2_config_td_queue_command( &sc->sc_config_td, NULL, &musbotg_root_ctrl_task, 0, 0); } static void musbotg_root_ctrl_task(struct musbotg_softc *sc, struct musbotg_config_copy *cc, uint16_t refcount) { musbotg_root_ctrl_poll(sc); } static void musbotg_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct musbotg_softc *sc = xfer->usb2_sc; uint16_t value; uint16_t index; uint8_t use_polling; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_SETUP) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ musbotg_device_done(xfer, std->err); } goto done; } /* buffer reset */ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0); std->len = 0; value = UGETW(std->req.wValue); index = UGETW(std->req.wIndex); use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; /* demultiplex the control request */ switch (std->req.bmRequestType) { case UT_READ_DEVICE: switch (std->req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (std->req.bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_DESCRIPTOR: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: switch (UGETW(std->req.wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (UGETW(std->req.wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; case UR_SYNCH_FRAME: goto tr_valid; /* nop */ default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (std->req.bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; case UT_WRITE_INTERFACE: switch (std->req.bRequest) { case UR_SET_INTERFACE: goto tr_handle_set_interface; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (std->req.bRequest) { case UR_GET_INTERFACE: goto tr_handle_get_interface; case UR_GET_STATUS: goto tr_handle_get_iface_status; default: goto tr_stalled; } break; case UT_WRITE_CLASS_INTERFACE: case UT_WRITE_VENDOR_INTERFACE: /* XXX forward */ break; case UT_READ_CLASS_INTERFACE: case UT_READ_VENDOR_INTERFACE: /* XXX forward */ break; case UT_WRITE_CLASS_DEVICE: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: goto tr_valid; case UR_SET_DESCRIPTOR: case UR_SET_FEATURE: break; default: goto tr_stalled; } break; case UT_WRITE_CLASS_OTHER: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: goto tr_handle_clear_port_feature; case UR_SET_FEATURE: goto tr_handle_set_port_feature; case UR_CLEAR_TT_BUFFER: case UR_RESET_TT: case UR_STOP_TT: goto tr_valid; default: goto tr_stalled; } break; case UT_READ_CLASS_OTHER: switch (std->req.bRequest) { case UR_GET_TT_STATE: goto tr_handle_get_tt_state; case UR_GET_STATUS: goto tr_handle_get_port_status; default: goto tr_stalled; } break; case UT_READ_CLASS_DEVICE: switch (std->req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; case UR_GET_STATUS: goto tr_handle_get_class_status; default: goto tr_stalled; } break; default: goto tr_stalled; } goto tr_valid; tr_handle_get_descriptor: switch (value >> 8) { case UDESC_DEVICE: if (value & 0xff) { goto tr_stalled; } std->len = sizeof(musbotg_devd); std->ptr = USB_ADD_BYTES(&musbotg_devd, 0); goto tr_valid; case UDESC_CONFIG: if (value & 0xff) { goto tr_stalled; } std->len = sizeof(musbotg_confd); std->ptr = USB_ADD_BYTES(&musbotg_confd, 0); goto tr_valid; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ std->len = sizeof(musbotg_langtab); std->ptr = USB_ADD_BYTES(&musbotg_langtab, 0); goto tr_valid; case 1: /* Vendor */ std->len = sizeof(musbotg_vendor); std->ptr = USB_ADD_BYTES(&musbotg_vendor, 0); goto tr_valid; case 2: /* Product */ std->len = sizeof(musbotg_product); std->ptr = USB_ADD_BYTES(&musbotg_product, 0); goto tr_valid; default: break; } break; default: goto tr_stalled; } goto tr_stalled; tr_handle_get_config: std->len = 1; sc->sc_hub_temp.wValue[0] = sc->sc_conf; goto tr_valid; tr_handle_get_status: std->len = 2; USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); goto tr_valid; tr_handle_set_address: if (value & 0xFF00) { goto tr_stalled; } sc->sc_rt_addr = value; goto tr_valid; tr_handle_set_config: if (value >= 2) { goto tr_stalled; } sc->sc_conf = value; goto tr_valid; tr_handle_get_interface: std->len = 1; sc->sc_hub_temp.wValue[0] = 0; goto tr_valid; tr_handle_get_tt_state: tr_handle_get_class_status: tr_handle_get_iface_status: tr_handle_get_ep_status: std->len = 2; USETW(sc->sc_hub_temp.wValue, 0); goto tr_valid; tr_handle_set_halt: tr_handle_set_interface: tr_handle_set_wakeup: tr_handle_clear_wakeup: tr_handle_clear_halt: goto tr_valid; tr_handle_clear_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(8, "UR_CLEAR_PORT_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_SUSPEND: musbotg_wakeup_peer(xfer); break; case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 0; break; case UHF_PORT_TEST: case UHF_PORT_INDICATOR: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 0; musbotg_pull_down(sc); musbotg_clocks_off(sc); break; case UHF_C_PORT_CONNECTION: sc->sc_flags.change_connect = 0; break; case UHF_C_PORT_SUSPEND: sc->sc_flags.change_suspend = 0; break; default: std->err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_set_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(8, "UR_SET_PORT_FEATURE\n"); switch (value) { case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 1; break; case UHF_PORT_SUSPEND: case UHF_PORT_RESET: case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 1; break; default: std->err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_get_port_status: DPRINTFN(8, "UR_GET_PORT_STATUS\n"); if (index != 1) { goto tr_stalled; } if (sc->sc_flags.status_vbus) { musbotg_clocks_on(sc); musbotg_pull_up(sc); } else { musbotg_pull_down(sc); musbotg_clocks_off(sc); } /* Select Device Side Mode */ value = UPS_PORT_MODE_DEVICE; if (sc->sc_flags.status_high_speed) { value |= UPS_HIGH_SPEED; } if (sc->sc_flags.port_powered) { value |= UPS_PORT_POWER; } if (sc->sc_flags.port_enabled) { value |= UPS_PORT_ENABLED; } if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { value |= UPS_CURRENT_CONNECT_STATUS; } if (sc->sc_flags.status_suspend) { value |= UPS_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortStatus, value); value = 0; if (sc->sc_flags.change_connect) { value |= UPS_C_CONNECT_STATUS; if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { /* reset EP0 state */ sc->sc_ep0_busy = 0; sc->sc_ep0_cmd = 0; } } if (sc->sc_flags.change_suspend) { value |= UPS_C_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortChange, value); std->len = sizeof(sc->sc_hub_temp.ps); goto tr_valid; tr_handle_get_class_descriptor: if (value & 0xFF) { goto tr_stalled; } std->ptr = USB_ADD_BYTES(&musbotg_hubd, 0); std->len = sizeof(musbotg_hubd); goto tr_valid; tr_stalled: std->err = USB_ERR_STALLED; tr_valid: done: return; } static void musbotg_root_ctrl_poll(struct musbotg_softc *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &musbotg_root_ctrl_done); } struct usb2_pipe_methods musbotg_root_ctrl_methods = { .open = musbotg_root_ctrl_open, .close = musbotg_root_ctrl_close, .enter = musbotg_root_ctrl_enter, .start = musbotg_root_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 0, }; /*------------------------------------------------------------------------* * musbotg root interrupt support *------------------------------------------------------------------------*/ static void musbotg_root_intr_open(struct usb2_xfer *xfer) { return; } static void musbotg_root_intr_close(struct usb2_xfer *xfer) { struct musbotg_softc *sc = xfer->usb2_sc; if (sc->sc_root_intr.xfer == xfer) { sc->sc_root_intr.xfer = NULL; } musbotg_device_done(xfer, USB_ERR_CANCELLED); } static void musbotg_root_intr_enter(struct usb2_xfer *xfer) { return; } static void musbotg_root_intr_start(struct usb2_xfer *xfer) { struct musbotg_softc *sc = xfer->usb2_sc; sc->sc_root_intr.xfer = xfer; } struct usb2_pipe_methods musbotg_root_intr_methods = { .open = musbotg_root_intr_open, .close = musbotg_root_intr_close, .enter = musbotg_root_intr_enter, .start = musbotg_root_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; static void musbotg_xfer_setup(struct usb2_setup_params *parm) { const struct usb2_hw_ep_profile *pf; struct musbotg_softc *sc; struct usb2_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; uint8_t ep_no; sc = MUSBOTG_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * setup xfer */ xfer->usb2_sc = sc; /* * NOTE: This driver does not use any of the parameters that * are computed from the following values. Just set some * reasonable dummies: */ parm->hc_max_packet_size = 0x400; parm->hc_max_frame_size = 0x400; if ((parm->methods == &musbotg_device_isoc_methods) || (parm->methods == &musbotg_device_intr_methods)) parm->hc_max_packet_count = 3; else parm->hc_max_packet_count = 1; usb2_transfer_setup_sub(parm); /* * compute maximum number of TDs */ if (parm->methods == &musbotg_device_ctrl_methods) { ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ; } else if (parm->methods == &musbotg_device_bulk_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &musbotg_device_intr_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &musbotg_device_isoc_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else { ntd = 0; } /* * check if "usb2_transfer_setup_sub" set an error */ if (parm->err) { return; } /* * allocate transfer descriptors */ last_obj = NULL; /* * get profile stuff */ if (ntd) { ep_no = xfer->endpoint & UE_ADDR; musbotg_get_hw_ep_profile(parm->udev, &pf, ep_no); if (pf == NULL) { /* should not happen */ parm->err = USB_ERR_INVAL; return; } } else { ep_no = 0; pf = NULL; } /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); for (n = 0; n != ntd; n++) { struct musbotg_td *td; if (parm->buf) { td = USB_ADD_BYTES(parm->buf, parm->size[0]); /* init TD */ td->max_frame_size = xfer->max_frame_size; td->ep_no = ep_no; td->obj_next = last_obj; last_obj = td; } parm->size[0] += sizeof(*td); } xfer->td_start[0] = last_obj; } static void musbotg_xfer_unsetup(struct usb2_xfer *xfer) { return; } static void musbotg_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) { struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus); DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, udev->flags.usb2_mode, sc->sc_rt_addr); if (udev->device_index == sc->sc_rt_addr) { if (udev->flags.usb2_mode != USB_MODE_HOST) { /* not supported */ return; } switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &musbotg_root_ctrl_methods; break; case UE_DIR_IN | MUSBOTG_INTR_ENDPT: pipe->methods = &musbotg_root_intr_methods; break; default: /* do nothing */ break; } } else { if (udev->flags.usb2_mode != USB_MODE_DEVICE) { /* not supported */ return; } if ((udev->speed != USB_SPEED_FULL) && (udev->speed != USB_SPEED_HIGH)) { /* not supported */ return; } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &musbotg_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &musbotg_device_intr_methods; break; case UE_ISOCHRONOUS: pipe->methods = &musbotg_device_isoc_methods; break; case UE_BULK: pipe->methods = &musbotg_device_bulk_methods; break; default: /* do nothing */ break; } } } struct usb2_bus_methods musbotg_bus_methods = { .pipe_init = &musbotg_pipe_init, .xfer_setup = &musbotg_xfer_setup, .xfer_unsetup = &musbotg_xfer_unsetup, .do_poll = &musbotg_do_poll, .get_hw_ep_profile = &musbotg_get_hw_ep_profile, .set_stall = &musbotg_set_stall, .clear_stall = &musbotg_clear_stall, .vbus_interrupt = &musbotg_vbus_interrupt, .rem_wakeup_set = &musbotg_rem_wakeup_set, }; Index: projects/cambria/sys/dev/usb2/controller/musb2_otg_atmelarm.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/musb2_otg_atmelarm.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/musb2_otg_atmelarm.c (revision 186460) @@ -1,251 +1,252 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t musbotg_probe; static device_attach_t musbotg_attach; static device_detach_t musbotg_detach; static device_shutdown_t musbotg_shutdown; struct musbotg_super_softc { struct musbotg_softc sc_otg; /* must be first */ }; static void musbotg_vbus_interrupt(struct musbotg_super_softc *sc) { uint8_t vbus_val = 1; /* fake VBUS on - TODO */ /* just forward it */ (sc->sc_otg.sc_bus.methods->vbus_interrupt) (&sc->sc_otg.sc_bus, vbus_val); } static void musbotg_clocks_on(void *arg) { #if 0 struct musbotg_super_softc *sc = arg; #endif } static void musbotg_clocks_off(void *arg) { #if 0 struct musbotg_super_softc *sc = arg; #endif } static int musbotg_probe(device_t dev) { device_set_desc(dev, "MUSB OTG integrated USB controller"); return (0); } static int musbotg_attach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); int err; int rid; if (sc == NULL) { return (ENXIO); } /* setup MUSB OTG USB controller interface softc */ sc->sc_otg.sc_clocks_on = &musbotg_clocks_on; sc->sc_otg.sc_clocks_off = &musbotg_clocks_off; sc->sc_otg.sc_clocks_arg = sc; /* get all DMA memory */ + sc->sc_otg.sc_bus.parent = dev; if (usb2_bus_mem_alloc_all(&sc->sc_otg.sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_otg.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res); rid = 0; sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_otg.sc_irq_res)) { goto error; } sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_otg.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); err = usb2_config_td_setup(&sc->sc_otg.sc_config_td, sc, &sc->sc_otg.sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(dev, "could not setup config thread!\n"); goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); #endif if (err) { sc->sc_otg.sc_intr_hdl = NULL; goto error; } err = musbotg_init(&sc->sc_otg); if (!err) { err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev); } if (err) { goto error; } else { /* poll VBUS one time */ musbotg_vbus_interrupt(sc); } return (0); error: musbotg_detach(dev); return (ENXIO); } static int musbotg_detach(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_otg.sc_bus.bdev) { bdev = sc->sc_otg.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(dev); if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) { /* * only call musbotg_uninit() after musbotg_init() */ musbotg_uninit(&sc->sc_otg); err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res, sc->sc_otg.sc_intr_hdl); sc->sc_otg.sc_intr_hdl = NULL; } /* free IRQ channel, if any */ if (sc->sc_otg.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_otg.sc_irq_res); sc->sc_otg.sc_irq_res = NULL; } /* free memory resource, if any */ if (sc->sc_otg.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_otg.sc_io_res); sc->sc_otg.sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_otg.sc_config_td); usb2_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL); return (0); } static int musbotg_shutdown(device_t dev) { struct musbotg_super_softc *sc = device_get_softc(dev); int err; err = bus_generic_shutdown(dev); if (err) return (err); musbotg_uninit(&sc->sc_otg); return (0); } static device_method_t musbotg_methods[] = { /* Device interface */ DEVMETHOD(device_probe, musbotg_probe), DEVMETHOD(device_attach, musbotg_attach), DEVMETHOD(device_detach, musbotg_detach), DEVMETHOD(device_shutdown, musbotg_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t musbotg_driver = { "musbotg", musbotg_methods, sizeof(struct musbotg_super_softc), }; static devclass_t musbotg_devclass; DRIVER_MODULE(musbotg, atmelarm, musbotg_driver, musbotg_devclass, 0, 0); MODULE_DEPEND(musbotg, usb2_controller, 1, 1, 1); MODULE_DEPEND(musbotg, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/controller/ohci2.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/ohci2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/ohci2.c (revision 186460) @@ -1,2758 +1,2750 @@ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); /* * USB Open Host Controller driver. * * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html * USB spec: http://www.usb.org/developers/docs/usbspec.zip */ #include #include #include #include #define USB_DEBUG_VAR ohcidebug #define usb2_config_td_cc ohci_config_copy #define usb2_config_td_softc ohci_softc #include #include #include #include #include #include #include #include #include #include #include #include #include #define OHCI_BUS2SC(bus) ((ohci_softc_t *)(((uint8_t *)(bus)) - \ USB_P2U(&(((ohci_softc_t *)0)->sc_bus)))) #if USB_DEBUG static int ohcidebug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci"); SYSCTL_INT(_hw_usb2_ohci, OID_AUTO, debug, CTLFLAG_RW, &ohcidebug, 0, "ohci debug level"); static void ohci_dumpregs(ohci_softc_t *); static void ohci_dump_tds(ohci_td_t *); static uint8_t ohci_dump_td(ohci_td_t *); static void ohci_dump_ed(ohci_ed_t *); static uint8_t ohci_dump_itd(ohci_itd_t *); static void ohci_dump_itds(ohci_itd_t *); #endif #define OBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define OWRITE1(sc, r, x) \ do { OBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) #define OWRITE2(sc, r, x) \ do { OBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) #define OWRITE4(sc, r, x) \ do { OBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0) #define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define OHCI_INTR_ENDPT 1 extern struct usb2_bus_methods ohci_bus_methods; extern struct usb2_pipe_methods ohci_device_bulk_methods; extern struct usb2_pipe_methods ohci_device_ctrl_methods; extern struct usb2_pipe_methods ohci_device_intr_methods; extern struct usb2_pipe_methods ohci_device_isoc_methods; extern struct usb2_pipe_methods ohci_root_ctrl_methods; extern struct usb2_pipe_methods ohci_root_intr_methods; static usb2_config_td_command_t ohci_root_ctrl_task; static void ohci_root_ctrl_poll(struct ohci_softc *sc); static void ohci_do_poll(struct usb2_bus *bus); static void ohci_device_done(struct usb2_xfer *xfer, usb2_error_t error); static usb2_sw_transfer_func_t ohci_root_intr_done; static usb2_sw_transfer_func_t ohci_root_ctrl_done; static void ohci_timeout(void *arg); static uint8_t ohci_check_transfer(struct usb2_xfer *xfer); struct ohci_std_temp { struct usb2_page_cache *pc; ohci_td_t *td; ohci_td_t *td_next; uint32_t average; uint32_t td_flags; uint32_t len; uint16_t max_frame_size; uint8_t shortpkt; uint8_t setup_alt_next; uint8_t short_frames_ok; }; static struct ohci_hcca * ohci_get_hcca(ohci_softc_t *sc) { usb2_pc_cpu_invalidate(&sc->sc_hw.hcca_pc); return (sc->sc_hcca_p); } void ohci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) { struct ohci_softc *sc = OHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.hcca_pc, &sc->sc_hw.hcca_pg, sizeof(ohci_hcca_t), OHCI_HCCA_ALIGN); cb(bus, &sc->sc_hw.ctrl_start_pc, &sc->sc_hw.ctrl_start_pg, sizeof(ohci_ed_t), OHCI_ED_ALIGN); cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg, sizeof(ohci_ed_t), OHCI_ED_ALIGN); cb(bus, &sc->sc_hw.isoc_start_pc, &sc->sc_hw.isoc_start_pg, sizeof(ohci_ed_t), OHCI_ED_ALIGN); for (i = 0; i != OHCI_NO_EDS; i++) { cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, sizeof(ohci_ed_t), OHCI_ED_ALIGN); } } static usb2_error_t ohci_controller_init(ohci_softc_t *sc) { struct usb2_page_search buf_res; uint32_t i; uint32_t ctl; uint32_t ival; uint32_t hcr; uint32_t fm; uint32_t per; uint32_t desca; /* Determine in what context we are running. */ ctl = OREAD4(sc, OHCI_CONTROL); if (ctl & OHCI_IR) { /* SMM active, request change */ DPRINTF("SMM active, request owner change\n"); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_OCR); for (i = 0; (i < 100) && (ctl & OHCI_IR); i++) { usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); ctl = OREAD4(sc, OHCI_CONTROL); } if (ctl & OHCI_IR) { device_printf(sc->sc_bus.bdev, "SMM does not respond, resetting\n"); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); goto reset; } } else { DPRINTF("cold started\n"); reset: /* controller was cold started */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_BUS_RESET_DELAY); } /* * This reset should not be necessary according to the OHCI spec, but * without it some controllers do not start. */ DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_BUS_RESET_DELAY); /* we now own the host controller and the bus has been reset */ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL)); OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */ /* nominal time for a reset is 10 us */ for (i = 0; i < 10; i++) { DELAY(10); hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR; if (!hcr) { break; } } if (hcr) { device_printf(sc->sc_bus.bdev, "reset timeout\n"); return (USB_ERR_IOERROR); } #if USB_DEBUG if (ohcidebug > 15) { ohci_dumpregs(sc); } #endif /* The controller is now in SUSPEND state, we have 2ms to finish. */ /* set up HC registers */ usb2_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res); OWRITE4(sc, OHCI_HCCA, buf_res.physaddr); usb2_get_page(&sc->sc_hw.ctrl_start_pc, 0, &buf_res); OWRITE4(sc, OHCI_CONTROL_HEAD_ED, buf_res.physaddr); usb2_get_page(&sc->sc_hw.bulk_start_pc, 0, &buf_res); OWRITE4(sc, OHCI_BULK_HEAD_ED, buf_res.physaddr); /* disable all interrupts and then switch on all desired interrupts */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE); /* switch on desired functional features */ ctl = OREAD4(sc, OHCI_CONTROL); ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR); ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE | OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL; /* And finally start it! */ OWRITE4(sc, OHCI_CONTROL, ctl); /* * The controller is now OPERATIONAL. Set a some final * registers that should be set earlier, but that the * controller ignores when in the SUSPEND state. */ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT; fm |= OHCI_FSMPS(ival) | ival; OWRITE4(sc, OHCI_FM_INTERVAL, fm); per = OHCI_PERIODIC(ival); /* 90% periodic */ OWRITE4(sc, OHCI_PERIODIC_START, per); /* Fiddle the No OverCurrent Protection bit to avoid chip bug. */ desca = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca | OHCI_NOCP); OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, OHCI_ENABLE_POWER_DELAY); OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca); /* * The AMD756 requires a delay before re-reading the register, * otherwise it will occasionally report 0 ports. */ sc->sc_noport = 0; for (i = 0; (i < 10) && (sc->sc_noport == 0); i++) { usb2_pause_mtx(&sc->sc_bus.bus_mtx, OHCI_READ_DESC_DELAY); sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A)); } #if USB_DEBUG if (ohcidebug > 5) { ohci_dumpregs(sc); } #endif return (USB_ERR_NORMAL_COMPLETION); } static struct ohci_ed * ohci_init_ed(struct usb2_page_cache *pc) { struct usb2_page_search buf_res; struct ohci_ed *ed; usb2_get_page(pc, 0, &buf_res); ed = buf_res.buffer; ed->ed_self = htole32(buf_res.physaddr); ed->ed_flags = htole32(OHCI_ED_SKIP); ed->page_cache = pc; return (ed); } usb2_error_t ohci_init(ohci_softc_t *sc) { struct usb2_page_search buf_res; uint16_t i; uint16_t bit; uint16_t x; uint16_t y; USB_BUS_LOCK(&sc->sc_bus); DPRINTF("start\n"); sc->sc_eintrs = OHCI_NORMAL_INTRS; /* * Setup all ED's */ sc->sc_ctrl_p_last = ohci_init_ed(&sc->sc_hw.ctrl_start_pc); sc->sc_bulk_p_last = ohci_init_ed(&sc->sc_hw.bulk_start_pc); sc->sc_isoc_p_last = ohci_init_ed(&sc->sc_hw.isoc_start_pc); for (i = 0; i != OHCI_NO_EDS; i++) { sc->sc_intr_p_last[i] = ohci_init_ed(sc->sc_hw.intr_start_pc + i); } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = OHCI_NO_EDS / 2; while (bit) { x = bit; while (x & bit) { ohci_ed_t *ed_x; ohci_ed_t *ed_y; y = (x ^ bit) | (bit / 2); /* * the next QH has half the poll interval */ ed_x = sc->sc_intr_p_last[x]; ed_y = sc->sc_intr_p_last[y]; ed_x->next = NULL; ed_x->ed_next = ed_y->ed_self; x++; } bit >>= 1; } if (1) { ohci_ed_t *ed_int; ohci_ed_t *ed_isc; ed_int = sc->sc_intr_p_last[0]; ed_isc = sc->sc_isoc_p_last; /* the last (1ms) QH */ ed_int->next = ed_isc; ed_int->ed_next = ed_isc->ed_self; } usb2_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res); sc->sc_hcca_p = buf_res.buffer; /* * Fill HCCA interrupt table. The bit reversal is to get * the tree set up properly to spread the interrupts. */ for (i = 0; i != OHCI_NO_INTRS; i++) { sc->sc_hcca_p->hcca_interrupt_table[i] = sc->sc_intr_p_last[i | (OHCI_NO_EDS / 2)]->ed_self; } /* flush all cache into memory */ usb2_bus_mem_flush_all(&sc->sc_bus, &ohci_iterate_hw_softc); /* set up the bus struct */ sc->sc_bus.methods = &ohci_bus_methods; - usb2_callout_init_mtx(&sc->sc_tmo_rhsc, &sc->sc_bus.bus_mtx, - CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_tmo_rhsc, &sc->sc_bus.bus_mtx, 0); #if USB_DEBUG if (ohcidebug > 15) { for (i = 0; i != OHCI_NO_EDS; i++) { printf("ed#%d ", i); ohci_dump_ed(sc->sc_intr_p_last[i]); } printf("iso "); ohci_dump_ed(sc->sc_isoc_p_last); } #endif sc->sc_bus.usbrev = USB_REV_1_0; if (ohci_controller_init(sc)) { USB_BUS_UNLOCK(&sc->sc_bus); return (USB_ERR_INVAL); } else { USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ ohci_do_poll(&sc->sc_bus); return (USB_ERR_NORMAL_COMPLETION); } } /* * shut down the controller when the system is going down */ void ohci_detach(struct ohci_softc *sc) { USB_BUS_LOCK(&sc->sc_bus); usb2_callout_stop(&sc->sc_tmo_rhsc); OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); /* XXX let stray task complete */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 50); USB_BUS_UNLOCK(&sc->sc_bus); usb2_callout_drain(&sc->sc_tmo_rhsc); } /* NOTE: suspend/resume is called from * interrupt context and cannot sleep! */ void ohci_suspend(ohci_softc_t *sc) { uint32_t ctl; USB_BUS_LOCK(&sc->sc_bus); #if USB_DEBUG DPRINTF("\n"); if (ohcidebug > 2) { ohci_dumpregs(sc); } #endif ctl = OREAD4(sc, OHCI_CONTROL) & ~OHCI_HCFS_MASK; if (sc->sc_control == 0) { /* * Preserve register values, in case that APM BIOS * does not recover them. */ sc->sc_control = ctl; sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE); } ctl |= OHCI_HCFS_SUSPEND; OWRITE4(sc, OHCI_CONTROL, ctl); usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_WAIT); USB_BUS_UNLOCK(&sc->sc_bus); } void ohci_resume(ohci_softc_t *sc) { uint32_t ctl; USB_BUS_LOCK(&sc->sc_bus); #if USB_DEBUG DPRINTF("\n"); if (ohcidebug > 2) { ohci_dumpregs(sc); } #endif /* some broken BIOSes never initialize the Controller chip */ ohci_controller_init(sc); if (sc->sc_intre) { OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_intre & (OHCI_ALL_INTRS | OHCI_MIE)); } if (sc->sc_control) ctl = sc->sc_control; else ctl = OREAD4(sc, OHCI_CONTROL); ctl |= OHCI_HCFS_RESUME; OWRITE4(sc, OHCI_CONTROL, ctl); usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_DELAY); ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL; OWRITE4(sc, OHCI_CONTROL, ctl); usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_RECOVERY); sc->sc_control = sc->sc_intre = 0; USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ ohci_do_poll(&sc->sc_bus); } #if USB_DEBUG static void ohci_dumpregs(ohci_softc_t *sc) { struct ohci_hcca *hcca; DPRINTF("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n", OREAD4(sc, OHCI_REVISION), OREAD4(sc, OHCI_CONTROL), OREAD4(sc, OHCI_COMMAND_STATUS)); DPRINTF(" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n", OREAD4(sc, OHCI_INTERRUPT_STATUS), OREAD4(sc, OHCI_INTERRUPT_ENABLE), OREAD4(sc, OHCI_INTERRUPT_DISABLE)); DPRINTF(" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n", OREAD4(sc, OHCI_HCCA), OREAD4(sc, OHCI_PERIOD_CURRENT_ED), OREAD4(sc, OHCI_CONTROL_HEAD_ED)); DPRINTF(" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n", OREAD4(sc, OHCI_CONTROL_CURRENT_ED), OREAD4(sc, OHCI_BULK_HEAD_ED), OREAD4(sc, OHCI_BULK_CURRENT_ED)); DPRINTF(" done=0x%08x fmival=0x%08x fmrem=0x%08x\n", OREAD4(sc, OHCI_DONE_HEAD), OREAD4(sc, OHCI_FM_INTERVAL), OREAD4(sc, OHCI_FM_REMAINING)); DPRINTF(" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n", OREAD4(sc, OHCI_FM_NUMBER), OREAD4(sc, OHCI_PERIODIC_START), OREAD4(sc, OHCI_LS_THRESHOLD)); DPRINTF(" desca=0x%08x descb=0x%08x stat=0x%08x\n", OREAD4(sc, OHCI_RH_DESCRIPTOR_A), OREAD4(sc, OHCI_RH_DESCRIPTOR_B), OREAD4(sc, OHCI_RH_STATUS)); DPRINTF(" port1=0x%08x port2=0x%08x\n", OREAD4(sc, OHCI_RH_PORT_STATUS(1)), OREAD4(sc, OHCI_RH_PORT_STATUS(2))); hcca = ohci_get_hcca(sc); DPRINTF(" HCCA: frame_number=0x%04x done_head=0x%08x\n", le32toh(hcca->hcca_frame_number), le32toh(hcca->hcca_done_head)); } static void ohci_dump_tds(ohci_td_t *std) { for (; std; std = std->obj_next) { if (ohci_dump_td(std)) { break; } } } static uint8_t ohci_dump_td(ohci_td_t *std) { uint32_t td_flags; uint8_t temp; usb2_pc_cpu_invalidate(std->page_cache); td_flags = le32toh(std->td_flags); temp = (std->td_next == 0); printf("TD(%p) at 0x%08x: %s%s%s%s%s delay=%d ec=%d " "cc=%d\ncbp=0x%08x next=0x%08x be=0x%08x\n", std, le32toh(std->td_self), (td_flags & OHCI_TD_R) ? "-R" : "", (td_flags & OHCI_TD_OUT) ? "-OUT" : "", (td_flags & OHCI_TD_IN) ? "-IN" : "", ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_1) ? "-TOG1" : "", ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_0) ? "-TOG0" : "", OHCI_TD_GET_DI(td_flags), OHCI_TD_GET_EC(td_flags), OHCI_TD_GET_CC(td_flags), le32toh(std->td_cbp), le32toh(std->td_next), le32toh(std->td_be)); return (temp); } static uint8_t ohci_dump_itd(ohci_itd_t *sitd) { uint32_t itd_flags; uint16_t i; uint8_t temp; usb2_pc_cpu_invalidate(sitd->page_cache); itd_flags = le32toh(sitd->itd_flags); temp = (sitd->itd_next == 0); printf("ITD(%p) at 0x%08x: sf=%d di=%d fc=%d cc=%d\n" "bp0=0x%08x next=0x%08x be=0x%08x\n", sitd, le32toh(sitd->itd_self), OHCI_ITD_GET_SF(itd_flags), OHCI_ITD_GET_DI(itd_flags), OHCI_ITD_GET_FC(itd_flags), OHCI_ITD_GET_CC(itd_flags), le32toh(sitd->itd_bp0), le32toh(sitd->itd_next), le32toh(sitd->itd_be)); for (i = 0; i < OHCI_ITD_NOFFSET; i++) { printf("offs[%d]=0x%04x ", i, (uint32_t)le16toh(sitd->itd_offset[i])); } printf("\n"); return (temp); } static void ohci_dump_itds(ohci_itd_t *sitd) { for (; sitd; sitd = sitd->obj_next) { if (ohci_dump_itd(sitd)) { break; } } } static void ohci_dump_ed(ohci_ed_t *sed) { uint32_t ed_flags; uint32_t ed_headp; usb2_pc_cpu_invalidate(sed->page_cache); ed_flags = le32toh(sed->ed_flags); ed_headp = le32toh(sed->ed_headp); printf("ED(%p) at 0x%08x: addr=%d endpt=%d maxp=%d flags=%s%s%s%s%s\n" "tailp=0x%08x headflags=%s%s headp=0x%08x nexted=0x%08x\n", sed, le32toh(sed->ed_self), OHCI_ED_GET_FA(ed_flags), OHCI_ED_GET_EN(ed_flags), OHCI_ED_GET_MAXP(ed_flags), (ed_flags & OHCI_ED_DIR_OUT) ? "-OUT" : "", (ed_flags & OHCI_ED_DIR_IN) ? "-IN" : "", (ed_flags & OHCI_ED_SPEED) ? "-LOWSPEED" : "", (ed_flags & OHCI_ED_SKIP) ? "-SKIP" : "", (ed_flags & OHCI_ED_FORMAT_ISO) ? "-ISO" : "", le32toh(sed->ed_tailp), (ed_headp & OHCI_HALTED) ? "-HALTED" : "", (ed_headp & OHCI_TOGGLECARRY) ? "-CARRY" : "", le32toh(sed->ed_headp), le32toh(sed->ed_next)); } #endif static void ohci_transfer_intr_enqueue(struct usb2_xfer *xfer) { /* check for early completion */ if (ohci_check_transfer(xfer)) { return; } /* put transfer on interrupt queue */ usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usb2_transfer_timeout_ms(xfer, &ohci_timeout, xfer->timeout); } } #define OHCI_APPEND_QH(sed,td_self,last) (last) = _ohci_append_qh(sed,td_self,last) static ohci_ed_t * _ohci_append_qh(ohci_ed_t *sed, uint32_t td_self, ohci_ed_t *last) { DPRINTFN(11, "%p to %p\n", sed, last); /* (sc->sc_bus.bus_mtx) must be locked */ sed->next = last->next; sed->ed_next = last->ed_next; sed->ed_tailp = 0; sed->ed_headp = td_self; sed->prev = last; usb2_pc_cpu_flush(sed->page_cache); /* * the last->next->prev is never followed: sed->next->prev = sed; */ last->next = sed; last->ed_next = sed->ed_self; usb2_pc_cpu_flush(last->page_cache); return (sed); } #define OHCI_REMOVE_QH(sed,last) (last) = _ohci_remove_qh(sed,last) static ohci_ed_t * _ohci_remove_qh(ohci_ed_t *sed, ohci_ed_t *last) { DPRINTFN(11, "%p from %p\n", sed, last); /* (sc->sc_bus.bus_mtx) must be locked */ /* only remove if not removed from a queue */ if (sed->prev) { sed->prev->next = sed->next; sed->prev->ed_next = sed->ed_next; usb2_pc_cpu_flush(sed->prev->page_cache); if (sed->next) { sed->next->prev = sed->prev; usb2_pc_cpu_flush(sed->next->page_cache); } /* * terminate transfer in case the transferred packet was * short so that the ED still points at the last used TD */ sed->ed_flags |= htole32(OHCI_ED_SKIP); sed->ed_headp = sed->ed_tailp; last = ((last == sed) ? sed->prev : last); sed->prev = 0; usb2_pc_cpu_flush(sed->page_cache); } return (last); } static void ohci_isoc_done(struct usb2_xfer *xfer) { uint8_t nframes; uint32_t *plen = xfer->frlengths; volatile uint16_t *olen; uint16_t len = 0; ohci_itd_t *td = xfer->td_transfer_first; while (1) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } #if USB_DEBUG if (ohcidebug > 5) { DPRINTF("isoc TD\n"); ohci_dump_itd(td); } #endif usb2_pc_cpu_invalidate(td->page_cache); nframes = td->frames; olen = &td->itd_offset[0]; if (nframes > 8) { nframes = 8; } while (nframes--) { len = le16toh(*olen); if ((len >> 12) == OHCI_CC_NOT_ACCESSED) { len = 0; } else { len &= ((1 << 12) - 1); } if (len > *plen) { len = 0;/* invalid length */ } *plen = len; plen++; olen++; } if (((void *)td) == xfer->td_transfer_last) { break; } td = td->obj_next; } xfer->aframes = xfer->nframes; ohci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); } #if USB_DEBUG static const char *const ohci_cc_strs[] = { "NO_ERROR", "CRC", "BIT_STUFFING", "DATA_TOGGLE_MISMATCH", "STALL", "DEVICE_NOT_RESPONDING", "PID_CHECK_FAILURE", "UNEXPECTED_PID", "DATA_OVERRUN", "DATA_UNDERRUN", "BUFFER_OVERRUN", "BUFFER_UNDERRUN", "reserved", "reserved", "NOT_ACCESSED", "NOT_ACCESSED" }; #endif static usb2_error_t ohci_non_isoc_done_sub(struct usb2_xfer *xfer) { ohci_td_t *td; ohci_td_t *td_alt_next; uint32_t temp; uint32_t phy_start; uint32_t phy_end; uint32_t td_flags; uint16_t cc; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; td_flags = 0; while (1) { usb2_pc_cpu_invalidate(td->page_cache); phy_start = le32toh(td->td_cbp); td_flags = le32toh(td->td_flags); cc = OHCI_TD_GET_CC(td_flags); if (phy_start) { /* * short transfer - compute the number of remaining * bytes in the hardware buffer: */ phy_end = le32toh(td->td_be); temp = (OHCI_PAGE(phy_start ^ phy_end) ? (OHCI_PAGE_SIZE + 1) : 0x0001); temp += OHCI_PAGE_OFFSET(phy_end); temp -= OHCI_PAGE_OFFSET(phy_start); if (temp > td->len) { /* guard against corruption */ cc = OHCI_CC_STALL; } else if (xfer->aframes != xfer->nframes) { /* * subtract remaining length from * "frlengths[]" */ xfer->frlengths[xfer->aframes] -= temp; } } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } /* Check transfer status */ if (cc) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (phy_start) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; DPRINTFN(16, "error cc=%d (%s)\n", cc, ohci_cc_strs[cc]); return ((cc == 0) ? USB_ERR_NORMAL_COMPLETION : (cc == OHCI_CC_STALL) ? USB_ERR_STALLED : USB_ERR_IOERROR); } static void ohci_non_isoc_done(struct usb2_xfer *xfer) { usb2_error_t err = 0; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); #if USB_DEBUG if (ohcidebug > 10) { ohci_dump_tds(xfer->td_transfer_first); } #endif /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = ohci_non_isoc_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = ohci_non_isoc_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = ohci_non_isoc_done_sub(xfer); } done: ohci_device_done(xfer, err); } /*------------------------------------------------------------------------* * ohci_check_transfer_sub *------------------------------------------------------------------------*/ static void ohci_check_transfer_sub(struct usb2_xfer *xfer) { ohci_td_t *td; ohci_ed_t *ed; uint32_t phy_start; uint32_t td_flags; uint32_t td_next; uint16_t cc; td = xfer->td_transfer_cache; while (1) { usb2_pc_cpu_invalidate(td->page_cache); phy_start = le32toh(td->td_cbp); td_flags = le32toh(td->td_flags); td_next = le32toh(td->td_next); /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { /* the transfer is finished */ td = NULL; break; } /* Check transfer status */ cc = OHCI_TD_GET_CC(td_flags); if (cc) { /* the transfer is finished */ td = NULL; break; } /* * Check if we reached the last packet * or if there is a short packet: */ if (((td_next & (~0xF)) == OHCI_TD_NEXT_END) || phy_start) { /* follow alt next */ td = td->alt_next; break; } td = td->obj_next; } /* update transfer cache */ xfer->td_transfer_cache = td; if (td) { ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; ed->ed_headp = td->td_self; usb2_pc_cpu_flush(ed->page_cache); DPRINTFN(13, "xfer=%p following alt next\n", xfer); } } /*------------------------------------------------------------------------* * ohci_check_transfer * * Return values: * 0: USB transfer is not finished * Else: USB transfer is finished *------------------------------------------------------------------------*/ static uint8_t ohci_check_transfer(struct usb2_xfer *xfer) { ohci_ed_t *ed; uint32_t ed_flags; uint32_t ed_headp; uint32_t ed_tailp; DPRINTFN(13, "xfer=%p checking transfer\n", xfer); ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; usb2_pc_cpu_invalidate(ed->page_cache); ed_flags = le32toh(ed->ed_flags); ed_headp = le32toh(ed->ed_headp); ed_tailp = le32toh(ed->ed_tailp); if ((ed_flags & OHCI_ED_SKIP) || (ed_headp & OHCI_HALTED) || (((ed_headp ^ ed_tailp) & (~0xF)) == 0)) { if (xfer->pipe->methods == &ohci_device_isoc_methods) { /* isochronous transfer */ ohci_isoc_done(xfer); } else { if (xfer->flags_int.short_frames_ok) { ohci_check_transfer_sub(xfer); if (xfer->td_transfer_cache) { /* not finished yet */ return (0); } } /* store data-toggle */ if (ed_headp & OHCI_TOGGLECARRY) { xfer->pipe->toggle_next = 1; } else { xfer->pipe->toggle_next = 0; } /* non-isochronous transfer */ ohci_non_isoc_done(xfer); } return (1); } DPRINTFN(13, "xfer=%p is still active\n", xfer); return (0); } static void ohci_rhsc_enable(ohci_softc_t *sc) { DPRINTFN(5, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); sc->sc_eintrs |= OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC); /* acknowledge any RHSC interrupt */ OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_RHSC); usb2_sw_transfer(&sc->sc_root_intr, &ohci_root_intr_done); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void ohci_interrupt_poll(ohci_softc_t *sc) { struct usb2_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* * check if transfer is transferred */ if (ohci_check_transfer(xfer)) { /* queue has been modified */ goto repeat; } } } /*------------------------------------------------------------------------* * ohci_interrupt - OHCI interrupt handler * * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, * hence the interrupt handler will be setup before "sc->sc_bus.bdev" * is present ! *------------------------------------------------------------------------*/ void ohci_interrupt(ohci_softc_t *sc) { struct ohci_hcca *hcca; uint32_t status; uint32_t done; USB_BUS_LOCK(&sc->sc_bus); hcca = ohci_get_hcca(sc); DPRINTFN(16, "real interrupt\n"); #if USB_DEBUG if (ohcidebug > 15) { ohci_dumpregs(sc); } #endif done = le32toh(hcca->hcca_done_head); /* * The LSb of done is used to inform the HC Driver that an interrupt * condition exists for both the Done list and for another event * recorded in HcInterruptStatus. On an interrupt from the HC, the * HC Driver checks the HccaDoneHead Value. If this value is 0, then * the interrupt was caused by other than the HccaDoneHead update * and the HcInterruptStatus register needs to be accessed to * determine that exact interrupt cause. If HccaDoneHead is nonzero, * then a Done list update interrupt is indicated and if the LSb of * done is nonzero, then an additional interrupt event is indicated * and HcInterruptStatus should be checked to determine its cause. */ if (done != 0) { status = 0; if (done & ~OHCI_DONE_INTRS) { status |= OHCI_WDH; } if (done & OHCI_DONE_INTRS) { status |= OREAD4(sc, OHCI_INTERRUPT_STATUS); } hcca->hcca_done_head = 0; usb2_pc_cpu_flush(&sc->sc_hw.hcca_pc); } else { status = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH; } status &= ~OHCI_MIE; if (status == 0) { /* * nothing to be done (PCI shared * interrupt) */ goto done; } OWRITE4(sc, OHCI_INTERRUPT_STATUS, status); /* Acknowledge */ status &= sc->sc_eintrs; if (status == 0) { goto done; } if (status & (OHCI_SO | OHCI_RD | OHCI_UE | OHCI_RHSC)) { #if 0 if (status & OHCI_SO) { /* XXX do what */ } #endif if (status & OHCI_RD) { printf("%s: resume detect\n", __FUNCTION__); /* XXX process resume detect */ } if (status & OHCI_UE) { printf("%s: unrecoverable error, " "controller halted\n", __FUNCTION__); OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET); /* XXX what else */ } if (status & OHCI_RHSC) { /* * Disable RHSC interrupt for now, because it will be * on until the port has been reset. */ sc->sc_eintrs &= ~OHCI_RHSC; OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC); usb2_sw_transfer(&sc->sc_root_intr, &ohci_root_intr_done); /* do not allow RHSC interrupts > 1 per second */ usb2_callout_reset(&sc->sc_tmo_rhsc, hz, (void *)&ohci_rhsc_enable, sc); } } status &= ~(OHCI_RHSC | OHCI_WDH | OHCI_SO); if (status != 0) { /* Block unprocessed interrupts. XXX */ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, status); sc->sc_eintrs &= ~status; printf("%s: blocking intrs 0x%x\n", __FUNCTION__, status); } /* poll all the USB transfers */ ohci_interrupt_poll(sc); done: USB_BUS_UNLOCK(&sc->sc_bus); } /* * called when a request does not complete */ static void ohci_timeout(void *arg) { struct usb2_xfer *xfer = arg; - ohci_softc_t *sc = xfer->usb2_sc; DPRINTF("xfer=%p\n", xfer); - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* transfer is transferred */ ohci_device_done(xfer, USB_ERR_TIMEOUT); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void ohci_do_poll(struct usb2_bus *bus) { struct ohci_softc *sc = OHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); ohci_interrupt_poll(sc); ohci_root_ctrl_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void ohci_setup_standard_chain_sub(struct ohci_std_temp *temp) { struct usb2_page_search buf_res; ohci_td_t *td; ohci_td_t *td_next; ohci_td_t *td_alt_next; uint32_t buf_offset; uint32_t average; uint32_t len_old; uint8_t shortpkt_old; uint8_t precompute; td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; len_old = temp->len; precompute = 1; /* software is used to detect short incoming transfers */ if ((temp->td_flags & htole32(OHCI_TD_DP_MASK)) == htole32(OHCI_TD_IN)) { temp->td_flags |= htole32(OHCI_TD_R); } else { temp->td_flags &= ~htole32(OHCI_TD_R); } restart: td = temp->td; td_next = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) { break; } /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; average = 0; } else { average = temp->average; if (temp->len < average) { if (temp->len % temp->max_frame_size) { temp->shortpkt = 1; } average = temp->len; } } if (td_next == NULL) { panic("%s: out of OHCI transfer descriptors!", __FUNCTION__); } /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->td_flags = temp->td_flags; /* the next TD uses TOGGLE_CARRY */ temp->td_flags &= ~htole32(OHCI_TD_TOGGLE_MASK); if (average == 0) { td->td_cbp = 0; td->td_be = ~0; td->len = 0; } else { usb2_get_page(temp->pc, buf_offset, &buf_res); td->td_cbp = htole32(buf_res.physaddr); buf_offset += (average - 1); usb2_get_page(temp->pc, buf_offset, &buf_res); td->td_be = htole32(buf_res.physaddr); buf_offset++; td->len = average; /* update remaining length */ temp->len -= average; } if ((td_next == td_alt_next) && temp->setup_alt_next) { /* we need to receive these frames one by one ! */ td->td_flags &= htole32(~OHCI_TD_INTR_MASK); td->td_flags |= htole32(OHCI_TD_SET_DI(1)); td->td_next = htole32(OHCI_TD_NEXT_END); } else { if (td_next) { /* link the current TD with the next one */ td->td_next = td_next->td_self; } } td->alt_next = td_alt_next; usb2_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* setup alt next pointer, if any */ if (temp->short_frames_ok) { if (temp->setup_alt_next) { td_alt_next = td_next; } } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } temp->td = td; temp->td_next = td_next; } static void ohci_setup_standard_chain(struct usb2_xfer *xfer, ohci_ed_t **ed_last) { struct ohci_std_temp temp; struct usb2_pipe_methods *methods; ohci_ed_t *ed; ohci_td_t *td; uint32_t ed_flags; uint32_t x; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->sumlen, usb2_get_speed(xfer->udev)); temp.average = xfer->max_usb2_frame_size; temp.max_frame_size = xfer->max_frame_size; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; temp.td = NULL; temp.td_next = td; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.short_frames_ok = xfer->flags_int.short_frames_ok; methods = xfer->pipe->methods; /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC | OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR); temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; ohci_setup_standard_chain_sub(&temp); /* * XXX assume that the setup message is * contained within one USB packet: */ xfer->pipe->toggle_next = 1; } x = 1; } else { x = 0; } temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR); /* set data toggle */ if (xfer->pipe->toggle_next) { temp.td_flags |= htole32(OHCI_TD_TOGGLE_1); } else { temp.td_flags |= htole32(OHCI_TD_TOGGLE_0); } /* set endpoint direction */ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { temp.td_flags |= htole32(OHCI_TD_IN); } else { temp.td_flags |= htole32(OHCI_TD_OUT); } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.pc = xfer->frbuffers + x; x++; if (x == xfer->nframes) { temp.setup_alt_next = 0; } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; } else { /* regular data transfer */ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; } ohci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * Send a DATA1 message and invert the current endpoint * direction. */ /* set endpoint direction and data toggle */ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { temp.td_flags = htole32(OHCI_TD_OUT | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); } else { temp.td_flags = htole32(OHCI_TD_IN | OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1)); } temp.len = 0; temp.pc = NULL; temp.shortpkt = 0; ohci_setup_standard_chain_sub(&temp); } td = temp.td; td->td_next = htole32(OHCI_TD_NEXT_END); td->td_flags &= ~htole32(OHCI_TD_INTR_MASK); td->td_flags |= htole32(OHCI_TD_SET_DI(1)); usb2_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ xfer->td_transfer_last = td; #if USB_DEBUG if (ohcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next); ohci_dump_tds(xfer->td_transfer_first); } #endif ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; ed_flags = (OHCI_ED_SET_FA(xfer->address) | OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) | OHCI_ED_SET_MAXP(xfer->max_frame_size)); ed_flags |= (OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD); if (xfer->udev->speed == USB_SPEED_LOW) { ed_flags |= OHCI_ED_SPEED; } ed->ed_flags = htole32(ed_flags); usb2_pc_cpu_flush(ed->page_cache); td = xfer->td_transfer_first; OHCI_APPEND_QH(ed, td->td_self, *ed_last); if (methods == &ohci_device_bulk_methods) { ohci_softc_t *sc = xfer->usb2_sc; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); } if (methods == &ohci_device_ctrl_methods) { ohci_softc_t *sc = xfer->usb2_sc; OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); } } static void ohci_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { ohci_softc_t *sc = xfer->usb2_sc; uint32_t hstatus; uint16_t i; uint16_t m; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_PRE_DATA) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ ohci_device_done(xfer, std->err); } goto done; } /* setup buffer */ std->ptr = sc->sc_hub_idata; std->len = sizeof(sc->sc_hub_idata); /* clear any old interrupt data */ bzero(sc->sc_hub_idata, sizeof(sc->sc_hub_idata)); hstatus = OREAD4(sc, OHCI_RH_STATUS); DPRINTF("sc=%p xfer=%p hstatus=0x%08x\n", sc, xfer, hstatus); /* set bits */ m = (sc->sc_noport + 1); if (m > (8 * sizeof(sc->sc_hub_idata))) { m = (8 * sizeof(sc->sc_hub_idata)); } for (i = 1; i < m; i++) { /* pick out CHANGE bits from the status register */ if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16) { sc->sc_hub_idata[i / 8] |= 1 << (i % 8); DPRINTF("port %d changed\n", i); } } done: return; } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void ohci_device_done(struct usb2_xfer *xfer, usb2_error_t error) { struct usb2_pipe_methods *methods = xfer->pipe->methods; ohci_softc_t *sc = xfer->usb2_sc; ohci_ed_t *ed; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error); ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (ed) { usb2_pc_cpu_invalidate(ed->page_cache); } if (methods == &ohci_device_bulk_methods) { OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last); } if (methods == &ohci_device_ctrl_methods) { OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last); } if (methods == &ohci_device_intr_methods) { OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); } if (methods == &ohci_device_isoc_methods) { OHCI_REMOVE_QH(ed, sc->sc_isoc_p_last); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; /* dequeue transfer and start next transfer */ usb2_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * ohci bulk support *------------------------------------------------------------------------*/ static void ohci_device_bulk_open(struct usb2_xfer *xfer) { return; } static void ohci_device_bulk_close(struct usb2_xfer *xfer) { ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_bulk_enter(struct usb2_xfer *xfer) { return; } static void ohci_device_bulk_start(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_bulk_p_last); /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ohci_device_bulk_methods = { .open = ohci_device_bulk_open, .close = ohci_device_bulk_close, .enter = ohci_device_bulk_enter, .start = ohci_device_bulk_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ohci control support *------------------------------------------------------------------------*/ static void ohci_device_ctrl_open(struct usb2_xfer *xfer) { return; } static void ohci_device_ctrl_close(struct usb2_xfer *xfer) { ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_ctrl_enter(struct usb2_xfer *xfer) { return; } static void ohci_device_ctrl_start(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_ctrl_p_last); /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ohci_device_ctrl_methods = { .open = ohci_device_ctrl_open, .close = ohci_device_ctrl_close, .enter = ohci_device_ctrl_enter, .start = ohci_device_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ohci interrupt support *------------------------------------------------------------------------*/ static void ohci_device_intr_open(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; uint16_t best; uint16_t bit; uint16_t x; best = 0; bit = OHCI_NO_EDS / 2; while (bit) { if (xfer->interval >= bit) { x = bit; best = bit; while (x & bit) { if (sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(3, "best=%d interval=%d\n", best, xfer->interval); } static void ohci_device_intr_close(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; sc->sc_intr_stat[xfer->qh_pos]--; ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_intr_enter(struct usb2_xfer *xfer) { return; } static void ohci_device_intr_start(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; /* setup TD's and QH */ ohci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]); /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ohci_device_intr_methods = { .open = ohci_device_intr_open, .close = ohci_device_intr_close, .enter = ohci_device_intr_enter, .start = ohci_device_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ohci isochronous support *------------------------------------------------------------------------*/ static void ohci_device_isoc_open(struct usb2_xfer *xfer) { return; } static void ohci_device_isoc_close(struct usb2_xfer *xfer) { /**/ ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_device_isoc_enter(struct usb2_xfer *xfer) { struct usb2_page_search buf_res; ohci_softc_t *sc = xfer->usb2_sc; struct ohci_hcca *hcca; uint32_t buf_offset; uint32_t nframes; uint32_t ed_flags; uint32_t *plen; uint16_t itd_offset[OHCI_ITD_NOFFSET]; uint16_t length; uint8_t ncur; ohci_itd_t *td; ohci_itd_t *td_last = NULL; ohci_ed_t *ed; hcca = ohci_get_hcca(sc); nframes = le32toh(hcca->hcca_frame_number); DPRINTFN(6, "xfer=%p isoc_next=%u nframes=%u hcca_fn=%u\n", xfer, xfer->pipe->isoc_next, xfer->nframes, nframes); if ((xfer->pipe->is_synced == 0) || (((nframes - xfer->pipe->isoc_next) & 0xFFFF) < xfer->nframes) || (((xfer->pipe->isoc_next - nframes) & 0xFFFF) >= 128)) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->pipe->isoc_next = (nframes + 3) & 0xFFFF; xfer->pipe->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ buf_offset = ((xfer->pipe->isoc_next - nframes) & 0xFFFF); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = (usb2_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset + xfer->nframes); /* get the real number of frames */ nframes = xfer->nframes; buf_offset = 0; plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; ncur = 0; length = 0; while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } itd_offset[ncur] = length; buf_offset += *plen; length += *plen; plen++; ncur++; if ( /* check if the ITD is full */ (ncur == OHCI_ITD_NOFFSET) || /* check if we have put more than 4K into the ITD */ (length & 0xF000) || /* check if it is the last frame */ (nframes == 0)) { /* fill current ITD */ td->itd_flags = htole32( OHCI_ITD_NOCC | OHCI_ITD_SET_SF(xfer->pipe->isoc_next) | OHCI_ITD_NOINTR | OHCI_ITD_SET_FC(ncur)); td->frames = ncur; xfer->pipe->isoc_next += ncur; if (length == 0) { /* all zero */ td->itd_bp0 = 0; td->itd_be = ~0; while (ncur--) { td->itd_offset[ncur] = htole16(OHCI_ITD_MK_OFFS(0)); } } else { usb2_get_page(xfer->frbuffers, buf_offset - length, &buf_res); length = OHCI_PAGE_MASK(buf_res.physaddr); buf_res.physaddr = OHCI_PAGE(buf_res.physaddr); td->itd_bp0 = htole32(buf_res.physaddr); usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res); td->itd_be = htole32(buf_res.physaddr); while (ncur--) { itd_offset[ncur] += length; itd_offset[ncur] = OHCI_ITD_MK_OFFS(itd_offset[ncur]); td->itd_offset[ncur] = htole16(itd_offset[ncur]); } } ncur = 0; length = 0; td_last = td; td = td->obj_next; if (td) { /* link the last TD with the next one */ td_last->itd_next = td->itd_self; } usb2_pc_cpu_flush(td_last->page_cache); } } /* update the last TD */ td_last->itd_flags &= ~htole32(OHCI_ITD_NOINTR); td_last->itd_flags |= htole32(OHCI_ITD_SET_DI(0)); td_last->itd_next = 0; usb2_pc_cpu_flush(td_last->page_cache); xfer->td_transfer_last = td_last; #if USB_DEBUG if (ohcidebug > 8) { DPRINTF("data before transfer:\n"); ohci_dump_itds(xfer->td_transfer_first); } #endif ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ed_flags = (OHCI_ED_DIR_IN | OHCI_ED_FORMAT_ISO); else ed_flags = (OHCI_ED_DIR_OUT | OHCI_ED_FORMAT_ISO); ed_flags |= (OHCI_ED_SET_FA(xfer->address) | OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) | OHCI_ED_SET_MAXP(xfer->max_frame_size)); if (xfer->udev->speed == USB_SPEED_LOW) { ed_flags |= OHCI_ED_SPEED; } ed->ed_flags = htole32(ed_flags); usb2_pc_cpu_flush(ed->page_cache); td = xfer->td_transfer_first; OHCI_APPEND_QH(ed, td->itd_self, sc->sc_isoc_p_last); } static void ohci_device_isoc_start(struct usb2_xfer *xfer) { /* put transfer on interrupt queue */ ohci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods ohci_device_isoc_methods = { .open = ohci_device_isoc_open, .close = ohci_device_isoc_close, .enter = ohci_device_isoc_enter, .start = ohci_device_isoc_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * ohci root control support *------------------------------------------------------------------------* * simulate a hardware hub by handling * all the necessary requests *------------------------------------------------------------------------*/ static void ohci_root_ctrl_open(struct usb2_xfer *xfer) { return; } static void ohci_root_ctrl_close(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; if (sc->sc_root_ctrl.xfer == xfer) { sc->sc_root_ctrl.xfer = NULL; } ohci_device_done(xfer, USB_ERR_CANCELLED); } /* data structures and routines * to emulate the root hub: */ static const struct usb2_device_descriptor ohci_devd = { sizeof(struct usb2_device_descriptor), UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0}, {0}, {0x00, 0x01}, /* device id */ 1, 2, 0, /* string indicies */ 1 /* # of configurations */ }; static const struct ohci_config_desc ohci_confd = { .confd = { .bLength = sizeof(struct usb2_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(ohci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, /* max power */ }, .ifcd = { .bLength = sizeof(struct usb2_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_FSHUB, }, .endpd = { .bLength = sizeof(struct usb2_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | OHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 32,/* max packet (255 ports) */ .bInterval = 255, }, }; static const struct usb2_hub_descriptor ohci_hubd = { 0, /* dynamic length */ UDESC_HUB, 0, {0, 0}, 0, 0, {0}, }; static void ohci_root_ctrl_enter(struct usb2_xfer *xfer) { return; } static void ohci_root_ctrl_start(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; sc->sc_root_ctrl.xfer = xfer; usb2_config_td_queue_command (&sc->sc_config_td, NULL, &ohci_root_ctrl_task, 0, 0); } static void ohci_root_ctrl_task(struct ohci_softc *sc, struct ohci_config_copy *cc, uint16_t refcount) { ohci_root_ctrl_poll(sc); } static void ohci_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { ohci_softc_t *sc = xfer->usb2_sc; char *ptr; uint32_t port; uint32_t v; uint16_t value; uint16_t index; uint8_t l; uint8_t use_polling; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_SETUP) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ ohci_device_done(xfer, std->err); } goto done; } /* buffer reset */ std->ptr = sc->sc_hub_desc.temp; std->len = 0; value = UGETW(std->req.wValue); index = UGETW(std->req.wIndex); use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", std->req.bmRequestType, std->req.bRequest, UGETW(std->req.wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(std->req.bRequest, std->req.bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): std->len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(ohci_devd); sc->sc_hub_desc.devd = ohci_devd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(ohci_confd); std->ptr = USB_ADD_BYTES(&ohci_confd, 0); break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ ptr = "\001"; break; case 1: /* Vendor */ ptr = sc->sc_vendor; break; case 2: /* Product */ ptr = "OHCI root HUB"; break; default: ptr = ""; break; } std->len = usb2_make_str_desc (sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), ptr); break; default: std->err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): std->len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): std->len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): std->len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= USB_MAX_DEVICES) { std->err = USB_ERR_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if ((value != 0) && (value != 1)) { std->err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): std->err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(9, "UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value); if ((index < 1) || (index > sc->sc_noport)) { std->err = USB_ERR_IOERROR; goto done; } port = OHCI_RH_PORT_STATUS(index); switch (value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR); break; case UHF_PORT_POWER: /* Yes, writing to the LOW_SPEED bit clears power. */ OWRITE4(sc, port, UPS_LOW_SPEED); break; case UHF_C_PORT_CONNECTION: OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16); break; case UHF_C_PORT_ENABLE: OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16); break; case UHF_C_PORT_SUSPEND: OWRITE4(sc, port, UPS_C_SUSPEND << 16); break; case UHF_C_PORT_OVER_CURRENT: OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16); break; case UHF_C_PORT_RESET: OWRITE4(sc, port, UPS_C_PORT_RESET << 16); break; default: std->err = USB_ERR_IOERROR; goto done; } switch (value) { case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* enable RHSC interrupt if condition is cleared. */ - if ((OREAD4(sc, port) >> 16) == 0) { + if ((OREAD4(sc, port) >> 16) == 0) ohci_rhsc_enable(sc); - USB_BUS_LOCK(&sc->sc_bus); - } break; default: break; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A); sc->sc_hub_desc.hubd = ohci_hubd; sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport; USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, (v & OHCI_NPS ? UHD_PWR_NO_SWITCH : v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL) /* XXX overcurrent */ ); sc->sc_hub_desc.hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v); v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B); for (l = 0; l < sc->sc_noport; l++) { if (v & 1) { sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] |= (1 << (l % 8)); } v >>= 1; } sc->sc_hub_desc.hubd.bDescLength = 8 + ((sc->sc_noport + 7) / 8); std->len = sc->sc_hub_desc.hubd.bDescLength; break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): std->len = 16; bzero(sc->sc_hub_desc.temp, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "get port status i=%d\n", index); if ((index < 1) || (index > sc->sc_noport)) { std->err = USB_ERR_IOERROR; goto done; } v = OREAD4(sc, OHCI_RH_PORT_STATUS(index)); DPRINTFN(9, "port status=0x%04x\n", v); USETW(sc->sc_hub_desc.ps.wPortStatus, v); USETW(sc->sc_hub_desc.ps.wPortChange, v >> 16); std->len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): std->err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if ((index < 1) || (index > sc->sc_noport)) { std->err = USB_ERR_IOERROR; goto done; } port = OHCI_RH_PORT_STATUS(index); switch (value) { case UHF_PORT_ENABLE: OWRITE4(sc, port, UPS_PORT_ENABLED); break; case UHF_PORT_SUSPEND: OWRITE4(sc, port, UPS_SUSPEND); break; case UHF_PORT_RESET: DPRINTFN(6, "reset port %d\n", index); OWRITE4(sc, port, UPS_RESET); for (v = 0;; v++) { if (v < 12) { if (use_polling) { /* polling */ DELAY(USB_PORT_ROOT_RESET_DELAY * 1000); } else { usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_PORT_ROOT_RESET_DELAY); } if ((OREAD4(sc, port) & UPS_RESET) == 0) { break; } } else { std->err = USB_ERR_TIMEOUT; goto done; } } DPRINTFN(9, "ohci port %d reset, status = 0x%04x\n", index, OREAD4(sc, port)); break; case UHF_PORT_POWER: DPRINTFN(3, "set port power %d\n", index); OWRITE4(sc, port, UPS_PORT_POWER); break; default: std->err = USB_ERR_IOERROR; goto done; } break; default: std->err = USB_ERR_IOERROR; goto done; } done: return; } static void ohci_root_ctrl_poll(struct ohci_softc *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &ohci_root_ctrl_done); } struct usb2_pipe_methods ohci_root_ctrl_methods = { .open = ohci_root_ctrl_open, .close = ohci_root_ctrl_close, .enter = ohci_root_ctrl_enter, .start = ohci_root_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 0, }; /*------------------------------------------------------------------------* * ohci root interrupt support *------------------------------------------------------------------------*/ static void ohci_root_intr_open(struct usb2_xfer *xfer) { return; } static void ohci_root_intr_close(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; if (sc->sc_root_intr.xfer == xfer) { sc->sc_root_intr.xfer = NULL; } ohci_device_done(xfer, USB_ERR_CANCELLED); } static void ohci_root_intr_enter(struct usb2_xfer *xfer) { return; } static void ohci_root_intr_start(struct usb2_xfer *xfer) { ohci_softc_t *sc = xfer->usb2_sc; sc->sc_root_intr.xfer = xfer; } struct usb2_pipe_methods ohci_root_intr_methods = { .open = ohci_root_intr_open, .close = ohci_root_intr_close, .enter = ohci_root_intr_enter, .start = ohci_root_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; static void ohci_xfer_setup(struct usb2_setup_params *parm) { struct usb2_page_search page_info; struct usb2_page_cache *pc; ohci_softc_t *sc; struct usb2_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t nitd; uint32_t nqh; uint32_t n; sc = OHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * setup xfer */ xfer->usb2_sc = sc; parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = OHCI_PAGE_SIZE; /* * calculate ntd and nqh */ if (parm->methods == &ohci_device_ctrl_methods) { xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nitd = 0; ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_usb2_frame_size)); nqh = 1; } else if (parm->methods == &ohci_device_bulk_methods) { xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nitd = 0; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_usb2_frame_size)); nqh = 1; } else if (parm->methods == &ohci_device_intr_methods) { xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nitd = 0; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_usb2_frame_size)); nqh = 1; } else if (parm->methods == &ohci_device_isoc_methods) { xfer->flags_int.bdma_enable = 1; usb2_transfer_setup_sub(parm); nitd = ((xfer->max_data_length / OHCI_PAGE_SIZE) + ((xfer->nframes + OHCI_ITD_NOFFSET - 1) / OHCI_ITD_NOFFSET) + 1 /* EXTRA */ ); ntd = 0; nqh = 1; } else { usb2_transfer_setup_sub(parm); nitd = 0; ntd = 0; nqh = 0; } alloc_dma_set: if (parm->err) { return; } last_obj = NULL; if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ohci_td_t), OHCI_TD_ALIGN, ntd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != ntd; n++) { ohci_td_t *td; usb2_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ td->td_self = htole32(page_info.physaddr); td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb2_pc_cpu_flush(pc + n); } } if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ohci_itd_t), OHCI_ITD_ALIGN, nitd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nitd; n++) { ohci_itd_t *itd; usb2_get_page(pc + n, 0, &page_info); itd = page_info.buffer; /* init TD */ itd->itd_self = htole32(page_info.physaddr); itd->obj_next = last_obj; itd->page_cache = pc + n; last_obj = itd; usb2_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(ohci_ed_t), OHCI_ED_ALIGN, nqh)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqh; n++) { ohci_ed_t *ed; usb2_get_page(pc + n, 0, &page_info); ed = page_info.buffer; /* init QH */ ed->ed_self = htole32(page_info.physaddr); ed->obj_next = last_obj; ed->page_cache = pc + n; last_obj = ed; usb2_pc_cpu_flush(pc + n); } } xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static void ohci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) { ohci_softc_t *sc = OHCI_BUS2SC(udev->bus); DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, udev->flags.usb2_mode, sc->sc_addr); if (udev->flags.usb2_mode != USB_MODE_HOST) { /* not supported */ return; } if (udev->device_index == sc->sc_addr) { switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &ohci_root_ctrl_methods; break; case UE_DIR_IN | OHCI_INTR_ENDPT: pipe->methods = &ohci_root_intr_methods; break; default: /* do nothing */ break; } } else { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &ohci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &ohci_device_intr_methods; break; case UE_ISOCHRONOUS: if (udev->speed == USB_SPEED_FULL) { pipe->methods = &ohci_device_isoc_methods; } break; case UE_BULK: if (udev->speed != USB_SPEED_LOW) { pipe->methods = &ohci_device_bulk_methods; } break; default: /* do nothing */ break; } } } static void ohci_xfer_unsetup(struct usb2_xfer *xfer) { return; } static void ohci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) { /* * Wait until hardware has finished any possible use of the * transfer descriptor(s) and QH */ *pus = (1125); /* microseconds */ } struct usb2_bus_methods ohci_bus_methods = { .pipe_init = ohci_pipe_init, .xfer_setup = ohci_xfer_setup, .xfer_unsetup = ohci_xfer_unsetup, .do_poll = ohci_do_poll, .get_dma_delay = ohci_get_dma_delay, }; Index: projects/cambria/sys/dev/usb2/controller/ohci2_atmelarm.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/ohci2_atmelarm.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/ohci2_atmelarm.c (revision 186460) @@ -1,232 +1,233 @@ /*- * Copyright (c) 2006 M. Warner Losh. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_RID 0 static device_probe_t ohci_atmelarm_probe; static device_attach_t ohci_atmelarm_attach; static device_detach_t ohci_atmelarm_detach; struct at91_ohci_softc { struct ohci_softc sc_ohci; /* must be first */ struct at91_pmc_clock *iclk; struct at91_pmc_clock *fclk; }; static int ohci_atmelarm_probe(device_t dev) { device_set_desc(dev, "AT91 integrated OHCI controller"); return (BUS_PROBE_DEFAULT); } static int ohci_atmelarm_attach(device_t dev) { struct at91_ohci_softc *sc = device_get_softc(dev); int err; int rid; if (sc == NULL) { return (ENXIO); } /* get all DMA memory */ + sc->sc_ohci.sc_bus.parent = dev; if (usb2_bus_mem_alloc_all(&sc->sc_ohci.sc_bus, USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) { return ENOMEM; } sc->iclk = at91_pmc_clock_ref("ohci_clk"); sc->fclk = at91_pmc_clock_ref("uhpck"); sc->sc_ohci.sc_dev = dev; rid = MEM_RID; sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_io_res)) { err = ENOMEM; goto error; } sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res); rid = 0; sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!(sc->sc_ohci.sc_irq_res)) { goto error; } sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_ohci.sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus); strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor)); err = usb2_config_td_setup(&sc->sc_ohci.sc_config_td, sc, &sc->sc_ohci.sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(dev, "could not setup config thread!\n"); goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl); #endif if (err) { sc->sc_ohci.sc_intr_hdl = NULL; goto error; } /* * turn on the clocks from the AT91's point of view. Keep the unit in reset. */ at91_pmc_clock_enable(sc->iclk); at91_pmc_clock_enable(sc->fclk); bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); err = ohci_init(&sc->sc_ohci); if (!err) { err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev); } if (err) { goto error; } return (0); error: ohci_atmelarm_detach(dev); return (ENXIO); } static int ohci_atmelarm_detach(device_t dev) { struct at91_ohci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_ohci.sc_bus.bdev) { bdev = sc->sc_ohci.sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(dev); /* * Put the controller into reset, then disable clocks and do * the MI tear down. We have to disable the clocks/hardware * after we do the rest of the teardown. We also disable the * clocks in the opposite order we acquire them, but that * doesn't seem to be absolutely necessary. We free up the * clocks after we disable them, so the system could, in * theory, reuse them. */ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0); at91_pmc_clock_disable(sc->fclk); at91_pmc_clock_disable(sc->iclk); at91_pmc_clock_deref(sc->fclk); at91_pmc_clock_deref(sc->iclk); if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(&sc->sc_ohci); err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl); sc->sc_ohci.sc_intr_hdl = NULL; } if (sc->sc_ohci.sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res); sc->sc_ohci.sc_irq_res = NULL; } if (sc->sc_ohci.sc_io_res) { bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID, sc->sc_ohci.sc_io_res); sc->sc_ohci.sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_ohci.sc_config_td); usb2_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc); return (0); } static device_method_t ohci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ohci_atmelarm_probe), DEVMETHOD(device_attach, ohci_atmelarm_attach), DEVMETHOD(device_detach, ohci_atmelarm_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t ohci_driver = { "ohci", ohci_methods, sizeof(struct at91_ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, atmelarm, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb2_controller, 1, 1, 1); MODULE_DEPEND(ohci, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/controller/ohci2_pci.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/ohci2_pci.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/ohci2_pci.c (revision 186460) @@ -1,392 +1,393 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include __FBSDID("$FreeBSD$"); /* * USB Open Host Controller driver. * * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf */ /* The low level controller code for OHCI has been split into * PCI probes and OHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_OHCI_VENDORID_ACERLABS 0x10b9 #define PCI_OHCI_VENDORID_AMD 0x1022 #define PCI_OHCI_VENDORID_APPLE 0x106b #define PCI_OHCI_VENDORID_ATI 0x1002 #define PCI_OHCI_VENDORID_CMDTECH 0x1095 #define PCI_OHCI_VENDORID_NEC 0x1033 #define PCI_OHCI_VENDORID_NVIDIA 0x12D2 #define PCI_OHCI_VENDORID_NVIDIA2 0x10DE #define PCI_OHCI_VENDORID_OPTI 0x1045 #define PCI_OHCI_VENDORID_SIS 0x1039 #define PCI_OHCI_VENDORID_SUN 0x108e #define PCI_OHCI_BASE_REG 0x10 static device_probe_t ohci_pci_probe; static device_attach_t ohci_pci_attach; static device_detach_t ohci_pci_detach; static device_suspend_t ohci_pci_suspend; static device_resume_t ohci_pci_resume; static int ohci_pci_suspend(device_t self) { ohci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_suspend(self); if (err) { return (err); } ohci_suspend(sc); return (0); } static int ohci_pci_resume(device_t self) { ohci_softc_t *sc = device_get_softc(self); uint32_t reg, int_line; if (pci_get_powerstate(self) != PCI_POWERSTATE_D0) { device_printf(self, "chip is in D%d mode " "-- setting to D0\n", pci_get_powerstate(self)); reg = pci_read_config(self, PCI_CBMEM, 4); int_line = pci_read_config(self, PCIR_INTLINE, 4); pci_set_powerstate(self, PCI_POWERSTATE_D0); pci_write_config(self, PCI_CBMEM, reg, 4); pci_write_config(self, PCIR_INTLINE, int_line, 4); } ohci_resume(sc); bus_generic_resume(self); return (0); } static const char * ohci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x523710b9: return ("AcerLabs M5237 (Aladdin-V) USB controller"); case 0x740c1022: return ("AMD-756 USB Controller"); case 0x74141022: return ("AMD-766 USB Controller"); case 0x43741002: return "ATI SB400 USB Controller"; case 0x43751002: return "ATI SB400 USB Controller"; case 0x06701095: return ("CMD Tech 670 (USB0670) USB controller"); case 0x06731095: return ("CMD Tech 673 (USB0673) USB controller"); case 0xc8611045: return ("OPTi 82C861 (FireLink) USB controller"); case 0x00351033: return ("NEC uPD 9210 USB controller"); case 0x00d710de: return ("nVidia nForce3 USB Controller"); case 0x70011039: return ("SiS 5571 USB controller"); case 0x1103108e: return "Sun PCIO-2 USB controller"; case 0x0019106b: return ("Apple KeyLargo USB controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_OHCI)) { return ("OHCI (generic) USB controller"); } return (NULL); } static int ohci_pci_probe(device_t self) { const char *desc = ohci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static int ohci_pci_attach(device_t self) { ohci_softc_t *sc = device_get_softc(self); int rid; int err; if (sc == NULL) { device_printf(self, "Could not allocate sc\n"); return (ENXIO); } /* get all DMA memory */ + sc->sc_bus.parent = self; if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &ohci_iterate_hw_softc)) { return ENOMEM; } sc->sc_dev = self; pci_enable_busmaster(self); rid = PCI_CBMEM; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map memory\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); /* * ohci_pci_match will never return NULL if ohci_pci_probe * succeeded */ device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_OHCI_VENDORID_ACERLABS: sprintf(sc->sc_vendor, "AcerLabs"); break; case PCI_OHCI_VENDORID_AMD: sprintf(sc->sc_vendor, "AMD"); break; case PCI_OHCI_VENDORID_APPLE: sprintf(sc->sc_vendor, "Apple"); break; case PCI_OHCI_VENDORID_ATI: sprintf(sc->sc_vendor, "ATI"); break; case PCI_OHCI_VENDORID_CMDTECH: sprintf(sc->sc_vendor, "CMDTECH"); break; case PCI_OHCI_VENDORID_NEC: sprintf(sc->sc_vendor, "NEC"); break; case PCI_OHCI_VENDORID_NVIDIA: case PCI_OHCI_VENDORID_NVIDIA2: sprintf(sc->sc_vendor, "nVidia"); break; case PCI_OHCI_VENDORID_OPTI: sprintf(sc->sc_vendor, "OPTi"); break; case PCI_OHCI_VENDORID_SIS: sprintf(sc->sc_vendor, "SiS"); break; case PCI_OHCI_VENDORID_SUN: sprintf(sc->sc_vendor, "SUN"); break; default: if (bootverbose) { device_printf(self, "(New OHCI DeviceId=0x%08x)\n", pci_get_devid(self)); } sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } err = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(self, "could not setup config thread!\n"); goto error; } /* sc->sc_bus.usbrev; set by ohci_init() */ #if (__FreeBSD_version >= 700031) err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)(void *)ohci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)(void *)ohci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } err = ohci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed\n"); goto error; } return (0); error: ohci_pci_detach(self); return (ENXIO); } static int ohci_pci_detach(device_t self) { ohci_softc_t *sc = device_get_softc(self); device_t bdev; usb2_config_td_drain(&sc->sc_config_td); if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(self); pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call ohci_detach() after ohci_init() */ ohci_detach(sc); int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) { /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); } sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM, sc->sc_io_res); sc->sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_config_td); usb2_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc); return (0); } static driver_t ohci_driver = { .name = "ohci", .methods = (device_method_t[]){ /* device interface */ DEVMETHOD(device_probe, ohci_pci_probe), DEVMETHOD(device_attach, ohci_pci_attach), DEVMETHOD(device_detach, ohci_pci_detach), DEVMETHOD(device_suspend, ohci_pci_suspend), DEVMETHOD(device_resume, ohci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }, .size = sizeof(struct ohci_softc), }; static devclass_t ohci_devclass; DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0); DRIVER_MODULE(ohci, cardbus, ohci_driver, ohci_devclass, 0, 0); MODULE_DEPEND(ohci, usb2_controller, 1, 1, 1); MODULE_DEPEND(ohci, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/controller/uhci2.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/uhci2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/uhci2.c (revision 186460) @@ -1,3204 +1,3200 @@ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); /* * USB Universal Host Controller driver. * Handles e.g. PIIX3 and PIIX4. * * UHCI spec: http://developer.intel.com/design/USB/UHCI11D.htm * USB spec: http://www.usb.org/developers/docs/usbspec.zip * PIIXn spec: ftp://download.intel.com/design/intarch/datashts/29055002.pdf * ftp://download.intel.com/design/intarch/datashts/29056201.pdf */ #include #include #include #include #define USB_DEBUG_VAR uhcidebug #define usb2_config_td_cc uhci_config_copy #define usb2_config_td_softc uhci_softc #include #include #include #include #include #include #include #include #include #include #include #include #include #define alt_next next #define UHCI_BUS2SC(bus) ((uhci_softc_t *)(((uint8_t *)(bus)) - \ USB_P2U(&(((uhci_softc_t *)0)->sc_bus)))) #if USB_DEBUG static int uhcidebug = 0; static int uhcinoloop = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci"); SYSCTL_INT(_hw_usb2_uhci, OID_AUTO, debug, CTLFLAG_RW, &uhcidebug, 0, "uhci debug level"); SYSCTL_INT(_hw_usb2_uhci, OID_AUTO, loop, CTLFLAG_RW, &uhcinoloop, 0, "uhci noloop"); static void uhci_dumpregs(uhci_softc_t *sc); static void uhci_dump_tds(uhci_td_t *td); #endif #define UBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) #define UWRITE1(sc, r, x) \ do { UBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ } while (/*CONSTCOND*/0) #define UWRITE2(sc, r, x) \ do { UBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ } while (/*CONSTCOND*/0) #define UWRITE4(sc, r, x) \ do { UBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \ } while (/*CONSTCOND*/0) #define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r))) #define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd) #define UHCISTS(sc) UREAD2(sc, UHCI_STS) #define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */ #define UHCI_INTR_ENDPT 1 struct uhci_mem_layout { struct usb2_page_search buf_res; struct usb2_page_search fix_res; struct usb2_page_cache *buf_pc; struct usb2_page_cache *fix_pc; uint32_t buf_offset; uint16_t max_frame_size; }; struct uhci_std_temp { struct uhci_mem_layout ml; uhci_td_t *td; uhci_td_t *td_next; uint32_t average; uint32_t td_status; uint32_t td_token; uint32_t len; uint16_t max_frame_size; uint8_t shortpkt; uint8_t setup_alt_next; uint8_t short_frames_ok; }; extern struct usb2_bus_methods uhci_bus_methods; extern struct usb2_pipe_methods uhci_device_bulk_methods; extern struct usb2_pipe_methods uhci_device_ctrl_methods; extern struct usb2_pipe_methods uhci_device_intr_methods; extern struct usb2_pipe_methods uhci_device_isoc_methods; extern struct usb2_pipe_methods uhci_root_ctrl_methods; extern struct usb2_pipe_methods uhci_root_intr_methods; static usb2_config_td_command_t uhci_root_ctrl_task; static void uhci_root_ctrl_poll(struct uhci_softc *); static void uhci_do_poll(struct usb2_bus *); static void uhci_device_done(struct usb2_xfer *, usb2_error_t); static void uhci_transfer_intr_enqueue(struct usb2_xfer *); static void uhci_root_intr_check(void *); static void uhci_timeout(void *); static uint8_t uhci_check_transfer(struct usb2_xfer *); void uhci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) { struct uhci_softc *sc = UHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, sizeof(uint32_t) * UHCI_FRAMELIST_COUNT, UHCI_FRAMELIST_ALIGN); cb(bus, &sc->sc_hw.ls_ctl_start_pc, &sc->sc_hw.ls_ctl_start_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.fs_ctl_start_pc, &sc->sc_hw.fs_ctl_start_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.last_qh_pc, &sc->sc_hw.last_qh_pg, sizeof(uhci_qh_t), UHCI_QH_ALIGN); cb(bus, &sc->sc_hw.last_td_pc, &sc->sc_hw.last_td_pg, sizeof(uhci_td_t), UHCI_TD_ALIGN); for (i = 0; i != UHCI_VFRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.isoc_start_pc + i, sc->sc_hw.isoc_start_pg + i, sizeof(uhci_td_t), UHCI_TD_ALIGN); } for (i = 0; i != UHCI_IFRAMELIST_COUNT; i++) { cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i, sizeof(uhci_qh_t), UHCI_QH_ALIGN); } } static void uhci_mem_layout_init(struct uhci_mem_layout *ml, struct usb2_xfer *xfer) { ml->buf_pc = xfer->frbuffers + 0; ml->fix_pc = xfer->buf_fixup; ml->buf_offset = 0; ml->max_frame_size = xfer->max_frame_size; } static void uhci_mem_layout_fixup(struct uhci_mem_layout *ml, struct uhci_td *td) { usb2_get_page(ml->buf_pc, ml->buf_offset, &ml->buf_res); if (ml->buf_res.length < td->len) { /* need to do a fixup */ usb2_get_page(ml->fix_pc, 0, &ml->fix_res); td->td_buffer = htole32(ml->fix_res.physaddr); /* * The UHCI driver cannot handle * page crossings, so a fixup is * needed: * * +----+----+ - - - * | YYY|Y | * +----+----+ - - - * \ \ * \ \ * +----+ * |YYYY| (fixup) * +----+ */ if ((td->td_token & htole32(UHCI_TD_PID)) == htole32(UHCI_TD_PID_IN)) { td->fix_pc = ml->fix_pc; usb2_pc_cpu_invalidate(ml->fix_pc); } else { td->fix_pc = NULL; /* copy data to fixup location */ usb2_copy_out(ml->buf_pc, ml->buf_offset, ml->fix_res.buffer, td->len); usb2_pc_cpu_flush(ml->fix_pc); } /* prepare next fixup */ ml->fix_pc++; } else { td->td_buffer = htole32(ml->buf_res.physaddr); td->fix_pc = NULL; } /* prepare next data location */ ml->buf_offset += td->len; } void uhci_reset(uhci_softc_t *sc) { struct usb2_page_search buf_res; uint16_t n; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTF("resetting the HC\n"); /* disable interrupts */ UWRITE2(sc, UHCI_INTR, 0); /* global reset */ UHCICMD(sc, UHCI_CMD_GRESET); /* wait */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_BUS_RESET_DELAY); /* terminate all transfers */ UHCICMD(sc, UHCI_CMD_HCRESET); /* the reset bit goes low when the controller is done */ n = UHCI_RESET_TIMEOUT; while (n--) { /* wait one millisecond */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET)) { goto done_1; } } device_printf(sc->sc_bus.bdev, "controller did not reset\n"); done_1: n = 10; while (n--) { /* wait one millisecond */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); /* check if HC is stopped */ if (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) { goto done_2; } } device_printf(sc->sc_bus.bdev, "controller did not stop\n"); done_2: /* reload the configuration */ usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); UWRITE4(sc, UHCI_FLBASEADDR, buf_res.physaddr); UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum); UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof); } static void uhci_start(uhci_softc_t *sc) { USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "enabling\n"); /* enable interrupts */ UWRITE2(sc, UHCI_INTR, (UHCI_INTR_TOCRCIE | UHCI_INTR_RIE | UHCI_INTR_IOCE | UHCI_INTR_SPIE)); /* * assume 64 byte packets at frame end and start HC controller */ UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); uint8_t n = 10; while (n--) { /* wait one millisecond */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); /* check that controller has started */ if (!(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH)) { goto done; } } device_printf(sc->sc_bus.bdev, "cannot start HC controller\n"); done: return; } static struct uhci_qh * uhci_init_qh(struct usb2_page_cache *pc) { struct usb2_page_search buf_res; struct uhci_qh *qh; usb2_get_page(pc, 0, &buf_res); qh = buf_res.buffer; qh->qh_self = htole32(buf_res.physaddr) | htole32(UHCI_PTR_QH); qh->page_cache = pc; return (qh); } static struct uhci_td * uhci_init_td(struct usb2_page_cache *pc) { struct usb2_page_search buf_res; struct uhci_td *td; usb2_get_page(pc, 0, &buf_res); td = buf_res.buffer; td->td_self = htole32(buf_res.physaddr) | htole32(UHCI_PTR_TD); td->page_cache = pc; return (td); } usb2_error_t uhci_init(uhci_softc_t *sc) { uint16_t bit; uint16_t x; uint16_t y; USB_BUS_LOCK(&sc->sc_bus); DPRINTF("start\n"); #if USB_DEBUG if (uhcidebug > 2) { uhci_dumpregs(sc); } #endif sc->sc_saved_sof = 0x40; /* default value */ sc->sc_saved_frnum = 0; /* default frame number */ /* * Setup QH's */ sc->sc_ls_ctl_p_last = uhci_init_qh(&sc->sc_hw.ls_ctl_start_pc); sc->sc_fs_ctl_p_last = uhci_init_qh(&sc->sc_hw.fs_ctl_start_pc); sc->sc_bulk_p_last = uhci_init_qh(&sc->sc_hw.bulk_start_pc); #if 0 sc->sc_reclaim_qh_p = sc->sc_fs_ctl_p_last; #else /* setup reclaim looping point */ sc->sc_reclaim_qh_p = sc->sc_bulk_p_last; #endif sc->sc_last_qh_p = uhci_init_qh(&sc->sc_hw.last_qh_pc); sc->sc_last_td_p = uhci_init_td(&sc->sc_hw.last_td_pc); for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) { sc->sc_isoc_p_last[x] = uhci_init_td(sc->sc_hw.isoc_start_pc + x); } for (x = 0; x != UHCI_IFRAMELIST_COUNT; x++) { sc->sc_intr_p_last[x] = uhci_init_qh(sc->sc_hw.intr_start_pc + x); } /* * the QHs are arranged to give poll intervals that are * powers of 2 times 1ms */ bit = UHCI_IFRAMELIST_COUNT / 2; while (bit) { x = bit; while (x & bit) { uhci_qh_t *qh_x; uhci_qh_t *qh_y; y = (x ^ bit) | (bit / 2); /* * the next QH has half the poll interval */ qh_x = sc->sc_intr_p_last[x]; qh_y = sc->sc_intr_p_last[y]; qh_x->h_next = NULL; qh_x->qh_h_next = qh_y->qh_self; qh_x->e_next = NULL; qh_x->qh_e_next = htole32(UHCI_PTR_T); x++; } bit >>= 1; } if (1) { uhci_qh_t *qh_ls; uhci_qh_t *qh_intr; qh_ls = sc->sc_ls_ctl_p_last; qh_intr = sc->sc_intr_p_last[0]; /* start QH for interrupt traffic */ qh_intr->h_next = qh_ls; qh_intr->qh_h_next = qh_ls->qh_self; qh_intr->e_next = 0; qh_intr->qh_e_next = htole32(UHCI_PTR_T); } for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) { uhci_td_t *td_x; uhci_qh_t *qh_intr; td_x = sc->sc_isoc_p_last[x]; qh_intr = sc->sc_intr_p_last[x | (UHCI_IFRAMELIST_COUNT / 2)]; /* start TD for isochronous traffic */ td_x->next = NULL; td_x->td_next = qh_intr->qh_self; td_x->td_status = htole32(UHCI_TD_IOS); td_x->td_token = htole32(0); td_x->td_buffer = htole32(0); } if (1) { uhci_qh_t *qh_ls; uhci_qh_t *qh_fs; qh_ls = sc->sc_ls_ctl_p_last; qh_fs = sc->sc_fs_ctl_p_last; /* start QH where low speed control traffic will be queued */ qh_ls->h_next = qh_fs; qh_ls->qh_h_next = qh_fs->qh_self; qh_ls->e_next = 0; qh_ls->qh_e_next = htole32(UHCI_PTR_T); } if (1) { uhci_qh_t *qh_ctl; uhci_qh_t *qh_blk; uhci_qh_t *qh_lst; uhci_td_t *td_lst; qh_ctl = sc->sc_fs_ctl_p_last; qh_blk = sc->sc_bulk_p_last; /* start QH where full speed control traffic will be queued */ qh_ctl->h_next = qh_blk; qh_ctl->qh_h_next = qh_blk->qh_self; qh_ctl->e_next = 0; qh_ctl->qh_e_next = htole32(UHCI_PTR_T); qh_lst = sc->sc_last_qh_p; /* start QH where bulk traffic will be queued */ qh_blk->h_next = qh_lst; qh_blk->qh_h_next = qh_lst->qh_self; qh_blk->e_next = 0; qh_blk->qh_e_next = htole32(UHCI_PTR_T); td_lst = sc->sc_last_td_p; /* end QH which is used for looping the QHs */ qh_lst->h_next = 0; qh_lst->qh_h_next = htole32(UHCI_PTR_T); /* end of QH chain */ qh_lst->e_next = td_lst; qh_lst->qh_e_next = td_lst->td_self; /* * end TD which hangs from the last QH, to avoid a bug in the PIIX * that makes it run berserk otherwise */ td_lst->next = 0; td_lst->td_next = htole32(UHCI_PTR_T); td_lst->td_status = htole32(0); /* inactive */ td_lst->td_token = htole32(0); td_lst->td_buffer = htole32(0); } if (1) { struct usb2_page_search buf_res; uint32_t *pframes; usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); pframes = buf_res.buffer; /* * Setup UHCI framelist * * Execution order: * * pframes -> full speed isochronous -> interrupt QH's -> low * speed control -> full speed control -> bulk transfers * */ for (x = 0; x != UHCI_FRAMELIST_COUNT; x++) { pframes[x] = sc->sc_isoc_p_last[x % UHCI_VFRAMELIST_COUNT]->td_self; } } /* flush all cache into memory */ usb2_bus_mem_flush_all(&sc->sc_bus, &uhci_iterate_hw_softc); /* set up the bus struct */ sc->sc_bus.methods = &uhci_bus_methods; /* reset the controller */ uhci_reset(sc); /* start the controller */ uhci_start(sc); USB_BUS_UNLOCK(&sc->sc_bus); /* catch lost interrupts */ uhci_do_poll(&sc->sc_bus); return (0); } /* NOTE: suspend/resume is called from * interrupt context and cannot sleep! */ void uhci_suspend(uhci_softc_t *sc) { USB_BUS_LOCK(&sc->sc_bus); #if USB_DEBUG if (uhcidebug > 2) { uhci_dumpregs(sc); } #endif /* save some state if BIOS doesn't */ sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM); sc->sc_saved_sof = UREAD1(sc, UHCI_SOF); /* stop the controller */ uhci_reset(sc); /* enter global suspend */ UHCICMD(sc, UHCI_CMD_EGSM); usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_WAIT); USB_BUS_UNLOCK(&sc->sc_bus); } void uhci_resume(uhci_softc_t *sc) { USB_BUS_LOCK(&sc->sc_bus); /* reset the controller */ uhci_reset(sc); /* force global resume */ UHCICMD(sc, UHCI_CMD_FGR); usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_RESUME_DELAY); /* and start traffic again */ uhci_start(sc); #if USB_DEBUG if (uhcidebug > 2) { uhci_dumpregs(sc); } #endif USB_BUS_UNLOCK(&sc->sc_bus); /* catch lost interrupts */ uhci_do_poll(&sc->sc_bus); } #if USB_DEBUG static void uhci_dumpregs(uhci_softc_t *sc) { DPRINTFN(0, "%s regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, " "flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n", device_get_nameunit(sc->sc_bus.bdev), UREAD2(sc, UHCI_CMD), UREAD2(sc, UHCI_STS), UREAD2(sc, UHCI_INTR), UREAD2(sc, UHCI_FRNUM), UREAD4(sc, UHCI_FLBASEADDR), UREAD1(sc, UHCI_SOF), UREAD2(sc, UHCI_PORTSC1), UREAD2(sc, UHCI_PORTSC2)); } static uint8_t uhci_dump_td(uhci_td_t *p) { uint32_t td_next; uint32_t td_status; uint32_t td_token; uint8_t temp; usb2_pc_cpu_invalidate(p->page_cache); td_next = le32toh(p->td_next); td_status = le32toh(p->td_status); td_token = le32toh(p->td_token); /* * Check whether the link pointer in this TD marks the link pointer * as end of queue: */ temp = ((td_next & UHCI_PTR_T) || (td_next == 0)); printf("TD(%p) at 0x%08x = link=0x%08x status=0x%08x " "token=0x%08x buffer=0x%08x\n", p, le32toh(p->td_self), td_next, td_status, td_token, le32toh(p->td_buffer)); printf("TD(%p) td_next=%s%s%s td_status=%s%s%s%s%s%s%s%s%s%s%s, errcnt=%d, actlen=%d pid=%02x," "addr=%d,endpt=%d,D=%d,maxlen=%d\n", p, (td_next & 1) ? "-T" : "", (td_next & 2) ? "-Q" : "", (td_next & 4) ? "-VF" : "", (td_status & UHCI_TD_BITSTUFF) ? "-BITSTUFF" : "", (td_status & UHCI_TD_CRCTO) ? "-CRCTO" : "", (td_status & UHCI_TD_NAK) ? "-NAK" : "", (td_status & UHCI_TD_BABBLE) ? "-BABBLE" : "", (td_status & UHCI_TD_DBUFFER) ? "-DBUFFER" : "", (td_status & UHCI_TD_STALLED) ? "-STALLED" : "", (td_status & UHCI_TD_ACTIVE) ? "-ACTIVE" : "", (td_status & UHCI_TD_IOC) ? "-IOC" : "", (td_status & UHCI_TD_IOS) ? "-IOS" : "", (td_status & UHCI_TD_LS) ? "-LS" : "", (td_status & UHCI_TD_SPD) ? "-SPD" : "", UHCI_TD_GET_ERRCNT(td_status), UHCI_TD_GET_ACTLEN(td_status), UHCI_TD_GET_PID(td_token), UHCI_TD_GET_DEVADDR(td_token), UHCI_TD_GET_ENDPT(td_token), UHCI_TD_GET_DT(td_token), UHCI_TD_GET_MAXLEN(td_token)); return (temp); } static uint8_t uhci_dump_qh(uhci_qh_t *sqh) { uint8_t temp; uint32_t qh_h_next; uint32_t qh_e_next; usb2_pc_cpu_invalidate(sqh->page_cache); qh_h_next = le32toh(sqh->qh_h_next); qh_e_next = le32toh(sqh->qh_e_next); DPRINTFN(0, "QH(%p) at 0x%08x: h_next=0x%08x e_next=0x%08x\n", sqh, le32toh(sqh->qh_self), qh_h_next, qh_e_next); temp = ((((sqh->h_next != NULL) && !(qh_h_next & UHCI_PTR_T)) ? 1 : 0) | (((sqh->e_next != NULL) && !(qh_e_next & UHCI_PTR_T)) ? 2 : 0)); return (temp); } static void uhci_dump_all(uhci_softc_t *sc) { uhci_dumpregs(sc); uhci_dump_qh(sc->sc_ls_ctl_p_last); uhci_dump_qh(sc->sc_fs_ctl_p_last); uhci_dump_qh(sc->sc_bulk_p_last); uhci_dump_qh(sc->sc_last_qh_p); } static void uhci_dump_qhs(uhci_qh_t *sqh) { uint8_t temp; temp = uhci_dump_qh(sqh); /* * uhci_dump_qhs displays all the QHs and TDs from the given QH * onwards Traverses sideways first, then down. * * QH1 QH2 No QH TD2.1 TD2.2 TD1.1 etc. * * TD2.x being the TDs queued at QH2 and QH1 being referenced from QH1. */ if (temp & 1) uhci_dump_qhs(sqh->h_next); else DPRINTF("No QH\n"); if (temp & 2) uhci_dump_tds(sqh->e_next); else DPRINTF("No TD\n"); } static void uhci_dump_tds(uhci_td_t *td) { for (; td != NULL; td = td->obj_next) { if (uhci_dump_td(td)) { break; } } } #endif /* * Let the last QH loop back to the full speed control transfer QH. * This is what intel calls "bandwidth reclamation" and improves * USB performance a lot for some devices. * If we are already looping, just count it. */ static void uhci_add_loop(uhci_softc_t *sc) { struct uhci_qh *qh_lst; struct uhci_qh *qh_rec; #if USB_DEBUG if (uhcinoloop) { return; } #endif if (++(sc->sc_loops) == 1) { DPRINTFN(6, "add\n"); qh_lst = sc->sc_last_qh_p; qh_rec = sc->sc_reclaim_qh_p; /* NOTE: we don't loop back the soft pointer */ qh_lst->qh_h_next = qh_rec->qh_self; usb2_pc_cpu_flush(qh_lst->page_cache); } } static void uhci_rem_loop(uhci_softc_t *sc) { struct uhci_qh *qh_lst; #if USB_DEBUG if (uhcinoloop) { return; } #endif if (--(sc->sc_loops) == 0) { DPRINTFN(6, "remove\n"); qh_lst = sc->sc_last_qh_p; qh_lst->qh_h_next = htole32(UHCI_PTR_T); usb2_pc_cpu_flush(qh_lst->page_cache); } } static void uhci_transfer_intr_enqueue(struct usb2_xfer *xfer) { /* check for early completion */ if (uhci_check_transfer(xfer)) { return; } /* put transfer on interrupt queue */ usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usb2_transfer_timeout_ms(xfer, &uhci_timeout, xfer->timeout); } } #define UHCI_APPEND_TD(std,last) (last) = _uhci_append_td(std,last) static uhci_td_t * _uhci_append_td(uhci_td_t *std, uhci_td_t *last) { DPRINTFN(11, "%p to %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->next = last->next; std->td_next = last->td_next; std->prev = last; usb2_pc_cpu_flush(std->page_cache); /* * the last->next->prev is never followed: std->next->prev = std; */ last->next = std; last->td_next = std->td_self; usb2_pc_cpu_flush(last->page_cache); return (std); } #define UHCI_APPEND_QH(sqh,td,last) (last) = _uhci_append_qh(sqh,td,last) static uhci_qh_t * _uhci_append_qh(uhci_qh_t *sqh, uhci_td_t *td, uhci_qh_t *last) { DPRINTFN(11, "%p to %p\n", sqh, last); /* (sc->sc_bus.mtx) must be locked */ sqh->e_next = td; sqh->qh_e_next = td->td_self; sqh->h_next = last->h_next; sqh->qh_h_next = last->qh_h_next; sqh->h_prev = last; usb2_pc_cpu_flush(sqh->page_cache); /* * The "last->h_next->h_prev" is never followed: * * "sqh->h_next->h_prev" = sqh; */ last->h_next = sqh; last->qh_h_next = sqh->qh_self; usb2_pc_cpu_flush(last->page_cache); return (sqh); } /**/ #define UHCI_REMOVE_TD(std,last) (last) = _uhci_remove_td(std,last) static uhci_td_t * _uhci_remove_td(uhci_td_t *std, uhci_td_t *last) { DPRINTFN(11, "%p from %p\n", std, last); /* (sc->sc_bus.mtx) must be locked */ std->prev->next = std->next; std->prev->td_next = std->td_next; usb2_pc_cpu_flush(std->prev->page_cache); if (std->next) { std->next->prev = std->prev; usb2_pc_cpu_flush(std->next->page_cache); } return ((last == std) ? std->prev : last); } #define UHCI_REMOVE_QH(sqh,last) (last) = _uhci_remove_qh(sqh,last) static uhci_qh_t * _uhci_remove_qh(uhci_qh_t *sqh, uhci_qh_t *last) { DPRINTFN(11, "%p from %p\n", sqh, last); /* (sc->sc_bus.mtx) must be locked */ /* only remove if not removed from a queue */ if (sqh->h_prev) { sqh->h_prev->h_next = sqh->h_next; sqh->h_prev->qh_h_next = sqh->qh_h_next; usb2_pc_cpu_flush(sqh->h_prev->page_cache); if (sqh->h_next) { sqh->h_next->h_prev = sqh->h_prev; usb2_pc_cpu_flush(sqh->h_next->page_cache); } /* * set the Terminate-bit in the e_next of the QH, in case * the transferred packet was short so that the QH still * points at the last used TD */ sqh->qh_e_next = htole32(UHCI_PTR_T); last = ((last == sqh) ? sqh->h_prev : last); sqh->h_prev = 0; usb2_pc_cpu_flush(sqh->page_cache); } return (last); } static void uhci_isoc_done(uhci_softc_t *sc, struct usb2_xfer *xfer) { struct usb2_page_search res; uint32_t nframes = xfer->nframes; uint32_t status; uint32_t offset = 0; uint32_t *plen = xfer->frlengths; uint16_t len = 0; uhci_td_t *td = xfer->td_transfer_first; uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos]; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); /* sync any DMA memory before doing fixups */ usb2_bdma_post_sync(xfer); while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_p_last[0]; } #if USB_DEBUG if (uhcidebug > 5) { DPRINTF("isoc TD\n"); uhci_dump_td(td); } #endif usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); len = UHCI_TD_GET_ACTLEN(status); if (len > *plen) { len = *plen; } if (td->fix_pc) { usb2_get_page(td->fix_pc, 0, &res); /* copy data from fixup location to real location */ usb2_pc_cpu_invalidate(td->fix_pc); usb2_copy_in(xfer->frbuffers, offset, res.buffer, len); } offset += *plen; *plen = len; /* remove TD from schedule */ UHCI_REMOVE_TD(td, *pp_last); pp_last++; plen++; td = td->obj_next; } xfer->aframes = xfer->nframes; } static usb2_error_t uhci_non_isoc_done_sub(struct usb2_xfer *xfer) { struct usb2_page_search res; uhci_td_t *td; uhci_td_t *td_alt_next; uint32_t status; uint32_t token; uint16_t len; td = xfer->td_transfer_cache; td_alt_next = td->alt_next; if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] = 0; } while (1) { usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); token = le32toh(td->td_token); /* * Verify the status and add * up the actual length: */ len = UHCI_TD_GET_ACTLEN(status); if (len > td->len) { /* should not happen */ DPRINTF("Invalid status length, " "0x%04x/0x%04x bytes\n", len, td->len); status |= UHCI_TD_STALLED; } else if ((xfer->aframes != xfer->nframes) && (len > 0)) { if (td->fix_pc) { usb2_get_page(td->fix_pc, 0, &res); /* * copy data from fixup location to real * location */ usb2_pc_cpu_invalidate(td->fix_pc); usb2_copy_in(xfer->frbuffers + xfer->aframes, xfer->frlengths[xfer->aframes], res.buffer, len); } /* update actual length */ xfer->frlengths[xfer->aframes] += len; } /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; break; } if (status & UHCI_TD_STALLED) { /* the transfer is finished */ td = NULL; break; } /* Check for short transfer */ if (len != td->len) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ td = td->alt_next; } else { /* the transfer is finished */ td = NULL; } break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* this USB frame is complete */ break; } } /* update transfer cache */ xfer->td_transfer_cache = td; /* update data toggle */ xfer->pipe->toggle_next = (token & UHCI_TD_SET_DT(1)) ? 0 : 1; #if USB_DEBUG if (status & UHCI_TD_ERROR) { DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x " "status=%s%s%s%s%s%s%s%s%s%s%s\n", xfer->address, xfer->endpoint, xfer->aframes, (status & UHCI_TD_BITSTUFF) ? "[BITSTUFF]" : "", (status & UHCI_TD_CRCTO) ? "[CRCTO]" : "", (status & UHCI_TD_NAK) ? "[NAK]" : "", (status & UHCI_TD_BABBLE) ? "[BABBLE]" : "", (status & UHCI_TD_DBUFFER) ? "[DBUFFER]" : "", (status & UHCI_TD_STALLED) ? "[STALLED]" : "", (status & UHCI_TD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]", (status & UHCI_TD_IOC) ? "[IOC]" : "", (status & UHCI_TD_IOS) ? "[IOS]" : "", (status & UHCI_TD_LS) ? "[LS]" : "", (status & UHCI_TD_SPD) ? "[SPD]" : ""); } #endif return (status & UHCI_TD_STALLED) ? USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION; } static void uhci_non_isoc_done(struct usb2_xfer *xfer) { usb2_error_t err = 0; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); #if USB_DEBUG if (uhcidebug > 10) { uhci_dump_tds(xfer->td_transfer_first); } #endif /* sync any DMA memory before doing fixups */ usb2_bdma_post_sync(xfer); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = uhci_non_isoc_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = uhci_non_isoc_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = uhci_non_isoc_done_sub(xfer); } done: uhci_device_done(xfer, err); } /*------------------------------------------------------------------------* * uhci_check_transfer_sub * * The main purpose of this function is to update the data-toggle * in case it is wrong. *------------------------------------------------------------------------*/ static void uhci_check_transfer_sub(struct usb2_xfer *xfer) { uhci_qh_t *qh; uhci_td_t *td; uhci_td_t *td_alt_next; uint32_t td_token; uint32_t td_self; td = xfer->td_transfer_cache; qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; td_token = td->obj_next->td_token; td = td->alt_next; xfer->td_transfer_cache = td; td_self = td->td_self; td_alt_next = td->alt_next; if ((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1))) { /* * The data toggle is wrong and * we need to switch it ! */ while (1) { td->td_token ^= htole32(UHCI_TD_SET_DT(1)); usb2_pc_cpu_flush(td->page_cache); if (td == xfer->td_transfer_last) { /* last transfer */ break; } td = td->obj_next; if (td->alt_next != td_alt_next) { /* next frame */ break; } } } /* update the QH */ qh->qh_e_next = td_self; usb2_pc_cpu_flush(qh->page_cache); DPRINTFN(13, "xfer=%p following alt next\n", xfer); } /*------------------------------------------------------------------------* * uhci_check_transfer * * Return values: * 0: USB transfer is not finished * Else: USB transfer is finished *------------------------------------------------------------------------*/ static uint8_t uhci_check_transfer(struct usb2_xfer *xfer) { uint32_t status; uint32_t token; uhci_td_t *td; DPRINTFN(16, "xfer=%p checking transfer\n", xfer); if (xfer->pipe->methods == &uhci_device_isoc_methods) { /* isochronous transfer */ td = xfer->td_transfer_last; usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); /* check also if the first is complete */ td = xfer->td_transfer_first; usb2_pc_cpu_invalidate(td->page_cache); status |= le32toh(td->td_status); if (!(status & UHCI_TD_ACTIVE)) { uhci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } } else { /* non-isochronous transfer */ /* * check whether there is an error somewhere * in the middle, or whether there was a short * packet (SPD and not ACTIVE) */ td = xfer->td_transfer_cache; while (1) { usb2_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); token = le32toh(td->td_token); /* * if there is an active TD the transfer isn't done */ if (status & UHCI_TD_ACTIVE) { /* update cache */ xfer->td_transfer_cache = td; goto done; } /* * last transfer descriptor makes the transfer done */ if (((void *)td) == xfer->td_transfer_last) { break; } /* * any kind of error makes the transfer done */ if (status & UHCI_TD_STALLED) { break; } /* * check if we reached the last packet * or if there is a short packet: */ if ((td->td_next == htole32(UHCI_PTR_T)) || (UHCI_TD_GET_ACTLEN(status) < td->len)) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { /* update cache */ xfer->td_transfer_cache = td; uhci_check_transfer_sub(xfer); goto done; } } /* transfer is done */ break; } td = td->obj_next; } uhci_non_isoc_done(xfer); goto transferred; } done: DPRINTFN(13, "xfer=%p is still active\n", xfer); return (0); transferred: return (1); } static void uhci_interrupt_poll(uhci_softc_t *sc) { struct usb2_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { /* * check if transfer is transferred */ if (uhci_check_transfer(xfer)) { /* queue has been modified */ goto repeat; } } } /*------------------------------------------------------------------------* * uhci_interrupt - UHCI interrupt handler * * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler, * hence the interrupt handler will be setup before "sc->sc_bus.bdev" * is present ! *------------------------------------------------------------------------*/ void uhci_interrupt(uhci_softc_t *sc) { uint32_t status; USB_BUS_LOCK(&sc->sc_bus); DPRINTFN(16, "real interrupt\n"); #if USB_DEBUG if (uhcidebug > 15) { uhci_dumpregs(sc); } #endif status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS; if (status == 0) { /* the interrupt was not for us */ goto done; } if (status & (UHCI_STS_RD | UHCI_STS_HSE | UHCI_STS_HCPE | UHCI_STS_HCH)) { if (status & UHCI_STS_RD) { #if USB_DEBUG printf("%s: resume detect\n", __FUNCTION__); #endif } if (status & UHCI_STS_HSE) { printf("%s: host system error\n", __FUNCTION__); } if (status & UHCI_STS_HCPE) { printf("%s: host controller process error\n", __FUNCTION__); } if (status & UHCI_STS_HCH) { /* no acknowledge needed */ printf("%s: host controller halted\n", __FUNCTION__); #if USB_DEBUG uhci_dump_all(sc); #endif } } /* get acknowledge bits */ status &= (UHCI_STS_USBINT | UHCI_STS_USBEI | UHCI_STS_RD | UHCI_STS_HSE | UHCI_STS_HCPE); if (status == 0) { /* nothing to acknowledge */ goto done; } /* acknowledge interrupts */ UWRITE2(sc, UHCI_STS, status); /* poll all the USB transfers */ uhci_interrupt_poll(sc); done: USB_BUS_UNLOCK(&sc->sc_bus); } /* * called when a request does not complete */ static void uhci_timeout(void *arg) { struct usb2_xfer *xfer = arg; - uhci_softc_t *sc = xfer->usb2_sc; DPRINTF("xfer=%p\n", xfer); - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* transfer is transferred */ uhci_device_done(xfer, USB_ERR_TIMEOUT); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void uhci_do_poll(struct usb2_bus *bus) { struct uhci_softc *sc = UHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); uhci_interrupt_poll(sc); uhci_root_ctrl_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void uhci_setup_standard_chain_sub(struct uhci_std_temp *temp) { uhci_td_t *td; uhci_td_t *td_next; uhci_td_t *td_alt_next; uint32_t average; uint32_t len_old; uint8_t shortpkt_old; uint8_t precompute; td_alt_next = NULL; shortpkt_old = temp->shortpkt; len_old = temp->len; precompute = 1; /* software is used to detect short incoming transfers */ if ((temp->td_token & htole32(UHCI_TD_PID)) == htole32(UHCI_TD_PID_IN)) { temp->td_status |= htole32(UHCI_TD_SPD); } else { temp->td_status &= ~htole32(UHCI_TD_SPD); } temp->ml.buf_offset = 0; restart: temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0)); temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->average)); td = temp->td; td_next = temp->td_next; while (1) { if (temp->len == 0) { if (temp->shortpkt) { break; } /* send a Zero Length Packet, ZLP, last */ temp->shortpkt = 1; temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(0)); average = 0; } else { average = temp->average; if (temp->len < average) { temp->shortpkt = 1; temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0)); temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->len)); average = temp->len; } } if (td_next == NULL) { panic("%s: out of UHCI transfer descriptors!", __FUNCTION__); } /* get next TD */ td = td_next; td_next = td->obj_next; /* check if we are pre-computing */ if (precompute) { /* update remaining length */ temp->len -= average; continue; } /* fill out current TD */ td->td_status = temp->td_status; td->td_token = temp->td_token; /* update data toggle */ temp->td_token ^= htole32(UHCI_TD_SET_DT(1)); if (average == 0) { td->len = 0; td->td_buffer = 0; td->fix_pc = NULL; } else { /* update remaining length */ temp->len -= average; td->len = average; /* fill out buffer pointer and do fixup, if any */ uhci_mem_layout_fixup(&temp->ml, td); } td->alt_next = td_alt_next; if ((td_next == td_alt_next) && temp->setup_alt_next) { /* we need to receive these frames one by one ! */ td->td_status |= htole32(UHCI_TD_IOC); td->td_next = htole32(UHCI_PTR_T); } else { if (td_next) { /* link the current TD with the next one */ td->td_next = td_next->td_self; } } usb2_pc_cpu_flush(td->page_cache); } if (precompute) { precompute = 0; /* setup alt next pointer, if any */ if (temp->short_frames_ok) { if (temp->setup_alt_next) { td_alt_next = td_next; } } else { /* we use this field internally */ td_alt_next = td_next; } /* restore */ temp->shortpkt = shortpkt_old; temp->len = len_old; goto restart; } temp->td = td; temp->td_next = td_next; } static uhci_td_t * uhci_setup_standard_chain(struct usb2_xfer *xfer) { struct uhci_std_temp temp; uhci_td_t *td; uint32_t x; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->sumlen, usb2_get_speed(xfer->udev)); temp.average = xfer->max_frame_size; temp.max_frame_size = xfer->max_frame_size; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; temp.td = NULL; temp.td_next = td; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.short_frames_ok = xfer->flags_int.short_frames_ok; uhci_mem_layout_init(&temp.ml, xfer); temp.td_status = htole32(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE)); if (xfer->udev->speed == USB_SPEED_LOW) { temp.td_status |= htole32(UHCI_TD_LS); } temp.td_token = htole32(UHCI_TD_SET_ENDPT(xfer->endpoint) | UHCI_TD_SET_DEVADDR(xfer->address)); if (xfer->pipe->toggle_next) { /* DATA1 is next */ temp.td_token |= htole32(UHCI_TD_SET_DT(1)); } /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF)); temp.td_token |= htole32(UHCI_TD_PID_SETUP | UHCI_TD_SET_DT(0)); temp.len = xfer->frlengths[0]; temp.ml.buf_pc = xfer->frbuffers + 0; temp.shortpkt = temp.len ? 1 : 0; uhci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; temp.ml.buf_pc = xfer->frbuffers + x; x++; if (x == xfer->nframes) { temp.setup_alt_next = 0; } /* * Keep previous data toggle, * device address and endpoint number: */ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF) | UHCI_TD_SET_DT(1)); if (temp.len == 0) { /* make sure that we send an USB packet */ temp.shortpkt = 0; } else { /* regular data transfer */ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1; } /* set endpoint direction */ temp.td_token |= (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? htole32(UHCI_TD_PID_IN) : htole32(UHCI_TD_PID_OUT); uhci_setup_standard_chain_sub(&temp); } /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { /* * send a DATA1 message and reverse the current endpoint * direction */ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) | UHCI_TD_SET_ENDPT(0xF) | UHCI_TD_SET_DT(1)); temp.td_token |= (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? htole32(UHCI_TD_PID_IN | UHCI_TD_SET_DT(1)) : htole32(UHCI_TD_PID_OUT | UHCI_TD_SET_DT(1)); temp.len = 0; temp.ml.buf_pc = NULL; temp.shortpkt = 0; uhci_setup_standard_chain_sub(&temp); } td = temp.td; td->td_next = htole32(UHCI_PTR_T); /* set interrupt bit */ td->td_status |= htole32(UHCI_TD_IOC); usb2_pc_cpu_flush(td->page_cache); /* must have at least one frame! */ xfer->td_transfer_last = td; #if USB_DEBUG if (uhcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next); uhci_dump_tds(xfer->td_transfer_first); } #endif return (xfer->td_transfer_first); } /* NOTE: "done" can be run two times in a row, * from close and from interrupt */ static void uhci_device_done(struct usb2_xfer *xfer, usb2_error_t error) { struct usb2_pipe_methods *methods = xfer->pipe->methods; uhci_softc_t *sc = xfer->usb2_sc; uhci_qh_t *qh; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error); qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (qh) { usb2_pc_cpu_invalidate(qh->page_cache); qh->e_next = 0; qh->qh_e_next = htole32(UHCI_PTR_T); usb2_pc_cpu_flush(qh->page_cache); } if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; uhci_rem_loop(sc); } if (methods == &uhci_device_bulk_methods) { UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last); } if (methods == &uhci_device_ctrl_methods) { if (xfer->udev->speed == USB_SPEED_LOW) { UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last); } else { UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last); } } if (methods == &uhci_device_intr_methods) { UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); } /* * Only finish isochronous transfers once * which will update "xfer->frlengths". */ if (xfer->td_transfer_first && xfer->td_transfer_last) { if (methods == &uhci_device_isoc_methods) { uhci_isoc_done(sc, xfer); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } /* dequeue transfer and start next transfer */ usb2_transfer_done(xfer, error); } /*------------------------------------------------------------------------* * uhci bulk support *------------------------------------------------------------------------*/ static void uhci_device_bulk_open(struct usb2_xfer *xfer) { return; } static void uhci_device_bulk_close(struct usb2_xfer *xfer) { uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_bulk_enter(struct usb2_xfer *xfer) { return; } static void uhci_device_bulk_start(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; uhci_td_t *td; uhci_qh_t *qh; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; UHCI_APPEND_QH(qh, td, sc->sc_bulk_p_last); uhci_add_loop(sc); xfer->flags_int.bandwidth_reclaimed = 1; /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods uhci_device_bulk_methods = { .open = uhci_device_bulk_open, .close = uhci_device_bulk_close, .enter = uhci_device_bulk_enter, .start = uhci_device_bulk_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * uhci control support *------------------------------------------------------------------------*/ static void uhci_device_ctrl_open(struct usb2_xfer *xfer) { return; } static void uhci_device_ctrl_close(struct usb2_xfer *xfer) { uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_ctrl_enter(struct usb2_xfer *xfer) { return; } static void uhci_device_ctrl_start(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; uhci_qh_t *qh; uhci_td_t *td; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* * NOTE: some devices choke on bandwidth- reclamation for control * transfers */ if (xfer->udev->speed == USB_SPEED_LOW) { UHCI_APPEND_QH(qh, td, sc->sc_ls_ctl_p_last); } else { UHCI_APPEND_QH(qh, td, sc->sc_fs_ctl_p_last); } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods uhci_device_ctrl_methods = { .open = uhci_device_ctrl_open, .close = uhci_device_ctrl_close, .enter = uhci_device_ctrl_enter, .start = uhci_device_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * uhci interrupt support *------------------------------------------------------------------------*/ static void uhci_device_intr_open(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; uint16_t best; uint16_t bit; uint16_t x; best = 0; bit = UHCI_IFRAMELIST_COUNT / 2; while (bit) { if (xfer->interval >= bit) { x = bit; best = bit; while (x & bit) { if (sc->sc_intr_stat[x] < sc->sc_intr_stat[best]) { best = x; } x++; } break; } bit >>= 1; } sc->sc_intr_stat[best]++; xfer->qh_pos = best; DPRINTFN(3, "best=%d interval=%d\n", best, xfer->interval); } static void uhci_device_intr_close(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; sc->sc_intr_stat[xfer->qh_pos]--; uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_intr_enter(struct usb2_xfer *xfer) { return; } static void uhci_device_intr_start(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; uhci_qh_t *qh; uhci_td_t *td; /* setup TD's */ td = uhci_setup_standard_chain(xfer); /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* enter QHs into the controller data structures */ UHCI_APPEND_QH(qh, td, sc->sc_intr_p_last[xfer->qh_pos]); /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods uhci_device_intr_methods = { .open = uhci_device_intr_open, .close = uhci_device_intr_close, .enter = uhci_device_intr_enter, .start = uhci_device_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * uhci isochronous support *------------------------------------------------------------------------*/ static void uhci_device_isoc_open(struct usb2_xfer *xfer) { uhci_td_t *td; uint32_t td_token; uint8_t ds; td_token = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? UHCI_TD_IN(0, xfer->endpoint, xfer->address, 0) : UHCI_TD_OUT(0, xfer->endpoint, xfer->address, 0); td_token = htole32(td_token); /* initialize all TD's */ for (ds = 0; ds != 2; ds++) { for (td = xfer->td_start[ds]; td; td = td->obj_next) { /* mark TD as inactive */ td->td_status = htole32(UHCI_TD_IOS); td->td_token = td_token; usb2_pc_cpu_flush(td->page_cache); } } } static void uhci_device_isoc_close(struct usb2_xfer *xfer) { uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_device_isoc_enter(struct usb2_xfer *xfer) { struct uhci_mem_layout ml; uhci_softc_t *sc = xfer->usb2_sc; uint32_t nframes; uint32_t temp; uint32_t *plen; #if USB_DEBUG uint8_t once = 1; #endif uhci_td_t *td; uhci_td_t *td_last = NULL; uhci_td_t **pp_last; DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes); nframes = UREAD2(sc, UHCI_FRNUM); temp = (nframes - xfer->pipe->isoc_next) & (UHCI_VFRAMELIST_COUNT - 1); if ((xfer->pipe->is_synced == 0) || (temp < xfer->nframes)) { /* * If there is data underflow or the pipe queue is empty we * schedule the transfer a few frames ahead of the current * frame position. Else two isochronous transfers might * overlap. */ xfer->pipe->isoc_next = (nframes + 3) & (UHCI_VFRAMELIST_COUNT - 1); xfer->pipe->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->pipe->isoc_next - nframes) & (UHCI_VFRAMELIST_COUNT - 1); /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + xfer->nframes; /* get the real number of frames */ nframes = xfer->nframes; uhci_mem_layout_init(&ml, xfer); plen = xfer->frlengths; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; /* get next DMA set */ td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_p_last[xfer->pipe->isoc_next]; /* store starting position */ xfer->qh_pos = xfer->pipe->isoc_next; while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) { pp_last = &sc->sc_isoc_p_last[0]; } if (*plen > xfer->max_frame_size) { #if USB_DEBUG if (once) { once = 0; printf("%s: frame length(%d) exceeds %d " "bytes (frame truncated)\n", __FUNCTION__, *plen, xfer->max_frame_size); } #endif *plen = xfer->max_frame_size; } /* reuse td_token from last transfer */ td->td_token &= htole32(~UHCI_TD_MAXLEN_MASK); td->td_token |= htole32(UHCI_TD_SET_MAXLEN(*plen)); td->len = *plen; if (td->len == 0) { /* * Do not call "uhci_mem_layout_fixup()" when the * length is zero! */ td->td_buffer = 0; td->fix_pc = NULL; } else { /* fill out buffer pointer and do fixup, if any */ uhci_mem_layout_fixup(&ml, td); } /* update status */ if (nframes == 0) { td->td_status = htole32 (UHCI_TD_ZERO_ACTLEN (UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS | UHCI_TD_IOC)); } else { td->td_status = htole32 (UHCI_TD_ZERO_ACTLEN (UHCI_TD_SET_ERRCNT(0) | UHCI_TD_ACTIVE | UHCI_TD_IOS)); } usb2_pc_cpu_flush(td->page_cache); #if USB_DEBUG if (uhcidebug > 5) { DPRINTF("TD %d\n", nframes); uhci_dump_td(td); } #endif /* insert TD into schedule */ UHCI_APPEND_TD(td, *pp_last); pp_last++; plen++; td_last = td; td = td->obj_next; } xfer->td_transfer_last = td_last; /* update isoc_next */ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_p_last[0]) & (UHCI_VFRAMELIST_COUNT - 1); } static void uhci_device_isoc_start(struct usb2_xfer *xfer) { /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); } struct usb2_pipe_methods uhci_device_isoc_methods = { .open = uhci_device_isoc_open, .close = uhci_device_isoc_close, .enter = uhci_device_isoc_enter, .start = uhci_device_isoc_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * uhci root control support *------------------------------------------------------------------------* * simulate a hardware hub by handling * all the necessary requests *------------------------------------------------------------------------*/ static void uhci_root_ctrl_open(struct usb2_xfer *xfer) { return; } static void uhci_root_ctrl_close(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; if (sc->sc_root_ctrl.xfer == xfer) { sc->sc_root_ctrl.xfer = NULL; } uhci_device_done(xfer, USB_ERR_CANCELLED); } /* data structures and routines * to emulate the root hub: */ static const struct usb2_device_descriptor uhci_devd = { sizeof(struct usb2_device_descriptor), UDESC_DEVICE, /* type */ {0x00, 0x01}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_FSHUB, /* protocol */ 64, /* max packet */ {0}, {0}, {0x00, 0x01}, /* device id */ 1, 2, 0, /* string indicies */ 1 /* # of configurations */ }; static const struct uhci_config_desc uhci_confd = { .confd = { .bLength = sizeof(struct usb2_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(uhci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0 /* max power */ }, .ifcd = { .bLength = sizeof(struct usb2_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_FSHUB, }, .endpd = { .bLength = sizeof(struct usb2_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = UE_DIR_IN | UHCI_INTR_ENDPT, .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, /* max packet (63 ports) */ .bInterval = 255, }, }; static const struct usb2_hub_descriptor_min uhci_hubd_piix = { sizeof(uhci_hubd_piix), UDESC_HUB, 2, {UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0}, 50, /* power on to power good */ 0, {0x00}, /* both ports are removable */ }; /* * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also * enables the port, and also states that SET_FEATURE(PORT_ENABLE) * should not be used by the USB subsystem. As we cannot issue a * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port * will be enabled as part of the reset. * * On the VT83C572, the port cannot be successfully enabled until the * outstanding "port enable change" and "connection status change" * events have been reset. */ static usb2_error_t uhci_portreset(uhci_softc_t *sc, uint16_t index, uint8_t use_polling) { uint16_t port; uint16_t x; uint8_t lim; if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else return (USB_ERR_IOERROR); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PR); if (use_polling) { /* polling */ DELAY(USB_PORT_ROOT_RESET_DELAY * 1000); } else { usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_PORT_ROOT_RESET_DELAY); } DPRINTFN(4, "uhci port %d reset, status0 = 0x%04x\n", index, UREAD2(sc, port)); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); if (use_polling) { /* polling */ DELAY(1000); } else { usb2_pause_mtx(&sc->sc_bus.bus_mtx, 1); } DPRINTFN(4, "uhci port %d reset, status1 = 0x%04x\n", index, UREAD2(sc, port)); x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); for (lim = 0; lim < 12; lim++) { if (use_polling) { /* polling */ DELAY(USB_PORT_RESET_DELAY * 1000); } else { usb2_pause_mtx(&sc->sc_bus.bus_mtx, USB_PORT_RESET_DELAY); } x = UREAD2(sc, port); DPRINTFN(4, "uhci port %d iteration %u, status = 0x%04x\n", index, lim, x); if (!(x & UHCI_PORTSC_CCS)) { /* * No device is connected (or was disconnected * during reset). Consider the port reset. * The delay must be long enough to ensure on * the initial iteration that the device * connection will have been registered. 50ms * appears to be sufficient, but 20ms is not. */ DPRINTFN(4, "uhci port %d loop %u, device detached\n", index, lim); goto done; } if (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) { /* * Port enabled changed and/or connection * status changed were set. Reset either or * both raised flags (by writing a 1 to that * bit), and wait again for state to settle. */ UWRITE2(sc, port, URWMASK(x) | (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC))); continue; } if (x & UHCI_PORTSC_PE) { /* port is enabled */ goto done; } UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE); } DPRINTFN(2, "uhci port %d reset timed out\n", index); return (USB_ERR_TIMEOUT); done: DPRINTFN(4, "uhci port %d reset, status2 = 0x%04x\n", index, UREAD2(sc, port)); sc->sc_isreset = 1; return (USB_ERR_NORMAL_COMPLETION); } static void uhci_root_ctrl_enter(struct usb2_xfer *xfer) { return; } static void uhci_root_ctrl_start(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; DPRINTF("\n"); sc->sc_root_ctrl.xfer = xfer; usb2_config_td_queue_command (&sc->sc_config_td, NULL, &uhci_root_ctrl_task, 0, 0); } static void uhci_root_ctrl_task(struct uhci_softc *sc, struct uhci_config_copy *cc, uint16_t refcount) { uhci_root_ctrl_poll(sc); } static void uhci_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { uhci_softc_t *sc = xfer->usb2_sc; char *ptr; uint16_t x; uint16_t port; uint16_t value; uint16_t index; uint16_t status; uint16_t change; uint8_t use_polling; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_SETUP) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ uhci_device_done(xfer, std->err); } goto done; } /* buffer reset */ std->ptr = sc->sc_hub_desc.temp; std->len = 0; value = UGETW(std->req.wValue); index = UGETW(std->req.wIndex); use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x " "wValue=0x%04x wIndex=0x%04x\n", std->req.bmRequestType, std->req.bRequest, UGETW(std->req.wLength), value, index); #define C(x,y) ((x) | ((y) << 8)) switch (C(std->req.bRequest, std->req.bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): std->len = 1; sc->sc_hub_desc.temp[0] = sc->sc_conf; break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): switch (value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(uhci_devd); sc->sc_hub_desc.devd = uhci_devd; break; case UDESC_CONFIG: if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(uhci_confd); std->ptr = USB_ADD_BYTES(&uhci_confd, 0); break; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ ptr = "\001"; break; case 1: /* Vendor */ ptr = sc->sc_vendor; break; case 2: /* Product */ ptr = "UHCI root HUB"; break; default: ptr = ""; break; } std->len = usb2_make_str_desc (sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), ptr); break; default: std->err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): std->len = 1; sc->sc_hub_desc.temp[0] = 0; break; case C(UR_GET_STATUS, UT_READ_DEVICE): std->len = 2; USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED); break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): std->len = 2; USETW(sc->sc_hub_desc.stat.wStatus, 0); break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= USB_MAX_DEVICES) { std->err = USB_ERR_IOERROR; goto done; } sc->sc_addr = value; break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if ((value != 0) && (value != 1)) { std->err = USB_ERR_IOERROR; goto done; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): std->err = USB_ERR_IOERROR; goto done; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(4, "UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value); if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { std->err = USB_ERR_IOERROR; goto done; } switch (value) { case UHF_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x & ~UHCI_PORTSC_PR); break; case UHF_C_PORT_CONNECTION: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_CSC); break; case UHF_C_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_POEDC); break; case UHF_C_PORT_OVER_CURRENT: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_OCIC); break; case UHF_C_PORT_RESET: sc->sc_isreset = 0; std->err = USB_ERR_NORMAL_COMPLETION; goto done; case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_POWER: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: default: std->err = USB_ERR_IOERROR; goto done; } break; case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { std->err = USB_ERR_IOERROR; goto done; } std->len = 1; sc->sc_hub_desc.temp[0] = ((UREAD2(sc, port) & UHCI_PORTSC_LS) >> UHCI_PORTSC_LS_SHIFT); break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if ((value & 0xff) != 0) { std->err = USB_ERR_IOERROR; goto done; } std->len = sizeof(uhci_hubd_piix); std->ptr = USB_ADD_BYTES(&uhci_hubd_piix, 0); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): std->len = 16; bzero(sc->sc_hub_desc.temp, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { std->err = USB_ERR_IOERROR; goto done; } x = UREAD2(sc, port); status = change = 0; if (x & UHCI_PORTSC_CCS) status |= UPS_CURRENT_CONNECT_STATUS; if (x & UHCI_PORTSC_CSC) change |= UPS_C_CONNECT_STATUS; if (x & UHCI_PORTSC_PE) status |= UPS_PORT_ENABLED; if (x & UHCI_PORTSC_POEDC) change |= UPS_C_PORT_ENABLED; if (x & UHCI_PORTSC_OCI) status |= UPS_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_OCIC) change |= UPS_C_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_SUSP) status |= UPS_SUSPEND; if (x & UHCI_PORTSC_LSDA) status |= UPS_LOW_SPEED; status |= UPS_PORT_POWER; if (sc->sc_isreset) change |= UPS_C_PORT_RESET; USETW(sc->sc_hub_desc.ps.wPortStatus, status); USETW(sc->sc_hub_desc.ps.wPortChange, change); std->len = sizeof(sc->sc_hub_desc.ps); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): std->err = USB_ERR_IOERROR; goto done; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): if (index == 1) port = UHCI_PORTSC1; else if (index == 2) port = UHCI_PORTSC2; else { std->err = USB_ERR_IOERROR; goto done; } switch (value) { case UHF_PORT_ENABLE: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_PE); break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); UWRITE2(sc, port, x | UHCI_PORTSC_SUSP); break; case UHF_PORT_RESET: std->err = uhci_portreset(sc, index, use_polling); goto done; case UHF_PORT_POWER: /* pretend we turned on power */ std->err = USB_ERR_NORMAL_COMPLETION; goto done; case UHF_C_PORT_CONNECTION: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_LOW_SPEED: case UHF_C_PORT_SUSPEND: case UHF_C_PORT_RESET: default: std->err = USB_ERR_IOERROR; goto done; } break; default: std->err = USB_ERR_IOERROR; goto done; } done: return; } static void uhci_root_ctrl_poll(struct uhci_softc *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &uhci_root_ctrl_done); } struct usb2_pipe_methods uhci_root_ctrl_methods = { .open = uhci_root_ctrl_open, .close = uhci_root_ctrl_close, .enter = uhci_root_ctrl_enter, .start = uhci_root_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 0, }; /*------------------------------------------------------------------------* * uhci root interrupt support *------------------------------------------------------------------------*/ static void uhci_root_intr_open(struct usb2_xfer *xfer) { return; } static void uhci_root_intr_close(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; if (sc->sc_root_intr.xfer == xfer) { sc->sc_root_intr.xfer = NULL; } uhci_device_done(xfer, USB_ERR_CANCELLED); } static void uhci_root_intr_enter(struct usb2_xfer *xfer) { return; } static void uhci_root_intr_start(struct usb2_xfer *xfer) { uhci_softc_t *sc = xfer->usb2_sc; sc->sc_root_intr.xfer = xfer; usb2_transfer_timeout_ms(xfer, &uhci_root_intr_check, xfer->interval); } static void uhci_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { uhci_softc_t *sc = xfer->usb2_sc; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_PRE_DATA) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer is transferred */ uhci_device_done(xfer, std->err); } goto done; } /* setup buffer */ std->ptr = sc->sc_hub_idata; std->len = sizeof(sc->sc_hub_idata); done: return; } /* * this routine is executed periodically and simulates interrupts * from the root controller interrupt pipe for port status change */ static void uhci_root_intr_check(void *arg) { struct usb2_xfer *xfer = arg; uhci_softc_t *sc = xfer->usb2_sc; DPRINTFN(21, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); sc->sc_hub_idata[0] = 0; if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) { sc->sc_hub_idata[0] |= 1 << 1; } if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) { sc->sc_hub_idata[0] |= 1 << 2; } if ((sc->sc_hub_idata[0] == 0) || !(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) { /* * no change or controller not running, try again in a while */ uhci_root_intr_start(xfer); } else { usb2_sw_transfer(&sc->sc_root_intr, &uhci_root_intr_done); } - USB_BUS_UNLOCK(&sc->sc_bus); } struct usb2_pipe_methods uhci_root_intr_methods = { .open = uhci_root_intr_open, .close = uhci_root_intr_close, .enter = uhci_root_intr_enter, .start = uhci_root_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; static void uhci_xfer_setup(struct usb2_setup_params *parm) { struct usb2_page_search page_info; struct usb2_page_cache *pc; uhci_softc_t *sc; struct usb2_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t nqh; uint32_t nfixup; uint32_t n; uint16_t align; sc = UHCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * setup xfer */ xfer->usb2_sc = sc; parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x500; /* * compute ntd and nqh */ if (parm->methods == &uhci_device_ctrl_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usb2_transfer_setup_sub(parm); /* see EHCI HC driver for proof of "ntd" formula */ nqh = 1; ntd = ((2 * xfer->nframes) + 1 /* STATUS */ + (xfer->max_data_length / xfer->max_frame_size)); } else if (parm->methods == &uhci_device_bulk_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usb2_transfer_setup_sub(parm); nqh = 1; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_frame_size)); } else if (parm->methods == &uhci_device_intr_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usb2_transfer_setup_sub(parm); nqh = 1; ntd = ((2 * xfer->nframes) + (xfer->max_data_length / xfer->max_frame_size)); } else if (parm->methods == &uhci_device_isoc_methods) { xfer->flags_int.bdma_enable = 1; xfer->flags_int.bdma_no_post_sync = 1; usb2_transfer_setup_sub(parm); nqh = 0; ntd = xfer->nframes; } else { usb2_transfer_setup_sub(parm); nqh = 0; ntd = 0; } if (parm->err) { return; } /* * NOTE: the UHCI controller requires that * every packet must be contiguous on * the same USB memory page ! */ nfixup = (parm->bufsize / USB_PAGE_SIZE) + 1; /* * Compute a suitable power of two alignment * for our "max_frame_size" fixup buffer(s): */ align = xfer->max_frame_size; n = 0; while (align) { align >>= 1; n++; } /* check for power of two */ if (!(xfer->max_frame_size & (xfer->max_frame_size - 1))) { n--; } /* * We don't allow alignments of * less than 8 bytes: * * NOTE: Allocating using an aligment * of 1 byte has special meaning! */ if (n < 3) { n = 3; } align = (1 << n); if (usb2_transfer_setup_sub_malloc( parm, &pc, xfer->max_frame_size, align, nfixup)) { parm->err = USB_ERR_NOMEM; return; } xfer->buf_fixup = pc; alloc_dma_set: if (parm->err) { return; } last_obj = NULL; if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(uhci_td_t), UHCI_TD_ALIGN, ntd)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != ntd; n++) { uhci_td_t *td; usb2_get_page(pc + n, 0, &page_info); td = page_info.buffer; /* init TD */ if ((parm->methods == &uhci_device_bulk_methods) || (parm->methods == &uhci_device_ctrl_methods) || (parm->methods == &uhci_device_intr_methods)) { /* set depth first bit */ td->td_self = htole32(page_info.physaddr | UHCI_PTR_TD | UHCI_PTR_VF); } else { td->td_self = htole32(page_info.physaddr | UHCI_PTR_TD); } td->obj_next = last_obj; td->page_cache = pc + n; last_obj = td; usb2_pc_cpu_flush(pc + n); } } xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; if (usb2_transfer_setup_sub_malloc( parm, &pc, sizeof(uhci_qh_t), UHCI_QH_ALIGN, nqh)) { parm->err = USB_ERR_NOMEM; return; } if (parm->buf) { for (n = 0; n != nqh; n++) { uhci_qh_t *qh; usb2_get_page(pc + n, 0, &page_info); qh = page_info.buffer; /* init QH */ qh->qh_self = htole32(page_info.physaddr | UHCI_PTR_QH); qh->obj_next = last_obj; qh->page_cache = pc + n; last_obj = qh; usb2_pc_cpu_flush(pc + n); } } xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; if (!xfer->flags_int.curr_dma_set) { xfer->flags_int.curr_dma_set = 1; goto alloc_dma_set; } } static void uhci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) { uhci_softc_t *sc = UHCI_BUS2SC(udev->bus); DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, udev->flags.usb2_mode, sc->sc_addr); if (udev->flags.usb2_mode != USB_MODE_HOST) { /* not supported */ return; } if (udev->device_index == sc->sc_addr) { switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &uhci_root_ctrl_methods; break; case UE_DIR_IN | UHCI_INTR_ENDPT: pipe->methods = &uhci_root_intr_methods; break; default: /* do nothing */ break; } } else { switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &uhci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &uhci_device_intr_methods; break; case UE_ISOCHRONOUS: if (udev->speed == USB_SPEED_FULL) { pipe->methods = &uhci_device_isoc_methods; } break; case UE_BULK: if (udev->speed != USB_SPEED_LOW) { pipe->methods = &uhci_device_bulk_methods; } break; default: /* do nothing */ break; } } } static void uhci_xfer_unsetup(struct usb2_xfer *xfer) { return; } static void uhci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) { /* * Wait until hardware has finished any possible use of the * transfer descriptor(s) and QH */ *pus = (1125); /* microseconds */ } struct usb2_bus_methods uhci_bus_methods = { .pipe_init = uhci_pipe_init, .xfer_setup = uhci_xfer_setup, .xfer_unsetup = uhci_xfer_unsetup, .do_poll = uhci_do_poll, .get_dma_delay = uhci_get_dma_delay, }; Index: projects/cambria/sys/dev/usb2/controller/uhci2_pci.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/uhci2_pci.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/uhci2_pci.c (revision 186460) @@ -1,453 +1,454 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (augustss@carlstedt.se) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include __FBSDID("$FreeBSD$"); /* Universal Host Controller Interface * * UHCI spec: http://www.intel.com/ */ /* The low level controller code for UHCI has been split into * PCI probes and UHCI specific code. This was done to facilitate the * sharing of code between *BSD's */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCI_UHCI_VENDORID_INTEL 0x8086 #define PCI_UHCI_VENDORID_VIA 0x1106 /* PIIX4E has no separate stepping */ #define PCI_UHCI_BASE_REG 0x20 static device_probe_t uhci_pci_probe; static device_attach_t uhci_pci_attach; static device_detach_t uhci_pci_detach; static device_suspend_t uhci_pci_suspend; static device_resume_t uhci_pci_resume; static int uhci_pci_suspend(device_t self) { uhci_softc_t *sc = device_get_softc(self); int err; err = bus_generic_suspend(self); if (err) { return (err); } uhci_suspend(sc); return (0); } static int uhci_pci_resume(device_t self) { uhci_softc_t *sc = device_get_softc(self); pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); uhci_resume(sc); bus_generic_resume(self); return (0); } static const char * uhci_pci_match(device_t self) { uint32_t device_id = pci_get_devid(self); switch (device_id) { case 0x26888086: return ("Intel 631XESB/632XESB/3100 USB controller USB-1"); case 0x26898086: return ("Intel 631XESB/632XESB/3100 USB controller USB-2"); case 0x268a8086: return ("Intel 631XESB/632XESB/3100 USB controller USB-3"); case 0x268b8086: return ("Intel 631XESB/632XESB/3100 USB controller USB-4"); case 0x70208086: return ("Intel 82371SB (PIIX3) USB controller"); case 0x71128086: return ("Intel 82371AB/EB (PIIX4) USB controller"); case 0x24128086: return ("Intel 82801AA (ICH) USB controller"); case 0x24228086: return ("Intel 82801AB (ICH0) USB controller"); case 0x24428086: return ("Intel 82801BA/BAM (ICH2) USB controller USB-A"); case 0x24448086: return ("Intel 82801BA/BAM (ICH2) USB controller USB-B"); case 0x24828086: return ("Intel 82801CA/CAM (ICH3) USB controller USB-A"); case 0x24848086: return ("Intel 82801CA/CAM (ICH3) USB controller USB-B"); case 0x24878086: return ("Intel 82801CA/CAM (ICH3) USB controller USB-C"); case 0x24c28086: return ("Intel 82801DB (ICH4) USB controller USB-A"); case 0x24c48086: return ("Intel 82801DB (ICH4) USB controller USB-B"); case 0x24c78086: return ("Intel 82801DB (ICH4) USB controller USB-C"); case 0x24d28086: return ("Intel 82801EB (ICH5) USB controller USB-A"); case 0x24d48086: return ("Intel 82801EB (ICH5) USB controller USB-B"); case 0x24d78086: return ("Intel 82801EB (ICH5) USB controller USB-C"); case 0x24de8086: return ("Intel 82801EB (ICH5) USB controller USB-D"); case 0x26588086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A"); case 0x26598086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B"); case 0x265a8086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C"); case 0x265b8086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D"); case 0x28308086: return ("Intel 82801H (ICH8) USB controller USB-A"); case 0x28318086: return ("Intel 82801H (ICH8) USB controller USB-B"); case 0x28328086: return ("Intel 82801H (ICH8) USB controller USB-C"); case 0x28348086: return ("Intel 82801H (ICH8) USB controller USB-D"); case 0x28358086: return ("Intel 82801H (ICH8) USB controller USB-E"); case 0x29348086: return ("Intel 82801I (ICH9) USB controller"); case 0x29358086: return ("Intel 82801I (ICH9) USB controller"); case 0x29368086: return ("Intel 82801I (ICH9) USB controller"); case 0x29378086: return ("Intel 82801I (ICH9) USB controller"); case 0x29388086: return ("Intel 82801I (ICH9) USB controller"); case 0x29398086: return ("Intel 82801I (ICH9) USB controller"); case 0x719a8086: return ("Intel 82443MX USB controller"); case 0x76028086: return ("Intel 82372FB/82468GX USB controller"); case 0x30381106: return ("VIA 83C572 USB controller"); default: break; } if ((pci_get_class(self) == PCIC_SERIALBUS) && (pci_get_subclass(self) == PCIS_SERIALBUS_USB) && (pci_get_progif(self) == PCI_INTERFACE_UHCI)) { return ("UHCI (generic) USB controller"); } return (NULL); } static int uhci_pci_probe(device_t self) { const char *desc = uhci_pci_match(self); if (desc) { device_set_desc(self, desc); return (0); } else { return (ENXIO); } } static int uhci_pci_attach(device_t self) { uhci_softc_t *sc = device_get_softc(self); int rid; int err; if (sc == NULL) { device_printf(self, "Could not allocate sc\n"); return (ENXIO); } /* get all DMA memory */ + sc->sc_bus.parent = self; if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self), &uhci_iterate_hw_softc)) { return ENOMEM; } sc->sc_dev = self; pci_enable_busmaster(self); rid = PCI_UHCI_BASE_REG; sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (!sc->sc_io_res) { device_printf(self, "Could not map ports\n"); goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); /* disable interrupts */ bus_space_write_2(sc->sc_io_tag, sc->sc_io_hdl, UHCI_INTR, 0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(self, "Could not allocate irq\n"); goto error; } sc->sc_bus.bdev = device_add_child(self, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(self, "Could not add USB device\n"); goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); /* * uhci_pci_match must never return NULL if uhci_pci_probe * succeeded */ device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self)); switch (pci_get_vendor(self)) { case PCI_UHCI_VENDORID_INTEL: sprintf(sc->sc_vendor, "Intel"); break; case PCI_UHCI_VENDORID_VIA: sprintf(sc->sc_vendor, "VIA"); break; default: if (bootverbose) { device_printf(self, "(New UHCI DeviceId=0x%08x)\n", pci_get_devid(self)); } sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self)); } switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) { case PCI_USB_REV_PRE_1_0: sc->sc_bus.usbrev = USB_REV_PRE_1_0; break; case PCI_USB_REV_1_0: sc->sc_bus.usbrev = USB_REV_1_0; break; default: sc->sc_bus.usbrev = USB_REV_UNKNOWN; break; } err = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(self, "could not setup config thread!\n"); goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)(void *)uhci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)(void *)uhci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { device_printf(self, "Could not setup irq, %d\n", err); sc->sc_intr_hdl = NULL; goto error; } /* * Set the PIRQD enable bit and switch off all the others. We don't * want legacy support to interfere with us XXX Does this also mean * that the BIOS won't touch the keyboard anymore if it is connected * to the ports of the root hub? */ #if USB_DEBUG if (pci_read_config(self, PCI_LEGSUP, 2) != PCI_LEGSUP_USBPIRQDEN) { device_printf(self, "LegSup = 0x%04x\n", pci_read_config(self, PCI_LEGSUP, 2)); } #endif pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2); err = uhci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); } if (err) { device_printf(self, "USB init failed\n"); goto error; } return (0); error: uhci_pci_detach(self); return (ENXIO); } int uhci_pci_detach(device_t self) { uhci_softc_t *sc = device_get_softc(self); device_t bdev; usb2_config_td_drain(&sc->sc_config_td); if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(self, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(self); /* * disable interrupts that might have been switched on in * uhci_init. */ if (sc->sc_io_res) { USB_BUS_LOCK(&sc->sc_bus); /* stop the controller */ uhci_reset(sc); USB_BUS_UNLOCK(&sc->sc_bus); } pci_disable_busmaster(self); if (sc->sc_irq_res && sc->sc_intr_hdl) { int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); if (err) { /* XXX or should we panic? */ device_printf(self, "Could not tear down irq, %d\n", err); } sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG, sc->sc_io_res); sc->sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_config_td); usb2_bus_mem_free_all(&sc->sc_bus, &uhci_iterate_hw_softc); return (0); } static driver_t uhci_driver = { .name = "uhci", .methods = (device_method_t[]){ /* device interface */ DEVMETHOD(device_probe, uhci_pci_probe), DEVMETHOD(device_attach, uhci_pci_attach), DEVMETHOD(device_detach, uhci_pci_detach), DEVMETHOD(device_suspend, uhci_pci_suspend), DEVMETHOD(device_resume, uhci_pci_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }, .size = sizeof(struct uhci_softc), }; static devclass_t uhci_devclass; DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0); DRIVER_MODULE(uhci, cardbus, uhci_driver, uhci_devclass, 0, 0); MODULE_DEPEND(uhci, usb2_controller, 1, 1, 1); MODULE_DEPEND(uhci, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/controller/usb2_bus.h =================================================================== --- projects/cambria/sys/dev/usb2/controller/usb2_bus.h (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/usb2_bus.h (revision 186460) @@ -1,88 +1,89 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #ifndef _USB2_BUS_H_ #define _USB2_BUS_H_ /* * The following structure defines the USB explore message sent to the * USB explore process. */ struct usb2_bus_msg { struct usb2_proc_msg hdr; struct usb2_bus *bus; }; /* * The following structure defines the USB statistics structure. */ struct usb2_bus_stat { uint32_t uds_requests[4]; }; /* * The following structure defines an USB BUS. There is one USB BUS * for every Host or Device controller. */ struct usb2_bus { struct usb2_bus_stat stats_err; struct usb2_bus_stat stats_ok; struct usb2_process explore_proc; struct usb2_bus_msg explore_msg[2]; struct usb2_bus_msg detach_msg[2]; struct mtx bus_mtx; /* This mutex protects the USB * hardware */ struct usb2_perm perm; struct usb2_xfer_queue intr_q; + device_t parent; device_t bdev; /* filled by HC driver */ struct usb2_dma_parent_tag dma_parent_tag[1]; struct usb2_dma_tag dma_tags[USB_BUS_DMA_TAG_MAX]; struct usb2_bus_methods *methods; /* filled by HC driver */ struct usb2_device *devices[USB_MAX_DEVICES]; uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; uint32_t transfer_count[4]; uint16_t isoc_time_last; /* in milliseconds */ uint8_t alloc_failed; /* Set if memory allocation failed. */ uint8_t driver_added_refcount; /* Current driver generation count */ uint8_t usbrev; /* USB revision. See "USB_REV_XXX". */ uint8_t devices_max; /* maximum number of USB devices */ uint8_t do_probe; /* set if USB BUS should be re-probed */ union { struct usb2_hw_ep_scratch hw_ep_scratch[1]; struct usb2_temp_setup temp_setup[1]; uint8_t data[128]; } scratch[1]; }; #endif /* _USB2_BUS_H_ */ Index: projects/cambria/sys/dev/usb2/controller/usb2_controller.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/usb2_controller.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/usb2_controller.c (revision 186460) @@ -1,472 +1,472 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #define USB_DEBUG_VAR usb2_ctrl_debug #include #include #include #include #include #include #include #include #include /* function prototypes */ static device_probe_t usb2_probe; static device_attach_t usb2_attach; static device_detach_t usb2_detach; static void usb2_attach_sub(device_t, struct usb2_bus *); static void usb2_post_init(void *); static void usb2_bus_mem_flush_all_cb(struct usb2_bus *, struct usb2_page_cache *, struct usb2_page *, uint32_t, uint32_t); static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *, struct usb2_page_cache *, struct usb2_page *, uint32_t, uint32_t); static void usb2_bus_mem_free_all_cb(struct usb2_bus *, struct usb2_page_cache *, struct usb2_page *, uint32_t, uint32_t); /* static variables */ #if USB_DEBUG static int usb2_ctrl_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); SYSCTL_INT(_hw_usb2_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb2_ctrl_debug, 0, "Debug level"); #endif static uint8_t usb2_post_init_called = 0; static devclass_t usb2_devclass; static device_method_t usb2_methods[] = { DEVMETHOD(device_probe, usb2_probe), DEVMETHOD(device_attach, usb2_attach), DEVMETHOD(device_detach, usb2_detach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), {0, 0} }; static driver_t usb2_driver = { .name = "usbus", .methods = usb2_methods, .size = 0, }; DRIVER_MODULE(usbus, ohci, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, uhci, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, ehci, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, at91_udp, usb2_driver, usb2_devclass, 0, 0); DRIVER_MODULE(usbus, uss820, usb2_driver, usb2_devclass, 0, 0); MODULE_DEPEND(usb2_controller, usb2_core, 1, 1, 1); MODULE_VERSION(usb2_controller, 1); /*------------------------------------------------------------------------* * usb2_probe * * This function is called from "{ehci,ohci,uhci}_pci_attach()". *------------------------------------------------------------------------*/ static int usb2_probe(device_t dev) { DPRINTF("\n"); return (0); } /*------------------------------------------------------------------------* * usb2_attach *------------------------------------------------------------------------*/ static int usb2_attach(device_t dev) { struct usb2_bus *bus = device_get_ivars(dev); DPRINTF("\n"); if (bus == NULL) { DPRINTFN(0, "USB device has no ivars\n"); return (ENXIO); } if (usb2_post_init_called) { mtx_lock(&Giant); usb2_attach_sub(dev, bus); mtx_unlock(&Giant); usb2_needs_explore(bus, 1); } return (0); /* return success */ } /*------------------------------------------------------------------------* * usb2_detach *------------------------------------------------------------------------*/ static int usb2_detach(device_t dev) { struct usb2_bus *bus = device_get_softc(dev); DPRINTF("\n"); if (bus == NULL) { /* was never setup properly */ return (0); } /* Let the USB explore process detach all devices. */ USB_BUS_LOCK(bus); if (usb2_proc_msignal(&bus->explore_proc, &bus->detach_msg[0], &bus->detach_msg[1])) { /* ignore */ } /* Wait for detach to complete */ usb2_proc_mwait(&bus->explore_proc, &bus->detach_msg[0], &bus->detach_msg[1]); USB_BUS_UNLOCK(bus); /* Get rid of USB explore process */ usb2_proc_unsetup(&bus->explore_proc); return (0); } /*------------------------------------------------------------------------* * usb2_bus_explore * * This function is used to explore the device tree from the root. *------------------------------------------------------------------------*/ static void usb2_bus_explore(struct usb2_proc_msg *pm) { struct usb2_bus *bus; struct usb2_device *udev; bus = ((struct usb2_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; if (udev && udev->hub) { if (bus->do_probe) { bus->do_probe = 0; bus->driver_added_refcount++; } if (bus->driver_added_refcount == 0) { /* avoid zero, hence that is memory default */ bus->driver_added_refcount = 1; } USB_BUS_UNLOCK(bus); mtx_lock(&Giant); /* * Explore the Root USB HUB. This call can sleep, * exiting Giant, which is actually Giant. */ (udev->hub->explore) (udev); mtx_unlock(&Giant); USB_BUS_LOCK(bus); } } /*------------------------------------------------------------------------* * usb2_bus_detach * * This function is used to detach the device tree from the root. *------------------------------------------------------------------------*/ static void usb2_bus_detach(struct usb2_proc_msg *pm) { struct usb2_bus *bus; struct usb2_device *udev; device_t dev; bus = ((struct usb2_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; dev = bus->bdev; /* clear the softc */ device_set_softc(dev, NULL); USB_BUS_UNLOCK(bus); mtx_lock(&Giant); /* detach children first */ bus_generic_detach(dev); /* * Free USB Root device, but not any sub-devices, hence they * are freed by the caller of this function: */ usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 0); usb2_free_device(udev); mtx_unlock(&Giant); USB_BUS_LOCK(bus); /* clear bdev variable last */ bus->bdev = NULL; } /*------------------------------------------------------------------------* * usb2_attach_sub * * This function is the real USB bus attach code. It is factored out, * hence it can be called at two different places in time. During * bootup this function is called from "usb2_post_init". During * hot-plug it is called directly from the "usb2_attach()" method. *------------------------------------------------------------------------*/ static void usb2_attach_sub(device_t dev, struct usb2_bus *bus) { struct usb2_device *child; usb2_error_t err; uint8_t speed; DPRINTF("\n"); mtx_assert(&Giant, MA_OWNED); switch (bus->usbrev) { case USB_REV_1_0: speed = USB_SPEED_FULL; device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); break; case USB_REV_1_1: speed = USB_SPEED_FULL; device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); break; case USB_REV_2_0: speed = USB_SPEED_HIGH; device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); break; case USB_REV_2_5: speed = USB_SPEED_VARIABLE; device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); break; default: device_printf(bus->bdev, "Unsupported USB revision!\n"); return; } /* Allocate the Root USB device */ child = usb2_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, speed, USB_MODE_HOST); if (child) { err = usb2_probe_and_attach(child, USB_IFACE_INDEX_ANY); if (!err) { if (!bus->devices[USB_ROOT_HUB_ADDR]->hub) { err = USB_ERR_NO_ROOT_HUB; } } } else { err = USB_ERR_NOMEM; } if (err) { device_printf(bus->bdev, "Root HUB problem, error=%s\n", usb2_errstr(err)); } /* Initialise USB process messages */ bus->explore_msg[0].hdr.pm_callback = &usb2_bus_explore; bus->explore_msg[0].bus = bus; bus->explore_msg[1].hdr.pm_callback = &usb2_bus_explore; bus->explore_msg[1].bus = bus; bus->detach_msg[0].hdr.pm_callback = &usb2_bus_detach; bus->detach_msg[0].bus = bus; bus->detach_msg[1].hdr.pm_callback = &usb2_bus_detach; bus->detach_msg[1].bus = bus; /* Create a new USB process */ if (usb2_proc_setup(&bus->explore_proc, &bus->bus_mtx, USB_PRI_MED)) { printf("WARNING: Creation of USB explore process failed.\n"); } /* set softc - we are ready */ device_set_softc(dev, bus); } /*------------------------------------------------------------------------* * usb2_post_init * * This function is called to attach all USB busses that were found * during bootup. *------------------------------------------------------------------------*/ static void usb2_post_init(void *arg) { struct usb2_bus *bus; devclass_t dc; device_t dev; int max; int n; mtx_lock(&Giant); usb2_devclass_ptr = devclass_find("usbus"); dc = usb2_devclass_ptr; if (dc) { max = devclass_get_maxunit(dc) + 1; for (n = 0; n != max; n++) { dev = devclass_get_device(dc, n); if (dev && device_is_attached(dev)) { bus = device_get_ivars(dev); if (bus) { mtx_lock(&Giant); usb2_attach_sub(dev, bus); mtx_unlock(&Giant); } } } } else { DPRINTFN(0, "no devclass\n"); } usb2_post_init_called = 1; /* explore all USB busses in parallell */ usb2_needs_explore_all(); mtx_unlock(&Giant); } SYSINIT(usb2_post_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_post_init, NULL); SYSUNINIT(usb2_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb2_bus_unload, NULL); /*------------------------------------------------------------------------* * usb2_bus_mem_flush_all_cb *------------------------------------------------------------------------*/ static void usb2_bus_mem_flush_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align) { usb2_pc_cpu_flush(pc); } /*------------------------------------------------------------------------* * usb2_bus_mem_flush_all - factored out code *------------------------------------------------------------------------*/ void usb2_bus_mem_flush_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb) { if (cb) { cb(bus, &usb2_bus_mem_flush_all_cb); } } /*------------------------------------------------------------------------* * usb2_bus_mem_alloc_all_cb *------------------------------------------------------------------------*/ static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align) { /* need to initialize the page cache */ pc->tag_parent = bus->dma_parent_tag; if (usb2_pc_alloc_mem(pc, pg, size, align)) { bus->alloc_failed = 1; } } /*------------------------------------------------------------------------* * usb2_bus_mem_alloc_all - factored out code * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat, usb2_bus_mem_cb_t *cb) { bus->alloc_failed = 0; bus->devices_max = USB_MAX_DEVICES; - mtx_init(&bus->bus_mtx, "USB bus lock", + mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), NULL, MTX_DEF | MTX_RECURSE); TAILQ_INIT(&bus->intr_q.head); usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, dmat, &bus->bus_mtx, NULL, NULL, 32, USB_BUS_DMA_TAG_MAX); if (cb) { cb(bus, &usb2_bus_mem_alloc_all_cb); } if (bus->alloc_failed) { usb2_bus_mem_free_all(bus, cb); } return (bus->alloc_failed); } /*------------------------------------------------------------------------* * usb2_bus_mem_free_all_cb *------------------------------------------------------------------------*/ static void usb2_bus_mem_free_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align) { usb2_pc_free_mem(pc); } /*------------------------------------------------------------------------* * usb2_bus_mem_free_all - factored out code *------------------------------------------------------------------------*/ void usb2_bus_mem_free_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb) { if (cb) { cb(bus, &usb2_bus_mem_free_all_cb); } usb2_dma_tag_unsetup(bus->dma_parent_tag); mtx_destroy(&bus->bus_mtx); } Index: projects/cambria/sys/dev/usb2/controller/uss820dci.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/uss820dci.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/uss820dci.c (revision 186460) @@ -1,2525 +1,2522 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * This file contains the driver for the USS820 series USB Device * Controller * * NOTE: The datasheet does not document everything! */ #include #include #include #include #include #define USB_DEBUG_VAR uss820dcidebug #define usb2_config_td_cc uss820dci_config_copy #define usb2_config_td_softc uss820dci_softc #include #include #include #include #include #include #include #include #include #include #include #include #include #define USS820_DCI_BUS2SC(bus) \ ((struct uss820dci_softc *)(((uint8_t *)(bus)) - \ USB_P2U(&(((struct uss820dci_softc *)0)->sc_bus)))) #define USS820_DCI_PC2SC(pc) \ USS820_DCI_BUS2SC((pc)->tag_parent->info->bus) #if USB_DEBUG static int uss820dcidebug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, uss820dci, CTLFLAG_RW, 0, "USB uss820dci"); SYSCTL_INT(_hw_usb2_uss820dci, OID_AUTO, debug, CTLFLAG_RW, &uss820dcidebug, 0, "uss820dci debug level"); #endif #define USS820_DCI_INTR_ENDPT 1 /* prototypes */ struct usb2_bus_methods uss820dci_bus_methods; struct usb2_pipe_methods uss820dci_device_bulk_methods; struct usb2_pipe_methods uss820dci_device_ctrl_methods; struct usb2_pipe_methods uss820dci_device_intr_methods; struct usb2_pipe_methods uss820dci_device_isoc_fs_methods; struct usb2_pipe_methods uss820dci_root_ctrl_methods; struct usb2_pipe_methods uss820dci_root_intr_methods; static uss820dci_cmd_t uss820dci_setup_rx; static uss820dci_cmd_t uss820dci_data_rx; static uss820dci_cmd_t uss820dci_data_tx; static uss820dci_cmd_t uss820dci_data_tx_sync; static void uss820dci_device_done(struct usb2_xfer *, usb2_error_t); static void uss820dci_do_poll(struct usb2_bus *); static void uss820dci_root_ctrl_poll(struct uss820dci_softc *); static void uss820dci_standard_done(struct usb2_xfer *); static void uss820dci_intr_set(struct usb2_xfer *, uint8_t); static void uss820dci_update_shared_1(struct uss820dci_softc *, uint8_t, uint8_t, uint8_t); static usb2_sw_transfer_func_t uss820dci_root_intr_done; static usb2_sw_transfer_func_t uss820dci_root_ctrl_done; static usb2_config_td_command_t uss820dci_root_ctrl_task; /* * Here is a list of what the USS820D chip can support. The main * limitation is that the sum of the buffer sizes must be less than * 1120 bytes. */ static const struct usb2_hw_ep_profile uss820dci_ep_profile[] = { [0] = { .max_in_frame_size = 32, .max_out_frame_size = 32, .is_simplex = 0, .support_control = 1, }, [1] = { .max_in_frame_size = 64, .max_out_frame_size = 64, .is_simplex = 0, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_in = 1, .support_out = 1, }, [2] = { .max_in_frame_size = 8, .max_out_frame_size = 8, .is_simplex = 0, .support_multi_buffer = 1, .support_bulk = 1, .support_interrupt = 1, .support_in = 1, .support_out = 1, }, [3] = { .max_in_frame_size = 256, .max_out_frame_size = 256, .is_simplex = 0, .support_multi_buffer = 1, .support_isochronous = 1, .support_in = 1, .support_out = 1, }, }; static void uss820dci_update_shared_1(struct uss820dci_softc *sc, uint8_t reg, uint8_t keep_mask, uint8_t set_mask) { uint8_t temp; USS820_WRITE_1(sc, USS820_PEND, 1); temp = USS820_READ_1(sc, reg); temp &= (keep_mask); temp |= (set_mask); USS820_WRITE_1(sc, reg, temp); USS820_WRITE_1(sc, USS820_PEND, 0); } static void uss820dci_get_hw_ep_profile(struct usb2_device *udev, const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr) { if (ep_addr == 0) { *ppf = uss820dci_ep_profile + 0; } else if (ep_addr < 5) { *ppf = uss820dci_ep_profile + 1; } else if (ep_addr < 7) { *ppf = uss820dci_ep_profile + 2; } else if (ep_addr == 7) { *ppf = uss820dci_ep_profile + 3; } else { *ppf = NULL; } } static void uss820dci_pull_up(struct uss820dci_softc *sc) { uint8_t temp; /* pullup D+, if possible */ if (!sc->sc_flags.d_pulled_up && sc->sc_flags.port_powered) { sc->sc_flags.d_pulled_up = 1; DPRINTF("\n"); temp = USS820_READ_1(sc, USS820_MCSR); temp |= USS820_MCSR_DPEN; USS820_WRITE_1(sc, USS820_MCSR, temp); } } static void uss820dci_pull_down(struct uss820dci_softc *sc) { uint8_t temp; /* pulldown D+, if possible */ if (sc->sc_flags.d_pulled_up) { sc->sc_flags.d_pulled_up = 0; DPRINTF("\n"); temp = USS820_READ_1(sc, USS820_MCSR); temp &= ~USS820_MCSR_DPEN; USS820_WRITE_1(sc, USS820_MCSR, temp); } } static void uss820dci_wakeup_peer(struct uss820dci_softc *sc) { if (!(sc->sc_flags.status_suspend)) { return; } DPRINTFN(0, "not supported\n"); } static void uss820dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) { struct uss820dci_softc *sc; uint8_t temp; DPRINTFN(5, "is_on=%u\n", is_on); USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); sc = USS820_DCI_BUS2SC(udev->bus); temp = USS820_READ_1(sc, USS820_SCR); if (is_on) { temp |= USS820_SCR_RWUPE; } else { temp &= ~USS820_SCR_RWUPE; } USS820_WRITE_1(sc, USS820_SCR, temp); } static void uss820dci_set_address(struct uss820dci_softc *sc, uint8_t addr) { DPRINTFN(5, "addr=%d\n", addr); USS820_WRITE_1(sc, USS820_FADDR, addr); } static uint8_t uss820dci_setup_rx(struct uss820dci_td *td) { struct uss820dci_softc *sc; struct usb2_device_request req; uint16_t count; uint8_t rx_stat; uint8_t temp; /* select the correct endpoint */ bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index); /* read out FIFO status */ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_stat_reg); /* get pointer to softc */ sc = USS820_DCI_PC2SC(td->pc); DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder); if (!(rx_stat & USS820_RXSTAT_RXSETUP)) { /* abort any ongoing transfer */ if (!td->did_stall) { DPRINTFN(5, "stalling\n"); /* set stall */ uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, (USS820_EPCON_TXSTL | USS820_EPCON_RXSTL)); td->did_stall = 1; } goto not_complete; } /* clear stall and all I/O */ uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF ^ (USS820_EPCON_TXSTL | USS820_EPCON_RXSTL | USS820_EPCON_RXIE | USS820_EPCON_TXOE), 0); /* clear end overwrite flag */ uss820dci_update_shared_1(sc, USS820_RXSTAT, 0xFF ^ USS820_RXSTAT_EDOVW, 0); /* get the packet byte count */ count = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_count_low_reg); count |= (bus_space_read_1(td->io_tag, td->io_hdl, td->rx_count_high_reg) << 8); count &= 0x3FF; /* verify data length */ if (count != td->remainder) { DPRINTFN(0, "Invalid SETUP packet " "length, %d bytes\n", count); goto not_complete; } if (count != sizeof(req)) { DPRINTFN(0, "Unsupported SETUP packet " "length, %d bytes\n", count); goto not_complete; } /* receive data */ bus_space_read_multi_1(td->io_tag, td->io_hdl, td->rx_fifo_reg, (void *)&req, sizeof(req)); /* read out FIFO status */ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_stat_reg); if (rx_stat & (USS820_RXSTAT_EDOVW | USS820_RXSTAT_STOVW)) { DPRINTF("new SETUP packet received\n"); return (1); /* not complete */ } /* clear receive setup bit */ uss820dci_update_shared_1(sc, USS820_RXSTAT, 0xFF ^ (USS820_RXSTAT_RXSETUP | USS820_RXSTAT_EDOVW | USS820_RXSTAT_STOVW), 0); /* set RXFFRC bit */ temp = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_cntl_reg); temp |= USS820_RXCON_RXFFRC; bus_space_write_1(td->io_tag, td->io_hdl, td->rx_cntl_reg, temp); /* copy data into real buffer */ usb2_copy_in(td->pc, 0, &req, sizeof(req)); td->offset = sizeof(req); td->remainder = 0; /* sneak peek the set address */ if ((req.bmRequestType == UT_WRITE_DEVICE) && (req.bRequest == UR_SET_ADDRESS)) { sc->sc_dv_addr = req.wValue[0] & 0x7F; } else { sc->sc_dv_addr = 0xFF; } return (0); /* complete */ not_complete: /* clear end overwrite flag, if any */ if (rx_stat & USS820_RXSTAT_RXSETUP) { uss820dci_update_shared_1(sc, USS820_RXSTAT, 0xFF ^ (USS820_RXSTAT_EDOVW | USS820_RXSTAT_STOVW | USS820_RXSTAT_RXSETUP), 0); } return (1); /* not complete */ } static uint8_t uss820dci_data_rx(struct uss820dci_td *td) { struct usb2_page_search buf_res; uint16_t count; uint8_t rx_flag; uint8_t rx_stat; uint8_t rx_cntl; uint8_t to; uint8_t got_short; to = 2; /* don't loop forever! */ got_short = 0; /* select the correct endpoint */ bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index); /* check if any of the FIFO banks have data */ repeat: /* read out FIFO flag */ rx_flag = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_flag_reg); /* read out FIFO status */ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_stat_reg); DPRINTFN(5, "rx_stat=0x%02x rx_flag=0x%02x rem=%u\n", rx_stat, rx_flag, td->remainder); if (rx_stat & (USS820_RXSTAT_RXSETUP | USS820_RXSTAT_RXSOVW | USS820_RXSTAT_EDOVW)) { if (td->remainder == 0) { /* * We are actually complete and have * received the next SETUP */ DPRINTFN(5, "faking complete\n"); return (0); /* complete */ } /* * USB Host Aborted the transfer. */ td->error = 1; return (0); /* complete */ } /* check for errors */ if (rx_flag & (USS820_RXFLG_RXOVF | USS820_RXFLG_RXURF)) { DPRINTFN(5, "overflow or underflow\n"); /* should not happen */ td->error = 1; return (0); /* complete */ } /* check status */ if (!(rx_flag & (USS820_RXFLG_RXFIF0 | USS820_RXFLG_RXFIF1))) { /* read out EPCON register */ /* enable RX input */ if (!td->did_stall) { uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc), USS820_EPCON, 0xFF, USS820_EPCON_RXIE); td->did_stall = 1; } return (1); /* not complete */ } /* get the packet byte count */ count = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_count_low_reg); count |= (bus_space_read_1(td->io_tag, td->io_hdl, td->rx_count_high_reg) << 8); count &= 0x3FF; DPRINTFN(5, "count=0x%04x\n", count); /* verify the packet byte count */ if (count != td->max_packet_size) { if (count < td->max_packet_size) { /* we have a short packet */ td->short_pkt = 1; got_short = 1; } else { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } } /* verify the packet byte count */ if (count > td->remainder) { /* invalid USB packet */ td->error = 1; return (0); /* we are complete */ } while (count > 0) { usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* receive data */ bus_space_read_multi_1(td->io_tag, td->io_hdl, td->rx_fifo_reg, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* set RXFFRC bit */ rx_cntl = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_cntl_reg); rx_cntl |= USS820_RXCON_RXFFRC; bus_space_write_1(td->io_tag, td->io_hdl, td->rx_cntl_reg, rx_cntl); /* check if we are complete */ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ return (0); } /* else need to receive a zero length packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t uss820dci_data_tx(struct uss820dci_td *td) { struct usb2_page_search buf_res; uint16_t count; uint16_t count_copy; uint8_t rx_stat; uint8_t tx_flag; uint8_t to; /* select the correct endpoint */ bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index); to = 2; /* don't loop forever! */ repeat: /* read out TX FIFO flags */ tx_flag = bus_space_read_1(td->io_tag, td->io_hdl, td->tx_flag_reg); /* read out RX FIFO status last */ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_stat_reg); DPRINTFN(5, "rx_stat=0x%02x tx_flag=0x%02x rem=%u\n", rx_stat, tx_flag, td->remainder); if (rx_stat & (USS820_RXSTAT_RXSETUP | USS820_RXSTAT_RXSOVW | USS820_RXSTAT_EDOVW)) { /* * The current transfer was aborted * by the USB Host */ td->error = 1; return (0); /* complete */ } if (tx_flag & (USS820_TXFLG_TXOVF | USS820_TXFLG_TXURF)) { td->error = 1; return (0); /* complete */ } if (tx_flag & USS820_TXFLG_TXFIF0) { if (tx_flag & USS820_TXFLG_TXFIF1) { return (1); /* not complete */ } } if ((!td->support_multi_buffer) && (tx_flag & (USS820_TXFLG_TXFIF0 | USS820_TXFLG_TXFIF1))) { return (1); /* not complete */ } count = td->max_packet_size; if (td->remainder < count) { /* we have a short packet */ td->short_pkt = 1; count = td->remainder; } count_copy = count; while (count > 0) { usb2_get_page(td->pc, td->offset, &buf_res); /* get correct length */ if (buf_res.length > count) { buf_res.length = count; } /* transmit data */ bus_space_write_multi_1(td->io_tag, td->io_hdl, td->tx_fifo_reg, buf_res.buffer, buf_res.length); /* update counters */ count -= buf_res.length; td->offset += buf_res.length; td->remainder -= buf_res.length; } /* post-write high packet byte count first */ bus_space_write_1(td->io_tag, td->io_hdl, td->tx_count_high_reg, count_copy >> 8); /* post-write low packet byte count last */ bus_space_write_1(td->io_tag, td->io_hdl, td->tx_count_low_reg, count_copy); /* * Enable TX output, which must happen after that we have written * data into the FIFO. This is undocumented. */ if (!td->did_stall) { uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc), USS820_EPCON, 0xFF, USS820_EPCON_TXOE); td->did_stall = 1; } /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) { return (0); /* complete */ } /* else we need to transmit a short packet */ } if (--to) { goto repeat; } return (1); /* not complete */ } static uint8_t uss820dci_data_tx_sync(struct uss820dci_td *td) { struct uss820dci_softc *sc; uint8_t rx_stat; uint8_t tx_flag; /* select the correct endpoint */ bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index); /* read out TX FIFO flag */ tx_flag = bus_space_read_1(td->io_tag, td->io_hdl, td->tx_flag_reg); /* read out RX FIFO status last */ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl, td->rx_stat_reg); DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder); if (rx_stat & (USS820_RXSTAT_RXSETUP | USS820_RXSTAT_RXSOVW | USS820_RXSTAT_EDOVW)) { DPRINTFN(5, "faking complete\n"); /* Race condition */ return (0); /* complete */ } DPRINTFN(5, "tx_flag=0x%02x rem=%u\n", tx_flag, td->remainder); if (tx_flag & (USS820_TXFLG_TXOVF | USS820_TXFLG_TXURF)) { td->error = 1; return (0); /* complete */ } if (tx_flag & (USS820_TXFLG_TXFIF0 | USS820_TXFLG_TXFIF1)) { return (1); /* not complete */ } sc = USS820_DCI_PC2SC(td->pc); if (sc->sc_dv_addr != 0xFF) { /* write function address */ uss820dci_set_address(sc, sc->sc_dv_addr); } return (0); /* complete */ } static uint8_t uss820dci_xfer_do_fifo(struct usb2_xfer *xfer) { struct uss820dci_td *td; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; while (1) { if ((td->func) (td)) { /* operation in progress */ break; } if (((void *)td) == xfer->td_transfer_last) { goto done; } if (td->error) { goto done; } else if (td->remainder > 0) { /* * We had a short transfer. If there is no alternate * next, stop processing ! */ if (!td->alt_next) { goto done; } } /* * Fetch the next transfer descriptor. */ td = td->obj_next; xfer->td_transfer_cache = td; } return (1); /* not complete */ done: /* compute all actual lengths */ uss820dci_standard_done(xfer); return (0); /* complete */ } static void uss820dci_interrupt_poll(struct uss820dci_softc *sc) { struct usb2_xfer *xfer; repeat: TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { if (!uss820dci_xfer_do_fifo(xfer)) { /* queue has been modified */ goto repeat; } } } static void uss820dci_wait_suspend(struct uss820dci_softc *sc, uint8_t on) { uint8_t scr; uint8_t scratch; scr = USS820_READ_1(sc, USS820_SCR); scratch = USS820_READ_1(sc, USS820_SCRATCH); if (on) { scr |= USS820_SCR_IE_SUSP; scratch &= ~USS820_SCRATCH_IE_RESUME; } else { scr &= ~USS820_SCR_IE_SUSP; scratch |= USS820_SCRATCH_IE_RESUME; } USS820_WRITE_1(sc, USS820_SCR, scr); USS820_WRITE_1(sc, USS820_SCRATCH, scratch); } void uss820dci_interrupt(struct uss820dci_softc *sc) { uint8_t ssr; uint8_t event; USB_BUS_LOCK(&sc->sc_bus); ssr = USS820_READ_1(sc, USS820_SSR); ssr &= (USS820_SSR_SUSPEND | USS820_SSR_RESUME | USS820_SSR_RESET); /* acknowledge all interrupts */ uss820dci_update_shared_1(sc, USS820_SSR, 0, 0); /* check for any bus state change interrupts */ if (ssr) { event = 0; if (ssr & USS820_SSR_RESET) { sc->sc_flags.status_bus_reset = 1; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; /* disable resume interrupt */ uss820dci_wait_suspend(sc, 1); event = 1; } /* * If "RESUME" and "SUSPEND" is set at the same time * we interpret that like "RESUME". Resume is set when * there is at least 3 milliseconds of inactivity on * the USB BUS. */ if (ssr & USS820_SSR_RESUME) { if (sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 1; /* disable resume interrupt */ uss820dci_wait_suspend(sc, 1); event = 1; } } else if (ssr & USS820_SSR_SUSPEND) { if (!sc->sc_flags.status_suspend) { sc->sc_flags.status_suspend = 1; sc->sc_flags.change_suspend = 1; /* enable resume interrupt */ uss820dci_wait_suspend(sc, 0); event = 1; } } if (event) { DPRINTF("real bus interrupt 0x%02x\n", ssr); /* complete root HUB interrupt endpoint */ usb2_sw_transfer(&sc->sc_root_intr, &uss820dci_root_intr_done); } } /* acknowledge all SBI interrupts */ uss820dci_update_shared_1(sc, USS820_SBI, 0, 0); /* acknowledge all SBI1 interrupts */ uss820dci_update_shared_1(sc, USS820_SBI1, 0, 0); /* poll all active transfers */ uss820dci_interrupt_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } static void uss820dci_setup_standard_chain_sub(struct uss820_std_temp *temp) { struct uss820dci_td *td; /* get current Transfer Descriptor */ td = temp->td_next; temp->td = td; /* prepare for next TD */ temp->td_next = td->obj_next; /* fill out the Transfer Descriptor */ td->func = temp->func; td->pc = temp->pc; td->offset = temp->offset; td->remainder = temp->len; td->error = 0; td->did_stall = 0; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; } static void uss820dci_setup_standard_chain(struct usb2_xfer *xfer) { struct uss820_std_temp temp; struct uss820dci_softc *sc; struct uss820dci_td *td; uint32_t x; uint8_t ep_no; DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpoint), xfer->sumlen, usb2_get_speed(xfer->udev)); temp.max_frame_size = xfer->max_frame_size; td = xfer->td_start[0]; xfer->td_transfer_first = td; xfer->td_transfer_cache = td; /* setup temp */ temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.offset = 0; sc = xfer->usb2_sc; ep_no = (xfer->endpoint & UE_ADDR); /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { temp.func = &uss820dci_setup_rx; temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; uss820dci_setup_standard_chain_sub(&temp); } x = 1; } else { x = 0; } if (x != xfer->nframes) { if (xfer->endpoint & UE_DIR_IN) { temp.func = &uss820dci_data_tx; } else { temp.func = &uss820dci_data_rx; } /* setup "pc" pointer */ temp.pc = xfer->frbuffers + x; } while (x != xfer->nframes) { /* DATA0 / DATA1 message */ temp.len = xfer->frlengths[x]; x++; if (x == xfer->nframes) { temp.setup_alt_next = 0; } if (temp.len == 0) { /* make sure that we send an USB packet */ temp.short_pkt = 0; } else { /* regular data transfer */ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1; } uss820dci_setup_standard_chain_sub(&temp); if (xfer->flags_int.isochronous_xfr) { temp.offset += temp.len; } else { /* get next Page Cache pointer */ temp.pc = xfer->frbuffers + x; } } /* always setup a valid "pc" pointer for status and sync */ temp.pc = xfer->frbuffers + 0; /* check if we should append a status stage */ if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { uint8_t need_sync; /* * Send a DATA1 message and invert the current * endpoint direction. */ if (xfer->endpoint & UE_DIR_IN) { temp.func = &uss820dci_data_rx; need_sync = 0; } else { temp.func = &uss820dci_data_tx; need_sync = 1; } temp.len = 0; temp.short_pkt = 0; uss820dci_setup_standard_chain_sub(&temp); if (need_sync) { /* we need a SYNC point after TX */ temp.func = &uss820dci_data_tx_sync; temp.len = 0; temp.short_pkt = 0; uss820dci_setup_standard_chain_sub(&temp); } } /* must have at least one frame! */ td = temp.td; xfer->td_transfer_last = td; } static void uss820dci_timeout(void *arg) { struct usb2_xfer *xfer = arg; - struct uss820dci_softc *sc = xfer->usb2_sc; DPRINTF("xfer=%p\n", xfer); - USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); + USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* transfer is transferred */ uss820dci_device_done(xfer, USB_ERR_TIMEOUT); - - USB_BUS_UNLOCK(&sc->sc_bus); } static void uss820dci_intr_set(struct usb2_xfer *xfer, uint8_t set) { struct uss820dci_softc *sc = xfer->usb2_sc; uint8_t ep_no = (xfer->endpoint & UE_ADDR); uint8_t ep_reg; uint8_t temp; DPRINTFN(15, "endpoint 0x%02x\n", xfer->endpoint); if (ep_no > 3) { ep_reg = USS820_SBIE1; } else { ep_reg = USS820_SBIE; } ep_no &= 3; ep_no = 1 << (2 * ep_no); if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { ep_no <<= 1; /* RX interrupt only */ } else { ep_no |= (ep_no << 1); /* RX and TX interrupt */ } } else { if (!(xfer->endpoint & UE_DIR_IN)) { ep_no <<= 1; } } temp = USS820_READ_1(sc, ep_reg); if (set) { temp |= ep_no; } else { temp &= ~ep_no; } USS820_WRITE_1(sc, ep_reg, temp); } static void uss820dci_start_standard_chain(struct usb2_xfer *xfer) { DPRINTFN(9, "\n"); /* poll one time */ if (uss820dci_xfer_do_fifo(xfer)) { /* * Only enable the endpoint interrupt when we are * actually waiting for data, hence we are dealing * with level triggered interrupts ! */ uss820dci_intr_set(xfer, 1); /* put transfer on interrupt queue */ usb2_transfer_enqueue(&xfer->udev->bus->intr_q, xfer); /* start timeout, if any */ if (xfer->timeout != 0) { usb2_transfer_timeout_ms(xfer, &uss820dci_timeout, xfer->timeout); } } } static void uss820dci_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct uss820dci_softc *sc = xfer->usb2_sc; DPRINTFN(9, "\n"); USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_PRE_DATA) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ uss820dci_device_done(xfer, std->err); } goto done; } /* setup buffer */ std->ptr = sc->sc_hub_idata; std->len = sizeof(sc->sc_hub_idata); /* set port bit */ sc->sc_hub_idata[0] = 0x02; /* we only have one port */ done: return; } static usb2_error_t uss820dci_standard_done_sub(struct usb2_xfer *xfer) { struct uss820dci_td *td; uint32_t len; uint8_t error; DPRINTFN(9, "\n"); td = xfer->td_transfer_cache; do { len = td->remainder; if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract * the remainder from "frlengths[]": */ if (len > xfer->frlengths[xfer->aframes]) { td->error = 1; } else { xfer->frlengths[xfer->aframes] -= len; } } /* Check for transfer error */ if (td->error) { /* the transfer is finished */ error = 1; td = NULL; break; } /* Check for short transfer */ if (len > 0) { if (xfer->flags_int.short_frames_ok) { /* follow alt next */ if (td->alt_next) { td = td->obj_next; } else { td = NULL; } } else { /* the transfer is finished */ td = NULL; } error = 0; break; } td = td->obj_next; /* this USB frame is complete */ error = 0; break; } while (0); /* update transfer cache */ xfer->td_transfer_cache = td; return (error ? USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION); } static void uss820dci_standard_done(struct usb2_xfer *xfer) { usb2_error_t err = 0; DPRINTFN(13, "xfer=%p pipe=%p transfer done\n", xfer, xfer->pipe); /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { err = uss820dci_standard_done_sub(xfer); } xfer->aframes = 1; if (xfer->td_transfer_cache == NULL) { goto done; } } while (xfer->aframes != xfer->nframes) { err = uss820dci_standard_done_sub(xfer); xfer->aframes++; if (xfer->td_transfer_cache == NULL) { goto done; } } if (xfer->flags_int.control_xfr && !xfer->flags_int.control_act) { err = uss820dci_standard_done_sub(xfer); } done: uss820dci_device_done(xfer, err); } /*------------------------------------------------------------------------* * uss820dci_device_done * * NOTE: this function can be called more than one time on the * same USB transfer! *------------------------------------------------------------------------*/ static void uss820dci_device_done(struct usb2_xfer *xfer, usb2_error_t error) { USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error); if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { uss820dci_intr_set(xfer, 0); } /* dequeue transfer and start next transfer */ usb2_transfer_done(xfer, error); } static void uss820dci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer, struct usb2_pipe *pipe) { struct uss820dci_softc *sc; uint8_t ep_no; uint8_t ep_type; uint8_t ep_dir; uint8_t temp; USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); DPRINTFN(5, "pipe=%p\n", pipe); if (xfer) { /* cancel any ongoing transfers */ uss820dci_device_done(xfer, USB_ERR_STALLED); } /* set FORCESTALL */ sc = USS820_DCI_BUS2SC(udev->bus); ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR); ep_dir = (pipe->edesc->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)); ep_type = (pipe->edesc->bmAttributes & UE_XFERTYPE); if (ep_type == UE_CONTROL) { /* should not happen */ return; } USS820_WRITE_1(sc, USS820_EPINDEX, ep_no); if (ep_dir == UE_DIR_IN) { temp = USS820_EPCON_TXSTL; } else { temp = USS820_EPCON_RXSTL; } uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp); } static void uss820dci_clear_stall_sub(struct uss820dci_softc *sc, uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir) { uint8_t temp; if (ep_type == UE_CONTROL) { /* clearing stall is not needed */ return; } /* select endpoint index */ USS820_WRITE_1(sc, USS820_EPINDEX, ep_no); /* clear stall and disable I/O transfers */ if (ep_dir == UE_DIR_IN) { temp = 0xFF ^ (USS820_EPCON_TXOE | USS820_EPCON_TXSTL); } else { temp = 0xFF ^ (USS820_EPCON_RXIE | USS820_EPCON_RXSTL); } uss820dci_update_shared_1(sc, USS820_EPCON, temp, 0); if (ep_dir == UE_DIR_IN) { /* reset data toggle */ USS820_WRITE_1(sc, USS820_TXSTAT, USS820_TXSTAT_TXSOVW); /* reset FIFO */ temp = USS820_READ_1(sc, USS820_TXCON); temp |= USS820_TXCON_TXCLR; USS820_WRITE_1(sc, USS820_TXCON, temp); temp &= ~USS820_TXCON_TXCLR; USS820_WRITE_1(sc, USS820_TXCON, temp); } else { /* reset data toggle */ uss820dci_update_shared_1(sc, USS820_RXSTAT, 0, USS820_RXSTAT_RXSOVW); /* reset FIFO */ temp = USS820_READ_1(sc, USS820_RXCON); temp |= USS820_RXCON_RXCLR; temp &= ~USS820_RXCON_RXFFRC; USS820_WRITE_1(sc, USS820_RXCON, temp); temp &= ~USS820_RXCON_RXCLR; USS820_WRITE_1(sc, USS820_RXCON, temp); } } static void uss820dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe) { struct uss820dci_softc *sc; struct usb2_endpoint_descriptor *ed; USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); DPRINTFN(5, "pipe=%p\n", pipe); /* check mode */ if (udev->flags.usb2_mode != USB_MODE_DEVICE) { /* not supported */ return; } /* get softc */ sc = USS820_DCI_BUS2SC(udev->bus); /* get endpoint descriptor */ ed = pipe->edesc; /* reset endpoint */ uss820dci_clear_stall_sub(sc, (ed->bEndpointAddress & UE_ADDR), (ed->bmAttributes & UE_XFERTYPE), (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT))); } usb2_error_t uss820dci_init(struct uss820dci_softc *sc) { const struct usb2_hw_ep_profile *pf; uint8_t n; uint8_t temp; DPRINTF("start\n"); /* set up the bus structure */ sc->sc_bus.usbrev = USB_REV_1_1; sc->sc_bus.methods = &uss820dci_bus_methods; USB_BUS_LOCK(&sc->sc_bus); /* we always have VBUS */ sc->sc_flags.status_vbus = 1; /* reset the chip */ USS820_WRITE_1(sc, USS820_SCR, USS820_SCR_SRESET); DELAY(100); USS820_WRITE_1(sc, USS820_SCR, 0); /* wait for reset to complete */ for (n = 0;; n++) { temp = USS820_READ_1(sc, USS820_MCSR); if (temp & USS820_MCSR_INIT) { break; } if (n == 100) { USB_BUS_UNLOCK(&sc->sc_bus); return (USB_ERR_INVAL); } /* wait a little for things to stabilise */ DELAY(100); } /* do a pulldown */ uss820dci_pull_down(sc); /* wait 10ms for pulldown to stabilise */ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 10); /* check hardware revision */ temp = USS820_READ_1(sc, USS820_REV); if (temp < 0x13) { USB_BUS_UNLOCK(&sc->sc_bus); return (USB_ERR_INVAL); } /* enable interrupts */ USS820_WRITE_1(sc, USS820_SCR, USS820_SCR_T_IRQ | USS820_SCR_IE_RESET | USS820_SCR_IE_SUSP | USS820_SCR_IRQPOL); /* enable interrupts */ USS820_WRITE_1(sc, USS820_SCRATCH, USS820_SCRATCH_IE_RESUME); /* enable features */ USS820_WRITE_1(sc, USS820_MCSR, USS820_MCSR_BDFEAT | USS820_MCSR_FEAT); sc->sc_flags.mcsr_feat = 1; /* disable interrupts */ USS820_WRITE_1(sc, USS820_SBIE, 0); /* disable interrupts */ USS820_WRITE_1(sc, USS820_SBIE1, 0); /* disable all endpoints */ for (n = 0; n != USS820_EP_MAX; n++) { /* select endpoint */ USS820_WRITE_1(sc, USS820_EPINDEX, n); /* disable endpoint */ uss820dci_update_shared_1(sc, USS820_EPCON, 0, 0); } /* * Initialise default values for some registers that cannot be * changed during operation! */ for (n = 0; n != USS820_EP_MAX; n++) { uss820dci_get_hw_ep_profile(NULL, &pf, n); /* the maximum frame sizes should be the same */ if (pf->max_in_frame_size != pf->max_out_frame_size) { DPRINTF("Max frame size mismatch %u != %u\n", pf->max_in_frame_size, pf->max_out_frame_size); } if (pf->support_isochronous) { if (pf->max_in_frame_size <= 64) { temp = (USS820_TXCON_FFSZ_16_64 | USS820_TXCON_TXISO | USS820_TXCON_ATM); } else if (pf->max_in_frame_size <= 256) { temp = (USS820_TXCON_FFSZ_64_256 | USS820_TXCON_TXISO | USS820_TXCON_ATM); } else if (pf->max_in_frame_size <= 512) { temp = (USS820_TXCON_FFSZ_8_512 | USS820_TXCON_TXISO | USS820_TXCON_ATM); } else { /* 1024 bytes */ temp = (USS820_TXCON_FFSZ_32_1024 | USS820_TXCON_TXISO | USS820_TXCON_ATM); } } else { if ((pf->max_in_frame_size <= 8) && (sc->sc_flags.mcsr_feat)) { temp = (USS820_TXCON_FFSZ_8_512 | USS820_TXCON_ATM); } else if (pf->max_in_frame_size <= 16) { temp = (USS820_TXCON_FFSZ_16_64 | USS820_TXCON_ATM); } else if ((pf->max_in_frame_size <= 32) && (sc->sc_flags.mcsr_feat)) { temp = (USS820_TXCON_FFSZ_32_1024 | USS820_TXCON_ATM); } else { /* 64 bytes */ temp = (USS820_TXCON_FFSZ_64_256 | USS820_TXCON_ATM); } } /* need to configure the chip early */ USS820_WRITE_1(sc, USS820_EPINDEX, n); USS820_WRITE_1(sc, USS820_TXCON, temp); USS820_WRITE_1(sc, USS820_RXCON, temp); if (pf->support_control) { temp = USS820_EPCON_CTLEP | USS820_EPCON_RXSPM | USS820_EPCON_RXIE | USS820_EPCON_RXEPEN | USS820_EPCON_TXOE | USS820_EPCON_TXEPEN; } else { temp = USS820_EPCON_RXEPEN | USS820_EPCON_TXEPEN; } uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp); } USB_BUS_UNLOCK(&sc->sc_bus); /* catch any lost interrupts */ uss820dci_do_poll(&sc->sc_bus); return (0); /* success */ } void uss820dci_uninit(struct uss820dci_softc *sc) { uint8_t temp; USB_BUS_LOCK(&sc->sc_bus); /* disable all interrupts */ temp = USS820_READ_1(sc, USS820_SCR); temp &= ~USS820_SCR_T_IRQ; USS820_WRITE_1(sc, USS820_SCR, temp); sc->sc_flags.port_powered = 0; sc->sc_flags.status_vbus = 0; sc->sc_flags.status_bus_reset = 0; sc->sc_flags.status_suspend = 0; sc->sc_flags.change_suspend = 0; sc->sc_flags.change_connect = 1; uss820dci_pull_down(sc); USB_BUS_UNLOCK(&sc->sc_bus); } void uss820dci_suspend(struct uss820dci_softc *sc) { return; } void uss820dci_resume(struct uss820dci_softc *sc) { return; } static void uss820dci_do_poll(struct usb2_bus *bus) { struct uss820dci_softc *sc = USS820_DCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); uss820dci_interrupt_poll(sc); uss820dci_root_ctrl_poll(sc); USB_BUS_UNLOCK(&sc->sc_bus); } /*------------------------------------------------------------------------* * at91dci bulk support *------------------------------------------------------------------------*/ static void uss820dci_device_bulk_open(struct usb2_xfer *xfer) { return; } static void uss820dci_device_bulk_close(struct usb2_xfer *xfer) { uss820dci_device_done(xfer, USB_ERR_CANCELLED); } static void uss820dci_device_bulk_enter(struct usb2_xfer *xfer) { return; } static void uss820dci_device_bulk_start(struct usb2_xfer *xfer) { /* setup TDs */ uss820dci_setup_standard_chain(xfer); uss820dci_start_standard_chain(xfer); } struct usb2_pipe_methods uss820dci_device_bulk_methods = { .open = uss820dci_device_bulk_open, .close = uss820dci_device_bulk_close, .enter = uss820dci_device_bulk_enter, .start = uss820dci_device_bulk_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci control support *------------------------------------------------------------------------*/ static void uss820dci_device_ctrl_open(struct usb2_xfer *xfer) { return; } static void uss820dci_device_ctrl_close(struct usb2_xfer *xfer) { uss820dci_device_done(xfer, USB_ERR_CANCELLED); } static void uss820dci_device_ctrl_enter(struct usb2_xfer *xfer) { return; } static void uss820dci_device_ctrl_start(struct usb2_xfer *xfer) { /* setup TDs */ uss820dci_setup_standard_chain(xfer); uss820dci_start_standard_chain(xfer); } struct usb2_pipe_methods uss820dci_device_ctrl_methods = { .open = uss820dci_device_ctrl_open, .close = uss820dci_device_ctrl_close, .enter = uss820dci_device_ctrl_enter, .start = uss820dci_device_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci interrupt support *------------------------------------------------------------------------*/ static void uss820dci_device_intr_open(struct usb2_xfer *xfer) { return; } static void uss820dci_device_intr_close(struct usb2_xfer *xfer) { uss820dci_device_done(xfer, USB_ERR_CANCELLED); } static void uss820dci_device_intr_enter(struct usb2_xfer *xfer) { return; } static void uss820dci_device_intr_start(struct usb2_xfer *xfer) { /* setup TDs */ uss820dci_setup_standard_chain(xfer); uss820dci_start_standard_chain(xfer); } struct usb2_pipe_methods uss820dci_device_intr_methods = { .open = uss820dci_device_intr_open, .close = uss820dci_device_intr_close, .enter = uss820dci_device_intr_enter, .start = uss820dci_device_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci full speed isochronous support *------------------------------------------------------------------------*/ static void uss820dci_device_isoc_fs_open(struct usb2_xfer *xfer) { return; } static void uss820dci_device_isoc_fs_close(struct usb2_xfer *xfer) { uss820dci_device_done(xfer, USB_ERR_CANCELLED); } static void uss820dci_device_isoc_fs_enter(struct usb2_xfer *xfer) { struct uss820dci_softc *sc = xfer->usb2_sc; uint32_t temp; uint32_t nframes; DPRINTFN(6, "xfer=%p next=%d nframes=%d\n", xfer, xfer->pipe->isoc_next, xfer->nframes); /* get the current frame index - we don't need the high bits */ nframes = USS820_READ_1(sc, USS820_SOFL); /* * check if the frame index is within the window where the * frames will be inserted */ temp = (nframes - xfer->pipe->isoc_next) & USS820_SOFL_MASK; if ((xfer->pipe->is_synced == 0) || (temp < xfer->nframes)) { /* * If there is data underflow or the pipe queue is * empty we schedule the transfer a few frames ahead * of the current frame position. Else two isochronous * transfers might overlap. */ xfer->pipe->isoc_next = (nframes + 3) & USS820_SOFL_MASK; xfer->pipe->is_synced = 1; DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next); } /* * compute how many milliseconds the insertion is ahead of the * current frame position: */ temp = (xfer->pipe->isoc_next - nframes) & USS820_SOFL_MASK; /* * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp + xfer->nframes; /* compute frame number for next insertion */ xfer->pipe->isoc_next += xfer->nframes; /* setup TDs */ uss820dci_setup_standard_chain(xfer); } static void uss820dci_device_isoc_fs_start(struct usb2_xfer *xfer) { /* start TD chain */ uss820dci_start_standard_chain(xfer); } struct usb2_pipe_methods uss820dci_device_isoc_fs_methods = { .open = uss820dci_device_isoc_fs_open, .close = uss820dci_device_isoc_fs_close, .enter = uss820dci_device_isoc_fs_enter, .start = uss820dci_device_isoc_fs_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; /*------------------------------------------------------------------------* * at91dci root control support *------------------------------------------------------------------------* * simulate a hardware HUB by handling * all the necessary requests *------------------------------------------------------------------------*/ static void uss820dci_root_ctrl_open(struct usb2_xfer *xfer) { return; } static void uss820dci_root_ctrl_close(struct usb2_xfer *xfer) { struct uss820dci_softc *sc = xfer->usb2_sc; if (sc->sc_root_ctrl.xfer == xfer) { sc->sc_root_ctrl.xfer = NULL; } uss820dci_device_done(xfer, USB_ERR_CANCELLED); } /* * USB descriptors for the virtual Root HUB: */ static const struct usb2_device_descriptor uss820dci_devd = { .bLength = sizeof(struct usb2_device_descriptor), .bDescriptorType = UDESC_DEVICE, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_HSHUBSTT, .bMaxPacketSize = 64, .bcdDevice = {0x00, 0x01}, .iManufacturer = 1, .iProduct = 2, .bNumConfigurations = 1, }; static const struct usb2_device_qualifier uss820dci_odevd = { .bLength = sizeof(struct usb2_device_qualifier), .bDescriptorType = UDESC_DEVICE_QUALIFIER, .bcdUSB = {0x00, 0x02}, .bDeviceClass = UDCLASS_HUB, .bDeviceSubClass = UDSUBCLASS_HUB, .bDeviceProtocol = UDPROTO_FSHUB, .bMaxPacketSize0 = 0, .bNumConfigurations = 0, }; static const struct uss820dci_config_desc uss820dci_confd = { .confd = { .bLength = sizeof(struct usb2_config_descriptor), .bDescriptorType = UDESC_CONFIG, .wTotalLength[0] = sizeof(uss820dci_confd), .bNumInterface = 1, .bConfigurationValue = 1, .iConfiguration = 0, .bmAttributes = UC_SELF_POWERED, .bMaxPower = 0, }, .ifcd = { .bLength = sizeof(struct usb2_interface_descriptor), .bDescriptorType = UDESC_INTERFACE, .bNumEndpoints = 1, .bInterfaceClass = UICLASS_HUB, .bInterfaceSubClass = UISUBCLASS_HUB, .bInterfaceProtocol = UIPROTO_HSHUBSTT, }, .endpd = { .bLength = sizeof(struct usb2_endpoint_descriptor), .bDescriptorType = UDESC_ENDPOINT, .bEndpointAddress = (UE_DIR_IN | USS820_DCI_INTR_ENDPT), .bmAttributes = UE_INTERRUPT, .wMaxPacketSize[0] = 8, .bInterval = 255, }, }; static const struct usb2_hub_descriptor_min uss820dci_hubd = { .bDescLength = sizeof(uss820dci_hubd), .bDescriptorType = UDESC_HUB, .bNbrPorts = 1, .wHubCharacteristics[0] = (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF, .wHubCharacteristics[1] = (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16, .bPwrOn2PwrGood = 50, .bHubContrCurrent = 0, .DeviceRemovable = {0}, /* port is removable */ }; #define STRING_LANG \ 0x09, 0x04, /* American English */ #define STRING_VENDOR \ 'A', 0, 'G', 0, 'E', 0, 'R', 0, 'E', 0 #define STRING_PRODUCT \ 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \ 'U', 0, 'B', 0, USB_MAKE_STRING_DESC(STRING_LANG, uss820dci_langtab); USB_MAKE_STRING_DESC(STRING_VENDOR, uss820dci_vendor); USB_MAKE_STRING_DESC(STRING_PRODUCT, uss820dci_product); static void uss820dci_root_ctrl_enter(struct usb2_xfer *xfer) { return; } static void uss820dci_root_ctrl_start(struct usb2_xfer *xfer) { struct uss820dci_softc *sc = xfer->usb2_sc; sc->sc_root_ctrl.xfer = xfer; usb2_config_td_queue_command( &sc->sc_config_td, NULL, &uss820dci_root_ctrl_task, 0, 0); } static void uss820dci_root_ctrl_task(struct uss820dci_softc *sc, struct uss820dci_config_copy *cc, uint16_t refcount) { uss820dci_root_ctrl_poll(sc); } static void uss820dci_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { struct uss820dci_softc *sc = xfer->usb2_sc; uint16_t value; uint16_t index; uint8_t use_polling; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); if (std->state != USB_SW_TR_SETUP) { if (std->state == USB_SW_TR_PRE_CALLBACK) { /* transfer transferred */ uss820dci_device_done(xfer, std->err); } goto done; } /* buffer reset */ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0); std->len = 0; value = UGETW(std->req.wValue); index = UGETW(std->req.wIndex); use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; /* demultiplex the control request */ switch (std->req.bmRequestType) { case UT_READ_DEVICE: switch (std->req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_descriptor; case UR_GET_CONFIG: goto tr_handle_get_config; case UR_GET_STATUS: goto tr_handle_get_status; default: goto tr_stalled; } break; case UT_WRITE_DEVICE: switch (std->req.bRequest) { case UR_SET_ADDRESS: goto tr_handle_set_address; case UR_SET_CONFIG: goto tr_handle_set_config; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_DESCRIPTOR: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_WRITE_ENDPOINT: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: switch (UGETW(std->req.wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_clear_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_clear_wakeup; default: goto tr_stalled; } break; case UR_SET_FEATURE: switch (UGETW(std->req.wValue)) { case UF_ENDPOINT_HALT: goto tr_handle_set_halt; case UF_DEVICE_REMOTE_WAKEUP: goto tr_handle_set_wakeup; default: goto tr_stalled; } break; case UR_SYNCH_FRAME: goto tr_valid; /* nop */ default: goto tr_stalled; } break; case UT_READ_ENDPOINT: switch (std->req.bRequest) { case UR_GET_STATUS: goto tr_handle_get_ep_status; default: goto tr_stalled; } break; case UT_WRITE_INTERFACE: switch (std->req.bRequest) { case UR_SET_INTERFACE: goto tr_handle_set_interface; case UR_CLEAR_FEATURE: goto tr_valid; /* nop */ case UR_SET_FEATURE: default: goto tr_stalled; } break; case UT_READ_INTERFACE: switch (std->req.bRequest) { case UR_GET_INTERFACE: goto tr_handle_get_interface; case UR_GET_STATUS: goto tr_handle_get_iface_status; default: goto tr_stalled; } break; case UT_WRITE_CLASS_INTERFACE: case UT_WRITE_VENDOR_INTERFACE: /* XXX forward */ break; case UT_READ_CLASS_INTERFACE: case UT_READ_VENDOR_INTERFACE: /* XXX forward */ break; case UT_WRITE_CLASS_DEVICE: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: goto tr_valid; case UR_SET_DESCRIPTOR: case UR_SET_FEATURE: break; default: goto tr_stalled; } break; case UT_WRITE_CLASS_OTHER: switch (std->req.bRequest) { case UR_CLEAR_FEATURE: goto tr_handle_clear_port_feature; case UR_SET_FEATURE: goto tr_handle_set_port_feature; case UR_CLEAR_TT_BUFFER: case UR_RESET_TT: case UR_STOP_TT: goto tr_valid; default: goto tr_stalled; } break; case UT_READ_CLASS_OTHER: switch (std->req.bRequest) { case UR_GET_TT_STATE: goto tr_handle_get_tt_state; case UR_GET_STATUS: goto tr_handle_get_port_status; default: goto tr_stalled; } break; case UT_READ_CLASS_DEVICE: switch (std->req.bRequest) { case UR_GET_DESCRIPTOR: goto tr_handle_get_class_descriptor; case UR_GET_STATUS: goto tr_handle_get_class_status; default: goto tr_stalled; } break; default: goto tr_stalled; } goto tr_valid; tr_handle_get_descriptor: switch (value >> 8) { case UDESC_DEVICE: if (value & 0xff) { goto tr_stalled; } std->len = sizeof(uss820dci_devd); std->ptr = USB_ADD_BYTES(&uss820dci_devd, 0); goto tr_valid; case UDESC_CONFIG: if (value & 0xff) { goto tr_stalled; } std->len = sizeof(uss820dci_confd); std->ptr = USB_ADD_BYTES(&uss820dci_confd, 0); goto tr_valid; case UDESC_STRING: switch (value & 0xff) { case 0: /* Language table */ std->len = sizeof(uss820dci_langtab); std->ptr = USB_ADD_BYTES(&uss820dci_langtab, 0); goto tr_valid; case 1: /* Vendor */ std->len = sizeof(uss820dci_vendor); std->ptr = USB_ADD_BYTES(&uss820dci_vendor, 0); goto tr_valid; case 2: /* Product */ std->len = sizeof(uss820dci_product); std->ptr = USB_ADD_BYTES(&uss820dci_product, 0); goto tr_valid; default: break; } break; default: goto tr_stalled; } goto tr_stalled; tr_handle_get_config: std->len = 1; sc->sc_hub_temp.wValue[0] = sc->sc_conf; goto tr_valid; tr_handle_get_status: std->len = 2; USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED); goto tr_valid; tr_handle_set_address: if (value & 0xFF00) { goto tr_stalled; } sc->sc_rt_addr = value; goto tr_valid; tr_handle_set_config: if (value >= 2) { goto tr_stalled; } sc->sc_conf = value; goto tr_valid; tr_handle_get_interface: std->len = 1; sc->sc_hub_temp.wValue[0] = 0; goto tr_valid; tr_handle_get_tt_state: tr_handle_get_class_status: tr_handle_get_iface_status: tr_handle_get_ep_status: std->len = 2; USETW(sc->sc_hub_temp.wValue, 0); goto tr_valid; tr_handle_set_halt: tr_handle_set_interface: tr_handle_set_wakeup: tr_handle_clear_wakeup: tr_handle_clear_halt: goto tr_valid; tr_handle_clear_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index); switch (value) { case UHF_PORT_SUSPEND: uss820dci_wakeup_peer(sc); break; case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 0; break; case UHF_PORT_TEST: case UHF_PORT_INDICATOR: case UHF_C_PORT_ENABLE: case UHF_C_PORT_OVER_CURRENT: case UHF_C_PORT_RESET: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 0; uss820dci_pull_down(sc); break; case UHF_C_PORT_CONNECTION: sc->sc_flags.change_connect = 0; break; case UHF_C_PORT_SUSPEND: sc->sc_flags.change_suspend = 0; break; default: std->err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_set_port_feature: if (index != 1) { goto tr_stalled; } DPRINTFN(9, "UR_SET_PORT_FEATURE\n"); switch (value) { case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 1; break; case UHF_PORT_SUSPEND: case UHF_PORT_RESET: case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ break; case UHF_PORT_POWER: sc->sc_flags.port_powered = 1; break; default: std->err = USB_ERR_IOERROR; goto done; } goto tr_valid; tr_handle_get_port_status: DPRINTFN(9, "UR_GET_PORT_STATUS\n"); if (index != 1) { goto tr_stalled; } if (sc->sc_flags.status_vbus) { uss820dci_pull_up(sc); } else { uss820dci_pull_down(sc); } /* Select FULL-speed and Device Side Mode */ value = UPS_PORT_MODE_DEVICE; if (sc->sc_flags.port_powered) { value |= UPS_PORT_POWER; } if (sc->sc_flags.port_enabled) { value |= UPS_PORT_ENABLED; } if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { value |= UPS_CURRENT_CONNECT_STATUS; } if (sc->sc_flags.status_suspend) { value |= UPS_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortStatus, value); value = 0; if (sc->sc_flags.change_connect) { value |= UPS_C_CONNECT_STATUS; } if (sc->sc_flags.change_suspend) { value |= UPS_C_SUSPEND; } USETW(sc->sc_hub_temp.ps.wPortChange, value); std->len = sizeof(sc->sc_hub_temp.ps); goto tr_valid; tr_handle_get_class_descriptor: if (value & 0xFF) { goto tr_stalled; } std->ptr = USB_ADD_BYTES(&uss820dci_hubd, 0); std->len = sizeof(uss820dci_hubd); goto tr_valid; tr_stalled: std->err = USB_ERR_STALLED; tr_valid: done: return; } static void uss820dci_root_ctrl_poll(struct uss820dci_softc *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &uss820dci_root_ctrl_done); } struct usb2_pipe_methods uss820dci_root_ctrl_methods = { .open = uss820dci_root_ctrl_open, .close = uss820dci_root_ctrl_close, .enter = uss820dci_root_ctrl_enter, .start = uss820dci_root_ctrl_start, .enter_is_cancelable = 1, .start_is_cancelable = 0, }; /*------------------------------------------------------------------------* * at91dci root interrupt support *------------------------------------------------------------------------*/ static void uss820dci_root_intr_open(struct usb2_xfer *xfer) { return; } static void uss820dci_root_intr_close(struct usb2_xfer *xfer) { struct uss820dci_softc *sc = xfer->usb2_sc; if (sc->sc_root_intr.xfer == xfer) { sc->sc_root_intr.xfer = NULL; } uss820dci_device_done(xfer, USB_ERR_CANCELLED); } static void uss820dci_root_intr_enter(struct usb2_xfer *xfer) { return; } static void uss820dci_root_intr_start(struct usb2_xfer *xfer) { struct uss820dci_softc *sc = xfer->usb2_sc; sc->sc_root_intr.xfer = xfer; } struct usb2_pipe_methods uss820dci_root_intr_methods = { .open = uss820dci_root_intr_open, .close = uss820dci_root_intr_close, .enter = uss820dci_root_intr_enter, .start = uss820dci_root_intr_start, .enter_is_cancelable = 1, .start_is_cancelable = 1, }; static void uss820dci_xfer_setup(struct usb2_setup_params *parm) { const struct usb2_hw_ep_profile *pf; struct uss820dci_softc *sc; struct usb2_xfer *xfer; void *last_obj; uint32_t ntd; uint32_t n; uint8_t ep_no; sc = USS820_DCI_BUS2SC(parm->udev->bus); xfer = parm->curr_xfer; /* * setup xfer */ xfer->usb2_sc = sc; /* * NOTE: This driver does not use any of the parameters that * are computed from the following values. Just set some * reasonable dummies: */ parm->hc_max_packet_size = 0x500; parm->hc_max_packet_count = 1; parm->hc_max_frame_size = 0x500; usb2_transfer_setup_sub(parm); /* * compute maximum number of TDs */ if (parm->methods == &uss820dci_device_ctrl_methods) { ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ; } else if (parm->methods == &uss820dci_device_bulk_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &uss820dci_device_intr_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else if (parm->methods == &uss820dci_device_isoc_fs_methods) { ntd = xfer->nframes + 1 /* SYNC */ ; } else { ntd = 0; } /* * check if "usb2_transfer_setup_sub" set an error */ if (parm->err) { return; } /* * allocate transfer descriptors */ last_obj = NULL; /* * get profile stuff */ if (ntd) { ep_no = xfer->endpoint & UE_ADDR; uss820dci_get_hw_ep_profile(parm->udev, &pf, ep_no); if (pf == NULL) { /* should not happen */ parm->err = USB_ERR_INVAL; return; } } else { ep_no = 0; pf = NULL; } /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); for (n = 0; n != ntd; n++) { struct uss820dci_td *td; if (parm->buf) { td = USB_ADD_BYTES(parm->buf, parm->size[0]); /* init TD */ td->io_tag = sc->sc_io_tag; td->io_hdl = sc->sc_io_hdl; td->max_packet_size = xfer->max_packet_size; td->rx_stat_reg = USS820_GET_REG(sc, USS820_RXSTAT); td->tx_stat_reg = USS820_GET_REG(sc, USS820_TXSTAT); td->rx_flag_reg = USS820_GET_REG(sc, USS820_RXFLG); td->tx_flag_reg = USS820_GET_REG(sc, USS820_TXFLG); td->rx_fifo_reg = USS820_GET_REG(sc, USS820_RXDAT); td->tx_fifo_reg = USS820_GET_REG(sc, USS820_TXDAT); td->rx_count_low_reg = USS820_GET_REG(sc, USS820_RXCNTL); td->rx_count_high_reg = USS820_GET_REG(sc, USS820_RXCNTH); td->tx_count_low_reg = USS820_GET_REG(sc, USS820_TXCNTL); td->tx_count_high_reg = USS820_GET_REG(sc, USS820_TXCNTH); td->rx_cntl_reg = USS820_GET_REG(sc, USS820_RXCON); td->tx_cntl_reg = USS820_GET_REG(sc, USS820_TXCON); td->pend_reg = USS820_GET_REG(sc, USS820_PEND); td->ep_reg = USS820_GET_REG(sc, USS820_EPINDEX); td->ep_index = ep_no; if (pf->support_multi_buffer && (parm->methods != &uss820dci_device_ctrl_methods)) { td->support_multi_buffer = 1; } td->obj_next = last_obj; last_obj = td; } parm->size[0] += sizeof(*td); } xfer->td_start[0] = last_obj; } static void uss820dci_xfer_unsetup(struct usb2_xfer *xfer) { return; } static void uss820dci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe) { struct uss820dci_softc *sc = USS820_DCI_BUS2SC(udev->bus); DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n", pipe, udev->address, edesc->bEndpointAddress, udev->flags.usb2_mode, sc->sc_rt_addr); if (udev->device_index == sc->sc_rt_addr) { if (udev->flags.usb2_mode != USB_MODE_HOST) { /* not supported */ return; } switch (edesc->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->methods = &uss820dci_root_ctrl_methods; break; case UE_DIR_IN | USS820_DCI_INTR_ENDPT: pipe->methods = &uss820dci_root_intr_methods; break; default: /* do nothing */ break; } } else { if (udev->flags.usb2_mode != USB_MODE_DEVICE) { /* not supported */ return; } if (udev->speed != USB_SPEED_FULL) { /* not supported */ return; } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: pipe->methods = &uss820dci_device_ctrl_methods; break; case UE_INTERRUPT: pipe->methods = &uss820dci_device_intr_methods; break; case UE_ISOCHRONOUS: pipe->methods = &uss820dci_device_isoc_fs_methods; break; case UE_BULK: pipe->methods = &uss820dci_device_bulk_methods; break; default: /* do nothing */ break; } } } struct usb2_bus_methods uss820dci_bus_methods = { .pipe_init = &uss820dci_pipe_init, .xfer_setup = &uss820dci_xfer_setup, .xfer_unsetup = &uss820dci_xfer_unsetup, .do_poll = &uss820dci_do_poll, .get_hw_ep_profile = &uss820dci_get_hw_ep_profile, .set_stall = &uss820dci_set_stall, .clear_stall = &uss820dci_clear_stall, .rem_wakeup_set = &uss820dci_rem_wakeup_set, }; Index: projects/cambria/sys/dev/usb2/controller/uss820dci_atmelarm.c =================================================================== --- projects/cambria/sys/dev/usb2/controller/uss820dci_atmelarm.c (revision 186459) +++ projects/cambria/sys/dev/usb2/controller/uss820dci_atmelarm.c (revision 186460) @@ -1,247 +1,248 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2008 Hans Petter Selasky * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static device_probe_t uss820_atmelarm_probe; static device_attach_t uss820_atmelarm_attach; static device_detach_t uss820_atmelarm_detach; static device_suspend_t uss820_atmelarm_suspend; static device_resume_t uss820_atmelarm_resume; static device_shutdown_t uss820_atmelarm_shutdown; static device_method_t uss820dci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uss820_atmelarm_probe), DEVMETHOD(device_attach, uss820_atmelarm_attach), DEVMETHOD(device_detach, uss820_atmelarm_detach), DEVMETHOD(device_suspend, uss820_atmelarm_suspend), DEVMETHOD(device_resume, uss820_atmelarm_resume), DEVMETHOD(device_shutdown, uss820_atmelarm_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), {0, 0} }; static driver_t uss820dci_driver = { .name = "uss820", .methods = uss820dci_methods, .size = sizeof(struct uss820dci_softc), }; static devclass_t uss820dci_devclass; DRIVER_MODULE(uss820, atmelarm, uss820dci_driver, uss820dci_devclass, 0, 0); MODULE_DEPEND(uss820, usb2_controller, 1, 1, 1); MODULE_DEPEND(uss820, usb2_core, 1, 1, 1); static const char *const uss820_desc = "USS820 USB Device Controller"; static int uss820_atmelarm_suspend(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); int err; err = bus_generic_suspend(dev); if (err == 0) { uss820dci_suspend(sc); } return (err); } static int uss820_atmelarm_resume(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); int err; uss820dci_resume(sc); err = bus_generic_resume(dev); return (err); } static int uss820_atmelarm_shutdown(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); int err; err = bus_generic_shutdown(dev); if (err) return (err); uss820dci_uninit(sc); return (0); } static int uss820_atmelarm_probe(device_t dev) { device_set_desc(dev, uss820_desc); return (0); /* success */ } static int uss820_atmelarm_attach(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); int err; int rid; if (sc == NULL) { return (ENXIO); } /* get all DMA memory */ + sc->sc_bus.parent = dev; if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), NULL)) { return (ENOMEM); } rid = 0; sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (!sc->sc_io_res) { goto error; } sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); sc->sc_io_size = rman_get_size(sc->sc_io_res); /* multiply all addresses by 4 */ sc->sc_reg_shift = 2; rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sc_irq_res == NULL) { goto error; } sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!(sc->sc_bus.bdev)) { goto error; } device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); err = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_bus.bus_mtx, NULL, 0, 4); if (err) { device_printf(dev, "could not setup config thread!\n"); goto error; } #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl); #endif if (err) { sc->sc_intr_hdl = NULL; goto error; } err = uss820dci_init(sc); if (err) { device_printf(dev, "Init failed\n"); goto error; } err = device_probe_and_attach(sc->sc_bus.bdev); if (err) { device_printf(dev, "USB probe and attach failed\n"); goto error; } return (0); error: uss820_atmelarm_detach(dev); return (ENXIO); } static int uss820_atmelarm_detach(device_t dev) { struct uss820dci_softc *sc = device_get_softc(dev); device_t bdev; int err; if (sc->sc_bus.bdev) { bdev = sc->sc_bus.bdev; device_detach(bdev); device_delete_child(dev, bdev); } /* during module unload there are lots of children leftover */ device_delete_all_children(dev); if (sc->sc_irq_res && sc->sc_intr_hdl) { /* * only call at91_udp_uninit() after at91_udp_init() */ uss820dci_uninit(sc); err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); sc->sc_intr_hdl = NULL; } if (sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); sc->sc_irq_res = NULL; } if (sc->sc_io_res) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->sc_io_res); sc->sc_io_res = NULL; } usb2_config_td_unsetup(&sc->sc_config_td); usb2_bus_mem_free_all(&sc->sc_bus, NULL); return (0); } Index: projects/cambria/sys/dev/usb2/core/usb2_transfer.c =================================================================== --- projects/cambria/sys/dev/usb2/core/usb2_transfer.c (revision 186459) +++ projects/cambria/sys/dev/usb2/core/usb2_transfer.c (revision 186460) @@ -1,2794 +1,2786 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #define USB_DEBUG_VAR usb2_debug #include #include #include #include #include #include #include #include #include struct usb2_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; /* * This table stores the all the allowed packet sizes based on * endpoint type and USB speed: */ static const struct usb2_std_packet_size usb2_std_packet_size[4][USB_SPEED_MAX] = { [UE_INTERRUPT] = { [USB_SPEED_LOW] = {.range = {0, 8}}, [USB_SPEED_FULL] = {.range = {0, 64}}, [USB_SPEED_HIGH] = {.range = {0, 1024}}, [USB_SPEED_VARIABLE] = {.range = {0, 1024}}, }, [UE_CONTROL] = { [USB_SPEED_LOW] = {.fixed = {8, 8, 8, 8}}, [USB_SPEED_FULL] = {.fixed = {8, 16, 32, 64}}, [USB_SPEED_HIGH] = {.fixed = {64, 64, 64, 64}}, [USB_SPEED_VARIABLE] = {.fixed = {512, 512, 512, 512}}, }, [UE_BULK] = { [USB_SPEED_LOW] = {.fixed = {0, 0, 0, 0}}, /* invalid */ [USB_SPEED_FULL] = {.fixed = {8, 16, 32, 64}}, [USB_SPEED_HIGH] = {.fixed = {512, 512, 512, 512}}, [USB_SPEED_VARIABLE] = {.fixed = {512, 512, 1024, 1536}}, }, [UE_ISOCHRONOUS] = { [USB_SPEED_LOW] = {.fixed = {0, 0, 0, 0}}, /* invalid */ [USB_SPEED_FULL] = {.range = {0, 1023}}, [USB_SPEED_HIGH] = {.range = {0, 1024}}, [USB_SPEED_VARIABLE] = {.range = {0, 3584}}, }, }; static const struct usb2_config usb2_control_ep_cfg[USB_DEFAULT_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .mh.bufsize = 1024, /* bytes */ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, .mh.callback = &usb2_do_request_callback, .md.bufsize = 1024, /* bytes */ .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 0,}, .md.callback = &usb2_handle_request_callback, }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &usb2_do_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; /* function prototypes */ static void usb2_update_max_frame_size(struct usb2_xfer *); static uint32_t usb2_get_dma_delay(struct usb2_bus *); static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *, uint8_t); static void usb2_control_transfer_init(struct usb2_xfer *); static uint8_t usb2_start_hardware_sub(struct usb2_xfer *); static void usb2_callback_proc(struct usb2_proc_msg *); static void usb2_callback_ss_done_defer(struct usb2_xfer *); static void usb2_callback_wrapper(struct usb2_xfer_queue *); static void usb2_dma_delay_done_cb(void *); static void usb2_transfer_start_cb(void *); static uint8_t usb2_callback_wrapper_sub(struct usb2_xfer *); /*------------------------------------------------------------------------* * usb2_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usb2_update_max_frame_size(struct usb2_xfer *xfer) { /* compute maximum frame size */ if (xfer->max_packet_count == 2) { xfer->max_frame_size = 2 * xfer->max_packet_size; } else if (xfer->max_packet_count == 3) { xfer->max_frame_size = 3 * xfer->max_packet_size; } else { xfer->max_frame_size = xfer->max_packet_size; } } /*------------------------------------------------------------------------* * usb2_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ static uint32_t usb2_get_dma_delay(struct usb2_bus *bus) { uint32_t temp = 0; if (bus->methods->get_dma_delay) { (bus->methods->get_dma_delay) (bus, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usb2_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ uint8_t usb2_transfer_setup_sub_malloc(struct usb2_setup_params *parm, struct usb2_page_cache **ppc, uint32_t size, uint32_t align, uint32_t count) { struct usb2_page_cache *pc; struct usb2_page *pg; void *buf; uint32_t n_dma_pc; uint32_t n_obj; uint32_t x; uint32_t y; uint32_t r; uint32_t z; USB_ASSERT(align > 1, ("Invalid alignment, 0x%08x!\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0!\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (size >= PAGE_SIZE) { n_dma_pc = count; n_obj = 1; } else { /* compute number of objects per page */ n_obj = (PAGE_SIZE / size); /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = ((count + n_obj - 1) / n_obj); } if (parm->buf == NULL) { /* for the future */ parm->dma_page_ptr += n_dma_pc; parm->dma_page_cache_ptr += n_dma_pc; parm->dma_page_ptr += count; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->usb2_root->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->usb2_root->dma_parent_tag; } if (ppc) { *ppc = parm->xfer_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb2_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and one page */ parm->dma_page_cache_ptr++; pg++; for (y = 0; (y != n_obj); y++, r--, pc++, pg++) { /* Load sub-chunk into DMA */ if (usb2_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb2_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } /*------------------------------------------------------------------------* * usb2_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb2_xfer" * structure. *------------------------------------------------------------------------*/ void usb2_transfer_setup_sub(struct usb2_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb2_xfer *xfer = parm->curr_xfer; const struct usb2_config_sub *setup_sub = parm->curr_setup_sub; struct usb2_endpoint_descriptor *edesc; struct usb2_std_packet_size std_size; uint32_t n_frlengths; uint32_t n_frbuffers; uint32_t x; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->pipe->edesc; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup_sub->flags; xfer->nframes = setup_sub->frames; xfer->timeout = setup_sub->timeout; xfer->callback = setup_sub->callback; xfer->interval = setup_sub->interval; xfer->endpoint = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb2_mode = parm->udev->flags.usb2_mode; parm->bufsize = setup_sub->bufsize; if (parm->speed == USB_SPEED_HIGH) { xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; xfer->max_packet_size &= 0x7FF; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ std_size = usb2_std_packet_size[type][parm->speed]; if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* compute "max_frame_size" */ usb2_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint32_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } if (parm->speed == USB_SPEED_HIGH) { frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; } else { frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * if a value is specified use that else check the endpoint * descriptor */ if (xfer->interval == 0) { if (type == UE_INTERRUPT) { xfer->interval = edesc->bInterval; if (parm->speed == USB_SPEED_HIGH) { xfer->interval /= 8; /* 125us -> 1ms */ } if (xfer->interval == 0) { /* * one millisecond is the smallest * interval */ xfer->interval = 1; } } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to alot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usb2_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* setup "frlengths" */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usb2_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usb2_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb2_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_usb2_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_usb2_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->usb2_root->dma_parent_tag; if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb2_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_usb2_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } /*------------------------------------------------------------------------* * usb2_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usb2_transfer_unsetup" after calling * "usb2_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb2_error_t usb2_transfer_setup(struct usb2_device *udev, const uint8_t *ifaces, struct usb2_xfer **ppxfer, const struct usb2_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx) { struct usb2_xfer dummy; struct usb2_setup_params parm; const struct usb2_config *setup_end = setup_start + n_setup; const struct usb2_config *setup; struct usb2_pipe *pipe; struct usb2_xfer_root *info; struct usb2_xfer *xfer; void *buf = NULL; uint16_t n; uint16_t refcount; parm.err = 0; refcount = 0; info = NULL; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usb2_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == 0) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (priv_mtx == NULL) { DPRINTFN(6, "using global lock\n"); priv_mtx = &Giant; } /* sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if ((setup->mh.bufsize == 0xffffffff) || (setup->md.bufsize == 0xffffffff)) { parm.err = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if ((setup->mh.callback == NULL) && (setup->md.callback == NULL)) { parm.err = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (parm.err) { goto done; } bzero(&parm, sizeof(parm)); parm.udev = udev; parm.speed = usb2_get_speed(udev); parm.hc_max_packet_count = 1; if (parm.speed >= USB_SPEED_MAX) { parm.err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb2_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm.size[0]; info->dma_page_cache_start = USB_ADD_BYTES(buf, parm.size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm.size[5]); info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm.size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm.size[2]); usb2_cv_init(&info->cv_drain, "WDRAIN"); info->priv_mtx = priv_mtx; usb2_dma_tag_setup(&info->dma_parent_tag, parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, priv_mtx, &usb2_bdma_done_event, info, 32, parm.dma_tag_max); info->bus = udev->bus; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usb2_callback_wrapper; TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb2_bdma_work_loop; info->done_m[0].hdr.pm_callback = &usb2_callback_proc; info->done_m[0].usb2_root = info; info->done_m[1].hdr.pm_callback = &usb2_callback_proc; info->done_m[1].usb2_root = info; /* create a callback thread */ if (usb2_proc_setup(&info->done_p, &udev->bus->bus_mtx, USB_PRI_HIGH)) { parm.err = USB_ERR_NO_INTR_THREAD; goto done; } } /* reset sizes */ parm.size[0] = 0; parm.buf = buf; parm.size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* select mode specific structure */ if (udev->flags.usb2_mode == USB_MODE_HOST) { parm.curr_setup_sub = &setup->mh; } else { parm.curr_setup_sub = &setup->md; } /* skip USB transfers without callbacks: */ if (parm.curr_setup_sub->callback == NULL) { continue; } /* see if there is a matching endpoint */ pipe = usb2_get_pipe(udev, ifaces[setup->if_index], setup); if (!pipe) { if (parm.curr_setup_sub->flags.no_pipe_ok) { continue; } parm.err = USB_ERR_NO_PIPE; goto done; } /* store current setup pointer */ parm.curr_setup = setup; /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); if (buf) { /* * Common initialization of the * "usb2_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm.size[0]); ppxfer[n] = xfer; xfer->udev = udev; xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xfer_mtx = priv_mtx; xfer->usb2_root = info; info->setup_refcount++; usb2_callout_init_mtx(&xfer->timeout_handle, - &udev->bus->bus_mtx, CALLOUT_RETURNUNLOCKED); + &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb2_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &dummy; bzero(&dummy, sizeof(dummy)); refcount++; } parm.size[0] += sizeof(xfer[0]); xfer->pipe = pipe; if (buf) { /* * Increment the pipe refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "pipe->refcount" if you * want more information. */ xfer->pipe->refcount++; } parm.methods = xfer->pipe->methods; parm.curr_xfer = xfer; /* * Call the Host or Device controller transfer setup * routine: */ (udev->bus->methods->xfer_setup) (&parm); if (parm.err) { goto done; } } if (buf || parm.err) { goto done; } if (refcount == 0) { /* no transfers - nothing to do ! */ goto done; } /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[1] = parm.size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is two. */ parm.dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm.dma_tag_max += 8; parm.dma_tag_p += parm.dma_tag_max; parm.size[0] += ((uint8_t *)parm.dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[3] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[4] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm.size[5] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm.size[2] = parm.size[0]; /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); parm.size[6] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm.err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm.size[0]); goto done; } parm.dma_tag_p = USB_ADD_BYTES(buf, parm.size[1]); parm.dma_page_ptr = USB_ADD_BYTES(buf, parm.size[3]); parm.dma_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[4]); parm.xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[5]); parm.xfer_length_ptr = USB_ADD_BYTES(buf, parm.size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usb2_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usb2_transfer_unsetup_sub(info, 0); } } if (parm.err) { usb2_transfer_unsetup(ppxfer, n_setup); } return (parm.err); } /*------------------------------------------------------------------------* * usb2_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *info, uint8_t needs_delay) { struct usb2_page_cache *pc; uint32_t temp; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { temp = usb2_get_dma_delay(info->bus); usb2_pause_mtx(&info->bus->bus_mtx, temp); } USB_BUS_UNLOCK(info->bus); /* wait for interrupt thread to exit */ usb2_proc_unsetup(&info->done_p); /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb2_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb2_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb2_dma_tag_unsetup(&info->dma_parent_tag); usb2_cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usb2_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usb2_transfer_unsetup(struct usb2_xfer **pxfer, uint16_t n_setup) { struct usb2_xfer *xfer; struct usb2_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usb2_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer) { if (xfer->pipe) { USB_XFER_LOCK(xfer); USB_BUS_LOCK(xfer->udev->bus); /* * HINT: when you start/stop a transfer, it * might be a good idea to directly use the * "pxfer[]" structure: * * usb2_transfer_start(sc->pxfer[0]); * usb2_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that * will not stop running under the same * lock, in other words "priv_mtx", the * usb2_transfer_start and * usb2_transfer_stop functions will simply * return when they detect a NULL pointer * argument. * * To avoid any races we clear the "pxfer[]" * pointer while holding the private mutex * of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(xfer->udev->bus); USB_XFER_UNLOCK(xfer); usb2_transfer_drain(xfer); if (xfer->flags_int.bdma_enable) { needs_delay = 1; } /* * NOTE: default pipe does not have an * interface, even if pipe->iface_index == 0 */ xfer->pipe->refcount--; } else { /* clear the transfer pointer */ pxfer[n_setup] = NULL; } usb2_callout_drain(&xfer->timeout_handle); if (xfer->usb2_root) { info = xfer->usb2_root; USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count!\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usb2_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } } } /*------------------------------------------------------------------------* * usb2_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb2_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usb2_control_transfer_init(struct usb2_xfer *xfer) { struct usb2_device_request req; /* copy out the USB request header */ usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpoint &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpoint |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usb2_start_hardware_sub * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t usb2_start_hardware_sub(struct usb2_xfer *xfer) { uint32_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe) { /* no longer active */ xfer->flags_int.control_act = 0; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { usb2_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb2_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb2_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usb2_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usb2_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb2_device_request)); } /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length greater than remaining length!\n"); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set!\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usb2_start_hardware - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usb2_start_hardware(struct usb2_xfer *xfer) { uint32_t x; DPRINTF("xfer=%p, pipe=%p, nframes=%d, dir=%s\n", xfer, xfer->pipe, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #if USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(xfer->udev->bus); usb2_dump_pipe(xfer->pipe); USB_BUS_UNLOCK(xfer->udev->bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(xfer->udev->bus); (xfer->pipe->methods->open) (xfer); USB_BUS_UNLOCK(xfer->udev->bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(xfer->udev->bus); usb2_transfer_dequeue(xfer); USB_BUS_UNLOCK(xfer->udev->bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(xfer->udev->bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb2_command_wrapper(&xfer->pipe->pipe_q, xfer); USB_BUS_UNLOCK(xfer->udev->bus); return; } USB_BUS_LOCK(xfer->udev->bus); usb2_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(xfer->udev->bus); return; } /* compute total transfer length */ for (x = 0; x != xfer->nframes; x++) { xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(xfer->udev->bus); usb2_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(xfer->udev->bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usb2_start_hardware_sub(xfer)) { USB_BUS_LOCK(xfer->udev->bus); usb2_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(xfer->udev->bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags_int.control_xfr) { /* * Control transfers do not support reception * of multiple short USB frames ! */ if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; } } else { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb2_command_wrapper(&xfer->usb2_root->dma_q, xfer); return; } /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usb2_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usb2_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usb2_pipe_enter(struct usb2_xfer *xfer) { struct usb2_pipe *pipe; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->udev->bus); pipe = xfer->pipe; DPRINTF("enter\n"); /* enter the transfer */ (pipe->methods->enter) (xfer); /* check cancelability */ if (pipe->methods->enter_is_cancelable) { xfer->flags_int.can_cancel_immed = 1; /* check for transfer error */ if (xfer->error) { /* some error has happened */ usb2_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->udev->bus); return; } } else { xfer->flags_int.can_cancel_immed = 0; } /* start the transfer */ usb2_command_wrapper(&pipe->pipe_q, xfer); USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usb2_transfer_start(struct usb2_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { xfer->flags_int.started = 1; } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->udev->bus); /* call the USB transfer callback */ usb2_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usb2_transfer_drain()". *------------------------------------------------------------------------*/ void usb2_transfer_stop(struct usb2_xfer *xfer) { struct usb2_pipe *pipe; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { /* nothing to do except clearing the "started" flag */ xfer->flags_int.started = 0; return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->udev->bus); xfer->error = USB_ERR_CANCELLED;/* override any previous error */ /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->pipe->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->pipe->methods->close) (xfer); /* * Any additional DMA delay is done by * "usb2_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * pipe. */ pipe = xfer->pipe; /* * If the current USB transfer is completing we need * to start the next one: */ if (pipe->pipe_q.curr == xfer) { usb2_command_wrapper(&pipe->pipe_q, NULL); } } USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usb2_transfer_pending(struct usb2_xfer *xfer) { struct usb2_xfer_root *info; struct usb2_xfer_queue *pq; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->udev->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->udev->bus); return (1); } info = xfer->usb2_root; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->udev->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->udev->bus); return (0); } /*------------------------------------------------------------------------* * usb2_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usb2_transfer_drain(struct usb2_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usb2_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usb2_transfer_stop(xfer); while (usb2_transfer_pending(xfer)) { xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ usb2_cv_wait(&xfer->usb2_root->cv_drain, xfer->xfer_mtx); } USB_XFER_UNLOCK(xfer); } /*------------------------------------------------------------------------* * usb2_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usb2_set_frame_data(struct usb2_xfer *xfer, void *ptr, uint32_t frindex) { /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; } /*------------------------------------------------------------------------* * usb2_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usb2_set_frame_offset(struct usb2_xfer *xfer, uint32_t offset, uint32_t frindex) { USB_ASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external!\n")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } /*------------------------------------------------------------------------* * usb2_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb2_callback_proc(struct usb2_proc_msg *_pm) { struct usb2_done_msg *pm = (void *)_pm; struct usb2_xfer_root *info = pm->usb2_root; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->priv_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb2_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->priv_mtx); } /*------------------------------------------------------------------------* * usb2_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usb2_callback_ss_done_defer(struct usb2_xfer *xfer) { struct usb2_xfer_root *info = xfer->usb2_root; struct usb2_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); if (pq->curr != xfer) { usb2_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb2_proc_msignal(&info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usb2_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usb2_callback_wrapper(struct usb2_xfer_queue *pq) { struct usb2_xfer *xfer = pq->curr; struct usb2_xfer_root *info = xfer->usb2_root; USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); if (!mtx_owned(xfer->xfer_mtx)) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. */ DPRINTFN(3, "case 5\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb2_proc_msignal(&info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; USB_BUS_UNLOCK(xfer->udev->bus); USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb2_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(xfer->udev->bus); goto done; } } else { if (usb2_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(xfer->udev->bus); goto done; } xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb2_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb2_state = USB_ST_TRANSFERRED; /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb2_bdma_post_sync(xfer); } } } /* call processing routine */ (xfer->callback) (xfer); /* pickup the USB mutex again */ USB_BUS_LOCK(xfer->udev->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. Check if we are * draining. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb2_state == USB_ST_ERROR)) { /* try to loop, but not recursivly */ usb2_command_wrapper(&info->done_q, xfer); return; } else if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usb2_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; usb2_cv_broadcast(&xfer->usb2_root->cv_drain); } done: /* do the next callback, if any */ usb2_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb2_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is ususally only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ static void usb2_dma_delay_done_cb(void *arg) { struct usb2_xfer *xfer = arg; USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usb2_transfer_done(xfer, 0); - - USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usb2_transfer_dequeue(struct usb2_xfer *xfer) { struct usb2_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usb2_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usb2_transfer_enqueue(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usb2_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usb2_transfer_done(struct usb2_xfer *xfer, usb2_error_t error) { struct usb2_xfer_queue *pq; USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); DPRINTF("err=%s\n", usb2_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); return; } /* only set transfer error if not already set */ if (!xfer->error) { xfer->error = error; } /* stop any callouts */ usb2_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usb2_transfer_dequeue(xfer); if (mtx_owned(xfer->xfer_mtx)) { /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &xfer->usb2_root->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb2_command_wrapper(pq, NULL); } } /* keep some statistics */ if (xfer->error) { xfer->udev->bus->stats_err.uds_requests [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; } else { xfer->udev->bus->stats_ok.uds_requests [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usb2_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usb2_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usb2_transfer_start_cb(void *arg) { struct usb2_xfer *xfer = arg; struct usb2_pipe *pipe = xfer->pipe; USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); DPRINTF("start\n"); /* start the transfer */ (pipe->methods->start) (xfer); /* check cancelability */ if (pipe->methods->start_is_cancelable) { xfer->flags_int.can_cancel_immed = 1; if (xfer->error) { /* some error has happened */ usb2_transfer_done(xfer, 0); } } else { xfer->flags_int.can_cancel_immed = 0; } - USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_transfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_transfer_set_stall(struct usb2_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->udev->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usb2_transfer_clear_stall(struct usb2_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->udev->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->udev->bus); } /*------------------------------------------------------------------------* * usb2_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usb2_pipe_start(struct usb2_xfer_queue *pq) { struct usb2_pipe *pipe; struct usb2_xfer *xfer; uint8_t type; xfer = pq->curr; pipe = xfer->pipe; USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* * If the pipe is already stalled we do nothing ! */ if (pipe->is_stalled) { return; } /* * Check if we are supposed to stall the pipe: */ if (xfer->flags.stall_pipe) { /* clear stall command */ xfer->flags.stall_pipe = 0; /* * Only stall BULK and INTERRUPT endpoints. */ type = (pipe->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { struct usb2_device *udev; struct usb2_xfer_root *info; udev = xfer->udev; pipe->is_stalled = 1; if (udev->flags.usb2_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, NULL, pipe); } else if (udev->default_xfer[1]) { info = udev->default_xfer[1]->usb2_root; if (usb2_proc_msignal(&info->done_p, &udev->cs_msg[0], &udev->cs_msg[1])) { /* ignore */ } } else { /* should not happen */ DPRINTFN(0, "No stall handler!\n"); } /* * We get started again when the stall is cleared! */ return; } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usb2_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (pipe->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usb2_transfer_timeout_ms(xfer, &usb2_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); /* start USB transfer */ (pipe->methods->start) (xfer); /* check cancelability */ if (pipe->methods->start_is_cancelable) { xfer->flags_int.can_cancel_immed = 1; if (xfer->error) { /* some error has happened */ usb2_transfer_done(xfer, 0); } } else { xfer->flags_int.can_cancel_immed = 0; } } /*------------------------------------------------------------------------* * usb2_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usb2_transfer_timeout_ms(struct usb2_xfer *xfer, void (*cb) (void *arg), uint32_t ms) { USB_BUS_LOCK_ASSERT(xfer->udev->bus, MA_OWNED); /* defer delay */ usb2_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms), cb, xfer); } /*------------------------------------------------------------------------* * usb2_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * pipe transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usb2_callback_wrapper_sub(struct usb2_xfer *xfer) { struct usb2_pipe *pipe; uint32_t x; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(xfer->udev->bus); (xfer->pipe->methods->close) (xfer); USB_BUS_UNLOCK(xfer->udev->bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (((xfer->error == USB_ERR_CANCELLED) || (xfer->error == USB_ERR_TIMEOUT)) && (!xfer->flags_int.did_dma_delay)) { uint32_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usb2_get_dma_delay(xfer->udev->bus); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(xfer->udev->bus); usb2_transfer_timeout_ms(xfer, &usb2_dma_delay_done_cb, temp); USB_BUS_UNLOCK(xfer->udev->bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d!\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { xfer->frlengths[x] = 0; } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d!\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(6, "xfer=%p pipe=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->pipe, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on pipe=%p\n", xfer, xfer->pipe); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on pipe %p.\n", xfer, xfer->pipe); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on pipe=%p\n", xfer, xfer->pipe); goto done; } } } pipe = xfer->pipe; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(xfer->udev->bus); if (pipe->pipe_q.curr == xfer) { usb2_command_wrapper(&pipe->pipe_q, NULL); if (pipe->pipe_q.curr || TAILQ_FIRST(&pipe->pipe_q.head)) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->pipe->is_synced = 0; } } USB_BUS_UNLOCK(xfer->udev->bus); done: return (0); } /*------------------------------------------------------------------------* * usb2_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb2_command_wrapper(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usb2_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { do { /* set both recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usb2_default_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usb2_default_transfer_setup(struct usb2_device *udev) { struct usb2_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; repeat: xfer = udev->default_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->default_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb2_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usb2_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->default_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX); /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usb2_transfer_setup(udev, &iface_index, udev->default_xfer, usb2_control_ep_cfg, USB_DEFAULT_XFER_MAX, NULL, udev->default_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer!\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usb2_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usb2_clear_data_toggle(struct usb2_device *udev, struct usb2_pipe *pipe) { DPRINTFN(5, "udev=%p pipe=%p\n", udev, pipe); USB_BUS_LOCK(udev->bus); pipe->toggle_next = 0; USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usb2_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb2_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb2_device_request), * .mh.timeout = 1000, //1.000 seconds * .mh.flags = { }, * .mh.callback = &my_clear_stall_callback, // ** * }; * * ** "my_clear_stall_callback" calls "usb2_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usb2_clear_stall_callback(struct usb2_xfer *xfer1, struct usb2_xfer *xfer2) { struct usb2_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usb2_clear_data_toggle(xfer2->udev, xfer2->pipe); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->pipe->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usb2_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usb2_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usb2_start_hardware(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } #if (USB_NO_POLL == 0) /*------------------------------------------------------------------------* * usb2_callout_poll *------------------------------------------------------------------------*/ static void usb2_callout_poll(struct usb2_xfer *xfer) { struct usb2_callout *co; void (*cb) (void *); void *arg; struct mtx *mtx; uint32_t delta; if (xfer == NULL) { return; } co = &xfer->timeout_handle; #if __FreeBSD_version >= 800000 mtx = (void *)(co->co.c_lock); #else mtx = co->co.c_mtx; #endif mtx_lock(mtx); if (usb2_callout_pending(co)) { delta = ticks - co->co.c_time; if (!(delta & 0x80000000)) { cb = co->co.c_func; arg = co->co.c_arg; /* timed out */ usb2_callout_stop(co); (cb) (arg); - - /* the callback should drop the mutex */ - } else { - mtx_unlock(mtx); } - } else { - mtx_unlock(mtx); } + mtx_unlock(mtx); } /*------------------------------------------------------------------------* * usb2_do_poll * * This function is called from keyboard driver when in polling * mode. *------------------------------------------------------------------------*/ void usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max) { struct usb2_xfer *xfer; struct usb2_xfer_root *usb2_root; struct usb2_device *udev; struct usb2_proc_msg *pm; uint32_t to; uint16_t n; /* compute system tick delay */ to = ((uint32_t)(1000000)) / ((uint32_t)(hz)); DELAY(to); atomic_add_int((volatile int *)&ticks, 1); for (n = 0; n != max; n++) { xfer = ppxfer[n]; if (xfer) { usb2_root = xfer->usb2_root; udev = xfer->udev; /* * Poll hardware - signal that we are polling by * locking the private mutex: */ USB_XFER_LOCK(xfer); (udev->bus->methods->do_poll) (udev->bus); USB_XFER_UNLOCK(xfer); /* poll clear stall start */ USB_BUS_LOCK(xfer->udev->bus); pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xfer->udev->bus); if (udev->default_xfer[1]) { /* poll timeout */ usb2_callout_poll(udev->default_xfer[1]); /* poll clear stall done thread */ USB_BUS_LOCK(xfer->udev->bus); pm = &udev->default_xfer[1]-> usb2_root->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xfer->udev->bus); } /* poll timeout */ usb2_callout_poll(xfer); /* poll done thread */ USB_BUS_LOCK(xfer->udev->bus); pm = &usb2_root->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xfer->udev->bus); } } } #else void usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max) { /* polling not supported */ } #endif Index: projects/cambria/sys/dev/usb2/ethernet/if_aue2.c =================================================================== --- projects/cambria/sys/dev/usb2/ethernet/if_aue2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/ethernet/if_aue2.c (revision 186460) @@ -1,1547 +1,1542 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver. * Datasheet is available from http://www.admtek.com.tw. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet * support: the control endpoint for reading/writing registers, burst * read endpoint for packet reception, burst write for packet transmission * and one for "interrupts." The chip uses the same RX filter scheme * as the other ADMtek ethernet parts: one perfect filter entry for the * the station address and a 64-bit multicast hash table. The chip supports * both MII and HomePNA attachments. * * Since the maximum data transfer speed of USB is supposed to be 12Mbps, * you're never really going to get 100Mbps speeds from this device. I * think the idea is to allow the device to connect to 10 or 100Mbps * networks, not necessarily to provide 100Mbps performance. Also, since * the controller uses an external PHY chip, it's possible that board * designers might simply choose a 10Mbps PHY. * * Registers are accessed using usb2_do_request(). Packet transfers are * done using usb2_transfer() and friends. */ /* * NOTE: all function names beginning like "aue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc usb2_ether_cc #define usb2_config_td_softc aue_softc #define USB_DEBUG_VAR aue_debug #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(aue, usb2_ethernet, 1, 1, 1); MODULE_DEPEND(aue, usb2_core, 1, 1, 1); MODULE_DEPEND(aue, ether, 1, 1, 1); MODULE_DEPEND(aue, miibus, 1, 1, 1); #if USB_DEBUG static int aue_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue"); SYSCTL_INT(_hw_usb2_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const struct usb2_device_id aue_devs[] = { {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1, AUE_FLAG_PNA | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4, AUE_FLAG_PNA)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5, AUE_FLAG_PNA)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9, AUE_FLAG_PNA)}, {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC, 0)}, {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY)}, {USB_VPI(USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100, 0)}, {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100, 0)}, {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100, AUE_FLAG_PNA)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX, 0)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA, AUE_FLAG_PNA)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN, AUE_FLAG_PNA | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0, 0)}, {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2, 0)}, {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W, 0)}, {USB_VPI(USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HN210E, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX, 0)}, {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX, 0)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, AUE_FLAG_LSYS)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5, 0)}, {USB_VPI(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB, 0)}, {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB, AUE_FLAG_PII)}, {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100, 0)}, {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110, AUE_FLAG_PII)}, }; /* prototypes */ static device_probe_t aue_probe; static device_attach_t aue_attach; static device_detach_t aue_detach; static device_shutdown_t aue_shutdown; static usb2_callback_t aue_intr_clear_stall_callback; static usb2_callback_t aue_intr_callback; static usb2_callback_t aue_bulk_read_clear_stall_callback; static usb2_callback_t aue_bulk_read_callback; static usb2_callback_t aue_bulk_write_clear_stall_callback; static usb2_callback_t aue_bulk_write_callback; static void aue_cfg_do_request(struct aue_softc *, struct usb2_device_request *, void *); static uint8_t aue_cfg_csr_read_1(struct aue_softc *, uint16_t); static uint16_t aue_cfg_csr_read_2(struct aue_softc *, uint16_t); static void aue_cfg_csr_write_1(struct aue_softc *, uint16_t, uint8_t); static void aue_cfg_csr_write_2(struct aue_softc *, uint16_t, uint16_t); static void aue_cfg_eeprom_getword(struct aue_softc *, uint8_t, uint8_t *); static void aue_cfg_read_eeprom(struct aue_softc *, uint8_t *, uint16_t, uint16_t); static miibus_readreg_t aue_cfg_miibus_readreg; static miibus_writereg_t aue_cfg_miibus_writereg; static miibus_statchg_t aue_cfg_miibus_statchg; static usb2_config_td_command_t aue_cfg_setmulti; static usb2_config_td_command_t aue_cfg_first_time_setup; static usb2_config_td_command_t aue_config_copy; static usb2_config_td_command_t aue_cfg_tick; static usb2_config_td_command_t aue_cfg_pre_init; static usb2_config_td_command_t aue_cfg_init; static usb2_config_td_command_t aue_cfg_promisc_upd; static usb2_config_td_command_t aue_cfg_ifmedia_upd; static usb2_config_td_command_t aue_cfg_pre_stop; static usb2_config_td_command_t aue_cfg_stop; static void aue_cfg_reset_pegasus_II(struct aue_softc *); static void aue_cfg_reset(struct aue_softc *); static void aue_start_cb(struct ifnet *); static void aue_init_cb(void *); static void aue_start_transfers(struct aue_softc *); static int aue_ifmedia_upd_cb(struct ifnet *); static void aue_ifmedia_sts_cb(struct ifnet *, struct ifmediareq *); static int aue_ioctl_cb(struct ifnet *, u_long, caddr_t); static void aue_watchdog(void *); static const struct usb2_config aue_config[AUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (MCLBYTES + 2), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &aue_bulk_write_callback, .mh.timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &aue_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &aue_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &aue_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [4] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.bufsize = 0, /* use wMaxPacketSize */ .mh.callback = &aue_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &aue_intr_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static device_method_t aue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aue_probe), DEVMETHOD(device_attach, aue_attach), DEVMETHOD(device_detach, aue_detach), DEVMETHOD(device_shutdown, aue_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, aue_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, aue_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, aue_cfg_miibus_statchg), {0, 0} }; static driver_t aue_driver = { .name = "aue", .methods = aue_methods, .size = sizeof(struct aue_softc) }; static devclass_t aue_devclass; DRIVER_MODULE(aue, ushub, aue_driver, aue_devclass, NULL, 0); DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0); static void aue_cfg_do_request(struct aue_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } #define AUE_CFG_SETBIT(sc, reg, x) \ aue_cfg_csr_write_1(sc, reg, aue_cfg_csr_read_1(sc, reg) | (x)) #define AUE_CFG_CLRBIT(sc, reg, x) \ aue_cfg_csr_write_1(sc, reg, aue_cfg_csr_read_1(sc, reg) & ~(x)) static uint8_t aue_cfg_csr_read_1(struct aue_softc *sc, uint16_t reg) { struct usb2_device_request req; uint8_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = AUE_UR_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); aue_cfg_do_request(sc, &req, &val); return (val); } static uint16_t aue_cfg_csr_read_2(struct aue_softc *sc, uint16_t reg) { struct usb2_device_request req; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = AUE_UR_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 2); aue_cfg_do_request(sc, &req, &val); return (le16toh(val)); } static void aue_cfg_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AUE_UR_WRITEREG; req.wValue[0] = val; req.wValue[1] = 0; USETW(req.wIndex, reg); USETW(req.wLength, 1); aue_cfg_do_request(sc, &req, &val); } static void aue_cfg_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = AUE_UR_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 2); val = htole16(val); aue_cfg_do_request(sc, &req, &val); } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void aue_cfg_eeprom_getword(struct aue_softc *sc, uint8_t addr, uint8_t *dest) { uint16_t i; aue_cfg_csr_write_1(sc, AUE_EE_REG, addr); aue_cfg_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); for (i = 0;; i++) { if (i < AUE_TIMEOUT) { if (aue_cfg_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { DPRINTF("EEPROM read timed out!\n"); break; } } i = aue_cfg_csr_read_2(sc, AUE_EE_DATA); dest[0] = (i & 0xFF); dest[1] = (i >> 8); } /* * Read a sequence of words from the EEPROM. */ static void aue_cfg_read_eeprom(struct aue_softc *sc, uint8_t *dest, uint16_t off, uint16_t len) { uint16_t i; for (i = 0; i < len; i++) { aue_cfg_eeprom_getword(sc, off + i, dest + (i * 2)); } } static int aue_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct aue_softc *sc = device_get_softc(dev); uint16_t i; uint8_t do_unlock; /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } /* * The Am79C901 HomePNA PHY actually contains * two transceivers: a 1Mbps HomePNA PHY and a * 10Mbps full/half duplex ethernet PHY with * NWAY autoneg. However in the ADMtek adapter, * only the 1Mbps PHY is actually connected to * anything, so we ignore the 10Mbps one. It * happens to be configured for MII address 3, * so we filter that out. */ if (sc->sc_flags & AUE_FLAG_DUAL_PHY) { if (phy == 3) { i = 0; goto done; } #if 0 if (phy != 1) { i = 0; goto done; } #endif } aue_cfg_csr_write_1(sc, AUE_PHY_ADDR, phy); aue_cfg_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); for (i = 0;; i++) { if (i < AUE_TIMEOUT) { if (aue_cfg_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { DPRINTF("MII read timed out\n"); break; } } i = aue_cfg_csr_read_2(sc, AUE_PHY_DATA); done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (i); } static int aue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) { struct aue_softc *sc = device_get_softc(dev); uint16_t i; uint8_t do_unlock; if (phy == 3) { return (0); } /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } aue_cfg_csr_write_2(sc, AUE_PHY_DATA, data); aue_cfg_csr_write_1(sc, AUE_PHY_ADDR, phy); aue_cfg_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); for (i = 0;; i++) { if (i < AUE_TIMEOUT) { if (aue_cfg_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { DPRINTF("MII write timed out\n"); break; } } if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (0); } static void aue_cfg_miibus_statchg(device_t dev) { struct aue_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); uint8_t do_unlock; /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } AUE_CFG_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); } else { AUE_CFG_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL); } if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); } else { AUE_CFG_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX); } AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB); /* * Set the LED modes on the LinkSys adapter. * This turns on the 'dual link LED' bin in the auxmode * register of the Broadcom PHY. */ if (sc->sc_flags & AUE_FLAG_LSYS) { uint16_t auxmode; auxmode = aue_cfg_miibus_readreg(dev, 0, 0x1b); aue_cfg_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04); } if (do_unlock) { mtx_unlock(&sc->sc_mtx); } } static void aue_cfg_setmulti(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t i; if ((cc->if_flags & IFF_ALLMULTI) || (cc->if_flags & IFF_PROMISC)) { AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); return; } AUE_CFG_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI); /* clear existing ones */ for (i = 0; i < 8; i++) { aue_cfg_csr_write_1(sc, AUE_MAR0 + i, 0); } /* now program new ones */ for (i = 0; i < 8; i++) { aue_cfg_csr_write_1(sc, AUE_MAR0 + i, cc->if_hash[i]); } } static void aue_cfg_reset_pegasus_II(struct aue_softc *sc) { /* Magic constants taken from Linux driver. */ aue_cfg_csr_write_1(sc, AUE_REG_1D, 0); aue_cfg_csr_write_1(sc, AUE_REG_7B, 2); #if 0 if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode) aue_cfg_csr_write_1(sc, AUE_REG_81, 6); else #endif aue_cfg_csr_write_1(sc, AUE_REG_81, 2); } static void aue_cfg_reset(struct aue_softc *sc) { uint16_t i; AUE_CFG_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); for (i = 0;; i++) { if (i < AUE_TIMEOUT) { if (!(aue_cfg_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { DPRINTF("reset timed out\n"); break; } } /* * The PHY(s) attached to the Pegasus chip may be held * in reset until we flip on the GPIO outputs. Make sure * to set the GPIO pins high so that the PHY(s) will * be enabled. * * Note: We force all of the GPIO pins low first, *then* * enable the ones we want. */ aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_OUT0 | AUE_GPIO_SEL0)); aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_OUT0 | AUE_GPIO_SEL0 | AUE_GPIO_SEL1)); if (sc->sc_flags & AUE_FLAG_LSYS) { /* Grrr. LinkSys has to be different from everyone else. */ aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_SEL0 | AUE_GPIO_SEL1)); aue_cfg_csr_write_1(sc, AUE_GPIO0, (AUE_GPIO_SEL0 | AUE_GPIO_SEL1 | AUE_GPIO_OUT0)); } if (sc->sc_flags & AUE_FLAG_PII) { aue_cfg_reset_pegasus_II(sc); } /* wait a little while for the chip to get its brains in order: */ usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } /* * Probe for a Pegasus chip. */ static int aue_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != AUE_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != AUE_IFACE_IDX) { return (ENXIO); } /* * Belkin USB Bluetooth dongles of the F8T012xx1 model series * conflict with older Belkin USB2LAN adapters. Skip if_aue if * we detect one of the devices that look like Bluetooth * adapters. */ if ((uaa->info.idVendor == USB_VENDOR_BELKIN) && (uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012) && (uaa->info.bcdDevice == 0x0413)) { return (ENXIO); } return (usb2_lookup_id_by_uaa(aue_devs, sizeof(aue_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int aue_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct aue_softc *sc = device_get_softc(dev); int32_t error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); sc->sc_flags = USB_GET_DRIVER_INFO(uaa); if (uaa->info.bcdDevice >= 0x0201) { sc->sc_flags |= AUE_FLAG_VER_2; /* XXX currently undocumented */ } device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_mtx, "aue lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = AUE_IFACE_IDX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, aue_config, AUE_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, NULL, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); sc->sc_flags |= AUE_FLAG_WAIT_LINK; /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &aue_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - aue_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: aue_detach(dev); return (ENXIO); /* failure */ } static void aue_cfg_first_time_setup(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; int error; uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; /* reset the adapter */ aue_cfg_reset(sc); /* set default value */ bzero(eaddr, sizeof(eaddr)); /* get station address from the EEPROM */ aue_cfg_read_eeprom(sc, eaddr, 0, 3); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } sc->sc_evilhack = ifp; ifp->if_softc = sc; if_initname(ifp, "aue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = aue_ioctl_cb; ifp->if_start = aue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = aue_init_cb; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); /* * XXX need Giant when accessing the device structures ! */ mtx_unlock(&sc->sc_mtx); mtx_lock(&Giant); error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, &aue_ifmedia_upd_cb, &aue_ifmedia_sts_cb); mtx_unlock(&Giant); mtx_lock(&sc->sc_mtx); /* * Do MII setup. * NOTE: Doing this causes child devices to be attached to us, * which we would normally disconnect at in the detach routine * using device_delete_child(). However the USB code is set up * such that when this driver is removed, all children devices * are removed as well. In effect, the USB code ends up detaching * all of our children for us, so we don't have to do is ourselves * in aue_detach(). It's important to point this out since if * we *do* try to detach the child devices ourselves, we will * end up getting the children deleted twice, which will crash * the system. */ if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; mtx_unlock(&sc->sc_mtx); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); mtx_lock(&sc->sc_mtx); done: return; } static int aue_detach(device_t dev) { struct aue_softc *sc = device_get_softc(dev); struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); aue_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, AUE_ENDPT_MAX); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void aue_intr_clear_stall_callback(struct usb2_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[4]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~AUE_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); } } static void aue_intr_callback(struct usb2_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct aue_intrpkt pkt; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && (xfer->actlen >= sizeof(pkt))) { usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt)); if (pkt.aue_txstat0) { ifp->if_oerrors++; } if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL & AUE_TXSTAT0_EXCESSCOLL)) { ifp->if_collisions++; } } case USB_ST_SETUP: if (sc->sc_flags & AUE_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer[5]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* start clear stall */ sc->sc_flags |= AUE_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[5]); } return; } } static void aue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~AUE_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void aue_bulk_read_callback(struct usb2_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m = NULL; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "received %d bytes\n", xfer->actlen); if (sc->sc_flags & AUE_FLAG_VER_2) { if (xfer->actlen == 0) { ifp->if_ierrors++; goto tr_setup; } } else { if (xfer->actlen <= (4 + ETHER_CRC_LEN)) { ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, xfer->actlen - 4, &sc->sc_rxpkt, sizeof(sc->sc_rxpkt)); /* * turn off all the non-error bits in the rx status * word: */ sc->sc_rxpkt.aue_rxstat &= AUE_RXSTAT_MASK; if (sc->sc_rxpkt.aue_rxstat) { ifp->if_ierrors++; goto tr_setup; } /* No errors; receive the packet. */ xfer->actlen -= (4 + ETHER_CRC_LEN); } m = usb2_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usb2_copy_out(xfer->frbuffers, 0, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; case USB_ST_SETUP: tr_setup: if (sc->sc_flags & AUE_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "if_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); (ifp->if_input) (ifp, m); mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AUE_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } DPRINTF("bulk read error, %s\n", usb2_errstr(xfer->error)); return; } } static void aue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~AUE_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void aue_bulk_write_callback(struct usb2_xfer *xfer) { struct aue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint8_t buf[2]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer of %d bytes complete\n", xfer->actlen); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & AUE_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & AUE_FLAG_WAIT_LINK) { /* * don't send anything if there is no link ! */ goto done; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } if (sc->sc_flags & AUE_FLAG_VER_2) { xfer->frlengths[0] = m->m_pkthdr.len; usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); } else { xfer->frlengths[0] = (m->m_pkthdr.len + 2); /* * The ADMtek documentation says that the packet length is * supposed to be specified in the first two bytes of the * transfer, however it actually seems to ignore this info * and base the frame size on the bulk transfer length. */ buf[0] = (uint8_t)(m->m_pkthdr.len); buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); usb2_copy_in(xfer->frbuffers, 0, buf, 2); usb2_m_copy_in(xfer->frbuffers, 2, m, 0, m->m_pkthdr.len); } /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usb2_start_hardware(xfer); done: return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AUE_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; return; } } #define AUE_BITS 6 static void aue_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) { uint8_t h; h = ether_crc32_le(ptr, ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1); cc->if_hash[(h >> 3)] |= (1 << (h & 7)); } static void aue_config_copy(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { bzero(cc, sizeof(*cc)); usb2_ether_cc(sc->sc_ifp, &aue_mchash, cc); } static void aue_cfg_tick(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & AUE_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~AUE_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ aue_start_transfers(sc); } static void aue_start_cb(struct ifnet *ifp) { struct aue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); aue_start_transfers(sc); mtx_unlock(&sc->sc_mtx); } static void aue_init_cb(void *arg) { struct aue_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &aue_cfg_pre_init, &aue_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static void aue_start_transfers(struct aue_softc *sc) { if ((sc->sc_flags & AUE_FLAG_LL_READY) && (sc->sc_flags & AUE_FLAG_HL_READY)) { /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[4]); usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); } } static void aue_cfg_pre_init(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ aue_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= AUE_FLAG_HL_READY; } static void aue_cfg_init(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct mii_data *mii = GET_MII(sc); uint8_t i; /* * Cancel pending I/O */ aue_cfg_stop(sc, cc, 0); /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) { aue_cfg_csr_write_1(sc, AUE_PAR0 + i, cc->if_lladdr[i]); } /* update promiscuous setting */ aue_cfg_promisc_upd(sc, cc, 0); /* load the multicast filter */ aue_cfg_setmulti(sc, cc, 0); /* enable RX and TX */ aue_cfg_csr_write_1(sc, AUE_CTL0, (AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB)); AUE_CFG_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); AUE_CFG_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); mii_mediachg(mii); sc->sc_flags |= (AUE_FLAG_READ_STALL | AUE_FLAG_WRITE_STALL | AUE_FLAG_LL_READY); aue_start_transfers(sc); } static void aue_cfg_promisc_upd(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* if we want promiscuous mode, set the allframes bit: */ if (cc->if_flags & IFF_PROMISC) { AUE_CFG_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); } else { AUE_CFG_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC); } } /* * Set media options. */ static int aue_ifmedia_upd_cb(struct ifnet *ifp) { struct aue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &aue_cfg_ifmedia_upd, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } static void aue_cfg_ifmedia_upd(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= AUE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); } /* * Report current media status. */ static void aue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct aue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; mtx_unlock(&sc->sc_mtx); } static int aue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct aue_softc *sc = ifp->if_softc; struct mii_data *mii; int error = 0; switch (command) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &aue_config_copy, &aue_cfg_promisc_upd, 0, 0); } else { usb2_config_td_queue_command (&sc->sc_config_td, &aue_cfg_pre_init, &aue_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &aue_cfg_pre_stop, &aue_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &aue_config_copy, &aue_cfg_setmulti, 0, 0); mtx_unlock(&sc->sc_mtx); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &mii->mii_media, command); } break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void aue_watchdog(void *arg) { struct aue_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &aue_cfg_tick, 0, 0); usb2_callout_reset(&sc->sc_watchdog, hz, &aue_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. * * NOTE: can be called when "ifp" is NULL */ static void aue_cfg_pre_stop(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ aue_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(AUE_FLAG_HL_READY | AUE_FLAG_LL_READY); sc->sc_flags |= AUE_FLAG_WAIT_LINK; /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); usb2_transfer_stop(sc->sc_xfer[4]); usb2_transfer_stop(sc->sc_xfer[5]); } static void aue_cfg_stop(struct aue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { aue_cfg_csr_write_1(sc, AUE_CTL0, 0); aue_cfg_csr_write_1(sc, AUE_CTL1, 0); aue_cfg_reset(sc); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int aue_shutdown(device_t dev) { struct aue_softc *sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &aue_cfg_pre_stop, &aue_cfg_stop, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } Index: projects/cambria/sys/dev/usb2/ethernet/if_axe2.c =================================================================== --- projects/cambria/sys/dev/usb2/ethernet/if_axe2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/ethernet/if_axe2.c (revision 186460) @@ -1,1490 +1,1485 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000-2003 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver. Used in the * LinkSys USB200M and various other adapters. * * Manuals available from: * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF * Note: you need the manual for the AX88170 chip (USB 1.x ethernet * controller) to find the definitions for the RX control register. * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF * * Written by Bill Paul * Senior Engineer * Wind River Systems */ /* * The AX88172 provides USB ethernet supports at 10 and 100Mbps. * It uses an external PHY (reference designs use a RealTek chip), * and has a 64-bit multicast hash filter. There is some information * missing from the manual which one needs to know in order to make * the chip function: * * - You must set bit 7 in the RX control register, otherwise the * chip won't receive any packets. * - You must initialize all 3 IPG registers, or you won't be able * to send any packets. * * Note that this device appears to only support loading the station * address via autload from the EEPROM (i.e. there's no way to manaully * set it). * * (Adam Weinberger wanted me to name this driver if_gir.c.) */ /* * Ax88178 and Ax88772 support backported from the OpenBSD driver. * 2007/02/12, J.R. Oldroyd, fbsd@opal.com * * Manual here: * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf */ /* * NOTE: all function names beginning like "axe_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc usb2_ether_cc #define usb2_config_td_softc axe_softc #define USB_DEBUG_VAR axe_debug #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(axe, usb2_ethernet, 1, 1, 1); MODULE_DEPEND(axe, usb2_core, 1, 1, 1); MODULE_DEPEND(axe, ether, 1, 1, 1); MODULE_DEPEND(axe, miibus, 1, 1, 1); #if USB_DEBUG static int axe_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe"); SYSCTL_INT(_hw_usb2_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const struct usb2_device_id axe_devs[] = { {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200, 0)}, {USB_VPI(USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2, 0)}, {USB_VPI(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET, AXE_FLAG_772)}, {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172, 0)}, {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178, AXE_FLAG_178)}, {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772, AXE_FLAG_772)}, {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055, AXE_FLAG_178)}, {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2, AXE_FLAG_772)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX, 0)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100, 0)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1, AXE_FLAG_772)}, {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E, 0)}, {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2, AXE_FLAG_178)}, {USB_VPI(USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1, 0)}, {USB_VPI(USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M, 0)}, {USB_VPI(USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000, AXE_FLAG_178)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX, 0)}, {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120, 0)}, {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS, AXE_FLAG_772)}, {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T, AXE_FLAG_178)}, {USB_VPI(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029, 0)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028, AXE_FLAG_178)}, {USB_VPI(USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL, 0)}, }; static device_probe_t axe_probe; static device_attach_t axe_attach; static device_detach_t axe_detach; static device_shutdown_t axe_shutdown; static usb2_callback_t axe_intr_clear_stall_callback; static usb2_callback_t axe_intr_callback; static usb2_callback_t axe_bulk_read_clear_stall_callback; static usb2_callback_t axe_bulk_read_callback; static usb2_callback_t axe_bulk_write_clear_stall_callback; static usb2_callback_t axe_bulk_write_callback; static miibus_readreg_t axe_cfg_miibus_readreg; static miibus_writereg_t axe_cfg_miibus_writereg; static miibus_statchg_t axe_cfg_miibus_statchg; static usb2_config_td_command_t axe_cfg_ifmedia_upd; static usb2_config_td_command_t axe_config_copy; static usb2_config_td_command_t axe_cfg_setmulti; static usb2_config_td_command_t axe_cfg_first_time_setup; static usb2_config_td_command_t axe_cfg_tick; static usb2_config_td_command_t axe_cfg_pre_init; static usb2_config_td_command_t axe_cfg_init; static usb2_config_td_command_t axe_cfg_promisc_upd; static usb2_config_td_command_t axe_cfg_pre_stop; static usb2_config_td_command_t axe_cfg_stop; static int axe_ifmedia_upd_cb(struct ifnet *); static void axe_ifmedia_sts_cb(struct ifnet *, struct ifmediareq *); static void axe_cfg_reset(struct axe_softc *); static void axe_start_cb(struct ifnet *); static void axe_start_transfers(struct axe_softc *); static void axe_init_cb(void *); static int axe_ioctl_cb(struct ifnet *, u_long, caddr_t); static void axe_watchdog(void *); static void axe_cfg_cmd(struct axe_softc *, uint16_t, uint16_t, uint16_t, void *); static void axe_cfg_ax88178_init(struct axe_softc *); static void axe_cfg_ax88772_init(struct axe_softc *); static const struct usb2_config axe_config[AXE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = AXE_BULK_BUF_SIZE, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &axe_bulk_write_callback, .mh.timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, #if (MCLBYTES < 2048) #error "(MCLBYTES < 2048)" #endif .mh.bufsize = MCLBYTES, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &axe_bulk_read_callback, .mh.timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &axe_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &axe_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [4] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.bufsize = 0, /* use wMaxPacketSize */ .mh.callback = &axe_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &axe_intr_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static device_method_t axe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, axe_probe), DEVMETHOD(device_attach, axe_attach), DEVMETHOD(device_detach, axe_detach), DEVMETHOD(device_shutdown, axe_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, axe_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, axe_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, axe_cfg_miibus_statchg), {0, 0} }; static driver_t axe_driver = { .name = "axe", .methods = axe_methods, .size = sizeof(struct axe_softc), }; static devclass_t axe_devclass; DRIVER_MODULE(axe, ushub, axe_driver, axe_devclass, NULL, 0); DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0); static void axe_cfg_cmd(struct axe_softc *sc, uint16_t cmd, uint16_t index, uint16_t val, void *buf) { struct usb2_device_request req; usb2_error_t err; uint16_t length = AXE_CMD_LEN(cmd); req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ? UT_WRITE_VENDOR_DEVICE : UT_READ_VENDOR_DEVICE); req.bRequest = AXE_CMD_CMD(cmd); USETW(req.wValue, val); USETW(req.wIndex, index); USETW(req.wLength, length); if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, &req, buf, 0, NULL, 1000); if (err) { DPRINTFN(0, "device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); error: if ((req.bmRequestType & UT_READ) && length) { bzero(buf, length); } } } static int axe_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct axe_softc *sc = device_get_softc(dev); uint16_t val; uint8_t do_unlock; /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } #if 0 /* * The chip tells us the MII address of any supported * PHYs attached to the chip, so only read from those. */ if ((sc->sc_phyaddrs[0] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[0])) { val = 0; goto done; } if ((sc->sc_phyaddrs[1] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[1])) { val = 0; goto done; } #endif if ((sc->sc_phyaddrs[0] != 0xFF) && (sc->sc_phyaddrs[0] != phy)) { val = 0; goto done; } axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cfg_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); val = le16toh(val); if (val && (val != 0xffff)) { sc->sc_phyaddrs[0] = phy; } done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (val); } static int axe_cfg_miibus_writereg(device_t dev, int phy, int reg, int val) { struct axe_softc *sc = device_get_softc(dev); uint8_t do_unlock; val = htole16(val); /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cfg_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (0); } static void axe_cfg_miibus_statchg(device_t dev) { struct axe_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); uint16_t val; uint8_t do_unlock; /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) val = AXE_MEDIA_FULL_DUPLEX; else val = 0; if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { val |= (AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC); switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_1000_T: val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; break; case IFM_100_TX: val |= AXE_178_MEDIA_100TX; break; case IFM_10_T: /* doesn't need to be handled */ break; } } axe_cfg_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); if (do_unlock) { mtx_unlock(&sc->sc_mtx); } } /* * Set media options. */ static int axe_ifmedia_upd_cb(struct ifnet *ifp) { struct axe_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &axe_cfg_ifmedia_upd, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } static void axe_cfg_ifmedia_upd(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= AXE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); } /* * Report current media status. */ static void axe_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct axe_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; mtx_unlock(&sc->sc_mtx); } static void axe_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) { uint8_t h; h = (ether_crc32_be(ptr, ETHER_ADDR_LEN) >> 26); cc->if_hash[(h >> 3)] |= (1 << (h & 7)); } static void axe_config_copy(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { bzero(cc, sizeof(*cc)); usb2_ether_cc(sc->sc_ifp, &axe_mchash, cc); } static void axe_cfg_setmulti(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t rxmode; axe_cfg_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); if (cc->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { rxmode |= AXE_RXCMD_ALLMULTI; axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); return; } rxmode &= ~AXE_RXCMD_ALLMULTI; axe_cfg_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, cc->if_hash); axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); } static void axe_cfg_reset(struct axe_softc *sc) { struct usb2_config_descriptor *cd; usb2_error_t err; cd = usb2_get_config_descriptor(sc->sc_udev); err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) { DPRINTF("reset failed (ignored)\n"); } /* * wait a little while for the chip to get its brains in order: */ err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } /* * Probe for a AX88172 chip. */ static int axe_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != AXE_CONFIG_IDX) { return (ENXIO); } if (uaa->info.bIfaceIndex != AXE_IFACE_IDX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int axe_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct axe_softc *sc = device_get_softc(dev); int32_t error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); sc->sc_flags = USB_GET_DRIVER_INFO(uaa); device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_mtx, "axe lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = AXE_IFACE_IDX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, axe_config, AXE_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, NULL, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); sc->sc_flags |= AXE_FLAG_WAIT_LINK; /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &axe_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - axe_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: axe_detach(dev); return (ENXIO); /* failure */ } static void axe_cfg_ax88178_init(struct axe_softc *sc) { uint16_t eeprom; uint16_t phymode; uint16_t gpio0; uint8_t err; DPRINTF("\n"); axe_cfg_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL); /* XXX magic */ axe_cfg_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom); axe_cfg_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL); /* For big-endian machines: */ eeprom = le16toh(eeprom); /* if EEPROM is invalid we have to use to GPIO0 */ if (eeprom == 0xffff) { phymode = 0; gpio0 = 1; } else { phymode = (eeprom & 7); gpio0 = (eeprom & 0x80) ? 0 : 1; } axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); if ((eeprom >> 8) != 0x01) { axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 3); axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); } else { axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 32); } /* soft reset */ axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } static void axe_cfg_ax88772_init(struct axe_softc *sc) { uint8_t err; DPRINTF("\n"); axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); if (sc->sc_phyaddrs[1] == AXE_INTPHY) { /* ask for the embedded PHY */ axe_cfg_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 64); /* power down and reset state, pin reset state */ axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); /* power down/reset state, pin operating state */ axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); /* power up, reset */ axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL); /* power up, operating */ axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL); } else { /* ask for external PHY */ axe_cfg_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 64); /* power down internal PHY */ axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL); } err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } static void axe_cfg_first_time_setup(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; int error; uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; /* set default value */ bzero(eaddr, sizeof(eaddr)); /* * Load PHY indexes first. Needed by axe_xxx_init(). */ axe_cfg_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); if (sc->sc_flags & AXE_FLAG_178) { axe_cfg_ax88178_init(sc); } else if (sc->sc_flags & AXE_FLAG_772) { axe_cfg_ax88772_init(sc); } /* * Get station address. */ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) axe_cfg_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, eaddr); else axe_cfg_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, eaddr); /* * Fetch IPG values. */ axe_cfg_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); /* * Work around broken adapters that appear to lie about * their PHY addresses. */ sc->sc_phyaddrs[0] = sc->sc_phyaddrs[1] = 0xFF; mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } sc->sc_evilhack = ifp; ifp->if_softc = sc; if_initname(ifp, "axe", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = axe_ioctl_cb; ifp->if_start = axe_start_cb; ifp->if_watchdog = NULL; ifp->if_init = axe_init_cb; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); /* * XXX need Giant when accessing the device structures ! */ mtx_unlock(&sc->sc_mtx); mtx_lock(&Giant); error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, &axe_ifmedia_upd_cb, &axe_ifmedia_sts_cb); mtx_unlock(&Giant); mtx_lock(&sc->sc_mtx); if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; mtx_unlock(&sc->sc_mtx); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); mtx_lock(&sc->sc_mtx); done: return; } static int axe_detach(device_t dev) { struct axe_softc *sc = device_get_softc(dev); struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); axe_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, AXE_ENDPT_MAX); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void axe_intr_clear_stall_callback(struct usb2_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[4]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~AXE_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); } } static void axe_intr_callback(struct usb2_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* do nothing */ case USB_ST_SETUP: if (sc->sc_flags & AXE_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer[5]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* start clear stall */ sc->sc_flags |= AXE_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[5]); } return; } } static void axe_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~AXE_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } #if (AXE_BULK_BUF_SIZE >= 0x10000) #error "Please update axe_bulk_read_callback()!" #endif static void axe_bulk_read_callback(struct usb2_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct axe_sframe_hdr hdr; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; struct { /* mini-queue */ struct mbuf *ifq_head; struct mbuf *ifq_tail; uint16_t ifq_len; } mq = { NULL, NULL, 0 }; uint16_t pos; uint16_t len; uint16_t adjust; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pos = 0; while (1) { if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { if (xfer->actlen < sizeof(hdr)) { /* too little data */ break; } usb2_copy_out(xfer->frbuffers, pos, &hdr, sizeof(hdr)); if ((hdr.len ^ hdr.ilen) != 0xFFFF) { /* we lost sync */ break; } xfer->actlen -= sizeof(hdr); pos += sizeof(hdr); len = le16toh(hdr.len); if (len > xfer->actlen) { /* invalid length */ break; } adjust = (len & 1); } else { len = xfer->actlen; adjust = 0; } if (len < sizeof(struct ether_header)) { ifp->if_ierrors++; goto skip; } m = usb2_ether_get_mbuf(); if (m == NULL) { /* we are out of memory */ break; } if (m->m_len > len) { m->m_len = len; } usb2_copy_out(xfer->frbuffers, pos, m->m_data, m->m_len); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len; /* enqueue */ _IF_ENQUEUE(&mq, m); skip: pos += len; xfer->actlen -= len; if (xfer->actlen <= adjust) { /* we are finished */ goto tr_setup; } pos += adjust; xfer->actlen -= adjust; } /* count an error */ ifp->if_ierrors++; case USB_ST_SETUP: tr_setup: if (sc->sc_flags & AXE_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "if_input" here, and not some lines up! */ if (mq.ifq_head) { mtx_unlock(&sc->sc_mtx); while (1) { _IF_DEQUEUE(&mq, m); if (m == NULL) break; (ifp->if_input) (ifp, m); } mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AXE_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } DPRINTF("bulk read error, %s\n", usb2_errstr(xfer->error)); return; } } static void axe_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~AXE_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } #if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4))) #error "Please update axe_bulk_write_callback()!" #endif static void axe_bulk_write_callback(struct usb2_xfer *xfer) { struct axe_softc *sc = xfer->priv_sc; struct axe_sframe_hdr hdr; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t pos; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & AXE_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & AXE_FLAG_WAIT_LINK) { /* * don't send anything if there is no link ! */ goto done; } pos = 0; while (1) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { if (pos > 0) break; /* send out data */ else goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { hdr.len = htole16(m->m_pkthdr.len); hdr.ilen = ~hdr.len; usb2_copy_in(xfer->frbuffers, pos, &hdr, sizeof(hdr)); pos += sizeof(hdr); /* * NOTE: Some drivers force a short packet * by appending a dummy header with zero * length at then end of the USB transfer. * This driver uses the * USB_FORCE_SHORT_XFER flag instead. */ } usb2_m_copy_in(xfer->frbuffers, pos, m, 0, m->m_pkthdr.len); pos += m->m_pkthdr.len; /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { if (pos > (AXE_BULK_BUF_SIZE - MCLBYTES - sizeof(hdr))) { /* send out frame(s) */ break; } } else { /* send out frame */ break; } } xfer->frlengths[0] = pos; usb2_start_hardware(xfer); done: return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= AXE_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; return; } } static void axe_cfg_tick(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & AXE_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~AXE_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ axe_start_transfers(sc); } static void axe_start_cb(struct ifnet *ifp) { struct axe_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); axe_start_transfers(sc); mtx_unlock(&sc->sc_mtx); } static void axe_start_transfers(struct axe_softc *sc) { if ((sc->sc_flags & AXE_FLAG_LL_READY) && (sc->sc_flags & AXE_FLAG_HL_READY)) { /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[4]); usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); } } static void axe_init_cb(void *arg) { struct axe_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &axe_cfg_pre_init, &axe_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static void axe_cfg_pre_init(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ axe_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= AXE_FLAG_HL_READY; } static void axe_cfg_init(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct mii_data *mii = GET_MII(sc); uint16_t rxmode; /* * Cancel pending I/O */ axe_cfg_stop(sc, cc, 0); #if 0 /* Set MAC address */ axe_mac(sc, cc->if_lladdr); #endif /* Set transmitter IPG values */ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { axe_cfg_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2], (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL); } else { axe_cfg_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL); axe_cfg_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL); axe_cfg_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL); } /* Enable receiver, set RX mode */ rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE); if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { rxmode |= AXE_178_RXCMD_MFB_2048; /* chip default */ } else { rxmode |= AXE_172_RXCMD_UNICAST; } /* If we want promiscuous mode, set the allframes bit. */ if (cc->if_flags & IFF_PROMISC) { rxmode |= AXE_RXCMD_PROMISC; } if (cc->if_flags & IFF_BROADCAST) { rxmode |= AXE_RXCMD_BROADCAST; } axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); /* Load the multicast filter. */ axe_cfg_setmulti(sc, cc, 0); mii_mediachg(mii); sc->sc_flags |= (AXE_FLAG_READ_STALL | AXE_FLAG_WRITE_STALL | AXE_FLAG_LL_READY); axe_start_transfers(sc); } static void axe_cfg_promisc_upd(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t rxmode; axe_cfg_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode); rxmode = le16toh(rxmode); if (cc->if_flags & IFF_PROMISC) { rxmode |= AXE_RXCMD_PROMISC; } else { rxmode &= ~AXE_RXCMD_PROMISC; } axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL); axe_cfg_setmulti(sc, cc, 0); } static int axe_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct axe_softc *sc = ifp->if_softc; struct mii_data *mii; int error = 0; switch (command) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &axe_config_copy, &axe_cfg_promisc_upd, 0, 0); } else { usb2_config_td_queue_command (&sc->sc_config_td, &axe_cfg_pre_init, &axe_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &axe_cfg_pre_stop, &axe_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &axe_config_copy, &axe_cfg_setmulti, 0, 0); mtx_unlock(&sc->sc_mtx); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &mii->mii_media, command); } break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void axe_watchdog(void *arg) { struct axe_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &axe_cfg_tick, 0, 0); usb2_callout_reset(&sc->sc_watchdog, hz, &axe_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } /* * NOTE: can be called when "ifp" is NULL */ static void axe_cfg_pre_stop(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ axe_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(AXE_FLAG_HL_READY | AXE_FLAG_LL_READY); sc->sc_flags |= AXE_FLAG_WAIT_LINK; /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); usb2_transfer_stop(sc->sc_xfer[4]); usb2_transfer_stop(sc->sc_xfer[5]); } static void axe_cfg_stop(struct axe_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { axe_cfg_reset(sc); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int axe_shutdown(device_t dev) { struct axe_softc *sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &axe_cfg_pre_stop, &axe_cfg_stop, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } Index: projects/cambria/sys/dev/usb2/ethernet/if_cue2.c =================================================================== --- projects/cambria/sys/dev/usb2/ethernet/if_cue2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/ethernet/if_cue2.c (revision 186460) @@ -1,944 +1,939 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate * adapters and others. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The * RX filter uses a 512-bit multicast hash table, single perfect entry * for the station address, and promiscuous mode. Unlike the ADMtek * and KLSI chips, the CATC ASIC supports read and write combining * mode where multiple packets can be transfered using a single bulk * transaction, which helps performance a great deal. */ /* * NOTE: all function names beginning like "cue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc usb2_ether_cc #define usb2_config_td_softc cue_softc #define USB_DEBUG_VAR cue_debug #include #include #include #include #include #include #include #include #include #include /* * Various supported device vendors/products. */ /* Belkin F5U111 adapter covered by NETMATE entry */ static const struct usb2_device_id cue_devs[] = { {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE, 0)}, {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2, 0)}, {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK, 0)}, }; /* prototypes */ static device_probe_t cue_probe; static device_attach_t cue_attach; static device_detach_t cue_detach; static device_shutdown_t cue_shutdown; static usb2_callback_t cue_bulk_read_clear_stall_callback; static usb2_callback_t cue_bulk_read_callback; static usb2_callback_t cue_bulk_write_clear_stall_callback; static usb2_callback_t cue_bulk_write_callback; static usb2_config_td_command_t cue_cfg_promisc_upd; static usb2_config_td_command_t cue_config_copy; static usb2_config_td_command_t cue_cfg_first_time_setup; static usb2_config_td_command_t cue_cfg_tick; static usb2_config_td_command_t cue_cfg_pre_init; static usb2_config_td_command_t cue_cfg_init; static usb2_config_td_command_t cue_cfg_pre_stop; static usb2_config_td_command_t cue_cfg_stop; static void cue_cfg_do_request(struct cue_softc *, struct usb2_device_request *, void *); static uint8_t cue_cfg_csr_read_1(struct cue_softc *, uint16_t); static uint16_t cue_cfg_csr_read_2(struct cue_softc *, uint8_t); static void cue_cfg_csr_write_1(struct cue_softc *, uint16_t, uint16_t); static void cue_cfg_mem(struct cue_softc *, uint8_t, uint16_t, void *, uint16_t); static void cue_cfg_getmac(struct cue_softc *, void *); static void cue_mchash(struct usb2_config_td_cc *, const uint8_t *); static void cue_cfg_reset(struct cue_softc *); static void cue_start_cb(struct ifnet *); static void cue_start_transfers(struct cue_softc *); static void cue_init_cb(void *); static int cue_ioctl_cb(struct ifnet *, u_long, caddr_t); static void cue_watchdog(void *); #if USB_DEBUG static int cue_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue"); SYSCTL_INT(_hw_usb2_cue, OID_AUTO, debug, CTLFLAG_RW, &cue_debug, 0, "Debug level"); #endif static const struct usb2_config cue_config[CUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (MCLBYTES + 2), .mh.flags = {.pipe_bof = 1,}, .mh.callback = &cue_bulk_write_callback, .mh.timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + 2), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &cue_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &cue_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &cue_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static device_method_t cue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cue_probe), DEVMETHOD(device_attach, cue_attach), DEVMETHOD(device_detach, cue_detach), DEVMETHOD(device_shutdown, cue_shutdown), {0, 0} }; static driver_t cue_driver = { .name = "cue", .methods = cue_methods, .size = sizeof(struct cue_softc), }; static devclass_t cue_devclass; DRIVER_MODULE(cue, ushub, cue_driver, cue_devclass, NULL, 0); MODULE_DEPEND(cue, usb2_ethernet, 1, 1, 1); MODULE_DEPEND(cue, usb2_core, 1, 1, 1); MODULE_DEPEND(cue, ether, 1, 1, 1); static void cue_cfg_do_request(struct cue_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } #define CUE_CFG_SETBIT(sc, reg, x) \ cue_cfg_csr_write_1(sc, reg, cue_cfg_csr_read_1(sc, reg) | (x)) #define CUE_CFG_CLRBIT(sc, reg, x) \ cue_cfg_csr_write_1(sc, reg, cue_cfg_csr_read_1(sc, reg) & ~(x)) static uint8_t cue_cfg_csr_read_1(struct cue_softc *sc, uint16_t reg) { struct usb2_device_request req; uint8_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); cue_cfg_do_request(sc, &req, &val); return (val); } static uint16_t cue_cfg_csr_read_2(struct cue_softc *sc, uint8_t reg) { struct usb2_device_request req; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_READREG; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 2); cue_cfg_do_request(sc, &req, &val); return (le16toh(val)); } static void cue_cfg_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_WRITEREG; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); cue_cfg_do_request(sc, &req, NULL); } static void cue_cfg_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; if (cmd == CUE_CMD_READSRAM) { req.bmRequestType = UT_READ_VENDOR_DEVICE; } else { req.bmRequestType = UT_WRITE_VENDOR_DEVICE; } req.bRequest = cmd; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); cue_cfg_do_request(sc, &req, buf); } static void cue_cfg_getmac(struct cue_softc *sc, void *buf) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = CUE_CMD_GET_MACADDR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, ETHER_ADDR_LEN); cue_cfg_do_request(sc, &req, buf); } #define CUE_BITS 9 static void cue_mchash(struct usb2_config_td_cc *cc, const uint8_t *addr) { uint16_t h; h = ether_crc32_le(addr, ETHER_ADDR_LEN) & ((1 << CUE_BITS) - 1); cc->if_hash[h >> 3] |= 1 << (h & 0x7); } static void cue_cfg_promisc_upd(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* if we want promiscuous mode, set the allframes bit */ if (cc->if_flags & IFF_PROMISC) { CUE_CFG_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); } else { CUE_CFG_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC); } /* write multicast hash-bits */ cue_cfg_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, cc->if_hash, CUE_MCAST_TABLE_LEN); } static void cue_config_copy(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { bzero(cc, sizeof(*cc)); usb2_ether_cc(sc->sc_ifp, &cue_mchash, cc); } static void cue_cfg_reset(struct cue_softc *sc) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = CUE_CMD_RESET; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 0); cue_cfg_do_request(sc, &req, NULL); /* * wait a little while for the chip to get its brains in order: */ (void)usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } static int cue_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != CUE_CONFIG_IDX) { return (ENXIO); } if (uaa->info.bIfaceIndex != CUE_IFACE_IDX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa)); } static int cue_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct cue_softc *sc = device_get_softc(dev); uint8_t iface_index; int32_t error; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "cue lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = CUE_IFACE_IDX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, cue_config, CUE_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, NULL, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &cue_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - cue_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: cue_detach(dev); return (ENXIO); /* failure */ } static void cue_cfg_first_time_setup(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint8_t eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; #if 0 /* Reset the adapter. */ cue_cfg_reset(sc); #endif /* * Get station address. */ cue_cfg_getmac(sc, eaddr); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { printf("cue%d: could not if_alloc()\n", sc->sc_unit); goto done; } sc->sc_evilhack = ifp; ifp->if_softc = sc; if_initname(ifp, "cue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = cue_ioctl_cb; ifp->if_start = cue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = cue_init_cb; ifp->if_baudrate = 10000000; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); sc->sc_ifp = ifp; mtx_unlock(&sc->sc_mtx); ether_ifattach(ifp, eaddr); mtx_lock(&sc->sc_mtx); done: return; } static int cue_detach(device_t dev) { struct cue_softc *sc = device_get_softc(dev); struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); cue_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, CUE_ENDPT_MAX); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void cue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~CUE_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void cue_bulk_read_callback(struct usb2_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m = NULL; uint8_t buf[2]; uint16_t len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen <= (2 + sizeof(struct ether_header))) { ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, buf, 2); len = buf[0] | (buf[1] << 8); xfer->actlen -= 2; m = usb2_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); xfer->actlen = min(xfer->actlen, len); usb2_copy_out(xfer->frbuffers, 2, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; case USB_ST_SETUP: tr_setup: if (sc->sc_flags & CUE_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "if_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); (ifp->if_input) (ifp, m); mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= CUE_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } DPRINTF("bulk read error, %s\n", usb2_errstr(xfer->error)); return; } } static void cue_cfg_tick(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if ((ifp == NULL)) { /* not ready */ return; } ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_SINGLECOLL); ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_MULTICOLL); ifp->if_collisions += cue_cfg_csr_read_2(sc, CUE_TX_EXCESSCOLL); if (cue_cfg_csr_read_2(sc, CUE_RX_FRAMEERR)) { ifp->if_ierrors++; } /* start stopped transfers, if any */ cue_start_transfers(sc); } static void cue_start_cb(struct ifnet *ifp) { struct cue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); cue_start_transfers(sc); mtx_unlock(&sc->sc_mtx); } static void cue_start_transfers(struct cue_softc *sc) { if ((sc->sc_flags & CUE_FLAG_LL_READY) && (sc->sc_flags & CUE_FLAG_HL_READY)) { /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); } } static void cue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~CUE_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void cue_bulk_write_callback(struct usb2_xfer *xfer) { struct cue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint8_t buf[2]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & CUE_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); goto done; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } xfer->frlengths[0] = (m->m_pkthdr.len + 2); /* the first two bytes are the frame length */ buf[0] = (uint8_t)(m->m_pkthdr.len); buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); usb2_copy_in(xfer->frbuffers, 0, buf, 2); usb2_m_copy_in(xfer->frbuffers, 2, m, 0, m->m_pkthdr.len); /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m); m_freem(m); usb2_start_hardware(xfer); done: return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= CUE_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; return; } } static void cue_init_cb(void *arg) { struct cue_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &cue_cfg_pre_init, &cue_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static void cue_cfg_pre_init(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ cue_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= CUE_FLAG_HL_READY; } static void cue_cfg_init(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint8_t i; /* * Cancel pending I/O and free all RX/TX buffers. */ cue_cfg_stop(sc, cc, 0); #if 0 cue_cfg_reset(sc); #endif /* Set MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) { cue_cfg_csr_write_1(sc, CUE_PAR0 - i, cc->if_lladdr[i]); } /* Enable RX logic. */ cue_cfg_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON); /* Load the multicast filter */ cue_cfg_promisc_upd(sc, cc, 0); /* * Set the number of RX and TX buffers that we want * to reserve inside the ASIC. */ cue_cfg_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES); cue_cfg_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES); /* Set advanced operation modes. */ cue_cfg_csr_write_1(sc, CUE_ADVANCED_OPMODES, CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */ /* Program the LED operation. */ cue_cfg_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK); sc->sc_flags |= (CUE_FLAG_READ_STALL | CUE_FLAG_WRITE_STALL | CUE_FLAG_LL_READY); cue_start_transfers(sc); } static int cue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct cue_softc *sc = ifp->if_softc; int error = 0; switch (command) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &cue_config_copy, &cue_cfg_promisc_upd, 0, 0); } else { usb2_config_td_queue_command (&sc->sc_config_td, &cue_cfg_pre_init, &cue_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &cue_cfg_pre_stop, &cue_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &cue_config_copy, &cue_cfg_promisc_upd, 0, 0); mtx_unlock(&sc->sc_mtx); break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void cue_watchdog(void *arg) { struct cue_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &cue_cfg_tick, 0, 0); usb2_callout_reset(&sc->sc_watchdog, hz, &cue_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void cue_cfg_pre_stop(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ cue_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(CUE_FLAG_HL_READY | CUE_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); } static void cue_cfg_stop(struct cue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { cue_cfg_csr_write_1(sc, CUE_ETHCTL, 0); cue_cfg_reset(sc); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int cue_shutdown(device_t dev) { struct cue_softc *sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &cue_cfg_pre_stop, &cue_cfg_stop, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } Index: projects/cambria/sys/dev/usb2/ethernet/if_kue2.c =================================================================== --- projects/cambria/sys/dev/usb2/ethernet/if_kue2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/ethernet/if_kue2.c (revision 186460) @@ -1,995 +1,990 @@ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The KLSI USB to ethernet adapter chip contains an USB serial interface, * ethernet MAC and embedded microcontroller (called the QT Engine). * The chip must have firmware loaded into it before it will operate. * Packets are passed between the chip and host via bulk transfers. * There is an interrupt endpoint mentioned in the software spec, however * it's currently unused. This device is 10Mbps half-duplex only, hence * there is no media selection logic. The MAC supports a 128 entry * multicast filter, though the exact size of the filter can depend * on the firmware. Curiously, while the software spec describes various * ethernet statistics counters, my sample adapter and firmware combination * claims not to support any statistics counters at all. * * Note that once we load the firmware in the device, we have to be * careful not to load it again: if you restart your computer but * leave the adapter attached to the USB controller, it may remain * powered on and retain its firmware. In this case, we don't need * to load the firmware a second time. * * Special thanks to Rob Furr for providing an ADS Technologies * adapter for development and testing. No monkeys were harmed during * the development of this driver. */ /* * NOTE: all function names beginning like "kue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc usb2_ether_cc #define usb2_config_td_softc kue_softc #define USB_DEBUG_VAR kue_debug #include #include #include #include #include #include #include #include #include #include #include /* * Various supported device vendors/products. */ static const struct usb2_device_id kue_devs[] = { {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250, 0)}, {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450, 0)}, {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT, 0)}, {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BTX, 0)}, {USB_VPI(USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101, 0)}, {USB_VPI(USB_VENDOR_ASANTE, USB_PRODUCT_ASANTE_EA, 0)}, {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_DSB650C, 0)}, {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T, 0)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T, 0)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C, 0)}, {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45, 0)}, {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX1, 0)}, {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX2, 0)}, {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT, 0)}, {USB_VPI(USB_VENDOR_JATON, USB_PRODUCT_JATON_EDA, 0)}, {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_XX1, 0)}, {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101, 0)}, {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT, 0)}, {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN, 0)}, {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, 0)}, {USB_VPI(USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EA, 0)}, {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101, 0)}, {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101X, 0)}, {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET, 0)}, {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2, 0)}, {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3, 0)}, {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA8, 0)}, {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA9, 0)}, {USB_VPI(USB_VENDOR_PORTSMITH, USB_PRODUCT_PORTSMITH_EEA, 0)}, {USB_VPI(USB_VENDOR_SHARK, USB_PRODUCT_SHARK_PA, 0)}, {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE, 0)}, {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_U2E, 0)}, {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB, 0)}, }; /* prototypes */ static device_probe_t kue_probe; static device_attach_t kue_attach; static device_detach_t kue_detach; static device_shutdown_t kue_shutdown; static usb2_callback_t kue_bulk_read_clear_stall_callback; static usb2_callback_t kue_bulk_read_callback; static usb2_callback_t kue_bulk_write_clear_stall_callback; static usb2_callback_t kue_bulk_write_callback; static usb2_config_td_command_t kue_cfg_promisc_upd; static usb2_config_td_command_t kue_config_copy; static usb2_config_td_command_t kue_cfg_first_time_setup; static usb2_config_td_command_t kue_cfg_pre_init; static usb2_config_td_command_t kue_cfg_init; static usb2_config_td_command_t kue_cfg_tick; static usb2_config_td_command_t kue_cfg_pre_stop; static usb2_config_td_command_t kue_cfg_stop; static void kue_cfg_do_request(struct kue_softc *, struct usb2_device_request *, void *); static void kue_cfg_setword(struct kue_softc *, uint8_t, uint16_t); static void kue_cfg_ctl(struct kue_softc *, uint8_t, uint8_t, uint16_t, void *, uint16_t); static void kue_cfg_load_fw(struct kue_softc *); static void kue_cfg_reset(struct kue_softc *); static void kue_start_cb(struct ifnet *); static void kue_start_transfers(struct kue_softc *); static void kue_init_cb(void *); static int kue_ioctl_cb(struct ifnet *, u_long, caddr_t); static void kue_watchdog(void *); #if USB_DEBUG static int kue_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue"); SYSCTL_INT(_hw_usb2_kue, OID_AUTO, debug, CTLFLAG_RW, &kue_debug, 0, "Debug level"); #endif static const struct usb2_config kue_config[KUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (MCLBYTES + 2 + 64), .mh.flags = {.pipe_bof = 1,}, .mh.callback = &kue_bulk_write_callback, .mh.timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + 2), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &kue_bulk_read_callback, .mh.timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &kue_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &kue_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static device_method_t kue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, kue_probe), DEVMETHOD(device_attach, kue_attach), DEVMETHOD(device_detach, kue_detach), DEVMETHOD(device_shutdown, kue_shutdown), {0, 0} }; static driver_t kue_driver = { .name = "kue", .methods = kue_methods, .size = sizeof(struct kue_softc), }; static devclass_t kue_devclass; DRIVER_MODULE(kue, ushub, kue_driver, kue_devclass, NULL, 0); MODULE_DEPEND(kue, usb2_ethernet, 1, 1, 1); MODULE_DEPEND(kue, usb2_core, 1, 1, 1); MODULE_DEPEND(kue, ether, 1, 1, 1); /* * We have a custom do_request function which is almost like the * regular do_request function, except it has a much longer timeout. * Why? Because we need to make requests over the control endpoint * to download the firmware to the device, which can take longer * than the default timeout. */ static void kue_cfg_do_request(struct kue_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 60000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } static void kue_cfg_setword(struct kue_softc *sc, uint8_t breq, uint16_t word) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = breq; USETW(req.wValue, word); USETW(req.wIndex, 0); USETW(req.wLength, 0); kue_cfg_do_request(sc, &req, NULL); } static void kue_cfg_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq, uint16_t val, void *data, uint16_t len) { struct usb2_device_request req; if (rw == KUE_CTL_WRITE) { req.bmRequestType = UT_WRITE_VENDOR_DEVICE; } else { req.bmRequestType = UT_READ_VENDOR_DEVICE; } req.bRequest = breq; USETW(req.wValue, val); USETW(req.wIndex, 0); USETW(req.wLength, len); kue_cfg_do_request(sc, &req, data); } static void kue_cfg_load_fw(struct kue_softc *sc) { struct usb2_device_descriptor *dd; uint16_t hwrev; dd = usb2_get_device_descriptor(sc->sc_udev); hwrev = UGETW(dd->bcdDevice); /* * First, check if we even need to load the firmware. * If the device was still attached when the system was * rebooted, it may already have firmware loaded in it. * If this is the case, we don't need to do it again. * And in fact, if we try to load it again, we'll hang, * so we have to avoid this condition if we don't want * to look stupid. * * We can test this quickly by checking the bcdRevision * code. The NIC will return a different revision code if * it's probed while the firmware is still loaded and * running. */ if (hwrev == 0x0202) { return; } /* load code segment */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_code_seg, sizeof(kue_code_seg)); /* load fixup segment */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_fix_seg, sizeof(kue_fix_seg)); /* send trigger command */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN, 0, kue_trig_seg, sizeof(kue_trig_seg)); } static void kue_cfg_promisc_upd(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS, cc->if_nhash, cc->if_hash, cc->if_nhash * ETHER_ADDR_LEN); kue_cfg_setword(sc, KUE_CMD_SET_PKT_FILTER, cc->if_rxfilt); } static void kue_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) { uint8_t i; i = cc->if_nhash; /* * If there are too many addresses for the internal filter, * switch over to allmulti mode. */ if (i == cc->if_mhash) { cc->if_rxfilt |= KUE_RXFILT_ALLMULTI; } else { bcopy(ptr, cc->if_hash + (i * ETHER_ADDR_LEN), ETHER_ADDR_LEN); cc->if_nhash++; } } static void kue_config_copy(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { bzero(cc, sizeof(*cc)); cc->if_mhash = sc->sc_mcfilt_max; cc->if_rxfilt = (KUE_RXFILT_UNICAST | KUE_RXFILT_BROADCAST); usb2_ether_cc(sc->sc_ifp, &kue_mchash, cc); /* * If we want promiscuous mode, set the all-frames bit: */ if (cc->if_flags & IFF_PROMISC) { cc->if_rxfilt |= KUE_RXFILT_PROMISC; } if ((cc->if_flags & IFF_ALLMULTI) || (cc->if_flags & IFF_PROMISC)) { cc->if_rxfilt |= KUE_RXFILT_ALLMULTI; } else if (cc->if_nhash) { cc->if_rxfilt |= KUE_RXFILT_MULTICAST; } } /* * Issue a SET_CONFIGURATION command to reset the MAC. This should be * done after the firmware is loaded into the adapter in order to * bring it into proper operation. */ static void kue_cfg_reset(struct kue_softc *sc) { struct usb2_config_descriptor *cd; usb2_error_t err; cd = usb2_get_config_descriptor(sc->sc_udev); err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) { DPRINTF("reset failed (ignored)\n"); } /* * wait a little while for the chip to get its brains in order: */ err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } /* * Probe for a KLSI chip. */ static int kue_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != KUE_CONFIG_IDX) { return (ENXIO); } if (uaa->info.bIfaceIndex != KUE_IFACE_IDX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(kue_devs, sizeof(kue_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do * setup and ethernet/BPF attach. */ static int kue_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct kue_softc *sc = device_get_softc(dev); int32_t error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "kue lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = KUE_IFACE_IDX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, kue_config, KUE_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, NULL, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &kue_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - kue_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: kue_detach(dev); return (ENXIO); /* failure */ } static void kue_cfg_first_time_setup(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; /* load the firmware into the NIC */ kue_cfg_load_fw(sc); /* reset the adapter */ kue_cfg_reset(sc); /* read ethernet descriptor */ kue_cfg_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR, 0, &sc->sc_desc, sizeof(sc->sc_desc)); sc->sc_mcfilt_max = KUE_MCFILTCNT(sc); if (sc->sc_mcfilt_max > KUE_MCFILT_MAX) { sc->sc_mcfilt_max = KUE_MCFILT_MAX; } mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { printf("kue%d: could not if_alloc()\n", sc->sc_unit); goto done; } sc->sc_evilhack = ifp; ifp->if_softc = sc; if_initname(ifp, "kue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = kue_ioctl_cb; ifp->if_start = kue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = kue_init_cb; ifp->if_baudrate = 10000000; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); sc->sc_ifp = ifp; mtx_unlock(&sc->sc_mtx); ether_ifattach(ifp, sc->sc_desc.kue_macaddr); mtx_lock(&sc->sc_mtx); done: return; } static int kue_detach(device_t dev) { struct kue_softc *sc = device_get_softc(dev); struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); kue_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, KUE_ENDPT_MAX); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void kue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~KUE_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void kue_bulk_read_callback(struct usb2_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m = NULL; uint8_t buf[2]; uint16_t len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen <= (2 + sizeof(struct ether_header))) { ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, buf, 2); len = buf[0] | (buf[1] << 8); xfer->actlen -= 2; m = usb2_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); xfer->actlen = min(xfer->actlen, len); usb2_copy_out(xfer->frbuffers, 2, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; case USB_ST_SETUP: tr_setup: if (sc->sc_flags & KUE_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "if_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); (ifp->if_input) (ifp, m); mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= KUE_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } DPRINTF("bulk read error, %s\n", usb2_errstr(xfer->error)); return; } } static void kue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~KUE_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void kue_bulk_write_callback(struct usb2_xfer *xfer) { struct kue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint32_t total_len; uint32_t temp_len; uint8_t buf[2]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & KUE_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); goto done; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } temp_len = (m->m_pkthdr.len + 2); total_len = (temp_len + (64 - (temp_len % 64))); /* the first two bytes are the frame length */ buf[0] = (uint8_t)(m->m_pkthdr.len); buf[1] = (uint8_t)(m->m_pkthdr.len >> 8); usb2_copy_in(xfer->frbuffers, 0, buf, 2); usb2_m_copy_in(xfer->frbuffers, 2, m, 0, m->m_pkthdr.len); usb2_bzero(xfer->frbuffers, temp_len, total_len - temp_len); xfer->frlengths[0] = total_len; /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usb2_start_hardware(xfer); done: return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= KUE_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; return; } } static void kue_start_cb(struct ifnet *ifp) { struct kue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); kue_start_transfers(sc); mtx_unlock(&sc->sc_mtx); } static void kue_start_transfers(struct kue_softc *sc) { if ((sc->sc_flags & KUE_FLAG_LL_READY) && (sc->sc_flags & KUE_FLAG_HL_READY)) { /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); } } static void kue_init_cb(void *arg) { struct kue_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &kue_cfg_pre_init, &kue_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static void kue_cfg_pre_init(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ kue_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= KUE_FLAG_HL_READY; } static void kue_cfg_init(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* set MAC address */ kue_cfg_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC, 0, cc->if_lladdr, ETHER_ADDR_LEN); /* I'm not sure how to tune these. */ #if 0 /* * Leave this one alone for now; setting it * wrong causes lockups on some machines/controllers. */ kue_cfg_setword(sc, KUE_CMD_SET_SOFS, 1); #endif kue_cfg_setword(sc, KUE_CMD_SET_URB_SIZE, 64); /* load the multicast filter */ kue_cfg_promisc_upd(sc, cc, 0); sc->sc_flags |= (KUE_FLAG_READ_STALL | KUE_FLAG_WRITE_STALL | KUE_FLAG_LL_READY); kue_start_transfers(sc); } static int kue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct kue_softc *sc = ifp->if_softc; int error = 0; switch (command) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &kue_config_copy, &kue_cfg_promisc_upd, 0, 0); } else { usb2_config_td_queue_command (&sc->sc_config_td, &kue_cfg_pre_init, &kue_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &kue_cfg_pre_stop, &kue_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &kue_config_copy, &kue_cfg_promisc_upd, 0, 0); mtx_unlock(&sc->sc_mtx); break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void kue_cfg_tick(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if ((ifp == NULL)) { /* not ready */ return; } /* start stopped transfers, if any */ kue_start_transfers(sc); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void kue_watchdog(void *arg) { struct kue_softc *sc = arg; usb2_config_td_queue_command (&sc->sc_config_td, NULL, &kue_cfg_tick, 0, 0); usb2_callout_reset(&sc->sc_watchdog, hz, &kue_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } static void kue_cfg_pre_stop(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ kue_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(KUE_FLAG_HL_READY | KUE_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); } static void kue_cfg_stop(struct kue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int kue_shutdown(device_t dev) { struct kue_softc *sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &kue_cfg_pre_stop, &kue_cfg_stop, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } Index: projects/cambria/sys/dev/usb2/ethernet/if_rue2.c =================================================================== --- projects/cambria/sys/dev/usb2/ethernet/if_rue2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/ethernet/if_rue2.c (revision 186460) @@ -1,1368 +1,1363 @@ /*- * Copyright (c) 2001-2003, Shunsuke Akiyama . * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /*- * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * RealTek RTL8150 USB to fast ethernet controller driver. * Datasheet is available from * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/. */ /* * NOTE: all function names beginning like "rue_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc usb2_ether_cc #define usb2_config_td_softc rue_softc #define USB_DEBUG_VAR rue_debug #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int rue_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue"); SYSCTL_INT(_hw_usb2_rue, OID_AUTO, debug, CTLFLAG_RW, &rue_debug, 0, "Debug level"); #endif /* * Various supported device vendors/products. */ static const struct usb2_device_id rue_devs[] = { {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)}, {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)}, }; /* prototypes */ static device_probe_t rue_probe; static device_attach_t rue_attach; static device_detach_t rue_detach; static device_shutdown_t rue_shutdown; static usb2_callback_t rue_intr_clear_stall_callback; static usb2_callback_t rue_intr_callback; static usb2_callback_t rue_bulk_read_clear_stall_callback; static usb2_callback_t rue_bulk_read_callback; static usb2_callback_t rue_bulk_write_clear_stall_callback; static usb2_callback_t rue_bulk_write_callback; static usb2_config_td_command_t rue_config_copy; static usb2_config_td_command_t rue_cfg_promisc_upd; static usb2_config_td_command_t rue_cfg_first_time_setup; static usb2_config_td_command_t rue_cfg_tick; static usb2_config_td_command_t rue_cfg_pre_init; static usb2_config_td_command_t rue_cfg_init; static usb2_config_td_command_t rue_cfg_ifmedia_upd; static usb2_config_td_command_t rue_cfg_pre_stop; static usb2_config_td_command_t rue_cfg_stop; static void rue_cfg_do_request(struct rue_softc *sc, struct usb2_device_request *req, void *data); static void rue_cfg_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, uint16_t len); static void rue_cfg_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, uint16_t len); static uint8_t rue_cfg_csr_read_1(struct rue_softc *sc, uint16_t reg); static uint16_t rue_cfg_csr_read_2(struct rue_softc *sc, uint16_t reg); static void rue_cfg_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val); static void rue_cfg_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val); static void rue_cfg_csr_write_4(struct rue_softc *sc, int reg, uint32_t val); static miibus_readreg_t rue_cfg_miibus_readreg; static miibus_writereg_t rue_cfg_miibus_writereg; static miibus_statchg_t rue_cfg_miibus_statchg; static void rue_cfg_reset(struct rue_softc *sc); static void rue_start_cb(struct ifnet *ifp); static void rue_start_transfers(struct rue_softc *sc); static void rue_init_cb(void *arg); static int rue_ifmedia_upd_cb(struct ifnet *ifp); static void rue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr); static int rue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data); static void rue_watchdog(void *arg); static const struct usb2_config rue_config[RUE_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = MCLBYTES, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &rue_bulk_write_callback, .mh.timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + 4), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &rue_bulk_read_callback, .mh.timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &rue_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &rue_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [4] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.bufsize = 0, /* use wMaxPacketSize */ .mh.callback = &rue_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &rue_intr_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static device_method_t rue_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rue_probe), DEVMETHOD(device_attach, rue_attach), DEVMETHOD(device_detach, rue_detach), DEVMETHOD(device_shutdown, rue_shutdown), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, rue_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, rue_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, rue_cfg_miibus_statchg), {0, 0} }; static driver_t rue_driver = { .name = "rue", .methods = rue_methods, .size = sizeof(struct rue_softc), }; static devclass_t rue_devclass; DRIVER_MODULE(rue, ushub, rue_driver, rue_devclass, NULL, 0); DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(rue, usb2_ethernet, 1, 1, 1); MODULE_DEPEND(rue, usb2_core, 1, 1, 1); MODULE_DEPEND(rue, ether, 1, 1, 1); MODULE_DEPEND(rue, miibus, 1, 1, 1); static void rue_cfg_do_request(struct rue_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } #define RUE_CFG_SETBIT(sc, reg, x) \ rue_cfg_csr_write_1(sc, reg, rue_cfg_csr_read_1(sc, reg) | (x)) #define RUE_CFG_CLRBIT(sc, reg, x) \ rue_cfg_csr_write_1(sc, reg, rue_cfg_csr_read_1(sc, reg) & ~(x)) static void rue_cfg_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); rue_cfg_do_request(sc, &req, buf); } static void rue_cfg_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UR_SET_ADDRESS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); rue_cfg_do_request(sc, &req, buf); } static uint8_t rue_cfg_csr_read_1(struct rue_softc *sc, uint16_t reg) { uint8_t val; rue_cfg_read_mem(sc, reg, &val, 1); return (val); } static uint16_t rue_cfg_csr_read_2(struct rue_softc *sc, uint16_t reg) { uint8_t val[2]; rue_cfg_read_mem(sc, reg, &val, 2); return (UGETW(val)); } static void rue_cfg_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val) { rue_cfg_write_mem(sc, reg, &val, 1); } static void rue_cfg_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val) { uint8_t temp[2]; USETW(temp, val); rue_cfg_write_mem(sc, reg, &temp, 2); } static void rue_cfg_csr_write_4(struct rue_softc *sc, int reg, uint32_t val) { uint8_t temp[4]; USETDW(temp, val); rue_cfg_write_mem(sc, reg, &temp, 4); } static int rue_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct rue_softc *sc = device_get_softc(dev); uint16_t rval; uint16_t ruereg; uint8_t do_unlock; if (phy != 0) { /* RTL8150 supports PHY == 0, only */ return (0); } /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: rval = 0; goto done; default: if ((RUE_REG_MIN <= reg) && (reg <= RUE_REG_MAX)) { rval = rue_cfg_csr_read_1(sc, reg); goto done; } printf("rue%d: bad phy register\n", sc->sc_unit); rval = 0; goto done; } rval = rue_cfg_csr_read_2(sc, ruereg); done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (rval); } static int rue_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rue_softc *sc = device_get_softc(dev); uint16_t ruereg; uint8_t do_unlock; if (phy != 0) { /* RTL8150 supports PHY == 0, only */ return (0); } /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } switch (reg) { case MII_BMCR: ruereg = RUE_BMCR; break; case MII_BMSR: ruereg = RUE_BMSR; break; case MII_ANAR: ruereg = RUE_ANAR; break; case MII_ANER: ruereg = RUE_AER; break; case MII_ANLPAR: ruereg = RUE_ANLP; break; case MII_PHYIDR1: case MII_PHYIDR2: goto done; default: if ((RUE_REG_MIN <= reg) && (reg <= RUE_REG_MAX)) { rue_cfg_csr_write_1(sc, reg, data); goto done; } printf("%s: bad phy register\n", sc->sc_name); goto done; } rue_cfg_csr_write_2(sc, ruereg, data); done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (0); } static void rue_cfg_miibus_statchg(device_t dev) { /* * When the code below is enabled the card starts doing weird * things after link going from UP to DOWN and back UP. * * Looks like some of register writes below messes up PHY * interface. * * No visible regressions were found after commenting this code * out, so that disable it for good. */ #if 0 struct rue_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); uint16_t bmcr; uint8_t do_unlock; /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } RUE_CFG_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); bmcr = rue_cfg_csr_read_2(sc, RUE_BMCR); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) bmcr |= RUE_BMCR_SPD_SET; else bmcr &= ~RUE_BMCR_SPD_SET; if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) bmcr |= RUE_BMCR_DUPLEX; else bmcr &= ~RUE_BMCR_DUPLEX; rue_cfg_csr_write_2(sc, RUE_BMCR, bmcr); RUE_CFG_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE)); if (do_unlock) { mtx_unlock(&sc->sc_mtx); } #endif } static void rue_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) { uint8_t h; h = ether_crc32_be(ptr, ETHER_ADDR_LEN) >> 26; cc->if_hash[h / 8] |= 1 << (h & 7); cc->if_nhash = 1; } static void rue_config_copy(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { bzero(cc, sizeof(*cc)); usb2_ether_cc(sc->sc_ifp, &rue_mchash, cc); } /* * Program the 64-bit multicast hash filter. */ static void rue_cfg_promisc_upd(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t rxcfg; rxcfg = rue_cfg_csr_read_2(sc, RUE_RCR); if ((cc->if_flags & IFF_ALLMULTI) || (cc->if_flags & IFF_PROMISC)) { rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP); rxcfg &= ~RUE_RCR_AM; rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); rue_cfg_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF); rue_cfg_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF); return; } /* first, zero all the existing hash bits */ rue_cfg_csr_write_4(sc, RUE_MAR0, 0); rue_cfg_csr_write_4(sc, RUE_MAR4, 0); if (cc->if_nhash) rxcfg |= RUE_RCR_AM; else rxcfg &= ~RUE_RCR_AM; rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP); rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); rue_cfg_write_mem(sc, RUE_MAR0, cc->if_hash, 4); rue_cfg_write_mem(sc, RUE_MAR4, cc->if_hash + 4, 4); } static void rue_cfg_reset(struct rue_softc *sc) { usb2_error_t err; uint16_t to; rue_cfg_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST); for (to = 0;; to++) { if (to < RUE_TIMEOUT) { err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); if (err) { break; } if (!(rue_cfg_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST)) { break; } } else { printf("%s: reset timeout!\n", sc->sc_name); break; } } err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } /* * Probe for a RTL8150 chip. */ static int rue_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != RUE_CONFIG_IDX) { return (ENXIO); } if (uaa->info.bIfaceIndex != RUE_IFACE_IDX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(rue_devs, sizeof(rue_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int rue_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct rue_softc *sc = device_get_softc(dev); int32_t error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_mtx, "rue lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = RUE_IFACE_IDX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rue_config, RUE_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, NULL, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); sc->sc_flags |= RUE_FLAG_WAIT_LINK; /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rue_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - rue_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: rue_detach(dev); return (ENXIO); /* failure */ } static void rue_cfg_first_time_setup(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; int error; uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; /* reset the adapter */ rue_cfg_reset(sc); /* get station address from the EEPROM */ rue_cfg_read_mem(sc, RUE_EEPROM_IDR0, eaddr, ETHER_ADDR_LEN); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } sc->sc_evilhack = ifp; ifp->if_softc = sc; if_initname(ifp, "rue", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = rue_ioctl_cb; ifp->if_start = rue_start_cb; ifp->if_watchdog = NULL; ifp->if_init = rue_init_cb; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); /* * XXX need Giant when accessing the device structures ! */ mtx_unlock(&sc->sc_mtx); mtx_lock(&Giant); /* MII setup */ error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, &rue_ifmedia_upd_cb, &rue_ifmedia_sts_cb); mtx_unlock(&Giant); mtx_lock(&sc->sc_mtx); if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; mtx_unlock(&sc->sc_mtx); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); mtx_lock(&sc->sc_mtx); done: return; } static int rue_detach(device_t dev) { struct rue_softc *sc = device_get_softc(dev); struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); rue_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, RUE_ENDPT_MAX); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void rue_intr_clear_stall_callback(struct usb2_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[4]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUE_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); } } static void rue_intr_callback(struct usb2_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct rue_intrpkt pkt; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) && (xfer->actlen >= sizeof(pkt))) { usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt)); ifp->if_ierrors += pkt.rue_rxlost_cnt; ifp->if_ierrors += pkt.rue_crcerr_cnt; ifp->if_collisions += pkt.rue_col_cnt; } case USB_ST_SETUP: if (sc->sc_flags & RUE_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer[5]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* start clear stall */ sc->sc_flags |= RUE_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[5]); } return; } } static void rue_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUE_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void rue_bulk_read_callback(struct usb2_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; uint16_t status; struct mbuf *m = NULL; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen < 4) { ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, xfer->actlen - 4, &status, sizeof(status)); status = le16toh(status); /* check recieve packet was valid or not */ if ((status & RUE_RXSTAT_VALID) == 0) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen -= 4; if (xfer->actlen < sizeof(struct ether_header)) { ifp->if_ierrors++; goto tr_setup; } m = usb2_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usb2_copy_out(xfer->frbuffers, 0, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; case USB_ST_SETUP: tr_setup: if (sc->sc_flags & RUE_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "if_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); (ifp->if_input) (ifp, m); mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUE_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } DPRINTF("bulk read error, %s\n", usb2_errstr(xfer->error)); return; } } static void rue_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUE_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void rue_bulk_write_callback(struct usb2_xfer *xfer) { struct rue_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint32_t temp_len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & RUE_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & RUE_FLAG_WAIT_LINK) { /* * don't send anything if there is no link ! */ goto done; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } temp_len = m->m_pkthdr.len; usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* * This is an undocumented behavior. * RTL8150 chip doesn't send frame length smaller than * RUE_MIN_FRAMELEN (60) byte packet. */ if (temp_len < RUE_MIN_FRAMELEN) { usb2_bzero(xfer->frbuffers, temp_len, RUE_MIN_FRAMELEN - temp_len); temp_len = RUE_MIN_FRAMELEN; } xfer->frlengths[0] = temp_len; /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); usb2_start_hardware(xfer); done: return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUE_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; return; } } static void rue_cfg_tick(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & RUE_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~RUE_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ rue_start_transfers(sc); } static void rue_start_cb(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); rue_start_transfers(sc); mtx_unlock(&sc->sc_mtx); } static void rue_start_transfers(struct rue_softc *sc) { if ((sc->sc_flags & RUE_FLAG_LL_READY) && (sc->sc_flags & RUE_FLAG_HL_READY)) { /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[4]); usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); } } static void rue_init_cb(void *arg) { struct rue_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rue_cfg_pre_init, &rue_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static void rue_cfg_pre_init(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ rue_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= RUE_FLAG_HL_READY; } static void rue_cfg_init(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct mii_data *mii = GET_MII(sc); uint16_t rxcfg; /* * Cancel pending I/O */ rue_cfg_stop(sc, cc, 0); /* set MAC address */ rue_cfg_write_mem(sc, RUE_IDR0, cc->if_lladdr, ETHER_ADDR_LEN); /* * Set the initial TX and RX configuration. */ rue_cfg_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG); rxcfg = RUE_RCR_CONFIG; /* Set capture broadcast bit to capture broadcast frames. */ if (cc->if_flags & IFF_BROADCAST) rxcfg |= RUE_RCR_AB; else rxcfg &= ~RUE_RCR_AB; rue_cfg_csr_write_2(sc, RUE_RCR, rxcfg); /* Load the multicast filter */ rue_cfg_promisc_upd(sc, cc, 0); /* Enable RX and TX */ rue_cfg_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN)); mii_mediachg(mii); sc->sc_flags |= (RUE_FLAG_READ_STALL | RUE_FLAG_WRITE_STALL | RUE_FLAG_LL_READY); rue_start_transfers(sc); } /* * Set media options. */ static int rue_ifmedia_upd_cb(struct ifnet *ifp) { struct rue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rue_cfg_ifmedia_upd, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } static void rue_cfg_ifmedia_upd(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= RUE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); } /* * Report current media status. */ static void rue_ifmedia_sts_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rue_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; mtx_unlock(&sc->sc_mtx); } static int rue_ioctl_cb(struct ifnet *ifp, u_long command, caddr_t data) { struct rue_softc *sc = ifp->if_softc; struct mii_data *mii; int error = 0; switch (command) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &rue_config_copy, &rue_cfg_promisc_upd, 0, 0); } else { usb2_config_td_queue_command (&sc->sc_config_td, &rue_cfg_pre_init, &rue_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &rue_cfg_pre_stop, &rue_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rue_config_copy, &rue_cfg_promisc_upd, 0, 0); mtx_unlock(&sc->sc_mtx); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &mii->mii_media, command); } break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void rue_watchdog(void *arg) { struct rue_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rue_cfg_tick, 0, 0); usb2_callout_reset(&sc->sc_watchdog, hz, &rue_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } /* * NOTE: can be called when "ifp" is NULL */ static void rue_cfg_pre_stop(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ rue_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(RUE_FLAG_HL_READY | RUE_FLAG_LL_READY); sc->sc_flags |= RUE_FLAG_WAIT_LINK; /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); usb2_transfer_stop(sc->sc_xfer[4]); usb2_transfer_stop(sc->sc_xfer[5]); } static void rue_cfg_stop(struct rue_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { rue_cfg_csr_write_1(sc, RUE_CR, 0x00); rue_cfg_reset(sc); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int rue_shutdown(device_t dev) { struct rue_softc *sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rue_cfg_pre_stop, &rue_cfg_stop, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } Index: projects/cambria/sys/dev/usb2/ethernet/if_udav2.c =================================================================== --- projects/cambria/sys/dev/usb2/ethernet/if_udav2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/ethernet/if_udav2.c (revision 186460) @@ -1,1329 +1,1324 @@ /* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */ /* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2003 * Shingo WATANABE . 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. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * */ /* * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY) * The spec can be found at the following url. * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf */ /* * NOTE: all function names beginning like "udav_cfg_" can only * be called from within the config thread function ! */ /* * TODO: * Interrupt Endpoint support * External PHYs */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #define usb2_config_td_cc usb2_ether_cc #define usb2_config_td_softc udav_softc #define USB_DEBUG_VAR udav_debug #include #include #include #include #include #include #include #include #include #include /* prototypes */ static device_probe_t udav_probe; static device_attach_t udav_attach; static device_detach_t udav_detach; static device_shutdown_t udav_shutdown; static usb2_callback_t udav_bulk_write_clear_stall_callback; static usb2_callback_t udav_bulk_write_callback; static usb2_callback_t udav_bulk_read_clear_stall_callback; static usb2_callback_t udav_bulk_read_callback; static usb2_callback_t udav_intr_clear_stall_callback; static usb2_callback_t udav_intr_callback; static usb2_config_td_command_t udav_cfg_first_time_setup; static usb2_config_td_command_t udav_cfg_pre_init; static usb2_config_td_command_t udav_cfg_init; static usb2_config_td_command_t udav_config_copy; static usb2_config_td_command_t udav_cfg_promisc_upd; static usb2_config_td_command_t udav_cfg_pre_stop; static usb2_config_td_command_t udav_cfg_stop; static usb2_config_td_command_t udav_cfg_ifmedia_change; static usb2_config_td_command_t udav_cfg_tick; static void udav_cfg_do_request(struct udav_softc *, struct usb2_device_request *, void *); static void udav_cfg_csr_read(struct udav_softc *, uint16_t, void *, uint16_t); static void udav_cfg_csr_write(struct udav_softc *, uint16_t, void *, uint16_t); static uint8_t udav_cfg_csr_read1(struct udav_softc *, uint16_t); static void udav_cfg_csr_write1(struct udav_softc *, uint16_t, uint8_t); static void udav_init_cb(void *); static void udav_cfg_reset(struct udav_softc *); static void udav_start_cb(struct ifnet *); static void udav_start_transfers(struct udav_softc *); static int udav_ioctl_cb(struct ifnet *, u_long, caddr_t); static void udav_watchdog(void *); static int udav_ifmedia_change_cb(struct ifnet *); static void udav_ifmedia_status_cb(struct ifnet *, struct ifmediareq *); static miibus_readreg_t udav_cfg_miibus_readreg; static miibus_writereg_t udav_cfg_miibus_writereg; static miibus_statchg_t udav_cfg_miibus_statchg; static const struct usb2_config udav_config[UDAV_ENDPT_MAX] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (MCLBYTES + 2), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &udav_bulk_write_callback, .mh.timeout = 10000, /* 10 seconds */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + 3), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &udav_bulk_read_callback, .mh.timeout = 0, /* no timeout */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &udav_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &udav_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [4] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.bufsize = 0, /* use wMaxPacketSize */ .mh.callback = &udav_intr_callback, }, [5] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &udav_intr_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static device_method_t udav_methods[] = { /* Device interface */ DEVMETHOD(device_probe, udav_probe), DEVMETHOD(device_attach, udav_attach), DEVMETHOD(device_detach, udav_detach), DEVMETHOD(device_shutdown, udav_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, udav_cfg_miibus_readreg), DEVMETHOD(miibus_writereg, udav_cfg_miibus_writereg), DEVMETHOD(miibus_statchg, udav_cfg_miibus_statchg), {0, 0} }; static driver_t udav_driver = { .name = "udav", .methods = udav_methods, .size = sizeof(struct udav_softc), }; static devclass_t udav_devclass; DRIVER_MODULE(udav, ushub, udav_driver, udav_devclass, NULL, 0); DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(udav, usb2_ethernet, 1, 1, 1); MODULE_DEPEND(udav, usb2_core, 1, 1, 1); MODULE_DEPEND(udav, ether, 1, 1, 1); MODULE_DEPEND(udav, miibus, 1, 1, 1); #if USB_DEBUG static int udav_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav"); SYSCTL_INT(_hw_usb2_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0, "Debug level"); #endif #define UDAV_CFG_SETBIT(sc, reg, x) \ udav_cfg_csr_write1(sc, reg, udav_cfg_csr_read1(sc, reg) | (x)) #define UDAV_CFG_CLRBIT(sc, reg, x) \ udav_cfg_csr_write1(sc, reg, udav_cfg_csr_read1(sc, reg) & ~(x)) static const struct usb2_device_id udav_devs[] = { /* ShanTou DM9601 USB NIC */ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)}, /* ShanTou ST268 USB NIC */ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)}, /* Corega USB-TXC */ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)}, }; static int udav_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa)); } static int udav_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct udav_softc *sc = device_get_softc(dev); int32_t error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); sc->sc_flags = USB_GET_DRIVER_INFO(uaa); device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); mtx_init(&sc->sc_mtx, "udav lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = UDAV_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, udav_config, UDAV_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed!\n"); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, NULL, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); sc->sc_flags |= UDAV_FLAG_WAIT_LINK; /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &udav_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - udav_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: udav_detach(dev); return (ENXIO); /* failure */ } static void udav_cfg_first_time_setup(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; int error; uint8_t eaddr[min(ETHER_ADDR_LEN, 6)]; /* reset the adapter */ udav_cfg_reset(sc); /* get Ethernet Address */ udav_cfg_csr_read(sc, UDAV_PAR, eaddr, ETHER_ADDR_LEN); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { printf("%s: could not if_alloc()\n", sc->sc_name); goto done; } sc->sc_evilhack = ifp; ifp->if_softc = sc; if_initname(ifp, "udav", sc->sc_unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = udav_start_cb; ifp->if_ioctl = udav_ioctl_cb; ifp->if_watchdog = NULL; ifp->if_init = udav_init_cb; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); /* * XXX need Giant when accessing the device structures ! */ mtx_unlock(&sc->sc_mtx); mtx_lock(&Giant); error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, &udav_ifmedia_change_cb, &udav_ifmedia_status_cb); mtx_unlock(&Giant); mtx_lock(&sc->sc_mtx); if (error) { printf("%s: MII without any PHY!\n", sc->sc_name); if_free(ifp); goto done; } sc->sc_ifp = ifp; mtx_unlock(&sc->sc_mtx); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); mtx_lock(&sc->sc_mtx); done: return; } static int udav_detach(device_t dev) { struct udav_softc *sc = device_get_softc(dev); struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); udav_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, UDAV_ENDPT_MAX); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { ether_ifdetach(ifp); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void udav_cfg_do_request(struct udav_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } #if 0 static void udav_cfg_mem_read(struct udav_softc *sc, uint16_t offset, void *buf, uint16_t len) { struct usb2_device_request req; len &= 0xff; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_READ; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); } static void udav_cfg_mem_write(struct udav_softc *sc, uint16_t offset, void *buf, uint16_t len) { struct usb2_device_request req; len &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_WRITE; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); } static void udav_cfg_mem_write1(struct udav_softc *sc, uint16_t offset, uint8_t ch) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_MEM_WRITE1; USETW(req.wValue, ch); USETW(req.wIndex, offset); USETW(req.wLength, 0x0000); udav_cfg_do_request(sc, &req, NULL); } #endif static void udav_cfg_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, uint16_t len) { struct usb2_device_request req; len &= 0xff; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_READ; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); } static void udav_cfg_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, uint16_t len) { struct usb2_device_request req; offset &= 0xff; len &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_WRITE; USETW(req.wValue, 0x0000); USETW(req.wIndex, offset); USETW(req.wLength, len); udav_cfg_do_request(sc, &req, buf); } static uint8_t udav_cfg_csr_read1(struct udav_softc *sc, uint16_t offset) { uint8_t val; udav_cfg_csr_read(sc, offset, &val, 1); return (val); } static void udav_cfg_csr_write1(struct udav_softc *sc, uint16_t offset, uint8_t ch) { struct usb2_device_request req; offset &= 0xff; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UDAV_REQ_REG_WRITE1; USETW(req.wValue, ch); USETW(req.wIndex, offset); USETW(req.wLength, 0x0000); udav_cfg_do_request(sc, &req, NULL); } static void udav_init_cb(void *arg) { struct udav_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &udav_cfg_pre_init, &udav_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static void udav_cfg_pre_init(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; /* immediate configuration */ udav_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= UDAV_FLAG_HL_READY; } static void udav_cfg_init(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct mii_data *mii = GET_MII(sc); /* * Cancel pending I/O */ udav_cfg_stop(sc, cc, 0); /* set MAC address */ udav_cfg_csr_write(sc, UDAV_PAR, cc->if_lladdr, ETHER_ADDR_LEN); /* initialize network control register */ /* disable loopback */ UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1); /* Initialize RX control register */ UDAV_CFG_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC); /* load multicast filter and update promiscious mode bit */ udav_cfg_promisc_upd(sc, cc, 0); /* enable RX */ UDAV_CFG_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN); /* clear POWER_DOWN state of internal PHY */ UDAV_CFG_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0); UDAV_CFG_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0); mii_mediachg(mii); sc->sc_flags |= (UDAV_FLAG_READ_STALL | UDAV_FLAG_WRITE_STALL | UDAV_FLAG_LL_READY); udav_start_transfers(sc); } static void udav_cfg_reset(struct udav_softc *sc) { usb2_error_t err; uint16_t to; /* Select PHY */ #if 1 /* * XXX: force select internal phy. * external phy routines are not tested. */ UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); #else if (sc->sc_flags & UDAV_EXT_PHY) { UDAV_CFG_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); } else { UDAV_CFG_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY); } #endif UDAV_CFG_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST); for (to = 0;; to++) { if (to < 100) { err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); if (err) { break; } if (!(udav_cfg_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST)) { break; } } else { printf("%s: reset timeout!\n", sc->sc_name); break; } } err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } #define UDAV_BITS 6 static void udav_mchash(struct usb2_config_td_cc *cc, const uint8_t *ptr) { uint8_t h; h = ether_crc32_le(ptr, ETHER_ADDR_LEN) & ((1 << UDAV_BITS) - 1); cc->if_hash[h >> 3] |= 1 << (h & 0x7); } static void udav_config_copy(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { bzero(cc, sizeof(*cc)); usb2_ether_cc(sc->sc_ifp, &udav_mchash, cc); } static void udav_cfg_promisc_upd(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint8_t rxmode; rxmode = udav_cfg_csr_read1(sc, UDAV_RCR); rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC); if (cc->if_flags & IFF_PROMISC) { rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC; } else if (cc->if_flags & IFF_ALLMULTI) { rxmode |= UDAV_RCR_ALL; } /* write hash value to the register */ udav_cfg_csr_write(sc, UDAV_MAR, cc->if_hash, 8); /* write new mode bits */ udav_cfg_csr_write1(sc, UDAV_RCR, rxmode); } static void udav_start_cb(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); udav_start_transfers(sc); mtx_unlock(&sc->sc_mtx); } static void udav_start_transfers(struct udav_softc *sc) { if ((sc->sc_flags & UDAV_FLAG_LL_READY) && (sc->sc_flags & UDAV_FLAG_HL_READY)) { /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[4]); usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); } } static void udav_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UDAV_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void udav_bulk_write_callback(struct usb2_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint32_t extra_len; uint32_t temp_len; uint8_t buf[2]; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & UDAV_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); goto done; } if (sc->sc_flags & UDAV_FLAG_WAIT_LINK) { /* * don't send anything if there is no link ! */ goto done; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { goto done; } if (m->m_pkthdr.len > MCLBYTES) { m->m_pkthdr.len = MCLBYTES; } if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) { extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len; } else { extra_len = 0; } temp_len = (m->m_pkthdr.len + extra_len); /* * the frame length is specified in the first 2 bytes of the * buffer */ buf[0] = (uint8_t)(temp_len); buf[1] = (uint8_t)(temp_len >> 8); temp_len += 2; usb2_copy_in(xfer->frbuffers, 0, buf, 2); usb2_m_copy_in(xfer->frbuffers, 2, m, 0, m->m_pkthdr.len); if (extra_len) { usb2_bzero(xfer->frbuffers, temp_len - extra_len, extra_len); } /* * if there's a BPF listener, bounce a copy * of this frame to him: */ BPF_MTAP(ifp, m); m_freem(m); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); done: return; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDAV_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; return; } } static void udav_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UDAV_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void udav_bulk_read_callback(struct usb2_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; uint8_t status; uint16_t total_len; struct mbuf *m = NULL; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen < 1) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen -= 1; usb2_copy_out(xfer->frbuffers, 0, &status, 1); if (status & UDAV_RSR_LCS) { ifp->if_collisions++; goto tr_setup; } if ((status & UDAV_RSR_ERR) || (xfer->actlen < 2)) { ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 1, &total_len, 2); total_len = le16toh(total_len); xfer->actlen -= 2; xfer->actlen = min(xfer->actlen, total_len); if (xfer->actlen < (sizeof(struct ether_header) + ETHER_CRC_LEN)) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen -= ETHER_CRC_LEN; m = usb2_ether_get_mbuf(); if (m == NULL) { ifp->if_ierrors++; goto tr_setup; } xfer->actlen = min(xfer->actlen, m->m_len); usb2_copy_out(xfer->frbuffers, 3, m->m_data, xfer->actlen); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = xfer->actlen; case USB_ST_SETUP: tr_setup: if (sc->sc_flags & UDAV_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "if_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); (ifp->if_input) (ifp, m); mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UDAV_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } DPRINTF("bulk read error, %s\n", usb2_errstr(xfer->error)); return; } } static void udav_intr_clear_stall_callback(struct usb2_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[4]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UDAV_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); } } static void udav_intr_callback(struct usb2_xfer *xfer) { struct udav_softc *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: if (sc->sc_flags & UDAV_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer[5]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* start clear stall */ sc->sc_flags |= UDAV_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[5]); } return; } } static int udav_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct udav_softc *sc = ifp->if_softc; struct mii_data *mii; int error = 0; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &udav_config_copy, &udav_cfg_promisc_upd, 0, 0); } else { usb2_config_td_queue_command (&sc->sc_config_td, &udav_cfg_pre_init, &udav_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &udav_cfg_pre_stop, &udav_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); break; case SIOCADDMULTI: case SIOCDELMULTI: mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &udav_config_copy, &udav_cfg_promisc_upd, 0, 0); mtx_unlock(&sc->sc_mtx); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = GET_MII(sc); if (mii == NULL) { error = EINVAL; } else { error = ifmedia_ioctl (ifp, (void *)data, &mii->mii_media, cmd); } break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void udav_watchdog(void *arg) { struct udav_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &udav_cfg_tick, 0, 0); usb2_callout_reset(&sc->sc_watchdog, hz, &udav_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } /* * NOTE: can be called when "ifp" is NULL */ static void udav_cfg_pre_stop(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ udav_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(UDAV_FLAG_HL_READY | UDAV_FLAG_LL_READY); sc->sc_flags |= UDAV_FLAG_WAIT_LINK; /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); usb2_transfer_stop(sc->sc_xfer[4]); usb2_transfer_stop(sc->sc_xfer[5]); } /* * NOTE: can be called when "ifp" is NULL */ static void udav_cfg_stop(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { udav_cfg_reset(sc); } static int udav_ifmedia_change_cb(struct ifnet *ifp) { struct udav_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, NULL, &udav_cfg_ifmedia_change, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } static void udav_cfg_ifmedia_change(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } sc->sc_flags |= UDAV_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { mii_phy_reset(miisc); } } mii_mediachg(mii); } static void udav_ifmedia_status_cb(struct ifnet *ifp, struct ifmediareq *ifmr) { struct udav_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifmr->ifm_active = sc->sc_media_active; ifmr->ifm_status = sc->sc_media_status; } else { ifmr->ifm_active = IFM_ETHER | IFM_NONE; ifmr->ifm_status = 0; } mtx_unlock(&sc->sc_mtx); } static void udav_cfg_tick(struct udav_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct mii_data *mii = GET_MII(sc); if ((ifp == NULL) || (mii == NULL)) { /* not ready */ return; } mii_tick(mii); mii_pollstat(mii); if ((sc->sc_flags & UDAV_FLAG_WAIT_LINK) && (mii->mii_media_status & IFM_ACTIVE) && (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { sc->sc_flags &= ~UDAV_FLAG_WAIT_LINK; } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; /* start stopped transfers, if any */ udav_start_transfers(sc); } static int udav_cfg_miibus_readreg(device_t dev, int phy, int reg) { struct udav_softc *sc = device_get_softc(dev); uint16_t data16; uint8_t val[2]; uint8_t do_unlock; /* XXX: one PHY only for the internal PHY */ if (phy != 0) { return (0); } /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } /* select internal PHY and set PHY register address */ udav_cfg_csr_write1(sc, UDAV_EPAR, UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); /* select PHY operation and start read command */ udav_cfg_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR); /* XXX: should we wait? */ /* end read command */ UDAV_CFG_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR); /* retrieve the result from data registers */ udav_cfg_csr_read(sc, UDAV_EPDRL, val, 2); if (do_unlock) { mtx_unlock(&sc->sc_mtx); } data16 = (val[0] | (val[1] << 8)); DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n", phy, reg, data16); return (data16); } static int udav_cfg_miibus_writereg(device_t dev, int phy, int reg, int data) { struct udav_softc *sc = device_get_softc(dev); uint8_t val[2]; uint8_t do_unlock; /* XXX: one PHY only for the internal PHY */ if (phy != 0) { return (0); } /* avoid recursive locking */ if (mtx_owned(&sc->sc_mtx)) { do_unlock = 0; } else { mtx_lock(&sc->sc_mtx); do_unlock = 1; } /* select internal PHY and set PHY register address */ udav_cfg_csr_write1(sc, UDAV_EPAR, UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK)); /* put the value to the data registers */ val[0] = (data & 0xff); val[1] = (data >> 8) & 0xff; udav_cfg_csr_write(sc, UDAV_EPDRL, val, 2); /* select PHY operation and start write command */ udav_cfg_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW); /* XXX: should we wait? */ /* end write command */ UDAV_CFG_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW); if (do_unlock) { mtx_unlock(&sc->sc_mtx); } return (0); } static void udav_cfg_miibus_statchg(device_t dev) { /* nothing to do */ } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int udav_shutdown(device_t dev) { struct udav_softc *sc = device_get_softc(dev); mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &udav_cfg_pre_stop, &udav_cfg_stop, 0, 0); mtx_unlock(&sc->sc_mtx); return (0); } Index: projects/cambria/sys/dev/usb2/input/ukbd2.c =================================================================== --- projects/cambria/sys/dev/usb2/input/ukbd2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/input/ukbd2.c (revision 186460) @@ -1,1492 +1,1489 @@ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. * */ /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include "opt_compat.h" #include "opt_kbd.h" #include "opt_ukbd.h" #include #include #include #include #define USB_DEBUG_VAR ukbd_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* the initial key map, accent map and fkey strings */ #if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE) #define KBD_DFLT_KEYMAP #include "ukbdmap.h" #endif /* the following file must be included after "ukbdmap.h" */ #include #if USB_DEBUG static int ukbd_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd"); SYSCTL_INT(_hw_usb2_ukbd, OID_AUTO, debug, CTLFLAG_RW, &ukbd_debug, 0, "Debug level"); #endif #define UPROTO_BOOT_KEYBOARD 1 #define UKBD_EMULATE_ATSCANCODE 1 #define UKBD_DRIVER_NAME "ukbd" #define UKBD_NMOD 8 /* units */ #define UKBD_NKEYCODE 6 /* units */ #define UKBD_N_TRANSFER 3 /* units */ #define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */ #define UKBD_IN_BUF_FULL (UKBD_IN_BUF_SIZE / 2) /* bytes */ #define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */ struct ukbd_data { uint8_t modifiers; #define MOD_CONTROL_L 0x01 #define MOD_CONTROL_R 0x10 #define MOD_SHIFT_L 0x02 #define MOD_SHIFT_R 0x20 #define MOD_ALT_L 0x04 #define MOD_ALT_R 0x40 #define MOD_WIN_L 0x08 #define MOD_WIN_R 0x80 uint8_t reserved; uint8_t keycode[UKBD_NKEYCODE]; } __packed; struct ukbd_softc { keyboard_t sc_kbd; keymap_t sc_keymap; accentmap_t sc_accmap; fkeytab_t sc_fkeymap[UKBD_NFKEY]; struct usb2_callout sc_callout; struct ukbd_data sc_ndata; struct ukbd_data sc_odata; struct usb2_device *sc_udev; struct usb2_interface *sc_iface; struct usb2_xfer *sc_xfer[UKBD_N_TRANSFER]; uint32_t sc_ntime[UKBD_NKEYCODE]; uint32_t sc_otime[UKBD_NKEYCODE]; uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */ uint32_t sc_time_ms; uint32_t sc_composed_char; /* composed char code, if non-zero */ #ifdef UKBD_EMULATE_ATSCANCODE uint32_t sc_buffered_char[2]; #endif uint32_t sc_flags; /* flags */ #define UKBD_FLAG_COMPOSE 0x0001 #define UKBD_FLAG_POLLING 0x0002 #define UKBD_FLAG_SET_LEDS 0x0004 #define UKBD_FLAG_INTR_STALL 0x0008 #define UKBD_FLAG_ATTACHED 0x0010 #define UKBD_FLAG_GONE 0x0020 int32_t sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int32_t sc_state; /* shift/lock key state */ int32_t sc_accents; /* accent key index (> 0) */ uint16_t sc_inputs; uint16_t sc_inputhead; uint16_t sc_inputtail; uint8_t sc_leds; /* store for async led requests */ uint8_t sc_iface_index; uint8_t sc_iface_no; }; #define KEY_ERROR 0x01 #define KEY_PRESS 0 #define KEY_RELEASE 0x400 #define KEY_INDEX(c) ((c) & 0xFF) #define SCAN_PRESS 0 #define SCAN_RELEASE 0x80 #define SCAN_PREFIX_E0 0x100 #define SCAN_PREFIX_E1 0x200 #define SCAN_PREFIX_CTL 0x400 #define SCAN_PREFIX_SHIFT 0x800 #define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \ SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT) #define SCAN_CHAR(c) ((c) & 0x7f) struct ukbd_mods { uint32_t mask, key; }; static const struct ukbd_mods ukbd_mods[UKBD_NMOD] = { {MOD_CONTROL_L, 0xe0}, {MOD_CONTROL_R, 0xe4}, {MOD_SHIFT_L, 0xe1}, {MOD_SHIFT_R, 0xe5}, {MOD_ALT_L, 0xe2}, {MOD_ALT_R, 0xe6}, {MOD_WIN_L, 0xe3}, {MOD_WIN_R, 0xe7}, }; #define NN 0 /* no translation */ /* * Translate USB keycodes to AT keyboard scancodes. */ /* * FIXME: Mac USB keyboard generates: * 0x53: keypad NumLock/Clear * 0x66: Power * 0x67: keypad = * 0x68: F13 * 0x69: F14 * 0x6a: F15 */ static const uint8_t ukbd_trtab[256] = { 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */ 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */ 97, 100, 95, 69, 91, 55, 74, 78,/* 50 - 57 */ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */ 121, 120, NN, NN, NN, NN, NN, 115, /* 80 - 87 */ 112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */ }; /* prototypes */ static void ukbd_timeout(void *); static void ukbd_set_leds(struct ukbd_softc *, uint8_t); static int ukbd_set_typematic(keyboard_t *, int); #ifdef UKBD_EMULATE_ATSCANCODE static int ukbd_key2scan(struct ukbd_softc *, int, int, int); #endif static uint32_t ukbd_read_char(keyboard_t *, int); static void ukbd_clear_state(keyboard_t *); static int ukbd_ioctl(keyboard_t *, u_long, caddr_t); static int ukbd_enable(keyboard_t *); static int ukbd_disable(keyboard_t *); static void ukbd_interrupt(struct ukbd_softc *); static device_probe_t ukbd_probe; static device_attach_t ukbd_attach; static device_detach_t ukbd_detach; static device_resume_t ukbd_resume; static void ukbd_put_key(struct ukbd_softc *sc, uint32_t key) { mtx_assert(&Giant, MA_OWNED); DPRINTF("0x%02x (%d) %s\n", key, key, (key & KEY_RELEASE) ? "released" : "pressed"); if (sc->sc_inputs < UKBD_IN_BUF_SIZE) { sc->sc_input[sc->sc_inputtail] = key; ++(sc->sc_inputs); ++(sc->sc_inputtail); if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) { sc->sc_inputtail = 0; } } else { DPRINTF("input buffer is full\n"); } } static int32_t ukbd_get_key(struct ukbd_softc *sc, uint8_t wait) { int32_t c; mtx_assert(&Giant, MA_OWNED); if (sc->sc_inputs == 0) { /* start transfer, if not already started */ usb2_transfer_start(sc->sc_xfer[0]); } if (sc->sc_flags & UKBD_FLAG_POLLING) { DPRINTFN(2, "polling\n"); while (sc->sc_inputs == 0) { usb2_do_poll(sc->sc_xfer, UKBD_N_TRANSFER); DELAY(1000); /* delay 1 ms */ sc->sc_time_ms++; /* support repetition of keys: */ ukbd_interrupt(sc); if (!wait) { break; } } } if (sc->sc_inputs == 0) { c = -1; } else { c = sc->sc_input[sc->sc_inputhead]; --(sc->sc_inputs); ++(sc->sc_inputhead); if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) { sc->sc_inputhead = 0; } } return (c); } static void ukbd_interrupt(struct ukbd_softc *sc) { uint32_t n_mod; uint32_t o_mod; uint32_t now = sc->sc_time_ms; uint32_t dtime; uint32_t c; uint8_t key; uint8_t i; uint8_t j; if (sc->sc_ndata.keycode[0] == KEY_ERROR) { goto done; } n_mod = sc->sc_ndata.modifiers; o_mod = sc->sc_odata.modifiers; if (n_mod != o_mod) { for (i = 0; i < UKBD_NMOD; i++) { if ((n_mod & ukbd_mods[i].mask) != (o_mod & ukbd_mods[i].mask)) { ukbd_put_key(sc, ukbd_mods[i].key | ((n_mod & ukbd_mods[i].mask) ? KEY_PRESS : KEY_RELEASE)); } } } /* Check for released keys. */ for (i = 0; i < UKBD_NKEYCODE; i++) { key = sc->sc_odata.keycode[i]; if (key == 0) { continue; } for (j = 0; j < UKBD_NKEYCODE; j++) { if (sc->sc_ndata.keycode[j] == 0) { continue; } if (key == sc->sc_ndata.keycode[j]) { goto rfound; } } ukbd_put_key(sc, key | KEY_RELEASE); rfound: ; } /* Check for pressed keys. */ for (i = 0; i < UKBD_NKEYCODE; i++) { key = sc->sc_ndata.keycode[i]; if (key == 0) { continue; } sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1; for (j = 0; j < UKBD_NKEYCODE; j++) { if (sc->sc_odata.keycode[j] == 0) { continue; } if (key == sc->sc_odata.keycode[j]) { /* key is still pressed */ sc->sc_ntime[i] = sc->sc_otime[j]; dtime = (sc->sc_otime[j] - now); if (!(dtime & 0x80000000)) { /* time has not elapsed */ goto pfound; } sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2; break; } } ukbd_put_key(sc, key | KEY_PRESS); /* * If any other key is presently down, force its repeat to be * well in the future (100s). This makes the last key to be * pressed do the autorepeat. */ for (j = 0; j != UKBD_NKEYCODE; j++) { if (j != i) sc->sc_ntime[j] = now + (100 * 1000); } pfound: ; } sc->sc_odata = sc->sc_ndata; bcopy(sc->sc_ntime, sc->sc_otime, sizeof(sc->sc_otime)); if (sc->sc_inputs == 0) { goto done; } if (sc->sc_flags & UKBD_FLAG_POLLING) { goto done; } if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) { /* let the callback function process the input */ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); } else { /* read and discard the input, no one is waiting for it */ do { c = ukbd_read_char(&sc->sc_kbd, 0); } while (c != NOKEY); } done: return; } static void ukbd_timeout(void *arg) { struct ukbd_softc *sc = arg; mtx_assert(&Giant, MA_OWNED); if (!(sc->sc_flags & UKBD_FLAG_POLLING)) { sc->sc_time_ms += 25; /* milliseconds */ } ukbd_interrupt(sc); usb2_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc); - - mtx_unlock(&Giant); } static void ukbd_clear_stall_callback(struct usb2_xfer *xfer) { struct ukbd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UKBD_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); } } static void ukbd_intr_callback(struct usb2_xfer *xfer) { struct ukbd_softc *sc = xfer->priv_sc; uint16_t len = xfer->actlen; uint8_t i; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("actlen=%d bytes\n", len); if (len > sizeof(sc->sc_ndata)) { len = sizeof(sc->sc_ndata); } if (len) { bzero(&sc->sc_ndata, sizeof(sc->sc_ndata)); usb2_copy_out(xfer->frbuffers, 0, &sc->sc_ndata, len); #if USB_DEBUG if (sc->sc_ndata.modifiers) { DPRINTF("mod: 0x%04x\n", sc->sc_ndata.modifiers); } for (i = 0; i < UKBD_NKEYCODE; i++) { if (sc->sc_ndata.keycode[i]) { DPRINTF("[%d] = %d\n", i, sc->sc_ndata.keycode[i]); } } #endif /* USB_DEBUG */ ukbd_interrupt(sc); } case USB_ST_SETUP: if (sc->sc_flags & UKBD_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer[1]); return; } if (sc->sc_inputs < UKBD_IN_BUF_FULL) { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } else { DPRINTF("input queue is full!\n"); } return; default: /* Error */ DPRINTF("error=%s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= UKBD_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[1]); } return; } } static void ukbd_set_leds_callback(struct usb2_xfer *xfer) { struct usb2_device_request req; uint8_t buf[1]; struct ukbd_softc *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: if (sc->sc_flags & UKBD_FLAG_SET_LEDS) { sc->sc_flags &= ~UKBD_FLAG_SET_LEDS; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UR_SET_REPORT; USETW2(req.wValue, UHID_OUTPUT_REPORT, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); buf[0] = sc->sc_leds; usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf)); xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = sizeof(buf); xfer->nframes = 2; usb2_start_hardware(xfer); } return; default: /* Error */ DPRINTFN(0, "error=%s\n", usb2_errstr(xfer->error)); return; } } static const struct usb2_config ukbd_config[UKBD_N_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.bufsize = 0, /* use wMaxPacketSize */ .mh.callback = &ukbd_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ukbd_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request) + 1, .mh.callback = &ukbd_set_leds_callback, .mh.timeout = 1000, /* 1 second */ }, }; static int ukbd_probe(device_t dev) { keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME); struct usb2_attach_arg *uaa = device_get_ivars(dev); DPRINTFN(11, "\n"); if (sw == NULL) { return (ENXIO); } if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } /* check that the keyboard speaks the boot protocol: */ if ((uaa->info.bInterfaceClass == UICLASS_HID) && (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) && (uaa->info.bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)) { if (usb2_test_quirk(uaa, UQ_KBD_IGNORE)) return (ENXIO); else return (0); } return (ENXIO); } static int ukbd_attach(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); struct usb2_attach_arg *uaa = device_get_ivars(dev); int32_t unit = device_get_unit(dev); keyboard_t *kbd = &sc->sc_kbd; usb2_error_t err; uint16_t n; if (sc == NULL) { return (ENOMEM); } mtx_assert(&Giant, MA_OWNED); kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0); kbd->kb_data = (void *)sc; device_set_usb2_desc(dev); sc->sc_udev = uaa->device; sc->sc_iface = uaa->iface; sc->sc_iface_index = uaa->info.bIfaceIndex; sc->sc_iface_no = uaa->info.bIfaceNum; sc->sc_mode = K_XLATE; sc->sc_iface = uaa->iface; - usb2_callout_init_mtx(&sc->sc_callout, &Giant, - CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_callout, &Giant, 0); err = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config, UKBD_N_TRANSFER, sc, &Giant); if (err) { DPRINTF("error=%s\n", usb2_errstr(err)); goto detach; } /* setup default keyboard maps */ sc->sc_keymap = key_map; sc->sc_accmap = accent_map; for (n = 0; n < UKBD_NFKEY; n++) { sc->sc_fkeymap[n] = fkey_tab[n]; } kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap, sc->sc_fkeymap, UKBD_NFKEY); KBD_FOUND_DEVICE(kbd); ukbd_clear_state(kbd); /* * FIXME: set the initial value for lock keys in "sc_state" * according to the BIOS data? */ KBD_PROBE_DONE(kbd); /* ignore if SETIDLE fails, hence it is not crucial */ err = usb2_req_set_idle(sc->sc_udev, &Giant, sc->sc_iface_index, 0, 0); ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state); KBD_INIT_DONE(kbd); if (kbd_register(kbd) < 0) { goto detach; } KBD_CONFIG_DONE(kbd); ukbd_enable(kbd); #ifdef KBD_INSTALL_CDEV if (kbd_attach(kbd)) { goto detach; } #endif sc->sc_flags |= UKBD_FLAG_ATTACHED; if (bootverbose) { genkbd_diag(kbd, bootverbose); } /* lock keyboard mutex */ mtx_lock(&Giant); /* start the keyboard */ usb2_transfer_start(sc->sc_xfer[0]); /* start the timer */ - ukbd_timeout(sc); /* will unlock mutex */ - + ukbd_timeout(sc); + mtx_unlock(&Giant); return (0); /* success */ detach: ukbd_detach(dev); return (ENXIO); /* error */ } int ukbd_detach(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); int error; mtx_assert(&Giant, MA_OWNED); DPRINTF("\n"); if (sc->sc_flags & UKBD_FLAG_POLLING) { panic("cannot detach polled keyboard!\n"); } sc->sc_flags |= UKBD_FLAG_GONE; usb2_callout_stop(&sc->sc_callout); ukbd_disable(&sc->sc_kbd); #ifdef KBD_INSTALL_CDEV if (sc->sc_flags & UKBD_FLAG_ATTACHED) { error = kbd_detach(&sc->sc_kbd); if (error) { /* usb attach cannot return an error */ device_printf(dev, "WARNING: kbd_detach() " "returned non-zero! (ignored)\n"); } } #endif if (KBD_IS_CONFIGURED(&sc->sc_kbd)) { error = kbd_unregister(&sc->sc_kbd); if (error) { /* usb attach cannot return an error */ device_printf(dev, "WARNING: kbd_unregister() " "returned non-zero! (ignored)\n"); } } sc->sc_kbd.kb_flags = 0; usb2_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER); usb2_callout_drain(&sc->sc_callout); DPRINTF("%s: disconnected\n", device_get_nameunit(dev)); return (0); } static int ukbd_resume(device_t dev) { struct ukbd_softc *sc = device_get_softc(dev); mtx_assert(&Giant, MA_OWNED); ukbd_clear_state(&sc->sc_kbd); return (0); } /* early keyboard probe, not supported */ static int ukbd_configure(int flags) { return (0); } /* detect a keyboard, not used */ static int ukbd__probe(int unit, void *arg, int flags) { mtx_assert(&Giant, MA_OWNED); return (ENXIO); } /* reset and initialize the device, not used */ static int ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) { mtx_assert(&Giant, MA_OWNED); return (ENXIO); } /* test the interface to the device, not used */ static int ukbd_test_if(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); return (0); } /* finish using this keyboard, not used */ static int ukbd_term(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); return (ENXIO); } /* keyboard interrupt routine, not used */ static int ukbd_intr(keyboard_t *kbd, void *arg) { mtx_assert(&Giant, MA_OWNED); return (0); } /* lock the access to the keyboard, not used */ static int ukbd_lock(keyboard_t *kbd, int lock) { mtx_assert(&Giant, MA_OWNED); return (1); } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int ukbd_enable(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); KBD_ACTIVATE(kbd); return (0); } /* disallow the access to the device */ static int ukbd_disable(keyboard_t *kbd) { mtx_assert(&Giant, MA_OWNED); KBD_DEACTIVATE(kbd); return (0); } /* check if data is waiting */ static int ukbd_check(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return (0); /* XXX */ } mtx_assert(&Giant, MA_OWNED); if (!KBD_IS_ACTIVE(kbd)) { return (0); } #ifdef UKBD_EMULATE_ATSCANCODE if (sc->sc_buffered_char[0]) { return (1); } #endif if (sc->sc_inputs > 0) { return (1); } return (0); } /* check if char is waiting */ static int ukbd_check_char(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return (0); /* XXX */ } mtx_assert(&Giant, MA_OWNED); if (!KBD_IS_ACTIVE(kbd)) { return (0); } if ((sc->sc_composed_char > 0) && (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { return (1); } return (ukbd_check(kbd)); } /* read one byte from the keyboard if it's allowed */ static int ukbd_read(keyboard_t *kbd, int wait) { struct ukbd_softc *sc = kbd->kb_data; int32_t usbcode; #ifdef UKBD_EMULATE_ATSCANCODE uint32_t keycode; uint32_t scancode; #endif if (!mtx_owned(&Giant)) { return -1; /* XXX */ } mtx_assert(&Giant, MA_OWNED); #ifdef UKBD_EMULATE_ATSCANCODE if (sc->sc_buffered_char[0]) { scancode = sc->sc_buffered_char[0]; if (scancode & SCAN_PREFIX) { sc->sc_buffered_char[0] &= ~SCAN_PREFIX; return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } sc->sc_buffered_char[0] = sc->sc_buffered_char[1]; sc->sc_buffered_char[1] = 0; return (scancode); } #endif /* UKBD_EMULATE_ATSCANCODE */ /* XXX */ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1); if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) { return -1; } ++(kbd->kb_count); #ifdef UKBD_EMULATE_ATSCANCODE keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) { return -1; } return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers, (usbcode & KEY_RELEASE))); #else /* !UKBD_EMULATE_ATSCANCODE */ return (usbcode); #endif /* UKBD_EMULATE_ATSCANCODE */ } /* read char from the keyboard */ static uint32_t ukbd_read_char(keyboard_t *kbd, int wait) { struct ukbd_softc *sc = kbd->kb_data; uint32_t action; uint32_t keycode; int32_t usbcode; #ifdef UKBD_EMULATE_ATSCANCODE uint32_t scancode; #endif if (!mtx_owned(&Giant)) { return (NOKEY); /* XXX */ } mtx_assert(&Giant, MA_OWNED); next_code: /* do we have a composed char to return ? */ if ((sc->sc_composed_char > 0) && (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { action = sc->sc_composed_char; sc->sc_composed_char = 0; if (action > 0xFF) { goto errkey; } goto done; } #ifdef UKBD_EMULATE_ATSCANCODE /* do we have a pending raw scan code? */ if (sc->sc_mode == K_RAW) { scancode = sc->sc_buffered_char[0]; if (scancode) { if (scancode & SCAN_PREFIX) { sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX); return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } sc->sc_buffered_char[0] = sc->sc_buffered_char[1]; sc->sc_buffered_char[1] = 0; return (scancode); } } #endif /* UKBD_EMULATE_ATSCANCODE */ /* see if there is something in the keyboard port */ /* XXX */ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1); if (usbcode == -1) { return (NOKEY); } ++kbd->kb_count; #ifdef UKBD_EMULATE_ATSCANCODE /* USB key index -> key code -> AT scan code */ keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) { return (NOKEY); } /* return an AT scan code for the K_RAW mode */ if (sc->sc_mode == K_RAW) { return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers, (usbcode & KEY_RELEASE))); } #else /* !UKBD_EMULATE_ATSCANCODE */ /* return the byte as is for the K_RAW mode */ if (sc->sc_mode == K_RAW) { return (usbcode); } /* USB key index -> key code */ keycode = ukbd_trtab[KEY_INDEX(usbcode)]; if (keycode == NN) { return (NOKEY); } #endif /* UKBD_EMULATE_ATSCANCODE */ switch (keycode) { case 0x38: /* left alt (compose key) */ if (usbcode & KEY_RELEASE) { if (sc->sc_flags & UKBD_FLAG_COMPOSE) { sc->sc_flags &= ~UKBD_FLAG_COMPOSE; if (sc->sc_composed_char > 0xFF) { sc->sc_composed_char = 0; } } } else { if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) { sc->sc_flags |= UKBD_FLAG_COMPOSE; sc->sc_composed_char = 0; } } break; /* XXX: I don't like these... */ case 0x5c: /* print screen */ if (sc->sc_flags & ALTS) { keycode = 0x54; /* sysrq */ } break; case 0x68: /* pause/break */ if (sc->sc_flags & CTLS) { keycode = 0x6c; /* break */ } break; } /* return the key code in the K_CODE mode */ if (usbcode & KEY_RELEASE) { keycode |= SCAN_RELEASE; } if (sc->sc_mode == K_CODE) { return (keycode); } /* compose a character code */ if (sc->sc_flags & UKBD_FLAG_COMPOSE) { switch (keycode) { /* key pressed, process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ sc->sc_composed_char *= 10; sc->sc_composed_char += keycode - 0x40; goto check_composed; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ sc->sc_composed_char *= 10; sc->sc_composed_char += keycode - 0x47; goto check_composed; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ sc->sc_composed_char *= 10; sc->sc_composed_char += keycode - 0x4E; goto check_composed; case 0x52: /* keypad 0 */ sc->sc_composed_char *= 10; goto check_composed; /* key released, no interest here */ case SCAN_RELEASE | 0x47: case SCAN_RELEASE | 0x48: case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */ case SCAN_RELEASE | 0x4B: case SCAN_RELEASE | 0x4C: case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */ case SCAN_RELEASE | 0x4F: case SCAN_RELEASE | 0x50: case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */ case SCAN_RELEASE | 0x52: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (sc->sc_composed_char > 0) { sc->sc_flags &= ~UKBD_FLAG_COMPOSE; sc->sc_composed_char = 0; goto errkey; } break; } } /* keycode to key action */ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode), (keycode & SCAN_RELEASE), &sc->sc_state, &sc->sc_accents); if (action == NOKEY) { goto next_code; } done: return (action); check_composed: if (sc->sc_composed_char <= 0xFF) { goto next_code; } errkey: return (ERRKEY); } /* some useful control functions */ static int ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { /* translate LED_XXX bits into the device specific bits */ static const uint8_t ledmap[8] = { 0, 2, 1, 3, 4, 6, 5, 7, }; struct ukbd_softc *sc = kbd->kb_data; int i; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif if (!mtx_owned(&Giant)) { /* * XXX big problem: If scroll lock is pressed and "printf()" * is called, the CPU will get here, to un-scroll lock the * keyboard. But if "printf()" acquires the "Giant" lock, * there will be a locking order reversal problem, so the * keyboard system must get out of "Giant" first, before the * CPU can proceed here ... */ return (EINVAL); } mtx_assert(&Giant, MA_OWNED); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = sc->sc_mode; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (sc->sc_mode != K_XLATE) { /* make lock key state and LED state match */ sc->sc_state &= ~LOCK_MASK; sc->sc_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { ukbd_clear_state(kbd); sc->sc_mode = *(int *)arg; } break; default: return (EINVAL); } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in "sc_state" won't be changed */ if (*(int *)arg & ~LOCK_MASK) { return (EINVAL); } i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (sc->sc_mode == K_XLATE && kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { ukbd_set_leds(sc, ledmap[i & LED_MASK]); } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = sc->sc_state & LOCK_MASK; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { return (EINVAL); } sc->sc_state &= ~LOCK_MASK; sc->sc_state |= *(int *)arg; /* set LEDs and quit */ return (ukbd_ioctl(kbd, KDSETLED, arg)); case KDSETREPEAT: /* set keyboard repeat rate (new * interface) */ if (!KBD_HAS_DEVICE(kbd)) { return (0); } if (((int *)arg)[1] < 0) { return (EINVAL); } if (((int *)arg)[0] < 0) { return (EINVAL); } if (((int *)arg)[0] < 200) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return (0); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat rate (old * interface) */ return (ukbd_set_typematic(kbd, *(int *)arg)); case PIO_KEYMAP: /* set keyboard translation table */ case PIO_KEYMAPENT: /* set keyboard translation table * entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ sc->sc_accents = 0; /* FALLTHROUGH */ default: return (genkbd_commonioctl(kbd, cmd, arg)); } return (0); } /* clear the internal state of the keyboard */ static void ukbd_clear_state(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return; /* XXX */ } mtx_assert(&Giant, MA_OWNED); sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ sc->sc_accents = 0; sc->sc_composed_char = 0; #ifdef UKBD_EMULATE_ATSCANCODE sc->sc_buffered_char[0] = 0; sc->sc_buffered_char[1] = 0; #endif bzero(&sc->sc_ndata, sizeof(sc->sc_ndata)); bzero(&sc->sc_odata, sizeof(sc->sc_odata)); bzero(&sc->sc_ntime, sizeof(sc->sc_ntime)); bzero(&sc->sc_otime, sizeof(sc->sc_otime)); } /* save the internal state, not used */ static int ukbd_get_state(keyboard_t *kbd, void *buf, size_t len) { mtx_assert(&Giant, MA_OWNED); return (len == 0) ? 1 : -1; } /* set the internal state, not used */ static int ukbd_set_state(keyboard_t *kbd, void *buf, size_t len) { mtx_assert(&Giant, MA_OWNED); return (EINVAL); } static int ukbd_poll(keyboard_t *kbd, int on) { struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { return (0); /* XXX */ } mtx_assert(&Giant, MA_OWNED); if (on) { sc->sc_flags |= UKBD_FLAG_POLLING; } else { sc->sc_flags &= ~UKBD_FLAG_POLLING; } return (0); } /* local functions */ static void ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds) { DPRINTF("leds=0x%02x\n", leds); sc->sc_leds = leds; sc->sc_flags |= UKBD_FLAG_SET_LEDS; /* start transfer, if not already started */ usb2_transfer_start(sc->sc_xfer[2]); } static int ukbd_set_typematic(keyboard_t *kbd, int code) { static const int delays[] = {250, 500, 750, 1000}; static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504}; if (code & ~0x7f) { return (EINVAL); } kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return (0); } #ifdef UKBD_EMULATE_ATSCANCODE static int ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up) { static const int scan[] = { 0x1c, 0x1d, 0x35, 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */ 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x46, /* XXX Pause/Break */ 0x5b, 0x5c, 0x5d, /* SUN TYPE 6 USB KEYBOARD */ 0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e, 0x20, }; if ((code >= 89) && (code < (89 + (sizeof(scan) / sizeof(scan[0]))))) { code = scan[code - 89] | SCAN_PREFIX_E0; } /* Pause/Break */ if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) { code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL); } if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) { code &= ~SCAN_PREFIX_SHIFT; } code |= (up ? SCAN_RELEASE : SCAN_PRESS); if (code & SCAN_PREFIX) { if (code & SCAN_PREFIX_CTL) { /* Ctrl */ sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE)); sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX); } else if (code & SCAN_PREFIX_SHIFT) { /* Shift */ sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE)); sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT); } else { sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX); sc->sc_buffered_char[1] = 0; } return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); } return (code); } #endif /* UKBD_EMULATE_ATSCANCODE */ keyboard_switch_t ukbdsw = { .probe = &ukbd__probe, .init = &ukbd_init, .term = &ukbd_term, .intr = &ukbd_intr, .test_if = &ukbd_test_if, .enable = &ukbd_enable, .disable = &ukbd_disable, .read = &ukbd_read, .check = &ukbd_check, .read_char = &ukbd_read_char, .check_char = &ukbd_check_char, .ioctl = &ukbd_ioctl, .lock = &ukbd_lock, .clear_state = &ukbd_clear_state, .get_state = &ukbd_get_state, .set_state = &ukbd_set_state, .get_fkeystr = &genkbd_get_fkeystr, .poll = &ukbd_poll, .diag = &genkbd_diag, }; KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure); static int ukbd_driver_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kbd_add_driver(&ukbd_kbd_driver); break; case MOD_UNLOAD: kbd_delete_driver(&ukbd_kbd_driver); break; } return (0); } static devclass_t ukbd_devclass; static device_method_t ukbd_methods[] = { DEVMETHOD(device_probe, ukbd_probe), DEVMETHOD(device_attach, ukbd_attach), DEVMETHOD(device_detach, ukbd_detach), DEVMETHOD(device_resume, ukbd_resume), {0, 0} }; static driver_t ukbd_driver = { .name = "ukbd", .methods = ukbd_methods, .size = sizeof(struct ukbd_softc), }; DRIVER_MODULE(ukbd, ushub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0); MODULE_DEPEND(ukbd, usb2_input, 1, 1, 1); MODULE_DEPEND(ukbd, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/input/ums2.c =================================================================== --- projects/cambria/sys/dev/usb2/input/ums2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/input/ums2.c (revision 186460) @@ -1,902 +1,899 @@ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #include __FBSDID("$FreeBSD$"); /* * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf */ #include #include #include #include #include #define USB_DEBUG_VAR ums_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int ums_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums"); SYSCTL_INT(_hw_usb2_ums, OID_AUTO, debug, CTLFLAG_RW, &ums_debug, 0, "Debug level"); #endif #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) #define UMS_BUF_SIZE 8 /* bytes */ #define UMS_IFQ_MAXLEN 50 /* units */ #define UMS_N_TRANSFER 2 /* units */ #define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */ #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) struct ums_softc { struct usb2_fifo_sc sc_fifo; struct mtx sc_mtx; struct usb2_callout sc_callout; struct hid_location sc_loc_w; struct hid_location sc_loc_x; struct hid_location sc_loc_y; struct hid_location sc_loc_z; struct hid_location sc_loc_t; struct hid_location sc_loc_btn[UMS_BUTTON_MAX]; mousehw_t sc_hw; mousemode_t sc_mode; mousestatus_t sc_status; struct usb2_xfer *sc_xfer[UMS_N_TRANSFER]; uint32_t sc_flags; #define UMS_FLAG_X_AXIS 0x0001 #define UMS_FLAG_Y_AXIS 0x0002 #define UMS_FLAG_Z_AXIS 0x0004 #define UMS_FLAG_T_AXIS 0x0008 #define UMS_FLAG_SBU 0x0010 /* spurious button up events */ #define UMS_FLAG_INTR_STALL 0x0020 /* set if transfer error */ #define UMS_FLAG_REVZ 0x0040 /* Z-axis is reversed */ #define UMS_FLAG_W_AXIS 0x0080 uint8_t sc_buttons; uint8_t sc_iid; uint8_t sc_temp[64]; }; static void ums_put_queue_timeout(void *__sc); static usb2_callback_t ums_clear_stall_callback; static usb2_callback_t ums_intr_callback; static device_probe_t ums_probe; static device_attach_t ums_attach; static device_detach_t ums_detach; static usb2_fifo_cmd_t ums_start_read; static usb2_fifo_cmd_t ums_stop_read; static usb2_fifo_open_t ums_open; static usb2_fifo_close_t ums_close; static usb2_fifo_ioctl_t ums_ioctl; static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons); static struct usb2_fifo_methods ums_fifo_methods = { .f_open = &ums_open, .f_close = &ums_close, .f_ioctl = &ums_ioctl, .f_start_read = &ums_start_read, .f_stop_read = &ums_stop_read, .basename[0] = "ums", }; static void ums_put_queue_timeout(void *__sc) { struct ums_softc *sc = __sc; mtx_assert(&sc->sc_mtx, MA_OWNED); ums_put_queue(sc, 0, 0, 0, 0, 0); - - mtx_unlock(&sc->sc_mtx); } static void ums_clear_stall_callback(struct usb2_xfer *xfer) { struct ums_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~UMS_FLAG_INTR_STALL; usb2_transfer_start(xfer_other); } } static void ums_intr_callback(struct usb2_xfer *xfer) { struct ums_softc *sc = xfer->priv_sc; uint8_t *buf = sc->sc_temp; uint16_t len = xfer->actlen; int32_t buttons = 0; int32_t dw; int32_t dx; int32_t dy; int32_t dz; int32_t dt; uint8_t i; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(6, "sc=%p actlen=%d\n", sc, len); if (len > sizeof(sc->sc_temp)) { DPRINTFN(6, "truncating large packet to %zu bytes\n", sizeof(sc->sc_temp)); len = sizeof(sc->sc_temp); } if (len == 0) { goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, buf, len); DPRINTFN(6, "data = %02x %02x %02x %02x " "%02x %02x %02x %02x\n", (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0, (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0, (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0, (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0); /* * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte * of data compared to most USB mice. This byte frequently * switches from 0x01 (usual state) to 0x02. I assume it is to * allow extra, non-standard, reporting (say battery-life). * * However at the same time it generates a left-click message * on the button byte which causes spurious left-click's where * there shouldn't be. This should sort that. Currently it's * the only user of UMS_FLAG_T_AXIS so use it as an * identifier. * * * UPDATE: This problem affects the M$ Wireless Notebook Optical Mouse, * too. However, the leading byte for this mouse is normally 0x11, * and the phantom mouse click occurs when its 0x14. * * We probably should switch to some more official quirk. */ if (sc->sc_iid) { if (sc->sc_flags & UMS_FLAG_T_AXIS) { if (*buf == 0x02) { goto tr_setup; } } else { if (*buf != sc->sc_iid) { goto tr_setup; } } len--; buf++; } else { if (sc->sc_flags & UMS_FLAG_SBU) { if ((*buf == 0x14) || (*buf == 0x15)) { goto tr_setup; } } } dw = (sc->sc_flags & UMS_FLAG_W_AXIS) ? hid_get_data(buf, len, &sc->sc_loc_w) : 0; dx = (sc->sc_flags & UMS_FLAG_X_AXIS) ? hid_get_data(buf, len, &sc->sc_loc_x) : 0; dy = (sc->sc_flags & UMS_FLAG_Y_AXIS) ? -hid_get_data(buf, len, &sc->sc_loc_y) : 0; dz = (sc->sc_flags & UMS_FLAG_Z_AXIS) ? -hid_get_data(buf, len, &sc->sc_loc_z) : 0; if (sc->sc_flags & UMS_FLAG_REVZ) { dz = -dz; } dt = (sc->sc_flags & UMS_FLAG_T_AXIS) ? -hid_get_data(buf, len, &sc->sc_loc_t): 0; for (i = 0; i < sc->sc_buttons; i++) { if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) { buttons |= (1 << UMS_BUT(i)); } } if (dx || dy || dz || dt || dw || (buttons != sc->sc_status.button)) { DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n", dx, dy, dz, dt, dw, buttons); sc->sc_status.button = buttons; sc->sc_status.dx += dx; sc->sc_status.dy += dy; sc->sc_status.dz += dz; /* * sc->sc_status.dt += dt; * no way to export this yet */ /* * The Qtronix keyboard has a built in PS/2 port for a mouse. * The firmware once in a while posts a spurious button up * event. This event we ignore by doing a timeout for 50 msecs. * If we receive dx=dy=dz=buttons=0 before we add the event to * the queue. * In any other case we delete the timeout event. */ if ((sc->sc_flags & UMS_FLAG_SBU) && (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) && (dw == 0) && (buttons == 0)) { usb2_callout_reset(&sc->sc_callout, hz / 20, &ums_put_queue_timeout, sc); } else { usb2_callout_stop(&sc->sc_callout); ums_put_queue(sc, dx, dy, dz, dt, buttons); } } case USB_ST_SETUP: tr_setup: if (sc->sc_flags & UMS_FLAG_INTR_STALL) { usb2_transfer_start(sc->sc_xfer[1]); } else { /* check if we can put more data into the FIFO */ if (usb2_fifo_put_bytes_max( sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* start clear stall */ sc->sc_flags |= UMS_FLAG_INTR_STALL; usb2_transfer_start(sc->sc_xfer[1]); } return; } } static const struct usb2_config ums_config[UMS_N_TRANSFER] = { [0] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.bufsize = 0, /* use wMaxPacketSize */ .mh.callback = &ums_intr_callback, }, [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ums_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static int ums_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct usb2_interface_descriptor *id; void *d_ptr; int32_t error = 0; uint16_t d_len; DPRINTFN(11, "\n"); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->iface == NULL) { return (ENXIO); } id = usb2_get_interface_descriptor(uaa->iface); if ((id == NULL) || (id->bInterfaceClass != UICLASS_HID)) { return (ENXIO); } error = usb2_req_get_hid_desc (uaa->device, &Giant, &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); if (error) { return (ENXIO); } if (hid_is_collection(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) { error = 0; } else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) && (id->bInterfaceProtocol == UIPROTO_MOUSE)) { error = 0; } else { error = ENXIO; } free(d_ptr, M_TEMP); return (error); } static int ums_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct ums_softc *sc = device_get_softc(dev); void *d_ptr = NULL; int unit = device_get_unit(dev); int32_t isize; uint32_t flags; int32_t err; uint16_t d_len; uint8_t i; DPRINTFN(11, "sc=%p\n", sc); device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_callout, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0); /* * Force the report (non-boot) protocol. * * Mice without boot protocol support may choose not to implement * Set_Protocol at all; Ignore any error. */ err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1); err = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config, UMS_N_TRANSFER, sc, &sc->sc_mtx); if (err) { DPRINTF("error=%s\n", usb2_errstr(err)); goto detach; } err = usb2_req_get_hid_desc (uaa->device, &Giant, &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); if (err) { device_printf(dev, "error reading report description\n"); goto detach; } if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), hid_input, &sc->sc_loc_x, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_X_AXIS; } } if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), hid_input, &sc->sc_loc_y, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_Y_AXIS; } } /* Try the wheel first as the Z activator since it's tradition. */ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags) || hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_Z_AXIS; } /* * We might have both a wheel and Z direction, if so put * put the Z on the W coordinate. */ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, &sc->sc_loc_w, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_W_AXIS; } } } else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, &sc->sc_loc_z, &flags)) { if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_Z_AXIS; } } /* * The Microsoft Wireless Intellimouse 2.0 reports it's wheel * using 0x0048, which is HUG_TWHEEL, and seems to expect you * to know that the byte after the wheel is the tilt axis. * There are no other HID axis descriptors other than X,Y and * TWHEEL */ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), hid_input, &sc->sc_loc_t, &flags)) { sc->sc_loc_t.pos += 8; if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) { sc->sc_flags |= UMS_FLAG_T_AXIS; } } /* figure out the number of buttons */ for (i = 0; i < UMS_BUTTON_MAX; i++) { if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)), hid_input, &sc->sc_loc_btn[i], NULL)) { break; } } sc->sc_buttons = i; isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid); /* * The Microsoft Wireless Notebook Optical Mouse seems to be in worse * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and * all of its other button positions are all off. It also reports that * it has two addional buttons and a tilt wheel. */ if (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) { sc->sc_flags = (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS | UMS_FLAG_SBU); sc->sc_buttons = 3; isize = 5; sc->sc_iid = 0; /* 1st byte of descriptor report contains garbage */ sc->sc_loc_x.pos = 16; sc->sc_loc_y.pos = 24; sc->sc_loc_z.pos = 32; sc->sc_loc_btn[0].pos = 8; sc->sc_loc_btn[1].pos = 9; sc->sc_loc_btn[2].pos = 10; } /* * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has * five Report IDs: 19 23 24 17 18 (in the order they appear in report * descriptor), it seems that report id 17 contains the necessary * mouse information(3-buttons,X,Y,wheel) so we specify it manually. */ if ((uaa->info.idVendor == USB_VENDOR_MICROSOFT) && (uaa->info.idProduct == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3)) { sc->sc_flags = (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS); sc->sc_buttons = 3; isize = 5; sc->sc_iid = 17; sc->sc_loc_x.pos = 8; sc->sc_loc_y.pos = 16; sc->sc_loc_z.pos = 24; sc->sc_loc_btn[0].pos = 0; sc->sc_loc_btn[1].pos = 1; sc->sc_loc_btn[2].pos = 2; } if (usb2_test_quirk(uaa, UQ_MS_REVZ)) { /* Some wheels need the Z axis reversed. */ sc->sc_flags |= UMS_FLAG_REVZ; } if (isize > sc->sc_xfer[0]->max_frame_size) { DPRINTF("WARNING: report size, %d bytes, is larger " "than interrupt size, %d bytes!\n", isize, sc->sc_xfer[0]->max_frame_size); } /* announce information about the mouse */ device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n", (sc->sc_buttons), (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "", (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "", (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "", (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "", (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : ""); free(d_ptr, M_TEMP); d_ptr = NULL; #if USB_DEBUG DPRINTF("sc=%p\n", sc); DPRINTF("X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size); DPRINTF("Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size); DPRINTF("Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size); DPRINTF("T\t%d/%d\n", sc->sc_loc_t.pos, sc->sc_loc_t.size); DPRINTF("W\t%d/%d\n", sc->sc_loc_w.pos, sc->sc_loc_w.size); for (i = 0; i < sc->sc_buttons; i++) { DPRINTF("B%d\t%d/%d\n", i + 1, sc->sc_loc_btn[i].pos, sc->sc_loc_btn[i].size); } DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid); #endif if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_hw.iftype = MOUSE_IF_USB; sc->sc_hw.type = MOUSE_MOUSE; sc->sc_hw.model = MOUSE_MODEL_GENERIC; sc->sc_hw.hwid = 0; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.rate = -1; sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; sc->sc_mode.accelfactor = 0; sc->sc_mode.level = 0; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; sc->sc_status.flags = 0; sc->sc_status.button = 0; sc->sc_status.obutton = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* set interface permissions */ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, &ums_fifo_methods, &sc->sc_fifo, unit, 0 - 1, uaa->info.bIfaceIndex); if (err) { goto detach; } return (0); detach: if (d_ptr) { free(d_ptr, M_TEMP); } ums_detach(dev); return (ENOMEM); } static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); DPRINTF("sc=%p\n", sc); usb2_fifo_detach(&sc->sc_fifo); usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER); usb2_callout_drain(&sc->sc_callout); mtx_destroy(&sc->sc_mtx); return (0); } static void ums_start_read(struct usb2_fifo *fifo) { struct ums_softc *sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[0]); } static void ums_stop_read(struct usb2_fifo *fifo) { struct ums_softc *sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[0]); usb2_callout_stop(&sc->sc_callout); } #if ((MOUSE_SYS_PACKETSIZE != 8) || \ (MOUSE_MSC_PACKETSIZE != 5)) #error "Software assumptions are not met. Please update code." #endif static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons) { uint8_t buf[8]; if (1) { if (dx > 254) dx = 254; if (dx < -256) dx = -256; if (dy > 254) dy = 254; if (dy < -256) dy = -256; if (dz > 126) dz = 126; if (dz < -128) dz = -128; if (dt > 126) dt = 126; if (dt < -128) dt = -128; buf[0] = sc->sc_mode.syncmask[1]; buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS; buf[1] = dx >> 1; buf[2] = dy >> 1; buf[3] = dx - (dx >> 1); buf[4] = dy - (dy >> 1); if (sc->sc_mode.level == 1) { buf[5] = dz >> 1; buf[6] = dz - (dz >> 1); buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS); } usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, sc->sc_mode.packetsize, 1); } else { DPRINTF("Buffer full, discarded packet\n"); } } static void ums_reset_buf(struct ums_softc *sc) { /* reset read queue */ usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); } static int ums_open(struct usb2_fifo *fifo, int fflags, struct thread *td) { struct ums_softc *sc = fifo->priv_sc0; DPRINTFN(2, "\n"); if (fflags & FREAD) { /* reset status */ sc->sc_status.flags = 0; sc->sc_status.button = 0; sc->sc_status.obutton = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* sc->sc_status.dt = 0; */ if (usb2_fifo_alloc_buffer(fifo, UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) { return (ENOMEM); } } return (0); } static void ums_close(struct usb2_fifo *fifo, int fflags, struct thread *td) { if (fflags & FREAD) { usb2_fifo_free_buffer(fifo); } } static int ums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags, struct thread *td) { struct ums_softc *sc = fifo->priv_sc0; mousemode_t mode; int error = 0; DPRINTFN(2, "\n"); mtx_lock(&sc->sc_mtx); switch (cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->sc_hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->sc_mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) { /* don't change the current setting */ } else if ((mode.level < 0) || (mode.level > 1)) { error = EINVAL; goto done; } else { sc->sc_mode.level = mode.level; } if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } ums_reset_buf(sc); break; case MOUSE_GETLEVEL: *(int *)addr = sc->sc_mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) { error = EINVAL; goto done; } sc->sc_mode.level = *(int *)addr; if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_MSC; sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->sc_mode.level == 1) { if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON) sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->sc_hw.buttons = sc->sc_buttons; sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; } ums_reset_buf(sc); break; case MOUSE_GETSTATUS:{ mousestatus_t *status = (mousestatus_t *)addr; *status = sc->sc_status; sc->sc_status.obutton = sc->sc_status.button; sc->sc_status.button = 0; sc->sc_status.dx = 0; sc->sc_status.dy = 0; sc->sc_status.dz = 0; /* sc->sc_status.dt = 0; */ if (status->dx || status->dy || status->dz /* || status->dt */ ) { status->flags |= MOUSE_POSCHANGED; } if (status->button != status->obutton) { status->flags |= MOUSE_BUTTONSCHANGED; } break; } default: error = ENOTTY; } done: mtx_unlock(&sc->sc_mtx); return (error); } static devclass_t ums_devclass; static device_method_t ums_methods[] = { DEVMETHOD(device_probe, ums_probe), DEVMETHOD(device_attach, ums_attach), DEVMETHOD(device_detach, ums_detach), {0, 0} }; static driver_t ums_driver = { .name = "ums", .methods = ums_methods, .size = sizeof(struct ums_softc), }; DRIVER_MODULE(ums, ushub, ums_driver, ums_devclass, NULL, 0); MODULE_DEPEND(ums, usb2_input, 1, 1, 1); MODULE_DEPEND(ums, usb2_core, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/serial/ulpt2.c =================================================================== --- projects/cambria/sys/dev/usb2/serial/ulpt2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/serial/ulpt2.c (revision 186460) @@ -1,786 +1,782 @@ #include __FBSDID("$FreeBSD$"); /* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */ /*- * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf */ #include #include #include #include #define USB_DEBUG_VAR ulpt_debug #include #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int ulpt_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt"); SYSCTL_INT(_hw_usb2_ulpt, OID_AUTO, debug, CTLFLAG_RW, &ulpt_debug, 0, "Debug level"); #endif #define ULPT_BSIZE (1<<15) /* bytes */ #define ULPT_IFQ_MAXLEN 2 /* units */ #define ULPT_N_TRANSFER 5 /* units */ #define UR_GET_DEVICE_ID 0x00 #define UR_GET_PORT_STATUS 0x01 #define UR_SOFT_RESET 0x02 #define LPS_NERR 0x08 /* printer no error */ #define LPS_SELECT 0x10 /* printer selected */ #define LPS_NOPAPER 0x20 /* printer out of paper */ #define LPS_INVERT (LPS_SELECT|LPS_NERR) #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) struct ulpt_softc { struct usb2_fifo_sc sc_fifo; struct usb2_fifo_sc sc_fifo_noreset; struct mtx sc_mtx; struct usb2_callout sc_watchdog; device_t sc_dev; struct usb2_device *sc_udev; struct usb2_fifo *sc_fifo_open[2]; struct usb2_xfer *sc_xfer[ULPT_N_TRANSFER]; int sc_fflags; /* current open flags, FREAD and * FWRITE */ uint8_t sc_flags; #define ULPT_FLAG_READ_STALL 0x04 /* read transfer stalled */ #define ULPT_FLAG_WRITE_STALL 0x08 /* write transfer stalled */ uint8_t sc_iface_no; uint8_t sc_last_status; uint8_t sc_zlps; /* number of consequtive zero length * packets received */ }; /* prototypes */ static device_probe_t ulpt_probe; static device_attach_t ulpt_attach; static device_detach_t ulpt_detach; static usb2_callback_t ulpt_write_callback; static usb2_callback_t ulpt_write_clear_stall_callback; static usb2_callback_t ulpt_read_callback; static usb2_callback_t ulpt_read_clear_stall_callback; static usb2_callback_t ulpt_status_callback; static void ulpt_reset(struct ulpt_softc *); static void ulpt_watchdog(void *); static usb2_fifo_close_t ulpt_close; static usb2_fifo_cmd_t ulpt_start_read; static usb2_fifo_cmd_t ulpt_start_write; static usb2_fifo_cmd_t ulpt_stop_read; static usb2_fifo_cmd_t ulpt_stop_write; static usb2_fifo_ioctl_t ulpt_ioctl; static usb2_fifo_open_t ulpt_open; static usb2_fifo_open_t unlpt_open; static struct usb2_fifo_methods ulpt_fifo_methods = { .f_close = &ulpt_close, .f_ioctl = &ulpt_ioctl, .f_open = &ulpt_open, .f_start_read = &ulpt_start_read, .f_start_write = &ulpt_start_write, .f_stop_read = &ulpt_stop_read, .f_stop_write = &ulpt_stop_write, .basename[0] = "ulpt", }; static struct usb2_fifo_methods unlpt_fifo_methods = { .f_close = &ulpt_close, .f_ioctl = &ulpt_ioctl, .f_open = &unlpt_open, .f_start_read = &ulpt_start_read, .f_start_write = &ulpt_start_write, .f_stop_read = &ulpt_stop_read, .f_stop_write = &ulpt_stop_write, .basename[0] = "unlpt", }; static void ulpt_reset(struct ulpt_softc *sc) { struct usb2_device_request req; DPRINTFN(2, "\n"); req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); /* * There was a mistake in the USB printer 1.0 spec that gave the * request type as UT_WRITE_CLASS_OTHER; it should have been * UT_WRITE_CLASS_INTERFACE. Many printers use the old one, * so we try both. */ mtx_lock(&sc->sc_mtx); req.bmRequestType = UT_WRITE_CLASS_OTHER; if (usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.0 */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; if (usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.1 */ /* ignore error */ } } mtx_unlock(&sc->sc_mtx); } static void ulpt_write_callback(struct usb2_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_TX]; uint32_t actlen; if (f == NULL) { /* should not happen */ DPRINTF("no FIFO\n"); return; } DPRINTF("state=0x%x\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: if (sc->sc_flags & ULPT_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[3]); break; } if (usb2_fifo_get_data(f, xfer->frbuffers, 0, xfer->max_data_length, &actlen, 0)) { xfer->frlengths[0] = actlen; usb2_start_hardware(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ULPT_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[3]); } break; } } static void ulpt_write_clear_stall_callback(struct usb2_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ULPT_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void ulpt_read_callback(struct usb2_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_RX]; if (f == NULL) { /* should not happen */ DPRINTF("no FIFO\n"); return; } DPRINTF("state=0x%x\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen == 0) { if (sc->sc_zlps == 4) { /* enable BULK throttle */ xfer->interval = 500; /* ms */ } else { sc->sc_zlps++; } } else { /* disable BULK throttle */ xfer->interval = 0; sc->sc_zlps = 0; } usb2_fifo_put_data(f, xfer->frbuffers, 0, xfer->actlen, 1); case USB_ST_SETUP: if (sc->sc_flags & ULPT_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[4]); break; } if (usb2_fifo_put_bytes_max(f) != 0) { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } break; default: /* Error */ /* disable BULK throttle */ xfer->interval = 0; sc->sc_zlps = 0; if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ULPT_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[4]); } break; } } static void ulpt_read_clear_stall_callback(struct usb2_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ULPT_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static void ulpt_status_callback(struct usb2_xfer *xfer) { struct ulpt_softc *sc = xfer->priv_sc; struct usb2_device_request req; uint8_t cur_status; uint8_t new_status; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: usb2_copy_out(xfer->frbuffers + 1, 0, &cur_status, 1); cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK; new_status = cur_status & ~sc->sc_last_status; sc->sc_last_status = cur_status; if (new_status & LPS_SELECT) log(LOG_NOTICE, "%s: offline\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NOPAPER) log(LOG_NOTICE, "%s: out of paper\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NERR) log(LOG_NOTICE, "%s: output error\n", device_get_nameunit(sc->sc_dev)); break; case USB_ST_SETUP: req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_PORT_STATUS; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); xfer->frlengths[0] = sizeof(req); xfer->frlengths[1] = 1; xfer->nframes = 2; usb2_start_hardware(xfer); break; default: /* Error */ DPRINTF("error=%s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* wait for next watchdog timeout */ } break; } } static const struct usb2_config ulpt_config[ULPT_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = ULPT_BSIZE, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1}, .mh.callback = &ulpt_write_callback, }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = ULPT_BSIZE, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1}, .mh.callback = &ulpt_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request) + 1, .mh.callback = &ulpt_status_callback, .mh.timeout = 1000, /* 1 second */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ulpt_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [4] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ulpt_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static void ulpt_start_read(struct usb2_fifo *fifo) { struct ulpt_softc *sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[1]); } static void ulpt_stop_read(struct usb2_fifo *fifo) { struct ulpt_softc *sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[4]); usb2_transfer_stop(sc->sc_xfer[1]); } static void ulpt_start_write(struct usb2_fifo *fifo) { struct ulpt_softc *sc = fifo->priv_sc0; usb2_transfer_start(sc->sc_xfer[0]); } static void ulpt_stop_write(struct usb2_fifo *fifo) { struct ulpt_softc *sc = fifo->priv_sc0; usb2_transfer_stop(sc->sc_xfer[3]); usb2_transfer_stop(sc->sc_xfer[0]); } static int ulpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td) { struct ulpt_softc *sc = fifo->priv_sc0; /* we assume that open is a serial process */ if (sc->sc_fflags == 0) { ulpt_reset(sc); } return (unlpt_open(fifo, fflags, td)); } static int unlpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td) { struct ulpt_softc *sc = fifo->priv_sc0; if (sc->sc_fflags & fflags) { return (EBUSY); } if (fflags & FREAD) { /* clear stall first */ mtx_lock(&sc->sc_mtx); sc->sc_flags |= ULPT_FLAG_READ_STALL; mtx_unlock(&sc->sc_mtx); if (usb2_fifo_alloc_buffer(fifo, sc->sc_xfer[1]->max_data_length, ULPT_IFQ_MAXLEN)) { return (ENOMEM); } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_RX] = fifo; } if (fflags & FWRITE) { /* clear stall first */ mtx_lock(&sc->sc_mtx); sc->sc_flags |= ULPT_FLAG_WRITE_STALL; mtx_unlock(&sc->sc_mtx); if (usb2_fifo_alloc_buffer(fifo, sc->sc_xfer[0]->max_data_length, ULPT_IFQ_MAXLEN)) { return (ENOMEM); } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_TX] = fifo; } sc->sc_fflags |= fflags & (FREAD | FWRITE); return (0); } static void ulpt_close(struct usb2_fifo *fifo, int fflags, struct thread *td) { struct ulpt_softc *sc = fifo->priv_sc0; sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); if (fflags & (FREAD | FWRITE)) { usb2_fifo_free_buffer(fifo); } } static int ulpt_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, int fflags, struct thread *td) { return (ENODEV); } static int ulpt_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); DPRINTFN(11, "\n"); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if ((uaa->info.bInterfaceClass == UICLASS_PRINTER) && (uaa->info.bInterfaceSubClass == UISUBCLASS_PRINTER) && ((uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_UNI) || (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_BI) || (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_1284))) { return (0); } return (ENXIO); } static int ulpt_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct ulpt_softc *sc = device_get_softc(dev); struct usb2_interface_descriptor *id; int unit = device_get_unit(dev); int error; uint8_t iface_index = uaa->info.bIfaceIndex; uint8_t alt_index; DPRINTFN(11, "sc=%p\n", sc); sc->sc_dev = dev; sc->sc_udev = uaa->device; device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); /* search through all the descriptors looking for bidir mode */ id = usb2_get_interface_descriptor(uaa->iface); alt_index = 0 - 1; while (1) { if (id == NULL) { break; } if ((id->bDescriptorType == UDESC_INTERFACE) && (id->bLength >= sizeof(*id))) { if (id->bInterfaceNumber != uaa->info.bIfaceNum) { break; } else { alt_index++; if ((id->bInterfaceClass == UICLASS_PRINTER) && (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) { goto found; } } } id = (void *)usb2_desc_foreach( usb2_get_config_descriptor(uaa->device), (void *)id); } goto detach; found: DPRINTF("setting alternate " "config number: %d\n", alt_index); if (alt_index) { error = usb2_set_alt_interface_index (uaa->device, iface_index, alt_index); if (error) { DPRINTF("could not set alternate " "config, error=%s\n", usb2_errstr(error)); goto detach; } } sc->sc_iface_no = id->bInterfaceNumber; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("error=%s\n", usb2_errstr(error)); goto detach; } device_printf(sc->sc_dev, "using bi-directional mode\n"); #if 0 /* * This code is disabled because for some mysterious reason it causes * printing not to work. But only sometimes, and mostly with * UHCI and less often with OHCI. *sigh* */ { struct usb2_config_descriptor *cd = usb2_get_config_descriptor(dev); struct usb2_device_request req; int len, alen; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_DEVICE_ID; USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); error = usb2_do_request_flags(dev, &req, devinfo, USB_SHORT_XFER_OK, &alen, USB_DEFAULT_TIMEOUT); if (error) { device_printf(sc->sc_dev, "cannot get device id\n"); } else if (alen <= 2) { device_printf(sc->sc_dev, "empty device id, no " "printer connected?\n"); } else { /* devinfo now contains an IEEE-1284 device ID */ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff); if (len > sizeof devinfo - 3) len = sizeof devinfo - 3; devinfo[len] = 0; printf("%s: device id <", device_get_nameunit(sc->sc_dev)); ieee1284_print_id(devinfo + 2); printf(">\n"); } } #endif /* set interface permissions */ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, &ulpt_fifo_methods, &sc->sc_fifo, unit, 0 - 1, uaa->info.bIfaceIndex); if (error) { goto detach; } error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, &unlpt_fifo_methods, &sc->sc_fifo_noreset, unit, 0 - 1, uaa->info.bIfaceIndex); if (error) { goto detach; } /* start reading of status */ mtx_lock(&sc->sc_mtx); - - ulpt_watchdog(sc); /* will unlock mutex */ - + ulpt_watchdog(sc); + mtx_unlock(&sc->sc_mtx); return (0); detach: ulpt_detach(dev); return (ENOMEM); } static int ulpt_detach(device_t dev) { struct ulpt_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); usb2_fifo_detach(&sc->sc_fifo); usb2_fifo_detach(&sc->sc_fifo_noreset); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); mtx_unlock(&sc->sc_mtx); usb2_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } #if 0 /* XXX This does not belong here. */ /* * Compare two strings until the second ends. */ static uint8_t ieee1284_compare(const char *a, const char *b) { while (1) { if (*b == 0) { break; } if (*a != *b) { return 1; } b++; a++; } return 0; } /* * Print select parts of an IEEE 1284 device ID. */ void ieee1284_print_id(char *str) { char *p, *q; for (p = str - 1; p; p = strchr(p, ';')) { p++; /* skip ';' */ if (ieee1284_compare(p, "MFG:") == 0 || ieee1284_compare(p, "MANUFACTURER:") == 0 || ieee1284_compare(p, "MDL:") == 0 || ieee1284_compare(p, "MODEL:") == 0) { q = strchr(p, ';'); if (q) printf("%.*s", (int)(q - p + 1), p); } } } #endif static void ulpt_watchdog(void *arg) { struct ulpt_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usb2_transfer_start(sc->sc_xfer[2]); usb2_callout_reset(&sc->sc_watchdog, hz, &ulpt_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } static devclass_t ulpt_devclass; static device_method_t ulpt_methods[] = { DEVMETHOD(device_probe, ulpt_probe), DEVMETHOD(device_attach, ulpt_attach), DEVMETHOD(device_detach, ulpt_detach), {0, 0} }; static driver_t ulpt_driver = { .name = "ulpt", .methods = ulpt_methods, .size = sizeof(struct ulpt_softc), }; DRIVER_MODULE(ulpt, ushub, ulpt_driver, ulpt_devclass, NULL, 0); MODULE_DEPEND(ulpt, usb2_core, 1, 1, 1); MODULE_DEPEND(ulpt, usb2_serial, 1, 1, 1); Index: projects/cambria/sys/dev/usb2/wlan/if_rum2.c =================================================================== --- projects/cambria/sys/dev/usb2/wlan/if_rum2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/wlan/if_rum2.c (revision 186460) @@ -1,2906 +1,2901 @@ /*- * Copyright (c) 2005-2007 Damien Bergamini * Copyright (c) 2006 Niall O'Higgins * Copyright (c) 2007-2008 Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * NOTE: all function names beginning like "rum_cfg_" can only * be called from within the config thread function ! */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2501USB/RT2601USB chipset driver * http://www.ralinktech.com.tw/ */ #include #include #include #include #define usb2_config_td_cc rum_config_copy #define usb2_config_td_softc rum_softc #define USB_DEBUG_VAR rum_debug #include #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int rum_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); SYSCTL_INT(_hw_usb2_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0, "Debug level"); #endif /* prototypes */ static device_probe_t rum_probe; static device_attach_t rum_attach; static device_detach_t rum_detach; static usb2_callback_t rum_bulk_read_callback; static usb2_callback_t rum_bulk_read_clear_stall_callback; static usb2_callback_t rum_bulk_write_callback; static usb2_callback_t rum_bulk_write_clear_stall_callback; static usb2_config_td_command_t rum_cfg_first_time_setup; static usb2_config_td_command_t rum_config_copy; static usb2_config_td_command_t rum_cfg_scan_start; static usb2_config_td_command_t rum_cfg_scan_end; static usb2_config_td_command_t rum_cfg_select_band; static usb2_config_td_command_t rum_cfg_set_chan; static usb2_config_td_command_t rum_cfg_enable_tsf_sync; static usb2_config_td_command_t rum_cfg_enable_mrr; static usb2_config_td_command_t rum_cfg_update_slot; static usb2_config_td_command_t rum_cfg_select_antenna; static usb2_config_td_command_t rum_cfg_set_txpreamble; static usb2_config_td_command_t rum_cfg_update_promisc; static usb2_config_td_command_t rum_cfg_pre_init; static usb2_config_td_command_t rum_cfg_init; static usb2_config_td_command_t rum_cfg_pre_stop; static usb2_config_td_command_t rum_cfg_stop; static usb2_config_td_command_t rum_cfg_amrr_timeout; static usb2_config_td_command_t rum_cfg_prepare_beacon; static usb2_config_td_command_t rum_cfg_newstate; static const char *rum_get_rf(uint32_t); static int rum_ioctl_cb(struct ifnet *, u_long, caddr_t); static void rum_std_command(struct ieee80211com *, usb2_config_td_command_t *); static void rum_scan_start_cb(struct ieee80211com *); static void rum_scan_end_cb(struct ieee80211com *); static void rum_set_channel_cb(struct ieee80211com *); static uint16_t rum_cfg_eeprom_read_2(struct rum_softc *, uint16_t); static uint32_t rum_cfg_bbp_disbusy(struct rum_softc *); static uint32_t rum_cfg_read(struct rum_softc *, uint16_t); static uint8_t rum_cfg_bbp_init(struct rum_softc *); static uint8_t rum_cfg_bbp_read(struct rum_softc *, uint8_t); static void rum_cfg_amrr_start(struct rum_softc *); static void rum_cfg_bbp_write(struct rum_softc *, uint8_t, uint8_t); static void rum_cfg_do_request(struct rum_softc *, struct usb2_device_request *, void *); static void rum_cfg_eeprom_read(struct rum_softc *, uint16_t, void *, uint16_t); static void rum_cfg_load_microcode(struct rum_softc *, const uint8_t *, uint16_t); static void rum_cfg_read_eeprom(struct rum_softc *); static void rum_cfg_read_multi(struct rum_softc *, uint16_t, void *, uint16_t); static void rum_cfg_rf_write(struct rum_softc *, uint8_t, uint32_t); static void rum_cfg_set_bssid(struct rum_softc *, uint8_t *); static void rum_cfg_set_macaddr(struct rum_softc *, uint8_t *); static void rum_cfg_write(struct rum_softc *, uint16_t, uint32_t); static void rum_cfg_write_multi(struct rum_softc *, uint16_t, void *, uint16_t); static void rum_end_of_commands(struct rum_softc *); static void rum_init_cb(void *); static void rum_start_cb(struct ifnet *); static void rum_watchdog(void *); static uint8_t rum_get_rssi(struct rum_softc *, uint8_t); static struct ieee80211vap *rum_vap_create(struct ieee80211com *, const char[], int, int, int, const uint8_t[], const uint8_t[]); static void rum_vap_delete(struct ieee80211vap *); static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *, const uint8_t[]); static void rum_newassoc(struct ieee80211_node *, int); static void rum_cfg_disable_tsf_sync(struct rum_softc *); static void rum_cfg_set_run(struct rum_softc *, struct rum_config_copy *); static void rum_fill_write_queue(struct rum_softc *); static void rum_tx_clean_queue(struct rum_softc *); static void rum_tx_freem(struct mbuf *); static void rum_tx_mgt(struct rum_softc *, struct mbuf *, struct ieee80211_node *); static struct ieee80211vap *rum_get_vap(struct rum_softc *); static void rum_tx_data(struct rum_softc *, struct mbuf *, struct ieee80211_node *); static void rum_tx_prot(struct rum_softc *, const struct mbuf *, struct ieee80211_node *, uint8_t, uint16_t); static void rum_tx_raw(struct rum_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int rum_raw_xmit_cb(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void rum_setup_desc_and_tx(struct rum_softc *, struct mbuf *, uint32_t, uint16_t, uint16_t); static int rum_newstate_cb(struct ieee80211vap *, enum ieee80211_state nstate, int arg); static void rum_update_mcast_cb(struct ifnet *); static void rum_update_promisc_cb(struct ifnet *); /* various supported device vendors/products */ static const struct usb2_device_id rum_devs[] = { {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4, 0)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700, 0)}, {USB_VPI(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO, 0)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1, 0)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR, 0)}, {USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2, 0)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL, 0)}, {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX, 0)}, {USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F, 0)}, {USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111, 0)}, {USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS, 0)}, {USB_VPI(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573, 0)}, {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573, 0)}, {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB, 0)}, {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP, 0)}, {USB_VPI(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4, 0)}, {USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573, 0)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP, 0)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2, 0)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM, 0)}, {USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573, 0)}, {USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671, 0)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2, 0)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172, 0)}, {USB_VPI(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573, 0)}, {USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573, 0)}, }; struct rum_def_mac { uint32_t reg; uint32_t val; }; static const struct rum_def_mac rum_def_mac[] = { {RT2573_TXRX_CSR0, 0x025fb032}, {RT2573_TXRX_CSR1, 0x9eaa9eaf}, {RT2573_TXRX_CSR2, 0x8a8b8c8d}, {RT2573_TXRX_CSR3, 0x00858687}, {RT2573_TXRX_CSR7, 0x2e31353b}, {RT2573_TXRX_CSR8, 0x2a2a2a2c}, {RT2573_TXRX_CSR15, 0x0000000f}, {RT2573_MAC_CSR6, 0x00000fff}, {RT2573_MAC_CSR8, 0x016c030a}, {RT2573_MAC_CSR10, 0x00000718}, {RT2573_MAC_CSR12, 0x00000004}, {RT2573_MAC_CSR13, 0x00007f00}, {RT2573_SEC_CSR0, 0x00000000}, {RT2573_SEC_CSR1, 0x00000000}, {RT2573_SEC_CSR5, 0x00000000}, {RT2573_PHY_CSR1, 0x000023b0}, {RT2573_PHY_CSR5, 0x00040a06}, {RT2573_PHY_CSR6, 0x00080606}, {RT2573_PHY_CSR7, 0x00000408}, {RT2573_AIFSN_CSR, 0x00002273}, {RT2573_CWMIN_CSR, 0x00002344}, {RT2573_CWMAX_CSR, 0x000034aa} }; struct rum_def_bbp { uint8_t reg; uint8_t val; }; static const struct rum_def_bbp rum_def_bbp[] = { {3, 0x80}, {15, 0x30}, {17, 0x20}, {21, 0xc8}, {22, 0x38}, {23, 0x06}, {24, 0xfe}, {25, 0x0a}, {26, 0x0d}, {32, 0x0b}, {34, 0x12}, {37, 0x07}, {39, 0xf8}, {41, 0x60}, {53, 0x10}, {54, 0x18}, {60, 0x10}, {61, 0x04}, {62, 0x04}, {75, 0xfe}, {86, 0xfe}, {88, 0xfe}, {90, 0x0f}, {99, 0x00}, {102, 0x16}, {107, 0x04} }; struct rfprog { uint8_t chan; uint32_t r1, r2, r3, r4; }; static const struct rfprog rum_rf5226[] = { {1, 0x00b03, 0x001e1, 0x1a014, 0x30282}, {2, 0x00b03, 0x001e1, 0x1a014, 0x30287}, {3, 0x00b03, 0x001e2, 0x1a014, 0x30282}, {4, 0x00b03, 0x001e2, 0x1a014, 0x30287}, {5, 0x00b03, 0x001e3, 0x1a014, 0x30282}, {6, 0x00b03, 0x001e3, 0x1a014, 0x30287}, {7, 0x00b03, 0x001e4, 0x1a014, 0x30282}, {8, 0x00b03, 0x001e4, 0x1a014, 0x30287}, {9, 0x00b03, 0x001e5, 0x1a014, 0x30282}, {10, 0x00b03, 0x001e5, 0x1a014, 0x30287}, {11, 0x00b03, 0x001e6, 0x1a014, 0x30282}, {12, 0x00b03, 0x001e6, 0x1a014, 0x30287}, {13, 0x00b03, 0x001e7, 0x1a014, 0x30282}, {14, 0x00b03, 0x001e8, 0x1a014, 0x30284}, {34, 0x00b03, 0x20266, 0x36014, 0x30282}, {38, 0x00b03, 0x20267, 0x36014, 0x30284}, {42, 0x00b03, 0x20268, 0x36014, 0x30286}, {46, 0x00b03, 0x20269, 0x36014, 0x30288}, {36, 0x00b03, 0x00266, 0x26014, 0x30288}, {40, 0x00b03, 0x00268, 0x26014, 0x30280}, {44, 0x00b03, 0x00269, 0x26014, 0x30282}, {48, 0x00b03, 0x0026a, 0x26014, 0x30284}, {52, 0x00b03, 0x0026b, 0x26014, 0x30286}, {56, 0x00b03, 0x0026c, 0x26014, 0x30288}, {60, 0x00b03, 0x0026e, 0x26014, 0x30280}, {64, 0x00b03, 0x0026f, 0x26014, 0x30282}, {100, 0x00b03, 0x0028a, 0x2e014, 0x30280}, {104, 0x00b03, 0x0028b, 0x2e014, 0x30282}, {108, 0x00b03, 0x0028c, 0x2e014, 0x30284}, {112, 0x00b03, 0x0028d, 0x2e014, 0x30286}, {116, 0x00b03, 0x0028e, 0x2e014, 0x30288}, {120, 0x00b03, 0x002a0, 0x2e014, 0x30280}, {124, 0x00b03, 0x002a1, 0x2e014, 0x30282}, {128, 0x00b03, 0x002a2, 0x2e014, 0x30284}, {132, 0x00b03, 0x002a3, 0x2e014, 0x30286}, {136, 0x00b03, 0x002a4, 0x2e014, 0x30288}, {140, 0x00b03, 0x002a6, 0x2e014, 0x30280}, {149, 0x00b03, 0x002a8, 0x2e014, 0x30287}, {153, 0x00b03, 0x002a9, 0x2e014, 0x30289}, {157, 0x00b03, 0x002ab, 0x2e014, 0x30281}, {161, 0x00b03, 0x002ac, 0x2e014, 0x30283}, {165, 0x00b03, 0x002ad, 0x2e014, 0x30285} }; static const struct rfprog rum_rf5225[] = { {1, 0x00b33, 0x011e1, 0x1a014, 0x30282}, {2, 0x00b33, 0x011e1, 0x1a014, 0x30287}, {3, 0x00b33, 0x011e2, 0x1a014, 0x30282}, {4, 0x00b33, 0x011e2, 0x1a014, 0x30287}, {5, 0x00b33, 0x011e3, 0x1a014, 0x30282}, {6, 0x00b33, 0x011e3, 0x1a014, 0x30287}, {7, 0x00b33, 0x011e4, 0x1a014, 0x30282}, {8, 0x00b33, 0x011e4, 0x1a014, 0x30287}, {9, 0x00b33, 0x011e5, 0x1a014, 0x30282}, {10, 0x00b33, 0x011e5, 0x1a014, 0x30287}, {11, 0x00b33, 0x011e6, 0x1a014, 0x30282}, {12, 0x00b33, 0x011e6, 0x1a014, 0x30287}, {13, 0x00b33, 0x011e7, 0x1a014, 0x30282}, {14, 0x00b33, 0x011e8, 0x1a014, 0x30284}, {34, 0x00b33, 0x01266, 0x26014, 0x30282}, {38, 0x00b33, 0x01267, 0x26014, 0x30284}, {42, 0x00b33, 0x01268, 0x26014, 0x30286}, {46, 0x00b33, 0x01269, 0x26014, 0x30288}, {36, 0x00b33, 0x01266, 0x26014, 0x30288}, {40, 0x00b33, 0x01268, 0x26014, 0x30280}, {44, 0x00b33, 0x01269, 0x26014, 0x30282}, {48, 0x00b33, 0x0126a, 0x26014, 0x30284}, {52, 0x00b33, 0x0126b, 0x26014, 0x30286}, {56, 0x00b33, 0x0126c, 0x26014, 0x30288}, {60, 0x00b33, 0x0126e, 0x26014, 0x30280}, {64, 0x00b33, 0x0126f, 0x26014, 0x30282}, {100, 0x00b33, 0x0128a, 0x2e014, 0x30280}, {104, 0x00b33, 0x0128b, 0x2e014, 0x30282}, {108, 0x00b33, 0x0128c, 0x2e014, 0x30284}, {112, 0x00b33, 0x0128d, 0x2e014, 0x30286}, {116, 0x00b33, 0x0128e, 0x2e014, 0x30288}, {120, 0x00b33, 0x012a0, 0x2e014, 0x30280}, {124, 0x00b33, 0x012a1, 0x2e014, 0x30282}, {128, 0x00b33, 0x012a2, 0x2e014, 0x30284}, {132, 0x00b33, 0x012a3, 0x2e014, 0x30286}, {136, 0x00b33, 0x012a4, 0x2e014, 0x30288}, {140, 0x00b33, 0x012a6, 0x2e014, 0x30280}, {149, 0x00b33, 0x012a8, 0x2e014, 0x30287}, {153, 0x00b33, 0x012a9, 0x2e014, 0x30289}, {157, 0x00b33, 0x012ab, 0x2e014, 0x30281}, {161, 0x00b33, 0x012ac, 0x2e014, 0x30283}, {165, 0x00b33, 0x012ad, 0x2e014, 0x30285} }; static const struct usb2_config rum_config[RUM_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &rum_bulk_write_callback, .mh.timeout = 5000, /* ms */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &rum_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &rum_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &rum_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t rum_devclass; static device_method_t rum_methods[] = { DEVMETHOD(device_probe, rum_probe), DEVMETHOD(device_attach, rum_attach), DEVMETHOD(device_detach, rum_detach), {0, 0} }; static driver_t rum_driver = { .name = "rum", .methods = rum_methods, .size = sizeof(struct rum_softc), }; DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0); MODULE_DEPEND(rum, usb2_wlan, 1, 1, 1); MODULE_DEPEND(rum, usb2_core, 1, 1, 1); MODULE_DEPEND(rum, wlan, 1, 1, 1); MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); static int rum_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa)); } static int rum_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct rum_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "rum lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_udev = uaa->device; sc->sc_unit = device_get_unit(dev); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = RT2573_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usb2_errstr(error)); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, &rum_end_of_commands, sizeof(struct usb2_config_td_cc), 24); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rum_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - rum_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: rum_detach(dev); return (ENXIO); /* failure */ } static int rum_detach(device_t dev) { struct rum_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); rum_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; ic = ifp->if_l2com; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } static void rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; repeat: if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); /* wait a little before next try */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) { goto error; } /* try until we are detached */ goto repeat; error: /* the device has been detached */ length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } static void rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); rum_cfg_do_request(sc, &req, buf); } static uint16_t rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr) { uint16_t tmp; rum_cfg_eeprom_read(sc, addr, &tmp, sizeof(tmp)); return (le16toh(tmp)); } static uint32_t rum_cfg_read(struct rum_softc *sc, uint16_t reg) { uint32_t val; rum_cfg_read_multi(sc, reg, &val, sizeof(val)); return (le32toh(val)); } static void rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); rum_cfg_do_request(sc, &req, buf); } static void rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val) { uint32_t tmp = htole32(val); rum_cfg_write_multi(sc, reg, &tmp, sizeof(tmp)); } static void rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); rum_cfg_do_request(sc, &req, buf); } static uint32_t rum_cfg_bbp_disbusy(struct rum_softc *sc) { uint32_t tmp; uint8_t to; for (to = 0;; to++) { if (to < 100) { tmp = rum_cfg_read(sc, RT2573_PHY_CSR3); if ((tmp & RT2573_BBP_BUSY) == 0) { return (tmp); } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { break; } } DPRINTF("could not disbusy BBP\n"); return (RT2573_BBP_BUSY); /* failure */ } static void rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) { return; } tmp = RT2573_BBP_BUSY | ((reg & 0x7f) << 8) | val; rum_cfg_write(sc, RT2573_PHY_CSR3, tmp); } static uint8_t rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg) { uint32_t val; if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) { return (0); } val = RT2573_BBP_BUSY | RT2573_BBP_READ | (reg << 8); rum_cfg_write(sc, RT2573_PHY_CSR3, val); val = rum_cfg_bbp_disbusy(sc); return (val & 0xff); } static void rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; uint8_t to; reg &= 3; for (to = 0;; to++) { if (to < 100) { tmp = rum_cfg_read(sc, RT2573_PHY_CSR4); if (!(tmp & RT2573_RF_BUSY)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } } else { DPRINTF("could not write to RF\n"); return; } } tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | ((val & 0xfffff) << 2) | reg; rum_cfg_write(sc, RT2573_PHY_CSR4, tmp); DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); } static void rum_cfg_first_time_setup(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211com *ic; struct ifnet *ifp; uint32_t tmp; uint16_t i; uint8_t bands; /* setup RX tap header */ sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT); /* setup TX tap header */ sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT); /* retrieve RT2573 rev. no */ for (i = 0; i < 100; i++) { tmp = rum_cfg_read(sc, RT2573_MAC_CSR0); if (tmp != 0) { break; } /* wait a little */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { /* device detached */ goto done; } } if (tmp == 0) { DPRINTF("chip is maybe not ready\n"); } /* retrieve MAC address and various other things from EEPROM */ rum_cfg_read_eeprom(sc); printf("%s: MAC/BBP RT2573 (rev 0x%05x), RF %s\n", sc->sc_name, tmp, rum_get_rf(sc->sc_rf_rev)); rum_cfg_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_IEEE80211); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { DPRINTFN(0, "could not if_alloc()!\n"); goto done; } sc->sc_evilhack = ifp; sc->sc_ifp = ifp; ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "rum", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &rum_init_cb; ifp->if_ioctl = &rum_ioctl_cb; ifp->if_start = &rum_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, &bands); if ((sc->sc_rf_rev == RT2573_RF_5225) || (sc->sc_rf_rev == RT2573_RF_5226)) { struct ieee80211_channel *c; /* set supported .11a channels */ for (i = 34; i <= 46; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } for (i = 36; i <= 64; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } for (i = 100; i <= 140; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } for (i = 149; i <= 165; i += 4) { c = ic->ic_channels + (ic->ic_nchans++); c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); c->ic_flags = IEEE80211_CHAN_A; c->ic_ieee = i; } } mtx_unlock(&sc->sc_mtx); ieee80211_ifattach(ic); mtx_lock(&sc->sc_mtx); ic->ic_newassoc = &rum_newassoc; ic->ic_raw_xmit = &rum_raw_xmit_cb; ic->ic_node_alloc = &rum_node_alloc; ic->ic_update_mcast = &rum_update_mcast_cb; ic->ic_update_promisc = &rum_update_promisc_cb; ic->ic_scan_start = &rum_scan_start_cb; ic->ic_scan_end = &rum_scan_end_cb; ic->ic_set_channel = &rum_set_channel_cb; ic->ic_vap_create = &rum_vap_create; ic->ic_vap_delete = &rum_vap_delete; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); mtx_unlock(&sc->sc_mtx); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); if (bootverbose) { ieee80211_announce(ic); } mtx_lock(&sc->sc_mtx); done: return; } static void rum_end_of_commands(struct rum_softc *sc) { sc->sc_flags &= ~RUM_FLAG_WAIT_COMMAND; /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); } static void rum_config_copy_chan(struct rum_config_copy_chan *cc, struct ieee80211com *ic, struct ieee80211_channel *c) { if (!c) return; cc->chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->chan_to_mode = ieee80211_chan2mode(c); if (IEEE80211_IS_CHAN_B(c)) cc->chan_is_b = 1; if (IEEE80211_IS_CHAN_A(c)) cc->chan_is_a = 1; if (IEEE80211_IS_CHAN_2GHZ(c)) cc->chan_is_2ghz = 1; if (IEEE80211_IS_CHAN_5GHZ(c)) cc->chan_is_5ghz = 1; if (IEEE80211_IS_CHAN_ANYG(c)) cc->chan_is_g = 1; } } static void rum_config_copy(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; struct ieee80211com *ic; struct ieee80211_node *ni; struct ieee80211vap *vap; const struct ieee80211_txparam *tp; bzero(cc, sizeof(*cc)); ifp = sc->sc_ifp; if (ifp) { cc->if_flags = ifp->if_flags; bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, sizeof(cc->if_broadcastaddr)); ic = ifp->if_l2com; if (ic) { rum_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); rum_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); vap = TAILQ_FIRST(&ic->ic_vaps); if (vap) { ni = vap->iv_bss; if (ni) { cc->iv_bss.ni_intval = ni->ni_intval; bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, sizeof(cc->iv_bss.ni_bssid)); } tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { cc->iv_bss.fixed_rate_none = 1; } } cc->ic_opmode = ic->ic_opmode; cc->ic_flags = ic->ic_flags; cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); } } sc->sc_flags |= RUM_FLAG_WAIT_COMMAND; } static const char * rum_get_rf(uint32_t rev) { ; /* indent fix */ switch (rev) { case RT2573_RF_2527: return "RT2527 (MIMO XR)"; case RT2573_RF_2528: return "RT2528"; case RT2573_RF_5225: return "RT5225 (MIMO XR)"; case RT2573_RF_5226: return "RT5226"; default: return "unknown"; } } static void rum_bulk_read_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct mbuf *m = NULL; uint32_t flags; uint32_t max_len; uint8_t rssi = 0; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); if (xfer->actlen < (RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { DPRINTF("too short transfer, " "%d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, &sc->sc_rx_desc, RT2573_RX_DESC_SIZE); flags = le32toh(sc->sc_rx_desc.flags); if (flags & RT2573_RX_CRC_ERROR) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTFN(6, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } max_len = (xfer->actlen - RT2573_RX_DESC_SIZE); usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE, m->m_data, max_len); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; if (m->m_len > max_len) { DPRINTF("invalid length in RX " "descriptor, %u bytes, received %u bytes\n", m->m_len, max_len); ifp->if_ierrors++; m_freem(m); m = NULL; goto tr_setup; } rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); DPRINTF("real length=%d bytes, rssi=%d\n", m->m_len, rssi); if (bpf_peers_present(ifp->if_bpf)) { struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (sc->sc_rx_desc.flags & htole32(RT2573_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->sc_rx_ant; tap->wr_antsignal = rssi; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } case USB_ST_SETUP: tr_setup: if (sc->sc_flags & RUM_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { if (ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0)) { /* ignore */ } /* node is no longer needed */ ieee80211_free_node(ni); } else { if (ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0)) { /* ignore */ } } mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUM_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } return; } } static void rum_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUM_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static uint8_t rum_plcp_signal(uint16_t rate) { ; /* indent fix */ switch (rate) { /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* XXX unsupported/unknown rate */ default: return (0xff); } } /* * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that * should be freed, when "rum_setup_desc_and_tx" is called. */ static void rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags, uint16_t xflags, uint16_t rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mm; enum ieee80211_phytype phytype; uint16_t plcp_length; uint16_t len; uint8_t remainder; uint8_t is_beacon; if (xflags & RT2573_TX_BEACON) { xflags &= ~RT2573_TX_BEACON; is_beacon = 1; } else { is_beacon = 0; } if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { /* free packet */ rum_tx_freem(m); ifp->if_oerrors++; return; } if (!((sc->sc_flags & RUM_FLAG_LL_READY) && (sc->sc_flags & RUM_FLAG_HL_READY))) { /* free packet */ rum_tx_freem(m); ifp->if_oerrors++; return; } if (rate < 2) { DPRINTF("rate < 2!\n"); /* avoid division by zero */ rate = 2; } ic->ic_lastdata = ticks; if (bpf_peers_present(ifp->if_bpf)) { struct rum_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->sc_tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } len = m->m_pkthdr.len; flags |= RT2573_TX_VALID; flags |= (len << 16); sc->sc_tx_desc.flags = htole32(flags); sc->sc_tx_desc.xflags = htole16(xflags); sc->sc_tx_desc.wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); /* setup PLCP fields */ sc->sc_tx_desc.plcp_signal = rum_plcp_signal(rate); sc->sc_tx_desc.plcp_service = 4; len += IEEE80211_CRC_LEN; phytype = ieee80211_rate2phytype(sc->sc_rates, rate); if (phytype == IEEE80211_T_OFDM) { sc->sc_tx_desc.flags |= htole32(RT2573_TX_OFDM); plcp_length = (len & 0xfff); sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = ((16 * len) + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if ((remainder != 0) && (remainder < 7)) { sc->sc_tx_desc.plcp_service |= RT2573_PLCP_LENGEXT; } } sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { sc->sc_tx_desc.plcp_signal |= 0x08; } } if (sizeof(sc->sc_tx_desc) > MHLEN) { DPRINTF("No room for header structure!\n"); rum_tx_freem(m); return; } mm = m_gethdr(M_NOWAIT, MT_DATA); if (mm == NULL) { DPRINTF("Could not allocate header mbuf!\n"); rum_tx_freem(m); return; } bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); mm->m_len = sizeof(sc->sc_tx_desc); mm->m_next = m; mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; mm->m_pkthdr.rcvif = NULL; if (is_beacon) { if (mm->m_pkthdr.len > sizeof(sc->sc_beacon_buf)) { DPRINTFN(0, "Truncating beacon" ", %u bytes!\n", mm->m_pkthdr.len); mm->m_pkthdr.len = sizeof(sc->sc_beacon_buf); } m_copydata(mm, 0, mm->m_pkthdr.len, sc->sc_beacon_buf); /* copy the first 24 bytes of Tx descriptor into NIC memory */ rum_cfg_write_multi(sc, RT2573_HW_BEACON_BASE0, sc->sc_beacon_buf, mm->m_pkthdr.len); rum_tx_freem(mm); return; } /* start write transfer, if not started */ _IF_ENQUEUE(&sc->sc_tx_queue, mm); usb2_transfer_start(sc->sc_xfer[0]); } static void rum_bulk_write_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t temp_len; uint8_t align; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & RUM_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); break; } if (sc->sc_flags & RUM_FLAG_WAIT_COMMAND) { /* * don't send anything while a command is pending ! */ break; } rum_fill_write_queue(sc); _IF_DEQUEUE(&sc->sc_tx_queue, m); if (m) { if (m->m_pkthdr.len > (MCLBYTES + RT2573_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE); } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* compute transfer length */ temp_len = m->m_pkthdr.len; /* make transfer length 32-bit aligned */ align = (-(temp_len)) & 3; /* check if we need to add four extra bytes */ if (((temp_len + align) % 64) == 0) { align += 4; } /* check if we need to align length */ if (align != 0) { /* zero the extra bytes */ usb2_bzero(xfer->frbuffers, temp_len, align); temp_len += align; } DPRINTFN(11, "sending frame len=%u ferlen=%u\n", m->m_pkthdr.len, temp_len); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); /* free mbuf and node */ rum_tx_freem(m); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= RUM_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; break; } } static void rum_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~RUM_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void rum_watchdog(void *arg) { struct rum_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_amrr_timer) { usb2_config_td_queue_command (&sc->sc_config_td, NULL, &rum_cfg_amrr_timeout, 0, 0); } usb2_callout_reset(&sc->sc_watchdog, hz, &rum_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } static void rum_init_cb(void *arg) { struct rum_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rum_cfg_pre_init, &rum_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static int rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct rum_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; int error; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { usb2_config_td_queue_command (&sc->sc_config_td, &rum_cfg_pre_init, &rum_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &rum_cfg_pre_stop, &rum_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); } return (error); } static void rum_start_cb(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); mtx_unlock(&sc->sc_mtx); } static void rum_cfg_newstate(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct rum_vap *uvp = RUM_VAP(vap); enum ieee80211_state ostate; enum ieee80211_state nstate; int arg; ostate = vap->iv_state; nstate = sc->sc_ns_state; arg = sc->sc_ns_arg; if (ostate == IEEE80211_S_INIT) { /* We are leaving INIT. TSF sync should be off. */ rum_cfg_disable_tsf_sync(sc); } switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: rum_cfg_set_run(sc, cc); break; default: break; } mtx_unlock(&sc->sc_mtx); IEEE80211_LOCK(ic); uvp->newstate(vap, nstate, arg); if (vap->iv_newstate_cb != NULL) vap->iv_newstate_cb(vap, nstate, arg); IEEE80211_UNLOCK(ic); mtx_lock(&sc->sc_mtx); } static int rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rum_vap *uvp = RUM_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct rum_softc *sc = ic->ic_ifp->if_softc; DPRINTF("setting new state: %d\n", nstate); /* Special case - cannot defer this call and cannot block ! */ if (nstate == IEEE80211_S_INIT) { /* stop timers */ mtx_lock(&sc->sc_mtx); sc->sc_amrr_timer = 0; mtx_unlock(&sc->sc_mtx); return (uvp->newstate(vap, nstate, arg)); } mtx_lock(&sc->sc_mtx); if (usb2_config_td_is_gone(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); return (0); /* nothing to do */ } /* store next state */ sc->sc_ns_state = nstate; sc->sc_ns_arg = arg; /* stop timers */ sc->sc_amrr_timer = 0; /* * USB configuration can only be done from the USB configuration * thread: */ usb2_config_td_queue_command (&sc->sc_config_td, &rum_config_copy, &rum_cfg_newstate, 0, 0); mtx_unlock(&sc->sc_mtx); return (EINPROGRESS); } static void rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) { struct rum_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&sc->sc_mtx); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); usb2_config_td_queue_command (&sc->sc_config_td, &rum_config_copy, func, 0, 0); mtx_unlock(&sc->sc_mtx); } static void rum_scan_start_cb(struct ieee80211com *ic) { rum_std_command(ic, &rum_cfg_scan_start); } static void rum_scan_end_cb(struct ieee80211com *ic) { rum_std_command(ic, &rum_cfg_scan_end); } static void rum_set_channel_cb(struct ieee80211com *ic) { rum_std_command(ic, &rum_cfg_set_chan); } static void rum_cfg_scan_start(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* abort TSF synchronization */ rum_cfg_disable_tsf_sync(sc); rum_cfg_set_bssid(sc, cc->if_broadcastaddr); } static void rum_cfg_scan_end(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* enable TSF synchronization */ rum_cfg_enable_tsf_sync(sc, cc, 0); rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } /* * Reprogram MAC/BBP to switch to a new band. Values taken from the reference * driver. */ static void rum_cfg_select_band(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; /* update all BBP registers that depend on the band */ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; if (cc->ic_curchan.chan_is_5ghz) { bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; } if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) || (cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) { bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; } sc->sc_bbp17 = bbp17; rum_cfg_bbp_write(sc, 17, bbp17); rum_cfg_bbp_write(sc, 96, bbp96); rum_cfg_bbp_write(sc, 104, bbp104); if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) || (cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) { rum_cfg_bbp_write(sc, 75, 0x80); rum_cfg_bbp_write(sc, 86, 0x80); rum_cfg_bbp_write(sc, 88, 0x80); } rum_cfg_bbp_write(sc, 35, bbp35); rum_cfg_bbp_write(sc, 97, bbp97); rum_cfg_bbp_write(sc, 98, bbp98); tmp = rum_cfg_read(sc, RT2573_PHY_CSR0); tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); if (cc->ic_curchan.chan_is_2ghz) tmp |= RT2573_PA_PE_2GHZ; else tmp |= RT2573_PA_PE_5GHZ; rum_cfg_write(sc, RT2573_PHY_CSR0, tmp); /* 802.11a uses a 16 microseconds short interframe space */ sc->sc_sifs = cc->ic_curchan.chan_is_5ghz ? 16 : 10; } static void rum_cfg_set_chan(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_RF5225 = (sizeof(rum_rf5225) / sizeof(rum_rf5225[0]))}; const struct rfprog *rfprog; uint32_t chan; uint16_t i; uint8_t bbp3; uint8_t bbp94 = RT2573_BBPR94_DEFAULT; int8_t power; chan = cc->ic_curchan.chan_to_ieee; if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { /* nothing to do */ return; } if (chan == sc->sc_last_chan) { return; } sc->sc_last_chan = chan; /* select the appropriate RF settings based on what EEPROM says */ rfprog = ((sc->sc_rf_rev == RT2573_RF_5225) || (sc->sc_rf_rev == RT2573_RF_2527)) ? rum_rf5225 : rum_rf5226; /* find the settings for this channel */ for (i = 0;; i++) { if (i == (N_RF5225 - 1)) break; if (rfprog[i].chan == chan) break; } DPRINTF("chan=%d, i=%d\n", chan, i); power = sc->sc_txpow[i]; if (power < 0) { bbp94 += power; power = 0; } else if (power > 31) { bbp94 += power - 31; power = 31; } /* * If we are switching from the 2GHz band to the 5GHz band or * vice-versa, BBP registers need to be reprogrammed. */ rum_cfg_select_band(sc, cc, 0); rum_cfg_select_antenna(sc, cc, 0); rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7)); rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7) | 1); rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7)); rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10)); if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } /* enable smart mode for MIMO-capable RFs */ bbp3 = rum_cfg_bbp_read(sc, 3); if ((sc->sc_rf_rev == RT2573_RF_5225) || (sc->sc_rf_rev == RT2573_RF_2527)) bbp3 &= ~RT2573_SMART_MODE; else bbp3 |= RT2573_SMART_MODE; rum_cfg_bbp_write(sc, 3, bbp3); rum_cfg_bbp_write(sc, 94, bbp94); /* update basic rate set */ if (cc->ic_curchan.chan_is_b) { /* 11b basic rates: 1, 2Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3); } else if (cc->ic_curchan.chan_is_a) { /* 11a basic rates: 6, 12, 24Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150); } else { /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf); } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } } static void rum_cfg_set_run(struct rum_softc *sc, struct usb2_config_td_cc *cc) { if (cc->ic_opmode != IEEE80211_M_MONITOR) { rum_cfg_update_slot(sc, cc, 0); rum_cfg_enable_mrr(sc, cc, 0); rum_cfg_set_txpreamble(sc, cc, 0); /* update basic rate set */ if (cc->ic_bsschan.chan_is_5ghz) { /* 11a basic rates: 6, 12, 24Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150); } else if (cc->ic_bsschan.chan_is_g) { /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf); } else { /* 11b basic rates: 1, 2Mbps */ rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3); } rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || (cc->ic_opmode == IEEE80211_M_IBSS)) { rum_cfg_prepare_beacon(sc, cc, 0); } if (cc->ic_opmode != IEEE80211_M_MONITOR) { rum_cfg_enable_tsf_sync(sc, cc, 0); } if (cc->iv_bss.fixed_rate_none) { /* enable automatic rate adaptation */ rum_cfg_amrr_start(sc); } } static void rum_cfg_enable_tsf_sync(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; if (cc->ic_opmode != IEEE80211_M_STA) { /* * Change default 16ms TBTT adjustment to 8ms. * Must be done before enabling beacon generation. */ rum_cfg_write(sc, RT2573_TXRX_CSR10, (1 << 12) | 8); } tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9) & 0xff000000; /* set beacon interval (in 1/16ms unit) */ tmp |= cc->iv_bss.ni_intval * 16; tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; if (cc->ic_opmode == IEEE80211_M_STA) tmp |= RT2573_TSF_MODE(1); else tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp); } static void rum_cfg_disable_tsf_sync(struct rum_softc *sc) { uint32_t tmp; /* abort TSF synchronization */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9); rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); } /* * Enable multi-rate retries for frames sent at OFDM rates. * In 802.11b/g mode, allow fallback to CCK rates. */ static void rum_cfg_enable_mrr(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4); if (cc->ic_curchan.chan_is_5ghz) tmp &= ~RT2573_MRR_CCK_FALLBACK; else tmp |= RT2573_MRR_CCK_FALLBACK; tmp |= RT2573_MRR_ENABLED; rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp); } static void rum_cfg_update_slot(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; uint8_t slottime; slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; tmp = rum_cfg_read(sc, RT2573_MAC_CSR9); tmp = (tmp & ~0xff) | slottime; rum_cfg_write(sc, RT2573_MAC_CSR9, tmp); DPRINTF("setting slot time to %u us\n", slottime); } static void rum_cfg_set_txpreamble(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4); if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RT2573_SHORT_PREAMBLE; else tmp &= ~RT2573_SHORT_PREAMBLE; rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp); } static void rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid) { uint32_t tmp; tmp = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24); rum_cfg_write(sc, RT2573_MAC_CSR4, tmp); tmp = (bssid[4]) | (bssid[5] << 8) | (RT2573_ONE_BSSID << 16); rum_cfg_write(sc, RT2573_MAC_CSR5, tmp); } static void rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr) { uint32_t tmp; tmp = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24); rum_cfg_write(sc, RT2573_MAC_CSR2, tmp); tmp = addr[4] | (addr[5] << 8) | (0xff << 16); rum_cfg_write(sc, RT2573_MAC_CSR3, tmp); } static void rum_cfg_update_promisc(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); if (cc->if_flags & IFF_PROMISC) tmp &= ~RT2573_DROP_NOT_TO_ME; else tmp |= RT2573_DROP_NOT_TO_ME; rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); DPRINTF("%s promiscuous mode\n", (cc->if_flags & IFF_PROMISC) ? "entering" : "leaving"); } static void rum_cfg_select_antenna(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; uint8_t bbp3; uint8_t bbp4; uint8_t bbp77; uint8_t rx_ant; uint8_t is_5ghz; bbp3 = rum_cfg_bbp_read(sc, 3); bbp4 = rum_cfg_bbp_read(sc, 4); bbp77 = rum_cfg_bbp_read(sc, 77); bbp3 &= ~0x01; bbp4 &= ~0x23; rx_ant = sc->sc_rx_ant; is_5ghz = cc->ic_curchan.chan_is_5ghz; switch (sc->sc_rf_rev) { case RT2573_RF_5226: case RT2573_RF_5225: if (rx_ant == 0) { /* Diversity */ bbp4 |= 0x02; if (is_5ghz == 0) bbp4 |= 0x20; } else if (rx_ant == 1) { /* RX: Antenna A */ bbp4 |= 0x01; if (is_5ghz) bbp77 &= ~0x03; else bbp77 |= 0x03; } else if (rx_ant == 2) { /* RX: Antenna B */ bbp4 |= 0x01; if (is_5ghz) bbp77 |= 0x03; else bbp77 &= ~0x03; } break; case RT2573_RF_2528: case RT2573_RF_2527: if (rx_ant == 0) { /* Diversity */ bbp4 |= 0x22; } else if (rx_ant == 1) { /* RX: Antenna A */ bbp4 |= 0x21; bbp77 |= 0x03; } else if (rx_ant == 2) { /* RX: Antenna B */ bbp4 |= 0x21; bbp77 &= ~0x03; } break; default: break; } bbp4 &= ~(sc->sc_ftype << 5); /* make sure Rx is disabled before switching antenna */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); rum_cfg_bbp_write(sc, 3, bbp3); rum_cfg_bbp_write(sc, 4, bbp4); rum_cfg_bbp_write(sc, 77, bbp77); rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); } static void rum_cfg_read_eeprom(struct rum_softc *sc) { uint16_t val; /* read MAC address */ rum_cfg_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_myaddr, 6); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_ANTENNA); sc->sc_rf_rev = (val >> 11) & 0x1f; sc->sc_hw_radio = (val >> 10) & 0x1; sc->sc_ftype = (val >> 6) & 0x1; sc->sc_rx_ant = (val >> 4) & 0x3; sc->sc_tx_ant = (val >> 2) & 0x3; sc->sc_nb_ant = (val & 0x3); DPRINTF("RF revision=%d\n", sc->sc_rf_rev); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_CONFIG2); sc->sc_ext_5ghz_lna = (val >> 6) & 0x1; sc->sc_ext_2ghz_lna = (val >> 4) & 0x1; DPRINTF("External 2GHz LNA=%d, External 5GHz LNA=%d\n", sc->sc_ext_2ghz_lna, sc->sc_ext_5ghz_lna); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET); if ((val & 0xff) != 0xff) sc->sc_rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ else sc->sc_rssi_2ghz_corr = 0; /* range check */ if ((sc->sc_rssi_2ghz_corr < -10) || (sc->sc_rssi_2ghz_corr > 10)) { sc->sc_rssi_2ghz_corr = 0; } val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET); if ((val & 0xff) != 0xff) sc->sc_rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ else sc->sc_rssi_5ghz_corr = 0; /* range check */ if ((sc->sc_rssi_5ghz_corr < -10) || (sc->sc_rssi_5ghz_corr > 10)) { sc->sc_rssi_5ghz_corr = 0; } if (sc->sc_ext_2ghz_lna) { sc->sc_rssi_2ghz_corr -= 14; } if (sc->sc_ext_5ghz_lna) { sc->sc_rssi_5ghz_corr -= 14; } DPRINTF("RSSI 2GHz corr=%d, RSSI 5GHz corr=%d\n", sc->sc_rssi_2ghz_corr, sc->sc_rssi_5ghz_corr); val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_FREQ_OFFSET); if ((val & 0xff) != 0xff) sc->sc_rffreq = (val & 0xff); else sc->sc_rffreq = 0; DPRINTF("RF freq=%d\n", sc->sc_rffreq); /* read Tx power for all a/b/g channels */ rum_cfg_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->sc_txpow, 14); /* XXX default Tx power for 802.11a channels */ memset(sc->sc_txpow + 14, 24, sizeof(sc->sc_txpow) - 14); /* read default values for BBP registers */ rum_cfg_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->sc_bbp_prom, 2 * 16); } static uint8_t rum_cfg_bbp_init(struct rum_softc *sc) { enum { N_DEF_BBP = (sizeof(rum_def_bbp) / sizeof(rum_def_bbp[0])), }; uint16_t i; uint8_t to; uint8_t tmp; /* wait for BBP to become ready */ for (to = 0;; to++) { if (to < 100) { tmp = rum_cfg_bbp_read(sc, 0); if ((tmp != 0x00) && (tmp != 0xff)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return (1); /* failure */ } } else { DPRINTF("timeout waiting for BBP\n"); return (1); /* failure */ } } /* initialize BBP registers to default values */ for (i = 0; i < N_DEF_BBP; i++) { rum_cfg_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); } /* write vendor-specific BBP values (from EEPROM) */ for (i = 0; i < 16; i++) { if ((sc->sc_bbp_prom[i].reg == 0) || (sc->sc_bbp_prom[i].reg == 0xff)) { continue; } rum_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val); } return (0); } static void rum_cfg_pre_init(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* immediate configuration */ rum_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= RUM_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); } static void rum_cfg_init(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_DEF_MAC = (sizeof(rum_def_mac) / sizeof(rum_def_mac[0])), }; uint32_t tmp; uint16_t i; uint8_t to; /* delayed configuration */ rum_cfg_stop(sc, cc, 0); /* initialize MAC registers to default values */ for (i = 0; i < N_DEF_MAC; i++) { rum_cfg_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); } /* set host ready */ rum_cfg_write(sc, RT2573_MAC_CSR1, 3); rum_cfg_write(sc, RT2573_MAC_CSR1, 0); /* wait for BBP/RF to wakeup */ for (to = 0;; to++) { if (to < 100) { if (rum_cfg_read(sc, RT2573_MAC_CSR12) & 8) { break; } rum_cfg_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { goto fail; } } else { DPRINTF("timeout waiting for " "BBP/RF to wakeup\n"); goto fail; } } if (rum_cfg_bbp_init(sc)) { goto fail; } /* select default channel */ sc->sc_last_chan = 0; rum_cfg_set_chan(sc, cc, 0); /* clear STA registers */ rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); /* set MAC address */ rum_cfg_set_macaddr(sc, cc->ic_myaddr); /* initialize ASIC */ rum_cfg_write(sc, RT2573_MAC_CSR1, 4); /* * make sure that the first transaction * clears the stall: */ sc->sc_flags |= (RUM_FLAG_READ_STALL | RUM_FLAG_WRITE_STALL | RUM_FLAG_LL_READY); if ((sc->sc_flags & RUM_FLAG_LL_READY) && (sc->sc_flags & RUM_FLAG_HL_READY)) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); /* * start IEEE802.11 layer */ mtx_unlock(&sc->sc_mtx); ieee80211_start_all(ic); mtx_lock(&sc->sc_mtx); } /* update Rx filter */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0) & 0xffff; tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; if (cc->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | RT2573_DROP_ACKCTS; if (cc->ic_opmode != IEEE80211_M_HOSTAP) { tmp |= RT2573_DROP_TODS; } if (!(cc->if_flags & IFF_PROMISC)) { tmp |= RT2573_DROP_NOT_TO_ME; } } rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp); return; fail: rum_cfg_pre_stop(sc, NULL, 0); if (cc) { rum_cfg_stop(sc, cc, 0); } } static void rum_cfg_pre_stop(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ rum_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(RUM_FLAG_HL_READY | RUM_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); /* clean up transmission */ rum_tx_clean_queue(sc); } static void rum_cfg_stop(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t tmp; /* disable Rx */ tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0); rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); /* reset ASIC */ rum_cfg_write(sc, RT2573_MAC_CSR1, 3); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); rum_cfg_write(sc, RT2573_MAC_CSR1, 0); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); } static void rum_cfg_amrr_start(struct rum_softc *sc) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = rum_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } /* init AMRR */ ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); /* enable AMRR timer */ sc->sc_amrr_timer = 1; } static void rum_cfg_amrr_timeout(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ieee80211_node *ni; uint32_t ok; uint32_t fail; /* clear statistic registers (STA_CSR0 to STA_CSR5) */ rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); vap = rum_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } if ((sc->sc_flags & RUM_FLAG_LL_READY) && (sc->sc_flags & RUM_FLAG_HL_READY)) { ok = (le32toh(sc->sc_sta[4]) >> 16) + /* TX ok w/o retry */ (le32toh(sc->sc_sta[5]) & 0xffff); /* TX ok w/ retry */ fail = (le32toh(sc->sc_sta[5]) >> 16); /* TX retry-fail count */ if (sc->sc_amrr_timer) { ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn, ok + fail, ok, (le32toh(sc->sc_sta[5]) & 0xffff) + fail); if (ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn)) { /* ignore */ } } ifp->if_oerrors += fail;/* count TX retry-fail as Tx errors */ } } static void rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size) { struct usb2_device_request req; uint16_t reg = RT2573_MCU_CODE_BASE; /* copy firmware image into NIC */ while (size >= 4) { rum_cfg_write(sc, reg, UGETDW(ucode)); reg += 4; ucode += 4; size -= 4; } if (size != 0) { DPRINTF("possibly invalid firmware\n"); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_MCU_CNTL; USETW(req.wValue, RT2573_MCU_RUN); USETW(req.wIndex, 0); USETW(req.wLength, 0); rum_cfg_do_request(sc, &req, NULL); } static void rum_cfg_prepare_beacon(struct rum_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211_node *ni; struct ieee80211vap *vap; struct ieee80211com *ic; const struct ieee80211_txparam *tp; struct mbuf *m; vap = rum_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } ic = vap->iv_ic; if (ic == NULL) { return; } DPRINTFN(11, "Sending beacon frame.\n"); m = ieee80211_beacon_alloc(ni, &RUM_VAP(vap)->bo); if (m == NULL) { DPRINTFN(0, "could not allocate beacon\n"); return; } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); rum_setup_desc_and_tx(sc, m, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ | RT2573_TX_BEACON, tp->mgmtrate); } static uint8_t rum_get_rssi(struct rum_softc *sc, uint8_t raw) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int16_t rssi; uint8_t lna; uint8_t agc; lna = (raw >> 5) & 0x3; agc = raw & 0x1f; if (lna == 0) { /* * No RSSI mapping * * NB: Since RSSI is relative to noise floor, -1 is * adequate for caller to know error happened. */ return (0); } rssi = (2 * agc) - RT2573_NOISE_FLOOR; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { rssi += sc->sc_rssi_2ghz_corr; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 74; else if (lna == 3) rssi -= 90; } else { rssi += sc->sc_rssi_5ghz_corr; if ((!sc->sc_ext_5ghz_lna) && (lna != 1)) rssi += 4; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 86; else if (lna == 3) rssi -= 100; } /* range check */ if (rssi < 0) rssi = 0; else if (rssi > 255) rssi = 255; return (rssi); } static struct ieee80211vap * rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct rum_vap *rvp; struct ieee80211vap *vap; struct rum_softc *sc = ic->ic_ifp->if_softc; DPRINTF("\n"); /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* config thread is gone */ return (NULL); } mtx_unlock(&sc->sc_mtx); if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; rvp = (struct rum_vap *)malloc(sizeof(struct rum_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (rvp == NULL) return NULL; vap = &rvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = &rum_newstate_cb; ieee80211_amrr_init(&rvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */ ); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); /* store current operation mode */ ic->ic_opmode = opmode; return (vap); } static void rum_vap_delete(struct ieee80211vap *vap) { struct rum_vap *rvp = RUM_VAP(vap); struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc; DPRINTF("\n"); /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { /* ignore */ } mtx_unlock(&sc->sc_mtx); ieee80211_amrr_cleanup(&rvp->amrr); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } /* ARGUSED */ static struct ieee80211_node * rum_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct rum_node *rn; rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ((rn != NULL) ? &rn->ni : NULL); } static void rum_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); } static void rum_fill_write_queue(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; /* * We only fill up half of the queue with data frames. The rest is * reserved for other kinds of frames. */ while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (void *)(m->m_pkthdr.rcvif); m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } rum_tx_data(sc, m, ni); } } static void rum_tx_clean_queue(struct rum_softc *sc) { struct mbuf *m; for (;;) { _IF_DEQUEUE(&sc->sc_tx_queue, m); if (!m) { break; } rum_tx_freem(m); } } static void rum_tx_freem(struct mbuf *m) { struct ieee80211_node *ni; while (m) { ni = (void *)(m->m_pkthdr.rcvif); if (!ni) { m = m_free(m); continue; } if (m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); } m_freem(m); ieee80211_free_node(ni); break; } } static void rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } wh = mtod(m, struct ieee80211_frame *); } flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2573_TX_NEED_ACK; dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) flags |= RT2573_TX_TIMESTAMP; } m->m_pkthdr.rcvif = (void *)ni; rum_setup_desc_and_tx(sc, m, flags, 0, tp->mgmtrate); } static struct ieee80211vap * rum_get_vap(struct rum_softc *sc) { struct ifnet *ifp; struct ieee80211com *ic; if (sc == NULL) { return NULL; } ifp = sc->sc_ifp; if (ifp == NULL) { return NULL; } ic = ifp->if_l2com; if (ic == NULL) { return NULL; } return TAILQ_FIRST(&ic->ic_vaps); } static void rum_tx_data(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; uint16_t rate; DPRINTFN(11, "Sending data.\n"); wh = mtod(m, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } /* packet header may have moved, reset our local pointer */ wh = mtod(m, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint8_t prot = IEEE80211_PROT_NONE; if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { rum_tx_prot(sc, m, ni, prot, rate); flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } flags |= RT2573_TX_NEED_ACK; flags |= RT2573_TX_MORE_FRAG; dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } m->m_pkthdr.rcvif = (void *)ni; rum_setup_desc_and_tx(sc, m, flags, 0, rate); } static void rum_tx_prot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct mbuf *mprot; uint32_t flags; uint16_t protrate; uint16_t ackrate; uint16_t pktlen; uint16_t dur; uint8_t isshort; KASSERT((prot == IEEE80211_PROT_RTSCTS) || (prot == IEEE80211_PROT_CTSONLY), ("protection %u", prot)); DPRINTFN(11, "Sending protection frame.\n"); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(sc->sc_rates, rate); ackrate = ieee80211_ack_rate(sc->sc_rates, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); +ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags = RT2573_TX_MORE_FRAG; if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags |= RT2573_TX_NEED_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { return; } mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); rum_setup_desc_and_tx(sc, mprot, flags, 0, protrate); } static void rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { uint32_t flags; uint16_t rate; DPRINTFN(11, "Sending raw frame.\n"); rate = params->ibp_rate0 & IEEE80211_RATE_VAL; /* XXX validate */ if (rate == 0) { m_freem(m); ieee80211_free_node(ni); return; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RT2573_TX_NEED_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { rum_tx_prot(sc, m, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } m->m_pkthdr.rcvif = (void *)ni; rum_setup_desc_and_tx(sc, m, flags, 0, rate); } static int rum_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ rum_tx_mgt(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ rum_tx_raw(sc, m, ni, params); } mtx_unlock(&sc->sc_mtx); return (0); } static void rum_update_mcast_cb(struct ifnet *ifp) { /* not supported */ } static void rum_update_promisc_cb(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &rum_config_copy, &rum_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); } Index: projects/cambria/sys/dev/usb2/wlan/if_ural2.c =================================================================== --- projects/cambria/sys/dev/usb2/wlan/if_ural2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/wlan/if_ural2.c (revision 186460) @@ -1,2739 +1,2734 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Copyright (c) 2006, 2008 * Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ /* * * NOTE: all function names beginning like "ural_cfg_" can only * be called from within the config thread function ! */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2500USB chipset driver * http://www.ralinktech.com/ */ #include #include #include #include #define usb2_config_td_cc ural_config_copy #define usb2_config_td_softc ural_softc #define USB_DEBUG_VAR ural_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int ural_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); SYSCTL_INT(_hw_usb2_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0, "Debug level"); #endif #define URAL_RSSI(rssi) \ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) /* prototypes */ static device_probe_t ural_probe; static device_attach_t ural_attach; static device_detach_t ural_detach; static usb2_callback_t ural_bulk_read_callback; static usb2_callback_t ural_bulk_read_clear_stall_callback; static usb2_callback_t ural_bulk_write_callback; static usb2_callback_t ural_bulk_write_clear_stall_callback; static usb2_config_td_command_t ural_cfg_first_time_setup; static usb2_config_td_command_t ural_config_copy; static usb2_config_td_command_t ural_cfg_scan_start; static usb2_config_td_command_t ural_cfg_scan_end; static usb2_config_td_command_t ural_cfg_set_chan; static usb2_config_td_command_t ural_cfg_enable_tsf_sync; static usb2_config_td_command_t ural_cfg_update_slot; static usb2_config_td_command_t ural_cfg_set_txpreamble; static usb2_config_td_command_t ural_cfg_update_promisc; static usb2_config_td_command_t ural_cfg_pre_init; static usb2_config_td_command_t ural_cfg_init; static usb2_config_td_command_t ural_cfg_pre_stop; static usb2_config_td_command_t ural_cfg_stop; static usb2_config_td_command_t ural_cfg_amrr_timeout; static usb2_config_td_command_t ural_cfg_newstate; static void ural_cfg_do_request(struct ural_softc *, struct usb2_device_request *, void *); static void ural_cfg_set_testmode(struct ural_softc *); static void ural_cfg_eeprom_read(struct ural_softc *, uint16_t, void *, uint16_t); static uint16_t ural_cfg_read(struct ural_softc *, uint16_t); static void ural_cfg_read_multi(struct ural_softc *, uint16_t, void *, uint16_t); static void ural_cfg_write(struct ural_softc *, uint16_t, uint16_t); static void ural_cfg_write_multi(struct ural_softc *, uint16_t, void *, uint16_t); static void ural_cfg_bbp_write(struct ural_softc *, uint8_t, uint8_t); static uint8_t ural_cfg_bbp_read(struct ural_softc *, uint8_t); static void ural_cfg_rf_write(struct ural_softc *, uint8_t, uint32_t); static void ural_end_of_commands(struct ural_softc *); static const char *ural_get_rf(int rev); static void ural_watchdog(void *); static void ural_init_cb(void *); static int ural_ioctl_cb(struct ifnet *, u_long cmd, caddr_t data); static void ural_start_cb(struct ifnet *); static int ural_newstate_cb(struct ieee80211vap *, enum ieee80211_state, int); static void ural_std_command(struct ieee80211com *, usb2_config_td_command_t *); static void ural_scan_start_cb(struct ieee80211com *); static void ural_scan_end_cb(struct ieee80211com *); static void ural_set_channel_cb(struct ieee80211com *); static void ural_cfg_disable_rf_tune(struct ural_softc *); static void ural_cfg_set_bssid(struct ural_softc *, uint8_t *); static void ural_cfg_set_macaddr(struct ural_softc *, uint8_t *); static void ural_cfg_set_txantenna(struct ural_softc *, uint8_t); static void ural_cfg_set_rxantenna(struct ural_softc *, uint8_t); static void ural_cfg_read_eeprom(struct ural_softc *); static uint8_t ural_cfg_bbp_init(struct ural_softc *); static void ural_cfg_amrr_start(struct ural_softc *); static struct ieee80211vap *ural_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_vap_delete(struct ieee80211vap *); static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_newassoc(struct ieee80211_node *, int); static void ural_cfg_disable_tsf_sync(struct ural_softc *); static void ural_cfg_set_run(struct ural_softc *, struct usb2_config_td_cc *); static void ural_fill_write_queue(struct ural_softc *); static void ural_tx_clean_queue(struct ural_softc *); static void ural_tx_freem(struct mbuf *); static void ural_tx_mgt(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static struct ieee80211vap *ural_get_vap(struct ural_softc *); static void ural_tx_bcn(struct ural_softc *); static void ural_tx_data(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static void ural_tx_prot(struct ural_softc *, const struct mbuf *, struct ieee80211_node *, uint8_t, uint16_t); static void ural_tx_raw(struct ural_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int ural_raw_xmit_cb(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void ural_setup_desc_and_tx(struct ural_softc *, struct mbuf *, uint32_t, uint16_t); static void ural_update_mcast_cb(struct ifnet *); static void ural_update_promisc_cb(struct ifnet *); /* various supported device vendors/products */ static const struct usb2_device_id ural_devs[] = { {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G, 0)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050, 0)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G, 0)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP, 0)}, {USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU, 0)}, {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G, 0)}, {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG, 0)}, {USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB, 0)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2, 0)}, {USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3, 0)}, {USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, {USB_VPI(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G, 0)}, {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG, 0)}, {USB_VPI(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R, 0)}, {USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570, 0)}, {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570, 0)}, {USB_VPI(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570, 0)}, }; /* * Default values for MAC registers; values taken from * the reference driver: */ struct ural_def_mac { uint16_t reg; uint16_t val; }; static const struct ural_def_mac ural_def_mac[] = { {RAL_TXRX_CSR5, 0x8c8d}, {RAL_TXRX_CSR6, 0x8b8a}, {RAL_TXRX_CSR7, 0x8687}, {RAL_TXRX_CSR8, 0x0085}, {RAL_MAC_CSR13, 0x1111}, {RAL_MAC_CSR14, 0x1e11}, {RAL_TXRX_CSR21, 0xe78f}, {RAL_MAC_CSR9, 0xff1d}, {RAL_MAC_CSR11, 0x0002}, {RAL_MAC_CSR22, 0x0053}, {RAL_MAC_CSR15, 0x0000}, {RAL_MAC_CSR8, RAL_FRAME_SIZE}, {RAL_TXRX_CSR19, 0x0000}, {RAL_TXRX_CSR18, 0x005a}, {RAL_PHY_CSR2, 0x0000}, {RAL_TXRX_CSR0, 0x1ec0}, {RAL_PHY_CSR4, 0x000f} }; /* * Default values for BBP registers; values taken from the reference driver. */ struct ural_def_bbp { uint8_t reg; uint8_t val; }; static const struct ural_def_bbp ural_def_bbp[] = { {3, 0x02}, {4, 0x19}, {14, 0x1c}, {15, 0x30}, {16, 0xac}, {17, 0x48}, {18, 0x18}, {19, 0xff}, {20, 0x1e}, {21, 0x08}, {22, 0x08}, {23, 0x08}, {24, 0x80}, {25, 0x50}, {26, 0x08}, {27, 0x23}, {30, 0x10}, {31, 0x2b}, {32, 0xb9}, {34, 0x12}, {35, 0x50}, {39, 0xc4}, {40, 0x02}, {41, 0x60}, {53, 0x10}, {54, 0x18}, {56, 0x08}, {57, 0x10}, {58, 0x08}, {61, 0x60}, {62, 0x10}, {75, 0xff} }; /* * Default values for RF register R2 indexed by channel numbers. */ static const uint32_t ural_rf2522_r2[] = { 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e }; static const uint32_t ural_rf2523_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2524_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2525_r2[] = { 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 }; static const uint32_t ural_rf2525_hi_r2[] = { 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e }; static const uint32_t ural_rf2525e_r2[] = { 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b }; static const uint32_t ural_rf2526_hi_r2[] = { 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 }; static const uint32_t ural_rf2526_r2[] = { 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d }; /* * For dual-band RF, RF registers R1 and R4 also depend on channel number; * values taken from the reference driver. */ struct ural_rf5222 { uint8_t chan; uint32_t r1; uint32_t r2; uint32_t r4; }; static const struct ural_rf5222 ural_rf5222[] = { {1, 0x08808, 0x0044d, 0x00282}, {2, 0x08808, 0x0044e, 0x00282}, {3, 0x08808, 0x0044f, 0x00282}, {4, 0x08808, 0x00460, 0x00282}, {5, 0x08808, 0x00461, 0x00282}, {6, 0x08808, 0x00462, 0x00282}, {7, 0x08808, 0x00463, 0x00282}, {8, 0x08808, 0x00464, 0x00282}, {9, 0x08808, 0x00465, 0x00282}, {10, 0x08808, 0x00466, 0x00282}, {11, 0x08808, 0x00467, 0x00282}, {12, 0x08808, 0x00468, 0x00282}, {13, 0x08808, 0x00469, 0x00282}, {14, 0x08808, 0x0046b, 0x00286}, {36, 0x08804, 0x06225, 0x00287}, {40, 0x08804, 0x06226, 0x00287}, {44, 0x08804, 0x06227, 0x00287}, {48, 0x08804, 0x06228, 0x00287}, {52, 0x08804, 0x06229, 0x00287}, {56, 0x08804, 0x0622a, 0x00287}, {60, 0x08804, 0x0622b, 0x00287}, {64, 0x08804, 0x0622c, 0x00287}, {100, 0x08804, 0x02200, 0x00283}, {104, 0x08804, 0x02201, 0x00283}, {108, 0x08804, 0x02202, 0x00283}, {112, 0x08804, 0x02203, 0x00283}, {116, 0x08804, 0x02204, 0x00283}, {120, 0x08804, 0x02205, 0x00283}, {124, 0x08804, 0x02206, 0x00283}, {128, 0x08804, 0x02207, 0x00283}, {132, 0x08804, 0x02208, 0x00283}, {136, 0x08804, 0x02209, 0x00283}, {140, 0x08804, 0x0220a, 0x00283}, {149, 0x08808, 0x02429, 0x00281}, {153, 0x08808, 0x0242b, 0x00281}, {157, 0x08808, 0x0242d, 0x00281}, {161, 0x08808, 0x0242f, 0x00281} }; static const struct usb2_config ural_config[URAL_N_TRANSFER] = { [0] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &ural_bulk_write_callback, .mh.timeout = 5000, /* ms */ }, [1] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &ural_bulk_read_callback, }, [2] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ural_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [3] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.callback = &ural_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t ural_devclass; static device_method_t ural_methods[] = { DEVMETHOD(device_probe, ural_probe), DEVMETHOD(device_attach, ural_attach), DEVMETHOD(device_detach, ural_detach), {0, 0} }; static driver_t ural_driver = { .name = "ural", .methods = ural_methods, .size = sizeof(struct ural_softc), }; DRIVER_MODULE(ural, ushub, ural_driver, ural_devclass, NULL, 0); MODULE_DEPEND(ural, usb2_wlan, 1, 1, 1); MODULE_DEPEND(ural, usb2_core, 1, 1, 1); MODULE_DEPEND(ural, wlan, 1, 1, 1); MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1); static int ural_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); } static int ural_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct ural_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } device_set_usb2_desc(dev); mtx_init(&sc->sc_mtx, "ural lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_udev = uaa->device; sc->sc_unit = device_get_unit(dev); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); iface_index = RAL_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ural_config, URAL_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB transfers, " "err=%s\n", usb2_errstr(error)); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, &ural_end_of_commands, sizeof(struct usb2_config_td_cc), 24); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &ural_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - ural_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: ural_detach(dev); return (ENXIO); /* failure */ } static int ural_detach(device_t dev) { struct ural_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); ural_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; ic = ifp->if_l2com; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } /*========================================================================* * REGISTER READ / WRITE WRAPPER ROUTINES *========================================================================*/ static void ural_cfg_do_request(struct ural_softc *sc, struct usb2_device_request *req, void *data) { uint16_t length; usb2_error_t err; repeat: if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTF("device request failed, err=%s " "(ignored)\n", usb2_errstr(err)); /* wait a little before next try */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) { goto error; } /* try until we are detached */ goto repeat; error: /* the device has been detached */ length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } static void ural_cfg_set_testmode(struct ural_softc *sc) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_VENDOR_REQUEST; USETW(req.wValue, 4); USETW(req.wIndex, 1); USETW(req.wLength, 0); ural_cfg_do_request(sc, &req, NULL); } static void ural_cfg_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); } static uint16_t ural_cfg_read(struct ural_softc *sc, uint16_t reg) { struct usb2_device_request req; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, sizeof(val)); ural_cfg_do_request(sc, &req, &val); return (le16toh(val)); } static void ural_cfg_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); } static void ural_cfg_write(struct ural_softc *sc, uint16_t reg, uint16_t val) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MAC; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); ural_cfg_do_request(sc, &req, NULL); } static void ural_cfg_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, uint16_t len) { struct usb2_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); ural_cfg_do_request(sc, &req, buf); } static uint8_t ural_cfg_bbp_disbusy(struct ural_softc *sc) { uint16_t tmp; uint8_t to; for (to = 0;; to++) { if (to < 100) { tmp = ural_cfg_read(sc, RAL_PHY_CSR8); tmp &= RAL_BBP_BUSY; if (tmp == 0) { return (0); } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { break; } } else { break; } } DPRINTF("could not disbusy BBP\n"); return (1); /* failure */ } static void ural_cfg_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) { uint16_t tmp; if (ural_cfg_bbp_disbusy(sc)) { return; } tmp = (reg << 8) | val; ural_cfg_write(sc, RAL_PHY_CSR7, tmp); } static uint8_t ural_cfg_bbp_read(struct ural_softc *sc, uint8_t reg) { uint16_t val; if (ural_cfg_bbp_disbusy(sc)) { return (0); } val = RAL_BBP_WRITE | (reg << 8); ural_cfg_write(sc, RAL_PHY_CSR7, val); if (ural_cfg_bbp_disbusy(sc)) { return (0); } return (ural_cfg_read(sc, RAL_PHY_CSR7) & 0xff); } static void ural_cfg_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; uint8_t to; reg &= 3; /* remember last written value */ sc->sc_rf_regs[reg] = val; for (to = 0;; to++) { if (to < 100) { tmp = ural_cfg_read(sc, RAL_PHY_CSR10); if (!(tmp & RAL_RF_LOBUSY)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } } else { DPRINTF("could not write to RF\n"); return; } } tmp = RAL_RF_BUSY | RAL_RF_20BIT | ((val & 0xfffff) << 2) | reg; ural_cfg_write(sc, RAL_PHY_CSR9, tmp & 0xffff); ural_cfg_write(sc, RAL_PHY_CSR10, tmp >> 16); DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff); } static void ural_cfg_first_time_setup(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211com *ic; struct ifnet *ifp; uint8_t bands; /* setup RX tap header */ sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT); /* setup TX tap header */ sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT); /* retrieve RT2570 rev. no */ sc->sc_asic_rev = ural_cfg_read(sc, RAL_MAC_CSR0); /* retrieve MAC address and various other things from EEPROM */ ural_cfg_read_eeprom(sc); printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n", sc->sc_name, sc->sc_asic_rev, ural_get_rf(sc->sc_rf_rev)); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_IEEE80211); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { DPRINTFN(0, "could not if_alloc()!\n"); goto done; } sc->sc_evilhack = ifp; sc->sc_ifp = ifp; ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "ural", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &ural_init_cb; ifp->if_ioctl = &ural_ioctl_cb; ifp->if_start = &ural_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->sc_rf_rev == RAL_RF_5222) { setbit(&bands, IEEE80211_MODE_11A); } ieee80211_init_channels(ic, NULL, &bands); mtx_unlock(&sc->sc_mtx); ieee80211_ifattach(ic); mtx_lock(&sc->sc_mtx); ic->ic_newassoc = &ural_newassoc; ic->ic_raw_xmit = &ural_raw_xmit_cb; ic->ic_node_alloc = &ural_node_alloc; ic->ic_update_mcast = &ural_update_mcast_cb; ic->ic_update_promisc = &ural_update_promisc_cb; ic->ic_scan_start = &ural_scan_start_cb; ic->ic_scan_end = &ural_scan_end_cb; ic->ic_set_channel = &ural_set_channel_cb; ic->ic_vap_create = &ural_vap_create; ic->ic_vap_delete = &ural_vap_delete; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); mtx_unlock(&sc->sc_mtx); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); if (bootverbose) { ieee80211_announce(ic); } mtx_lock(&sc->sc_mtx); done: return; } static void ural_end_of_commands(struct ural_softc *sc) { sc->sc_flags &= ~URAL_FLAG_WAIT_COMMAND; /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); } static void ural_config_copy_chan(struct ural_config_copy_chan *cc, struct ieee80211com *ic, struct ieee80211_channel *c) { if (!c) return; cc->chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->chan_to_mode = ieee80211_chan2mode(c); if (IEEE80211_IS_CHAN_B(c)) cc->chan_is_b = 1; if (IEEE80211_IS_CHAN_A(c)) cc->chan_is_a = 1; if (IEEE80211_IS_CHAN_2GHZ(c)) cc->chan_is_2ghz = 1; if (IEEE80211_IS_CHAN_5GHZ(c)) cc->chan_is_5ghz = 1; if (IEEE80211_IS_CHAN_ANYG(c)) cc->chan_is_g = 1; } } static void ural_config_copy(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp; struct ieee80211com *ic; struct ieee80211_node *ni; struct ieee80211vap *vap; const struct ieee80211_txparam *tp; bzero(cc, sizeof(*cc)); ifp = sc->sc_ifp; if (ifp) { cc->if_flags = ifp->if_flags; bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, sizeof(cc->if_broadcastaddr)); ic = ifp->if_l2com; if (ic) { ural_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); ural_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); vap = TAILQ_FIRST(&ic->ic_vaps); if (vap) { ni = vap->iv_bss; if (ni) { cc->iv_bss.ni_intval = ni->ni_intval; bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, sizeof(cc->iv_bss.ni_bssid)); } tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { cc->iv_bss.fixed_rate_none = 1; } } cc->ic_opmode = ic->ic_opmode; cc->ic_flags = ic->ic_flags; cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); } } sc->sc_flags |= URAL_FLAG_WAIT_COMMAND; } static const char * ural_get_rf(int rev) { switch (rev) { case RAL_RF_2522:return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: return "RT2524"; case RAL_RF_2525: return "RT2525"; case RAL_RF_2525E: return "RT2525e"; case RAL_RF_2526: return "RT2526"; case RAL_RF_5222: return "RT5222"; default: return "unknown"; } } /*------------------------------------------------------------------------* * ural_bulk_read_callback - data read "thread" *------------------------------------------------------------------------*/ static void ural_bulk_read_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct mbuf *m = NULL; uint32_t flags; uint32_t max_len; uint32_t real_len; uint8_t rssi = 0; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); if (xfer->actlen < RAL_RX_DESC_SIZE) { DPRINTF("too short transfer, " "%d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } max_len = (xfer->actlen - RAL_RX_DESC_SIZE); usb2_copy_out(xfer->frbuffers, max_len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE); flags = le32toh(sc->sc_rx_desc.flags); if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTFN(6, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } if (max_len > MCLBYTES) { max_len = MCLBYTES; } real_len = (flags >> 16) & 0xfff; if (real_len > max_len) { DPRINTF("invalid length in RX " "descriptor, %u bytes, received %u bytes\n", real_len, max_len); ifp->if_ierrors++; goto tr_setup; } /* ieee80211_input() will check if the mbuf is too short */ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, m->m_data, max_len); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = real_len; DPRINTF("real length=%d bytes\n", real_len); rssi = URAL_RSSI(sc->sc_rx_desc.rssi); if (bpf_peers_present(ifp->if_bpf)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (sc->sc_rx_desc.flags & htole32(RAL_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_antenna = sc->sc_rx_ant; tap->wr_antsignal = rssi; bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } /* Strip trailing 802.11 MAC FCS. */ m_adj(m, -IEEE80211_CRC_LEN); case USB_ST_SETUP: tr_setup: if (sc->sc_flags & URAL_FLAG_READ_STALL) { usb2_transfer_start(sc->sc_xfer[3]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (m) { mtx_unlock(&sc->sc_mtx); ni = ieee80211_find_rxnode(ic, (void *)(m->m_data)); if (ni) { /* send the frame to the 802.11 layer */ if (ieee80211_input(ni, m, rssi, RAL_NOISE_FLOOR, 0)) { /* ignore */ } /* node is no longer needed */ ieee80211_free_node(ni); } else { /* broadcast */ if (ieee80211_input_all(ic, m, rssi, RAL_NOISE_FLOOR, 0)) { /* ignore */ } } mtx_lock(&sc->sc_mtx); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URAL_FLAG_READ_STALL; usb2_transfer_start(sc->sc_xfer[3]); } return; } } static void ural_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[1]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~URAL_FLAG_READ_STALL; usb2_transfer_start(xfer_other); } } static uint8_t ural_plcp_signal(uint16_t rate) { ; /* indent fix */ switch (rate) { /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* XXX unsupported/unknown rate */ default: return (0xff); } } /* * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that * should be freed, when "ural_setup_desc_and_tx" is called. */ static void ural_setup_desc_and_tx(struct ural_softc *sc, struct mbuf *m, uint32_t flags, uint16_t rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mm; enum ieee80211_phytype phytype; uint16_t plcp_length; uint16_t len; uint8_t remainder; DPRINTF("in\n"); if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { /* free packet */ ural_tx_freem(m); ifp->if_oerrors++; return; } if (!((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY))) { /* free packet */ ural_tx_freem(m); ifp->if_oerrors++; return; } if (rate < 2) { DPRINTF("rate < 2!\n"); /* avoid division by zero */ rate = 2; } ic->ic_lastdata = ticks; if (bpf_peers_present(ifp->if_bpf)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wt_antenna = sc->sc_tx_ant; bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } len = m->m_pkthdr.len; sc->sc_tx_desc.flags = htole32(flags); sc->sc_tx_desc.flags |= htole32(RAL_TX_NEWSEQ); sc->sc_tx_desc.flags |= htole32(len << 16); sc->sc_tx_desc.wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5) | RAL_IVOFFSET(sizeof(struct ieee80211_frame))); /* setup PLCP fields */ sc->sc_tx_desc.plcp_signal = ural_plcp_signal(rate); sc->sc_tx_desc.plcp_service = 4; len += IEEE80211_CRC_LEN; phytype = ieee80211_rate2phytype(sc->sc_rates, rate); if (phytype == IEEE80211_T_OFDM) { sc->sc_tx_desc.flags |= htole32(RAL_TX_OFDM); plcp_length = len & 0xfff; sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = ((16 * len) + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if ((remainder != 0) && (remainder < 7)) { sc->sc_tx_desc.plcp_service |= RAL_PLCP_LENGEXT; } } sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8; sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff; if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { sc->sc_tx_desc.plcp_signal |= 0x08; } } sc->sc_tx_desc.iv = 0; sc->sc_tx_desc.eiv = 0; if (sizeof(sc->sc_tx_desc) > MHLEN) { DPRINTF("No room for header structure!\n"); ural_tx_freem(m); return; } mm = m_gethdr(M_NOWAIT, MT_DATA); if (mm == NULL) { DPRINTF("Could not allocate header mbuf!\n"); ural_tx_freem(m); return; } DPRINTF(" %zu %u (out)\n", sizeof(sc->sc_tx_desc), m->m_pkthdr.len); bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); mm->m_len = sizeof(sc->sc_tx_desc); mm->m_next = m; mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; mm->m_pkthdr.rcvif = NULL; /* start write transfer, if not started */ _IF_ENQUEUE(&sc->sc_tx_queue, mm); usb2_transfer_start(sc->sc_xfer[0]); } static void ural_bulk_write_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t temp_len; uint8_t align; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & URAL_FLAG_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[2]); break; } if (sc->sc_flags & URAL_FLAG_WAIT_COMMAND) { /* * don't send anything while a command is pending ! */ break; } ural_fill_write_queue(sc); _IF_DEQUEUE(&sc->sc_tx_queue, m); if (m) { if (m->m_pkthdr.len > (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* compute transfer length */ temp_len = m->m_pkthdr.len; /* make transfer length 16-bit aligned */ align = (temp_len & 1); /* check if we need to add two extra bytes */ if (((temp_len + align) % 64) == 0) { align += 2; } /* check if we need to align length */ if (align != 0) { /* zero the extra bytes */ usb2_bzero(xfer->frbuffers, temp_len, align); temp_len += align; } DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, temp_len); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); /* free mbuf and node */ ural_tx_freem(m); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= URAL_FLAG_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[2]); } ifp->if_oerrors++; break; } } static void ural_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[0]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~URAL_FLAG_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void ural_watchdog(void *arg) { struct ural_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_amrr_timer) { usb2_config_td_queue_command (&sc->sc_config_td, NULL, &ural_cfg_amrr_timeout, 0, 0); } usb2_callout_reset(&sc->sc_watchdog, hz, &ural_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } /*========================================================================* * IF-net callbacks *========================================================================*/ static void ural_init_cb(void *arg) { struct ural_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &ural_cfg_pre_init, &ural_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static int ural_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; int error; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { usb2_config_td_queue_command (&sc->sc_config_td, &ural_cfg_pre_init, &ural_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &ural_cfg_pre_stop, &ural_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); } return (error); } static void ural_start_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); mtx_unlock(&sc->sc_mtx); } static void ural_cfg_newstate(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ural_vap *uvp = URAL_VAP(vap); enum ieee80211_state ostate; enum ieee80211_state nstate; int arg; ostate = vap->iv_state; nstate = sc->sc_ns_state; arg = sc->sc_ns_arg; if (ostate == IEEE80211_S_INIT) { /* We are leaving INIT. TSF sync should be off. */ ural_cfg_disable_tsf_sync(sc); } switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: ural_cfg_set_run(sc, cc); break; default: break; } mtx_unlock(&sc->sc_mtx); IEEE80211_LOCK(ic); uvp->newstate(vap, nstate, arg); if (vap->iv_newstate_cb != NULL) vap->iv_newstate_cb(vap, nstate, arg); IEEE80211_UNLOCK(ic); mtx_lock(&sc->sc_mtx); } static int ural_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ural_vap *uvp = URAL_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ural_softc *sc = ic->ic_ifp->if_softc; DPRINTF("setting new state: %d\n", nstate); /* Special case - cannot defer this call and cannot block ! */ if (nstate == IEEE80211_S_INIT) { /* stop timers */ mtx_lock(&sc->sc_mtx); sc->sc_amrr_timer = 0; mtx_unlock(&sc->sc_mtx); return (uvp->newstate(vap, nstate, arg)); } mtx_lock(&sc->sc_mtx); if (usb2_config_td_is_gone(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); return (0); /* nothing to do */ } /* store next state */ sc->sc_ns_state = nstate; sc->sc_ns_arg = arg; /* stop timers */ sc->sc_amrr_timer = 0; /* * USB configuration can only be done from the USB configuration * thread: */ usb2_config_td_queue_command (&sc->sc_config_td, &ural_config_copy, &ural_cfg_newstate, 0, 0); mtx_unlock(&sc->sc_mtx); return (EINPROGRESS); } static void ural_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) { struct ural_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&sc->sc_mtx); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); usb2_config_td_queue_command (&sc->sc_config_td, &ural_config_copy, func, 0, 0); mtx_unlock(&sc->sc_mtx); } static void ural_scan_start_cb(struct ieee80211com *ic) { ural_std_command(ic, &ural_cfg_scan_start); } static void ural_scan_end_cb(struct ieee80211com *ic) { ural_std_command(ic, &ural_cfg_scan_end); } static void ural_set_channel_cb(struct ieee80211com *ic) { ural_std_command(ic, &ural_cfg_set_chan); } /*========================================================================* * configure sub-routines, ural_cfg_xxx *========================================================================*/ static void ural_cfg_scan_start(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* abort TSF synchronization */ ural_cfg_disable_tsf_sync(sc); ural_cfg_set_bssid(sc, cc->if_broadcastaddr); } static void ural_cfg_scan_end(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* enable TSF synchronization */ ural_cfg_enable_tsf_sync(sc, cc, 0); ural_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } static void ural_cfg_set_chan(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_RF5222 = (sizeof(ural_rf5222) / sizeof(ural_rf5222[0])), }; uint32_t i; uint32_t chan; uint8_t power; uint8_t tmp; chan = cc->ic_curchan.chan_to_ieee; if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { /* nothing to do */ return; } if (cc->ic_curchan.chan_is_2ghz) power = min(sc->sc_txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - cc->ic_txpowlimit) / 8; DPRINTFN(3, "setting channel to %u, " "tx-power to %u\n", chan, power); switch (sc->sc_rf_rev) { case RAL_RF_2522: ural_cfg_rf_write(sc, RAL_RF1, 0x00814); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); break; case RAL_RF_2523: ural_cfg_rf_write(sc, RAL_RF1, 0x08804); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x38044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2524: ural_cfg_rf_write(sc, RAL_RF1, 0x0c808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525: ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525E: ural_cfg_rf_write(sc, RAL_RF1, 0x08808); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RAL_RF_2526: ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); ural_cfg_rf_write(sc, RAL_RF1, 0x08804); ural_cfg_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x18044); ural_cfg_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RAL_RF_5222: for (i = 0; i < N_RF5222; i++) { if (ural_rf5222[i].chan == chan) { ural_cfg_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); ural_cfg_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); ural_cfg_rf_write(sc, RAL_RF3, (power << 7) | 0x00040); ural_cfg_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); break; } } break; } if ((cc->ic_opmode != IEEE80211_M_MONITOR) && (!(cc->ic_flags & IEEE80211_F_SCAN))) { /* set Japan filter bit for channel 14 */ tmp = ural_cfg_bbp_read(sc, 70); if (chan == 14) { tmp |= RAL_JAPAN_FILTER; } else { tmp &= ~RAL_JAPAN_FILTER; } ural_cfg_bbp_write(sc, 70, tmp); /* clear CRC errors */ ural_cfg_read(sc, RAL_STA_CSR0); ural_cfg_disable_rf_tune(sc); } /* update basic rate set */ if (cc->ic_curchan.chan_is_b) { /* 11b basic rates: 1, 2Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); } else if (cc->ic_curchan.chan_is_a) { /* 11a basic rates: 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); } else { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); } /* wait a little */ if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return; } } static void ural_cfg_set_run(struct ural_softc *sc, struct usb2_config_td_cc *cc) { if (cc->ic_opmode != IEEE80211_M_MONITOR) { ural_cfg_update_slot(sc, cc, 0); ural_cfg_set_txpreamble(sc, cc, 0); /* update basic rate set */ if (cc->ic_bsschan.chan_is_5ghz) { /* 11a basic rates: 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x150); } else if (cc->ic_bsschan.chan_is_g) { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); } else { /* 11b basic rates: 1, 2Mbps */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x3); } ural_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } if ((cc->ic_opmode == IEEE80211_M_HOSTAP) || (cc->ic_opmode == IEEE80211_M_IBSS)) { ural_tx_bcn(sc); } /* make tx led blink on tx (controlled by ASIC) */ ural_cfg_write(sc, RAL_MAC_CSR20, 1); if (cc->ic_opmode != IEEE80211_M_MONITOR) { ural_cfg_enable_tsf_sync(sc, cc, 0); } /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); if (cc->iv_bss.fixed_rate_none) { /* enable automatic rate adaptation */ ural_cfg_amrr_start(sc); } } /*------------------------------------------------------------------------* * ural_cfg_disable_rf_tune - disable RF auto-tuning *------------------------------------------------------------------------*/ static void ural_cfg_disable_rf_tune(struct ural_softc *sc) { uint32_t tmp; if (sc->sc_rf_rev != RAL_RF_2523) { tmp = sc->sc_rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; ural_cfg_rf_write(sc, RAL_RF1, tmp); } tmp = sc->sc_rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; ural_cfg_rf_write(sc, RAL_RF3, tmp); DPRINTFN(3, "disabling RF autotune\n"); } /*------------------------------------------------------------------------* * ural_cfg_enable_tsf_sync - refer to IEEE Std 802.11-1999 pp. 123 * for more information on TSF synchronization *------------------------------------------------------------------------*/ static void ural_cfg_enable_tsf_sync(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t logcwmin; uint16_t preload; uint16_t tmp; /* first, disable TSF synchronization */ ural_cfg_write(sc, RAL_TXRX_CSR19, 0); tmp = (16 * cc->iv_bss.ni_intval) << 4; ural_cfg_write(sc, RAL_TXRX_CSR18, tmp); logcwmin = (cc->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; preload = (cc->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; tmp = (logcwmin << 12) | preload; ural_cfg_write(sc, RAL_TXRX_CSR20, tmp); /* finally, enable TSF synchronization */ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; if (cc->ic_opmode == IEEE80211_M_STA) tmp |= RAL_ENABLE_TSF_SYNC(1); else tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; ural_cfg_write(sc, RAL_TXRX_CSR19, tmp); DPRINTF("enabling TSF synchronization\n"); } static void ural_cfg_disable_tsf_sync(struct ural_softc *sc) { /* abort TSF synchronization */ ural_cfg_write(sc, RAL_TXRX_CSR19, 0); /* force tx led to stop blinking */ ural_cfg_write(sc, RAL_MAC_CSR20, 0); } #define RAL_RXTX_TURNAROUND 5 /* us */ static void ural_cfg_update_slot(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t slottime; uint16_t sifs; uint16_t eifs; slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; /* * These settings may sound a bit inconsistent but this is what the * reference driver does. */ if (cc->ic_curmode == IEEE80211_MODE_11B) { sifs = 16 - RAL_RXTX_TURNAROUND; eifs = 364; } else { sifs = 10 - RAL_RXTX_TURNAROUND; eifs = 64; } ural_cfg_write(sc, RAL_MAC_CSR10, slottime); ural_cfg_write(sc, RAL_MAC_CSR11, sifs); ural_cfg_write(sc, RAL_MAC_CSR12, eifs); } static void ural_cfg_set_txpreamble(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t tmp; tmp = ural_cfg_read(sc, RAL_TXRX_CSR10); if (cc->ic_flags & IEEE80211_F_SHPREAMBLE) { tmp |= RAL_SHORT_PREAMBLE; } else { tmp &= ~RAL_SHORT_PREAMBLE; } ural_cfg_write(sc, RAL_TXRX_CSR10, tmp); } static void ural_cfg_set_bssid(struct ural_softc *sc, uint8_t *bssid) { ural_cfg_write_multi(sc, RAL_MAC_CSR5, bssid, IEEE80211_ADDR_LEN); DPRINTF("setting BSSID to 0x%02x%02x%02x%02x%02x%02x\n", bssid[5], bssid[4], bssid[3], bssid[2], bssid[1], bssid[0]); } static void ural_cfg_set_macaddr(struct ural_softc *sc, uint8_t *addr) { ural_cfg_write_multi(sc, RAL_MAC_CSR2, addr, IEEE80211_ADDR_LEN); DPRINTF("setting MAC to 0x%02x%02x%02x%02x%02x%02x\n", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); } static void ural_cfg_update_promisc(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint16_t tmp; tmp = ural_cfg_read(sc, RAL_TXRX_CSR2); if (cc->if_flags & IFF_PROMISC) { tmp &= ~RAL_DROP_NOT_TO_ME; } else { tmp |= RAL_DROP_NOT_TO_ME; } ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); DPRINTF("%s promiscuous mode\n", (cc->if_flags & IFF_PROMISC) ? "entering" : "leaving"); } static void ural_cfg_set_txantenna(struct ural_softc *sc, uint8_t antenna) { uint16_t tmp; uint8_t tx; tx = ural_cfg_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; if (antenna == 1) tx |= RAL_BBP_ANTA; else if (antenna == 2) tx |= RAL_BBP_ANTB; else tx |= RAL_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if ((sc->sc_rf_rev == RAL_RF_2525E) || (sc->sc_rf_rev == RAL_RF_2526) || (sc->sc_rf_rev == RAL_RF_5222)) { tx |= RAL_BBP_FLIPIQ; } ural_cfg_bbp_write(sc, RAL_BBP_TX, tx); /* update values in PHY_CSR5 and PHY_CSR6 */ tmp = ural_cfg_read(sc, RAL_PHY_CSR5) & ~0x7; ural_cfg_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); tmp = ural_cfg_read(sc, RAL_PHY_CSR6) & ~0x7; ural_cfg_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); } static void ural_cfg_set_rxantenna(struct ural_softc *sc, uint8_t antenna) { uint8_t rx; rx = ural_cfg_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; if (antenna == 1) rx |= RAL_BBP_ANTA; else if (antenna == 2) rx |= RAL_BBP_ANTB; else rx |= RAL_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if ((sc->sc_rf_rev == RAL_RF_2525E) || (sc->sc_rf_rev == RAL_RF_2526)) { rx &= ~RAL_BBP_FLIPIQ; } ural_cfg_bbp_write(sc, RAL_BBP_RX, rx); } static void ural_cfg_read_eeprom(struct ural_softc *sc) { uint16_t val; ural_cfg_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); val = le16toh(val); sc->sc_rf_rev = (val >> 11) & 0x7; sc->sc_hw_radio = (val >> 10) & 0x1; sc->sc_led_mode = (val >> 6) & 0x7; sc->sc_rx_ant = (val >> 4) & 0x3; sc->sc_tx_ant = (val >> 2) & 0x3; sc->sc_nb_ant = (val & 0x3); DPRINTF("val = 0x%04x\n", val); /* read MAC address */ ural_cfg_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_myaddr, sizeof(sc->sc_myaddr)); /* read default values for BBP registers */ ural_cfg_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->sc_bbp_prom, sizeof(sc->sc_bbp_prom)); /* read Tx power for all b/g channels */ ural_cfg_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->sc_txpow, sizeof(sc->sc_txpow)); } static uint8_t ural_cfg_bbp_init(struct ural_softc *sc) { enum { N_DEF_BBP = (sizeof(ural_def_bbp) / sizeof(ural_def_bbp[0])), }; uint16_t i; uint8_t to; /* wait for BBP to become ready */ for (to = 0;; to++) { if (to < 100) { if (ural_cfg_bbp_read(sc, RAL_BBP_VERSION) != 0) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { return (1); /* failure */ } } else { DPRINTF("timeout waiting for BBP\n"); return (1); /* failure */ } } /* initialize BBP registers to default values */ for (i = 0; i < N_DEF_BBP; i++) { ural_cfg_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); } #if 0 /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->sc_bbp_prom[i].reg == 0xff) { continue; } ural_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val); } #endif return (0); /* success */ } static void ural_cfg_pre_init(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* immediate configuration */ ural_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= URAL_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); } static void ural_cfg_init(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { enum { N_DEF_MAC = (sizeof(ural_def_mac) / sizeof(ural_def_mac[0])), }; uint16_t tmp; uint16_t i; uint8_t to; /* delayed configuration */ ural_cfg_set_testmode(sc); ural_cfg_write(sc, 0x308, 0x00f0); /* XXX magic */ ural_cfg_stop(sc, cc, 0); /* initialize MAC registers to default values */ for (i = 0; i < N_DEF_MAC; i++) { ural_cfg_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); } /* wait for BBP and RF to wake up (this can take a long time!) */ for (to = 0;; to++) { if (to < 100) { tmp = ural_cfg_read(sc, RAL_MAC_CSR17); if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == (RAL_BBP_AWAKE | RAL_RF_AWAKE)) { break; } if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) { goto fail; } } else { DPRINTF("timeout waiting for " "BBP/RF to wakeup\n"); goto fail; } } /* we're ready! */ ural_cfg_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); /* set basic rate set (will be updated later) */ ural_cfg_write(sc, RAL_TXRX_CSR11, 0x15f); if (ural_cfg_bbp_init(sc)) { goto fail; } /* set default BSS channel */ ural_cfg_set_chan(sc, cc, 0); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); DPRINTF("rx_ant=%d, tx_ant=%d\n", sc->sc_rx_ant, sc->sc_tx_ant); ural_cfg_set_txantenna(sc, sc->sc_tx_ant); ural_cfg_set_rxantenna(sc, sc->sc_rx_ant); ural_cfg_set_macaddr(sc, cc->ic_myaddr); /* * make sure that the first transaction * clears the stall: */ sc->sc_flags |= (URAL_FLAG_READ_STALL | URAL_FLAG_WRITE_STALL | URAL_FLAG_LL_READY); if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); /* * start IEEE802.11 layer */ mtx_unlock(&sc->sc_mtx); ieee80211_start_all(ic); mtx_lock(&sc->sc_mtx); } /* * start Rx */ tmp = RAL_DROP_PHY | RAL_DROP_CRC; if (cc->ic_opmode != IEEE80211_M_MONITOR) { tmp |= (RAL_DROP_CTL | RAL_DROP_BAD_VERSION); if (cc->ic_opmode != IEEE80211_M_HOSTAP) { tmp |= RAL_DROP_TODS; } if (!(cc->if_flags & IFF_PROMISC)) { tmp |= RAL_DROP_NOT_TO_ME; } } ural_cfg_write(sc, RAL_TXRX_CSR2, tmp); return; fail: ural_cfg_pre_stop(sc, NULL, 0); if (cc) { ural_cfg_stop(sc, cc, 0); } } static void ural_cfg_pre_stop(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ ural_config_copy(sc, cc, refcount); } /* immediate configuration */ if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(URAL_FLAG_HL_READY | URAL_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[0]); usb2_transfer_stop(sc->sc_xfer[1]); usb2_transfer_stop(sc->sc_xfer[2]); usb2_transfer_stop(sc->sc_xfer[3]); /* clean up transmission */ ural_tx_clean_queue(sc); } static void ural_cfg_stop(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* disable Rx */ ural_cfg_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); /* reset ASIC and BBP (but won't reset MAC registers!) */ ural_cfg_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); /* clear reset */ ural_cfg_write(sc, RAL_MAC_CSR1, 0); /* wait a little */ usb2_config_td_sleep(&sc->sc_config_td, hz / 10); } static void ural_cfg_amrr_start(struct ural_softc *sc) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = ural_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } /* init AMRR */ ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); /* enable AMRR timer */ sc->sc_amrr_timer = 1; } static void ural_cfg_amrr_timeout(struct ural_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ieee80211_node *ni; uint32_t ok; uint32_t fail; /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_cfg_read_multi(sc, RAL_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta)); vap = ural_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } if ((sc->sc_flags & URAL_FLAG_LL_READY) && (sc->sc_flags & URAL_FLAG_HL_READY)) { ok = sc->sc_sta[7] + /* TX ok w/o retry */ sc->sc_sta[8]; /* TX ok w/ retry */ fail = sc->sc_sta[9]; /* TX retry-fail count */ if (sc->sc_amrr_timer) { ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, ok + fail, ok, sc->sc_sta[8] + fail); if (ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn)) { /* ignore */ } } ifp->if_oerrors += fail; } } static struct ieee80211vap * ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ural_vap *uvp; struct ieee80211vap *vap; struct ural_softc *sc = ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* config thread is gone */ return (NULL); } mtx_unlock(&sc->sc_mtx); if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = (struct ural_vap *)malloc(sizeof(struct ural_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (uvp == NULL) return NULL; vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = &ural_newstate_cb; ieee80211_amrr_init(&uvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */ ); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); /* store current operation mode */ ic->ic_opmode = opmode; return (vap); } static void ural_vap_delete(struct ieee80211vap *vap) { struct ural_vap *uvp = URAL_VAP(vap); struct ural_softc *sc = vap->iv_ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { /* ignore */ } mtx_unlock(&sc->sc_mtx); ieee80211_amrr_cleanup(&uvp->amrr); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } /* ARGUSED */ static struct ieee80211_node * ural_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct ural_node *un; un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ((un != NULL) ? &un->ni : NULL); } static void ural_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); } static void ural_fill_write_queue(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; /* * We only fill up half of the queue with data frames. The rest is * reserved for other kinds of frames. */ while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (void *)(m->m_pkthdr.rcvif); m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } ural_tx_data(sc, m, ni); } } static void ural_tx_clean_queue(struct ural_softc *sc) { struct mbuf *m; for (;;) { _IF_DEQUEUE(&sc->sc_tx_queue, m); if (!m) { break; } ural_tx_freem(m); } } static void ural_tx_freem(struct mbuf *m) { struct ieee80211_node *ni; while (m) { ni = (void *)(m->m_pkthdr.rcvif); if (!ni) { m = m_free(m); continue; } if (m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); } m_freem(m); ieee80211_free_node(ni); break; } } static void ural_tx_mgt(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } wh = mtod(m, struct ieee80211_frame *); } flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= RAL_TX_TIMESTAMP; } m->m_pkthdr.rcvif = (void *)ni; ural_setup_desc_and_tx(sc, m, flags, tp->mgmtrate); } static struct ieee80211vap * ural_get_vap(struct ural_softc *sc) { struct ifnet *ifp; struct ieee80211com *ic; if (sc == NULL) { return NULL; } ifp = sc->sc_ifp; if (ifp == NULL) { return NULL; } ic = ifp->if_l2com; if (ic == NULL) { return NULL; } return TAILQ_FIRST(&ic->ic_vaps); } static void ural_tx_bcn(struct ural_softc *sc) { struct ieee80211_node *ni; struct ieee80211vap *vap; struct ieee80211com *ic; const struct ieee80211_txparam *tp; struct mbuf *m; vap = ural_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } ic = vap->iv_ic; if (ic == NULL) { return; } DPRINTFN(11, "Sending beacon frame.\n"); m = ieee80211_beacon_alloc(ni, &URAL_VAP(vap)->bo); if (m == NULL) { DPRINTFN(0, "could not allocate beacon\n"); return; } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); ural_setup_desc_and_tx(sc, m, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, tp->mgmtrate); } static void ural_tx_data(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; uint16_t rate; DPRINTFN(11, "Sending data.\n"); wh = mtod(m, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } /* packet header may have moved, reset our local pointer */ wh = mtod(m, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint8_t prot = IEEE80211_PROT_NONE; if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { ural_tx_prot(sc, m, ni, prot, rate); flags |= RAL_TX_IFS_SIFS; } flags |= RAL_TX_ACK; flags |= RAL_TX_RETRY(7); dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); USETW(wh->i_dur, dur); } m->m_pkthdr.rcvif = (void *)ni; ural_setup_desc_and_tx(sc, m, flags, rate); } static void ural_tx_prot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct mbuf *mprot; uint32_t flags; uint16_t protrate; uint16_t ackrate; uint16_t pktlen; uint16_t dur; uint8_t isshort; KASSERT((prot == IEEE80211_PROT_RTSCTS) || (prot == IEEE80211_PROT_CTSONLY), ("protection %u", prot)); DPRINTFN(16, "Sending protection frame.\n"); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(sc->sc_rates, rate); ackrate = ieee80211_ack_rate(sc->sc_rates, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort); +ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags = RAL_TX_RETRY(7); if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort); flags |= RAL_TX_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { return; } mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni); ural_setup_desc_and_tx(sc, mprot, flags, protrate); } static void ural_tx_raw(struct ural_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { uint32_t flags; uint16_t rate; DPRINTFN(11, "Sending raw frame.\n"); rate = params->ibp_rate0 & IEEE80211_RATE_VAL; /* XXX validate */ if (rate == 0) { m_freem(m); ieee80211_free_node(ni); return; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RAL_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { ural_tx_prot(sc, m, ni, (params->ibp_flags & IEEE80211_BPF_RTS) ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); flags |= RAL_TX_IFS_SIFS; } m->m_pkthdr.rcvif = (void *)ni; ural_setup_desc_and_tx(sc, m, flags, rate); } static int ural_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ ural_tx_mgt(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ ural_tx_raw(sc, m, ni, params); } mtx_unlock(&sc->sc_mtx); return (0); } static void ural_update_mcast_cb(struct ifnet *ifp) { /* not supported */ } static void ural_update_promisc_cb(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &ural_config_copy, &ural_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); } Index: projects/cambria/sys/dev/usb2/wlan/if_zyd2.c =================================================================== --- projects/cambria/sys/dev/usb2/wlan/if_zyd2.c (revision 186459) +++ projects/cambria/sys/dev/usb2/wlan/if_zyd2.c (revision 186460) @@ -1,3221 +1,3216 @@ /* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */ /* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 2006 by Damien Bergamini * Copyright (c) 2006 by Florian Stoehr * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * ZyDAS ZD1211/ZD1211B USB WLAN driver * * NOTE: all function names beginning like "zyd_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #define usb2_config_td_cc zyd_config_copy #define usb2_config_td_softc zyd_softc #define USB_DEBUG_VAR zyd_debug #include #include #include #include #include #include #include #include #include #include #include #if USB_DEBUG static int zyd_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd"); SYSCTL_INT(_hw_usb2_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0, "zyd debug level"); #endif #undef INDEXES #define INDEXES(a) (sizeof(a) / sizeof((a)[0])) static device_probe_t zyd_probe; static device_attach_t zyd_attach; static device_detach_t zyd_detach; static usb2_callback_t zyd_intr_read_clear_stall_callback; static usb2_callback_t zyd_intr_read_callback; static usb2_callback_t zyd_intr_write_clear_stall_callback; static usb2_callback_t zyd_intr_write_callback; static usb2_callback_t zyd_bulk_read_clear_stall_callback; static usb2_callback_t zyd_bulk_read_callback; static usb2_callback_t zyd_bulk_write_clear_stall_callback; static usb2_callback_t zyd_bulk_write_callback; static usb2_config_td_command_t zyd_cfg_first_time_setup; static usb2_config_td_command_t zyd_cfg_update_promisc; static usb2_config_td_command_t zyd_cfg_set_chan; static usb2_config_td_command_t zyd_cfg_pre_init; static usb2_config_td_command_t zyd_cfg_init; static usb2_config_td_command_t zyd_cfg_pre_stop; static usb2_config_td_command_t zyd_cfg_stop; static usb2_config_td_command_t zyd_config_copy; static usb2_config_td_command_t zyd_cfg_scan_start; static usb2_config_td_command_t zyd_cfg_scan_end; static usb2_config_td_command_t zyd_cfg_set_rxfilter; static usb2_config_td_command_t zyd_cfg_amrr_timeout; static uint8_t zyd_plcp2ieee(uint8_t, uint8_t); static void zyd_cfg_usbrequest(struct zyd_softc *, struct usb2_device_request *, uint8_t *); static void zyd_cfg_usb2_intr_read(struct zyd_softc *, void *, uint32_t); static void zyd_cfg_usb2_intr_write(struct zyd_softc *, const void *, uint16_t, uint32_t); static void zyd_cfg_read16(struct zyd_softc *, uint16_t, uint16_t *); static void zyd_cfg_read32(struct zyd_softc *, uint16_t, uint32_t *); static void zyd_cfg_write16(struct zyd_softc *, uint16_t, uint16_t); static void zyd_cfg_write32(struct zyd_softc *, uint16_t, uint32_t); static void zyd_cfg_rfwrite(struct zyd_softc *, uint32_t); static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *, const uint8_t *, uint32_t); static void zyd_cfg_lock_phy(struct zyd_softc *); static void zyd_cfg_unlock_phy(struct zyd_softc *); static void zyd_cfg_set_beacon_interval(struct zyd_softc *, uint32_t); static const char *zyd_rf_name(uint8_t); static void zyd_cfg_rf_rfmd_init(struct zyd_softc *, struct zyd_rf *); static void zyd_cfg_rf_rfmd_switch_radio(struct zyd_softc *, uint8_t); static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *, struct zyd_rf *, uint8_t); static void zyd_cfg_rf_al2230_switch_radio(struct zyd_softc *, uint8_t); static void zyd_cfg_rf_al2230_init(struct zyd_softc *, struct zyd_rf *); static void zyd_cfg_rf_al2230_init_b(struct zyd_softc *, struct zyd_rf *); static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *, struct zyd_rf *, uint8_t); static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *, struct zyd_rf *); static uint8_t zyd_cfg_hw_init(struct zyd_softc *); static void zyd_cfg_set_mac_addr(struct zyd_softc *, const uint8_t *); static void zyd_cfg_switch_radio(struct zyd_softc *, uint8_t); static void zyd_cfg_set_bssid(struct zyd_softc *, uint8_t *); static void zyd_start_cb(struct ifnet *); static void zyd_init_cb(void *); static int zyd_ioctl_cb(struct ifnet *, u_long command, caddr_t data); static void zyd_watchdog(void *); static void zyd_end_of_commands(struct zyd_softc *); static void zyd_newassoc_cb(struct ieee80211_node *, int isnew); static void zyd_scan_start_cb(struct ieee80211com *); static void zyd_scan_end_cb(struct ieee80211com *); static void zyd_set_channel_cb(struct ieee80211com *); static void zyd_cfg_set_led(struct zyd_softc *, uint32_t, uint8_t); static struct ieee80211vap *zyd_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void zyd_vap_delete(struct ieee80211vap *); static struct ieee80211_node *zyd_node_alloc_cb(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void zyd_cfg_set_run(struct zyd_softc *, struct usb2_config_td_cc *); static void zyd_fill_write_queue(struct zyd_softc *); static void zyd_tx_clean_queue(struct zyd_softc *); static void zyd_tx_freem(struct mbuf *); static void zyd_tx_mgt(struct zyd_softc *, struct mbuf *, struct ieee80211_node *); static struct ieee80211vap *zyd_get_vap(struct zyd_softc *); static void zyd_tx_data(struct zyd_softc *, struct mbuf *, struct ieee80211_node *); static int zyd_raw_xmit_cb(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void zyd_setup_desc_and_tx(struct zyd_softc *, struct mbuf *, uint16_t); static int zyd_newstate_cb(struct ieee80211vap *, enum ieee80211_state nstate, int arg); static void zyd_cfg_amrr_start(struct zyd_softc *); static void zyd_update_mcast_cb(struct ifnet *); static void zyd_update_promisc_cb(struct ifnet *); static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY; static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB; /* various supported device vendors/products */ #define ZYD_ZD1211 0 #define ZYD_ZD1211B 1 static const struct usb2_device_id zyd_devs[] = { /* ZYD_ZD1211 */ {USB_VPI(USB_VENDOR_3COM2, USB_PRODUCT_3COM2_3CRUSB10075, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WL54, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL159G, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_CYBERTAN, USB_PRODUCT_CYBERTAN_TG54USB, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_DRAYTEK, USB_PRODUCT_DRAYTEK_VIGOR550, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GD, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GZL, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54GZ, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54MINI, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG760A, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SENAO, USB_PRODUCT_SENAO_NUB8301, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_SWEEX, USB_PRODUCT_SWEEX_ZD1211, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_QUICKWLAN, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_1, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_2, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_G240, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_ALL0298V2, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB_A, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR055G, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_AG225H, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_ZYAIRG220, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G200V2, ZYD_ZD1211)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G202, ZYD_ZD1211)}, /* ZYD_ZD1211B */ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SMCWUSBG, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_A9T_WIFI, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050_V4000, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSBF54G, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_FIBERLINE, USB_PRODUCT_FIBERLINE_WL430U, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54L, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_SNU5600, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US54GXS, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG76NA, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UBC1, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_USR, USB_PRODUCT_USR_USR5423, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211B, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_M202, ZYD_ZD1211B)}, {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G220V2, ZYD_ZD1211B)}, }; static const struct usb2_config zyd_config[ZYD_N_TRANSFER] = { [ZYD_TR_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = ZYD_MAX_TXBUFSZ, .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &zyd_bulk_write_callback, .ep_index = 0, .mh.timeout = 10000, /* 10 seconds */ }, [ZYD_TR_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = ZYX_MAX_RXBUFSZ, .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &zyd_bulk_read_callback, .ep_index = 0, }, [ZYD_TR_BULK_CS_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_bulk_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [ZYD_TR_BULK_CS_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_bulk_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [ZYD_TR_INTR_DT_WR] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = sizeof(struct zyd_cmd), .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .mh.callback = &zyd_intr_write_callback, .mh.timeout = 1000, /* 1 second */ .ep_index = 1, }, [ZYD_TR_INTR_DT_RD] = { .type = UE_BULK_INTR, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = sizeof(struct zyd_cmd), .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .mh.callback = &zyd_intr_read_callback, .ep_index = 1, }, [ZYD_TR_INTR_CS_WR] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_intr_write_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, [ZYD_TR_INTR_CS_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .mh.bufsize = sizeof(struct usb2_device_request), .mh.flags = {}, .mh.callback = &zyd_intr_read_clear_stall_callback, .mh.timeout = 1000, /* 1 second */ .mh.interval = 50, /* 50ms */ }, }; static devclass_t zyd_devclass; static device_method_t zyd_methods[] = { DEVMETHOD(device_probe, zyd_probe), DEVMETHOD(device_attach, zyd_attach), DEVMETHOD(device_detach, zyd_detach), {0, 0} }; static driver_t zyd_driver = { .name = "zyd", .methods = zyd_methods, .size = sizeof(struct zyd_softc), }; DRIVER_MODULE(zyd, ushub, zyd_driver, zyd_devclass, NULL, 0); MODULE_DEPEND(zyd, usb2_wlan, 1, 1, 1); MODULE_DEPEND(zyd, usb2_core, 1, 1, 1); MODULE_DEPEND(zyd, wlan, 1, 1, 1); MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1); static uint8_t zyd_plcp2ieee(uint8_t signal, uint8_t isofdm) { if (isofdm) { static const uint8_t ofdmrates[16] = {0, 0, 0, 0, 0, 0, 0, 96, 48, 24, 12, 108, 72, 36, 18}; return ofdmrates[signal & 0xf]; } else { static const uint8_t cckrates[16] = {0, 0, 0, 0, 4, 0, 0, 11, 0, 0, 2, 0, 0, 0, 22, 0}; return cckrates[signal & 0xf]; } } /* * USB request basic wrapper */ static void zyd_cfg_usbrequest(struct zyd_softc *sc, struct usb2_device_request *req, uint8_t *data) { usb2_error_t err; uint16_t length; if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto error; } err = usb2_do_request_flags (sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000); if (err) { DPRINTFN(0, "%s: device request failed, err=%s " "(ignored)\n", sc->sc_name, usb2_errstr(err)); error: length = UGETW(req->wLength); if ((req->bmRequestType & UT_READ) && length) { bzero(data, length); } } } static void zyd_intr_read_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_RD]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_INTR_READ_STALL; usb2_transfer_start(xfer_other); } } /* * Callback handler for interrupt transfer */ static void zyd_intr_read_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct zyd_cmd *cmd = &sc->sc_intr_ibuf; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: actlen = xfer->actlen; DPRINTFN(3, "length=%d\n", actlen); if (actlen > sizeof(sc->sc_intr_ibuf)) { actlen = sizeof(sc->sc_intr_ibuf); } usb2_copy_out(xfer->frbuffers, 0, &sc->sc_intr_ibuf, actlen); switch (le16toh(cmd->code)) { case ZYD_NOTIF_RETRYSTATUS: goto handle_notif_retrystatus; case ZYD_NOTIF_IORD: goto handle_notif_iord; default: DPRINTFN(2, "unknown indication: 0x%04x\n", le16toh(cmd->code)); } /* fallthrough */ case USB_ST_SETUP: tr_setup: if (sc->sc_flags & ZYD_FLAG_INTR_READ_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); break; } xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); break; default: /* Error */ DPRINTFN(3, "error = %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_INTR_READ_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_RD]); } break; } return; handle_notif_retrystatus:{ struct zyd_notif_retry *retry = (void *)(cmd->data); struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ieee80211_node *ni; DPRINTF("retry intr: rate=0x%x " "addr=%02x:%02x:%02x:%02x:%02x:%02x count=%d (0x%x)\n", le16toh(retry->rate), retry->macaddr[0], retry->macaddr[1], retry->macaddr[2], retry->macaddr[3], retry->macaddr[4], retry->macaddr[5], le16toh(retry->count) & 0xff, le16toh(retry->count)); vap = zyd_get_vap(sc); if ((vap != NULL) && (sc->sc_amrr_timer)) { /* * Find the node to which the packet was sent * and update its retry statistics. In BSS * mode, this node is the AP we're associated * to so no lookup is actually needed. */ ni = ieee80211_find_txnode(vap, retry->macaddr); if (ni != NULL) { ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn, IEEE80211_AMRR_FAILURE, 1); ieee80211_free_node(ni); } } if (retry->count & htole16(0x100)) { ifp->if_oerrors++; /* too many retries */ } goto tr_setup; } handle_notif_iord: if (*(uint16_t *)cmd->data == htole16(ZYD_CR_INTERRUPT)) { goto tr_setup; /* HMAC interrupt */ } if (actlen < 4) { DPRINTFN(0, "too short, %u bytes\n", actlen); goto tr_setup; /* too short */ } actlen -= 4; sc->sc_intr_ilen = actlen; if (sc->sc_intr_iwakeup) { sc->sc_intr_iwakeup = 0; usb2_cv_signal(&sc->sc_intr_cv); } else { sc->sc_intr_iwakeup = 1; } /* * We pause reading data from the interrupt endpoint until the * data has been picked up! */ } /* * Interrupt call reply transfer, read */ static void zyd_cfg_usb2_intr_read(struct zyd_softc *sc, void *data, uint32_t size) { uint16_t actlen; uint16_t x; if (size > sizeof(sc->sc_intr_ibuf.data)) { DPRINTFN(0, "truncating transfer size!\n"); size = sizeof(sc->sc_intr_ibuf.data); } if (usb2_config_td_is_gone(&sc->sc_config_td)) { bzero(data, size); goto done; } if (sc->sc_intr_iwakeup) { DPRINTF("got data already!\n"); sc->sc_intr_iwakeup = 0; goto skip0; } repeat: sc->sc_intr_iwakeup = 1; while (sc->sc_intr_iwakeup) { /* wait for data */ usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); if (usb2_cv_timedwait(&sc->sc_intr_cv, &sc->sc_mtx, hz / 2)) { /* should not happen */ } if (usb2_config_td_is_gone(&sc->sc_config_td)) { bzero(data, size); goto done; } } skip0: if (size != sc->sc_intr_ilen) { DPRINTFN(0, "unexpected length %u != %u\n", size, sc->sc_intr_ilen); goto repeat; } actlen = sc->sc_intr_ilen; actlen /= 4; /* verify register values */ for (x = 0; x != actlen; x++) { if (sc->sc_intr_obuf.data[(2 * x)] != sc->sc_intr_ibuf.data[(4 * x)]) { /* invalid register */ DPRINTFN(0, "Invalid register (1) at %u!\n", x); goto repeat; } if (sc->sc_intr_obuf.data[(2 * x) + 1] != sc->sc_intr_ibuf.data[(4 * x) + 1]) { /* invalid register */ DPRINTFN(0, "Invalid register (2) at %u!\n", x); goto repeat; } } bcopy(sc->sc_intr_ibuf.data, data, size); /* * We have fetched the data from the shared buffer and it is * safe to restart the interrupt transfer! */ usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); done: return; } static void zyd_intr_write_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_INTR_DT_WR]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_INTR_WRITE_STALL; usb2_transfer_start(xfer_other); } } static void zyd_intr_write_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(3, "length=%d\n", xfer->actlen); goto wakeup; case USB_ST_SETUP: if (sc->sc_flags & ZYD_FLAG_INTR_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); goto wakeup; } if (sc->sc_intr_owakeup) { usb2_copy_in(xfer->frbuffers, 0, &sc->sc_intr_obuf, sc->sc_intr_olen); xfer->frlengths[0] = sc->sc_intr_olen; usb2_start_hardware(xfer); } break; default: /* Error */ DPRINTFN(3, "error = %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_INTR_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_CS_WR]); } goto wakeup; } return; wakeup: if (sc->sc_intr_owakeup) { sc->sc_intr_owakeup = 0; usb2_cv_signal(&sc->sc_intr_cv); } } /* * Interrupt transfer, write. * * Not always an "interrupt transfer". If operating in * full speed mode, EP4 is bulk out, not interrupt out. */ static void zyd_cfg_usb2_intr_write(struct zyd_softc *sc, const void *data, uint16_t code, uint32_t size) { if (size > sizeof(sc->sc_intr_obuf.data)) { DPRINTFN(0, "truncating transfer size!\n"); size = sizeof(sc->sc_intr_obuf.data); } if (usb2_config_td_is_gone(&sc->sc_config_td)) { goto done; } sc->sc_intr_olen = size + 2; sc->sc_intr_owakeup = 1; sc->sc_intr_obuf.code = htole16(code); bcopy(data, sc->sc_intr_obuf.data, size); usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_WR]); while (sc->sc_intr_owakeup) { if (usb2_cv_timedwait(&sc->sc_intr_cv, &sc->sc_mtx, hz / 2)) { /* should not happen */ } if (usb2_config_td_is_gone(&sc->sc_config_td)) { sc->sc_intr_owakeup = 0; goto done; } } done: return; } static void zyd_cfg_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, uint16_t ilen, void *odata, uint16_t olen, uint16_t flags) { zyd_cfg_usb2_intr_write(sc, idata, code, ilen); if (flags & ZYD_CMD_FLAG_READ) { zyd_cfg_usb2_intr_read(sc, odata, olen); } } static void zyd_cfg_read16(struct zyd_softc *sc, uint16_t addr, uint16_t *value) { struct zyd_pair tmp[1]; addr = htole16(addr); zyd_cfg_cmd(sc, ZYD_CMD_IORD, &addr, sizeof(addr), tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); *value = le16toh(tmp[0].val); } static void zyd_cfg_read32(struct zyd_softc *sc, uint16_t addr, uint32_t *value) { struct zyd_pair tmp[2]; uint16_t regs[2]; regs[0] = ZYD_REG32_HI(addr); regs[1] = ZYD_REG32_LO(addr); regs[0] = htole16(regs[0]); regs[1] = htole16(regs[1]); zyd_cfg_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp), ZYD_CMD_FLAG_READ); *value = (le16toh(tmp[0].val) << 16) | le16toh(tmp[1].val); } static void zyd_cfg_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val) { struct zyd_pair pair[1]; pair[0].reg = htole16(reg); pair[0].val = htole16(val); zyd_cfg_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); } static void zyd_cfg_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val) { struct zyd_pair pair[2]; pair[0].reg = htole16(ZYD_REG32_HI(reg)); pair[0].val = htole16(val >> 16); pair[1].reg = htole16(ZYD_REG32_LO(reg)); pair[1].val = htole16(val & 0xffff); zyd_cfg_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0); } /*------------------------------------------------------------------------* * zyd_cfg_rfwrite - write RF registers *------------------------------------------------------------------------*/ static void zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value) { struct zyd_rf *rf = &sc->sc_rf; struct zyd_rfwrite req; uint16_t cr203; uint16_t i; zyd_cfg_read16(sc, ZYD_CR203, &cr203); cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA); req.code = htole16(2); req.width = htole16(rf->width); for (i = 0; i != rf->width; i++) { req.bit[i] = htole16(cr203); if (value & (1 << (rf->width - 1 - i))) req.bit[i] |= htole16(ZYD_RF_DATA); } zyd_cfg_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + (2 * rf->width), NULL, 0, 0); } static void zyd_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_RD]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_BULK_READ_STALL; usb2_transfer_start(xfer_other); } } static void zyd_bulk_read_callback_sub(struct usb2_xfer *xfer, struct zyd_ifq *mq, uint32_t offset, uint16_t len) { enum { ZYD_OVERHEAD = (ZYD_HW_PADDING + IEEE80211_CRC_LEN), }; struct zyd_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct zyd_plcphdr plcp; struct zyd_rx_stat stat; struct mbuf *m; if (len < ZYD_OVERHEAD) { DPRINTF("frame too " "short (length=%d)\n", len); ifp->if_ierrors++; return; } usb2_copy_out(xfer->frbuffers, offset, &plcp, sizeof(plcp)); usb2_copy_out(xfer->frbuffers, offset + len - sizeof(stat), &stat, sizeof(stat)); if (stat.flags & ZYD_RX_ERROR) { DPRINTF("RX status indicated " "error (0x%02x)\n", stat.flags); ifp->if_ierrors++; return; } /* compute actual frame length */ len -= ZYD_OVERHEAD; /* allocate a mbuf to store the frame */ if (len > MCLBYTES) { DPRINTF("too large frame, " "%u bytes\n", len); return; } else if (len > MHLEN) m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); else m = m_gethdr(M_DONTWAIT, MT_DATA); if (m == NULL) { DPRINTF("could not allocate rx mbuf\n"); ifp->if_ierrors++; return; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len; m->m_len = len; usb2_copy_out(xfer->frbuffers, offset + sizeof(plcp), m->m_data, len); if (bpf_peers_present(ifp->if_bpf)) { struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32)) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; /* XXX toss, no way to express errors */ if (stat.flags & ZYD_RX_DECRYPTERR) tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS; tap->wr_rate = zyd_plcp2ieee(plcp.signal, stat.flags & ZYD_RX_OFDM); tap->wr_antsignal = stat.rssi + -95; tap->wr_antnoise = -95; /* XXX */ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } if (sizeof(m->m_hdr.pad) > 0) { m->m_hdr.pad[0] = stat.rssi; /* XXX hack */ } _IF_ENQUEUE(mq, m); } static void zyd_bulk_read_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct zyd_rx_desc rx_desc; struct zyd_ifq mq = {NULL, NULL, 0}; struct mbuf *m; uint32_t offset; uint16_t len16; uint8_t x; uint8_t rssi; int8_t nf; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen < MAX(sizeof(rx_desc), ZYD_MIN_FRAGSZ)) { DPRINTFN(0, "xfer too short, %d bytes\n", xfer->actlen); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, xfer->actlen - sizeof(rx_desc), &rx_desc, sizeof(rx_desc)); if (UGETW(rx_desc.tag) == ZYD_TAG_MULTIFRAME) { offset = 0; DPRINTFN(4, "received multi-frame transfer, " "%u bytes\n", xfer->actlen); for (x = 0; x < ZYD_MAX_RXFRAMECNT; x++) { len16 = UGETW(rx_desc.len[x]); if ((len16 == 0) || (len16 > xfer->actlen)) { break; } zyd_bulk_read_callback_sub(xfer, &mq, offset, len16); /* * next frame is aligned on a 32-bit * boundary */ len16 = (len16 + 3) & ~3; offset += len16; if (len16 > xfer->actlen) { break; } xfer->actlen -= len16; } } else { DPRINTFN(4, "received single-frame transfer, " "%u bytes\n", xfer->actlen); zyd_bulk_read_callback_sub(xfer, &mq, 0, xfer->actlen); } case USB_ST_SETUP: tr_setup: DPRINTF("setup\n"); if (sc->sc_flags & ZYD_FLAG_BULK_READ_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); } else { xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); } /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (mq.ifq_head) { mtx_unlock(&sc->sc_mtx); while (1) { _IF_DEQUEUE(&mq, m); if (m == NULL) break; rssi = m->m_hdr.pad[0]; /* XXX hack */ rssi = (rssi > 63) ? 127 : 2 * rssi; nf = -95; /* XXX */ ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { if (ieee80211_input(ni, m, rssi, nf, 0)) { /* ignore */ } ieee80211_free_node(ni); } else { if (ieee80211_input_all(ic, m, rssi, nf, 0)) { /* ignore */ } } } mtx_lock(&sc->sc_mtx); } break; default: /* Error */ DPRINTF("frame error: %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_BULK_READ_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); } break; } } /*------------------------------------------------------------------------* * zyd_cfg_uploadfirmware * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static uint8_t zyd_cfg_uploadfirmware(struct zyd_softc *sc, const uint8_t *fw_ptr, uint32_t fw_len) { struct usb2_device_request req; uint16_t temp; uint16_t addr; uint8_t stat; DPRINTF("firmware %p size=%u\n", fw_ptr, fw_len); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADREQ; USETW(req.wIndex, 0); temp = 64; addr = ZYD_FIRMWARE_START_ADDR; while (fw_len > 0) { if (fw_len < 64) { temp = fw_len; } DPRINTF("firmware block: fw_len=%u\n", fw_len); USETW(req.wValue, addr); USETW(req.wLength, temp); zyd_cfg_usbrequest(sc, &req, USB_ADD_BYTES(fw_ptr, 0)); addr += (temp / 2); fw_len -= temp; fw_ptr += temp; } /* check whether the upload succeeded */ req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = ZYD_DOWNLOADSTS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, sizeof(stat)); zyd_cfg_usbrequest(sc, &req, &stat); return ((stat & 0x80) ? 1 : 0); } /* * Driver OS interface */ /* * Probe for a ZD1211-containing product */ static int zyd_probe(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb2_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX) { return (ENXIO); } return (usb2_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa)); } /* * Attach the interface. Allocate softc structures, do * setup and ethernet/BPF attach. */ static int zyd_attach(device_t dev) { struct usb2_attach_arg *uaa = device_get_ivars(dev); struct zyd_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; if (sc == NULL) { return (ENOMEM); } if (uaa->info.bcdDevice < 0x4330) { device_printf(dev, "device version mismatch: 0x%X " "(only >= 43.30 supported)\n", uaa->info.bcdDevice); return (EINVAL); } device_set_usb2_desc(dev); snprintf(sc->sc_name, sizeof(sc->sc_name), "%s", device_get_nameunit(dev)); sc->sc_unit = device_get_unit(dev); sc->sc_udev = uaa->device; sc->sc_mac_rev = USB_GET_DRIVER_INFO(uaa); mtx_init(&sc->sc_mtx, "zyd lock", MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); usb2_cv_init(&sc->sc_intr_cv, "IWAIT"); - usb2_callout_init_mtx(&sc->sc_watchdog, - &sc->sc_mtx, CALLOUT_RETURNUNLOCKED); + usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); /* * Endpoint 1 = Bulk out (512b @ high speed / 64b @ full speed) * Endpoint 2 = Bulk in (512b @ high speed / 64b @ full speed) * Endpoint 3 = Intr in (64b) * Endpoint 4 = Intr out @ high speed / bulk out @ full speed (64b) */ iface_index = ZYD_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, zyd_config, ZYD_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "could not allocate USB " "transfers: %s\n", usb2_errstr(error)); goto detach; } error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx, &zyd_end_of_commands, sizeof(struct usb2_config_td_cc), 16); if (error) { device_printf(dev, "could not setup config " "thread!\n"); goto detach; } mtx_lock(&sc->sc_mtx); /* start setup */ usb2_config_td_queue_command (&sc->sc_config_td, NULL, &zyd_cfg_first_time_setup, 0, 0); - /* start watchdog (will exit mutex) */ - zyd_watchdog(sc); - + mtx_unlock(&sc->sc_mtx); return (0); detach: zyd_detach(dev); return (ENXIO); } /* * Lock PHY registers */ static void zyd_cfg_lock_phy(struct zyd_softc *sc) { uint32_t temp; zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); temp &= ~ZYD_UNLOCK_PHY_REGS; zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); } /* * Unlock PHY registers */ static void zyd_cfg_unlock_phy(struct zyd_softc *sc) { uint32_t temp; zyd_cfg_read32(sc, ZYD_MAC_MISC, &temp); temp |= ZYD_UNLOCK_PHY_REGS; zyd_cfg_write32(sc, ZYD_MAC_MISC, temp); } static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t bintval) { /* XXX this is probably broken.. */ zyd_cfg_write32(sc, ZYD_CR_ATIM_WND_PERIOD, bintval - 2); zyd_cfg_write32(sc, ZYD_CR_PRE_TBTT, bintval - 1); zyd_cfg_write32(sc, ZYD_CR_BCN_INTERVAL, bintval); } /* * Get RF name */ static const char * zyd_rf_name(uint8_t type) { static const char *const zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", "PV2000", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; return (zyd_rfs[(type > 15) ? 0 : type]); } /* * RF driver: Init for RFMD chip */ static void zyd_cfg_rf_rfmd_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY; static const uint32_t rfini[] = ZYD_RFMD_RF; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init RFMD radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } /* * RF driver: Switch radio on/off for RFMD chip */ static void zyd_cfg_rf_rfmd_switch_radio(struct zyd_softc *sc, uint8_t on) { zyd_cfg_write16(sc, ZYD_CR10, on ? 0x89 : 0x15); zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x81); } /* * RF driver: Channel setting for RFMD chip */ static void zyd_cfg_rf_rfmd_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct { uint32_t r1, r2; } rfprog[] = ZYD_RFMD_CHANTABLE; zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); } /* * RF driver: Switch radio on/off for AL2230 chip */ static void zyd_cfg_rf_al2230_switch_radio(struct zyd_softc *sc, uint8_t on) { uint8_t on251 = (sc->sc_mac_rev == ZYD_ZD1211) ? 0x3f : 0x7f; zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_cfg_write16(sc, ZYD_CR251, on ? on251 : 0x2f); } /* * RF driver: Init for AL2230 chip */ static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; static const uint32_t rfini[] = ZYD_AL2230_RF; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init AL2230 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } static void zyd_cfg_rf_al2230_init_b(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; static const uint32_t rfini[] = ZYD_AL2230_RF_B; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init AL2230 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } /* * RF driver: Channel setting for AL2230 chip */ static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE; zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r3); zyd_cfg_write16(sc, ZYD_CR138, 0x28); zyd_cfg_write16(sc, ZYD_CR203, 0x06); } /* * AL7230B RF methods. */ static void zyd_cfg_rf_al7230b_switch_radio(struct zyd_softc *sc, uint8_t on) { zyd_cfg_write16(sc, ZYD_CR11, on ? 0x00 : 0x04); zyd_cfg_write16(sc, ZYD_CR251, on ? 0x3f : 0x2f); } static void zyd_cfg_rf_al7230b_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1; static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2; static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3; static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1; static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2; uint32_t i; /* for AL7230B, PHY and RF need to be initialized in "phases" */ /* init RF-dependent PHY registers, part one */ for (i = 0; i != INDEXES(phyini_1); i++) { zyd_cfg_write16(sc, phyini_1[i].reg, phyini_1[i].val); } /* init AL7230B radio, part one */ for (i = 0; i != INDEXES(rfini_1); i++) { zyd_cfg_rfwrite(sc, rfini_1[i]); } /* init RF-dependent PHY registers, part two */ for (i = 0; i != INDEXES(phyini_2); i++) { zyd_cfg_write16(sc, phyini_2[i].reg, phyini_2[i].val); } /* init AL7230B radio, part two */ for (i = 0; i != INDEXES(rfini_2); i++) { zyd_cfg_rfwrite(sc, rfini_2[i]); } /* init RF-dependent PHY registers, part three */ for (i = 0; i != INDEXES(phyini_3); i++) { zyd_cfg_write16(sc, phyini_3[i].reg, phyini_3[i].val); } } static void zyd_cfg_rf_al7230b_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct { uint32_t r1, r2; } rfprog[] = ZYD_AL7230B_CHANTABLE; static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL; uint32_t i; zyd_cfg_write16(sc, ZYD_CR240, 0x57); zyd_cfg_write16(sc, ZYD_CR251, 0x2f); for (i = 0; i != INDEXES(rfsc); i++) { zyd_cfg_rfwrite(sc, rfsc[i]); } zyd_cfg_write16(sc, ZYD_CR128, 0x14); zyd_cfg_write16(sc, ZYD_CR129, 0x12); zyd_cfg_write16(sc, ZYD_CR130, 0x10); zyd_cfg_write16(sc, ZYD_CR38, 0x38); zyd_cfg_write16(sc, ZYD_CR136, 0xdf); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); zyd_cfg_rfwrite(sc, 0x3c9000); zyd_cfg_write16(sc, ZYD_CR251, 0x3f); zyd_cfg_write16(sc, ZYD_CR203, 0x06); zyd_cfg_write16(sc, ZYD_CR240, 0x08); } /* * AL2210 RF methods. */ static void zyd_cfg_rf_al2210_switch_radio(struct zyd_softc *sc, uint8_t on) { } static void zyd_cfg_rf_al2210_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY; static const uint32_t rfini[] = ZYD_AL2210_RF; uint32_t tmp; uint32_t i; zyd_cfg_write32(sc, ZYD_CR18, 2); /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init AL2210 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } zyd_cfg_write16(sc, ZYD_CR47, 0x1e); zyd_cfg_read32(sc, ZYD_CR_RADIO_PD, &tmp); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); zyd_cfg_write32(sc, ZYD_CR18, 3); } static void zyd_cfg_rf_al2210_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE; uint32_t tmp; zyd_cfg_write32(sc, ZYD_CR18, 2); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); zyd_cfg_read32(sc, ZYD_CR_RADIO_PD, &tmp); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); /* actually set the channel */ zyd_cfg_rfwrite(sc, rfprog[channel - 1]); zyd_cfg_write32(sc, ZYD_CR18, 3); } /* * GCT RF methods. */ static void zyd_cfg_rf_gct_switch_radio(struct zyd_softc *sc, uint8_t on) { /* vendor driver does nothing for this RF chip */ } static void zyd_cfg_rf_gct_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY; static const uint32_t rfini[] = ZYD_GCT_RF; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } /* init cgt radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } static void zyd_cfg_rf_gct_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE; zyd_cfg_rfwrite(sc, 0x1c0000); zyd_cfg_rfwrite(sc, rfprog[channel - 1]); zyd_cfg_rfwrite(sc, 0x1c0008); } /* * Maxim RF methods. */ static void zyd_cfg_rf_maxim_switch_radio(struct zyd_softc *sc, uint8_t on) { /* vendor driver does nothing for this RF chip */ } static void zyd_cfg_rf_maxim_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; static const uint32_t rfini[] = ZYD_MAXIM_RF; uint16_t tmp; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); } static void zyd_cfg_rf_maxim_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY; static const uint32_t rfini[] = ZYD_MAXIM_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM_CHANTABLE; uint16_t tmp; uint32_t i; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); /* init maxim radio - skipping the two first values */ if (INDEXES(rfini) > 2) { for (i = 2; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); } /* * Maxim2 RF methods. */ static void zyd_cfg_rf_maxim2_switch_radio(struct zyd_softc *sc, uint8_t on) { /* vendor driver does nothing for this RF chip */ } static void zyd_cfg_rf_maxim2_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; uint16_t tmp; uint32_t i; /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* init maxim2 radio */ for (i = 0; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); } static void zyd_cfg_rf_maxim2_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY; static const uint32_t rfini[] = ZYD_MAXIM2_RF; static const struct { uint32_t r1, r2; } rfprog[] = ZYD_MAXIM2_CHANTABLE; uint16_t tmp; uint32_t i; /* * Do the same as we do when initializing it, except for the channel * values coming from the two channel tables. */ /* init RF-dependent PHY registers */ for (i = 0; i != INDEXES(phyini); i++) { zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp & ~(1 << 4)); /* first two values taken from the chantables */ zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); /* init maxim2 radio - skipping the two first values */ if (INDEXES(rfini) > 2) { for (i = 2; i != INDEXES(rfini); i++) { zyd_cfg_rfwrite(sc, rfini[i]); } } zyd_cfg_read16(sc, ZYD_CR203, &tmp); zyd_cfg_write16(sc, ZYD_CR203, tmp | (1 << 4)); } /* * Assign drivers and init the RF */ static uint8_t zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf) { ; /* fix for indent */ switch (sc->sc_rf_rev) { case ZYD_RF_RFMD: rf->cfg_init_hw = zyd_cfg_rf_rfmd_init; rf->cfg_switch_radio = zyd_cfg_rf_rfmd_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_rfmd_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2230: if (sc->sc_mac_rev == ZYD_ZD1211B) rf->cfg_init_hw = zyd_cfg_rf_al2230_init_b; else rf->cfg_init_hw = zyd_cfg_rf_al2230_init; rf->cfg_switch_radio = zyd_cfg_rf_al2230_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_al2230_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL7230B: rf->cfg_init_hw = zyd_cfg_rf_al7230b_init; rf->cfg_switch_radio = zyd_cfg_rf_al7230b_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_al7230b_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2210: rf->cfg_init_hw = zyd_cfg_rf_al2210_init; rf->cfg_switch_radio = zyd_cfg_rf_al2210_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_al2210_set_channel; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_GCT: rf->cfg_init_hw = zyd_cfg_rf_gct_init; rf->cfg_switch_radio = zyd_cfg_rf_gct_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_gct_set_channel; rf->width = 21; /* 21-bit RF values */ break; case ZYD_RF_MAXIM_NEW: rf->cfg_init_hw = zyd_cfg_rf_maxim_init; rf->cfg_switch_radio = zyd_cfg_rf_maxim_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_maxim_set_channel; rf->width = 18; /* 18-bit RF values */ break; case ZYD_RF_MAXIM_NEW2: rf->cfg_init_hw = zyd_cfg_rf_maxim2_init; rf->cfg_switch_radio = zyd_cfg_rf_maxim2_switch_radio; rf->cfg_set_channel = zyd_cfg_rf_maxim2_set_channel; rf->width = 18; /* 18-bit RF values */ break; default: DPRINTFN(0, "%s: Sorry, radio %s is not supported yet\n", sc->sc_name, zyd_rf_name(sc->sc_rf_rev)); return (1); } zyd_cfg_lock_phy(sc); (rf->cfg_init_hw) (sc, rf); zyd_cfg_unlock_phy(sc); return (0); /* success */ } /* * Init the hardware */ static uint8_t zyd_cfg_hw_init(struct zyd_softc *sc) { const struct zyd_phy_pair *phyp; uint32_t tmp; /* specify that the plug and play is finished */ zyd_cfg_write32(sc, ZYD_MAC_AFTER_PNP, 1); zyd_cfg_read16(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_firmware_base); DPRINTF("firmware base address=0x%04x\n", sc->sc_firmware_base); /* retrieve firmware revision number */ zyd_cfg_read16(sc, sc->sc_firmware_base + ZYD_FW_FIRMWARE_REV, &sc->sc_fw_rev); zyd_cfg_write32(sc, ZYD_CR_GPI_EN, 0); zyd_cfg_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); /* disable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); /* PHY init */ zyd_cfg_lock_phy(sc); phyp = (sc->sc_mac_rev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy; for (; phyp->reg != 0; phyp++) { zyd_cfg_write16(sc, phyp->reg, phyp->val); } if (sc->sc_fix_cr157) { zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); zyd_cfg_write32(sc, ZYD_CR157, tmp >> 8); } zyd_cfg_unlock_phy(sc); /* HMAC init */ zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000020); zyd_cfg_write32(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); if (sc->sc_mac_rev == ZYD_ZD1211) { zyd_cfg_write32(sc, ZYD_MAC_RETRY, 0x00000002); } else { zyd_cfg_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); zyd_cfg_write32(sc, ZYD_MACB_TXOP, 0x01800824); } zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_GHTBL, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_GHTBH, 0x80000000); zyd_cfg_write32(sc, ZYD_MAC_MISC, 0x000000a4); zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f); zyd_cfg_write32(sc, ZYD_MAC_BCNCFG, 0x00f00401); zyd_cfg_write32(sc, ZYD_MAC_PHY_DELAY2, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000080); zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); zyd_cfg_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0547c032); zyd_cfg_write32(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); zyd_cfg_write32(sc, ZYD_CR_PS_CTRL, 0x10000000); zyd_cfg_write32(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); zyd_cfg_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); zyd_cfg_write32(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); /* init beacon interval to 100ms */ zyd_cfg_set_beacon_interval(sc, 100); return (0); /* success */ } /* * Read information from EEPROM */ static void zyd_cfg_read_eeprom(struct zyd_softc *sc) { uint32_t tmp; uint16_t i; uint16_t val; /* read MAC address */ zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P1, &tmp); sc->sc_myaddr[0] = tmp & 0xff; sc->sc_myaddr[1] = tmp >> 8; sc->sc_myaddr[2] = tmp >> 16; sc->sc_myaddr[3] = tmp >> 24; zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P2, &tmp); sc->sc_myaddr[4] = tmp & 0xff; sc->sc_myaddr[5] = tmp >> 8; zyd_cfg_read32(sc, ZYD_EEPROM_POD, &tmp); sc->sc_rf_rev = tmp & 0x0f; sc->sc_fix_cr47 = (tmp >> 8) & 0x01; sc->sc_fix_cr157 = (tmp >> 13) & 0x01; sc->sc_pa_rev = (tmp >> 16) & 0x0f; /* read regulatory domain (currently unused) */ zyd_cfg_read32(sc, ZYD_EEPROM_SUBID, &tmp); sc->sc_regdomain = tmp >> 16; DPRINTF("regulatory domain %x\n", sc->sc_regdomain); /* read Tx power calibration tables */ for (i = 0; i < 7; i++) { zyd_cfg_read16(sc, ZYD_EEPROM_PWR_CAL + i, &val); sc->sc_pwr_cal[(i * 2)] = val >> 8; sc->sc_pwr_cal[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_PWR_INT + i, &val); sc->sc_pwr_int[(i * 2)] = val >> 8; sc->sc_pwr_int[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_36M_CAL + i, &val); sc->sc_ofdm36_cal[(i * 2)] = val >> 8; sc->sc_ofdm36_cal[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_48M_CAL + i, &val); sc->sc_ofdm48_cal[(i * 2)] = val >> 8; sc->sc_ofdm48_cal[(i * 2) + 1] = val & 0xff; zyd_cfg_read16(sc, ZYD_EEPROM_54M_CAL + i, &val); sc->sc_ofdm54_cal[(i * 2)] = val >> 8; sc->sc_ofdm54_cal[(i * 2) + 1] = val & 0xff; } } static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr) { uint32_t tmp; tmp = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; zyd_cfg_write32(sc, ZYD_MAC_MACADRL, tmp); tmp = (addr[5] << 8) | addr[4]; zyd_cfg_write32(sc, ZYD_MAC_MACADRH, tmp); } /* * Switch radio on/off */ static void zyd_cfg_switch_radio(struct zyd_softc *sc, uint8_t onoff) { zyd_cfg_lock_phy(sc); (sc->sc_rf.cfg_switch_radio) (sc, onoff); zyd_cfg_unlock_phy(sc); } /* * Set BSSID */ static void zyd_cfg_set_bssid(struct zyd_softc *sc, uint8_t *addr) { uint32_t tmp; tmp = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; zyd_cfg_write32(sc, ZYD_MAC_BSSADRL, tmp); tmp = (addr[5] << 8) | addr[4]; zyd_cfg_write32(sc, ZYD_MAC_BSSADRH, tmp); } /* * Complete the attach process */ static void zyd_cfg_first_time_setup(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct usb2_config_descriptor *cd; struct ieee80211com *ic; struct ifnet *ifp; const uint8_t *fw_ptr; uint32_t fw_len; uint8_t bands; usb2_error_t err; /* setup RX tap header */ sc->sc_rxtap_len = sizeof(sc->sc_rxtap); sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT); /* setup TX tap header */ sc->sc_txtap_len = sizeof(sc->sc_txtap); sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT); if (sc->sc_mac_rev == ZYD_ZD1211) { fw_ptr = zd1211_firmware; fw_len = sizeof(zd1211_firmware); } else { fw_ptr = zd1211b_firmware; fw_len = sizeof(zd1211b_firmware); } if (zyd_cfg_uploadfirmware(sc, fw_ptr, fw_len)) { DPRINTFN(0, "%s: could not " "upload firmware!\n", sc->sc_name); return; } cd = usb2_get_config_descriptor(sc->sc_udev); /* reset device */ err = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) { DPRINTF("reset failed (ignored)\n"); } /* Read MAC and other stuff rom EEPROM */ zyd_cfg_read_eeprom(sc); /* Init hardware */ if (zyd_cfg_hw_init(sc)) { DPRINTFN(0, "%s: HW init failed!\n", sc->sc_name); return; } /* Now init the RF chip */ if (zyd_cfg_rf_init_hw(sc, &sc->sc_rf)) { DPRINTFN(0, "%s: RF init failed!\n", sc->sc_name); return; } printf("%s: HMAC ZD1211%s, FW %02x.%02x, RF %s, PA %x, address %02x:%02x:%02x:%02x:%02x:%02x\n", sc->sc_name, (sc->sc_mac_rev == ZYD_ZD1211) ? "" : "B", sc->sc_fw_rev >> 8, sc->sc_fw_rev & 0xff, zyd_rf_name(sc->sc_rf_rev), sc->sc_pa_rev, sc->sc_myaddr[0], sc->sc_myaddr[1], sc->sc_myaddr[2], sc->sc_myaddr[3], sc->sc_myaddr[4], sc->sc_myaddr[5]); mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_IEEE80211); mtx_lock(&sc->sc_mtx); if (ifp == NULL) { DPRINTFN(0, "%s: could not if_alloc()!\n", sc->sc_name); goto done; } sc->sc_evilhack = ifp; sc->sc_ifp = ifp; ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "zyd", sc->sc_unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = &zyd_init_cb; ifp->if_ioctl = &zyd_ioctl_cb; ifp->if_start = &zyd_start_cb; ifp->if_watchdog = NULL; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr)); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; ic->ic_opmode = IEEE80211_M_STA; /* Set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); ieee80211_init_channels(ic, NULL, &bands); mtx_unlock(&sc->sc_mtx); ieee80211_ifattach(ic); mtx_lock(&sc->sc_mtx); ic->ic_node_alloc = &zyd_node_alloc_cb; ic->ic_raw_xmit = &zyd_raw_xmit_cb; ic->ic_newassoc = &zyd_newassoc_cb; ic->ic_scan_start = &zyd_scan_start_cb; ic->ic_scan_end = &zyd_scan_end_cb; ic->ic_set_channel = &zyd_set_channel_cb; ic->ic_vap_create = &zyd_vap_create; ic->ic_vap_delete = &zyd_vap_delete; ic->ic_update_mcast = &zyd_update_mcast_cb; ic->ic_update_promisc = &zyd_update_promisc_cb; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); mtx_unlock(&sc->sc_mtx); bpfattach(ifp, DLT_IEEE802_11_RADIO, sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap)); mtx_lock(&sc->sc_mtx); if (bootverbose) { ieee80211_announce(ic); } usb2_transfer_start(sc->sc_xfer[ZYD_TR_INTR_DT_RD]); done: return; } /* * Detach device */ static int zyd_detach(device_t dev) { struct zyd_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; usb2_config_td_drain(&sc->sc_config_td); mtx_lock(&sc->sc_mtx); usb2_callout_stop(&sc->sc_watchdog); zyd_cfg_pre_stop(sc, NULL, 0); ifp = sc->sc_ifp; ic = ifp->if_l2com; mtx_unlock(&sc->sc_mtx); /* stop all USB transfers first */ usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER); /* get rid of any late children */ bus_generic_detach(dev); if (ifp) { bpfdetach(ifp); ieee80211_ifdetach(ic); if_free(ifp); } usb2_config_td_unsetup(&sc->sc_config_td); usb2_callout_drain(&sc->sc_watchdog); usb2_cv_destroy(&sc->sc_intr_cv); mtx_destroy(&sc->sc_mtx); return (0); } static void zyd_cfg_newstate(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct zyd_vap *uvp = ZYD_VAP(vap); enum ieee80211_state ostate; enum ieee80211_state nstate; int arg; ostate = vap->iv_state; nstate = sc->sc_ns_state; arg = sc->sc_ns_arg; switch (nstate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: zyd_cfg_set_run(sc, cc); break; default: break; } mtx_unlock(&sc->sc_mtx); IEEE80211_LOCK(ic); uvp->newstate(vap, nstate, arg); if (vap->iv_newstate_cb != NULL) vap->iv_newstate_cb(vap, nstate, arg); IEEE80211_UNLOCK(ic); mtx_lock(&sc->sc_mtx); } static void zyd_cfg_set_run(struct zyd_softc *sc, struct usb2_config_td_cc *cc) { zyd_cfg_set_chan(sc, cc, 0); if (cc->ic_opmode != IEEE80211_M_MONITOR) { /* turn link LED on */ zyd_cfg_set_led(sc, ZYD_LED1, 1); /* make data LED blink upon Tx */ zyd_cfg_write32(sc, sc->sc_firmware_base + ZYD_FW_LINK_STATUS, 1); zyd_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } if (cc->iv_bss.fixed_rate_none) { /* enable automatic rate adaptation */ zyd_cfg_amrr_start(sc); } } static int zyd_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct zyd_vap *uvp = ZYD_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct zyd_softc *sc = ic->ic_ifp->if_softc; DPRINTF("setting new state: %d\n", nstate); mtx_lock(&sc->sc_mtx); if (usb2_config_td_is_gone(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* Special case which happens at detach. */ if (nstate == IEEE80211_S_INIT) { (uvp->newstate) (vap, nstate, arg); } return (0); /* nothing to do */ } /* store next state */ sc->sc_ns_state = nstate; sc->sc_ns_arg = arg; /* stop timers */ sc->sc_amrr_timer = 0; /* * USB configuration can only be done from the USB configuration * thread: */ usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, &zyd_cfg_newstate, 0, 0); mtx_unlock(&sc->sc_mtx); return EINPROGRESS; } static void zyd_cfg_update_promisc(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t low; uint32_t high; if ((cc->ic_opmode == IEEE80211_M_MONITOR) || (cc->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { low = 0xffffffff; high = 0xffffffff; } else { low = cc->zyd_multi_low; high = cc->zyd_multi_high; } /* reprogram multicast global hash table */ zyd_cfg_write32(sc, ZYD_MAC_GHTBL, low); zyd_cfg_write32(sc, ZYD_MAC_GHTBH, high); } /* * Rate-to-bit-converter (Field "rate" in zyd_controlsetformat) */ static uint8_t zyd_plcp_signal(uint8_t rate) { ; /* fix for indent */ switch (rate) { /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return (0x0); case 4: return (0x1); case 11: return (0x2); case 22: return (0x3); /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return (0xb); case 18: return (0xf); case 24: return (0xa); case 36: return (0xe); case 48: return (0x9); case 72: return (0xd); case 96: return (0x8); case 108: return (0xc); /* XXX unsupported/unknown rate */ default: return (0xff); } } static void zyd_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func) { struct zyd_softc *sc = ic->ic_ifp->if_softc; mtx_lock(&sc->sc_mtx); sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, func, 0, 0); mtx_unlock(&sc->sc_mtx); } static void zyd_scan_start_cb(struct ieee80211com *ic) { zyd_std_command(ic, &zyd_cfg_scan_start); } static void zyd_scan_end_cb(struct ieee80211com *ic) { zyd_std_command(ic, &zyd_cfg_scan_end); } static void zyd_set_channel_cb(struct ieee80211com *ic) { zyd_std_command(ic, &zyd_cfg_set_chan); } /*========================================================================* * configure sub-routines, zyd_cfg_xxx *========================================================================*/ static void zyd_cfg_scan_start(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { zyd_cfg_set_bssid(sc, cc->if_broadcastaddr); } static void zyd_cfg_scan_end(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { zyd_cfg_set_bssid(sc, cc->iv_bss.ni_bssid); } static void zyd_cfg_set_chan(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t chan; uint32_t tmp; chan = cc->ic_curchan.chan_to_ieee; DPRINTF("Will try %d\n", chan); if ((chan == 0) || (chan == IEEE80211_CHAN_ANY)) { DPRINTF("0 or ANY, exiting\n"); return; } zyd_cfg_lock_phy(sc); (sc->sc_rf.cfg_set_channel) (sc, &sc->sc_rf, chan); /* update Tx power */ zyd_cfg_write16(sc, ZYD_CR31, sc->sc_pwr_int[chan - 1]); if (sc->sc_mac_rev == ZYD_ZD1211B) { zyd_cfg_write16(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR68, sc->sc_pwr_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR69, 0x28); zyd_cfg_write16(sc, ZYD_CR69, 0x2a); } if (sc->sc_fix_cr47) { /* set CCK baseband gain from EEPROM */ zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); zyd_cfg_write16(sc, ZYD_CR47, tmp & 0xff); } zyd_cfg_write32(sc, ZYD_CR_CONFIG_PHILIPS, 0); zyd_cfg_unlock_phy(sc); sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = htole16(cc->ic_curchan.ic_freq); sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = htole16(cc->ic_flags); } /* * Interface: init */ /* immediate configuration */ static void zyd_cfg_pre_init(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; zyd_cfg_pre_stop(sc, cc, 0); ifp->if_drv_flags |= IFF_DRV_RUNNING; sc->sc_flags |= ZYD_FLAG_HL_READY; IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); } /* delayed configuration */ static void zyd_cfg_init(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { zyd_cfg_stop(sc, cc, 0); /* Do initial setup */ zyd_cfg_set_mac_addr(sc, cc->ic_myaddr); zyd_cfg_write32(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER); /* promiscuous mode */ zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, (cc->ic_opmode == IEEE80211_M_MONITOR) ? 1 : 0); /* multicast setup */ zyd_cfg_update_promisc(sc, cc, refcount); zyd_cfg_set_rxfilter(sc, cc, refcount); /* switch radio transmitter ON */ zyd_cfg_switch_radio(sc, 1); /* XXX wrong, can't set here */ /* set basic rates */ if (cc->ic_curmode == IEEE80211_MODE_11B) zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x0003); else if (cc->ic_curmode == IEEE80211_MODE_11A) zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x1500); else /* assumes 802.11b/g */ zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x000f); /* set mandatory rates */ if (cc->ic_curmode == IEEE80211_MODE_11B) zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x000f); else if (cc->ic_curmode == IEEE80211_MODE_11A) zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x1500); else /* assumes 802.11b/g */ zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x150f); /* set default BSS channel */ zyd_cfg_set_chan(sc, cc, 0); /* enable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK); /* make sure that the transfers get started */ sc->sc_flags |= ( ZYD_FLAG_BULK_READ_STALL | ZYD_FLAG_BULK_WRITE_STALL | ZYD_FLAG_LL_READY); if ((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY)) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* * start the USB transfers, if not already started: */ usb2_transfer_start(sc->sc_xfer[1]); usb2_transfer_start(sc->sc_xfer[0]); /* * start IEEE802.11 layer */ mtx_unlock(&sc->sc_mtx); ieee80211_start_all(ic); mtx_lock(&sc->sc_mtx); } } /* immediate configuration */ static void zyd_cfg_pre_stop(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ifnet *ifp = sc->sc_ifp; if (cc) { /* copy the needed configuration */ zyd_config_copy(sc, cc, refcount); } if (ifp) { /* clear flags */ ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } sc->sc_flags &= ~(ZYD_FLAG_HL_READY | ZYD_FLAG_LL_READY); /* * stop all the transfers, if not already stopped: */ usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_DT_RD]); usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); usb2_transfer_stop(sc->sc_xfer[ZYD_TR_BULK_CS_RD]); /* clean up transmission */ zyd_tx_clean_queue(sc); } /* delayed configuration */ static void zyd_cfg_stop(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { /* switch radio transmitter OFF */ zyd_cfg_switch_radio(sc, 0); /* disable Rx */ zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0); /* disable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); } static void zyd_update_mcast_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, &zyd_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); } static void zyd_update_promisc_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_config_copy, &zyd_cfg_update_promisc, 0, 0); mtx_unlock(&sc->sc_mtx); } static void zyd_cfg_set_rxfilter(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { uint32_t rxfilter; switch (cc->ic_opmode) { case IEEE80211_M_STA: rxfilter = ZYD_FILTER_BSS; break; case IEEE80211_M_IBSS: case IEEE80211_M_HOSTAP: rxfilter = ZYD_FILTER_HOSTAP; break; case IEEE80211_M_MONITOR: rxfilter = ZYD_FILTER_MONITOR; break; default: /* should not get there */ return; } zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, rxfilter); } static void zyd_cfg_set_led(struct zyd_softc *sc, uint32_t which, uint8_t on) { uint32_t tmp; zyd_cfg_read32(sc, ZYD_MAC_TX_PE_CONTROL, &tmp); if (on) tmp |= which; else tmp &= ~which; zyd_cfg_write32(sc, ZYD_MAC_TX_PE_CONTROL, tmp); } static void zyd_start_cb(struct ifnet *ifp) { struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); } static void zyd_bulk_write_clear_stall_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct usb2_xfer *xfer_other = sc->sc_xfer[ZYD_TR_BULK_DT_WR]; if (usb2_clear_stall_callback(xfer, xfer_other)) { DPRINTF("stall cleared\n"); sc->sc_flags &= ~ZYD_FLAG_BULK_WRITE_STALL; usb2_transfer_start(xfer_other); } } /* * We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that * should be freed, when "zyd_setup_desc_and_tx" is called. */ static void zyd_setup_desc_and_tx(struct zyd_softc *sc, struct mbuf *m, uint16_t rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct mbuf *mm; enum ieee80211_phytype phytype; uint16_t len; uint16_t totlen; uint16_t pktlen; uint8_t remainder; if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) { /* free packet */ zyd_tx_freem(m); ifp->if_oerrors++; return; } if (!((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY))) { /* free packet */ zyd_tx_freem(m); ifp->if_oerrors++; return; } if (rate < 2) { DPRINTF("rate < 2!\n"); /* avoid division by zero */ rate = 2; } ic->ic_lastdata = ticks; if (bpf_peers_present(ifp->if_bpf)) { struct zyd_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m); } len = m->m_pkthdr.len; totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; phytype = ieee80211_rate2phytype(sc->sc_rates, rate); sc->sc_tx_desc.len = htole16(totlen); sc->sc_tx_desc.phy = zyd_plcp_signal(rate); if (phytype == IEEE80211_T_OFDM) { sc->sc_tx_desc.phy |= ZYD_TX_PHY_OFDM; if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) sc->sc_tx_desc.phy |= ZYD_TX_PHY_5GHZ; } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) sc->sc_tx_desc.phy |= ZYD_TX_PHY_SHPREAMBLE; /* actual transmit length (XXX why +10?) */ pktlen = sizeof(struct zyd_tx_desc) + 10; if (sc->sc_mac_rev == ZYD_ZD1211) pktlen += totlen; sc->sc_tx_desc.pktlen = htole16(pktlen); sc->sc_tx_desc.plcp_length = ((16 * totlen) + rate - 1) / rate; sc->sc_tx_desc.plcp_service = 0; if (rate == 22) { remainder = (16 * totlen) % 22; if ((remainder != 0) && (remainder < 7)) sc->sc_tx_desc.plcp_service |= ZYD_PLCP_LENGEXT; } if (sizeof(sc->sc_tx_desc) > MHLEN) { DPRINTF("No room for header structure!\n"); zyd_tx_freem(m); return; } mm = m_gethdr(M_NOWAIT, MT_DATA); if (mm == NULL) { DPRINTF("Could not allocate header mbuf!\n"); zyd_tx_freem(m); return; } bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc)); mm->m_len = sizeof(sc->sc_tx_desc); mm->m_next = m; mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len; mm->m_pkthdr.rcvif = NULL; /* start write transfer, if not started */ _IF_ENQUEUE(&sc->sc_tx_queue, mm); usb2_transfer_start(sc->sc_xfer[0]); } static void zyd_bulk_write_callback(struct usb2_xfer *xfer) { struct zyd_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct mbuf *m; uint16_t temp_len; DPRINTF("\n"); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete\n"); ifp->if_opackets++; case USB_ST_SETUP: if (sc->sc_flags & ZYD_FLAG_BULK_WRITE_STALL) { usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); DPRINTFN(11, "write stalled\n"); break; } if (sc->sc_flags & ZYD_FLAG_WAIT_COMMAND) { /* * don't send anything while a command is pending ! */ DPRINTFN(11, "wait command\n"); break; } zyd_fill_write_queue(sc); _IF_DEQUEUE(&sc->sc_tx_queue, m); if (m) { if (m->m_pkthdr.len > ZYD_MAX_TXBUFSZ) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = ZYD_MAX_TXBUFSZ; } usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len); /* get transfer length */ temp_len = m->m_pkthdr.len; DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, temp_len); xfer->frlengths[0] = temp_len; usb2_start_hardware(xfer); /* free mbuf and node */ zyd_tx_freem(m); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ sc->sc_flags |= ZYD_FLAG_BULK_WRITE_STALL; usb2_transfer_start(sc->sc_xfer[ZYD_TR_BULK_CS_WR]); } ifp->if_oerrors++; break; } } static void zyd_init_cb(void *arg) { struct zyd_softc *sc = arg; mtx_lock(&sc->sc_mtx); usb2_config_td_queue_command (&sc->sc_config_td, &zyd_cfg_pre_init, &zyd_cfg_init, 0, 0); mtx_unlock(&sc->sc_mtx); } static int zyd_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data) { struct zyd_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; int error; switch (cmd) { case SIOCSIFFLAGS: mtx_lock(&sc->sc_mtx); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { usb2_config_td_queue_command (&sc->sc_config_td, &zyd_cfg_pre_init, &zyd_cfg_init, 0, 0); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { usb2_config_td_queue_command (&sc->sc_config_td, &zyd_cfg_pre_stop, &zyd_cfg_stop, 0, 0); } } mtx_unlock(&sc->sc_mtx); error = 0; break; case SIOCGIFMEDIA: case SIOCADDMULTI: case SIOCDELMULTI: error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void zyd_watchdog(void *arg) { struct zyd_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); if (sc->sc_amrr_timer) { usb2_config_td_queue_command (&sc->sc_config_td, NULL, &zyd_cfg_amrr_timeout, 0, 0); } usb2_callout_reset(&sc->sc_watchdog, hz, &zyd_watchdog, sc); - - mtx_unlock(&sc->sc_mtx); } static void zyd_config_copy_chan(struct zyd_config_copy_chan *cc, struct ieee80211com *ic, struct ieee80211_channel *c) { if (!c) return; cc->chan_to_ieee = ieee80211_chan2ieee(ic, c); if (c != IEEE80211_CHAN_ANYC) { cc->chan_to_mode = ieee80211_chan2mode(c); cc->ic_freq = c->ic_freq; if (IEEE80211_IS_CHAN_B(c)) cc->chan_is_b = 1; if (IEEE80211_IS_CHAN_A(c)) cc->chan_is_a = 1; if (IEEE80211_IS_CHAN_2GHZ(c)) cc->chan_is_2ghz = 1; if (IEEE80211_IS_CHAN_5GHZ(c)) cc->chan_is_5ghz = 1; if (IEEE80211_IS_CHAN_ANYG(c)) cc->chan_is_g = 1; } } static void zyd_config_copy(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { const struct ieee80211_txparam *tp; struct ieee80211vap *vap; struct ifmultiaddr *ifma; struct ieee80211_node *ni; struct ieee80211com *ic; struct ifnet *ifp; bzero(cc, sizeof(*cc)); ifp = sc->sc_ifp; if (ifp) { cc->if_flags = ifp->if_flags; bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr, sizeof(cc->if_broadcastaddr)); cc->zyd_multi_low = 0x00000000; cc->zyd_multi_high = 0x80000000; IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { uint8_t v; if (ifma->ifma_addr->sa_family != AF_LINK) continue; v = ((uint8_t *)LLADDR((struct sockaddr_dl *) ifma->ifma_addr))[5] >> 2; if (v < 32) cc->zyd_multi_low |= 1 << v; else cc->zyd_multi_high |= 1 << (v - 32); } IF_ADDR_UNLOCK(ifp); ic = ifp->if_l2com; if (ic) { zyd_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan); zyd_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan); vap = TAILQ_FIRST(&ic->ic_vaps); if (vap) { ni = vap->iv_bss; if (ni) { cc->iv_bss.ni_intval = ni->ni_intval; bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid, sizeof(cc->iv_bss.ni_bssid)); } tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { cc->iv_bss.fixed_rate_none = 1; } } cc->ic_opmode = ic->ic_opmode; cc->ic_flags = ic->ic_flags; cc->ic_txpowlimit = ic->ic_txpowlimit; cc->ic_curmode = ic->ic_curmode; bcopy(ic->ic_myaddr, cc->ic_myaddr, sizeof(cc->ic_myaddr)); } } sc->sc_flags |= ZYD_FLAG_WAIT_COMMAND; } static void zyd_end_of_commands(struct zyd_softc *sc) { sc->sc_flags &= ~ZYD_FLAG_WAIT_COMMAND; /* start write transfer, if not started */ usb2_transfer_start(sc->sc_xfer[0]); } static void zyd_newassoc_cb(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); } static void zyd_cfg_amrr_timeout(struct zyd_softc *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = zyd_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } if ((sc->sc_flags & ZYD_FLAG_LL_READY) && (sc->sc_flags & ZYD_FLAG_HL_READY)) { if (sc->sc_amrr_timer) { if (ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn)) { /* ignore */ } } } } static void zyd_cfg_amrr_start(struct zyd_softc *sc) { struct ieee80211vap *vap; struct ieee80211_node *ni; vap = zyd_get_vap(sc); if (vap == NULL) { return; } ni = vap->iv_bss; if (ni == NULL) { return; } /* init AMRR */ ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni); /* enable AMRR timer */ sc->sc_amrr_timer = 1; } static struct ieee80211vap * zyd_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct zyd_vap *zvp; struct ieee80211vap *vap; struct zyd_softc *sc = ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { mtx_unlock(&sc->sc_mtx); /* config thread is gone */ return (NULL); } mtx_unlock(&sc->sc_mtx); if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; zvp = (struct zyd_vap *)malloc(sizeof(struct zyd_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (zvp == NULL) return NULL; vap = &zvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ zvp->newstate = vap->iv_newstate; vap->iv_newstate = &zyd_newstate_cb; ieee80211_amrr_init(&zvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */ ); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return (vap); } static void zyd_vap_delete(struct ieee80211vap *vap) { struct zyd_vap *zvp = ZYD_VAP(vap); struct zyd_softc *sc = vap->iv_ic->ic_ifp->if_softc; /* Need to sync with config thread: */ mtx_lock(&sc->sc_mtx); if (usb2_config_td_sync(&sc->sc_config_td)) { /* ignore */ } mtx_unlock(&sc->sc_mtx); ieee80211_amrr_cleanup(&zvp->amrr); ieee80211_vap_detach(vap); free(zvp, M_80211_VAP); } /* ARGUSED */ static struct ieee80211_node * zyd_node_alloc_cb(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct zyd_node *zn; zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO); return ((zn != NULL) ? &zn->ni : NULL); } static void zyd_fill_write_queue(struct zyd_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211_node *ni; struct mbuf *m; /* * We only fill up half of the queue with data frames. The rest is * reserved for other kinds of frames. */ while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (void *)(m->m_pkthdr.rcvif); m = ieee80211_encap(ni, m); if (m == NULL) { ieee80211_free_node(ni); continue; } zyd_tx_data(sc, m, ni); } } static void zyd_tx_clean_queue(struct zyd_softc *sc) { struct mbuf *m; for (;;) { _IF_DEQUEUE(&sc->sc_tx_queue, m); if (!m) { break; } zyd_tx_freem(m); } } static void zyd_tx_freem(struct mbuf *m) { struct ieee80211_node *ni; while (m) { ni = (void *)(m->m_pkthdr.rcvif); if (!ni) { m = m_free(m); continue; } if (m->m_flags & M_TXCB) { ieee80211_process_callback(ni, m, 0); } m_freem(m); ieee80211_free_node(ni); break; } } static void zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint16_t totlen; uint16_t rate; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; rate = tp->mgmtrate; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } wh = mtod(m, struct ieee80211_frame *); } /* fill Tx descriptor */ sc->sc_tx_desc.flags = ZYD_TX_FLAG_BACKOFF; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* get total length */ totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } } else sc->sc_tx_desc.flags |= ZYD_TX_FLAG_MULTICAST; if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); m->m_pkthdr.rcvif = (void *)ni; zyd_setup_desc_and_tx(sc, m, rate); } static void zyd_tx_data(struct zyd_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ieee80211_frame *wh; struct ieee80211_key *k; uint16_t rate; wh = mtod(m, struct ieee80211_frame *); sc->sc_tx_desc.flags = ZYD_TX_FLAG_BACKOFF; tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { rate = tp->mcastrate; sc->sc_tx_desc.flags |= ZYD_TX_FLAG_MULTICAST; } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { rate = tp->ucastrate; } else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); ieee80211_free_node(ni); return; } /* packet header may have moved, reset our local pointer */ wh = mtod(m, struct ieee80211_frame *); } /* fill Tx descriptor */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint16_t totlen; totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (totlen > vap->iv_rtsthreshold) { sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } else if (ZYD_RATE_IS_OFDM(rate) && (ic->ic_flags & IEEE80211_F_USEPROT)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_CTS_TO_SELF; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_RTS; } } if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL)) sc->sc_tx_desc.flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL); m->m_pkthdr.rcvif = (void *)ni; zyd_setup_desc_and_tx(sc, m, rate); } static int zyd_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct zyd_softc *sc = ifp->if_softc; mtx_lock(&sc->sc_mtx); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ zyd_tx_mgt(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ zyd_tx_mgt(sc, m, ni); /* XXX zyd_tx_raw() */ } mtx_unlock(&sc->sc_mtx); return (0); } static struct ieee80211vap * zyd_get_vap(struct zyd_softc *sc) { struct ifnet *ifp; struct ieee80211com *ic; if (sc == NULL) { return NULL; } ifp = sc->sc_ifp; if (ifp == NULL) { return NULL; } ic = ifp->if_l2com; if (ic == NULL) { return NULL; } return TAILQ_FIRST(&ic->ic_vaps); } Index: projects/cambria/sys/kern/sched_ule.c =================================================================== --- projects/cambria/sys/kern/sched_ule.c (revision 186459) +++ projects/cambria/sys/kern/sched_ule.c (revision 186460) @@ -1,2723 +1,2723 @@ /*- * Copyright (c) 2002-2007, Jeffrey Roberson * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /* * This file implements the ULE scheduler. ULE supports independent CPU * run queues and fine grain locking. It has superior interactive * performance under load even on uni-processor systems. * * etymology: * ULE is the last three letters in schedule. It owes its name to a * generic user created for a scheduling system by Paul Mikesell at * Isilon Systems and a general lack of creativity on the part of the author. */ #include __FBSDID("$FreeBSD$"); #include "opt_hwpmc_hooks.h" #include "opt_kdtrace.h" #include "opt_sched.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #include #endif #ifdef HWPMC_HOOKS #include #endif #ifdef KDTRACE_HOOKS #include int dtrace_vtime_active; dtrace_vtime_switch_func_t dtrace_vtime_switch_func; #endif #include #include #if defined(__sparc64__) || defined(__mips__) #error "This architecture is not currently compatible with ULE" #endif #define KTR_ULE 0 /* * Thread scheduler specific section. All fields are protected * by the thread lock. */ struct td_sched { struct runq *ts_runq; /* Run-queue we're queued on. */ short ts_flags; /* TSF_* flags. */ u_char ts_cpu; /* CPU that we have affinity for. */ int ts_rltick; /* Real last tick, for affinity. */ int ts_slice; /* Ticks of slice remaining. */ u_int ts_slptime; /* Number of ticks we vol. slept */ u_int ts_runtime; /* Number of ticks we were running */ int ts_ltick; /* Last tick that we were running on */ int ts_ftick; /* First tick that we were running on */ int ts_ticks; /* Tick count */ }; /* flags kept in ts_flags */ #define TSF_BOUND 0x0001 /* Thread can not migrate. */ #define TSF_XFERABLE 0x0002 /* Thread was added as transferable. */ static struct td_sched td_sched0; #define THREAD_CAN_MIGRATE(td) ((td)->td_pinned == 0) #define THREAD_CAN_SCHED(td, cpu) \ CPU_ISSET((cpu), &(td)->td_cpuset->cs_mask) /* * Cpu percentage computation macros and defines. * * SCHED_TICK_SECS: Number of seconds to average the cpu usage across. * SCHED_TICK_TARG: Number of hz ticks to average the cpu usage across. * SCHED_TICK_MAX: Maximum number of ticks before scaling back. * SCHED_TICK_SHIFT: Shift factor to avoid rounding away results. * SCHED_TICK_HZ: Compute the number of hz ticks for a given ticks count. * SCHED_TICK_TOTAL: Gives the amount of time we've been recording ticks. */ #define SCHED_TICK_SECS 10 #define SCHED_TICK_TARG (hz * SCHED_TICK_SECS) #define SCHED_TICK_MAX (SCHED_TICK_TARG + hz) #define SCHED_TICK_SHIFT 10 #define SCHED_TICK_HZ(ts) ((ts)->ts_ticks >> SCHED_TICK_SHIFT) #define SCHED_TICK_TOTAL(ts) (max((ts)->ts_ltick - (ts)->ts_ftick, hz)) /* * These macros determine priorities for non-interactive threads. They are * assigned a priority based on their recent cpu utilization as expressed * by the ratio of ticks to the tick total. NHALF priorities at the start * and end of the MIN to MAX timeshare range are only reachable with negative * or positive nice respectively. * * PRI_RANGE: Priority range for utilization dependent priorities. * PRI_NRESV: Number of nice values. * PRI_TICKS: Compute a priority in PRI_RANGE from the ticks count and total. * PRI_NICE: Determines the part of the priority inherited from nice. */ #define SCHED_PRI_NRESV (PRIO_MAX - PRIO_MIN) #define SCHED_PRI_NHALF (SCHED_PRI_NRESV / 2) #define SCHED_PRI_MIN (PRI_MIN_TIMESHARE + SCHED_PRI_NHALF) #define SCHED_PRI_MAX (PRI_MAX_TIMESHARE - SCHED_PRI_NHALF) #define SCHED_PRI_RANGE (SCHED_PRI_MAX - SCHED_PRI_MIN) #define SCHED_PRI_TICKS(ts) \ (SCHED_TICK_HZ((ts)) / \ (roundup(SCHED_TICK_TOTAL((ts)), SCHED_PRI_RANGE) / SCHED_PRI_RANGE)) #define SCHED_PRI_NICE(nice) (nice) /* * These determine the interactivity of a process. Interactivity differs from * cpu utilization in that it expresses the voluntary time slept vs time ran * while cpu utilization includes all time not running. This more accurately * models the intent of the thread. * * SLP_RUN_MAX: Maximum amount of sleep time + run time we'll accumulate * before throttling back. * SLP_RUN_FORK: Maximum slp+run time to inherit at fork time. * INTERACT_MAX: Maximum interactivity value. Smaller is better. * INTERACT_THRESH: Threshhold for placement on the current runq. */ #define SCHED_SLP_RUN_MAX ((hz * 5) << SCHED_TICK_SHIFT) #define SCHED_SLP_RUN_FORK ((hz / 2) << SCHED_TICK_SHIFT) #define SCHED_INTERACT_MAX (100) #define SCHED_INTERACT_HALF (SCHED_INTERACT_MAX / 2) #define SCHED_INTERACT_THRESH (30) /* * tickincr: Converts a stathz tick into a hz domain scaled by * the shift factor. Without the shift the error rate * due to rounding would be unacceptably high. * realstathz: stathz is sometimes 0 and run off of hz. * sched_slice: Runtime of each thread before rescheduling. * preempt_thresh: Priority threshold for preemption and remote IPIs. */ static int sched_interact = SCHED_INTERACT_THRESH; static int realstathz; static int tickincr; static int sched_slice = 1; #ifdef PREEMPTION #ifdef FULL_PREEMPTION static int preempt_thresh = PRI_MAX_IDLE; #else static int preempt_thresh = PRI_MIN_KERN; #endif #else static int preempt_thresh = 0; #endif static int static_boost = PRI_MIN_TIMESHARE; static int sched_idlespins = 10000; static int sched_idlespinthresh = 4; /* * tdq - per processor runqs and statistics. All fields are protected by the * tdq_lock. The load and lowpri may be accessed without to avoid excess * locking in sched_pickcpu(); */ struct tdq { /* Ordered to improve efficiency of cpu_search() and switch(). */ struct mtx tdq_lock; /* run queue lock. */ struct cpu_group *tdq_cg; /* Pointer to cpu topology. */ volatile int tdq_load; /* Aggregate load. */ int tdq_sysload; /* For loadavg, !ITHD load. */ int tdq_transferable; /* Transferable thread count. */ volatile int tdq_idlestate; /* State of the idle thread. */ short tdq_switchcnt; /* Switches this tick. */ short tdq_oldswitchcnt; /* Switches last tick. */ u_char tdq_lowpri; /* Lowest priority thread. */ u_char tdq_ipipending; /* IPI pending. */ u_char tdq_idx; /* Current insert index. */ u_char tdq_ridx; /* Current removal index. */ struct runq tdq_realtime; /* real-time run queue. */ struct runq tdq_timeshare; /* timeshare run queue. */ struct runq tdq_idle; /* Queue of IDLE threads. */ char tdq_name[sizeof("sched lock") + 6]; } __aligned(64); /* Idle thread states and config. */ #define TDQ_RUNNING 1 #define TDQ_IDLE 2 #ifdef SMP struct cpu_group *cpu_top; /* CPU topology */ #define SCHED_AFFINITY_DEFAULT (max(1, hz / 1000)) #define SCHED_AFFINITY(ts, t) ((ts)->ts_rltick > ticks - ((t) * affinity)) /* * Run-time tunables. */ static int rebalance = 1; static int balance_interval = 128; /* Default set in sched_initticks(). */ static int affinity; static int steal_htt = 1; static int steal_idle = 1; static int steal_thresh = 2; /* * One thread queue per processor. */ static struct tdq tdq_cpu[MAXCPU]; static struct tdq *balance_tdq; static int balance_ticks; #define TDQ_SELF() (&tdq_cpu[PCPU_GET(cpuid)]) #define TDQ_CPU(x) (&tdq_cpu[(x)]) #define TDQ_ID(x) ((int)((x) - tdq_cpu)) #else /* !SMP */ static struct tdq tdq_cpu; #define TDQ_ID(x) (0) #define TDQ_SELF() (&tdq_cpu) #define TDQ_CPU(x) (&tdq_cpu) #endif #define TDQ_LOCK_ASSERT(t, type) mtx_assert(TDQ_LOCKPTR((t)), (type)) #define TDQ_LOCK(t) mtx_lock_spin(TDQ_LOCKPTR((t))) #define TDQ_LOCK_FLAGS(t, f) mtx_lock_spin_flags(TDQ_LOCKPTR((t)), (f)) #define TDQ_UNLOCK(t) mtx_unlock_spin(TDQ_LOCKPTR((t))) #define TDQ_LOCKPTR(t) (&(t)->tdq_lock) static void sched_priority(struct thread *); static void sched_thread_priority(struct thread *, u_char); static int sched_interact_score(struct thread *); static void sched_interact_update(struct thread *); static void sched_interact_fork(struct thread *); static void sched_pctcpu_update(struct td_sched *); /* Operations on per processor queues */ static struct thread *tdq_choose(struct tdq *); static void tdq_setup(struct tdq *); static void tdq_load_add(struct tdq *, struct thread *); static void tdq_load_rem(struct tdq *, struct thread *); static __inline void tdq_runq_add(struct tdq *, struct thread *, int); static __inline void tdq_runq_rem(struct tdq *, struct thread *); static inline int sched_shouldpreempt(int, int, int); void tdq_print(int cpu); static void runq_print(struct runq *rq); static void tdq_add(struct tdq *, struct thread *, int); #ifdef SMP static int tdq_move(struct tdq *, struct tdq *); static int tdq_idled(struct tdq *); static void tdq_notify(struct tdq *, struct thread *); static struct thread *tdq_steal(struct tdq *, int); static struct thread *runq_steal(struct runq *, int); static int sched_pickcpu(struct thread *, int); static void sched_balance(void); static int sched_balance_pair(struct tdq *, struct tdq *); static inline struct tdq *sched_setcpu(struct thread *, int, int); static inline struct mtx *thread_block_switch(struct thread *); static inline void thread_unblock_switch(struct thread *, struct mtx *); static struct mtx *sched_switch_migrate(struct tdq *, struct thread *, int); static int sysctl_kern_sched_topology_spec(SYSCTL_HANDLER_ARGS); static int sysctl_kern_sched_topology_spec_internal(struct sbuf *sb, struct cpu_group *cg, int indent); #endif static void sched_setup(void *dummy); SYSINIT(sched_setup, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, sched_setup, NULL); static void sched_initticks(void *dummy); SYSINIT(sched_initticks, SI_SUB_CLOCKS, SI_ORDER_THIRD, sched_initticks, NULL); /* * Print the threads waiting on a run-queue. */ static void runq_print(struct runq *rq) { struct rqhead *rqh; struct thread *td; int pri; int j; int i; for (i = 0; i < RQB_LEN; i++) { printf("\t\trunq bits %d 0x%zx\n", i, rq->rq_status.rqb_bits[i]); for (j = 0; j < RQB_BPW; j++) if (rq->rq_status.rqb_bits[i] & (1ul << j)) { pri = j + (i << RQB_L2BPW); rqh = &rq->rq_queues[pri]; TAILQ_FOREACH(td, rqh, td_runq) { printf("\t\t\ttd %p(%s) priority %d rqindex %d pri %d\n", td, td->td_name, td->td_priority, td->td_rqindex, pri); } } } } /* * Print the status of a per-cpu thread queue. Should be a ddb show cmd. */ void tdq_print(int cpu) { struct tdq *tdq; tdq = TDQ_CPU(cpu); printf("tdq %d:\n", TDQ_ID(tdq)); printf("\tlock %p\n", TDQ_LOCKPTR(tdq)); printf("\tLock name: %s\n", tdq->tdq_name); printf("\tload: %d\n", tdq->tdq_load); printf("\tswitch cnt: %d\n", tdq->tdq_switchcnt); printf("\told switch cnt: %d\n", tdq->tdq_oldswitchcnt); printf("\tidle state: %d\n", tdq->tdq_idlestate); printf("\ttimeshare idx: %d\n", tdq->tdq_idx); printf("\ttimeshare ridx: %d\n", tdq->tdq_ridx); printf("\tload transferable: %d\n", tdq->tdq_transferable); printf("\tlowest priority: %d\n", tdq->tdq_lowpri); printf("\trealtime runq:\n"); runq_print(&tdq->tdq_realtime); printf("\ttimeshare runq:\n"); runq_print(&tdq->tdq_timeshare); printf("\tidle runq:\n"); runq_print(&tdq->tdq_idle); } static inline int sched_shouldpreempt(int pri, int cpri, int remote) { /* * If the new priority is not better than the current priority there is * nothing to do. */ if (pri >= cpri) return (0); /* * Always preempt idle. */ if (cpri >= PRI_MIN_IDLE) return (1); /* * If preemption is disabled don't preempt others. */ if (preempt_thresh == 0) return (0); /* * Preempt if we exceed the threshold. */ if (pri <= preempt_thresh) return (1); /* * If we're realtime or better and there is timeshare or worse running * preempt only remote processors. */ if (remote && pri <= PRI_MAX_REALTIME && cpri > PRI_MAX_REALTIME) return (1); return (0); } #define TS_RQ_PPQ (((PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE) + 1) / RQ_NQS) /* * Add a thread to the actual run-queue. Keeps transferable counts up to * date with what is actually on the run-queue. Selects the correct * queue position for timeshare threads. */ static __inline void tdq_runq_add(struct tdq *tdq, struct thread *td, int flags) { struct td_sched *ts; u_char pri; TDQ_LOCK_ASSERT(tdq, MA_OWNED); THREAD_LOCK_ASSERT(td, MA_OWNED); pri = td->td_priority; ts = td->td_sched; TD_SET_RUNQ(td); if (THREAD_CAN_MIGRATE(td)) { tdq->tdq_transferable++; ts->ts_flags |= TSF_XFERABLE; } if (pri <= PRI_MAX_REALTIME) { ts->ts_runq = &tdq->tdq_realtime; } else if (pri <= PRI_MAX_TIMESHARE) { ts->ts_runq = &tdq->tdq_timeshare; KASSERT(pri <= PRI_MAX_TIMESHARE && pri >= PRI_MIN_TIMESHARE, ("Invalid priority %d on timeshare runq", pri)); /* * This queue contains only priorities between MIN and MAX * realtime. Use the whole queue to represent these values. */ if ((flags & (SRQ_BORROWING|SRQ_PREEMPTED)) == 0) { pri = (pri - PRI_MIN_TIMESHARE) / TS_RQ_PPQ; pri = (pri + tdq->tdq_idx) % RQ_NQS; /* * This effectively shortens the queue by one so we * can have a one slot difference between idx and * ridx while we wait for threads to drain. */ if (tdq->tdq_ridx != tdq->tdq_idx && pri == tdq->tdq_ridx) pri = (unsigned char)(pri - 1) % RQ_NQS; } else pri = tdq->tdq_ridx; runq_add_pri(ts->ts_runq, td, pri, flags); return; } else ts->ts_runq = &tdq->tdq_idle; runq_add(ts->ts_runq, td, flags); } /* * Remove a thread from a run-queue. This typically happens when a thread * is selected to run. Running threads are not on the queue and the * transferable count does not reflect them. */ static __inline void tdq_runq_rem(struct tdq *tdq, struct thread *td) { struct td_sched *ts; ts = td->td_sched; TDQ_LOCK_ASSERT(tdq, MA_OWNED); KASSERT(ts->ts_runq != NULL, ("tdq_runq_remove: thread %p null ts_runq", td)); if (ts->ts_flags & TSF_XFERABLE) { tdq->tdq_transferable--; ts->ts_flags &= ~TSF_XFERABLE; } if (ts->ts_runq == &tdq->tdq_timeshare) { if (tdq->tdq_idx != tdq->tdq_ridx) runq_remove_idx(ts->ts_runq, td, &tdq->tdq_ridx); else runq_remove_idx(ts->ts_runq, td, NULL); } else runq_remove(ts->ts_runq, td); } /* * Load is maintained for all threads RUNNING and ON_RUNQ. Add the load * for this thread to the referenced thread queue. */ static void tdq_load_add(struct tdq *tdq, struct thread *td) { TDQ_LOCK_ASSERT(tdq, MA_OWNED); THREAD_LOCK_ASSERT(td, MA_OWNED); tdq->tdq_load++; if ((td->td_proc->p_flag & P_NOLOAD) == 0) tdq->tdq_sysload++; CTR2(KTR_SCHED, "cpu %d load: %d", TDQ_ID(tdq), tdq->tdq_load); } /* * Remove the load from a thread that is transitioning to a sleep state or * exiting. */ static void tdq_load_rem(struct tdq *tdq, struct thread *td) { THREAD_LOCK_ASSERT(td, MA_OWNED); TDQ_LOCK_ASSERT(tdq, MA_OWNED); KASSERT(tdq->tdq_load != 0, ("tdq_load_rem: Removing with 0 load on queue %d", TDQ_ID(tdq))); tdq->tdq_load--; if ((td->td_proc->p_flag & P_NOLOAD) == 0) tdq->tdq_sysload--; CTR1(KTR_SCHED, "load: %d", tdq->tdq_load); } /* * Set lowpri to its exact value by searching the run-queue and * evaluating curthread. curthread may be passed as an optimization. */ static void tdq_setlowpri(struct tdq *tdq, struct thread *ctd) { struct thread *td; TDQ_LOCK_ASSERT(tdq, MA_OWNED); if (ctd == NULL) ctd = pcpu_find(TDQ_ID(tdq))->pc_curthread; td = tdq_choose(tdq); if (td == NULL || td->td_priority > ctd->td_priority) tdq->tdq_lowpri = ctd->td_priority; else tdq->tdq_lowpri = td->td_priority; } #ifdef SMP struct cpu_search { cpumask_t cs_mask; /* Mask of valid cpus. */ u_int cs_load; u_int cs_cpu; int cs_limit; /* Min priority for low min load for high. */ }; #define CPU_SEARCH_LOWEST 0x1 #define CPU_SEARCH_HIGHEST 0x2 #define CPU_SEARCH_BOTH (CPU_SEARCH_LOWEST|CPU_SEARCH_HIGHEST) #define CPUMASK_FOREACH(cpu, mask) \ for ((cpu) = 0; (cpu) < sizeof((mask)) * 8; (cpu)++) \ if ((mask) & 1 << (cpu)) static __inline int cpu_search(struct cpu_group *cg, struct cpu_search *low, struct cpu_search *high, const int match); int cpu_search_lowest(struct cpu_group *cg, struct cpu_search *low); int cpu_search_highest(struct cpu_group *cg, struct cpu_search *high); int cpu_search_both(struct cpu_group *cg, struct cpu_search *low, struct cpu_search *high); /* * This routine compares according to the match argument and should be * reduced in actual instantiations via constant propagation and dead code * elimination. */ static __inline int cpu_compare(int cpu, struct cpu_search *low, struct cpu_search *high, const int match) { struct tdq *tdq; tdq = TDQ_CPU(cpu); if (match & CPU_SEARCH_LOWEST) if (low->cs_mask & (1 << cpu) && tdq->tdq_load < low->cs_load && tdq->tdq_lowpri > low->cs_limit) { low->cs_cpu = cpu; low->cs_load = tdq->tdq_load; } if (match & CPU_SEARCH_HIGHEST) if (high->cs_mask & (1 << cpu) && tdq->tdq_load >= high->cs_limit && tdq->tdq_load > high->cs_load && tdq->tdq_transferable) { high->cs_cpu = cpu; high->cs_load = tdq->tdq_load; } return (tdq->tdq_load); } /* * Search the tree of cpu_groups for the lowest or highest loaded cpu * according to the match argument. This routine actually compares the * load on all paths through the tree and finds the least loaded cpu on * the least loaded path, which may differ from the least loaded cpu in * the system. This balances work among caches and busses. * * This inline is instantiated in three forms below using constants for the * match argument. It is reduced to the minimum set for each case. It is * also recursive to the depth of the tree. */ static __inline int cpu_search(struct cpu_group *cg, struct cpu_search *low, struct cpu_search *high, const int match) { int total; total = 0; if (cg->cg_children) { struct cpu_search lgroup; struct cpu_search hgroup; struct cpu_group *child; u_int lload; int hload; int load; int i; lload = -1; hload = -1; for (i = 0; i < cg->cg_children; i++) { child = &cg->cg_child[i]; if (match & CPU_SEARCH_LOWEST) { lgroup = *low; lgroup.cs_load = -1; } if (match & CPU_SEARCH_HIGHEST) { hgroup = *high; lgroup.cs_load = 0; } switch (match) { case CPU_SEARCH_LOWEST: load = cpu_search_lowest(child, &lgroup); break; case CPU_SEARCH_HIGHEST: load = cpu_search_highest(child, &hgroup); break; case CPU_SEARCH_BOTH: load = cpu_search_both(child, &lgroup, &hgroup); break; } total += load; if (match & CPU_SEARCH_LOWEST) if (load < lload || low->cs_cpu == -1) { *low = lgroup; lload = load; } if (match & CPU_SEARCH_HIGHEST) if (load > hload || high->cs_cpu == -1) { hload = load; *high = hgroup; } } } else { int cpu; CPUMASK_FOREACH(cpu, cg->cg_mask) total += cpu_compare(cpu, low, high, match); } return (total); } /* * cpu_search instantiations must pass constants to maintain the inline * optimization. */ int cpu_search_lowest(struct cpu_group *cg, struct cpu_search *low) { return cpu_search(cg, low, NULL, CPU_SEARCH_LOWEST); } int cpu_search_highest(struct cpu_group *cg, struct cpu_search *high) { return cpu_search(cg, NULL, high, CPU_SEARCH_HIGHEST); } int cpu_search_both(struct cpu_group *cg, struct cpu_search *low, struct cpu_search *high) { return cpu_search(cg, low, high, CPU_SEARCH_BOTH); } /* * Find the cpu with the least load via the least loaded path that has a * lowpri greater than pri pri. A pri of -1 indicates any priority is * acceptable. */ static inline int sched_lowest(struct cpu_group *cg, cpumask_t mask, int pri) { struct cpu_search low; low.cs_cpu = -1; low.cs_load = -1; low.cs_mask = mask; low.cs_limit = pri; cpu_search_lowest(cg, &low); return low.cs_cpu; } /* * Find the cpu with the highest load via the highest loaded path. */ static inline int sched_highest(struct cpu_group *cg, cpumask_t mask, int minload) { struct cpu_search high; high.cs_cpu = -1; high.cs_load = 0; high.cs_mask = mask; high.cs_limit = minload; cpu_search_highest(cg, &high); return high.cs_cpu; } /* * Simultaneously find the highest and lowest loaded cpu reachable via * cg. */ static inline void sched_both(struct cpu_group *cg, cpumask_t mask, int *lowcpu, int *highcpu) { struct cpu_search high; struct cpu_search low; low.cs_cpu = -1; low.cs_limit = -1; low.cs_load = -1; low.cs_mask = mask; high.cs_load = 0; high.cs_cpu = -1; high.cs_limit = -1; high.cs_mask = mask; cpu_search_both(cg, &low, &high); *lowcpu = low.cs_cpu; *highcpu = high.cs_cpu; return; } static void sched_balance_group(struct cpu_group *cg) { cpumask_t mask; int high; int low; int i; mask = -1; for (;;) { sched_both(cg, mask, &low, &high); if (low == high || low == -1 || high == -1) break; if (sched_balance_pair(TDQ_CPU(high), TDQ_CPU(low))) break; /* * If we failed to move any threads determine which cpu * to kick out of the set and try again. */ if (TDQ_CPU(high)->tdq_transferable == 0) mask &= ~(1 << high); else mask &= ~(1 << low); } for (i = 0; i < cg->cg_children; i++) sched_balance_group(&cg->cg_child[i]); } static void sched_balance() { struct tdq *tdq; /* * Select a random time between .5 * balance_interval and * 1.5 * balance_interval. */ balance_ticks = max(balance_interval / 2, 1); balance_ticks += random() % balance_interval; if (smp_started == 0 || rebalance == 0) return; tdq = TDQ_SELF(); TDQ_UNLOCK(tdq); sched_balance_group(cpu_top); TDQ_LOCK(tdq); } /* * Lock two thread queues using their address to maintain lock order. */ static void tdq_lock_pair(struct tdq *one, struct tdq *two) { if (one < two) { TDQ_LOCK(one); TDQ_LOCK_FLAGS(two, MTX_DUPOK); } else { TDQ_LOCK(two); TDQ_LOCK_FLAGS(one, MTX_DUPOK); } } /* * Unlock two thread queues. Order is not important here. */ static void tdq_unlock_pair(struct tdq *one, struct tdq *two) { TDQ_UNLOCK(one); TDQ_UNLOCK(two); } /* * Transfer load between two imbalanced thread queues. */ static int sched_balance_pair(struct tdq *high, struct tdq *low) { int transferable; int high_load; int low_load; int moved; int move; int diff; int i; tdq_lock_pair(high, low); transferable = high->tdq_transferable; high_load = high->tdq_load; low_load = low->tdq_load; moved = 0; /* * Determine what the imbalance is and then adjust that to how many * threads we actually have to give up (transferable). */ if (transferable != 0) { diff = high_load - low_load; move = diff / 2; if (diff & 0x1) move++; move = min(move, transferable); for (i = 0; i < move; i++) moved += tdq_move(high, low); /* * IPI the target cpu to force it to reschedule with the new * workload. */ ipi_selected(1 << TDQ_ID(low), IPI_PREEMPT); } tdq_unlock_pair(high, low); return (moved); } /* * Move a thread from one thread queue to another. */ static int tdq_move(struct tdq *from, struct tdq *to) { struct td_sched *ts; struct thread *td; struct tdq *tdq; int cpu; TDQ_LOCK_ASSERT(from, MA_OWNED); TDQ_LOCK_ASSERT(to, MA_OWNED); tdq = from; cpu = TDQ_ID(to); td = tdq_steal(tdq, cpu); if (td == NULL) return (0); ts = td->td_sched; /* * Although the run queue is locked the thread may be blocked. Lock * it to clear this and acquire the run-queue lock. */ thread_lock(td); /* Drop recursive lock on from acquired via thread_lock(). */ TDQ_UNLOCK(from); sched_rem(td); ts->ts_cpu = cpu; td->td_lock = TDQ_LOCKPTR(to); tdq_add(to, td, SRQ_YIELDING); return (1); } /* * This tdq has idled. Try to steal a thread from another cpu and switch * to it. */ static int tdq_idled(struct tdq *tdq) { struct cpu_group *cg; struct tdq *steal; cpumask_t mask; int thresh; int cpu; if (smp_started == 0 || steal_idle == 0) return (1); mask = -1; mask &= ~PCPU_GET(cpumask); /* We don't want to be preempted while we're iterating. */ spinlock_enter(); for (cg = tdq->tdq_cg; cg != NULL; ) { if ((cg->cg_flags & (CG_FLAG_HTT | CG_FLAG_THREAD)) == 0) thresh = steal_thresh; else thresh = 1; cpu = sched_highest(cg, mask, thresh); if (cpu == -1) { cg = cg->cg_parent; continue; } steal = TDQ_CPU(cpu); mask &= ~(1 << cpu); tdq_lock_pair(tdq, steal); if (steal->tdq_load < thresh || steal->tdq_transferable == 0) { tdq_unlock_pair(tdq, steal); continue; } /* * If a thread was added while interrupts were disabled don't * steal one here. If we fail to acquire one due to affinity * restrictions loop again with this cpu removed from the * set. */ if (tdq->tdq_load == 0 && tdq_move(steal, tdq) == 0) { tdq_unlock_pair(tdq, steal); continue; } spinlock_exit(); TDQ_UNLOCK(steal); mi_switch(SW_VOL | SWT_IDLE, NULL); thread_unlock(curthread); return (0); } spinlock_exit(); return (1); } /* * Notify a remote cpu of new work. Sends an IPI if criteria are met. */ static void tdq_notify(struct tdq *tdq, struct thread *td) { struct thread *ctd; int pri; int cpu; if (tdq->tdq_ipipending) return; cpu = td->td_sched->ts_cpu; pri = td->td_priority; ctd = pcpu_find(cpu)->pc_curthread; if (!sched_shouldpreempt(pri, ctd->td_priority, 1)) return; if (TD_IS_IDLETHREAD(ctd)) { /* * If the idle thread is still 'running' it's probably * waiting on us to release the tdq spinlock already. No * need to ipi. */ if (tdq->tdq_idlestate == TDQ_RUNNING) return; /* * If the MD code has an idle wakeup routine try that before * falling back to IPI. */ if (cpu_idle_wakeup(cpu)) return; } tdq->tdq_ipipending = 1; ipi_selected(1 << cpu, IPI_PREEMPT); } /* * Steals load from a timeshare queue. Honors the rotating queue head * index. */ static struct thread * runq_steal_from(struct runq *rq, int cpu, u_char start) { struct rqbits *rqb; struct rqhead *rqh; struct thread *td; int first; int bit; int pri; int i; rqb = &rq->rq_status; bit = start & (RQB_BPW -1); pri = 0; first = 0; again: for (i = RQB_WORD(start); i < RQB_LEN; bit = 0, i++) { if (rqb->rqb_bits[i] == 0) continue; if (bit != 0) { for (pri = bit; pri < RQB_BPW; pri++) if (rqb->rqb_bits[i] & (1ul << pri)) break; if (pri >= RQB_BPW) continue; } else pri = RQB_FFS(rqb->rqb_bits[i]); pri += (i << RQB_L2BPW); rqh = &rq->rq_queues[pri]; TAILQ_FOREACH(td, rqh, td_runq) { if (first && THREAD_CAN_MIGRATE(td) && THREAD_CAN_SCHED(td, cpu)) return (td); first = 1; } } if (start != 0) { start = 0; goto again; } return (NULL); } /* * Steals load from a standard linear queue. */ static struct thread * runq_steal(struct runq *rq, int cpu) { struct rqhead *rqh; struct rqbits *rqb; struct thread *td; int word; int bit; rqb = &rq->rq_status; for (word = 0; word < RQB_LEN; word++) { if (rqb->rqb_bits[word] == 0) continue; for (bit = 0; bit < RQB_BPW; bit++) { if ((rqb->rqb_bits[word] & (1ul << bit)) == 0) continue; rqh = &rq->rq_queues[bit + (word << RQB_L2BPW)]; TAILQ_FOREACH(td, rqh, td_runq) if (THREAD_CAN_MIGRATE(td) && THREAD_CAN_SCHED(td, cpu)) return (td); } } return (NULL); } /* * Attempt to steal a thread in priority order from a thread queue. */ static struct thread * tdq_steal(struct tdq *tdq, int cpu) { struct thread *td; TDQ_LOCK_ASSERT(tdq, MA_OWNED); if ((td = runq_steal(&tdq->tdq_realtime, cpu)) != NULL) return (td); if ((td = runq_steal_from(&tdq->tdq_timeshare, cpu, tdq->tdq_ridx)) != NULL) return (td); return (runq_steal(&tdq->tdq_idle, cpu)); } /* * Sets the thread lock and ts_cpu to match the requested cpu. Unlocks the * current lock and returns with the assigned queue locked. */ static inline struct tdq * sched_setcpu(struct thread *td, int cpu, int flags) { struct tdq *tdq; THREAD_LOCK_ASSERT(td, MA_OWNED); tdq = TDQ_CPU(cpu); td->td_sched->ts_cpu = cpu; /* * If the lock matches just return the queue. */ if (td->td_lock == TDQ_LOCKPTR(tdq)) return (tdq); #ifdef notyet /* * If the thread isn't running its lockptr is a * turnstile or a sleepqueue. We can just lock_set without * blocking. */ if (TD_CAN_RUN(td)) { TDQ_LOCK(tdq); thread_lock_set(td, TDQ_LOCKPTR(tdq)); return (tdq); } #endif /* * The hard case, migration, we need to block the thread first to * prevent order reversals with other cpus locks. */ thread_lock_block(td); TDQ_LOCK(tdq); thread_lock_unblock(td, TDQ_LOCKPTR(tdq)); return (tdq); } SCHED_STAT_DEFINE(pickcpu_intrbind, "Soft interrupt binding"); SCHED_STAT_DEFINE(pickcpu_idle_affinity, "Picked idle cpu based on affinity"); SCHED_STAT_DEFINE(pickcpu_affinity, "Picked cpu based on affinity"); SCHED_STAT_DEFINE(pickcpu_lowest, "Selected lowest load"); SCHED_STAT_DEFINE(pickcpu_local, "Migrated to current cpu"); SCHED_STAT_DEFINE(pickcpu_migration, "Selection may have caused migration"); static int sched_pickcpu(struct thread *td, int flags) { struct cpu_group *cg; struct td_sched *ts; struct tdq *tdq; cpumask_t mask; int self; int pri; int cpu; self = PCPU_GET(cpuid); ts = td->td_sched; if (smp_started == 0) return (self); /* * Don't migrate a running thread from sched_switch(). */ if ((flags & SRQ_OURSELF) || !THREAD_CAN_MIGRATE(td)) return (ts->ts_cpu); /* * Prefer to run interrupt threads on the processors that generate * the interrupt. */ if (td->td_priority <= PRI_MAX_ITHD && THREAD_CAN_SCHED(td, self) && curthread->td_intr_nesting_level && ts->ts_cpu != self) { SCHED_STAT_INC(pickcpu_intrbind); ts->ts_cpu = self; } /* * If the thread can run on the last cpu and the affinity has not * expired or it is idle run it there. */ pri = td->td_priority; tdq = TDQ_CPU(ts->ts_cpu); if (THREAD_CAN_SCHED(td, ts->ts_cpu)) { if (tdq->tdq_lowpri > PRI_MIN_IDLE) { SCHED_STAT_INC(pickcpu_idle_affinity); return (ts->ts_cpu); } if (SCHED_AFFINITY(ts, CG_SHARE_L2) && tdq->tdq_lowpri > pri) { SCHED_STAT_INC(pickcpu_affinity); return (ts->ts_cpu); } } /* * Search for the highest level in the tree that still has affinity. */ cg = NULL; for (cg = tdq->tdq_cg; cg != NULL; cg = cg->cg_parent) if (SCHED_AFFINITY(ts, cg->cg_level)) break; cpu = -1; mask = td->td_cpuset->cs_mask.__bits[0]; if (cg) cpu = sched_lowest(cg, mask, pri); if (cpu == -1) cpu = sched_lowest(cpu_top, mask, -1); /* * Compare the lowest loaded cpu to current cpu. */ if (THREAD_CAN_SCHED(td, self) && TDQ_CPU(self)->tdq_lowpri > pri && TDQ_CPU(cpu)->tdq_lowpri < PRI_MIN_IDLE) { SCHED_STAT_INC(pickcpu_local); cpu = self; } else SCHED_STAT_INC(pickcpu_lowest); if (cpu != ts->ts_cpu) SCHED_STAT_INC(pickcpu_migration); KASSERT(cpu != -1, ("sched_pickcpu: Failed to find a cpu.")); return (cpu); } #endif /* * Pick the highest priority task we have and return it. */ static struct thread * tdq_choose(struct tdq *tdq) { struct thread *td; TDQ_LOCK_ASSERT(tdq, MA_OWNED); td = runq_choose(&tdq->tdq_realtime); if (td != NULL) return (td); td = runq_choose_from(&tdq->tdq_timeshare, tdq->tdq_ridx); if (td != NULL) { KASSERT(td->td_priority >= PRI_MIN_TIMESHARE, ("tdq_choose: Invalid priority on timeshare queue %d", td->td_priority)); return (td); } td = runq_choose(&tdq->tdq_idle); if (td != NULL) { KASSERT(td->td_priority >= PRI_MIN_IDLE, ("tdq_choose: Invalid priority on idle queue %d", td->td_priority)); return (td); } return (NULL); } /* * Initialize a thread queue. */ static void tdq_setup(struct tdq *tdq) { if (bootverbose) printf("ULE: setup cpu %d\n", TDQ_ID(tdq)); runq_init(&tdq->tdq_realtime); runq_init(&tdq->tdq_timeshare); runq_init(&tdq->tdq_idle); snprintf(tdq->tdq_name, sizeof(tdq->tdq_name), "sched lock %d", (int)TDQ_ID(tdq)); mtx_init(&tdq->tdq_lock, tdq->tdq_name, "sched lock", MTX_SPIN | MTX_RECURSE); } #ifdef SMP static void sched_setup_smp(void) { struct tdq *tdq; int i; cpu_top = smp_topo(); for (i = 0; i < MAXCPU; i++) { if (CPU_ABSENT(i)) continue; tdq = TDQ_CPU(i); tdq_setup(tdq); tdq->tdq_cg = smp_topo_find(cpu_top, i); if (tdq->tdq_cg == NULL) panic("Can't find cpu group for %d\n", i); } balance_tdq = TDQ_SELF(); sched_balance(); } #endif /* * Setup the thread queues and initialize the topology based on MD * information. */ static void sched_setup(void *dummy) { struct tdq *tdq; tdq = TDQ_SELF(); #ifdef SMP sched_setup_smp(); #else tdq_setup(tdq); #endif /* * To avoid divide-by-zero, we set realstathz a dummy value * in case which sched_clock() called before sched_initticks(). */ realstathz = hz; sched_slice = (realstathz/10); /* ~100ms */ tickincr = 1 << SCHED_TICK_SHIFT; /* Add thread0's load since it's running. */ TDQ_LOCK(tdq); thread0.td_lock = TDQ_LOCKPTR(TDQ_SELF()); tdq_load_add(tdq, &thread0); tdq->tdq_lowpri = thread0.td_priority; TDQ_UNLOCK(tdq); } /* * This routine determines the tickincr after stathz and hz are setup. */ /* ARGSUSED */ static void sched_initticks(void *dummy) { int incr; realstathz = stathz ? stathz : hz; sched_slice = (realstathz/10); /* ~100ms */ /* * tickincr is shifted out by 10 to avoid rounding errors due to * hz not being evenly divisible by stathz on all platforms. */ incr = (hz << SCHED_TICK_SHIFT) / realstathz; /* * This does not work for values of stathz that are more than * 1 << SCHED_TICK_SHIFT * hz. In practice this does not happen. */ if (incr == 0) incr = 1; tickincr = incr; #ifdef SMP /* * Set the default balance interval now that we know * what realstathz is. */ balance_interval = realstathz; /* * Set steal thresh to log2(mp_ncpu) but no greater than 4. This * prevents excess thrashing on large machines and excess idle on * smaller machines. */ steal_thresh = min(ffs(mp_ncpus) - 1, 3); affinity = SCHED_AFFINITY_DEFAULT; #endif } /* * This is the core of the interactivity algorithm. Determines a score based * on past behavior. It is the ratio of sleep time to run time scaled to * a [0, 100] integer. This is the voluntary sleep time of a process, which * differs from the cpu usage because it does not account for time spent * waiting on a run-queue. Would be prettier if we had floating point. */ static int sched_interact_score(struct thread *td) { struct td_sched *ts; int div; ts = td->td_sched; /* * The score is only needed if this is likely to be an interactive * task. Don't go through the expense of computing it if there's * no chance. */ if (sched_interact <= SCHED_INTERACT_HALF && ts->ts_runtime >= ts->ts_slptime) return (SCHED_INTERACT_HALF); if (ts->ts_runtime > ts->ts_slptime) { div = max(1, ts->ts_runtime / SCHED_INTERACT_HALF); return (SCHED_INTERACT_HALF + (SCHED_INTERACT_HALF - (ts->ts_slptime / div))); } if (ts->ts_slptime > ts->ts_runtime) { div = max(1, ts->ts_slptime / SCHED_INTERACT_HALF); return (ts->ts_runtime / div); } /* runtime == slptime */ if (ts->ts_runtime) return (SCHED_INTERACT_HALF); /* * This can happen if slptime and runtime are 0. */ return (0); } /* * Scale the scheduling priority according to the "interactivity" of this * process. */ static void sched_priority(struct thread *td) { int score; int pri; if (td->td_pri_class != PRI_TIMESHARE) return; /* * If the score is interactive we place the thread in the realtime * queue with a priority that is less than kernel and interrupt * priorities. These threads are not subject to nice restrictions. * * Scores greater than this are placed on the normal timeshare queue * where the priority is partially decided by the most recent cpu * utilization and the rest is decided by nice value. * * The nice value of the process has a linear effect on the calculated * score. Negative nice values make it easier for a thread to be * considered interactive. */ score = imax(0, sched_interact_score(td) - td->td_proc->p_nice); if (score < sched_interact) { pri = PRI_MIN_REALTIME; pri += ((PRI_MAX_REALTIME - PRI_MIN_REALTIME) / sched_interact) * score; KASSERT(pri >= PRI_MIN_REALTIME && pri <= PRI_MAX_REALTIME, ("sched_priority: invalid interactive priority %d score %d", pri, score)); } else { pri = SCHED_PRI_MIN; if (td->td_sched->ts_ticks) pri += SCHED_PRI_TICKS(td->td_sched); pri += SCHED_PRI_NICE(td->td_proc->p_nice); KASSERT(pri >= PRI_MIN_TIMESHARE && pri <= PRI_MAX_TIMESHARE, ("sched_priority: invalid priority %d: nice %d, " "ticks %d ftick %d ltick %d tick pri %d", pri, td->td_proc->p_nice, td->td_sched->ts_ticks, td->td_sched->ts_ftick, td->td_sched->ts_ltick, SCHED_PRI_TICKS(td->td_sched))); } sched_user_prio(td, pri); return; } /* * This routine enforces a maximum limit on the amount of scheduling history * kept. It is called after either the slptime or runtime is adjusted. This * function is ugly due to integer math. */ static void sched_interact_update(struct thread *td) { struct td_sched *ts; u_int sum; ts = td->td_sched; sum = ts->ts_runtime + ts->ts_slptime; if (sum < SCHED_SLP_RUN_MAX) return; /* * This only happens from two places: * 1) We have added an unusual amount of run time from fork_exit. * 2) We have added an unusual amount of sleep time from sched_sleep(). */ if (sum > SCHED_SLP_RUN_MAX * 2) { if (ts->ts_runtime > ts->ts_slptime) { ts->ts_runtime = SCHED_SLP_RUN_MAX; ts->ts_slptime = 1; } else { ts->ts_slptime = SCHED_SLP_RUN_MAX; ts->ts_runtime = 1; } return; } /* * If we have exceeded by more than 1/5th then the algorithm below * will not bring us back into range. Dividing by two here forces * us into the range of [4/5 * SCHED_INTERACT_MAX, SCHED_INTERACT_MAX] */ if (sum > (SCHED_SLP_RUN_MAX / 5) * 6) { ts->ts_runtime /= 2; ts->ts_slptime /= 2; return; } ts->ts_runtime = (ts->ts_runtime / 5) * 4; ts->ts_slptime = (ts->ts_slptime / 5) * 4; } /* * Scale back the interactivity history when a child thread is created. The * history is inherited from the parent but the thread may behave totally * differently. For example, a shell spawning a compiler process. We want * to learn that the compiler is behaving badly very quickly. */ static void sched_interact_fork(struct thread *td) { int ratio; int sum; sum = td->td_sched->ts_runtime + td->td_sched->ts_slptime; if (sum > SCHED_SLP_RUN_FORK) { ratio = sum / SCHED_SLP_RUN_FORK; td->td_sched->ts_runtime /= ratio; td->td_sched->ts_slptime /= ratio; } } /* * Called from proc0_init() to setup the scheduler fields. */ void schedinit(void) { /* * Set up the scheduler specific parts of proc0. */ proc0.p_sched = NULL; /* XXX */ thread0.td_sched = &td_sched0; td_sched0.ts_ltick = ticks; td_sched0.ts_ftick = ticks; td_sched0.ts_slice = sched_slice; } /* * This is only somewhat accurate since given many processes of the same * priority they will switch when their slices run out, which will be * at most sched_slice stathz ticks. */ int sched_rr_interval(void) { /* Convert sched_slice to hz */ return (hz/(realstathz/sched_slice)); } /* * Update the percent cpu tracking information when it is requested or * the total history exceeds the maximum. We keep a sliding history of * tick counts that slowly decays. This is less precise than the 4BSD * mechanism since it happens with less regular and frequent events. */ static void sched_pctcpu_update(struct td_sched *ts) { if (ts->ts_ticks == 0) return; if (ticks - (hz / 10) < ts->ts_ltick && SCHED_TICK_TOTAL(ts) < SCHED_TICK_MAX) return; /* * Adjust counters and watermark for pctcpu calc. */ if (ts->ts_ltick > ticks - SCHED_TICK_TARG) ts->ts_ticks = (ts->ts_ticks / (ticks - ts->ts_ftick)) * SCHED_TICK_TARG; else ts->ts_ticks = 0; ts->ts_ltick = ticks; ts->ts_ftick = ts->ts_ltick - SCHED_TICK_TARG; } /* * Adjust the priority of a thread. Move it to the appropriate run-queue * if necessary. This is the back-end for several priority related * functions. */ static void sched_thread_priority(struct thread *td, u_char prio) { struct td_sched *ts; struct tdq *tdq; int oldpri; CTR6(KTR_SCHED, "sched_prio: %p(%s) prio %d newprio %d by %p(%s)", td, td->td_name, td->td_priority, prio, curthread, curthread->td_name); ts = td->td_sched; THREAD_LOCK_ASSERT(td, MA_OWNED); if (td->td_priority == prio) return; /* * If the priority has been elevated due to priority * propagation, we may have to move ourselves to a new * queue. This could be optimized to not re-add in some * cases. */ if (TD_ON_RUNQ(td) && prio < td->td_priority) { sched_rem(td); td->td_priority = prio; sched_add(td, SRQ_BORROWING); return; } /* * If the thread is currently running we may have to adjust the lowpri * information so other cpus are aware of our current priority. */ if (TD_IS_RUNNING(td)) { tdq = TDQ_CPU(ts->ts_cpu); oldpri = td->td_priority; td->td_priority = prio; if (prio < tdq->tdq_lowpri) tdq->tdq_lowpri = prio; else if (tdq->tdq_lowpri == oldpri) tdq_setlowpri(tdq, td); return; } td->td_priority = prio; } /* * Update a thread's priority when it is lent another thread's * priority. */ void sched_lend_prio(struct thread *td, u_char prio) { td->td_flags |= TDF_BORROWING; sched_thread_priority(td, prio); } /* * Restore a thread's priority when priority propagation is * over. The prio argument is the minimum priority the thread * needs to have to satisfy other possible priority lending * requests. If the thread's regular priority is less * important than prio, the thread will keep a priority boost * of prio. */ void sched_unlend_prio(struct thread *td, u_char prio) { u_char base_pri; if (td->td_base_pri >= PRI_MIN_TIMESHARE && td->td_base_pri <= PRI_MAX_TIMESHARE) base_pri = td->td_user_pri; else base_pri = td->td_base_pri; if (prio >= base_pri) { td->td_flags &= ~TDF_BORROWING; sched_thread_priority(td, base_pri); } else sched_lend_prio(td, prio); } /* * Standard entry for setting the priority to an absolute value. */ void sched_prio(struct thread *td, u_char prio) { u_char oldprio; /* First, update the base priority. */ td->td_base_pri = prio; /* * If the thread is borrowing another thread's priority, don't * ever lower the priority. */ if (td->td_flags & TDF_BORROWING && td->td_priority < prio) return; /* Change the real priority. */ oldprio = td->td_priority; sched_thread_priority(td, prio); /* * If the thread is on a turnstile, then let the turnstile update * its state. */ if (TD_ON_LOCK(td) && oldprio != prio) turnstile_adjust(td, oldprio); } /* * Set the base user priority, does not effect current running priority. */ void sched_user_prio(struct thread *td, u_char prio) { u_char oldprio; td->td_base_user_pri = prio; if (td->td_flags & TDF_UBORROWING && td->td_user_pri <= prio) return; oldprio = td->td_user_pri; td->td_user_pri = prio; } void sched_lend_user_prio(struct thread *td, u_char prio) { u_char oldprio; THREAD_LOCK_ASSERT(td, MA_OWNED); td->td_flags |= TDF_UBORROWING; oldprio = td->td_user_pri; td->td_user_pri = prio; } void sched_unlend_user_prio(struct thread *td, u_char prio) { u_char base_pri; THREAD_LOCK_ASSERT(td, MA_OWNED); base_pri = td->td_base_user_pri; if (prio >= base_pri) { td->td_flags &= ~TDF_UBORROWING; sched_user_prio(td, base_pri); } else { sched_lend_user_prio(td, prio); } } /* * Block a thread for switching. Similar to thread_block() but does not * bump the spin count. */ static inline struct mtx * thread_block_switch(struct thread *td) { struct mtx *lock; THREAD_LOCK_ASSERT(td, MA_OWNED); lock = td->td_lock; td->td_lock = &blocked_lock; mtx_unlock_spin(lock); return (lock); } /* * Handle migration from sched_switch(). This happens only for * cpu binding. */ static struct mtx * sched_switch_migrate(struct tdq *tdq, struct thread *td, int flags) { struct tdq *tdn; tdn = TDQ_CPU(td->td_sched->ts_cpu); #ifdef SMP tdq_load_rem(tdq, td); /* * Do the lock dance required to avoid LOR. We grab an extra * spinlock nesting to prevent preemption while we're * not holding either run-queue lock. */ spinlock_enter(); thread_block_switch(td); /* This releases the lock on tdq. */ TDQ_LOCK(tdn); tdq_add(tdn, td, flags); tdq_notify(tdn, td); /* * After we unlock tdn the new cpu still can't switch into this * thread until we've unblocked it in cpu_switch(). The lock * pointers may match in the case of HTT cores. Don't unlock here * or we can deadlock when the other CPU runs the IPI handler. */ if (TDQ_LOCKPTR(tdn) != TDQ_LOCKPTR(tdq)) { TDQ_UNLOCK(tdn); TDQ_LOCK(tdq); } spinlock_exit(); #endif return (TDQ_LOCKPTR(tdn)); } /* * Release a thread that was blocked with thread_block_switch(). */ static inline void thread_unblock_switch(struct thread *td, struct mtx *mtx) { atomic_store_rel_ptr((volatile uintptr_t *)&td->td_lock, (uintptr_t)mtx); } /* * Switch threads. This function has to handle threads coming in while * blocked for some reason, running, or idle. It also must deal with * migrating a thread from one queue to another as running threads may * be assigned elsewhere via binding. */ void sched_switch(struct thread *td, struct thread *newtd, int flags) { struct tdq *tdq; struct td_sched *ts; struct mtx *mtx; int srqflag; int cpuid; THREAD_LOCK_ASSERT(td, MA_OWNED); KASSERT(newtd == NULL, ("sched_switch: Unsupported newtd argument")); cpuid = PCPU_GET(cpuid); tdq = TDQ_CPU(cpuid); ts = td->td_sched; mtx = td->td_lock; ts->ts_rltick = ticks; td->td_lastcpu = td->td_oncpu; td->td_oncpu = NOCPU; td->td_flags &= ~TDF_NEEDRESCHED; td->td_owepreempt = 0; tdq->tdq_switchcnt++; /* * The lock pointer in an idle thread should never change. Reset it * to CAN_RUN as well. */ if (TD_IS_IDLETHREAD(td)) { MPASS(td->td_lock == TDQ_LOCKPTR(tdq)); TD_SET_CAN_RUN(td); } else if (TD_IS_RUNNING(td)) { MPASS(td->td_lock == TDQ_LOCKPTR(tdq)); srqflag = (flags & SW_PREEMPT) ? SRQ_OURSELF|SRQ_YIELDING|SRQ_PREEMPTED : SRQ_OURSELF|SRQ_YIELDING; if (ts->ts_cpu == cpuid) tdq_runq_add(tdq, td, srqflag); else mtx = sched_switch_migrate(tdq, td, srqflag); } else { /* This thread must be going to sleep. */ TDQ_LOCK(tdq); mtx = thread_block_switch(td); tdq_load_rem(tdq, td); } /* * We enter here with the thread blocked and assigned to the * appropriate cpu run-queue or sleep-queue and with the current * thread-queue locked. */ TDQ_LOCK_ASSERT(tdq, MA_OWNED | MA_NOTRECURSED); newtd = choosethread(); /* * Call the MD code to switch contexts if necessary. */ if (td != newtd) { #ifdef HWPMC_HOOKS if (PMC_PROC_IS_USING_PMCS(td->td_proc)) PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT); #endif lock_profile_release_lock(&TDQ_LOCKPTR(tdq)->lock_object); TDQ_LOCKPTR(tdq)->mtx_lock = (uintptr_t)newtd; #ifdef KDTRACE_HOOKS /* * If DTrace has set the active vtime enum to anything * other than INACTIVE (0), then it should have set the * function to call. */ if (dtrace_vtime_active) (*dtrace_vtime_switch_func)(newtd); #endif cpu_switch(td, newtd, mtx); /* * We may return from cpu_switch on a different cpu. However, * we always return with td_lock pointing to the current cpu's * run queue lock. */ cpuid = PCPU_GET(cpuid); tdq = TDQ_CPU(cpuid); lock_profile_obtain_lock_success( &TDQ_LOCKPTR(tdq)->lock_object, 0, 0, __FILE__, __LINE__); #ifdef HWPMC_HOOKS if (PMC_PROC_IS_USING_PMCS(td->td_proc)) PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_IN); #endif } else thread_unblock_switch(td, mtx); /* * Assert that all went well and return. */ TDQ_LOCK_ASSERT(tdq, MA_OWNED|MA_NOTRECURSED); MPASS(td->td_lock == TDQ_LOCKPTR(tdq)); td->td_oncpu = cpuid; } /* * Adjust thread priorities as a result of a nice request. */ void sched_nice(struct proc *p, int nice) { struct thread *td; PROC_LOCK_ASSERT(p, MA_OWNED); p->p_nice = nice; FOREACH_THREAD_IN_PROC(p, td) { thread_lock(td); sched_priority(td); sched_prio(td, td->td_base_user_pri); thread_unlock(td); } } /* * Record the sleep time for the interactivity scorer. */ void sched_sleep(struct thread *td, int prio) { THREAD_LOCK_ASSERT(td, MA_OWNED); td->td_slptick = ticks; if (TD_IS_SUSPENDED(td) || prio <= PSOCK) td->td_flags |= TDF_CANSWAP; if (static_boost == 1 && prio) sched_prio(td, prio); else if (static_boost && td->td_priority > static_boost) sched_prio(td, static_boost); } /* * Schedule a thread to resume execution and record how long it voluntarily * slept. We also update the pctcpu, interactivity, and priority. */ void sched_wakeup(struct thread *td) { struct td_sched *ts; int slptick; THREAD_LOCK_ASSERT(td, MA_OWNED); ts = td->td_sched; td->td_flags &= ~TDF_CANSWAP; /* * If we slept for more than a tick update our interactivity and * priority. */ slptick = td->td_slptick; td->td_slptick = 0; if (slptick && slptick != ticks) { u_int hzticks; hzticks = (ticks - slptick) << SCHED_TICK_SHIFT; ts->ts_slptime += hzticks; sched_interact_update(td); sched_pctcpu_update(ts); } /* Reset the slice value after we sleep. */ ts->ts_slice = sched_slice; sched_add(td, SRQ_BORING); } /* * Penalize the parent for creating a new child and initialize the child's * priority. */ void sched_fork(struct thread *td, struct thread *child) { THREAD_LOCK_ASSERT(td, MA_OWNED); sched_fork_thread(td, child); /* * Penalize the parent and child for forking. */ sched_interact_fork(child); sched_priority(child); td->td_sched->ts_runtime += tickincr; sched_interact_update(td); sched_priority(td); } /* * Fork a new thread, may be within the same process. */ void sched_fork_thread(struct thread *td, struct thread *child) { struct td_sched *ts; struct td_sched *ts2; THREAD_LOCK_ASSERT(td, MA_OWNED); /* * Initialize child. */ ts = td->td_sched; ts2 = child->td_sched; child->td_lock = TDQ_LOCKPTR(TDQ_SELF()); child->td_cpuset = cpuset_ref(td->td_cpuset); ts2->ts_cpu = ts->ts_cpu; ts2->ts_flags = 0; /* * Grab our parents cpu estimation information and priority. */ ts2->ts_ticks = ts->ts_ticks; ts2->ts_ltick = ts->ts_ltick; ts2->ts_ftick = ts->ts_ftick; child->td_user_pri = td->td_user_pri; child->td_base_user_pri = td->td_base_user_pri; /* * And update interactivity score. */ ts2->ts_slptime = ts->ts_slptime; ts2->ts_runtime = ts->ts_runtime; ts2->ts_slice = 1; /* Attempt to quickly learn interactivity. */ } /* * Adjust the priority class of a thread. */ void sched_class(struct thread *td, int class) { THREAD_LOCK_ASSERT(td, MA_OWNED); if (td->td_pri_class == class) return; td->td_pri_class = class; } /* * Return some of the child's priority and interactivity to the parent. */ void sched_exit(struct proc *p, struct thread *child) { struct thread *td; CTR3(KTR_SCHED, "sched_exit: %p(%s) prio %d", child, child->td_name, child->td_priority); PROC_LOCK_ASSERT(p, MA_OWNED); td = FIRST_THREAD_IN_PROC(p); sched_exit_thread(td, child); } /* * Penalize another thread for the time spent on this one. This helps to * worsen the priority and interactivity of processes which schedule batch * jobs such as make. This has little effect on the make process itself but * causes new processes spawned by it to receive worse scores immediately. */ void sched_exit_thread(struct thread *td, struct thread *child) { CTR3(KTR_SCHED, "sched_exit_thread: %p(%s) prio %d", child, child->td_name, child->td_priority); /* * Give the child's runtime to the parent without returning the * sleep time as a penalty to the parent. This causes shells that * launch expensive things to mark their children as expensive. */ thread_lock(td); td->td_sched->ts_runtime += child->td_sched->ts_runtime; sched_interact_update(td); sched_priority(td); thread_unlock(td); } void sched_preempt(struct thread *td) { struct tdq *tdq; thread_lock(td); tdq = TDQ_SELF(); TDQ_LOCK_ASSERT(tdq, MA_OWNED); tdq->tdq_ipipending = 0; if (td->td_priority > tdq->tdq_lowpri) { int flags; flags = SW_INVOL | SW_PREEMPT; if (td->td_critnest > 1) td->td_owepreempt = 1; else if (TD_IS_IDLETHREAD(td)) mi_switch(flags | SWT_REMOTEWAKEIDLE, NULL); else mi_switch(flags | SWT_REMOTEPREEMPT, NULL); } thread_unlock(td); } /* * Fix priorities on return to user-space. Priorities may be elevated due * to static priorities in msleep() or similar. */ void sched_userret(struct thread *td) { /* * XXX we cheat slightly on the locking here to avoid locking in * the usual case. Setting td_priority here is essentially an * incomplete workaround for not setting it properly elsewhere. * Now that some interrupt handlers are threads, not setting it * properly elsewhere can clobber it in the window between setting * it here and returning to user mode, so don't waste time setting * it perfectly here. */ KASSERT((td->td_flags & TDF_BORROWING) == 0, ("thread with borrowed priority returning to userland")); if (td->td_priority != td->td_user_pri) { thread_lock(td); td->td_priority = td->td_user_pri; td->td_base_pri = td->td_user_pri; tdq_setlowpri(TDQ_SELF(), td); thread_unlock(td); } } /* * Handle a stathz tick. This is really only relevant for timeshare * threads. */ void sched_clock(struct thread *td) { struct tdq *tdq; struct td_sched *ts; THREAD_LOCK_ASSERT(td, MA_OWNED); tdq = TDQ_SELF(); #ifdef SMP /* * We run the long term load balancer infrequently on the first cpu. */ if (balance_tdq == tdq) { if (balance_ticks && --balance_ticks == 0) sched_balance(); } #endif /* * Save the old switch count so we have a record of the last ticks * activity. Initialize the new switch count based on our load. * If there is some activity seed it to reflect that. */ tdq->tdq_oldswitchcnt = tdq->tdq_switchcnt; tdq->tdq_switchcnt = tdq->tdq_load; /* * Advance the insert index once for each tick to ensure that all * threads get a chance to run. */ if (tdq->tdq_idx == tdq->tdq_ridx) { tdq->tdq_idx = (tdq->tdq_idx + 1) % RQ_NQS; if (TAILQ_EMPTY(&tdq->tdq_timeshare.rq_queues[tdq->tdq_ridx])) tdq->tdq_ridx = tdq->tdq_idx; } ts = td->td_sched; if (td->td_pri_class & PRI_FIFO_BIT) return; if (td->td_pri_class == PRI_TIMESHARE) { /* * We used a tick; charge it to the thread so * that we can compute our interactivity. */ td->td_sched->ts_runtime += tickincr; sched_interact_update(td); sched_priority(td); } /* * We used up one time slice. */ if (--ts->ts_slice > 0) return; /* * We're out of time, force a requeue at userret(). */ ts->ts_slice = sched_slice; td->td_flags |= TDF_NEEDRESCHED; } /* * Called once per hz tick. Used for cpu utilization information. This * is easier than trying to scale based on stathz. */ void sched_tick(void) { struct td_sched *ts; ts = curthread->td_sched; /* * Ticks is updated asynchronously on a single cpu. Check here to * avoid incrementing ts_ticks multiple times in a single tick. */ if (ts->ts_ltick == ticks) return; /* Adjust ticks for pctcpu */ ts->ts_ticks += 1 << SCHED_TICK_SHIFT; ts->ts_ltick = ticks; /* * Update if we've exceeded our desired tick threshhold by over one * second. */ if (ts->ts_ftick + SCHED_TICK_MAX < ts->ts_ltick) sched_pctcpu_update(ts); } /* * Return whether the current CPU has runnable tasks. Used for in-kernel * cooperative idle threads. */ int sched_runnable(void) { struct tdq *tdq; int load; load = 1; tdq = TDQ_SELF(); if ((curthread->td_flags & TDF_IDLETD) != 0) { if (tdq->tdq_load > 0) goto out; } else if (tdq->tdq_load - 1 > 0) goto out; load = 0; out: return (load); } /* * Choose the highest priority thread to run. The thread is removed from * the run-queue while running however the load remains. For SMP we set * the tdq in the global idle bitmask if it idles here. */ struct thread * sched_choose(void) { struct thread *td; struct tdq *tdq; tdq = TDQ_SELF(); TDQ_LOCK_ASSERT(tdq, MA_OWNED); td = tdq_choose(tdq); if (td) { td->td_sched->ts_ltick = ticks; tdq_runq_rem(tdq, td); tdq->tdq_lowpri = td->td_priority; return (td); } tdq->tdq_lowpri = PRI_MAX_IDLE; return (PCPU_GET(idlethread)); } /* * Set owepreempt if necessary. Preemption never happens directly in ULE, * we always request it once we exit a critical section. */ static inline void sched_setpreempt(struct thread *td) { struct thread *ctd; int cpri; int pri; THREAD_LOCK_ASSERT(curthread, MA_OWNED); ctd = curthread; pri = td->td_priority; cpri = ctd->td_priority; if (pri < cpri) ctd->td_flags |= TDF_NEEDRESCHED; if (panicstr != NULL || pri >= cpri || cold || TD_IS_INHIBITED(ctd)) return; if (!sched_shouldpreempt(pri, cpri, 0)) return; ctd->td_owepreempt = 1; } /* * Add a thread to a thread queue. Select the appropriate runq and add the * thread to it. This is the internal function called when the tdq is * predetermined. */ void tdq_add(struct tdq *tdq, struct thread *td, int flags) { TDQ_LOCK_ASSERT(tdq, MA_OWNED); KASSERT((td->td_inhibitors == 0), ("sched_add: trying to run inhibited thread")); KASSERT((TD_CAN_RUN(td) || TD_IS_RUNNING(td)), ("sched_add: bad thread state")); KASSERT(td->td_flags & TDF_INMEM, ("sched_add: thread swapped out")); if (td->td_priority < tdq->tdq_lowpri) tdq->tdq_lowpri = td->td_priority; tdq_runq_add(tdq, td, flags); tdq_load_add(tdq, td); } /* * Select the target thread queue and add a thread to it. Request * preemption or IPI a remote processor if required. */ void sched_add(struct thread *td, int flags) { struct tdq *tdq; #ifdef SMP int cpu; #endif CTR5(KTR_SCHED, "sched_add: %p(%s) prio %d by %p(%s)", td, td->td_name, td->td_priority, curthread, curthread->td_name); THREAD_LOCK_ASSERT(td, MA_OWNED); /* * Recalculate the priority before we select the target cpu or * run-queue. */ if (PRI_BASE(td->td_pri_class) == PRI_TIMESHARE) sched_priority(td); #ifdef SMP /* * Pick the destination cpu and if it isn't ours transfer to the * target cpu. */ cpu = sched_pickcpu(td, flags); tdq = sched_setcpu(td, cpu, flags); tdq_add(tdq, td, flags); if (cpu != PCPU_GET(cpuid)) { tdq_notify(tdq, td); return; } #else tdq = TDQ_SELF(); TDQ_LOCK(tdq); /* * Now that the thread is moving to the run-queue, set the lock * to the scheduler's lock. */ thread_lock_set(td, TDQ_LOCKPTR(tdq)); tdq_add(tdq, td, flags); #endif if (!(flags & SRQ_YIELDING)) sched_setpreempt(td); } /* * Remove a thread from a run-queue without running it. This is used * when we're stealing a thread from a remote queue. Otherwise all threads * exit by calling sched_exit_thread() and sched_throw() themselves. */ void sched_rem(struct thread *td) { struct tdq *tdq; CTR5(KTR_SCHED, "sched_rem: %p(%s) prio %d by %p(%s)", td, td->td_name, td->td_priority, curthread, curthread->td_name); tdq = TDQ_CPU(td->td_sched->ts_cpu); TDQ_LOCK_ASSERT(tdq, MA_OWNED); MPASS(td->td_lock == TDQ_LOCKPTR(tdq)); KASSERT(TD_ON_RUNQ(td), ("sched_rem: thread not on run queue")); tdq_runq_rem(tdq, td); tdq_load_rem(tdq, td); TD_SET_CAN_RUN(td); if (td->td_priority == tdq->tdq_lowpri) tdq_setlowpri(tdq, NULL); } /* * Fetch cpu utilization information. Updates on demand. */ fixpt_t sched_pctcpu(struct thread *td) { fixpt_t pctcpu; struct td_sched *ts; pctcpu = 0; ts = td->td_sched; if (ts == NULL) return (0); thread_lock(td); if (ts->ts_ticks) { int rtick; sched_pctcpu_update(ts); /* How many rtick per second ? */ rtick = min(SCHED_TICK_HZ(ts) / SCHED_TICK_SECS, hz); pctcpu = (FSCALE * ((FSCALE * rtick)/hz)) >> FSHIFT; } thread_unlock(td); return (pctcpu); } /* * Enforce affinity settings for a thread. Called after adjustments to * cpumask. */ void sched_affinity(struct thread *td) { #ifdef SMP struct td_sched *ts; int cpu; THREAD_LOCK_ASSERT(td, MA_OWNED); ts = td->td_sched; if (THREAD_CAN_SCHED(td, ts->ts_cpu)) return; if (!TD_IS_RUNNING(td)) return; td->td_flags |= TDF_NEEDRESCHED; if (!THREAD_CAN_MIGRATE(td)) return; /* * Assign the new cpu and force a switch before returning to * userspace. If the target thread is not running locally send * an ipi to force the issue. */ cpu = ts->ts_cpu; ts->ts_cpu = sched_pickcpu(td, 0); if (cpu != PCPU_GET(cpuid)) ipi_selected(1 << cpu, IPI_PREEMPT); #endif } /* * Bind a thread to a target cpu. */ void sched_bind(struct thread *td, int cpu) { struct td_sched *ts; THREAD_LOCK_ASSERT(td, MA_OWNED|MA_NOTRECURSED); ts = td->td_sched; if (ts->ts_flags & TSF_BOUND) sched_unbind(td); ts->ts_flags |= TSF_BOUND; sched_pin(); if (PCPU_GET(cpuid) == cpu) return; ts->ts_cpu = cpu; /* When we return from mi_switch we'll be on the correct cpu. */ mi_switch(SW_VOL, NULL); } /* * Release a bound thread. */ void sched_unbind(struct thread *td) { struct td_sched *ts; THREAD_LOCK_ASSERT(td, MA_OWNED); ts = td->td_sched; if ((ts->ts_flags & TSF_BOUND) == 0) return; ts->ts_flags &= ~TSF_BOUND; sched_unpin(); } int sched_is_bound(struct thread *td) { THREAD_LOCK_ASSERT(td, MA_OWNED); return (td->td_sched->ts_flags & TSF_BOUND); } /* * Basic yield call. */ void sched_relinquish(struct thread *td) { thread_lock(td); mi_switch(SW_VOL | SWT_RELINQUISH, NULL); thread_unlock(td); } /* * Return the total system load. */ int sched_load(void) { #ifdef SMP int total; int i; total = 0; for (i = 0; i <= mp_maxid; i++) total += TDQ_CPU(i)->tdq_sysload; return (total); #else return (TDQ_SELF()->tdq_sysload); #endif } int sched_sizeof_proc(void) { return (sizeof(struct proc)); } int sched_sizeof_thread(void) { return (sizeof(struct thread) + sizeof(struct td_sched)); } /* * The actual idle process. */ void sched_idletd(void *dummy) { struct thread *td; struct tdq *tdq; int switchcnt; int i; td = curthread; tdq = TDQ_SELF(); mtx_assert(&Giant, MA_NOTOWNED); /* ULE relies on preemption for idle interruption. */ for (;;) { tdq->tdq_idlestate = TDQ_RUNNING; #ifdef SMP if (tdq_idled(tdq) == 0) continue; #endif switchcnt = tdq->tdq_switchcnt + tdq->tdq_oldswitchcnt; /* * If we're switching very frequently, spin while checking * for load rather than entering a low power state that * requires an IPI. */ if (switchcnt > sched_idlespinthresh) { for (i = 0; i < sched_idlespins; i++) { if (tdq->tdq_load) break; cpu_spinwait(); } } /* * We must set our state to IDLE before checking * tdq_load for the last time to avoid a race with * tdq_notify(). */ if (tdq->tdq_load == 0) { switchcnt = tdq->tdq_switchcnt + tdq->tdq_oldswitchcnt; tdq->tdq_idlestate = TDQ_IDLE; if (tdq->tdq_load == 0) cpu_idle(switchcnt > 1); } if (tdq->tdq_load) { thread_lock(td); mi_switch(SW_VOL | SWT_IDLE, NULL); thread_unlock(td); } } } /* * A CPU is entering for the first time or a thread is exiting. */ void sched_throw(struct thread *td) { struct thread *newtd; struct tdq *tdq; tdq = TDQ_SELF(); if (td == NULL) { /* Correct spinlock nesting and acquire the correct lock. */ TDQ_LOCK(tdq); spinlock_exit(); } else { MPASS(td->td_lock == TDQ_LOCKPTR(tdq)); tdq_load_rem(tdq, td); lock_profile_release_lock(&TDQ_LOCKPTR(tdq)->lock_object); } KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count")); newtd = choosethread(); TDQ_LOCKPTR(tdq)->mtx_lock = (uintptr_t)newtd; PCPU_SET(switchtime, cpu_ticks()); PCPU_SET(switchticks, ticks); cpu_throw(td, newtd); /* doesn't return */ } /* * This is called from fork_exit(). Just acquire the correct locks and * let fork do the rest of the work. */ void sched_fork_exit(struct thread *td) { struct td_sched *ts; struct tdq *tdq; int cpuid; /* * Finish setting up thread glue so that it begins execution in a * non-nested critical section with the scheduler lock held. */ cpuid = PCPU_GET(cpuid); tdq = TDQ_CPU(cpuid); ts = td->td_sched; if (TD_IS_IDLETHREAD(td)) td->td_lock = TDQ_LOCKPTR(tdq); MPASS(td->td_lock == TDQ_LOCKPTR(tdq)); td->td_oncpu = cpuid; TDQ_LOCK_ASSERT(tdq, MA_OWNED | MA_NOTRECURSED); lock_profile_obtain_lock_success( &TDQ_LOCKPTR(tdq)->lock_object, 0, 0, __FILE__, __LINE__); } #ifdef SMP /* * Build the CPU topology dump string. Is recursively called to collect * the topology tree. */ static int sysctl_kern_sched_topology_spec_internal(struct sbuf *sb, struct cpu_group *cg, int indent) { int i, first; sbuf_printf(sb, "%*s\n", indent, "", indent, cg->cg_level); sbuf_printf(sb, "%*s ", indent, "", cg->cg_count, cg->cg_mask); first = TRUE; for (i = 0; i < MAXCPU; i++) { if ((cg->cg_mask & (1 << i)) != 0) { if (!first) sbuf_printf(sb, ", "); else first = FALSE; sbuf_printf(sb, "%d", i); } } sbuf_printf(sb, "\n"); sbuf_printf(sb, "%*s ", indent, ""); if (cg->cg_flags != 0) { if ((cg->cg_flags & CG_FLAG_HTT) != 0) - sbuf_printf(sb, "HTT group"); + sbuf_printf(sb, "HTT group\n"); if ((cg->cg_flags & CG_FLAG_THREAD) != 0) - sbuf_printf(sb, "SMT group"); + sbuf_printf(sb, "SMT group\n"); } sbuf_printf(sb, "\n"); if (cg->cg_children > 0) { sbuf_printf(sb, "%*s \n", indent, ""); for (i = 0; i < cg->cg_children; i++) sysctl_kern_sched_topology_spec_internal(sb, &cg->cg_child[i], indent+2); sbuf_printf(sb, "%*s \n", indent, ""); } sbuf_printf(sb, "%*s\n", indent, ""); return (0); } /* * Sysctl handler for retrieving topology dump. It's a wrapper for * the recursive sysctl_kern_smp_topology_spec_internal(). */ static int sysctl_kern_sched_topology_spec(SYSCTL_HANDLER_ARGS) { struct sbuf *topo; int err; KASSERT(cpu_top != NULL, ("cpu_top isn't initialized")); topo = sbuf_new(NULL, NULL, 500, SBUF_AUTOEXTEND); if (topo == NULL) return (ENOMEM); sbuf_printf(topo, "\n"); err = sysctl_kern_sched_topology_spec_internal(topo, cpu_top, 1); sbuf_printf(topo, "\n"); if (err == 0) { sbuf_finish(topo); err = SYSCTL_OUT(req, sbuf_data(topo), sbuf_len(topo)); } sbuf_delete(topo); return (err); } #endif SYSCTL_NODE(_kern, OID_AUTO, sched, CTLFLAG_RW, 0, "Scheduler"); SYSCTL_STRING(_kern_sched, OID_AUTO, name, CTLFLAG_RD, "ULE", 0, "Scheduler name"); SYSCTL_INT(_kern_sched, OID_AUTO, slice, CTLFLAG_RW, &sched_slice, 0, "Slice size for timeshare threads"); SYSCTL_INT(_kern_sched, OID_AUTO, interact, CTLFLAG_RW, &sched_interact, 0, "Interactivity score threshold"); SYSCTL_INT(_kern_sched, OID_AUTO, preempt_thresh, CTLFLAG_RW, &preempt_thresh, 0,"Min priority for preemption, lower priorities have greater precedence"); SYSCTL_INT(_kern_sched, OID_AUTO, static_boost, CTLFLAG_RW, &static_boost, 0,"Controls whether static kernel priorities are assigned to sleeping threads."); SYSCTL_INT(_kern_sched, OID_AUTO, idlespins, CTLFLAG_RW, &sched_idlespins, 0,"Number of times idle will spin waiting for new work."); SYSCTL_INT(_kern_sched, OID_AUTO, idlespinthresh, CTLFLAG_RW, &sched_idlespinthresh, 0,"Threshold before we will permit idle spinning."); #ifdef SMP SYSCTL_INT(_kern_sched, OID_AUTO, affinity, CTLFLAG_RW, &affinity, 0, "Number of hz ticks to keep thread affinity for"); SYSCTL_INT(_kern_sched, OID_AUTO, balance, CTLFLAG_RW, &rebalance, 0, "Enables the long-term load balancer"); SYSCTL_INT(_kern_sched, OID_AUTO, balance_interval, CTLFLAG_RW, &balance_interval, 0, "Average frequency in stathz ticks to run the long-term balancer"); SYSCTL_INT(_kern_sched, OID_AUTO, steal_htt, CTLFLAG_RW, &steal_htt, 0, "Steals work from another hyper-threaded core on idle"); SYSCTL_INT(_kern_sched, OID_AUTO, steal_idle, CTLFLAG_RW, &steal_idle, 0, "Attempts to steal work from other cores before idling"); SYSCTL_INT(_kern_sched, OID_AUTO, steal_thresh, CTLFLAG_RW, &steal_thresh, 0, "Minimum load on remote cpu before we'll steal"); /* Retrieve SMP topology */ SYSCTL_PROC(_kern_sched, OID_AUTO, topology_spec, CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, sysctl_kern_sched_topology_spec, "A", "XML dump of detected CPU topology"); #endif /* ps compat. All cpu percentages from ULE are weighted. */ static int ccpu = 0; SYSCTL_INT(_kern, OID_AUTO, ccpu, CTLFLAG_RD, &ccpu, 0, ""); Index: projects/cambria/sys/kern/subr_prf.c =================================================================== --- projects/cambria/sys/kern/subr_prf.c (revision 186459) +++ projects/cambria/sys/kern/subr_prf.c (revision 186460) @@ -1,1049 +1,1057 @@ /*- * Copyright (c) 1986, 1988, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 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. * * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_printf.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif /* * Note that stdarg.h and the ANSI style va_start macro is used for both * ANSI and traditional C compilers. */ #include #define TOCONS 0x01 #define TOTTY 0x02 #define TOLOG 0x04 /* Max number conversion buffer length: a u_quad_t in base 2, plus NUL byte. */ #define MAXNBUF (sizeof(intmax_t) * NBBY + 1) struct putchar_arg { int flags; int pri; struct tty *tty; char *p_bufr; size_t n_bufr; char *p_next; size_t remain; }; struct snprintf_arg { char *str; size_t remain; }; extern int log_open; static void msglogchar(int c, int pri); static void putchar(int ch, void *arg); static char *ksprintn(char *nbuf, uintmax_t num, int base, int *len, int upper); static void snprintf_func(int ch, void *arg); static int msgbufmapped; /* Set when safe to use msgbuf */ int msgbuftrigger; static int log_console_output = 1; TUNABLE_INT("kern.log_console_output", &log_console_output); SYSCTL_INT(_kern, OID_AUTO, log_console_output, CTLFLAG_RW, &log_console_output, 0, "Duplicate console output to the syslog."); static int always_console_output = 0; TUNABLE_INT("kern.always_console_output", &always_console_output); SYSCTL_INT(_kern, OID_AUTO, always_console_output, CTLFLAG_RW, &always_console_output, 0, "Always output to console despite TIOCCONS."); /* * Warn that a system table is full. */ void tablefull(const char *tab) { log(LOG_ERR, "%s: table is full\n", tab); } /* * Uprintf prints to the controlling terminal for the current process. */ int uprintf(const char *fmt, ...) { struct thread *td = curthread; struct proc *p = td->td_proc; va_list ap; struct putchar_arg pca; int retval; if (td == NULL || TD_IS_IDLETHREAD(td)) return (0); sx_slock(&proctree_lock); p = td->td_proc; PROC_LOCK(p); if ((p->p_flag & P_CONTROLT) == 0) { PROC_UNLOCK(p); retval = 0; goto out; } SESS_LOCK(p->p_session); pca.tty = p->p_session->s_ttyp; SESS_UNLOCK(p->p_session); PROC_UNLOCK(p); if (pca.tty == NULL) { retval = 0; goto out; } pca.flags = TOTTY; va_start(ap, fmt); tty_lock(pca.tty); retval = kvprintf(fmt, putchar, &pca, 10, ap); tty_unlock(pca.tty); va_end(ap); out: sx_sunlock(&proctree_lock); return (retval); } /* * tprintf prints on the controlling terminal associated with the given * session, possibly to the log as well. */ void tprintf(struct proc *p, int pri, const char *fmt, ...) { struct tty *tp = NULL; int flags = 0; va_list ap; struct putchar_arg pca; struct session *sess = NULL; sx_slock(&proctree_lock); if (pri != -1) flags |= TOLOG; if (p != NULL) { PROC_LOCK(p); if (p->p_flag & P_CONTROLT && p->p_session->s_ttyvp) { sess = p->p_session; sess_hold(sess); PROC_UNLOCK(p); tp = sess->s_ttyp; if (tp != NULL && tty_checkoutq(tp)) flags |= TOTTY; else tp = NULL; } else PROC_UNLOCK(p); } pca.pri = pri; pca.tty = tp; pca.flags = flags; va_start(ap, fmt); if (pca.tty != NULL) tty_lock(pca.tty); kvprintf(fmt, putchar, &pca, 10, ap); if (pca.tty != NULL) tty_unlock(pca.tty); va_end(ap); if (sess != NULL) sess_release(sess); msgbuftrigger = 1; sx_sunlock(&proctree_lock); } /* * Ttyprintf displays a message on a tty; it should be used only by * the tty driver, or anything that knows the underlying tty will not * be revoke(2)'d away. Other callers should use tprintf. */ int ttyprintf(struct tty *tp, const char *fmt, ...) { va_list ap; struct putchar_arg pca; int retval; va_start(ap, fmt); pca.tty = tp; pca.flags = TOTTY; retval = kvprintf(fmt, putchar, &pca, 10, ap); va_end(ap); return (retval); } /* * Log writes to the log buffer, and guarantees not to sleep (so can be * called by interrupt routines). If there is no process reading the * log yet, it writes to the console also. */ void log(int level, const char *fmt, ...) { va_list ap; struct putchar_arg pca; pca.tty = NULL; pca.pri = level; pca.flags = log_open ? TOLOG : TOCONS; pca.p_bufr = NULL; va_start(ap, fmt); kvprintf(fmt, putchar, &pca, 10, ap); va_end(ap); msgbuftrigger = 1; } #define CONSCHUNK 128 void log_console(struct uio *uio) { - int c, i, error; + int c, i, error, nl; char *consbuffer; int pri; if (!log_console_output) return; pri = LOG_INFO | LOG_CONSOLE; uio = cloneuio(uio); consbuffer = malloc(CONSCHUNK, M_TEMP, M_WAITOK); + nl = 0; while (uio->uio_resid > 0) { c = imin(uio->uio_resid, CONSCHUNK); error = uiomove(consbuffer, c, uio); if (error != 0) break; - for (i = 0; i < c; i++) + for (i = 0; i < c; i++) { msglogchar(consbuffer[i], pri); + if (consbuffer[i] == '\n') + nl = 1; + else + nl = 0; + } } + if (!nl) + msglogchar('\n', pri); msgbuftrigger = 1; free(uio, M_IOV); free(consbuffer, M_TEMP); return; } int printf(const char *fmt, ...) { va_list ap; struct putchar_arg pca; int retval; #ifdef PRINTF_BUFR_SIZE char bufr[PRINTF_BUFR_SIZE]; #endif va_start(ap, fmt); pca.tty = NULL; pca.flags = TOCONS | TOLOG; pca.pri = -1; #ifdef PRINTF_BUFR_SIZE pca.p_bufr = bufr; pca.p_next = pca.p_bufr; pca.n_bufr = sizeof(bufr); pca.remain = sizeof(bufr); *pca.p_next = '\0'; #else /* Don't buffer console output. */ pca.p_bufr = NULL; #endif retval = kvprintf(fmt, putchar, &pca, 10, ap); va_end(ap); #ifdef PRINTF_BUFR_SIZE /* Write any buffered console output: */ if (*pca.p_bufr != '\0') cnputs(pca.p_bufr); #endif if (!panicstr) msgbuftrigger = 1; return (retval); } int vprintf(const char *fmt, va_list ap) { struct putchar_arg pca; int retval; #ifdef PRINTF_BUFR_SIZE char bufr[PRINTF_BUFR_SIZE]; #endif pca.tty = NULL; pca.flags = TOCONS | TOLOG; pca.pri = -1; #ifdef PRINTF_BUFR_SIZE pca.p_bufr = bufr; pca.p_next = pca.p_bufr; pca.n_bufr = sizeof(bufr); pca.remain = sizeof(bufr); *pca.p_next = '\0'; #else /* Don't buffer console output. */ pca.p_bufr = NULL; #endif retval = kvprintf(fmt, putchar, &pca, 10, ap); #ifdef PRINTF_BUFR_SIZE /* Write any buffered console output: */ if (*pca.p_bufr != '\0') cnputs(pca.p_bufr); #endif if (!panicstr) msgbuftrigger = 1; return (retval); } static void putcons(int c, struct putchar_arg *ap) { /* Check if no console output buffer was provided. */ if (ap->p_bufr == NULL) /* Output direct to the console. */ cnputc(c); else { /* Buffer the character: */ if (c == '\n') { *ap->p_next++ = '\r'; ap->remain--; } *ap->p_next++ = c; ap->remain--; /* Always leave the buffer zero terminated. */ *ap->p_next = '\0'; /* Check if the buffer needs to be flushed. */ if (ap->remain < 3 || c == '\n') { cnputs(ap->p_bufr); ap->p_next = ap->p_bufr; ap->remain = ap->n_bufr; *ap->p_next = '\0'; } } } /* * Print a character on console or users terminal. If destination is * the console then the last bunch of characters are saved in msgbuf for * inspection later. */ static void putchar(int c, void *arg) { struct putchar_arg *ap = (struct putchar_arg*) arg; struct tty *tp = ap->tty; int flags = ap->flags; /* Don't use the tty code after a panic or while in ddb. */ if (kdb_active) { if (c != '\0') cnputc(c); } else if (panicstr || ((flags & TOCONS) && constty == NULL)) { if (c != '\0') putcons(c, ap); } else { if ((flags & TOTTY) && tp != NULL) tty_putchar(tp, c); if (flags & TOCONS) { if (constty != NULL) msgbuf_addchar(&consmsgbuf, c); if (always_console_output && c != '\0') putcons(c, ap); } } if ((flags & TOLOG)) msglogchar(c, ap->pri); } /* * Scaled down version of sprintf(3). */ int sprintf(char *buf, const char *cfmt, ...) { int retval; va_list ap; va_start(ap, cfmt); retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); buf[retval] = '\0'; va_end(ap); return (retval); } /* * Scaled down version of vsprintf(3). */ int vsprintf(char *buf, const char *cfmt, va_list ap) { int retval; retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap); buf[retval] = '\0'; return (retval); } /* * Scaled down version of snprintf(3). */ int snprintf(char *str, size_t size, const char *format, ...) { int retval; va_list ap; va_start(ap, format); retval = vsnprintf(str, size, format, ap); va_end(ap); return(retval); } /* * Scaled down version of vsnprintf(3). */ int vsnprintf(char *str, size_t size, const char *format, va_list ap) { struct snprintf_arg info; int retval; info.str = str; info.remain = size; retval = kvprintf(format, snprintf_func, &info, 10, ap); if (info.remain >= 1) *info.str++ = '\0'; return (retval); } /* * Kernel version which takes radix argument vsnprintf(3). */ int vsnrprintf(char *str, size_t size, int radix, const char *format, va_list ap) { struct snprintf_arg info; int retval; info.str = str; info.remain = size; retval = kvprintf(format, snprintf_func, &info, radix, ap); if (info.remain >= 1) *info.str++ = '\0'; return (retval); } static void snprintf_func(int ch, void *arg) { struct snprintf_arg *const info = arg; if (info->remain >= 2) { *info->str++ = ch; info->remain--; } } /* * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse * order; return an optional length and a pointer to the last character * written in the buffer (i.e., the first character of the string). * The buffer pointed to by `nbuf' must have length >= MAXNBUF. */ static char * ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) { char *p, c; p = nbuf; *p = '\0'; do { c = hex2ascii(num % base); *++p = upper ? toupper(c) : c; } while (num /= base); if (lenp) *lenp = p - nbuf; return (p); } /* * Scaled down version of printf(3). * * Two additional formats: * * The format %b is supported to decode error registers. * Its usage is: * * printf("reg=%b\n", regval, "*"); * * where is the output base expressed as a control character, e.g. * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, * the first of which gives the bit number to be inspected (origin 1), and * the next characters (up to a control character, i.e. a character <= 32), * give the name of the register. Thus: * * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); * * would produce output: * * reg=3 * * XXX: %D -- Hexdump, takes pointer and separator string: * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX * ("%*D", len, ptr, " " -> XX XX XX XX ... */ int kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap) { #define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; } char nbuf[MAXNBUF]; char *d; const char *p, *percent, *q; u_char *up; int ch, n; uintmax_t num; int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; int cflag, hflag, jflag, tflag, zflag; int dwidth, upper; char padc; int stop = 0, retval = 0; num = 0; if (!func) d = (char *) arg; else d = NULL; if (fmt == NULL) fmt = "(fmt null)\n"; if (radix < 2 || radix > 36) radix = 10; for (;;) { padc = ' '; width = 0; while ((ch = (u_char)*fmt++) != '%' || stop) { if (ch == '\0') return (retval); PCHAR(ch); } percent = fmt - 1; qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; sign = 0; dot = 0; dwidth = 0; upper = 0; cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; reswitch: switch (ch = (u_char)*fmt++) { case '.': dot = 1; goto reswitch; case '#': sharpflag = 1; goto reswitch; case '+': sign = 1; goto reswitch; case '-': ladjust = 1; goto reswitch; case '%': PCHAR(ch); break; case '*': if (!dot) { width = va_arg(ap, int); if (width < 0) { ladjust = !ladjust; width = -width; } } else { dwidth = va_arg(ap, int); } goto reswitch; case '0': if (!dot) { padc = '0'; goto reswitch; } case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (n = 0;; ++fmt) { n = n * 10 + ch - '0'; ch = *fmt; if (ch < '0' || ch > '9') break; } if (dot) dwidth = n; else width = n; goto reswitch; case 'b': num = (u_int)va_arg(ap, int); p = va_arg(ap, char *); for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) PCHAR(*q--); if (num == 0) break; for (tmp = 0; *p;) { n = *p++; if (num & (1 << (n - 1))) { PCHAR(tmp ? ',' : '<'); for (; (n = *p) > ' '; ++p) PCHAR(n); tmp = 1; } else for (; *p > ' '; ++p) continue; } if (tmp) PCHAR('>'); break; case 'c': PCHAR(va_arg(ap, int)); break; case 'D': up = va_arg(ap, u_char *); p = va_arg(ap, char *); if (!width) width = 16; while(width--) { PCHAR(hex2ascii(*up >> 4)); PCHAR(hex2ascii(*up & 0x0f)); up++; if (width) for (q=p;*q;q++) PCHAR(*q); } break; case 'd': case 'i': base = 10; sign = 1; goto handle_sign; case 'h': if (hflag) { hflag = 0; cflag = 1; } else hflag = 1; goto reswitch; case 'j': jflag = 1; goto reswitch; case 'l': if (lflag) { lflag = 0; qflag = 1; } else lflag = 1; goto reswitch; case 'n': if (jflag) *(va_arg(ap, intmax_t *)) = retval; else if (qflag) *(va_arg(ap, quad_t *)) = retval; else if (lflag) *(va_arg(ap, long *)) = retval; else if (zflag) *(va_arg(ap, size_t *)) = retval; else if (hflag) *(va_arg(ap, short *)) = retval; else if (cflag) *(va_arg(ap, char *)) = retval; else *(va_arg(ap, int *)) = retval; break; case 'o': base = 8; goto handle_nosign; case 'p': base = 16; sharpflag = (width == 0); sign = 0; num = (uintptr_t)va_arg(ap, void *); goto number; case 'q': qflag = 1; goto reswitch; case 'r': base = radix; if (sign) goto handle_sign; goto handle_nosign; case 's': p = va_arg(ap, char *); if (p == NULL) p = "(null)"; if (!dot) n = strlen (p); else for (n = 0; n < dwidth && p[n]; n++) continue; width -= n; if (!ladjust && width > 0) while (width--) PCHAR(padc); while (n--) PCHAR(*p++); if (ladjust && width > 0) while (width--) PCHAR(padc); break; case 't': tflag = 1; goto reswitch; case 'u': base = 10; goto handle_nosign; case 'X': upper = 1; case 'x': base = 16; goto handle_nosign; case 'y': base = 16; sign = 1; goto handle_sign; case 'z': zflag = 1; goto reswitch; handle_nosign: sign = 0; if (jflag) num = va_arg(ap, uintmax_t); else if (qflag) num = va_arg(ap, u_quad_t); else if (tflag) num = va_arg(ap, ptrdiff_t); else if (lflag) num = va_arg(ap, u_long); else if (zflag) num = va_arg(ap, size_t); else if (hflag) num = (u_short)va_arg(ap, int); else if (cflag) num = (u_char)va_arg(ap, int); else num = va_arg(ap, u_int); goto number; handle_sign: if (jflag) num = va_arg(ap, intmax_t); else if (qflag) num = va_arg(ap, quad_t); else if (tflag) num = va_arg(ap, ptrdiff_t); else if (lflag) num = va_arg(ap, long); else if (zflag) num = va_arg(ap, ssize_t); else if (hflag) num = (short)va_arg(ap, int); else if (cflag) num = (char)va_arg(ap, int); else num = va_arg(ap, int); number: if (sign && (intmax_t)num < 0) { neg = 1; num = -(intmax_t)num; } p = ksprintn(nbuf, num, base, &tmp, upper); if (sharpflag && num != 0) { if (base == 8) tmp++; else if (base == 16) tmp += 2; } if (neg) tmp++; if (!ladjust && padc != '0' && width && (width -= tmp) > 0) while (width--) PCHAR(padc); if (neg) PCHAR('-'); if (sharpflag && num != 0) { if (base == 8) { PCHAR('0'); } else if (base == 16) { PCHAR('0'); PCHAR('x'); } } if (!ladjust && width && (width -= tmp) > 0) while (width--) PCHAR(padc); while (*p) PCHAR(*p--); if (ladjust && width && (width -= tmp) > 0) while (width--) PCHAR(padc); break; default: while (percent < fmt) PCHAR(*percent++); /* * Since we ignore an formatting argument it is no * longer safe to obey the remaining formatting * arguments as the arguments will no longer match * the format specs. */ stop = 1; break; } } #undef PCHAR } /* * Put character in log buffer with a particular priority. */ static void msglogchar(int c, int pri) { static int lastpri = -1; static int dangling; char nbuf[MAXNBUF]; char *p; if (!msgbufmapped) return; if (c == '\0' || c == '\r') return; if (pri != -1 && pri != lastpri) { if (dangling) { msgbuf_addchar(msgbufp, '\n'); dangling = 0; } msgbuf_addchar(msgbufp, '<'); for (p = ksprintn(nbuf, (uintmax_t)pri, 10, NULL, 0); *p;) msgbuf_addchar(msgbufp, *p--); msgbuf_addchar(msgbufp, '>'); lastpri = pri; } msgbuf_addchar(msgbufp, c); if (c == '\n') { dangling = 0; lastpri = -1; } else { dangling = 1; } } void msgbufinit(void *ptr, int size) { char *cp; static struct msgbuf *oldp = NULL; size -= sizeof(*msgbufp); cp = (char *)ptr; msgbufp = (struct msgbuf *)(cp + size); msgbuf_reinit(msgbufp, cp, size); if (msgbufmapped && oldp != msgbufp) msgbuf_copy(oldp, msgbufp); msgbufmapped = 1; oldp = msgbufp; } static int unprivileged_read_msgbuf = 1; SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf, CTLFLAG_RW, &unprivileged_read_msgbuf, 0, "Unprivileged processes may read the kernel message buffer"); /* Sysctls for accessing/clearing the msgbuf */ static int sysctl_kern_msgbuf(SYSCTL_HANDLER_ARGS) { char buf[128]; u_int seq; int error, len; if (!unprivileged_read_msgbuf) { error = priv_check(req->td, PRIV_MSGBUF); if (error) return (error); } /* Read the whole buffer, one chunk at a time. */ msgbuf_peekbytes(msgbufp, NULL, 0, &seq); while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) { error = sysctl_handle_opaque(oidp, buf, len, req); if (error) return (error); } return (0); } SYSCTL_PROC(_kern, OID_AUTO, msgbuf, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_kern_msgbuf, "A", "Contents of kernel message buffer"); static int msgbuf_clearflag; static int sysctl_kern_msgbuf_clear(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { msgbuf_clear(msgbufp); msgbuf_clearflag = 0; } return (error); } SYSCTL_PROC(_kern, OID_AUTO, msgbuf_clear, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE, &msgbuf_clearflag, 0, sysctl_kern_msgbuf_clear, "I", "Clear kernel message buffer"); #ifdef DDB DB_SHOW_COMMAND(msgbuf, db_show_msgbuf) { int i, j; if (!msgbufmapped) { db_printf("msgbuf not mapped yet\n"); return; } db_printf("msgbufp = %p\n", msgbufp); db_printf("magic = %x, size = %d, r= %u, w = %u, ptr = %p, cksum= %u\n", msgbufp->msg_magic, msgbufp->msg_size, msgbufp->msg_rseq, msgbufp->msg_wseq, msgbufp->msg_ptr, msgbufp->msg_cksum); for (i = 0; i < msgbufp->msg_size && !db_pager_quit; i++) { j = MSGBUF_SEQ_TO_POS(msgbufp, i + msgbufp->msg_rseq); db_printf("%c", msgbufp->msg_ptr[j]); } db_printf("\n"); } #endif /* DDB */ void hexdump(const void *ptr, int length, const char *hdr, int flags) { int i, j, k; int cols; const unsigned char *cp; char delim; if ((flags & HD_DELIM_MASK) != 0) delim = (flags & HD_DELIM_MASK) >> 8; else delim = ' '; if ((flags & HD_COLUMN_MASK) != 0) cols = flags & HD_COLUMN_MASK; else cols = 16; cp = ptr; for (i = 0; i < length; i+= cols) { if (hdr != NULL) printf("%s", hdr); if ((flags & HD_OMIT_COUNT) == 0) printf("%04x ", i); if ((flags & HD_OMIT_HEX) == 0) { for (j = 0; j < cols; j++) { k = i + j; if (k < length) printf("%c%02x", delim, cp[k]); else printf(" "); } } if ((flags & HD_OMIT_CHARS) == 0) { printf(" |"); for (j = 0; j < cols; j++) { k = i + j; if (k >= length) printf(" "); else if (cp[k] >= ' ' && cp[k] <= '~') printf("%c", cp[k]); else printf("."); } printf("|"); } printf("\n"); } } Index: projects/cambria/sys/kern/tty.c =================================================================== --- projects/cambria/sys/kern/tty.c (revision 186459) +++ projects/cambria/sys/kern/tty.c (revision 186460) @@ -1,2032 +1,2032 @@ /*- * Copyright (c) 2008 Ed Schouten * All rights reserved. * * Portions of this software were developed under sponsorship from Snow * B.V., the Netherlands. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #ifdef COMPAT_43TTY #include #endif /* COMPAT_43TTY */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TTYDEFCHARS #include #undef TTYDEFCHARS #include #include #include static MALLOC_DEFINE(M_TTY, "tty", "tty device"); static void tty_rel_free(struct tty *tp); static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); static struct sx tty_list_sx; SX_SYSINIT(tty_list, &tty_list_sx, "tty list"); static unsigned int tty_list_count = 0; /* Character device of /dev/console. */ static struct cdev *dev_console; static const char *dev_console_filename; /* * Flags that are supported and stored by this implementation. */ #define TTYSUP_IFLAG (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|\ INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL) #define TTYSUP_OFLAG (OPOST|ONLCR|TAB3|ONOEOT|OCRNL|ONOCR|ONLRET) #define TTYSUP_LFLAG (ECHOKE|ECHOE|ECHOK|ECHO|ECHONL|ECHOPRT|\ ECHOCTL|ISIG|ICANON|ALTWERASE|IEXTEN|TOSTOP|\ FLUSHO|NOKERNINFO|NOFLSH) #define TTYSUP_CFLAG (CIGNORE|CSIZE|CSTOPB|CREAD|PARENB|PARODD|\ HUPCL|CLOCAL|CCTS_OFLOW|CRTS_IFLOW|CDTR_IFLOW|\ CDSR_OFLOW|CCAR_OFLOW) #define TTY_CALLOUT(tp,d) ((d) != (tp)->t_dev && (d) != dev_console) /* * Set TTY buffer sizes. */ #define TTYBUF_MAX 65536 static void tty_watermarks(struct tty *tp) { size_t bs; /* Provide an input buffer for 0.2 seconds of data. */ bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); ttyinq_setsize(&tp->t_inq, tp, bs); /* Set low watermark at 10% (when 90% is available). */ tp->t_inlow = (ttyinq_getsize(&tp->t_inq) * 9) / 10; /* Provide an ouput buffer for 0.2 seconds of data. */ bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); ttyoutq_setsize(&tp->t_outq, tp, bs); /* Set low watermark at 10% (when 90% is available). */ tp->t_outlow = (ttyoutq_getsize(&tp->t_outq) * 9) / 10; } static int tty_drain(struct tty *tp) { int error; if (ttyhook_hashook(tp, getc_inject)) /* buffer is inaccessible */ return (0); while (ttyoutq_bytesused(&tp->t_outq) > 0) { ttydevsw_outwakeup(tp); /* Could be handled synchronously. */ if (ttyoutq_bytesused(&tp->t_outq) == 0) return (0); /* Wait for data to be drained. */ error = tty_wait(tp, &tp->t_outwait); if (error) return (error); } return (0); } /* * Though ttydev_enter() and ttydev_leave() seem to be related, they * don't have to be used together. ttydev_enter() is used by the cdev * operations to prevent an actual operation from being processed when * the TTY has been abandoned. ttydev_leave() is used by ttydev_open() * and ttydev_close() to determine whether per-TTY data should be * deallocated. */ static __inline int ttydev_enter(struct tty *tp) { tty_lock(tp); if (tty_gone(tp) || !tty_opened(tp)) { /* Device is already gone. */ tty_unlock(tp); return (ENXIO); } return (0); } static void ttydev_leave(struct tty *tp) { tty_lock_assert(tp, MA_OWNED); if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { /* Device is still opened somewhere. */ tty_unlock(tp); return; } tp->t_flags |= TF_OPENCLOSE; /* Stop asynchronous I/O. */ funsetown(&tp->t_sigio); /* Remove console TTY. */ if (constty == tp) constty_clear(); /* Drain any output. */ MPASS((tp->t_flags & TF_STOPPED) == 0); if (!tty_gone(tp)) tty_drain(tp); ttydisc_close(tp); /* Destroy associated buffers already. */ ttyinq_free(&tp->t_inq); tp->t_inlow = 0; ttyoutq_free(&tp->t_outq); tp->t_outlow = 0; knlist_clear(&tp->t_inpoll.si_note, 1); knlist_clear(&tp->t_outpoll.si_note, 1); if (!tty_gone(tp)) ttydevsw_close(tp); tp->t_flags &= ~TF_OPENCLOSE; tty_rel_free(tp); } /* * Operations that are exposed through the character device in /dev. */ static int ttydev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct tty *tp = dev->si_drv1; int error = 0; /* Disallow access when the TTY belongs to a different prison. */ if (dev->si_cred != NULL && dev->si_cred->cr_prison != td->td_ucred->cr_prison && priv_check(td, PRIV_TTY_PRISON)) { return (EPERM); } tty_lock(tp); if (tty_gone(tp)) { /* Device is already gone. */ tty_unlock(tp); return (ENXIO); } /* * Prevent the TTY from being opened when being torn down or * built up by unrelated processes. */ if (tp->t_flags & TF_OPENCLOSE) { tty_unlock(tp); return (EBUSY); } tp->t_flags |= TF_OPENCLOSE; /* * Make sure the "tty" and "cua" device cannot be opened at the * same time. */ if (TTY_CALLOUT(tp, dev)) { if (tp->t_flags & TF_OPENED_IN) { error = EBUSY; goto done; } } else { if (tp->t_flags & TF_OPENED_OUT) { error = EBUSY; goto done; } } if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { error = EBUSY; goto done; } if (!tty_opened(tp)) { /* Set proper termios flags. */ if (TTY_CALLOUT(tp, dev)) { tp->t_termios = tp->t_termios_init_out; } else { tp->t_termios = tp->t_termios_init_in; } ttydevsw_param(tp, &tp->t_termios); ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); error = ttydevsw_open(tp); if (error != 0) goto done; ttydisc_open(tp); tty_watermarks(tp); } /* Wait for Carrier Detect. */ if (!TTY_CALLOUT(tp, dev) && (oflags & O_NONBLOCK) == 0 && (tp->t_termios.c_cflag & CLOCAL) == 0) { while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { error = tty_wait(tp, &tp->t_dcdwait); if (error != 0) goto done; } } if (TTY_CALLOUT(tp, dev)) { tp->t_flags |= TF_OPENED_OUT; } else { tp->t_flags |= TF_OPENED_IN; } done: tp->t_flags &= ~TF_OPENCLOSE; ttydev_leave(tp); return (error); } static int ttydev_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct tty *tp = dev->si_drv1; tty_lock(tp); /* * This can only be called once. The callin and the callout * devices cannot be opened at the same time. */ MPASS((tp->t_flags & TF_OPENED) != TF_OPENED); tp->t_flags &= ~(TF_OPENED|TF_EXCLUDE|TF_STOPPED); /* Properly wake up threads that are stuck - revoke(). */ tp->t_revokecnt++; tty_wakeup(tp, FREAD|FWRITE); cv_broadcast(&tp->t_bgwait); ttydev_leave(tp); return (0); } static __inline int tty_is_ctty(struct tty *tp, struct proc *p) { tty_lock_assert(tp, MA_OWNED); return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); } static int tty_wait_background(struct tty *tp, struct thread *td, int sig) { struct proc *p = td->td_proc; struct pgrp *pg; int error; MPASS(sig == SIGTTIN || sig == SIGTTOU); tty_lock_assert(tp, MA_OWNED); for (;;) { PROC_LOCK(p); /* * The process should only sleep, when: * - This terminal is the controling terminal * - Its process group is not the foreground process * group * - The parent process isn't waiting for the child to * exit * - the signal to send to the process isn't masked */ if (!tty_is_ctty(tp, p) || p->p_pgrp == tp->t_pgrp || p->p_flag & P_PPWAIT || SIGISMEMBER(p->p_sigacts->ps_sigignore, sig) || SIGISMEMBER(td->td_sigmask, sig)) { /* Allow the action to happen. */ PROC_UNLOCK(p); return (0); } /* * Send the signal and sleep until we're the new * foreground process group. */ pg = p->p_pgrp; PROC_UNLOCK(p); if (pg->pg_jobc == 0) return (EIO); PGRP_LOCK(pg); pgsignal(pg, sig, 1); PGRP_UNLOCK(pg); error = tty_wait(tp, &tp->t_bgwait); if (error) return (error); } } static int ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) goto done; error = tty_wait_background(tp, curthread, SIGTTIN); if (error) { tty_unlock(tp); goto done; } error = ttydisc_read(tp, uio, ioflag); tty_unlock(tp); /* * The read() call should not throw an error when the device is * being destroyed. Silently convert it to an EOF. */ done: if (error == ENXIO) error = 0; return (error); } static int ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) return (error); if (tp->t_termios.c_lflag & TOSTOP) { error = tty_wait_background(tp, curthread, SIGTTOU); if (error) { tty_unlock(tp); return (error); } } error = ttydisc_write(tp, uio, ioflag); tty_unlock(tp); return (error); } static int ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) return (error); switch (cmd) { case TIOCCBRK: case TIOCCONS: case TIOCDRAIN: case TIOCEXCL: case TIOCFLUSH: case TIOCNXCL: case TIOCSBRK: case TIOCSCTTY: case TIOCSETA: case TIOCSETAF: case TIOCSETAW: case TIOCSPGRP: case TIOCSTART: case TIOCSTAT: case TIOCSTOP: case TIOCSWINSZ: #if 0 case TIOCSDRAINWAIT: case TIOCSETD: case TIOCSTI: #endif #ifdef COMPAT_43TTY case TIOCLBIC: case TIOCLBIS: case TIOCLSET: case TIOCSETC: case OTIOCSETD: case TIOCSETN: case TIOCSETP: case TIOCSLTC: #endif /* COMPAT_43TTY */ /* * If the ioctl() causes the TTY to be modified, let it * wait in the background. */ error = tty_wait_background(tp, curthread, SIGTTOU); if (error) goto done; } error = tty_ioctl(tp, cmd, data, td); done: tty_unlock(tp); return (error); } static int ttydev_poll(struct cdev *dev, int events, struct thread *td) { struct tty *tp = dev->si_drv1; int error, revents = 0; error = ttydev_enter(tp); if (error) { /* Don't return the error here, but the event mask. */ return (events & (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); } if (events & (POLLIN|POLLRDNORM)) { /* See if we can read something. */ if (ttydisc_read_poll(tp) > 0) revents |= events & (POLLIN|POLLRDNORM); } if (events & (POLLOUT|POLLWRNORM)) { /* See if we can write something. */ if (ttydisc_write_poll(tp) > 0) revents |= events & (POLLOUT|POLLWRNORM); } if (tp->t_flags & TF_ZOMBIE) /* Hangup flag on zombie state. */ revents |= events & POLLHUP; if (revents == 0) { if (events & (POLLIN|POLLRDNORM)) selrecord(td, &tp->t_inpoll); if (events & (POLLOUT|POLLWRNORM)) selrecord(td, &tp->t_outpoll); } tty_unlock(tp); return (revents); } static int ttydev_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { struct tty *tp = dev->si_drv1; int error; /* Handle mmap() through the driver. */ error = ttydev_enter(tp); if (error) return (-1); error = ttydevsw_mmap(tp, offset, paddr, nprot); tty_unlock(tp); return (error); } /* * kqueue support. */ static void tty_kqops_read_detach(struct knote *kn) { struct tty *tp = kn->kn_hook; knlist_remove(&tp->t_inpoll.si_note, kn, 0); } static int tty_kqops_read_event(struct knote *kn, long hint) { struct tty *tp = kn->kn_hook; tty_lock_assert(tp, MA_OWNED); if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { kn->kn_flags |= EV_EOF; return (1); } else { kn->kn_data = ttydisc_read_poll(tp); return (kn->kn_data > 0); } } static void tty_kqops_write_detach(struct knote *kn) { struct tty *tp = kn->kn_hook; knlist_remove(&tp->t_outpoll.si_note, kn, 0); } static int tty_kqops_write_event(struct knote *kn, long hint) { struct tty *tp = kn->kn_hook; tty_lock_assert(tp, MA_OWNED); if (tty_gone(tp)) { kn->kn_flags |= EV_EOF; return (1); } else { kn->kn_data = ttydisc_write_poll(tp); return (kn->kn_data > 0); } } static struct filterops tty_kqops_read = { 1, NULL, tty_kqops_read_detach, tty_kqops_read_event }; static struct filterops tty_kqops_write = { 1, NULL, tty_kqops_write_detach, tty_kqops_write_event }; static int ttydev_kqfilter(struct cdev *dev, struct knote *kn) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) return (error); switch (kn->kn_filter) { case EVFILT_READ: kn->kn_hook = tp; kn->kn_fop = &tty_kqops_read; knlist_add(&tp->t_inpoll.si_note, kn, 1); break; case EVFILT_WRITE: kn->kn_hook = tp; kn->kn_fop = &tty_kqops_write; knlist_add(&tp->t_outpoll.si_note, kn, 1); break; default: error = EINVAL; break; } tty_unlock(tp); return (error); } static struct cdevsw ttydev_cdevsw = { .d_version = D_VERSION, .d_open = ttydev_open, .d_close = ttydev_close, .d_read = ttydev_read, .d_write = ttydev_write, .d_ioctl = ttydev_ioctl, .d_kqfilter = ttydev_kqfilter, .d_poll = ttydev_poll, .d_mmap = ttydev_mmap, .d_name = "ttydev", .d_flags = D_TTY, }; /* * Init/lock-state devices */ static int ttyil_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct tty *tp = dev->si_drv1; int error = 0; tty_lock(tp); if (tty_gone(tp)) error = ENODEV; tty_unlock(tp); return (error); } static int ttyil_close(struct cdev *dev, int flag, int mode, struct thread *td) { return (0); } static int ttyil_rdwr(struct cdev *dev, struct uio *uio, int ioflag) { return (ENODEV); } static int ttyil_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct tty *tp = dev->si_drv1; int error = 0; tty_lock(tp); if (tty_gone(tp)) { error = ENODEV; goto done; } switch (cmd) { case TIOCGETA: /* Obtain terminal flags through tcgetattr(). */ bcopy(dev->si_drv2, data, sizeof(struct termios)); break; case TIOCSETA: /* Set terminal flags through tcsetattr(). */ error = priv_check(td, PRIV_TTY_SETA); if (error) break; bcopy(data, dev->si_drv2, sizeof(struct termios)); break; case TIOCGETD: *(int *)data = TTYDISC; break; case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); break; default: error = ENOTTY; } done: tty_unlock(tp); return (error); } static struct cdevsw ttyil_cdevsw = { .d_version = D_VERSION, .d_open = ttyil_open, .d_close = ttyil_close, .d_read = ttyil_rdwr, .d_write = ttyil_rdwr, .d_ioctl = ttyil_ioctl, .d_name = "ttyil", .d_flags = D_TTY, }; static void tty_init_termios(struct tty *tp) { struct termios *t = &tp->t_termios_init_in; t->c_cflag = TTYDEF_CFLAG; t->c_iflag = TTYDEF_IFLAG; t->c_lflag = TTYDEF_LFLAG; t->c_oflag = TTYDEF_OFLAG; t->c_ispeed = TTYDEF_SPEED; t->c_ospeed = TTYDEF_SPEED; bcopy(ttydefchars, &t->c_cc, sizeof ttydefchars); tp->t_termios_init_out = *t; } void tty_init_console(struct tty *tp, speed_t s) { struct termios *ti = &tp->t_termios_init_in; struct termios *to = &tp->t_termios_init_out; if (s != 0) { ti->c_ispeed = ti->c_ospeed = s; to->c_ispeed = to->c_ospeed = s; } ti->c_cflag |= CLOCAL; to->c_cflag |= CLOCAL; } /* * Standard device routine implementations, mostly meant for * pseudo-terminal device drivers. When a driver creates a new terminal * device class, missing routines are patched. */ static int ttydevsw_defopen(struct tty *tp) { return (0); } static void ttydevsw_defclose(struct tty *tp) { } static void ttydevsw_defoutwakeup(struct tty *tp) { panic("Terminal device has output, while not implemented"); } static void ttydevsw_definwakeup(struct tty *tp) { } static int ttydevsw_defioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { return (ENOIOCTL); } static int ttydevsw_defparam(struct tty *tp, struct termios *t) { /* Use a fake baud rate, we're not a real device. */ t->c_ispeed = t->c_ospeed = TTYDEF_SPEED; return (0); } static int ttydevsw_defmodem(struct tty *tp, int sigon, int sigoff) { /* Simulate a carrier to make the TTY layer happy. */ return (SER_DCD); } static int ttydevsw_defmmap(struct tty *tp, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { return (-1); } static void ttydevsw_defpktnotify(struct tty *tp, char event) { } static void ttydevsw_deffree(void *softc) { panic("Terminal device freed without a free-handler"); } /* * TTY allocation and deallocation. TTY devices can be deallocated when * the driver doesn't use it anymore, when the TTY isn't a session's * controlling TTY and when the device node isn't opened through devfs. */ struct tty * tty_alloc(struct ttydevsw *tsw, void *sc, struct mtx *mutex) { struct tty *tp; /* Make sure the driver defines all routines. */ #define PATCH_FUNC(x) do { \ if (tsw->tsw_ ## x == NULL) \ tsw->tsw_ ## x = ttydevsw_def ## x; \ } while (0) PATCH_FUNC(open); PATCH_FUNC(close); PATCH_FUNC(outwakeup); PATCH_FUNC(inwakeup); PATCH_FUNC(ioctl); PATCH_FUNC(param); PATCH_FUNC(modem); PATCH_FUNC(mmap); PATCH_FUNC(pktnotify); PATCH_FUNC(free); #undef PATCH_FUNC tp = malloc(sizeof(struct tty), M_TTY, M_WAITOK|M_ZERO); tp->t_devsw = tsw; tp->t_devswsoftc = sc; tp->t_flags = tsw->tsw_flags; tty_init_termios(tp); - cv_init(&tp->t_inwait, "ttyinp"); + cv_init(&tp->t_inwait, "ttyin"); cv_init(&tp->t_outwait, "ttyout"); - cv_init(&tp->t_bgwait, "ttybgw"); + cv_init(&tp->t_bgwait, "ttybg"); cv_init(&tp->t_dcdwait, "ttydcd"); ttyinq_init(&tp->t_inq); ttyoutq_init(&tp->t_outq); /* Allow drivers to use a custom mutex to lock the TTY. */ if (mutex != NULL) { tp->t_mtx = mutex; } else { tp->t_mtx = &tp->t_mtxobj; - mtx_init(&tp->t_mtxobj, "ttylck", NULL, MTX_DEF); + mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF); } knlist_init(&tp->t_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL); knlist_init(&tp->t_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL); sx_xlock(&tty_list_sx); TAILQ_INSERT_TAIL(&tty_list, tp, t_list); tty_list_count++; sx_xunlock(&tty_list_sx); return (tp); } static void tty_dealloc(void *arg) { struct tty *tp = arg; sx_xlock(&tty_list_sx); TAILQ_REMOVE(&tty_list, tp, t_list); tty_list_count--; sx_xunlock(&tty_list_sx); /* Make sure we haven't leaked buffers. */ MPASS(ttyinq_getsize(&tp->t_inq) == 0); MPASS(ttyoutq_getsize(&tp->t_outq) == 0); knlist_destroy(&tp->t_inpoll.si_note); knlist_destroy(&tp->t_outpoll.si_note); cv_destroy(&tp->t_inwait); cv_destroy(&tp->t_outwait); cv_destroy(&tp->t_bgwait); cv_destroy(&tp->t_dcdwait); if (tp->t_mtx == &tp->t_mtxobj) mtx_destroy(&tp->t_mtxobj); ttydevsw_free(tp); free(tp, M_TTY); } static void tty_rel_free(struct tty *tp) { struct cdev *dev; tty_lock_assert(tp, MA_OWNED); #define TF_ACTIVITY (TF_GONE|TF_OPENED|TF_HOOK|TF_OPENCLOSE) if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE) { /* TTY is still in use. */ tty_unlock(tp); return; } /* TTY can be deallocated. */ dev = tp->t_dev; tp->t_dev = NULL; tty_unlock(tp); destroy_dev_sched_cb(dev, tty_dealloc, tp); } void tty_rel_pgrp(struct tty *tp, struct pgrp *pg) { MPASS(tp->t_sessioncnt > 0); tty_lock_assert(tp, MA_OWNED); if (tp->t_pgrp == pg) tp->t_pgrp = NULL; tty_unlock(tp); } void tty_rel_sess(struct tty *tp, struct session *sess) { MPASS(tp->t_sessioncnt > 0); /* Current session has left. */ if (tp->t_session == sess) { tp->t_session = NULL; MPASS(tp->t_pgrp == NULL); } tp->t_sessioncnt--; tty_rel_free(tp); } void tty_rel_gone(struct tty *tp) { MPASS(!tty_gone(tp)); /* Simulate carrier removal. */ ttydisc_modem(tp, 0); /* Wake up all blocked threads. */ tty_wakeup(tp, FREAD|FWRITE); cv_broadcast(&tp->t_bgwait); cv_broadcast(&tp->t_dcdwait); tp->t_flags |= TF_GONE; tty_rel_free(tp); } /* * Exposing information about current TTY's through sysctl */ static void tty_to_xtty(struct tty *tp, struct xtty *xt) { tty_lock_assert(tp, MA_OWNED); xt->xt_size = sizeof(struct xtty); xt->xt_insize = ttyinq_getsize(&tp->t_inq); xt->xt_incc = ttyinq_bytescanonicalized(&tp->t_inq); xt->xt_inlc = ttyinq_bytesline(&tp->t_inq); xt->xt_inlow = tp->t_inlow; xt->xt_outsize = ttyoutq_getsize(&tp->t_outq); xt->xt_outcc = ttyoutq_bytesused(&tp->t_outq); xt->xt_outlow = tp->t_outlow; xt->xt_column = tp->t_column; xt->xt_pgid = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; xt->xt_sid = tp->t_session ? tp->t_session->s_sid : 0; xt->xt_flags = tp->t_flags; xt->xt_dev = tp->t_dev ? dev2udev(tp->t_dev) : NODEV; } static int sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) { unsigned long lsize; struct xtty *xtlist, *xt; struct tty *tp; int error; sx_slock(&tty_list_sx); lsize = tty_list_count * sizeof(struct xtty); if (lsize == 0) { sx_sunlock(&tty_list_sx); return (0); } xtlist = xt = malloc(lsize, M_TEMP, M_WAITOK); TAILQ_FOREACH(tp, &tty_list, t_list) { tty_lock(tp); tty_to_xtty(tp, xt); tty_unlock(tp); xt++; } sx_sunlock(&tty_list_sx); error = SYSCTL_OUT(req, xtlist, lsize); free(xtlist, M_TEMP); return (error); } SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_kern_ttys, "S,xtty", "List of TTYs"); /* * Device node creation. Device has been set up, now we can expose it to * the user. */ void tty_makedev(struct tty *tp, struct ucred *cred, const char *fmt, ...) { va_list ap; struct cdev *dev; const char *prefix = "tty"; char name[SPECNAMELEN - 3]; /* for "tty" and "cua". */ uid_t uid; gid_t gid; mode_t mode; /* Remove "tty" prefix from devices like PTY's. */ if (tp->t_flags & TF_NOPREFIX) prefix = ""; va_start(ap, fmt); vsnrprintf(name, sizeof name, 32, fmt, ap); va_end(ap); if (cred == NULL) { /* System device. */ uid = UID_ROOT; gid = GID_WHEEL; mode = S_IRUSR|S_IWUSR; } else { /* User device. */ uid = cred->cr_ruid; gid = GID_TTY; mode = S_IRUSR|S_IWUSR|S_IWGRP; } /* Master call-in device. */ dev = make_dev_cred(&ttydev_cdevsw, 0, cred, uid, gid, mode, "%s%s", prefix, name); dev->si_drv1 = tp; tp->t_dev = dev; /* Slave call-in devices. */ if (tp->t_flags & TF_INITLOCK) { dev = make_dev_cred(&ttyil_cdevsw, 0, cred, uid, gid, mode, "%s%s.init", prefix, name); dev_depends(tp->t_dev, dev); dev->si_drv1 = tp; dev->si_drv2 = &tp->t_termios_init_in; dev = make_dev_cred(&ttyil_cdevsw, 0, cred, uid, gid, mode, "%s%s.lock", prefix, name); dev_depends(tp->t_dev, dev); dev->si_drv1 = tp; dev->si_drv2 = &tp->t_termios_lock_in; } /* Call-out devices. */ if (tp->t_flags & TF_CALLOUT) { dev = make_dev_cred(&ttydev_cdevsw, 0, cred, UID_UUCP, GID_DIALER, 0660, "cua%s", name); dev_depends(tp->t_dev, dev); dev->si_drv1 = tp; /* Slave call-out devices. */ if (tp->t_flags & TF_INITLOCK) { dev = make_dev_cred(&ttyil_cdevsw, 0, cred, UID_UUCP, GID_DIALER, 0660, "cua%s.init", name); dev_depends(tp->t_dev, dev); dev->si_drv1 = tp; dev->si_drv2 = &tp->t_termios_init_out; dev = make_dev_cred(&ttyil_cdevsw, 0, cred, UID_UUCP, GID_DIALER, 0660, "cua%s.lock", name); dev_depends(tp->t_dev, dev); dev->si_drv1 = tp; dev->si_drv2 = &tp->t_termios_lock_out; } } } /* * Signalling processes. */ void tty_signal_sessleader(struct tty *tp, int sig) { struct proc *p; tty_lock_assert(tp, MA_OWNED); MPASS(sig >= 1 && sig < NSIG); /* Make signals start output again. */ tp->t_flags &= ~TF_STOPPED; if (tp->t_session != NULL && tp->t_session->s_leader != NULL) { p = tp->t_session->s_leader; PROC_LOCK(p); psignal(p, sig); PROC_UNLOCK(p); } } void tty_signal_pgrp(struct tty *tp, int sig) { tty_lock_assert(tp, MA_OWNED); MPASS(sig >= 1 && sig < NSIG); /* Make signals start output again. */ tp->t_flags &= ~TF_STOPPED; if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) tty_info(tp); if (tp->t_pgrp != NULL) { PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, sig, 1); PGRP_UNLOCK(tp->t_pgrp); } } void tty_wakeup(struct tty *tp, int flags) { if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); if (flags & FWRITE) { cv_broadcast(&tp->t_outwait); selwakeup(&tp->t_outpoll); KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); } if (flags & FREAD) { cv_broadcast(&tp->t_inwait); selwakeup(&tp->t_inpoll); KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); } } int tty_wait(struct tty *tp, struct cv *cv) { int error; int revokecnt = tp->t_revokecnt; tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); MPASS(!tty_gone(tp)); error = cv_wait_sig(cv, tp->t_mtx); /* Restart the system call when we may have been revoked. */ if (tp->t_revokecnt != revokecnt) return (ERESTART); /* Bail out when the device slipped away. */ if (tty_gone(tp)) return (ENXIO); return (error); } int tty_timedwait(struct tty *tp, struct cv *cv, int hz) { int error; int revokecnt = tp->t_revokecnt; tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); MPASS(!tty_gone(tp)); error = cv_timedwait_sig(cv, tp->t_mtx, hz); /* Restart the system call when we may have been revoked. */ if (tp->t_revokecnt != revokecnt) return (ERESTART); /* Bail out when the device slipped away. */ if (tty_gone(tp)) return (ENXIO); return (error); } void tty_flush(struct tty *tp, int flags) { if (flags & FWRITE) { tp->t_flags &= ~TF_HIWAT_OUT; ttyoutq_flush(&tp->t_outq); tty_wakeup(tp, FWRITE); ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE); } if (flags & FREAD) { tty_hiwat_in_unblock(tp); ttyinq_flush(&tp->t_inq); ttydevsw_inwakeup(tp); ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD); } } static int tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td) { int error; switch (cmd) { /* * Modem commands. * The SER_* and TIOCM_* flags are the same, but one bit * shifted. I don't know why. */ case TIOCSDTR: ttydevsw_modem(tp, SER_DTR, 0); return (0); case TIOCCDTR: ttydevsw_modem(tp, 0, SER_DTR); return (0); case TIOCMSET: { int bits = *(int *)data; ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); return (0); } case TIOCMBIS: { int bits = *(int *)data; ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); return (0); } case TIOCMBIC: { int bits = *(int *)data; ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); return (0); } case TIOCMGET: *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); return (0); case FIOASYNC: if (*(int *)data) tp->t_flags |= TF_ASYNC; else tp->t_flags &= ~TF_ASYNC; return (0); case FIONBIO: /* This device supports non-blocking operation. */ return (0); case FIONREAD: *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq); return (0); case FIOSETOWN: if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc)) /* Not allowed to set ownership. */ return (ENOTTY); /* Temporarily unlock the TTY to set ownership. */ tty_unlock(tp); error = fsetown(*(int *)data, &tp->t_sigio); tty_lock(tp); return (error); case FIOGETOWN: if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc)) /* Not allowed to set ownership. */ return (ENOTTY); /* Get ownership. */ *(int *)data = fgetown(&tp->t_sigio); return (0); case TIOCGETA: /* Obtain terminal flags through tcgetattr(). */ bcopy(&tp->t_termios, data, sizeof(struct termios)); return (0); case TIOCSETA: case TIOCSETAW: case TIOCSETAF: { struct termios *t = data; /* * Who makes up these funny rules? According to POSIX, * input baud rate is set equal to the output baud rate * when zero. */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* Discard any unsupported bits. */ t->c_iflag &= TTYSUP_IFLAG; t->c_oflag &= TTYSUP_OFLAG; t->c_lflag &= TTYSUP_LFLAG; t->c_cflag &= TTYSUP_CFLAG; /* Set terminal flags through tcsetattr(). */ if (cmd == TIOCSETAW || cmd == TIOCSETAF) { error = tty_drain(tp); if (error) return (error); if (cmd == TIOCSETAF) tty_flush(tp, FREAD); } /* * Only call param() when the flags really change. */ if ((t->c_cflag & CIGNORE) == 0 && (tp->t_termios.c_cflag != t->c_cflag || tp->t_termios.c_ispeed != t->c_ispeed || tp->t_termios.c_ospeed != t->c_ospeed)) { error = ttydevsw_param(tp, t); if (error) return (error); /* XXX: CLOCAL? */ tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE; tp->t_termios.c_ispeed = t->c_ispeed; tp->t_termios.c_ospeed = t->c_ospeed; /* Baud rate has changed - update watermarks. */ tty_watermarks(tp); } /* Copy new non-device driver parameters. */ tp->t_termios.c_iflag = t->c_iflag; tp->t_termios.c_oflag = t->c_oflag; tp->t_termios.c_lflag = t->c_lflag; bcopy(t->c_cc, &tp->t_termios.c_cc, sizeof(t->c_cc)); ttydisc_optimize(tp); if ((t->c_lflag & ICANON) == 0) { /* * When in non-canonical mode, wake up all * readers. Canonicalize any partial input. VMIN * and VTIME could also be adjusted. */ ttyinq_canonicalize(&tp->t_inq); tty_wakeup(tp, FREAD); } /* * For packet mode: notify the PTY consumer that VSTOP * and VSTART may have been changed. */ if (tp->t_termios.c_iflag & IXON && tp->t_termios.c_cc[VSTOP] == CTRL('S') && tp->t_termios.c_cc[VSTART] == CTRL('Q')) ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); else ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); return (0); } case TIOCGETD: /* For compatibility - we only support TTYDISC. */ *(int *)data = TTYDISC; return (0); case TIOCGPGRP: if (!tty_is_ctty(tp, td->td_proc)) return (ENOTTY); if (tp->t_pgrp != NULL) *(int *)data = tp->t_pgrp->pg_id; else *(int *)data = NO_PID; return (0); case TIOCGSID: if (!tty_is_ctty(tp, td->td_proc)) return (ENOTTY); MPASS(tp->t_session); *(int *)data = tp->t_session->s_sid; return (0); case TIOCSCTTY: { struct proc *p = td->td_proc; /* XXX: This looks awful. */ tty_unlock(tp); sx_xlock(&proctree_lock); tty_lock(tp); if (!SESS_LEADER(p)) { /* Only the session leader may do this. */ sx_xunlock(&proctree_lock); return (EPERM); } if (tp->t_session != NULL && tp->t_session == p->p_session) { /* This is already our controlling TTY. */ sx_xunlock(&proctree_lock); return (0); } if (!SESS_LEADER(p) || p->p_session->s_ttyvp != NULL || (tp->t_session != NULL && tp->t_session->s_ttyvp != NULL)) { /* * There is already a relation between a TTY and * a session, or the caller is not the session * leader. * * Allow the TTY to be stolen when the vnode is * NULL, but the reference to the TTY is still * active. */ sx_xunlock(&proctree_lock); return (EPERM); } /* Connect the session to the TTY. */ tp->t_session = p->p_session; tp->t_session->s_ttyp = tp; tp->t_sessioncnt++; sx_xunlock(&proctree_lock); /* Assign foreground process group. */ tp->t_pgrp = p->p_pgrp; PROC_LOCK(p); p->p_flag |= P_CONTROLT; PROC_UNLOCK(p); return (0); } case TIOCSPGRP: { struct pgrp *pg; /* * XXX: Temporarily unlock the TTY to locate the process * group. This code would be lot nicer if we would ever * decompose proctree_lock. */ tty_unlock(tp); sx_slock(&proctree_lock); pg = pgfind(*(int *)data); if (pg != NULL) PGRP_UNLOCK(pg); if (pg == NULL || pg->pg_session != td->td_proc->p_session) { sx_sunlock(&proctree_lock); tty_lock(tp); return (EPERM); } tty_lock(tp); /* * Determine if this TTY is the controlling TTY after * relocking the TTY. */ if (!tty_is_ctty(tp, td->td_proc)) { sx_sunlock(&proctree_lock); return (ENOTTY); } tp->t_pgrp = pg; sx_sunlock(&proctree_lock); /* Wake up the background process groups. */ cv_broadcast(&tp->t_bgwait); return (0); } case TIOCFLUSH: { int flags = *(int *)data; if (flags == 0) flags = (FREAD|FWRITE); else flags &= (FREAD|FWRITE); tty_flush(tp, flags); return (0); } case TIOCDRAIN: /* Drain TTY output. */ return tty_drain(tp); case TIOCCONS: /* Set terminal as console TTY. */ if (*(int *)data) { error = priv_check(td, PRIV_TTY_CONSOLE); if (error) return (error); /* * XXX: constty should really need to be locked! * XXX: allow disconnected constty's to be stolen! */ if (constty == tp) return (0); if (constty != NULL) return (EBUSY); tty_unlock(tp); constty_set(tp); tty_lock(tp); } else if (constty == tp) { constty_clear(); } return (0); case TIOCGWINSZ: /* Obtain window size. */ bcopy(&tp->t_winsize, data, sizeof(struct winsize)); return (0); case TIOCSWINSZ: /* Set window size. */ if (bcmp(&tp->t_winsize, data, sizeof(struct winsize)) == 0) return (0); bcopy(data, &tp->t_winsize, sizeof(struct winsize)); tty_signal_pgrp(tp, SIGWINCH); return (0); case TIOCEXCL: tp->t_flags |= TF_EXCLUDE; return (0); case TIOCNXCL: tp->t_flags &= ~TF_EXCLUDE; return (0); case TIOCOUTQ: *(unsigned int *)data = ttyoutq_bytesused(&tp->t_outq); return (0); case TIOCSTOP: tp->t_flags |= TF_STOPPED; ttydevsw_pktnotify(tp, TIOCPKT_STOP); return (0); case TIOCSTART: tp->t_flags &= ~TF_STOPPED; ttydevsw_outwakeup(tp); ttydevsw_pktnotify(tp, TIOCPKT_START); return (0); case TIOCSTAT: tty_info(tp); return (0); } #ifdef COMPAT_43TTY return tty_ioctl_compat(tp, cmd, data, td); #else /* !COMPAT_43TTY */ return (ENOIOCTL); #endif /* COMPAT_43TTY */ } int tty_ioctl(struct tty *tp, u_long cmd, void *data, struct thread *td) { int error; tty_lock_assert(tp, MA_OWNED); if (tty_gone(tp)) return (ENXIO); error = ttydevsw_ioctl(tp, cmd, data, td); if (error == ENOIOCTL) error = tty_generic_ioctl(tp, cmd, data, td); return (error); } dev_t tty_udev(struct tty *tp) { if (tp->t_dev) return dev2udev(tp->t_dev); else return NODEV; } int tty_checkoutq(struct tty *tp) { /* 256 bytes should be enough to print a log message. */ return (ttyoutq_bytesleft(&tp->t_outq) >= 256); } void tty_hiwat_in_block(struct tty *tp) { if ((tp->t_flags & TF_HIWAT_IN) == 0 && tp->t_termios.c_iflag & IXOFF && tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { /* * Input flow control. Only enter the high watermark when we * can successfully store the VSTOP character. */ if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTOP], 1) == 0) tp->t_flags |= TF_HIWAT_IN; } else { /* No input flow control. */ tp->t_flags |= TF_HIWAT_IN; } } void tty_hiwat_in_unblock(struct tty *tp) { if (tp->t_flags & TF_HIWAT_IN && tp->t_termios.c_iflag & IXOFF && tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { /* * Input flow control. Only leave the high watermark when we * can successfully store the VSTART character. */ if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTART], 1) == 0) tp->t_flags &= ~TF_HIWAT_IN; } else { /* No input flow control. */ tp->t_flags &= ~TF_HIWAT_IN; } if (!tty_gone(tp)) ttydevsw_inwakeup(tp); } /* * TTY hooks interface. */ static int ttyhook_defrint(struct tty *tp, char c, int flags) { if (ttyhook_rint_bypass(tp, &c, 1) != 1) return (-1); return (0); } int ttyhook_register(struct tty **rtp, struct proc *p, int fd, struct ttyhook *th, void *softc) { struct tty *tp; struct file *fp; struct cdev *dev; struct cdevsw *cdp; struct filedesc *fdp; int error; /* Validate the file descriptor. */ if ((fdp = p->p_fd) == NULL) return (EBADF); FILEDESC_SLOCK(fdp); if ((fp = fget_locked(fdp, fd)) == NULL || fp->f_ops == &badfileops) { FILEDESC_SUNLOCK(fdp); return (EBADF); } /* Make sure the vnode is bound to a character device. */ error = EINVAL; if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VCHR || fp->f_vnode->v_rdev == NULL) goto done1; dev = fp->f_vnode->v_rdev; /* Make sure it is a TTY. */ cdp = dev_refthread(dev); if (cdp == NULL) goto done1; if (cdp != &ttydev_cdevsw) goto done2; tp = dev->si_drv1; /* Try to attach the hook to the TTY. */ error = EBUSY; tty_lock(tp); MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); if (tp->t_flags & TF_HOOK) goto done3; tp->t_flags |= TF_HOOK; tp->t_hook = th; tp->t_hooksoftc = softc; *rtp = tp; error = 0; /* Maybe we can switch into bypass mode now. */ ttydisc_optimize(tp); /* Silently convert rint() calls to rint_bypass() when possible. */ if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) th->th_rint = ttyhook_defrint; done3: tty_unlock(tp); done2: dev_relthread(dev); done1: FILEDESC_SUNLOCK(fdp); return (error); } void ttyhook_unregister(struct tty *tp) { tty_lock_assert(tp, MA_OWNED); MPASS(tp->t_flags & TF_HOOK); /* Disconnect the hook. */ tp->t_flags &= ~TF_HOOK; tp->t_hook = NULL; /* Maybe we need to leave bypass mode. */ ttydisc_optimize(tp); /* Maybe deallocate the TTY as well. */ tty_rel_free(tp); } /* * /dev/console handling. */ static int ttyconsdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct tty *tp; /* System has no console device. */ if (dev_console_filename == NULL) return (ENXIO); /* Look up corresponding TTY by device name. */ sx_slock(&tty_list_sx); TAILQ_FOREACH(tp, &tty_list, t_list) { if (strcmp(dev_console_filename, tty_devname(tp)) == 0) { dev_console->si_drv1 = tp; break; } } sx_sunlock(&tty_list_sx); /* System console has no TTY associated. */ if (dev_console->si_drv1 == NULL) return (ENXIO); return (ttydev_open(dev, oflags, devtype, td)); } static int ttyconsdev_write(struct cdev *dev, struct uio *uio, int ioflag) { log_console(uio); return (ttydev_write(dev, uio, ioflag)); } /* * /dev/console is a little different than normal TTY's. Unlike regular * TTY device nodes, this device node will not revoke the entire TTY * upon closure and all data written to it will be logged. */ static struct cdevsw ttyconsdev_cdevsw = { .d_version = D_VERSION, .d_open = ttyconsdev_open, .d_read = ttydev_read, .d_write = ttyconsdev_write, .d_ioctl = ttydev_ioctl, .d_kqfilter = ttydev_kqfilter, .d_poll = ttydev_poll, .d_mmap = ttydev_mmap, .d_name = "ttyconsdev", .d_flags = D_TTY, }; static void ttyconsdev_init(void *unused) { dev_console = make_dev(&ttyconsdev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console"); } SYSINIT(tty, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyconsdev_init, NULL); void ttyconsdev_select(const char *name) { dev_console_filename = name; } /* * Debugging routines. */ #include "opt_ddb.h" #ifdef DDB #include #include static struct { int flag; char val; } ttystates[] = { #if 0 { TF_NOPREFIX, 'N' }, #endif { TF_INITLOCK, 'I' }, { TF_CALLOUT, 'C' }, /* Keep these together -> 'Oi' and 'Oo'. */ { TF_OPENED, 'O' }, { TF_OPENED_IN, 'i' }, { TF_OPENED_OUT,'o' }, { TF_GONE, 'G' }, { TF_OPENCLOSE, 'B' }, { TF_ASYNC, 'Y' }, { TF_LITERAL, 'L' }, /* Keep these together -> 'Hi' and 'Ho'. */ { TF_HIWAT, 'H' }, { TF_HIWAT_IN, 'i' }, { TF_HIWAT_OUT, 'o' }, { TF_STOPPED, 'S' }, { TF_EXCLUDE, 'X' }, { TF_BYPASS, 'l' }, { TF_ZOMBIE, 'Z' }, { TF_HOOK, 's' }, { 0, '\0' }, }; #define TTY_FLAG_BITS \ "\20\1NOPREFIX\2INITLOCK\3CALLOUT\4OPENED_IN\5OPENED_OUT\6GONE" \ "\7OPENCLOSE\10ASYNC\11LITERAL\12HIWAT_IN\13HIWAT_OUT\14STOPPED" \ "\15EXCLUDE\16BYPASS\17ZOMBIE\20HOOK" #define DB_PRINTSYM(name, addr) \ db_printf("%s " #name ": ", sep); \ db_printsym((db_addr_t) addr, DB_STGY_ANY); \ db_printf("\n"); static void _db_show_devsw(const char *sep, const struct ttydevsw *tsw) { db_printf("%sdevsw: ", sep); db_printsym((db_addr_t)tsw, DB_STGY_ANY); db_printf(" (%p)\n", tsw); DB_PRINTSYM(open, tsw->tsw_open); DB_PRINTSYM(close, tsw->tsw_close); DB_PRINTSYM(outwakeup, tsw->tsw_outwakeup); DB_PRINTSYM(inwakeup, tsw->tsw_inwakeup); DB_PRINTSYM(ioctl, tsw->tsw_ioctl); DB_PRINTSYM(param, tsw->tsw_param); DB_PRINTSYM(modem, tsw->tsw_modem); DB_PRINTSYM(mmap, tsw->tsw_mmap); DB_PRINTSYM(pktnotify, tsw->tsw_pktnotify); DB_PRINTSYM(free, tsw->tsw_free); } static void _db_show_hooks(const char *sep, const struct ttyhook *th) { db_printf("%shook: ", sep); db_printsym((db_addr_t)th, DB_STGY_ANY); db_printf(" (%p)\n", th); if (th == NULL) return; DB_PRINTSYM(rint, th->th_rint); DB_PRINTSYM(rint_bypass, th->th_rint_bypass); DB_PRINTSYM(rint_done, th->th_rint_done); DB_PRINTSYM(rint_poll, th->th_rint_poll); DB_PRINTSYM(getc_inject, th->th_getc_inject); DB_PRINTSYM(getc_capture, th->th_getc_capture); DB_PRINTSYM(getc_poll, th->th_getc_poll); DB_PRINTSYM(close, th->th_close); } static void _db_show_termios(const char *name, const struct termios *t) { db_printf("%s: iflag 0x%x oflag 0x%x cflag 0x%x " "lflag 0x%x ispeed %u ospeed %u\n", name, t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_ispeed, t->c_ospeed); } /* DDB command to show TTY statistics. */ DB_SHOW_COMMAND(tty, db_show_tty) { struct tty *tp; if (!have_addr) { db_printf("usage: show tty \n"); return; } tp = (struct tty *)addr; db_printf("0x%p: %s\n", tp, tty_devname(tp)); db_printf("\tmtx: %p\n", tp->t_mtx); db_printf("\tflags: %b\n", tp->t_flags, TTY_FLAG_BITS); db_printf("\trevokecnt: %u\n", tp->t_revokecnt); /* Buffering mechanisms. */ db_printf("\tinq: %p begin %u linestart %u reprint %u end %u " "nblocks %u quota %u\n", &tp->t_inq, tp->t_inq.ti_begin, tp->t_inq.ti_linestart, tp->t_inq.ti_reprint, tp->t_inq.ti_end, tp->t_inq.ti_nblocks, tp->t_inq.ti_quota); db_printf("\toutq: %p begin %u end %u nblocks %u quota %u\n", &tp->t_outq, tp->t_outq.to_begin, tp->t_outq.to_end, tp->t_outq.to_nblocks, tp->t_outq.to_quota); db_printf("\tinlow: %zu\n", tp->t_inlow); db_printf("\toutlow: %zu\n", tp->t_outlow); _db_show_termios("\ttermios", &tp->t_termios); db_printf("\twinsize: row %u col %u xpixel %u ypixel %u\n", tp->t_winsize.ws_row, tp->t_winsize.ws_col, tp->t_winsize.ws_xpixel, tp->t_winsize.ws_ypixel); db_printf("\tcolumn: %u\n", tp->t_column); db_printf("\twritepos: %u\n", tp->t_writepos); db_printf("\tcompatflags: 0x%x\n", tp->t_compatflags); /* Init/lock-state devices. */ _db_show_termios("\ttermios_init_in", &tp->t_termios_init_in); _db_show_termios("\ttermios_init_out", &tp->t_termios_init_out); _db_show_termios("\ttermios_lock_in", &tp->t_termios_lock_in); _db_show_termios("\ttermios_lock_out", &tp->t_termios_lock_out); /* Hooks */ _db_show_devsw("\t", tp->t_devsw); _db_show_hooks("\t", tp->t_hook); /* Process info. */ db_printf("\tpgrp: %p gid %d jobc %d\n", tp->t_pgrp, tp->t_pgrp ? tp->t_pgrp->pg_id : 0, tp->t_pgrp ? tp->t_pgrp->pg_jobc : 0); db_printf("\tsession: %p", tp->t_session); if (tp->t_session != NULL) db_printf(" count %u leader %p tty %p sid %d login %s", tp->t_session->s_count, tp->t_session->s_leader, tp->t_session->s_ttyp, tp->t_session->s_sid, tp->t_session->s_login); db_printf("\n"); db_printf("\tsessioncnt: %u\n", tp->t_sessioncnt); db_printf("\tdevswsoftc: %p\n", tp->t_devswsoftc); db_printf("\thooksoftc: %p\n", tp->t_hooksoftc); db_printf("\tdev: %p\n", tp->t_dev); } /* DDB command to list TTYs. */ DB_SHOW_ALL_COMMAND(ttys, db_show_all_ttys) { struct tty *tp; size_t isiz, osiz; int i, j; /* Make the output look like `pstat -t'. */ db_printf("PTR "); #if defined(__LP64__) db_printf(" "); #endif db_printf(" LINE INQ CAN LIN LOW OUTQ USE LOW " "COL SESS PGID STATE\n"); TAILQ_FOREACH(tp, &tty_list, t_list) { isiz = tp->t_inq.ti_nblocks * TTYINQ_DATASIZE; osiz = tp->t_outq.to_nblocks * TTYOUTQ_DATASIZE; db_printf("%p %10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d %5d ", tp, tty_devname(tp), isiz, tp->t_inq.ti_linestart - tp->t_inq.ti_begin, tp->t_inq.ti_end - tp->t_inq.ti_linestart, isiz - tp->t_inlow, osiz, tp->t_outq.to_end - tp->t_outq.to_begin, osiz - tp->t_outlow, MIN(tp->t_column, 99999), tp->t_session ? tp->t_session->s_sid : 0, tp->t_pgrp ? tp->t_pgrp->pg_id : 0); /* Flag bits. */ for (i = j = 0; ttystates[i].flag; i++) if (tp->t_flags & ttystates[i].flag) { db_printf("%c", ttystates[i].val); j++; } if (j == 0) db_printf("-"); db_printf("\n"); } } #endif /* DDB */ Index: projects/cambria/sys/kern/tty_pts.c =================================================================== --- projects/cambria/sys/kern/tty_pts.c (revision 186459) +++ projects/cambria/sys/kern/tty_pts.c (revision 186460) @@ -1,842 +1,841 @@ /*- * Copyright (c) 2008 Ed Schouten * All rights reserved. * * Portions of this software were developed under sponsorship from Snow * B.V., the Netherlands. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_tty.h" /* Add compatibility bits for FreeBSD. */ #define PTS_COMPAT #ifdef DEV_PTY /* Add /dev/ptyXX compat bits. */ #define PTS_EXTERNAL #endif /* DEV_PTY */ /* Add bits to make Linux binaries work. */ #define PTS_LINUX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct unrhdr *pts_pool; #define MAXPTSDEVS 999 static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device"); /* * Per-PTS structure. * * List of locks * (t) locked by tty_lock() * (c) const until freeing */ struct pts_softc { int pts_unit; /* (c) Device unit number. */ unsigned int pts_flags; /* (t) Device flags. */ #define PTS_PKT 0x1 /* Packet mode. */ #define PTS_FINISHED 0x2 /* Return errors on read()/write(). */ char pts_pkt; /* (t) Unread packet mode data. */ struct cv pts_inwait; /* (t) Blocking write() on master. */ struct selinfo pts_inpoll; /* (t) Select queue for write(). */ struct cv pts_outwait; /* (t) Blocking read() on master. */ struct selinfo pts_outpoll; /* (t) Select queue for read(). */ #ifdef PTS_EXTERNAL struct cdev *pts_cdev; /* (c) Master device node. */ #endif /* PTS_EXTERNAL */ struct uidinfo *pts_uidinfo; /* (c) Resource limit. */ }; /* * Controller-side file operations. */ static int ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); int error = 0; char pkt; if (uio->uio_resid == 0) return (0); tty_lock(tp); for (;;) { /* * Implement packet mode. When packet mode is turned on, * the first byte contains a bitmask of events that * occured (start, stop, flush, window size, etc). */ if (psc->pts_flags & PTS_PKT && psc->pts_pkt) { pkt = psc->pts_pkt; psc->pts_pkt = 0; tty_unlock(tp); error = ureadc(pkt, uio); return (error); } /* * Transmit regular data. * * XXX: We shouldn't use ttydisc_getc_poll()! Even * though in this implementation, there is likely going * to be data, we should just call ttydisc_getc_uio() * and use its return value to sleep. */ if (ttydisc_getc_poll(tp)) { if (psc->pts_flags & PTS_PKT) { /* * XXX: Small race. Fortunately PTY * consumers aren't multithreaded. */ tty_unlock(tp); error = ureadc(TIOCPKT_DATA, uio); if (error) return (error); tty_lock(tp); } error = ttydisc_getc_uio(tp, uio); break; } /* Maybe the device isn't used anyway. */ if (psc->pts_flags & PTS_FINISHED) break; /* Wait for more data. */ if (fp->f_flag & O_NONBLOCK) { error = EWOULDBLOCK; break; } error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx); if (error != 0) break; } tty_unlock(tp); return (error); } static int ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); char ib[256], *ibstart; size_t iblen, rintlen; int error = 0; if (uio->uio_resid == 0) return (0); for (;;) { ibstart = ib; iblen = MIN(uio->uio_resid, sizeof ib); error = uiomove(ib, iblen, uio); tty_lock(tp); if (error != 0) goto done; /* * When possible, avoid the slow path. rint_bypass() * copies all input to the input queue at once. */ MPASS(iblen > 0); do { if (ttydisc_can_bypass(tp)) { /* Store data at once. */ rintlen = ttydisc_rint_bypass(tp, ibstart, iblen); ibstart += rintlen; iblen -= rintlen; if (iblen == 0) { /* All data written. */ break; } } else { error = ttydisc_rint(tp, *ibstart, 0); if (error == 0) { /* Character stored successfully. */ ibstart++; iblen--; continue; } } /* Maybe the device isn't used anyway. */ if (psc->pts_flags & PTS_FINISHED) { error = EIO; goto done; } /* Wait for more data. */ if (fp->f_flag & O_NONBLOCK) { error = EWOULDBLOCK; goto done; } /* Wake up users on the slave side. */ ttydisc_rint_done(tp); error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx); if (error != 0) goto done; } while (iblen > 0); if (uio->uio_resid == 0) break; tty_unlock(tp); } done: ttydisc_rint_done(tp); tty_unlock(tp); return (error); } static int ptsdev_truncate(struct file *fp, off_t length, struct ucred *active_cred, struct thread *td) { return (EINVAL); } static int ptsdev_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); int error = 0, sig; switch (cmd) { case FIONBIO: /* This device supports non-blocking operation. */ return (0); case FIONREAD: tty_lock(tp); if (psc->pts_flags & PTS_FINISHED) { /* Force read() to be called. */ *(int *)data = 1; } else { *(int *)data = ttydisc_getc_poll(tp); } tty_unlock(tp); return (0); case FIODGNAME: { struct fiodgname_arg *fgn; const char *p; int i; /* Reverse device name lookups, for ptsname() and ttyname(). */ fgn = data; #ifdef PTS_EXTERNAL if (psc->pts_cdev != NULL) p = devtoname(psc->pts_cdev); else #endif /* PTS_EXTERNAL */ p = tty_devname(tp); i = strlen(p) + 1; if (i > fgn->len) return (EINVAL); return copyout(p, fgn->buf, i); } /* * We need to implement TIOCGPGRP and TIOCGSID here again. When * called on the pseudo-terminal master, it should not check if * the terminal is the foreground terminal of the calling * process. * * TIOCGETA is also implemented here. Various Linux PTY routines * often call isatty(), which is implemented by tcgetattr(). */ #ifdef PTS_LINUX case TIOCGETA: /* Obtain terminal flags through tcgetattr(). */ tty_lock(tp); bcopy(&tp->t_termios, data, sizeof(struct termios)); tty_unlock(tp); return (0); #endif /* PTS_LINUX */ case TIOCSETAF: case TIOCSETAW: /* * We must make sure we turn tcsetattr() calls of TCSAFLUSH and * TCSADRAIN into something different. If an application would * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may * deadlock waiting for all data to be read. */ cmd = TIOCSETA; break; #if defined(PTS_COMPAT) || defined(PTS_LINUX) case TIOCGPTN: /* * Get the device unit number. */ if (psc->pts_unit < 0) return (ENOTTY); *(unsigned int *)data = psc->pts_unit; return (0); #endif /* PTS_COMPAT || PTS_LINUX */ case TIOCGPGRP: /* Get the foreground process group ID. */ tty_lock(tp); if (tp->t_pgrp != NULL) *(int *)data = tp->t_pgrp->pg_id; else *(int *)data = NO_PID; tty_unlock(tp); return (0); case TIOCGSID: /* Get the session leader process ID. */ tty_lock(tp); if (tp->t_session == NULL) error = ENOTTY; else *(int *)data = tp->t_session->s_sid; tty_unlock(tp); return (error); case TIOCPTMASTER: /* Yes, we are a pseudo-terminal master. */ return (0); case TIOCSIG: /* Signal the foreground process group. */ sig = *(int *)data; if (sig < 1 || sig >= NSIG) return (EINVAL); tty_lock(tp); tty_signal_pgrp(tp, sig); tty_unlock(tp); return (0); case TIOCPKT: /* Enable/disable packet mode. */ tty_lock(tp); if (*(int *)data) psc->pts_flags |= PTS_PKT; else psc->pts_flags &= ~PTS_PKT; tty_unlock(tp); return (0); } /* Just redirect this ioctl to the slave device. */ tty_lock(tp); error = tty_ioctl(tp, cmd, data, td); tty_unlock(tp); return (error); } static int ptsdev_poll(struct file *fp, int events, struct ucred *active_cred, struct thread *td) { struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); int revents = 0; tty_lock(tp); if (psc->pts_flags & PTS_FINISHED) { /* Slave device is not opened. */ tty_unlock(tp); return (events & (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); } if (events & (POLLIN|POLLRDNORM)) { /* See if we can getc something. */ if (ttydisc_getc_poll(tp) || (psc->pts_flags & PTS_PKT && psc->pts_pkt)) revents |= events & (POLLIN|POLLRDNORM); } if (events & (POLLOUT|POLLWRNORM)) { /* See if we can rint something. */ if (ttydisc_rint_poll(tp)) revents |= events & (POLLOUT|POLLWRNORM); } /* * No need to check for POLLHUP here. This device cannot be used * as a callout device, which means we always have a carrier, * because the master is. */ if (revents == 0) { /* * This code might look misleading, but the naming of * poll events on this side is the opposite of the slave * device. */ if (events & (POLLIN|POLLRDNORM)) selrecord(td, &psc->pts_outpoll); if (events & (POLLOUT|POLLWRNORM)) selrecord(td, &psc->pts_inpoll); } tty_unlock(tp); return (revents); } /* * kqueue support. */ static void pts_kqops_read_detach(struct knote *kn) { struct file *fp = kn->kn_fp; struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); knlist_remove(&psc->pts_outpoll.si_note, kn, 0); } static int pts_kqops_read_event(struct knote *kn, long hint) { struct file *fp = kn->kn_fp; struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); if (psc->pts_flags & PTS_FINISHED) { kn->kn_flags |= EV_EOF; return (1); } else { kn->kn_data = ttydisc_getc_poll(tp); return (kn->kn_data > 0); } } static void pts_kqops_write_detach(struct knote *kn) { struct file *fp = kn->kn_fp; struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); knlist_remove(&psc->pts_inpoll.si_note, kn, 0); } static int pts_kqops_write_event(struct knote *kn, long hint) { struct file *fp = kn->kn_fp; struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); if (psc->pts_flags & PTS_FINISHED) { kn->kn_flags |= EV_EOF; return (1); } else { kn->kn_data = ttydisc_rint_poll(tp); return (kn->kn_data > 0); } } static struct filterops pts_kqops_read = { 1, NULL, pts_kqops_read_detach, pts_kqops_read_event }; static struct filterops pts_kqops_write = { 1, NULL, pts_kqops_write_detach, pts_kqops_write_event }; static int ptsdev_kqfilter(struct file *fp, struct knote *kn) { struct tty *tp = fp->f_data; struct pts_softc *psc = tty_softc(tp); int error = 0; tty_lock(tp); switch (kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &pts_kqops_read; knlist_add(&psc->pts_outpoll.si_note, kn, 1); break; case EVFILT_WRITE: kn->kn_fop = &pts_kqops_write; knlist_add(&psc->pts_inpoll.si_note, kn, 1); break; default: error = EINVAL; break; } tty_unlock(tp); return (error); } static int ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred, struct thread *td) { struct tty *tp = fp->f_data; #ifdef PTS_EXTERNAL struct pts_softc *psc = tty_softc(tp); #endif /* PTS_EXTERNAL */ struct cdev *dev = tp->t_dev; /* * According to POSIX, we must implement an fstat(). This also * makes this implementation compatible with Linux binaries, * because Linux calls fstat() on the pseudo-terminal master to * obtain st_rdev. * * XXX: POSIX also mentions we must fill in st_dev, but how? */ bzero(sb, sizeof *sb); #ifdef PTS_EXTERNAL if (psc->pts_cdev != NULL) sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev); else #endif /* PTS_EXTERNAL */ sb->st_ino = sb->st_rdev = tty_udev(tp); sb->st_atimespec = dev->si_atime; sb->st_ctimespec = dev->si_ctime; sb->st_mtimespec = dev->si_mtime; sb->st_uid = dev->si_uid; sb->st_gid = dev->si_gid; sb->st_mode = dev->si_mode | S_IFCHR; return (0); } static int ptsdev_close(struct file *fp, struct thread *td) { struct tty *tp = fp->f_data; /* Deallocate TTY device. */ tty_lock(tp); tty_rel_gone(tp); return (0); } static struct fileops ptsdev_ops = { .fo_read = ptsdev_read, .fo_write = ptsdev_write, .fo_truncate = ptsdev_truncate, .fo_ioctl = ptsdev_ioctl, .fo_poll = ptsdev_poll, .fo_kqfilter = ptsdev_kqfilter, .fo_stat = ptsdev_stat, .fo_close = ptsdev_close, .fo_flags = DFLAG_PASSABLE, }; /* * Driver-side hooks. */ static void ptsdrv_outwakeup(struct tty *tp) { struct pts_softc *psc = tty_softc(tp); cv_broadcast(&psc->pts_outwait); selwakeup(&psc->pts_outpoll); KNOTE_LOCKED(&psc->pts_outpoll.si_note, 0); } static void ptsdrv_inwakeup(struct tty *tp) { struct pts_softc *psc = tty_softc(tp); cv_broadcast(&psc->pts_inwait); selwakeup(&psc->pts_inpoll); KNOTE_LOCKED(&psc->pts_inpoll.si_note, 0); } static int ptsdrv_open(struct tty *tp) { struct pts_softc *psc = tty_softc(tp); psc->pts_flags &= ~PTS_FINISHED; return (0); } static void ptsdrv_close(struct tty *tp) { struct pts_softc *psc = tty_softc(tp); /* Wake up any blocked readers/writers. */ + psc->pts_flags |= PTS_FINISHED; ptsdrv_outwakeup(tp); ptsdrv_inwakeup(tp); - - psc->pts_flags |= PTS_FINISHED; } static void ptsdrv_pktnotify(struct tty *tp, char event) { struct pts_softc *psc = tty_softc(tp); /* * Clear conflicting flags. */ switch (event) { case TIOCPKT_STOP: psc->pts_pkt &= ~TIOCPKT_START; break; case TIOCPKT_START: psc->pts_pkt &= ~TIOCPKT_STOP; break; case TIOCPKT_NOSTOP: psc->pts_pkt &= ~TIOCPKT_DOSTOP; break; case TIOCPKT_DOSTOP: psc->pts_pkt &= ~TIOCPKT_NOSTOP; break; } psc->pts_pkt |= event; ptsdrv_outwakeup(tp); } static void ptsdrv_free(void *softc) { struct pts_softc *psc = softc; /* Make device number available again. */ if (psc->pts_unit >= 0) free_unr(pts_pool, psc->pts_unit); chgptscnt(psc->pts_uidinfo, -1, 0); uifree(psc->pts_uidinfo); knlist_destroy(&psc->pts_inpoll.si_note); knlist_destroy(&psc->pts_outpoll.si_note); #ifdef PTS_EXTERNAL /* Destroy master device as well. */ if (psc->pts_cdev != NULL) destroy_dev_sched(psc->pts_cdev); #endif /* PTS_EXTERNAL */ free(psc, M_PTS); } static struct ttydevsw pts_class = { .tsw_flags = TF_NOPREFIX, .tsw_outwakeup = ptsdrv_outwakeup, .tsw_inwakeup = ptsdrv_inwakeup, .tsw_open = ptsdrv_open, .tsw_close = ptsdrv_close, .tsw_pktnotify = ptsdrv_pktnotify, .tsw_free = ptsdrv_free, }; static int pts_alloc(int fflags, struct thread *td, struct file *fp) { int unit, ok; struct tty *tp; struct pts_softc *psc; struct proc *p = td->td_proc; struct uidinfo *uid = td->td_ucred->cr_ruidinfo; /* Resource limiting. */ PROC_LOCK(p); ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS)); PROC_UNLOCK(p); if (!ok) return (EAGAIN); /* Try to allocate a new pts unit number. */ unit = alloc_unr(pts_pool); if (unit < 0) { chgptscnt(uid, -1, 0); return (EAGAIN); } /* Allocate TTY and softc. */ psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO); cv_init(&psc->pts_inwait, "pts inwait"); cv_init(&psc->pts_outwait, "pts outwait"); psc->pts_unit = unit; psc->pts_uidinfo = uid; uihold(uid); tp = tty_alloc(&pts_class, psc, NULL); knlist_init(&psc->pts_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL); knlist_init(&psc->pts_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL); /* Expose the slave device as well. */ tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit); finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops); return (0); } #ifdef PTS_EXTERNAL int pts_alloc_external(int fflags, struct thread *td, struct file *fp, struct cdev *dev, const char *name) { int ok; struct tty *tp; struct pts_softc *psc; struct proc *p = td->td_proc; struct uidinfo *uid = td->td_ucred->cr_ruidinfo; /* Resource limiting. */ PROC_LOCK(p); ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS)); PROC_UNLOCK(p); if (!ok) return (EAGAIN); /* Allocate TTY and softc. */ psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO); cv_init(&psc->pts_inwait, "pts inwait"); cv_init(&psc->pts_outwait, "pts outwait"); psc->pts_unit = -1; psc->pts_cdev = dev; psc->pts_uidinfo = uid; uihold(uid); tp = tty_alloc(&pts_class, psc, NULL); knlist_init(&psc->pts_inpoll.si_note, tp->t_mtx, NULL, NULL, NULL); knlist_init(&psc->pts_outpoll.si_note, tp->t_mtx, NULL, NULL, NULL); /* Expose the slave device as well. */ tty_makedev(tp, td->td_ucred, "%s", name); finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops); return (0); } #endif /* PTS_EXTERNAL */ int posix_openpt(struct thread *td, struct posix_openpt_args *uap) { int error, fd; struct file *fp; /* * POSIX states it's unspecified when other flags are passed. We * don't allow this. */ if (uap->flags & ~(O_RDWR|O_NOCTTY)) return (EINVAL); error = falloc(td, &fp, &fd); if (error) return (error); /* Allocate the actual pseudo-TTY. */ error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp); if (error != 0) { fdclose(td->td_proc->p_fd, fp, fd, td); return (error); } /* Pass it back to userspace. */ td->td_retval[0] = fd; fdrop(fp, td); return (0); } #if defined(PTS_COMPAT) || defined(PTS_LINUX) static int ptmx_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp) { return (pts_alloc(fflags & (FREAD|FWRITE), td, fp)); } static struct cdevsw ptmx_cdevsw = { .d_version = D_VERSION, .d_fdopen = ptmx_fdopen, .d_name = "ptmx", }; #endif /* PTS_COMPAT || PTS_LINUX */ static void pts_init(void *unused) { pts_pool = new_unrhdr(0, MAXPTSDEVS, NULL); #if defined(PTS_COMPAT) || defined(PTS_LINUX) make_dev(&ptmx_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "ptmx"); #endif /* PTS_COMPAT || PTS_LINUX */ } SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL); Index: projects/cambria/sys/kern/uipc_domain.c =================================================================== --- projects/cambria/sys/kern/uipc_domain.c (revision 186459) +++ projects/cambria/sys/kern/uipc_domain.c (revision 186460) @@ -1,474 +1,479 @@ /*- * Copyright (c) 1982, 1986, 1993 * 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. * 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. * * @(#)uipc_domain.c 8.2 (Berkeley) 10/18/93 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include /* * System initialization * * Note: domain initialization takes place on a per domain basis * as a result of traversing a SYSINIT linker set. Most likely, * each domain would want to call DOMAIN_SET(9) itself, which * would cause the domain to be added just after domaininit() * is called during startup. * * See DOMAIN_SET(9) for details on its use. */ static void domaininit(void *); SYSINIT(domain, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, domaininit, NULL); static void domainfinalize(void *); SYSINIT(domainfin, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST, domainfinalize, NULL); static struct callout pffast_callout; static struct callout pfslow_callout; static void pffasttimo(void *); static void pfslowtimo(void *); struct domain *domains; /* registered protocol domains */ int domain_init_status = 0; struct mtx dom_mtx; /* domain list lock */ MTX_SYSINIT(domain, &dom_mtx, "domain list", MTX_DEF); /* * Dummy protocol specific user requests function pointer array. * All functions return EOPNOTSUPP. */ struct pr_usrreqs nousrreqs = { .pru_accept = pru_accept_notsupp, .pru_attach = pru_attach_notsupp, .pru_bind = pru_bind_notsupp, .pru_connect = pru_connect_notsupp, .pru_connect2 = pru_connect2_notsupp, .pru_control = pru_control_notsupp, .pru_disconnect = pru_disconnect_notsupp, .pru_listen = pru_listen_notsupp, .pru_peeraddr = pru_peeraddr_notsupp, .pru_rcvd = pru_rcvd_notsupp, .pru_rcvoob = pru_rcvoob_notsupp, .pru_send = pru_send_notsupp, .pru_sense = pru_sense_null, .pru_shutdown = pru_shutdown_notsupp, .pru_sockaddr = pru_sockaddr_notsupp, .pru_sosend = pru_sosend_notsupp, .pru_soreceive = pru_soreceive_notsupp, .pru_sopoll = pru_sopoll_notsupp, }; static void protosw_init(struct protosw *pr) { struct pr_usrreqs *pu; pu = pr->pr_usrreqs; KASSERT(pu != NULL, ("protosw_init: %ssw[%d] has no usrreqs!", pr->pr_domain->dom_name, (int)(pr - pr->pr_domain->dom_protosw))); #define DEFAULT(foo, bar) if ((foo) == NULL) (foo) = (bar) DEFAULT(pu->pru_accept, pru_accept_notsupp); + DEFAULT(pu->pru_bind, pru_bind_notsupp); DEFAULT(pu->pru_connect, pru_connect_notsupp); DEFAULT(pu->pru_connect2, pru_connect2_notsupp); DEFAULT(pu->pru_control, pru_control_notsupp); + DEFAULT(pu->pru_disconnect, pru_disconnect_notsupp); DEFAULT(pu->pru_listen, pru_listen_notsupp); + DEFAULT(pu->pru_peeraddr, pru_peeraddr_notsupp); DEFAULT(pu->pru_rcvd, pru_rcvd_notsupp); DEFAULT(pu->pru_rcvoob, pru_rcvoob_notsupp); DEFAULT(pu->pru_sense, pru_sense_null); + DEFAULT(pu->pru_shutdown, pru_shutdown_notsupp); + DEFAULT(pu->pru_sockaddr, pru_sockaddr_notsupp); DEFAULT(pu->pru_sosend, sosend_generic); DEFAULT(pu->pru_soreceive, soreceive_generic); DEFAULT(pu->pru_sopoll, sopoll_generic); #undef DEFAULT if (pr->pr_init) (*pr->pr_init)(); } /* * Add a new protocol domain to the list of supported domains * Note: you cant unload it again because a socket may be using it. * XXX can't fail at this time. */ static void net_init_domain(struct domain *dp) { struct protosw *pr; if (dp->dom_init) (*dp->dom_init)(); for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) protosw_init(pr); /* * update global information about maximums */ max_hdr = max_linkhdr + max_protohdr; max_datalen = MHLEN - max_hdr; if (max_datalen < 1) panic("%s: max_datalen < 1", __func__); } /* * Add a new protocol domain to the list of supported domains * Note: you cant unload it again because a socket may be using it. * XXX can't fail at this time. */ void net_add_domain(void *data) { struct domain *dp; dp = (struct domain *)data; mtx_lock(&dom_mtx); dp->dom_next = domains; domains = dp; KASSERT(domain_init_status >= 1, ("attempt to net_add_domain(%s) before domaininit()", dp->dom_name)); #ifndef INVARIANTS if (domain_init_status < 1) printf("WARNING: attempt to net_add_domain(%s) before " "domaininit()\n", dp->dom_name); #endif #ifdef notyet KASSERT(domain_init_status < 2, ("attempt to net_add_domain(%s) after domainfinalize()", dp->dom_name)); #else if (domain_init_status >= 2) printf("WARNING: attempt to net_add_domain(%s) after " "domainfinalize()\n", dp->dom_name); #endif mtx_unlock(&dom_mtx); net_init_domain(dp); } static void socket_zone_change(void *tag) { uma_zone_set_max(socket_zone, maxsockets); } /* ARGSUSED*/ static void domaininit(void *dummy) { /* * Before we do any setup, make sure to initialize the * zone allocator we get struct sockets from. */ socket_zone = uma_zcreate("socket", sizeof(struct socket), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); uma_zone_set_max(socket_zone, maxsockets); EVENTHANDLER_REGISTER(maxsockets_change, socket_zone_change, NULL, EVENTHANDLER_PRI_FIRST); if (max_linkhdr < 16) /* XXX */ max_linkhdr = 16; callout_init(&pffast_callout, CALLOUT_MPSAFE); callout_init(&pfslow_callout, CALLOUT_MPSAFE); mtx_lock(&dom_mtx); KASSERT(domain_init_status == 0, ("domaininit called too late!")); domain_init_status = 1; mtx_unlock(&dom_mtx); } /* ARGSUSED*/ static void domainfinalize(void *dummy) { mtx_lock(&dom_mtx); KASSERT(domain_init_status == 1, ("domainfinalize called too late!")); domain_init_status = 2; mtx_unlock(&dom_mtx); callout_reset(&pffast_callout, 1, pffasttimo, NULL); callout_reset(&pfslow_callout, 1, pfslowtimo, NULL); } struct protosw * pffindtype(int family, int type) { struct domain *dp; struct protosw *pr; for (dp = domains; dp; dp = dp->dom_next) if (dp->dom_family == family) goto found; return (0); found: for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_type && pr->pr_type == type) return (pr); return (0); } struct protosw * pffindproto(int family, int protocol, int type) { struct domain *dp; struct protosw *pr; struct protosw *maybe = 0; if (family == 0) return (0); for (dp = domains; dp; dp = dp->dom_next) if (dp->dom_family == family) goto found; return (0); found: for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { if ((pr->pr_protocol == protocol) && (pr->pr_type == type)) return (pr); if (type == SOCK_RAW && pr->pr_type == SOCK_RAW && pr->pr_protocol == 0 && maybe == (struct protosw *)0) maybe = pr; } return (maybe); } /* * The caller must make sure that the new protocol is fully set up and ready to * accept requests before it is registered. */ int pf_proto_register(int family, struct protosw *npr) { struct domain *dp; struct protosw *pr, *fpr; /* Sanity checks. */ if (family == 0) return (EPFNOSUPPORT); if (npr->pr_type == 0) return (EPROTOTYPE); if (npr->pr_protocol == 0) return (EPROTONOSUPPORT); if (npr->pr_usrreqs == NULL) return (ENXIO); /* Try to find the specified domain based on the family. */ for (dp = domains; dp; dp = dp->dom_next) if (dp->dom_family == family) goto found; return (EPFNOSUPPORT); found: /* Initialize backpointer to struct domain. */ npr->pr_domain = dp; fpr = NULL; /* * Protect us against races when two protocol registrations for * the same protocol happen at the same time. */ mtx_lock(&Giant); /* The new protocol must not yet exist. */ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { if ((pr->pr_type == npr->pr_type) && (pr->pr_protocol == npr->pr_protocol)) { mtx_unlock(&Giant); return (EEXIST); /* XXX: Check only protocol? */ } /* While here, remember the first free spacer. */ if ((fpr == NULL) && (pr->pr_protocol == PROTO_SPACER)) fpr = pr; } /* If no free spacer is found we can't add the new protocol. */ if (fpr == NULL) { mtx_unlock(&Giant); return (ENOMEM); } /* Copy the new struct protosw over the spacer. */ bcopy(npr, fpr, sizeof(*fpr)); /* Job is done, no more protection required. */ mtx_unlock(&Giant); /* Initialize and activate the protocol. */ protosw_init(fpr); return (0); } /* * The caller must make sure the protocol and its functions correctly shut down * all sockets and release all locks and memory references. */ int pf_proto_unregister(int family, int protocol, int type) { struct domain *dp; struct protosw *pr, *dpr; /* Sanity checks. */ if (family == 0) return (EPFNOSUPPORT); if (protocol == 0) return (EPROTONOSUPPORT); if (type == 0) return (EPROTOTYPE); /* Try to find the specified domain based on the family type. */ for (dp = domains; dp; dp = dp->dom_next) if (dp->dom_family == family) goto found; return (EPFNOSUPPORT); found: dpr = NULL; /* Lock out everyone else while we are manipulating the protosw. */ mtx_lock(&Giant); /* The protocol must exist and only once. */ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { if ((pr->pr_type == type) && (pr->pr_protocol == protocol)) { if (dpr != NULL) { mtx_unlock(&Giant); return (EMLINK); /* Should not happen! */ } else dpr = pr; } } /* Protocol does not exist. */ if (dpr == NULL) { mtx_unlock(&Giant); return (EPROTONOSUPPORT); } /* De-orbit the protocol and make the slot available again. */ dpr->pr_type = 0; dpr->pr_domain = dp; dpr->pr_protocol = PROTO_SPACER; dpr->pr_flags = 0; dpr->pr_input = NULL; dpr->pr_output = NULL; dpr->pr_ctlinput = NULL; dpr->pr_ctloutput = NULL; dpr->pr_ousrreq = NULL; dpr->pr_init = NULL; dpr->pr_fasttimo = NULL; dpr->pr_slowtimo = NULL; dpr->pr_drain = NULL; dpr->pr_usrreqs = &nousrreqs; /* Job is done, not more protection required. */ mtx_unlock(&Giant); return (0); } void pfctlinput(int cmd, struct sockaddr *sa) { struct domain *dp; struct protosw *pr; for (dp = domains; dp; dp = dp->dom_next) for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_ctlinput) (*pr->pr_ctlinput)(cmd, sa, (void *)0); } void pfctlinput2(int cmd, struct sockaddr *sa, void *ctlparam) { struct domain *dp; struct protosw *pr; if (!sa) return; for (dp = domains; dp; dp = dp->dom_next) { /* * the check must be made by xx_ctlinput() anyways, to * make sure we use data item pointed to by ctlparam in * correct way. the following check is made just for safety. */ if (dp->dom_family != sa->sa_family) continue; for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_ctlinput) (*pr->pr_ctlinput)(cmd, sa, ctlparam); } } static void pfslowtimo(void *arg) { struct domain *dp; struct protosw *pr; for (dp = domains; dp; dp = dp->dom_next) for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_slowtimo) (*pr->pr_slowtimo)(); callout_reset(&pfslow_callout, hz/2, pfslowtimo, NULL); } static void pffasttimo(void *arg) { struct domain *dp; struct protosw *pr; for (dp = domains; dp; dp = dp->dom_next) for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) if (pr->pr_fasttimo) (*pr->pr_fasttimo)(); callout_reset(&pffast_callout, hz/5, pffasttimo, NULL); } Index: projects/cambria/sys/kern/vfs_cache.c =================================================================== --- projects/cambria/sys/kern/vfs_cache.c (revision 186459) +++ projects/cambria/sys/kern/vfs_cache.c (revision 186460) @@ -1,1001 +1,1001 @@ /*- * Copyright (c) 1989, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Poul-Henning Kamp of the FreeBSD Project. * * 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. * 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. * * @(#)vfs_cache.c 8.5 (Berkeley) 3/22/95 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This structure describes the elements in the cache of recent * names looked up by namei. */ struct namecache { LIST_ENTRY(namecache) nc_hash; /* hash chain */ LIST_ENTRY(namecache) nc_src; /* source vnode list */ TAILQ_ENTRY(namecache) nc_dst; /* destination vnode list */ struct vnode *nc_dvp; /* vnode of parent of name */ struct vnode *nc_vp; /* vnode the name refers to */ u_char nc_flag; /* flag bits */ u_char nc_nlen; /* length of name */ char nc_name[0]; /* segment name */ }; /* * Name caching works as follows: * * Names found by directory scans are retained in a cache * for future reference. It is managed LRU, so frequently * used names will hang around. Cache is indexed by hash value * obtained from (vp, name) where vp refers to the directory * containing name. * * If it is a "negative" entry, (i.e. for a name that is known NOT to * exist) the vnode pointer will be NULL. * * Upon reaching the last segment of a path, if the reference * is for DELETE, or NOCACHE is set (rewrite), and the * name is located in the cache, it will be dropped. */ /* * Structures associated with name cacheing. */ #define NCHHASH(hash) \ (&nchashtbl[(hash) & nchash]) static LIST_HEAD(nchashhead, namecache) *nchashtbl; /* Hash Table */ static TAILQ_HEAD(, namecache) ncneg; /* Hash Table */ static u_long nchash; /* size of hash table */ SYSCTL_ULONG(_debug, OID_AUTO, nchash, CTLFLAG_RD, &nchash, 0, ""); static u_long ncnegfactor = 16; /* ratio of negative entries */ SYSCTL_ULONG(_debug, OID_AUTO, ncnegfactor, CTLFLAG_RW, &ncnegfactor, 0, ""); static u_long numneg; /* number of cache entries allocated */ SYSCTL_ULONG(_debug, OID_AUTO, numneg, CTLFLAG_RD, &numneg, 0, ""); static u_long numcache; /* number of cache entries allocated */ SYSCTL_ULONG(_debug, OID_AUTO, numcache, CTLFLAG_RD, &numcache, 0, ""); static u_long numcachehv; /* number of cache entries with vnodes held */ SYSCTL_ULONG(_debug, OID_AUTO, numcachehv, CTLFLAG_RD, &numcachehv, 0, ""); #if 0 static u_long numcachepl; /* number of cache purge for leaf entries */ SYSCTL_ULONG(_debug, OID_AUTO, numcachepl, CTLFLAG_RD, &numcachepl, 0, ""); #endif struct nchstats nchstats; /* cache effectiveness statistics */ static struct mtx cache_lock; MTX_SYSINIT(vfscache, &cache_lock, "Name Cache", MTX_DEF); #define CACHE_LOCK() mtx_lock(&cache_lock) #define CACHE_UNLOCK() mtx_unlock(&cache_lock) /* * UMA zones for the VFS cache. * * The small cache is used for entries with short names, which are the * most common. The large cache is used for entries which are too big to * fit in the small cache. */ static uma_zone_t cache_zone_small; static uma_zone_t cache_zone_large; #define CACHE_PATH_CUTOFF 32 #define CACHE_ZONE_SMALL (sizeof(struct namecache) + CACHE_PATH_CUTOFF) #define CACHE_ZONE_LARGE (sizeof(struct namecache) + NAME_MAX) #define cache_alloc(len) uma_zalloc(((len) <= CACHE_PATH_CUTOFF) ? \ cache_zone_small : cache_zone_large, M_WAITOK) #define cache_free(ncp) do { \ if (ncp != NULL) \ uma_zfree(((ncp)->nc_nlen <= CACHE_PATH_CUTOFF) ? \ cache_zone_small : cache_zone_large, (ncp)); \ } while (0) static int doingcache = 1; /* 1 => enable the cache */ SYSCTL_INT(_debug, OID_AUTO, vfscache, CTLFLAG_RW, &doingcache, 0, ""); /* Export size information to userland */ SYSCTL_INT(_debug_sizeof, OID_AUTO, namecache, CTLFLAG_RD, 0, sizeof(struct namecache), ""); /* * The new name cache statistics */ static SYSCTL_NODE(_vfs, OID_AUTO, cache, CTLFLAG_RW, 0, "Name cache statistics"); #define STATNODE(mode, name, var) \ SYSCTL_ULONG(_vfs_cache, OID_AUTO, name, mode, var, 0, ""); STATNODE(CTLFLAG_RD, numneg, &numneg); STATNODE(CTLFLAG_RD, numcache, &numcache); static u_long numcalls; STATNODE(CTLFLAG_RD, numcalls, &numcalls); static u_long dothits; STATNODE(CTLFLAG_RD, dothits, &dothits); static u_long dotdothits; STATNODE(CTLFLAG_RD, dotdothits, &dotdothits); static u_long numchecks; STATNODE(CTLFLAG_RD, numchecks, &numchecks); static u_long nummiss; STATNODE(CTLFLAG_RD, nummiss, &nummiss); static u_long nummisszap; STATNODE(CTLFLAG_RD, nummisszap, &nummisszap); static u_long numposzaps; STATNODE(CTLFLAG_RD, numposzaps, &numposzaps); static u_long numposhits; STATNODE(CTLFLAG_RD, numposhits, &numposhits); static u_long numnegzaps; STATNODE(CTLFLAG_RD, numnegzaps, &numnegzaps); static u_long numneghits; STATNODE(CTLFLAG_RD, numneghits, &numneghits); SYSCTL_OPAQUE(_vfs_cache, OID_AUTO, nchstats, CTLFLAG_RD, &nchstats, sizeof(nchstats), "LU", "VFS cache effectiveness statistics"); static void cache_zap(struct namecache *ncp); static int vn_vptocnp(struct vnode **vp, char **bp, char *buf, u_int *buflen); static int vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, char *buf, char **retbuf, u_int buflen); static MALLOC_DEFINE(M_VFSCACHE, "vfscache", "VFS name cache entries"); /* * Flags in namecache.nc_flag */ #define NCF_WHITE 1 /* * Grab an atomic snapshot of the name cache hash chain lengths */ SYSCTL_NODE(_debug, OID_AUTO, hashstat, CTLFLAG_RW, NULL, "hash table stats"); static int sysctl_debug_hashstat_rawnchash(SYSCTL_HANDLER_ARGS) { int error; struct nchashhead *ncpp; struct namecache *ncp; int n_nchash; int count; n_nchash = nchash + 1; /* nchash is max index, not count */ if (!req->oldptr) return SYSCTL_OUT(req, 0, n_nchash * sizeof(int)); /* Scan hash tables for applicable entries */ for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { CACHE_LOCK(); count = 0; LIST_FOREACH(ncp, ncpp, nc_hash) { count++; } CACHE_UNLOCK(); error = SYSCTL_OUT(req, &count, sizeof(count)); if (error) return (error); } return (0); } SYSCTL_PROC(_debug_hashstat, OID_AUTO, rawnchash, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_debug_hashstat_rawnchash, "S,int", "nchash chain lengths"); static int sysctl_debug_hashstat_nchash(SYSCTL_HANDLER_ARGS) { int error; struct nchashhead *ncpp; struct namecache *ncp; int n_nchash; int count, maxlength, used, pct; if (!req->oldptr) return SYSCTL_OUT(req, 0, 4 * sizeof(int)); n_nchash = nchash + 1; /* nchash is max index, not count */ used = 0; maxlength = 0; /* Scan hash tables for applicable entries */ for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { count = 0; CACHE_LOCK(); LIST_FOREACH(ncp, ncpp, nc_hash) { count++; } CACHE_UNLOCK(); if (count) used++; if (maxlength < count) maxlength = count; } n_nchash = nchash + 1; pct = (used * 100 * 100) / n_nchash; error = SYSCTL_OUT(req, &n_nchash, sizeof(n_nchash)); if (error) return (error); error = SYSCTL_OUT(req, &used, sizeof(used)); if (error) return (error); error = SYSCTL_OUT(req, &maxlength, sizeof(maxlength)); if (error) return (error); error = SYSCTL_OUT(req, &pct, sizeof(pct)); if (error) return (error); return (0); } SYSCTL_PROC(_debug_hashstat, OID_AUTO, nchash, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_debug_hashstat_nchash, "I", "nchash chain lengths"); /* * cache_zap(): * * Removes a namecache entry from cache, whether it contains an actual * pointer to a vnode or if it is just a negative cache entry. */ static void cache_zap(ncp) struct namecache *ncp; { struct vnode *vp; mtx_assert(&cache_lock, MA_OWNED); CTR2(KTR_VFS, "cache_zap(%p) vp %p", ncp, ncp->nc_vp); vp = NULL; LIST_REMOVE(ncp, nc_hash); LIST_REMOVE(ncp, nc_src); if (LIST_EMPTY(&ncp->nc_dvp->v_cache_src)) { vp = ncp->nc_dvp; numcachehv--; } if (ncp->nc_vp) { TAILQ_REMOVE(&ncp->nc_vp->v_cache_dst, ncp, nc_dst); ncp->nc_vp->v_dd = NULL; } else { TAILQ_REMOVE(&ncneg, ncp, nc_dst); numneg--; } numcache--; cache_free(ncp); if (vp) vdrop(vp); } /* * Lookup an entry in the cache * * Lookup is called with dvp pointing to the directory to search, * cnp pointing to the name of the entry being sought. If the lookup * succeeds, the vnode is returned in *vpp, and a status of -1 is * returned. If the lookup determines that the name does not exist * (negative cacheing), a status of ENOENT is returned. If the lookup * fails, a status of zero is returned. If the directory vnode is * recycled out from under us due to a forced unmount, a status of * EBADF is returned. * * vpp is locked and ref'd on return. If we're looking up DOTDOT, dvp is * unlocked. If we're looking up . an extra ref is taken, but the lock is * not recursively acquired. */ int cache_lookup(dvp, vpp, cnp) struct vnode *dvp; struct vnode **vpp; struct componentname *cnp; { struct namecache *ncp; u_int32_t hash; int error, ltype; if (!doingcache) { cnp->cn_flags &= ~MAKEENTRY; return (0); } retry: CACHE_LOCK(); numcalls++; if (cnp->cn_nameptr[0] == '.') { if (cnp->cn_namelen == 1) { *vpp = dvp; CTR2(KTR_VFS, "cache_lookup(%p, %s) found via .", dvp, cnp->cn_nameptr); dothits++; goto success; } if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { dotdothits++; if (dvp->v_dd == NULL || (cnp->cn_flags & MAKEENTRY) == 0) { CACHE_UNLOCK(); return (0); } *vpp = dvp->v_dd; CTR3(KTR_VFS, "cache_lookup(%p, %s) found %p via ..", dvp, cnp->cn_nameptr, *vpp); goto success; } } hash = fnv_32_buf(cnp->cn_nameptr, cnp->cn_namelen, FNV1_32_INIT); hash = fnv_32_buf(&dvp, sizeof(dvp), hash); LIST_FOREACH(ncp, (NCHHASH(hash)), nc_hash) { numchecks++; if (ncp->nc_dvp == dvp && ncp->nc_nlen == cnp->cn_namelen && !bcmp(ncp->nc_name, cnp->cn_nameptr, ncp->nc_nlen)) break; } /* We failed to find an entry */ if (ncp == 0) { if ((cnp->cn_flags & MAKEENTRY) == 0) { nummisszap++; } else { nummiss++; } nchstats.ncs_miss++; CACHE_UNLOCK(); return (0); } /* We don't want to have an entry, so dump it */ if ((cnp->cn_flags & MAKEENTRY) == 0) { numposzaps++; nchstats.ncs_badhits++; cache_zap(ncp); CACHE_UNLOCK(); return (0); } /* We found a "positive" match, return the vnode */ if (ncp->nc_vp) { numposhits++; nchstats.ncs_goodhits++; *vpp = ncp->nc_vp; CTR4(KTR_VFS, "cache_lookup(%p, %s) found %p via ncp %p", dvp, cnp->cn_nameptr, *vpp, ncp); goto success; } /* We found a negative match, and want to create it, so purge */ if (cnp->cn_nameiop == CREATE) { numnegzaps++; nchstats.ncs_badhits++; cache_zap(ncp); CACHE_UNLOCK(); return (0); } numneghits++; /* * We found a "negative" match, so we shift it to the end of * the "negative" cache entries queue to satisfy LRU. Also, * check to see if the entry is a whiteout; indicate this to * the componentname, if so. */ TAILQ_REMOVE(&ncneg, ncp, nc_dst); TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst); nchstats.ncs_neghits++; if (ncp->nc_flag & NCF_WHITE) cnp->cn_flags |= ISWHITEOUT; CACHE_UNLOCK(); return (ENOENT); success: /* * On success we return a locked and ref'd vnode as per the lookup * protocol. */ if (dvp == *vpp) { /* lookup on "." */ VREF(*vpp); CACHE_UNLOCK(); /* * When we lookup "." we still can be asked to lock it * differently... */ ltype = cnp->cn_lkflags & LK_TYPE_MASK; if (ltype != VOP_ISLOCKED(*vpp)) { if (ltype == LK_EXCLUSIVE) { vn_lock(*vpp, LK_UPGRADE | LK_RETRY); if ((*vpp)->v_iflag & VI_DOOMED) { /* forced unmount */ vrele(*vpp); *vpp = NULL; return (EBADF); } } else vn_lock(*vpp, LK_DOWNGRADE | LK_RETRY); } return (-1); } ltype = 0; /* silence gcc warning */ if (cnp->cn_flags & ISDOTDOT) { ltype = VOP_ISLOCKED(dvp); VOP_UNLOCK(dvp, 0); } VI_LOCK(*vpp); CACHE_UNLOCK(); error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, cnp->cn_thread); if (cnp->cn_flags & ISDOTDOT) vn_lock(dvp, ltype | LK_RETRY); if (error) { *vpp = NULL; goto retry; } if ((cnp->cn_flags & ISLASTCN) && (cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE) { ASSERT_VOP_ELOCKED(*vpp, "cache_lookup"); } return (-1); } /* * Add an entry to the cache. */ void cache_enter(dvp, vp, cnp) struct vnode *dvp; struct vnode *vp; struct componentname *cnp; { struct namecache *ncp, *n2; struct nchashhead *ncpp; u_int32_t hash; int hold; int zap; int len; CTR3(KTR_VFS, "cache_enter(%p, %p, %s)", dvp, vp, cnp->cn_nameptr); VNASSERT(vp == NULL || (vp->v_iflag & VI_DOOMED) == 0, vp, ("cahe_enter: Adding a doomed vnode")); if (!doingcache) return; if (cnp->cn_nameptr[0] == '.') { if (cnp->cn_namelen == 1) { return; } /* * For dotdot lookups only cache the v_dd pointer if the * directory has a link back to its parent via v_cache_dst. * Without this an unlinked directory would keep a soft * reference to its parent which could not be NULLd at * cache_purge() time. */ if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { CACHE_LOCK(); if (!TAILQ_EMPTY(&dvp->v_cache_dst)) dvp->v_dd = vp; CACHE_UNLOCK(); return; } } hold = 0; zap = 0; /* * Calculate the hash key and setup as much of the new * namecache entry as possible before acquiring the lock. */ ncp = cache_alloc(cnp->cn_namelen); ncp->nc_vp = vp; ncp->nc_dvp = dvp; len = ncp->nc_nlen = cnp->cn_namelen; hash = fnv_32_buf(cnp->cn_nameptr, len, FNV1_32_INIT); bcopy(cnp->cn_nameptr, ncp->nc_name, len); hash = fnv_32_buf(&dvp, sizeof(dvp), hash); CACHE_LOCK(); /* * See if this vnode is already in the cache with this name. * This can happen with concurrent lookups of the same path * name. */ if (vp) { TAILQ_FOREACH(n2, &vp->v_cache_dst, nc_dst) { if (n2->nc_dvp == dvp && n2->nc_nlen == cnp->cn_namelen && !bcmp(n2->nc_name, cnp->cn_nameptr, n2->nc_nlen)) { CACHE_UNLOCK(); cache_free(ncp); return; } } } else { TAILQ_FOREACH(n2, &ncneg, nc_dst) { if (n2->nc_nlen == cnp->cn_namelen && !bcmp(n2->nc_name, cnp->cn_nameptr, n2->nc_nlen)) { CACHE_UNLOCK(); cache_free(ncp); return; } } } numcache++; if (!vp) { numneg++; ncp->nc_flag = cnp->cn_flags & ISWHITEOUT ? NCF_WHITE : 0; } else if (vp->v_type == VDIR) { vp->v_dd = dvp; } else { vp->v_dd = NULL; } /* * Insert the new namecache entry into the appropriate chain * within the cache entries table. */ ncpp = NCHHASH(hash); LIST_INSERT_HEAD(ncpp, ncp, nc_hash); if (LIST_EMPTY(&dvp->v_cache_src)) { hold = 1; numcachehv++; } LIST_INSERT_HEAD(&dvp->v_cache_src, ncp, nc_src); /* * If the entry is "negative", we place it into the * "negative" cache queue, otherwise, we place it into the * destination vnode's cache entries queue. */ if (vp) { TAILQ_INSERT_HEAD(&vp->v_cache_dst, ncp, nc_dst); } else { TAILQ_INSERT_TAIL(&ncneg, ncp, nc_dst); } if (numneg * ncnegfactor > numcache) { ncp = TAILQ_FIRST(&ncneg); zap = 1; } if (hold) vhold(dvp); if (zap) cache_zap(ncp); CACHE_UNLOCK(); } /* * Name cache initialization, from vfs_init() when we are booting */ static void nchinit(void *dummy __unused) { TAILQ_INIT(&ncneg); cache_zone_small = uma_zcreate("S VFS Cache", CACHE_ZONE_SMALL, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_ZINIT); cache_zone_large = uma_zcreate("L VFS Cache", CACHE_ZONE_LARGE, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_ZINIT); nchashtbl = hashinit(desiredvnodes * 2, M_VFSCACHE, &nchash); } SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nchinit, NULL); /* * Invalidate all entries to a particular vnode. */ void cache_purge(vp) struct vnode *vp; { CTR1(KTR_VFS, "cache_purge(%p)", vp); CACHE_LOCK(); while (!LIST_EMPTY(&vp->v_cache_src)) cache_zap(LIST_FIRST(&vp->v_cache_src)); while (!TAILQ_EMPTY(&vp->v_cache_dst)) cache_zap(TAILQ_FIRST(&vp->v_cache_dst)); vp->v_dd = NULL; CACHE_UNLOCK(); } /* * Flush all entries referencing a particular filesystem. */ void cache_purgevfs(mp) struct mount *mp; { struct nchashhead *ncpp; struct namecache *ncp, *nnp; /* Scan hash tables for applicable entries */ CACHE_LOCK(); for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) { LIST_FOREACH_SAFE(ncp, ncpp, nc_hash, nnp) { if (ncp->nc_dvp->v_mount == mp) cache_zap(ncp); } } CACHE_UNLOCK(); } /* * Perform canonical checks and cache lookup and pass on to filesystem * through the vop_cachedlookup only if needed. */ int vfs_cache_lookup(ap) struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { struct vnode *dvp; int error; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct ucred *cred = cnp->cn_cred; int flags = cnp->cn_flags; struct thread *td = cnp->cn_thread; *vpp = NULL; dvp = ap->a_dvp; if (dvp->v_type != VDIR) return (ENOTDIR); if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); error = VOP_ACCESS(dvp, VEXEC, cred, td); if (error) return (error); error = cache_lookup(dvp, vpp, cnp); if (error == 0) return (VOP_CACHEDLOOKUP(dvp, vpp, cnp)); if (error == -1) return (0); return (error); } #ifndef _SYS_SYSPROTO_H_ struct __getcwd_args { u_char *buf; u_int buflen; }; #endif /* * XXX All of these sysctls would probably be more productive dead. */ static int disablecwd; SYSCTL_INT(_debug, OID_AUTO, disablecwd, CTLFLAG_RW, &disablecwd, 0, "Disable the getcwd syscall"); /* Implementation of the getcwd syscall. */ int __getcwd(td, uap) struct thread *td; struct __getcwd_args *uap; { return (kern___getcwd(td, uap->buf, UIO_USERSPACE, uap->buflen)); } int kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg, u_int buflen) { char *bp, *tmpbuf; struct filedesc *fdp; struct vnode *cdir, *rdir; int error, vfslocked; if (disablecwd) return (ENODEV); if (buflen < 2) return (EINVAL); if (buflen > MAXPATHLEN) buflen = MAXPATHLEN; tmpbuf = malloc(buflen, M_TEMP, M_WAITOK); fdp = td->td_proc->p_fd; FILEDESC_SLOCK(fdp); cdir = fdp->fd_cdir; VREF(cdir); rdir = fdp->fd_rdir; VREF(rdir); FILEDESC_SUNLOCK(fdp); error = vn_fullpath1(td, cdir, rdir, tmpbuf, &bp, buflen); vfslocked = VFS_LOCK_GIANT(rdir->v_mount); vrele(rdir); VFS_UNLOCK_GIANT(vfslocked); vfslocked = VFS_LOCK_GIANT(cdir->v_mount); vrele(cdir); VFS_UNLOCK_GIANT(vfslocked); if (!error) { if (bufseg == UIO_SYSSPACE) bcopy(bp, buf, strlen(bp) + 1); else error = copyout(bp, buf, strlen(bp) + 1); } free(tmpbuf, M_TEMP); return (error); } /* * Thus begins the fullpath magic. */ #undef STATNODE #define STATNODE(name) \ static u_int name; \ SYSCTL_UINT(_vfs_cache, OID_AUTO, name, CTLFLAG_RD, &name, 0, "") static int disablefullpath; SYSCTL_INT(_debug, OID_AUTO, disablefullpath, CTLFLAG_RW, &disablefullpath, 0, "Disable the vn_fullpath function"); /* These count for kern___getcwd(), too. */ STATNODE(numfullpathcalls); STATNODE(numfullpathfail1); STATNODE(numfullpathfail2); STATNODE(numfullpathfail4); STATNODE(numfullpathfound); /* * Retrieve the full filesystem path that correspond to a vnode from the name * cache (if available) */ int vn_fullpath(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf) { char *buf; struct filedesc *fdp; struct vnode *rdir; int error, vfslocked; if (disablefullpath) return (ENODEV); if (vn == NULL) return (EINVAL); buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); fdp = td->td_proc->p_fd; FILEDESC_SLOCK(fdp); rdir = fdp->fd_rdir; VREF(rdir); FILEDESC_SUNLOCK(fdp); error = vn_fullpath1(td, vn, rdir, buf, retbuf, MAXPATHLEN); vfslocked = VFS_LOCK_GIANT(rdir->v_mount); vrele(rdir); VFS_UNLOCK_GIANT(vfslocked); if (!error) *freebuf = buf; else free(buf, M_TEMP); return (error); } /* * This function is similar to vn_fullpath, but it attempts to lookup the * pathname relative to the global root mount point. This is required for the * auditing sub-system, as audited pathnames must be absolute, relative to the * global root mount point. */ int vn_fullpath_global(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf) { char *buf; int error; if (disablefullpath) return (ENODEV); if (vn == NULL) return (EINVAL); buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); error = vn_fullpath1(td, vn, rootvnode, buf, retbuf, MAXPATHLEN); if (!error) *freebuf = buf; else free(buf, M_TEMP); return (error); } static int vn_vptocnp(struct vnode **vp, char **bp, char *buf, u_int *buflen) { struct vnode *dvp; int error, vfslocked; vhold(*vp); CACHE_UNLOCK(); vfslocked = VFS_LOCK_GIANT((*vp)->v_mount); vn_lock(*vp, LK_SHARED | LK_RETRY); - vdrop(*vp); error = VOP_VPTOCNP(*vp, &dvp, buf, buflen); VOP_UNLOCK(*vp, 0); + vdrop(*vp); VFS_UNLOCK_GIANT(vfslocked); if (error) { numfullpathfail2++; return (error); } *bp = buf + *buflen; *vp = dvp; CACHE_LOCK(); if ((*vp)->v_iflag & VI_DOOMED) { /* forced unmount */ CACHE_UNLOCK(); vdrop(*vp); return (ENOENT); } vdrop(*vp); return (0); } /* * The magic behind kern___getcwd() and vn_fullpath(). */ static int vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir, char *buf, char **retbuf, u_int buflen) { char *bp; int error, i, slash_prefixed; struct namecache *ncp; buflen--; bp = buf + buflen; *bp = '\0'; error = 0; slash_prefixed = 0; CACHE_LOCK(); numfullpathcalls++; if (vp->v_type != VDIR) { ncp = TAILQ_FIRST(&vp->v_cache_dst); if (ncp != NULL) { for (i = ncp->nc_nlen - 1; i >= 0 && bp > buf; i--) *--bp = ncp->nc_name[i]; if (bp == buf) { numfullpathfail4++; CACHE_UNLOCK(); return (ENOMEM); } vp = ncp->nc_dvp; } else { error = vn_vptocnp(&vp, &bp, buf, &buflen); if (error) { return (error); } } *--bp = '/'; buflen--; if (buflen < 0) { numfullpathfail4++; CACHE_UNLOCK(); return (ENOMEM); } slash_prefixed = 1; } while (vp != rdir && vp != rootvnode) { if (vp->v_vflag & VV_ROOT) { if (vp->v_iflag & VI_DOOMED) { /* forced unmount */ CACHE_UNLOCK(); error = EBADF; break; } vp = vp->v_mount->mnt_vnodecovered; continue; } if (vp->v_type != VDIR) { numfullpathfail1++; CACHE_UNLOCK(); error = ENOTDIR; break; } ncp = TAILQ_FIRST(&vp->v_cache_dst); if (ncp != NULL) { MPASS(ncp->nc_dvp == vp->v_dd); buflen -= ncp->nc_nlen - 1; for (i = ncp->nc_nlen - 1; i >= 0 && bp != buf; i--) *--bp = ncp->nc_name[i]; if (bp == buf) { numfullpathfail4++; CACHE_UNLOCK(); error = ENOMEM; break; } vp = ncp->nc_dvp; } else { error = vn_vptocnp(&vp, &bp, buf, &buflen); if (error) { break; } } *--bp = '/'; buflen--; if (buflen < 0) { numfullpathfail4++; CACHE_UNLOCK(); error = ENOMEM; break; } slash_prefixed = 1; } if (error) return (error); if (!slash_prefixed) { if (bp == buf) { numfullpathfail4++; CACHE_UNLOCK(); return (ENOMEM); } else { *--bp = '/'; } } numfullpathfound++; CACHE_UNLOCK(); *retbuf = bp; return (0); } int vn_commname(struct vnode *vp, char *buf, u_int buflen) { struct namecache *ncp; int l; CACHE_LOCK(); ncp = TAILQ_FIRST(&vp->v_cache_dst); if (!ncp) { CACHE_UNLOCK(); return (ENOENT); } l = min(ncp->nc_nlen, buflen - 1); memcpy(buf, ncp->nc_name, l); CACHE_UNLOCK(); buf[l] = '\0'; return (0); } Index: projects/cambria/sys/net/if_tun.c =================================================================== --- projects/cambria/sys/net/if_tun.c (revision 186459) +++ projects/cambria/sys/net/if_tun.c (revision 186460) @@ -1,1069 +1,1078 @@ /* $NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $ */ /*- * Copyright (c) 1988, Julian Onions * Nottingham University 1987. * * This source may be freely distributed, however I would be interested * in any changes that are made. * * This driver takes packets off the IP i/f and hands them up to a * user process to have its wicked way with. This driver has it's * roots in a similar driver written by Phil Cockcroft (formerly) at * UCL. This driver is based much more on read/write/poll mode of * operation though. * * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipx.h" #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #endif #include #include #include +#include #include /* * tun_list is protected by global tunmtx. Other mutable fields are * protected by tun->tun_mtx, or by their owning subsystem. tun_dev is * static for the duration of a tunnel interface. */ struct tun_softc { TAILQ_ENTRY(tun_softc) tun_list; struct cdev *tun_dev; u_short tun_flags; /* misc flags */ #define TUN_OPEN 0x0001 #define TUN_INITED 0x0002 #define TUN_RCOLL 0x0004 #define TUN_IASET 0x0008 #define TUN_DSTADDR 0x0010 #define TUN_LMODE 0x0020 #define TUN_RWAIT 0x0040 #define TUN_ASYNC 0x0080 #define TUN_IFHEAD 0x0100 #define TUN_READY (TUN_OPEN | TUN_INITED) /* * XXXRW: tun_pid is used to exclusively lock /dev/tun. Is this * actually needed? Can we just return EBUSY if already open? * Problem is that this involved inherent races when a tun device * is handed off from one process to another, as opposed to just * being slightly stale informationally. */ pid_t tun_pid; /* owning pid */ struct ifnet *tun_ifp; /* the interface */ struct sigio *tun_sigio; /* information for async I/O */ struct selinfo tun_rsel; /* read select */ struct mtx tun_mtx; /* protect mutable softc fields */ + struct cv tun_cv; /* protect against ref'd dev destroy */ }; #define TUN2IFP(sc) ((sc)->tun_ifp) #define TUNDEBUG if (tundebug) if_printf #define TUNNAME "tun" /* * All mutable global variables in if_tun are locked using tunmtx, with * the exception of tundebug, which is used unlocked, and tunclones, * which is static after setup. */ static struct mtx tunmtx; static MALLOC_DEFINE(M_TUN, TUNNAME, "Tunnel Interface"); static int tundebug = 0; static int tundclone = 1; static struct clonedevs *tunclones; static TAILQ_HEAD(,tun_softc) tunhead = TAILQ_HEAD_INITIALIZER(tunhead); SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, ""); SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, OID_AUTO, tun, CTLFLAG_RW, 0, "IP tunnel software network interface."); SYSCTL_INT(_net_link_tun, OID_AUTO, devfs_cloning, CTLFLAG_RW, &tundclone, 0, "Enable legacy devfs interface creation."); TUNABLE_INT("net.link.tun.devfs_cloning", &tundclone); static void tunclone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev); static void tuncreate(const char *name, struct cdev *dev); static int tunifioctl(struct ifnet *, u_long, caddr_t); static int tuninit(struct ifnet *); static int tunmodevent(module_t, int, void *); static int tunoutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *rt); static void tunstart(struct ifnet *); static int tun_clone_create(struct if_clone *, int, caddr_t); static void tun_clone_destroy(struct ifnet *); IFC_SIMPLE_DECLARE(tun, 0); static d_open_t tunopen; static d_close_t tunclose; static d_read_t tunread; static d_write_t tunwrite; static d_ioctl_t tunioctl; static d_poll_t tunpoll; static d_kqfilter_t tunkqfilter; static int tunkqread(struct knote *, long); static int tunkqwrite(struct knote *, long); static void tunkqdetach(struct knote *); static struct filterops tun_read_filterops = { .f_isfd = 1, .f_attach = NULL, .f_detach = tunkqdetach, .f_event = tunkqread, }; static struct filterops tun_write_filterops = { .f_isfd = 1, .f_attach = NULL, .f_detach = tunkqdetach, .f_event = tunkqwrite, }; static struct cdevsw tun_cdevsw = { .d_version = D_VERSION, .d_flags = D_PSEUDO | D_NEEDGIANT | D_NEEDMINOR, .d_open = tunopen, .d_close = tunclose, .d_read = tunread, .d_write = tunwrite, .d_ioctl = tunioctl, .d_poll = tunpoll, .d_kqfilter = tunkqfilter, .d_name = TUNNAME, }; static int tun_clone_create(struct if_clone *ifc, int unit, caddr_t params) { struct cdev *dev; int i; /* find any existing device, or allocate new unit number */ i = clone_create(&tunclones, &tun_cdevsw, &unit, &dev, 0); if (i) { /* No preexisting struct cdev *, create one */ dev = make_dev(&tun_cdevsw, unit, UID_UUCP, GID_DIALER, 0600, "%s%d", ifc->ifc_name, unit); if (dev != NULL) { dev_ref(dev); dev->si_flags |= SI_CHEAPCLONE; } } tuncreate(ifc->ifc_name, dev); return (0); } static void tunclone(void *arg, struct ucred *cred, char *name, int namelen, struct cdev **dev) { char devname[SPECNAMELEN + 1]; int u, i, append_unit; if (*dev != NULL) return; /* * If tun cloning is enabled, only the superuser can create an * interface. */ if (!tundclone || priv_check_cred(cred, PRIV_NET_IFCREATE, 0) != 0) return; if (strcmp(name, TUNNAME) == 0) { u = -1; } else if (dev_stdclone(name, NULL, TUNNAME, &u) != 1) return; /* Don't recognise the name */ if (u != -1 && u > IF_MAXUNIT) return; /* Unit number too high */ if (u == -1) append_unit = 1; else append_unit = 0; CURVNET_SET(TD_TO_VNET(curthread)); /* find any existing device, or allocate new unit number */ i = clone_create(&tunclones, &tun_cdevsw, &u, dev, 0); if (i) { if (append_unit) { namelen = snprintf(devname, sizeof(devname), "%s%d", name, u); name = devname; } /* No preexisting struct cdev *, create one */ *dev = make_dev(&tun_cdevsw, u, UID_UUCP, GID_DIALER, 0600, "%s", name); if (*dev != NULL) { dev_ref(*dev); (*dev)->si_flags |= SI_CHEAPCLONE; } } if_clone_create(name, namelen, NULL); CURVNET_RESTORE(); } static void tun_destroy(struct tun_softc *tp) { struct cdev *dev; /* Unlocked read. */ - KASSERT((tp->tun_flags & TUN_OPEN) == 0, - ("tununits is out of sync - unit %d", TUN2IFP(tp)->if_dunit)); + mtx_lock(&tp->tun_mtx); + if ((tp->tun_flags & TUN_OPEN) != 0) + cv_wait_unlock(&tp->tun_cv, &tp->tun_mtx); CURVNET_SET(TUN2IFP(tp)->if_vnet); dev = tp->tun_dev; bpfdetach(TUN2IFP(tp)); if_detach(TUN2IFP(tp)); if_free(TUN2IFP(tp)); destroy_dev(dev); knlist_destroy(&tp->tun_rsel.si_note); mtx_destroy(&tp->tun_mtx); + cv_destroy(&tp->tun_cv); free(tp, M_TUN); CURVNET_RESTORE(); } static void tun_clone_destroy(struct ifnet *ifp) { struct tun_softc *tp = ifp->if_softc; mtx_lock(&tunmtx); TAILQ_REMOVE(&tunhead, tp, tun_list); mtx_unlock(&tunmtx); tun_destroy(tp); } static int tunmodevent(module_t mod, int type, void *data) { static eventhandler_tag tag; struct tun_softc *tp; switch (type) { case MOD_LOAD: mtx_init(&tunmtx, "tunmtx", NULL, MTX_DEF); clone_setup(&tunclones); tag = EVENTHANDLER_REGISTER(dev_clone, tunclone, 0, 1000); if (tag == NULL) return (ENOMEM); if_clone_attach(&tun_cloner); break; case MOD_UNLOAD: if_clone_detach(&tun_cloner); EVENTHANDLER_DEREGISTER(dev_clone, tag); mtx_lock(&tunmtx); while ((tp = TAILQ_FIRST(&tunhead)) != NULL) { TAILQ_REMOVE(&tunhead, tp, tun_list); mtx_unlock(&tunmtx); tun_destroy(tp); mtx_lock(&tunmtx); } mtx_unlock(&tunmtx); clone_cleanup(&tunclones); mtx_destroy(&tunmtx); break; default: return EOPNOTSUPP; } return 0; } static moduledata_t tun_mod = { "if_tun", tunmodevent, 0 }; DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); static void tunstart(struct ifnet *ifp) { struct tun_softc *tp = ifp->if_softc; struct mbuf *m; TUNDEBUG(ifp,"%s starting\n", ifp->if_xname); if (ALTQ_IS_ENABLED(&ifp->if_snd)) { IFQ_LOCK(&ifp->if_snd); IFQ_POLL_NOLOCK(&ifp->if_snd, m); if (m == NULL) { IFQ_UNLOCK(&ifp->if_snd); return; } IFQ_UNLOCK(&ifp->if_snd); } mtx_lock(&tp->tun_mtx); if (tp->tun_flags & TUN_RWAIT) { tp->tun_flags &= ~TUN_RWAIT; wakeup(tp); } if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio) { mtx_unlock(&tp->tun_mtx); pgsigio(&tp->tun_sigio, SIGIO, 0); } else mtx_unlock(&tp->tun_mtx); selwakeuppri(&tp->tun_rsel, PZERO + 1); KNOTE_UNLOCKED(&tp->tun_rsel.si_note, 0); } /* XXX: should return an error code so it can fail. */ static void tuncreate(const char *name, struct cdev *dev) { struct tun_softc *sc; struct ifnet *ifp; dev->si_flags &= ~SI_CHEAPCLONE; sc = malloc(sizeof(*sc), M_TUN, M_WAITOK | M_ZERO); mtx_init(&sc->tun_mtx, "tun_mtx", NULL, MTX_DEF); + cv_init(&sc->tun_cv, "tun_condvar"); sc->tun_flags = TUN_INITED; sc->tun_dev = dev; mtx_lock(&tunmtx); TAILQ_INSERT_TAIL(&tunhead, sc, tun_list); mtx_unlock(&tunmtx); ifp = sc->tun_ifp = if_alloc(IFT_PPP); if (ifp == NULL) panic("%s%d: failed to if_alloc() interface.\n", name, dev2unit(dev)); if_initname(ifp, name, dev2unit(dev)); ifp->if_mtu = TUNMTU; ifp->if_ioctl = tunifioctl; ifp->if_output = tunoutput; ifp->if_start = tunstart; ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ifp->if_softc = sc; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = 0; IFQ_SET_READY(&ifp->if_snd); knlist_init(&sc->tun_rsel.si_note, NULL, NULL, NULL, NULL); if_attach(ifp); bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); dev->si_drv1 = sc; TUNDEBUG(ifp, "interface %s is created, minor = %#x\n", ifp->if_xname, dev2unit(dev)); } static int tunopen(struct cdev *dev, int flag, int mode, struct thread *td) { struct ifnet *ifp; struct tun_softc *tp; /* * XXXRW: Non-atomic test and set of dev->si_drv1 requires * synchronization. */ tp = dev->si_drv1; if (!tp) { tuncreate(TUNNAME, dev); tp = dev->si_drv1; } /* * XXXRW: This use of tun_pid is subject to error due to the * fact that a reference to the tunnel can live beyond the * death of the process that created it. Can we replace this * with a simple busy flag? */ mtx_lock(&tp->tun_mtx); if (tp->tun_pid != 0 && tp->tun_pid != td->td_proc->p_pid) { mtx_unlock(&tp->tun_mtx); return (EBUSY); } tp->tun_pid = td->td_proc->p_pid; tp->tun_flags |= TUN_OPEN; mtx_unlock(&tp->tun_mtx); ifp = TUN2IFP(tp); if_link_state_change(ifp, LINK_STATE_UP); TUNDEBUG(ifp, "open\n"); return (0); } /* * tunclose - close the device - mark i/f down & delete * routing info */ static int tunclose(struct cdev *dev, int foo, int bar, struct thread *td) { struct tun_softc *tp; struct ifnet *ifp; int s; tp = dev->si_drv1; ifp = TUN2IFP(tp); mtx_lock(&tp->tun_mtx); tp->tun_flags &= ~TUN_OPEN; tp->tun_pid = 0; + mtx_unlock(&tp->tun_mtx); /* * junk all pending output */ CURVNET_SET(ifp->if_vnet); s = splimp(); IFQ_PURGE(&ifp->if_snd); splx(s); - mtx_unlock(&tp->tun_mtx); if (ifp->if_flags & IFF_UP) { s = splimp(); if_down(ifp); splx(s); } /* Delete all addresses and routes which reference this interface. */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) { struct ifaddr *ifa; s = splimp(); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { /* deal w/IPv4 PtP destination; unlocked read */ if (ifa->ifa_addr->sa_family == AF_INET) { rtinit(ifa, (int)RTM_DELETE, tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0); } else { rtinit(ifa, (int)RTM_DELETE, 0); } } if_purgeaddrs(ifp); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; splx(s); } if_link_state_change(ifp, LINK_STATE_DOWN); CURVNET_RESTORE(); + mtx_lock(&tp->tun_mtx); funsetown(&tp->tun_sigio); selwakeuppri(&tp->tun_rsel, PZERO + 1); KNOTE_UNLOCKED(&tp->tun_rsel.si_note, 0); TUNDEBUG (ifp, "closed\n"); + + cv_broadcast(&tp->tun_cv); + mtx_unlock(&tp->tun_mtx); return (0); } static int tuninit(struct ifnet *ifp) { #ifdef INET struct tun_softc *tp = ifp->if_softc; struct ifaddr *ifa; #endif int error = 0; TUNDEBUG(ifp, "tuninit\n"); ifp->if_flags |= IFF_UP; ifp->if_drv_flags |= IFF_DRV_RUNNING; getmicrotime(&ifp->if_lastchange); #ifdef INET TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET) { struct sockaddr_in *si; si = (struct sockaddr_in *)ifa->ifa_addr; mtx_lock(&tp->tun_mtx); if (si->sin_addr.s_addr) tp->tun_flags |= TUN_IASET; si = (struct sockaddr_in *)ifa->ifa_dstaddr; if (si && si->sin_addr.s_addr) tp->tun_flags |= TUN_DSTADDR; mtx_unlock(&tp->tun_mtx); } } #endif return (error); } /* * Process an ioctl request. */ static int tunifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ifreq *ifr = (struct ifreq *)data; struct tun_softc *tp = ifp->if_softc; struct ifstat *ifs; int error = 0, s; s = splimp(); switch(cmd) { case SIOCGIFSTATUS: ifs = (struct ifstat *)data; mtx_lock(&tp->tun_mtx); if (tp->tun_pid) sprintf(ifs->ascii + strlen(ifs->ascii), "\tOpened by PID %d\n", tp->tun_pid); mtx_unlock(&tp->tun_mtx); break; case SIOCSIFADDR: error = tuninit(ifp); TUNDEBUG(ifp, "address set, error=%d\n", error); break; case SIOCSIFDSTADDR: error = tuninit(ifp); TUNDEBUG(ifp, "destination address set, error=%d\n", error); break; case SIOCSIFMTU: ifp->if_mtu = ifr->ifr_mtu; TUNDEBUG(ifp, "mtu set\n"); break; case SIOCSIFFLAGS: case SIOCADDMULTI: case SIOCDELMULTI: break; default: error = EINVAL; } splx(s); return (error); } /* * tunoutput - queue packets from higher level ready to put out. */ static int tunoutput( struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst, struct rtentry *rt) { struct tun_softc *tp = ifp->if_softc; u_short cached_tun_flags; int error; u_int32_t af; TUNDEBUG (ifp, "tunoutput\n"); #ifdef MAC error = mac_ifnet_check_transmit(ifp, m0); if (error) { m_freem(m0); return (error); } #endif /* Could be unlocked read? */ mtx_lock(&tp->tun_mtx); cached_tun_flags = tp->tun_flags; mtx_unlock(&tp->tun_mtx); if ((cached_tun_flags & TUN_READY) != TUN_READY) { TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); m_freem (m0); return (EHOSTDOWN); } if ((ifp->if_flags & IFF_UP) != IFF_UP) { m_freem (m0); return (EHOSTDOWN); } /* BPF writes need to be handled specially. */ if (dst->sa_family == AF_UNSPEC) { bcopy(dst->sa_data, &af, sizeof(af)); dst->sa_family = af; } if (bpf_peers_present(ifp->if_bpf)) { af = dst->sa_family; bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m0); } /* prepend sockaddr? this may abort if the mbuf allocation fails */ if (cached_tun_flags & TUN_LMODE) { /* allocate space for sockaddr */ M_PREPEND(m0, dst->sa_len, M_DONTWAIT); /* if allocation failed drop packet */ if (m0 == NULL) { ifp->if_iqdrops++; ifp->if_oerrors++; return (ENOBUFS); } else { bcopy(dst, m0->m_data, dst->sa_len); } } if (cached_tun_flags & TUN_IFHEAD) { /* Prepend the address family */ M_PREPEND(m0, 4, M_DONTWAIT); /* if allocation failed drop packet */ if (m0 == NULL) { ifp->if_iqdrops++; ifp->if_oerrors++; return (ENOBUFS); } else *(u_int32_t *)m0->m_data = htonl(dst->sa_family); } else { #ifdef INET if (dst->sa_family != AF_INET) #endif { m_freem(m0); return (EAFNOSUPPORT); } } error = (ifp->if_transmit)(ifp, m0); if (error) { ifp->if_collisions++; return (ENOBUFS); } ifp->if_opackets++; return (0); } /* * the cdevsw interface is now pretty minimal. */ static int tunioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { int s; int error; struct tun_softc *tp = dev->si_drv1; struct tuninfo *tunp; switch (cmd) { case TUNSIFINFO: tunp = (struct tuninfo *)data; if (tunp->mtu < IF_MINMTU) return (EINVAL); if (TUN2IFP(tp)->if_mtu != tunp->mtu) { error = priv_check(td, PRIV_NET_SETIFMTU); if (error) return (error); } TUN2IFP(tp)->if_mtu = tunp->mtu; TUN2IFP(tp)->if_type = tunp->type; TUN2IFP(tp)->if_baudrate = tunp->baudrate; break; case TUNGIFINFO: tunp = (struct tuninfo *)data; tunp->mtu = TUN2IFP(tp)->if_mtu; tunp->type = TUN2IFP(tp)->if_type; tunp->baudrate = TUN2IFP(tp)->if_baudrate; break; case TUNSDEBUG: tundebug = *(int *)data; break; case TUNGDEBUG: *(int *)data = tundebug; break; case TUNSLMODE: mtx_lock(&tp->tun_mtx); if (*(int *)data) { tp->tun_flags |= TUN_LMODE; tp->tun_flags &= ~TUN_IFHEAD; } else tp->tun_flags &= ~TUN_LMODE; mtx_unlock(&tp->tun_mtx); break; case TUNSIFHEAD: mtx_lock(&tp->tun_mtx); if (*(int *)data) { tp->tun_flags |= TUN_IFHEAD; tp->tun_flags &= ~TUN_LMODE; } else tp->tun_flags &= ~TUN_IFHEAD; mtx_unlock(&tp->tun_mtx); break; case TUNGIFHEAD: /* Could be unlocked read? */ mtx_lock(&tp->tun_mtx); *(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0; mtx_unlock(&tp->tun_mtx); break; case TUNSIFMODE: /* deny this if UP */ if (TUN2IFP(tp)->if_flags & IFF_UP) return(EBUSY); switch (*(int *)data & ~IFF_MULTICAST) { case IFF_POINTOPOINT: case IFF_BROADCAST: TUN2IFP(tp)->if_flags &= ~(IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST); TUN2IFP(tp)->if_flags |= *(int *)data; break; default: return(EINVAL); } break; case TUNSIFPID: mtx_lock(&tp->tun_mtx); tp->tun_pid = curthread->td_proc->p_pid; mtx_unlock(&tp->tun_mtx); break; case FIONBIO: break; case FIOASYNC: mtx_lock(&tp->tun_mtx); if (*(int *)data) tp->tun_flags |= TUN_ASYNC; else tp->tun_flags &= ~TUN_ASYNC; mtx_unlock(&tp->tun_mtx); break; case FIONREAD: s = splimp(); if (!IFQ_IS_EMPTY(&TUN2IFP(tp)->if_snd)) { struct mbuf *mb; IFQ_LOCK(&TUN2IFP(tp)->if_snd); IFQ_POLL_NOLOCK(&TUN2IFP(tp)->if_snd, mb); for( *(int *)data = 0; mb != 0; mb = mb->m_next) *(int *)data += mb->m_len; IFQ_UNLOCK(&TUN2IFP(tp)->if_snd); } else *(int *)data = 0; splx(s); break; case FIOSETOWN: return (fsetown(*(int *)data, &tp->tun_sigio)); case FIOGETOWN: *(int *)data = fgetown(&tp->tun_sigio); return (0); /* This is deprecated, FIOSETOWN should be used instead. */ case TIOCSPGRP: return (fsetown(-(*(int *)data), &tp->tun_sigio)); /* This is deprecated, FIOGETOWN should be used instead. */ case TIOCGPGRP: *(int *)data = -fgetown(&tp->tun_sigio); return (0); default: return (ENOTTY); } return (0); } /* * The cdevsw read interface - reads a packet at a time, or at * least as much of a packet as can be read. */ static int tunread(struct cdev *dev, struct uio *uio, int flag) { struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = TUN2IFP(tp); struct mbuf *m; int error=0, len, s; TUNDEBUG (ifp, "read\n"); mtx_lock(&tp->tun_mtx); if ((tp->tun_flags & TUN_READY) != TUN_READY) { mtx_unlock(&tp->tun_mtx); TUNDEBUG (ifp, "not ready 0%o\n", tp->tun_flags); return (EHOSTDOWN); } tp->tun_flags &= ~TUN_RWAIT; mtx_unlock(&tp->tun_mtx); s = splimp(); do { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) { if (flag & O_NONBLOCK) { splx(s); return (EWOULDBLOCK); } mtx_lock(&tp->tun_mtx); tp->tun_flags |= TUN_RWAIT; mtx_unlock(&tp->tun_mtx); if ((error = tsleep(tp, PCATCH | (PZERO + 1), "tunread", 0)) != 0) { splx(s); return (error); } } } while (m == NULL); splx(s); while (m && uio->uio_resid > 0 && error == 0) { len = min(uio->uio_resid, m->m_len); if (len != 0) error = uiomove(mtod(m, void *), len, uio); m = m_free(m); } if (m) { TUNDEBUG(ifp, "Dropping mbuf\n"); m_freem(m); } return (error); } /* * the cdevsw write interface - an atomic write is a packet - or else! */ static int tunwrite(struct cdev *dev, struct uio *uio, int flag) { struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = TUN2IFP(tp); struct mbuf *m; int error = 0; uint32_t family; int isr; TUNDEBUG(ifp, "tunwrite\n"); if ((ifp->if_flags & IFF_UP) != IFF_UP) /* ignore silently */ return (0); if (uio->uio_resid == 0) return (0); if (uio->uio_resid < 0 || uio->uio_resid > TUNMRU) { TUNDEBUG(ifp, "len=%d!\n", uio->uio_resid); return (EIO); } if ((m = m_uiotombuf(uio, M_DONTWAIT, 0, 0, M_PKTHDR)) == NULL) { ifp->if_ierrors++; return (error); } m->m_pkthdr.rcvif = ifp; #ifdef MAC mac_ifnet_create_mbuf(ifp, m); #endif /* Could be unlocked read? */ mtx_lock(&tp->tun_mtx); if (tp->tun_flags & TUN_IFHEAD) { mtx_unlock(&tp->tun_mtx); if (m->m_len < sizeof(family) && (m = m_pullup(m, sizeof(family))) == NULL) return (ENOBUFS); family = ntohl(*mtod(m, u_int32_t *)); m_adj(m, sizeof(family)); } else { mtx_unlock(&tp->tun_mtx); family = AF_INET; } BPF_MTAP2(ifp, &family, sizeof(family), m); switch (family) { #ifdef INET case AF_INET: isr = NETISR_IP; break; #endif #ifdef INET6 case AF_INET6: isr = NETISR_IPV6; break; #endif #ifdef IPX case AF_IPX: isr = NETISR_IPX; break; #endif #ifdef NETATALK case AF_APPLETALK: isr = NETISR_ATALK2; break; #endif default: m_freem(m); return (EAFNOSUPPORT); } /* First chunk of an mbuf contains good junk */ if (harvest.point_to_point) random_harvest(m, 16, 3, 0, RANDOM_NET); ifp->if_ibytes += m->m_pkthdr.len; ifp->if_ipackets++; CURVNET_SET(ifp->if_vnet); netisr_dispatch(isr, m); CURVNET_RESTORE(); return (0); } /* * tunpoll - the poll interface, this is only useful on reads * really. The write detect always returns true, write never blocks * anyway, it either accepts the packet or drops it. */ static int tunpoll(struct cdev *dev, int events, struct thread *td) { int s; struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = TUN2IFP(tp); int revents = 0; struct mbuf *m; s = splimp(); TUNDEBUG(ifp, "tunpoll\n"); if (events & (POLLIN | POLLRDNORM)) { IFQ_LOCK(&ifp->if_snd); IFQ_POLL_NOLOCK(&ifp->if_snd, m); if (m != NULL) { TUNDEBUG(ifp, "tunpoll q=%d\n", ifp->if_snd.ifq_len); revents |= events & (POLLIN | POLLRDNORM); } else { TUNDEBUG(ifp, "tunpoll waiting\n"); selrecord(td, &tp->tun_rsel); } IFQ_UNLOCK(&ifp->if_snd); } if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLOUT | POLLWRNORM); splx(s); return (revents); } /* * tunkqfilter - support for the kevent() system call. */ static int tunkqfilter(struct cdev *dev, struct knote *kn) { int s; struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = TUN2IFP(tp); s = splimp(); switch(kn->kn_filter) { case EVFILT_READ: TUNDEBUG(ifp, "%s kqfilter: EVFILT_READ, minor = %#x\n", ifp->if_xname, dev2unit(dev)); kn->kn_fop = &tun_read_filterops; break; case EVFILT_WRITE: TUNDEBUG(ifp, "%s kqfilter: EVFILT_WRITE, minor = %#x\n", ifp->if_xname, dev2unit(dev)); kn->kn_fop = &tun_write_filterops; break; default: TUNDEBUG(ifp, "%s kqfilter: invalid filter, minor = %#x\n", ifp->if_xname, dev2unit(dev)); splx(s); return(EINVAL); } splx(s); kn->kn_hook = (caddr_t) dev; knlist_add(&tp->tun_rsel.si_note, kn, 0); return (0); } /* * Return true of there is data in the interface queue. */ static int tunkqread(struct knote *kn, long hint) { int ret, s; struct cdev *dev = (struct cdev *)(kn->kn_hook); struct tun_softc *tp = dev->si_drv1; struct ifnet *ifp = TUN2IFP(tp); s = splimp(); if ((kn->kn_data = ifp->if_snd.ifq_len) > 0) { TUNDEBUG(ifp, "%s have data in the queue. Len = %d, minor = %#x\n", ifp->if_xname, ifp->if_snd.ifq_len, dev2unit(dev)); ret = 1; } else { TUNDEBUG(ifp, "%s waiting for data, minor = %#x\n", ifp->if_xname, dev2unit(dev)); ret = 0; } splx(s); return (ret); } /* * Always can write, always return MTU in kn->data. */ static int tunkqwrite(struct knote *kn, long hint) { int s; struct tun_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1; struct ifnet *ifp = TUN2IFP(tp); s = splimp(); kn->kn_data = ifp->if_mtu; splx(s); return (1); } static void tunkqdetach(struct knote *kn) { struct tun_softc *tp = ((struct cdev *)kn->kn_hook)->si_drv1; knlist_remove(&tp->tun_rsel.si_note, kn, 0); } Index: projects/cambria/sys/netinet/ipprotosw.h =================================================================== --- projects/cambria/sys/netinet/ipprotosw.h (revision 186459) +++ projects/cambria/sys/netinet/ipprotosw.h (nonexistent) @@ -1,98 +0,0 @@ -/*- - * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. - * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. - */ - -/*- - * Copyright (c) 1982, 1986, 1993 - * 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. - * 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. - * - * @(#)protosw.h 8.1 (Berkeley) 6/2/93 - * $FreeBSD$ - */ - -#ifndef _NETINET_IPPROTOSW_H_ -#define _NETINET_IPPROTOSW_H_ - -/* - * For pfil_head structure. - */ -#include - -/* Forward declare these structures referenced from prototypes below. */ -struct mbuf; -struct sockaddr; -struct socket; -struct sockopt; - -struct ipprotosw { - short pr_type; /* socket type used for */ - struct domain *pr_domain; /* domain protocol a member of */ - short pr_protocol; /* protocol number */ - short pr_flags; /* see below */ -/* protocol-protocol hooks */ - pr_in_input_t *pr_input; /* input to protocol (from below) */ - pr_output_t *pr_output; /* output to protocol (from above) */ - pr_ctlinput_t *pr_ctlinput; /* control input (from below) */ - pr_ctloutput_t *pr_ctloutput; /* control output (from above) */ -/* user-protocol hook */ - void *pr_ousrreq; -/* utility hooks */ - pr_init_t *pr_init; - pr_fasttimo_t *pr_fasttimo; /* fast timeout (200ms) */ - pr_slowtimo_t *pr_slowtimo; /* slow timeout (500ms) */ - pr_drain_t *pr_drain; /* flush any excess space possible */ - - struct pr_usrreqs *pr_usrreqs; /* supersedes pr_usrreq() */ - struct pfil_head pr_pfh; -}; - -#endif /* !_NETINET_IPPROTOSW_H_ */ Property changes on: projects/cambria/sys/netinet/ipprotosw.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: projects/cambria/sys/netinet/if_ether.c =================================================================== --- projects/cambria/sys/netinet/if_ether.c (revision 186459) +++ projects/cambria/sys/netinet/if_ether.c (revision 186460) @@ -1,804 +1,805 @@ /*- * Copyright (c) 1982, 1986, 1988, 1993 * 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. * 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_ether.c 8.1 (Berkeley) 6/10/93 */ /* * Ethernet address resolution protocol. * TODO: * add "inuse/lock" bit (or ref. count) along with valid bit */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_mac.h" #include "opt_carp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEV_CARP #include #endif #include #define SIN(s) ((struct sockaddr_in *)s) #define SDL(s) ((struct sockaddr_dl *)s) #define LLTABLE(ifp) ((struct lltable *)(ifp)->if_afdata[AF_INET]) SYSCTL_DECL(_net_link_ether); SYSCTL_NODE(_net_link_ether, PF_INET, inet, CTLFLAG_RW, 0, ""); /* timer values */ #ifdef VIMAGE_GLOBALS static int arpt_keep; /* once resolved, good for 20 more minutes */ static int arp_maxtries; int useloopback; /* use loopback interface for local traffic */ static int arp_proxyall; #endif SYSCTL_V_INT(V_NET, vnet_inet, _net_link_ether_inet, OID_AUTO, max_age, CTLFLAG_RW, arpt_keep, 0, "ARP entry lifetime in seconds"); static struct ifqueue arpintrq; SYSCTL_V_INT(V_NET, vnet_inet, _net_link_ether_inet, OID_AUTO, maxtries, CTLFLAG_RW, arp_maxtries, 0, "ARP resolution attempts before returning error"); SYSCTL_V_INT(V_NET, vnet_inet, _net_link_ether_inet, OID_AUTO, useloopback, CTLFLAG_RW, useloopback, 0, "Use the loopback interface for local traffic"); SYSCTL_V_INT(V_NET, vnet_inet, _net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW, arp_proxyall, 0, "Enable proxy ARP for all suitable requests"); static void arp_init(void); void arprequest(struct ifnet *, struct in_addr *, struct in_addr *, u_char *); static void arpintr(struct mbuf *); static void arptimer(void *); #ifdef INET static void in_arpinput(struct mbuf *); #endif #ifdef AF_INET void arp_ifscrub(struct ifnet *ifp, uint32_t addr); /* * called by in_ifscrub to remove entry from the table when * the interface goes away */ void arp_ifscrub(struct ifnet *ifp, uint32_t addr) { struct sockaddr_in addr4; bzero((void *)&addr4, sizeof(addr4)); addr4.sin_len = sizeof(addr4); addr4.sin_family = AF_INET; addr4.sin_addr.s_addr = addr; IF_AFDATA_LOCK(ifp); lla_lookup(LLTABLE(ifp), (LLE_DELETE | LLE_IFADDR), (struct sockaddr *)&addr4); IF_AFDATA_UNLOCK(ifp); } #endif /* * Timeout routine. Age arp_tab entries periodically. */ static void arptimer(void *arg) { struct ifnet *ifp; struct llentry *lle = (struct llentry *)arg; if (lle == NULL) { panic("%s: NULL entry!\n", __func__); return; } ifp = lle->lle_tbl->llt_ifp; IF_AFDATA_LOCK(ifp); LLE_WLOCK(lle); if ((lle->la_flags & LLE_DELETED) || (time_second >= lle->la_expire)) { if (!callout_pending(&lle->la_timer) && callout_active(&lle->la_timer)) (void) llentry_free(lle); } else { /* * Still valid, just drop our reference */ LLE_FREE_LOCKED(lle); } IF_AFDATA_UNLOCK(ifp); } /* * Broadcast an ARP request. Caller specifies: * - arp header source ip address * - arp header target ip address * - arp header source ethernet address */ void arprequest(struct ifnet *ifp, struct in_addr *sip, struct in_addr *tip, u_char *enaddr) { struct mbuf *m; struct arphdr *ah; struct sockaddr sa; if (sip == NULL) { /* XXX don't believe this can happen (or explain why) */ /* * The caller did not supply a source address, try to find * a compatible one among those assigned to this interface. */ struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET) continue; sip = &SIN(ifa->ifa_addr)->sin_addr; if (0 == ((sip->s_addr ^ tip->s_addr) & SIN(ifa->ifa_netmask)->sin_addr.s_addr) ) break; /* found it. */ } if (sip == NULL) { printf("%s: cannot find matching address\n", __func__); return; } } if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) return; m->m_len = sizeof(*ah) + 2*sizeof(struct in_addr) + 2*ifp->if_data.ifi_addrlen; m->m_pkthdr.len = m->m_len; MH_ALIGN(m, m->m_len); ah = mtod(m, struct arphdr *); bzero((caddr_t)ah, m->m_len); #ifdef MAC mac_netinet_arp_send(ifp, m); #endif ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = ifp->if_addrlen; /* hardware address length */ ah->ar_pln = sizeof(struct in_addr); /* protocol address length */ ah->ar_op = htons(ARPOP_REQUEST); bcopy((caddr_t)enaddr, (caddr_t)ar_sha(ah), ah->ar_hln); bcopy((caddr_t)sip, (caddr_t)ar_spa(ah), ah->ar_pln); bcopy((caddr_t)tip, (caddr_t)ar_tpa(ah), ah->ar_pln); sa.sa_family = AF_ARP; sa.sa_len = 2; m->m_flags |= M_BCAST; (*ifp->if_output)(ifp, m, &sa, (struct rtentry *)0); } /* * Resolve an IP address into an ethernet address. * On input: * ifp is the interface we use * rt0 is the route to the final destination (possibly useless) * m is the mbuf. May be NULL if we don't have a packet. * dst is the next hop, * desten is where we want the address. * * On success, desten is filled in and the function returns 0; * If the packet must be held pending resolution, we return EWOULDBLOCK * On other errors, we return the corresponding error code. * Note that m_freem() handles NULL. */ int arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, struct sockaddr *dst, u_char *desten, struct llentry **lle) { INIT_VNET_INET(ifp->if_vnet); struct llentry *la = 0; u_int flags = 0; int error, renew; *lle = NULL; if (m != NULL) { if (m->m_flags & M_BCAST) { /* broadcast */ (void)memcpy(desten, ifp->if_broadcastaddr, ifp->if_addrlen); return (0); } if (m->m_flags & M_MCAST && ifp->if_type != IFT_ARCNET) { /* multicast */ ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten); return (0); } } /* XXXXX */ retry: IF_AFDATA_RLOCK(ifp); la = lla_lookup(LLTABLE(ifp), flags, dst); IF_AFDATA_RUNLOCK(ifp); if ((la == NULL) && ((flags & LLE_EXCLUSIVE) == 0) && ((ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) == 0)) { flags |= (LLE_CREATE | LLE_EXCLUSIVE); IF_AFDATA_WLOCK(ifp); la = lla_lookup(LLTABLE(ifp), flags, dst); IF_AFDATA_WUNLOCK(ifp); } if (la == NULL) { if (flags & LLE_CREATE) log(LOG_DEBUG, "arpresolve: can't allocate llinfo for %s\n", inet_ntoa(SIN(dst)->sin_addr)); m_freem(m); return (EINVAL); } if ((la->la_flags & LLE_VALID) && ((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) { bcopy(&la->ll_addr, desten, ifp->if_addrlen); /* * If entry has an expiry time and it is approaching, * see if we need to send an ARP request within this * arpt_down interval. */ if (!(la->la_flags & LLE_STATIC) && time_uptime + la->la_preempt > la->la_expire) { arprequest(ifp, NULL, &SIN(dst)->sin_addr, IF_LLADDR(ifp)); la->la_preempt--; } *lle = la; error = 0; goto done; } if (la->la_flags & LLE_STATIC) { /* should not happen! */ log(LOG_DEBUG, "arpresolve: ouch, empty static llinfo for %s\n", inet_ntoa(SIN(dst)->sin_addr)); m_freem(m); error = EINVAL; goto done; } renew = (la->la_asked == 0 || la->la_expire != time_uptime); if ((renew || m != NULL) && (flags & LLE_EXCLUSIVE) == 0) { flags |= LLE_EXCLUSIVE; LLE_RUNLOCK(la); goto retry; } /* * There is an arptab entry, but no ethernet address * response yet. Replace the held mbuf with this * latest one. */ if (m != NULL) { if (la->la_hold != NULL) m_freem(la->la_hold); la->la_hold = m; if (renew == 0 && (flags & LLE_EXCLUSIVE)) { flags &= ~LLE_EXCLUSIVE; LLE_DOWNGRADE(la); } } /* * Return EWOULDBLOCK if we have tried less than arp_maxtries. It * will be masked by ether_output(). Return EHOSTDOWN/EHOSTUNREACH * if we have already sent arp_maxtries ARP requests. Retransmit the * ARP request, but not faster than one request per second. */ if (la->la_asked < V_arp_maxtries) error = EWOULDBLOCK; /* First request. */ else error = (rt0->rt_flags & RTF_GATEWAY) ? EHOSTDOWN : EHOSTUNREACH; if (renew) { LLE_ADDREF(la); la->la_expire = time_uptime; callout_reset(&la->la_timer, hz, arptimer, la); la->la_asked++; LLE_WUNLOCK(la); arprequest(ifp, NULL, &SIN(dst)->sin_addr, IF_LLADDR(ifp)); return (error); } done: if (flags & LLE_EXCLUSIVE) LLE_WUNLOCK(la); else LLE_RUNLOCK(la); return (error); } /* * Common length and type checks are done here, * then the protocol-specific routine is called. */ static void arpintr(struct mbuf *m) { struct arphdr *ar; if (m->m_len < sizeof(struct arphdr) && ((m = m_pullup(m, sizeof(struct arphdr))) == NULL)) { log(LOG_ERR, "arp: runt packet -- m_pullup failed\n"); return; } ar = mtod(m, struct arphdr *); if (ntohs(ar->ar_hrd) != ARPHRD_ETHER && ntohs(ar->ar_hrd) != ARPHRD_IEEE802 && ntohs(ar->ar_hrd) != ARPHRD_ARCNET && ntohs(ar->ar_hrd) != ARPHRD_IEEE1394) { log(LOG_ERR, "arp: unknown hardware address format (0x%2D)\n", (unsigned char *)&ar->ar_hrd, ""); m_freem(m); return; } if (m->m_len < arphdr_len(ar)) { if ((m = m_pullup(m, arphdr_len(ar))) == NULL) { log(LOG_ERR, "arp: runt packet\n"); m_freem(m); return; } ar = mtod(m, struct arphdr *); } switch (ntohs(ar->ar_pro)) { #ifdef INET case ETHERTYPE_IP: in_arpinput(m); return; #endif } m_freem(m); } #ifdef INET /* * ARP for Internet protocols on 10 Mb/s Ethernet. * Algorithm is that given in RFC 826. * In addition, a sanity check is performed on the sender * protocol address, to catch impersonators. * We no longer handle negotiations for use of trailer protocol: * Formerly, ARP replied for protocol type ETHERTYPE_TRAIL sent * along with IP replies if we wanted trailers sent to us, * and also sent them in response to IP replies. * This allowed either end to announce the desire to receive * trailer packets. * We no longer reply to requests for ETHERTYPE_TRAIL protocol either, * but formerly didn't normally send requests. */ static int log_arp_wrong_iface = 1; static int log_arp_movements = 1; static int log_arp_permanent_modify = 1; SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_wrong_iface, CTLFLAG_RW, &log_arp_wrong_iface, 0, "log arp packets arriving on the wrong interface"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_movements, CTLFLAG_RW, &log_arp_movements, 0, "log arp replies from MACs different than the one in the cache"); SYSCTL_INT(_net_link_ether_inet, OID_AUTO, log_arp_permanent_modify, CTLFLAG_RW, &log_arp_permanent_modify, 0, "log arp replies from MACs different than the one in the permanent arp entry"); static void in_arpinput(struct mbuf *m) { struct arphdr *ah; struct ifnet *ifp = m->m_pkthdr.rcvif; struct llentry *la = NULL; struct rtentry *rt; struct ifaddr *ifa; struct in_ifaddr *ia; struct sockaddr sa; struct in_addr isaddr, itaddr, myaddr; u_int8_t *enaddr = NULL; int op, flags; struct mbuf *m0; int req_len; int bridged = 0, is_bridge = 0; #ifdef DEV_CARP int carp_match = 0; #endif struct sockaddr_in sin; sin.sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; INIT_VNET_INET(ifp->if_vnet); if (ifp->if_bridge) bridged = 1; if (ifp->if_type == IFT_BRIDGE) is_bridge = 1; req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr)); if (m->m_len < req_len && (m = m_pullup(m, req_len)) == NULL) { log(LOG_ERR, "in_arp: runt packet -- m_pullup failed\n"); return; } ah = mtod(m, struct arphdr *); op = ntohs(ah->ar_op); (void)memcpy(&isaddr, ar_spa(ah), sizeof (isaddr)); (void)memcpy(&itaddr, ar_tpa(ah), sizeof (itaddr)); /* * For a bridge, we want to check the address irrespective * of the receive interface. (This will change slightly * when we have clusters of interfaces). * If the interface does not match, but the recieving interface * is part of carp, we call carp_iamatch to see if this is a * request for the virtual host ip. * XXX: This is really ugly! */ LIST_FOREACH(ia, INADDR_HASH(itaddr.s_addr), ia_hash) { if (((bridged && ia->ia_ifp->if_bridge != NULL) || ia->ia_ifp == ifp) && itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) goto match; #ifdef DEV_CARP if (ifp->if_carp != NULL && carp_iamatch(ifp->if_carp, ia, &isaddr, &enaddr) && itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) { carp_match = 1; goto match; } #endif } LIST_FOREACH(ia, INADDR_HASH(isaddr.s_addr), ia_hash) if (((bridged && ia->ia_ifp->if_bridge != NULL) || ia->ia_ifp == ifp) && isaddr.s_addr == ia->ia_addr.sin_addr.s_addr) goto match; #define BDG_MEMBER_MATCHES_ARP(addr, ifp, ia) \ (ia->ia_ifp->if_bridge == ifp->if_softc && \ !bcmp(IF_LLADDR(ia->ia_ifp), IF_LLADDR(ifp), ifp->if_addrlen) && \ addr == ia->ia_addr.sin_addr.s_addr) /* * Check the case when bridge shares its MAC address with * some of its children, so packets are claimed by bridge * itself (bridge_input() does it first), but they are really * meant to be destined to the bridge member. */ if (is_bridge) { LIST_FOREACH(ia, INADDR_HASH(itaddr.s_addr), ia_hash) { if (BDG_MEMBER_MATCHES_ARP(itaddr.s_addr, ifp, ia)) { ifp = ia->ia_ifp; goto match; } } } #undef BDG_MEMBER_MATCHES_ARP /* * No match, use the first inet address on the receive interface * as a dummy address for the rest of the function. */ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) if (ifa->ifa_addr->sa_family == AF_INET) { ia = ifatoia(ifa); goto match; } /* * If bridging, fall back to using any inet address. */ if (!bridged || (ia = TAILQ_FIRST(&V_in_ifaddrhead)) == NULL) goto drop; match: if (!enaddr) enaddr = (u_int8_t *)IF_LLADDR(ifp); myaddr = ia->ia_addr.sin_addr; if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) goto drop; /* it's from me, ignore it. */ if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) { log(LOG_ERR, "arp: link address is broadcast for IP address %s!\n", inet_ntoa(isaddr)); goto drop; } /* * Warn if another host is using the same IP address, but only if the * IP address isn't 0.0.0.0, which is used for DHCP only, in which * case we suppress the warning to avoid false positive complaints of * potential misconfiguration. */ if (!bridged && isaddr.s_addr == myaddr.s_addr && myaddr.s_addr != 0) { log(LOG_ERR, "arp: %*D is using my IP address %s on %s!\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa(isaddr), ifp->if_xname); itaddr = myaddr; goto reply; } if (ifp->if_flags & IFF_STATICARP) goto reply; bzero(&sin, sizeof(sin)); sin.sin_len = sizeof(struct sockaddr_in); sin.sin_family = AF_INET; sin.sin_addr = isaddr; flags = (itaddr.s_addr == myaddr.s_addr) ? LLE_CREATE : 0; flags |= LLE_EXCLUSIVE; IF_AFDATA_LOCK(ifp); la = lla_lookup(LLTABLE(ifp), flags, (struct sockaddr *)&sin); IF_AFDATA_UNLOCK(ifp); if (la != NULL) { /* the following is not an error when doing bridging */ if (!bridged && la->lle_tbl->llt_ifp != ifp #ifdef DEV_CARP && (ifp->if_type != IFT_CARP || !carp_match) #endif ) { if (log_arp_wrong_iface) log(LOG_ERR, "arp: %s is on %s " "but got reply from %*D on %s\n", inet_ntoa(isaddr), la->lle_tbl->llt_ifp->if_xname, ifp->if_addrlen, (u_char *)ar_sha(ah), ":", ifp->if_xname); goto reply; } if ((la->la_flags & LLE_VALID) && bcmp(ar_sha(ah), &la->ll_addr, ifp->if_addrlen)) { if (la->la_flags & LLE_STATIC) { log(LOG_ERR, "arp: %*D attempts to modify permanent " "entry for %s on %s\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa(isaddr), ifp->if_xname); goto reply; } if (log_arp_movements) { log(LOG_INFO, "arp: %s moved from %*D " "to %*D on %s\n", inet_ntoa(isaddr), ifp->if_addrlen, (u_char *)&la->ll_addr, ":", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", ifp->if_xname); } } if (ifp->if_addrlen != ah->ar_hln) { log(LOG_WARNING, "arp from %*D: addr len: new %d, i/f %d (ignored)", ifp->if_addrlen, (u_char *) ar_sha(ah), ":", ah->ar_hln, ifp->if_addrlen); goto reply; } (void)memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen); la->la_flags |= LLE_VALID; if (!(la->la_flags & LLE_STATIC)) { la->la_expire = time_uptime + V_arpt_keep; callout_reset(&la->la_timer, hz * V_arpt_keep, arptimer, la); } la->la_asked = 0; la->la_preempt = V_arp_maxtries; if (la->la_hold != NULL) { m0 = la->la_hold; la->la_hold = 0; memcpy(&sa, L3_ADDR(la), sizeof(sa)); LLE_WUNLOCK(la); (*ifp->if_output)(ifp, m0, &sa, NULL); return; } } reply: if (op != ARPOP_REQUEST) goto drop; if (itaddr.s_addr == myaddr.s_addr) { /* Shortcut.. the receiving interface is the target. */ (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); (void)memcpy(ar_sha(ah), enaddr, ah->ar_hln); } else { struct llentry *lle = NULL; if (!V_arp_proxyall) goto drop; sin.sin_addr = itaddr; /* XXX MRT use table 0 for arp reply */ rt = in_rtalloc1((struct sockaddr *)&sin, 0, 0UL, 0); if (!rt) goto drop; /* * Don't send proxies for nodes on the same interface * as this one came out of, or we'll get into a fight * over who claims what Ether address. */ if (!rt->rt_ifp || rt->rt_ifp == ifp) { RTFREE_LOCKED(rt); goto drop; } IF_AFDATA_LOCK(rt->rt_ifp); lle = lla_lookup(LLTABLE(rt->rt_ifp), 0, (struct sockaddr *)&sin); IF_AFDATA_UNLOCK(rt->rt_ifp); RTFREE_LOCKED(rt); if (lle != NULL) { (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); (void)memcpy(ar_sha(ah), &lle->ll_addr, ah->ar_hln); LLE_RUNLOCK(lle); } else goto drop; /* * Also check that the node which sent the ARP packet * is on the the interface we expect it to be on. This * avoids ARP chaos if an interface is connected to the * wrong network. */ sin.sin_addr = isaddr; /* XXX MRT use table 0 for arp checks */ rt = in_rtalloc1((struct sockaddr *)&sin, 0, 0UL, 0); if (!rt) goto drop; if (rt->rt_ifp != ifp) { log(LOG_INFO, "arp_proxy: ignoring request" " from %s via %s, expecting %s\n", inet_ntoa(isaddr), ifp->if_xname, rt->rt_ifp->if_xname); RTFREE_LOCKED(rt); goto drop; } RTFREE_LOCKED(rt); #ifdef DEBUG_PROXY printf("arp: proxying for %s\n", inet_ntoa(itaddr)); #endif } if (la != NULL) LLE_WUNLOCK(la); if (itaddr.s_addr == myaddr.s_addr && IN_LINKLOCAL(ntohl(itaddr.s_addr))) { /* RFC 3927 link-local IPv4; always reply by broadcast. */ #ifdef DEBUG_LINKLOCAL printf("arp: sending reply for link-local addr %s\n", inet_ntoa(itaddr)); #endif m->m_flags |= M_BCAST; m->m_flags &= ~M_MCAST; } else { /* default behaviour; never reply by broadcast. */ m->m_flags &= ~(M_BCAST|M_MCAST); } (void)memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); (void)memcpy(ar_spa(ah), &itaddr, ah->ar_pln); ah->ar_op = htons(ARPOP_REPLY); ah->ar_pro = htons(ETHERTYPE_IP); /* let's be sure! */ m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln); m->m_pkthdr.len = m->m_len; sa.sa_family = AF_ARP; sa.sa_len = 2; (*ifp->if_output)(ifp, m, &sa, (struct rtentry *)0); return; drop: if (la != NULL) LLE_WUNLOCK(la); m_freem(m); } #endif void arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa) { struct llentry *lle; - if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) != INADDR_ANY) + if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) != INADDR_ANY) { arprequest(ifp, &IA_SIN(ifa)->sin_addr, &IA_SIN(ifa)->sin_addr, IF_LLADDR(ifp)); - /* - * interface address is considered static entry - * because the output of the arp utility shows - * that L2 entry as permanent - */ - IF_AFDATA_LOCK(ifp); - lle = lla_lookup(LLTABLE(ifp), (LLE_CREATE | LLE_IFADDR | LLE_STATIC), - (struct sockaddr *)IA_SIN(ifa)); - IF_AFDATA_UNLOCK(ifp); - if (lle == NULL) - log(LOG_INFO, "arp_ifinit: cannot create arp " - "entry for interface address\n"); - else - LLE_RUNLOCK(lle); + /* + * interface address is considered static entry + * because the output of the arp utility shows + * that L2 entry as permanent + */ + IF_AFDATA_LOCK(ifp); + lle = lla_lookup(LLTABLE(ifp), (LLE_CREATE | LLE_IFADDR | LLE_STATIC), + (struct sockaddr *)IA_SIN(ifa)); + IF_AFDATA_UNLOCK(ifp); + if (lle == NULL) + log(LOG_INFO, "arp_ifinit: cannot create arp " + "entry for interface address\n"); + else + LLE_RUNLOCK(lle); + } ifa->ifa_rtrequest = NULL; } void arp_ifinit2(struct ifnet *ifp, struct ifaddr *ifa, u_char *enaddr) { if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) != INADDR_ANY) arprequest(ifp, &IA_SIN(ifa)->sin_addr, &IA_SIN(ifa)->sin_addr, enaddr); ifa->ifa_rtrequest = NULL; } static void arp_init(void) { INIT_VNET_INET(curvnet); V_arpt_keep = (20*60); /* once resolved, good for 20 more minutes */ V_arp_maxtries = 5; V_useloopback = 1; /* use loopback interface for local traffic */ V_arp_proxyall = 0; arpintrq.ifq_maxlen = 50; mtx_init(&arpintrq.ifq_mtx, "arp_inq", NULL, MTX_DEF); netisr_register(NETISR_ARP, arpintr, &arpintrq, 0); } SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); Index: projects/cambria/sys/netinet6/in6.c =================================================================== --- projects/cambria/sys/netinet6/in6.c (revision 186459) +++ projects/cambria/sys/netinet6/in6.c (revision 186460) @@ -1,2396 +1,2398 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: in6.c,v 1.259 2002/01/21 11:37:50 keiichi Exp $ */ /*- * Copyright (c) 1982, 1986, 1991, 1993 * 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. * 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. * * @(#)in.c 8.2 (Berkeley) 11/15/93 */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "internet multicast address"); /* * Definitions of some costant IP6 addresses. */ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; const struct in6_addr in6addr_nodelocal_allnodes = IN6ADDR_NODELOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allnodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; const struct in6_addr in6mask0 = IN6MASK0; const struct in6_addr in6mask32 = IN6MASK32; const struct in6_addr in6mask64 = IN6MASK64; const struct in6_addr in6mask96 = IN6MASK96; const struct in6_addr in6mask128 = IN6MASK128; const struct sockaddr_in6 sa6_any = { sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 }; static int in6_lifaddr_ioctl __P((struct socket *, u_long, caddr_t, struct ifnet *, struct thread *)); static int in6_ifinit __P((struct ifnet *, struct in6_ifaddr *, struct sockaddr_in6 *, int)); static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); struct in6_multihead in6_multihead; /* XXX BSS initialization */ int (*faithprefix_p)(struct in6_addr *); int in6_mask2len(struct in6_addr *mask, u_char *lim0) { int x = 0, y; u_char *lim = lim0, *p; /* ignore the scope_id part */ if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) lim = (u_char *)mask + sizeof(*mask); for (p = (u_char *)mask; p < lim; x++, p++) { if (*p != 0xff) break; } y = 0; if (p < lim) { for (y = 0; y < 8; y++) { if ((*p & (0x80 >> y)) == 0) break; } } /* * when the limit pointer is given, do a stricter check on the * remaining bits. */ if (p < lim) { if (y != 0 && (*p & (0x00ff >> y)) != 0) return (-1); for (p = p + 1; p < lim; p++) if (*p != 0) return (-1); } return x * 8 + y; } #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) #define ia62ifa(ia6) (&((ia6)->ia_ifa)) int in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { INIT_VNET_INET6(curvnet); struct in6_ifreq *ifr = (struct in6_ifreq *)data; struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct sockaddr_in6 *sa6; int error; switch (cmd) { case SIOCGETSGCNT_IN6: case SIOCGETMIFCNT_IN6: return (mrt6_ioctl ? mrt6_ioctl(cmd, data) : EOPNOTSUPP); } switch(cmd) { case SIOCAADDRCTL_POLICY: case SIOCDADDRCTL_POLICY: if (td != NULL) { error = priv_check(td, PRIV_NETINET_ADDRCTRL6); if (error) return (error); } return (in6_src_ioctl(cmd, data)); } if (ifp == NULL) return (EOPNOTSUPP); switch (cmd) { case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: case SIOCSDEFIFACE_IN6: case SIOCSIFINFO_FLAGS: if (td != NULL) { error = priv_check(td, PRIV_NETINET_ND6); if (error) return (error); } /* FALLTHROUGH */ case OSIOCGIFINFO_IN6: case SIOCGIFINFO_IN6: case SIOCSIFINFO_IN6: case SIOCGDRLST_IN6: case SIOCGPRLST_IN6: case SIOCGNBRINFO_IN6: case SIOCGDEFIFACE_IN6: return (nd6_ioctl(cmd, data, ifp)); } switch (cmd) { case SIOCSIFPREFIX_IN6: case SIOCDIFPREFIX_IN6: case SIOCAIFPREFIX_IN6: case SIOCCIFPREFIX_IN6: case SIOCSGIFPREFIX_IN6: case SIOCGIFPREFIX_IN6: log(LOG_NOTICE, "prefix ioctls are now invalidated. " "please use ifconfig.\n"); return (EOPNOTSUPP); } switch (cmd) { case SIOCSSCOPE6: if (td != NULL) { error = priv_check(td, PRIV_NETINET_SCOPE6); if (error) return (error); } return (scope6_set(ifp, (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); case SIOCGSCOPE6: return (scope6_get(ifp, (struct scope6_id *)ifr->ifr_ifru.ifru_scope_id)); case SIOCGSCOPE6DEF: return (scope6_get_default((struct scope6_id *) ifr->ifr_ifru.ifru_scope_id)); } switch (cmd) { case SIOCALIFADDR: if (td != NULL) { error = priv_check(td, PRIV_NET_ADDIFADDR); if (error) return (error); } return in6_lifaddr_ioctl(so, cmd, data, ifp, td); case SIOCDLIFADDR: if (td != NULL) { error = priv_check(td, PRIV_NET_DELIFADDR); if (error) return (error); } /* FALLTHROUGH */ case SIOCGLIFADDR: return in6_lifaddr_ioctl(so, cmd, data, ifp, td); } /* * Find address for this interface, if it exists. * * In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation * only, and used the first interface address as the target of other * operations (without checking ifra_addr). This was because netinet * code/API assumed at most 1 interface address per interface. * Since IPv6 allows a node to assign multiple addresses * on a single interface, we almost always look and check the * presence of ifra_addr, and reject invalid ones here. * It also decreases duplicated code among SIOC*_IN6 operations. */ switch (cmd) { case SIOCAIFADDR_IN6: case SIOCSIFPHYADDR_IN6: sa6 = &ifra->ifra_addr; break; case SIOCSIFADDR_IN6: case SIOCGIFADDR_IN6: case SIOCSIFDSTADDR_IN6: case SIOCSIFNETMASK_IN6: case SIOCGIFDSTADDR_IN6: case SIOCGIFNETMASK_IN6: case SIOCDIFADDR_IN6: case SIOCGIFPSRCADDR_IN6: case SIOCGIFPDSTADDR_IN6: case SIOCGIFAFLAG_IN6: case SIOCSNDFLUSH_IN6: case SIOCSPFXFLUSH_IN6: case SIOCSRTRFLUSH_IN6: case SIOCGIFALIFETIME_IN6: case SIOCSIFALIFETIME_IN6: case SIOCGIFSTAT_IN6: case SIOCGIFSTAT_ICMP6: sa6 = &ifr->ifr_addr; break; default: sa6 = NULL; break; } if (sa6 && sa6->sin6_family == AF_INET6) { int error = 0; if (sa6->sin6_scope_id != 0) error = sa6_embedscope(sa6, 0); else error = in6_setscope(&sa6->sin6_addr, ifp, NULL); if (error != 0) return (error); ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); } else ia = NULL; switch (cmd) { case SIOCSIFADDR_IN6: case SIOCSIFDSTADDR_IN6: case SIOCSIFNETMASK_IN6: /* * Since IPv6 allows a node to assign multiple addresses * on a single interface, SIOCSIFxxx ioctls are deprecated. */ /* we decided to obsolete this command (20000704) */ return (EINVAL); case SIOCDIFADDR_IN6: /* * for IPv4, we look for existing in_ifaddr here to allow * "ifconfig if0 delete" to remove the first IPv4 address on * the interface. For IPv6, as the spec allows multiple * interface address from the day one, we consider "remove the * first one" semantics to be not preferable. */ if (ia == NULL) return (EADDRNOTAVAIL); /* FALLTHROUGH */ case SIOCAIFADDR_IN6: /* * We always require users to specify a valid IPv6 address for * the corresponding operation. */ if (ifra->ifra_addr.sin6_family != AF_INET6 || ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) return (EAFNOSUPPORT); if (td != NULL) { error = priv_check(td, (cmd == SIOCDIFADDR_IN6) ? PRIV_NET_DELIFADDR : PRIV_NET_ADDIFADDR); if (error) return (error); } break; case SIOCGIFADDR_IN6: /* This interface is basically deprecated. use SIOCGIFCONF. */ /* FALLTHROUGH */ case SIOCGIFAFLAG_IN6: case SIOCGIFNETMASK_IN6: case SIOCGIFDSTADDR_IN6: case SIOCGIFALIFETIME_IN6: /* must think again about its semantics */ if (ia == NULL) return (EADDRNOTAVAIL); break; case SIOCSIFALIFETIME_IN6: { struct in6_addrlifetime *lt; if (td != NULL) { error = priv_check(td, PRIV_NETINET_ALIFETIME6); if (error) return (error); } if (ia == NULL) return (EADDRNOTAVAIL); /* sanity for overflow - beware unsigned */ lt = &ifr->ifr_ifru.ifru_lifetime; if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME && lt->ia6t_vltime + time_second < time_second) { return EINVAL; } if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME && lt->ia6t_pltime + time_second < time_second) { return EINVAL; } break; } } switch (cmd) { case SIOCGIFADDR_IN6: ifr->ifr_addr = ia->ia_addr; if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0) return (error); break; case SIOCGIFDSTADDR_IN6: if ((ifp->if_flags & IFF_POINTOPOINT) == 0) return (EINVAL); /* * XXX: should we check if ifa_dstaddr is NULL and return * an error? */ ifr->ifr_dstaddr = ia->ia_dstaddr; if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0) return (error); break; case SIOCGIFNETMASK_IN6: ifr->ifr_addr = ia->ia_prefixmask; break; case SIOCGIFAFLAG_IN6: ifr->ifr_ifru.ifru_flags6 = ia->ia6_flags; break; case SIOCGIFSTAT_IN6: if (ifp == NULL) return EINVAL; bzero(&ifr->ifr_ifru.ifru_stat, sizeof(ifr->ifr_ifru.ifru_stat)); ifr->ifr_ifru.ifru_stat = *((struct in6_ifextra *)ifp->if_afdata[AF_INET6])->in6_ifstat; break; case SIOCGIFSTAT_ICMP6: if (ifp == NULL) return EINVAL; bzero(&ifr->ifr_ifru.ifru_icmp6stat, sizeof(ifr->ifr_ifru.ifru_icmp6stat)); ifr->ifr_ifru.ifru_icmp6stat = *((struct in6_ifextra *)ifp->if_afdata[AF_INET6])->icmp6_ifstat; break; case SIOCGIFALIFETIME_IN6: ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { time_t maxexpire; struct in6_addrlifetime *retlt = &ifr->ifr_ifru.ifru_lifetime; /* * XXX: adjust expiration time assuming time_t is * signed. */ maxexpire = (-1) & ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1)); if (ia->ia6_lifetime.ia6t_vltime < maxexpire - ia->ia6_updatetime) { retlt->ia6t_expire = ia->ia6_updatetime + ia->ia6_lifetime.ia6t_vltime; } else retlt->ia6t_expire = maxexpire; } if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { time_t maxexpire; struct in6_addrlifetime *retlt = &ifr->ifr_ifru.ifru_lifetime; /* * XXX: adjust expiration time assuming time_t is * signed. */ maxexpire = (-1) & ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1)); if (ia->ia6_lifetime.ia6t_pltime < maxexpire - ia->ia6_updatetime) { retlt->ia6t_preferred = ia->ia6_updatetime + ia->ia6_lifetime.ia6t_pltime; } else retlt->ia6t_preferred = maxexpire; } break; case SIOCSIFALIFETIME_IN6: ia->ia6_lifetime = ifr->ifr_ifru.ifru_lifetime; /* for sanity */ if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_second + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_second + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; break; case SIOCAIFADDR_IN6: { int i, error = 0; struct nd_prefixctl pr0; struct nd_prefix *pr; /* * first, make or update the interface address structure, * and link it to the list. */ if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0) return (error); if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) == NULL) { /* * this can happen when the user specify the 0 valid * lifetime. */ break; } /* * then, make the prefix on-link on the interface. * XXX: we'd rather create the prefix before the address, but * we need at least one address to install the corresponding * interface route, so we configure the address first. */ /* * convert mask to prefix length (prefixmask has already * been validated in in6_update_ifa(). */ bzero(&pr0, sizeof(pr0)); pr0.ndpr_ifp = ifp; pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL); if (pr0.ndpr_plen == 128) { break; /* we don't need to install a host route. */ } pr0.ndpr_prefix = ifra->ifra_addr; /* apply the mask for safety. */ for (i = 0; i < 4; i++) { pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= ifra->ifra_prefixmask.sin6_addr.s6_addr32[i]; } /* * XXX: since we don't have an API to set prefix (not address) * lifetimes, we just use the same lifetimes as addresses. * The (temporarily) installed lifetimes can be overridden by * later advertised RAs (when accept_rtadv is non 0), which is * an intended behavior. */ pr0.ndpr_raf_onlink = 1; /* should be configurable? */ pr0.ndpr_raf_auto = ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; /* add the prefix if not yet. */ if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { /* * nd6_prelist_add will install the corresponding * interface route. */ if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) return (error); if (pr == NULL) { log(LOG_ERR, "nd6_prelist_add succeeded but " "no prefix\n"); return (EINVAL); /* XXX panic here? */ } } /* relate the address to the prefix */ if (ia->ia6_ndpr == NULL) { ia->ia6_ndpr = pr; pr->ndpr_refcnt++; /* * If this is the first autoconf address from the * prefix, create a temporary address as well * (when required). */ if ((ia->ia6_flags & IN6_IFF_AUTOCONF) && V_ip6_use_tempaddr && pr->ndpr_refcnt == 1) { int e; if ((e = in6_tmpifadd(ia, 1, 0)) != 0) { log(LOG_NOTICE, "in6_control: failed " "to create a temporary address, " "errno=%d\n", e); } } } /* * this might affect the status of autoconfigured addresses, * that is, this address might make other addresses detached. */ pfxlist_onlink_check(); if (error == 0 && ia) EVENTHANDLER_INVOKE(ifaddr_event, ifp); break; } case SIOCDIFADDR_IN6: { struct nd_prefix *pr; /* * If the address being deleted is the only one that owns * the corresponding prefix, expire the prefix as well. * XXX: theoretically, we don't have to worry about such * relationship, since we separate the address management * and the prefix management. We do this, however, to provide * as much backward compatibility as possible in terms of * the ioctl operation. * Note that in6_purgeaddr() will decrement ndpr_refcnt. */ pr = ia->ia6_ndpr; in6_purgeaddr(&ia->ia_ifa); if (pr && pr->ndpr_refcnt == 0) prelist_remove(pr); EVENTHANDLER_INVOKE(ifaddr_event, ifp); break; } default: if (ifp == NULL || ifp->if_ioctl == 0) return (EOPNOTSUPP); return ((*ifp->if_ioctl)(ifp, cmd, data)); } return (0); } /* * Update parameters of an IPv6 interface address. * If necessary, a new entry is created and linked into address chains. * This function is separated from in6_control(). * XXX: should this be performed under splnet()? */ int in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { INIT_VNET_INET6(ifp->if_vnet); INIT_VPROCG(TD_TO_VPROCG(curthread)); /* XXX V_hostname needs this */ int error = 0, hostIsNew = 0, plen = -1; struct in6_ifaddr *oia; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; struct in6_multi_mship *imm; struct in6_multi *in6m_sol; struct rtentry *rt; int delay; char ip6buf[INET6_ADDRSTRLEN]; /* Validate parameters */ if (ifp == NULL || ifra == NULL) /* this maybe redundant */ return (EINVAL); /* * The destination address for a p2p link must have a family * of AF_UNSPEC or AF_INET6. */ if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && ifra->ifra_dstaddr.sin6_family != AF_INET6 && ifra->ifra_dstaddr.sin6_family != AF_UNSPEC) return (EAFNOSUPPORT); /* * validate ifra_prefixmask. don't check sin6_family, netmask * does not carry fields other than sin6_len. */ if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6)) return (EINVAL); /* * Because the IPv6 address architecture is classless, we require * users to specify a (non 0) prefix length (mask) for a new address. * We also require the prefix (when specified) mask is valid, and thus * reject a non-consecutive mask. */ if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0) return (EINVAL); if (ifra->ifra_prefixmask.sin6_len != 0) { plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, (u_char *)&ifra->ifra_prefixmask + ifra->ifra_prefixmask.sin6_len); if (plen <= 0) return (EINVAL); } else { /* * In this case, ia must not be NULL. We just use its prefix * length. */ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); } /* * If the destination address on a p2p interface is specified, * and the address is a scoped one, validate/set the scope * zone identifier. */ dst6 = ifra->ifra_dstaddr; if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 && (dst6.sin6_family == AF_INET6)) { struct in6_addr in6_tmp; u_int32_t zoneid; in6_tmp = dst6.sin6_addr; if (in6_setscope(&in6_tmp, ifp, &zoneid)) return (EINVAL); /* XXX: should be impossible */ if (dst6.sin6_scope_id != 0) { if (dst6.sin6_scope_id != zoneid) return (EINVAL); } else /* user omit to specify the ID. */ dst6.sin6_scope_id = zoneid; /* convert into the internal form */ if (sa6_embedscope(&dst6, 0)) return (EINVAL); /* XXX: should be impossible */ } /* * The destination address can be specified only for a p2p or a * loopback interface. If specified, the corresponding prefix length * must be 128. */ if (ifra->ifra_dstaddr.sin6_family == AF_INET6) { if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { /* XXX: noisy message */ nd6log((LOG_INFO, "in6_update_ifa: a destination can " "be specified for a p2p or a loopback IF only\n")); return (EINVAL); } if (plen != 128) { nd6log((LOG_INFO, "in6_update_ifa: prefixlen should " "be 128 when dstaddr is specified\n")); return (EINVAL); } } /* lifetime consistency check */ lt = &ifra->ifra_lifetime; if (lt->ia6t_pltime > lt->ia6t_vltime) return (EINVAL); if (lt->ia6t_vltime == 0) { /* * the following log might be noisy, but this is a typical * configuration mistake or a tool's bug. */ nd6log((LOG_INFO, "in6_update_ifa: valid lifetime is 0 for %s\n", ip6_sprintf(ip6buf, &ifra->ifra_addr.sin6_addr))); if (ia == NULL) return (0); /* there's nothing to do */ } /* * If this is a new address, allocate a new ifaddr and link it * into chains. */ if (ia == NULL) { hostIsNew = 1; /* * When in6_update_ifa() is called in a process of a received * RA, it is called under an interrupt context. So, we should * call malloc with M_NOWAIT. */ ia = (struct in6_ifaddr *) malloc(sizeof(*ia), M_IFADDR, M_NOWAIT); if (ia == NULL) return (ENOBUFS); bzero((caddr_t)ia, sizeof(*ia)); LIST_INIT(&ia->ia6_memberships); /* Initialize the address and masks, and put time stamp */ IFA_LOCK_INIT(&ia->ia_ifa); ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; ia->ia_addr.sin6_family = AF_INET6; ia->ia_addr.sin6_len = sizeof(ia->ia_addr); ia->ia6_createtime = time_second; if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { /* * XXX: some functions expect that ifa_dstaddr is not * NULL for p2p interfaces. */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; } else { ia->ia_ifa.ifa_dstaddr = NULL; } ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; if ((oia = V_in6_ifaddr) != NULL) { for ( ; oia->ia_next; oia = oia->ia_next) continue; oia->ia_next = ia; } else V_in6_ifaddr = ia; ia->ia_ifa.ifa_refcnt = 1; TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); } /* update timestamp */ ia->ia6_updatetime = time_second; /* set prefix mask */ if (ifra->ifra_prefixmask.sin6_len) { /* * We prohibit changing the prefix length of an existing * address, because * + such an operation should be rare in IPv6, and * + the operation would confuse prefix management. */ if (ia->ia_prefixmask.sin6_len && in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { nd6log((LOG_INFO, "in6_update_ifa: the prefix length of an" " existing (%s) address should not be changed\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); error = EINVAL; goto unlink; } ia->ia_prefixmask = ifra->ifra_prefixmask; } /* * If a new destination address is specified, scrub the old one and * install the new destination. Note that the interface must be * p2p or loopback (see the check above.) */ if (dst6.sin6_family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia->ia_dstaddr.sin6_addr)) { int e; if ((ia->ia_flags & IFA_ROUTE) != 0 && (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) != 0) { nd6log((LOG_ERR, "in6_update_ifa: failed to remove " "a route to the old destination: %s\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); /* proceed anyway... */ } else ia->ia_flags &= ~IFA_ROUTE; ia->ia_dstaddr = dst6; } /* * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred * to see if the address is deprecated or invalidated, but initialize * these members for applications. */ ia->ia6_lifetime = ifra->ifra_lifetime; if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_second + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_second + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; /* reset the interface and routing table appropriately. */ if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) goto unlink; /* * configure address flags. */ ia->ia6_flags = ifra->ifra_flags; /* * backward compatibility - if IN6_IFF_DEPRECATED is set from the * userland, make it deprecated. */ if ((ifra->ifra_flags & IN6_IFF_DEPRECATED) != 0) { ia->ia6_lifetime.ia6t_pltime = 0; ia->ia6_lifetime.ia6t_preferred = time_second; } /* * Make the address tentative before joining multicast addresses, * so that corresponding MLD responses would not have a tentative * source address. */ ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */ if (hostIsNew && in6if_do_dad(ifp)) ia->ia6_flags |= IN6_IFF_TENTATIVE; /* * We are done if we have simply modified an existing address. */ if (!hostIsNew) return (error); /* * Beyond this point, we should call in6_purgeaddr upon an error, * not just go to unlink. */ /* Join necessary multicast groups */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { struct sockaddr_in6 mltaddr, mltmask; struct in6_addr llsol; /* join solicited multicast addr for new host id */ bzero(&llsol, sizeof(struct in6_addr)); llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; llsol.s6_addr32[1] = 0; llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; llsol.s6_addr8[12] = 0xff; if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) { /* XXX: should not happen */ log(LOG_ERR, "in6_update_ifa: " "in6_setscope failed\n"); goto cleanup; } delay = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * We need a random delay for DAD on the address * being configured. It also means delaying * transmission of the corresponding MLD report to * avoid report collision. * [draft-ietf-ipv6-rfc2462bis-02.txt] */ delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); } imm = in6_joingroup(ifp, &llsol, &error, delay); if (imm == NULL) { nd6log((LOG_WARNING, "in6_update_ifa: addmulti failed for " "%s on %s (errno=%d)\n", ip6_sprintf(ip6buf, &llsol), if_name(ifp), error)); in6_purgeaddr((struct ifaddr *)ia); return (error); } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); in6m_sol = imm->i6mm_maddr; bzero(&mltmask, sizeof(mltmask)); mltmask.sin6_len = sizeof(struct sockaddr_in6); mltmask.sin6_family = AF_INET6; mltmask.sin6_addr = in6mask32; #define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ /* * join link-local all-nodes address */ bzero(&mltaddr, sizeof(mltaddr)); mltaddr.sin6_len = sizeof(struct sockaddr_in6); mltaddr.sin6_family = AF_INET6; mltaddr.sin6_addr = in6addr_linklocal_allnodes; if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) goto cleanup; /* XXX: should not fail */ /* * XXX: do we really need this automatic routes? * We should probably reconsider this stuff. Most applications * actually do not need the routes, since they usually specify * the outgoing interface. */ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); if (rt) { /* XXX: only works in !SCOPEDROUTING case. */ if (memcmp(&mltaddr.sin6_addr, &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, MLTMASK_LEN)) { RTFREE_LOCKED(rt); rt = NULL; } } if (!rt) { error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, RTF_UP, (struct rtentry **)0); if (error) goto cleanup; } else { RTFREE_LOCKED(rt); } imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); if (!imm) { nd6log((LOG_WARNING, "in6_update_ifa: addmulti failed for " "%s on %s (errno=%d)\n", ip6_sprintf(ip6buf, &mltaddr.sin6_addr), if_name(ifp), error)); goto cleanup; } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); /* * join node information group address */ #define hostnamelen strlen(V_hostname) delay = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * The spec doesn't say anything about delay for this * group, but the same logic should apply. */ delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); } mtx_lock(&hostname_mtx); if (in6_nigroup(ifp, V_hostname, hostnamelen, &mltaddr.sin6_addr) == 0) { mtx_unlock(&hostname_mtx); imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, delay); /* XXX jinmei */ if (!imm) { nd6log((LOG_WARNING, "in6_update_ifa: " "addmulti failed for %s on %s " "(errno=%d)\n", ip6_sprintf(ip6buf, &mltaddr.sin6_addr), if_name(ifp), error)); /* XXX not very fatal, go on... */ } else { LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } } else mtx_unlock(&hostname_mtx); #undef hostnamelen /* * join interface-local all-nodes address. * (ff01::1%ifN, and ff01::%ifN/32) */ mltaddr.sin6_addr = in6addr_nodelocal_allnodes; if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) goto cleanup; /* XXX: should not fail */ /* XXX: again, do we really need the route? */ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); if (rt) { if (memcmp(&mltaddr.sin6_addr, &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, MLTMASK_LEN)) { RTFREE_LOCKED(rt); rt = NULL; } } if (!rt) { error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, RTF_UP, (struct rtentry **)0); if (error) goto cleanup; } else RTFREE_LOCKED(rt); imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); if (!imm) { nd6log((LOG_WARNING, "in6_update_ifa: " "addmulti failed for %s on %s " "(errno=%d)\n", ip6_sprintf(ip6buf, &mltaddr.sin6_addr), if_name(ifp), error)); goto cleanup; } LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); #undef MLTMASK_LEN } /* * Perform DAD, if needed. * XXX It may be of use, if we can administratively * disable DAD. */ if (hostIsNew && in6if_do_dad(ifp) && ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) && (ia->ia6_flags & IN6_IFF_TENTATIVE)) { int mindelay, maxdelay; delay = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * We need to impose a delay before sending an NS * for DAD. Check if we also needed a delay for the * corresponding MLD message. If we did, the delay * should be larger than the MLD delay (this could be * relaxed a bit, but this simple logic is at least * safe). */ mindelay = 0; if (in6m_sol != NULL && in6m_sol->in6m_state == MLD_REPORTPENDING) { mindelay = in6m_sol->in6m_timer; } maxdelay = MAX_RTR_SOLICITATION_DELAY * hz; if (maxdelay - mindelay == 0) delay = 0; else { delay = (arc4random() % (maxdelay - mindelay)) + mindelay; } } nd6_dad_start((struct ifaddr *)ia, delay); } return (error); unlink: /* * XXX: if a change of an existing address failed, keep the entry * anyway. */ if (hostIsNew) in6_unlink_ifa(ia, ifp); return (error); cleanup: in6_purgeaddr(&ia->ia_ifa); return error; } void in6_purgeaddr(struct ifaddr *ifa) { struct ifnet *ifp = ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; struct in6_multi_mship *imm; /* stop DAD processing */ nd6_dad_stop(ifa); IF_AFDATA_LOCK(ifp); lla_lookup(LLTABLE6(ifp), (LLE_DELETE | LLE_IFADDR), (struct sockaddr *)&ia->ia_addr); IF_AFDATA_UNLOCK(ifp); /* * leave from multicast groups we have joined for the interface */ while ((imm = ia->ia6_memberships.lh_first) != NULL) { LIST_REMOVE(imm, i6mm_chain); in6_leavegroup(imm); } in6_unlink_ifa(ia, ifp); } static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { INIT_VNET_INET6(ifp->if_vnet); struct in6_ifaddr *oia; int s = splnet(); TAILQ_REMOVE(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); oia = ia; if (oia == (ia = V_in6_ifaddr)) V_in6_ifaddr = ia->ia_next; else { while (ia->ia_next && (ia->ia_next != oia)) ia = ia->ia_next; if (ia->ia_next) ia->ia_next = oia->ia_next; else { /* search failed */ printf("Couldn't unlink in6_ifaddr from in6_ifaddr\n"); } } /* * Release the reference to the base prefix. There should be a * positive reference. */ if (oia->ia6_ndpr == NULL) { nd6log((LOG_NOTICE, "in6_unlink_ifa: autoconf'ed address " "%p has no prefix\n", oia)); } else { oia->ia6_ndpr->ndpr_refcnt--; oia->ia6_ndpr = NULL; } /* * Also, if the address being removed is autoconf'ed, call * pfxlist_onlink_check() since the release might affect the status of * other (detached) addresses. */ if ((oia->ia6_flags & IN6_IFF_AUTOCONF)) { pfxlist_onlink_check(); } /* * release another refcnt for the link from in6_ifaddr. * Note that we should decrement the refcnt at least once for all *BSD. */ IFAFREE(&oia->ia_ifa); splx(s); } void in6_purgeif(struct ifnet *ifp) { struct ifaddr *ifa, *nifa; for (ifa = TAILQ_FIRST(&ifp->if_addrlist); ifa != NULL; ifa = nifa) { nifa = TAILQ_NEXT(ifa, ifa_list); if (ifa->ifa_addr->sa_family != AF_INET6) continue; in6_purgeaddr(ifa); } in6_ifdetach(ifp); } /* * SIOC[GAD]LIFADDR. * SIOCGLIFADDR: get first address. (?) * SIOCGLIFADDR with IFLR_PREFIX: * get first address that matches the specified prefix. * SIOCALIFADDR: add the specified address. * SIOCALIFADDR with IFLR_PREFIX: * add the specified prefix, filling hostid part from * the first link-local address. prefixlen must be <= 64. * SIOCDLIFADDR: delete the specified address. * SIOCDLIFADDR with IFLR_PREFIX: * delete the first address that matches the specified prefix. * return values: * EINVAL on invalid parameters * EADDRNOTAVAIL on prefix match failed/specified address not found * other values may be returned from in6_ioctl() * * NOTE: SIOCALIFADDR(with IFLR_PREFIX set) allows prefixlen less than 64. * this is to accomodate address naming scheme other than RFC2374, * in the future. * RFC2373 defines interface id to be 64bit, but it allows non-RFC2374 * address encoding scheme. (see figure on page 8) */ static int in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) { struct if_laddrreq *iflr = (struct if_laddrreq *)data; struct ifaddr *ifa; struct sockaddr *sa; /* sanity checks */ if (!data || !ifp) { panic("invalid argument to in6_lifaddr_ioctl"); /* NOTREACHED */ } switch (cmd) { case SIOCGLIFADDR: /* address must be specified on GET with IFLR_PREFIX */ if ((iflr->flags & IFLR_PREFIX) == 0) break; /* FALLTHROUGH */ case SIOCALIFADDR: case SIOCDLIFADDR: /* address must be specified on ADD and DELETE */ sa = (struct sockaddr *)&iflr->addr; if (sa->sa_family != AF_INET6) return EINVAL; if (sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; /* XXX need improvement */ sa = (struct sockaddr *)&iflr->dstaddr; if (sa->sa_family && sa->sa_family != AF_INET6) return EINVAL; if (sa->sa_len && sa->sa_len != sizeof(struct sockaddr_in6)) return EINVAL; break; default: /* shouldn't happen */ #if 0 panic("invalid cmd to in6_lifaddr_ioctl"); /* NOTREACHED */ #else return EOPNOTSUPP; #endif } if (sizeof(struct in6_addr) * 8 < iflr->prefixlen) return EINVAL; switch (cmd) { case SIOCALIFADDR: { struct in6_aliasreq ifra; struct in6_addr *hostid = NULL; int prefixlen; if ((iflr->flags & IFLR_PREFIX) != 0) { struct sockaddr_in6 *sin6; /* * hostid is to fill in the hostid part of the * address. hostid points to the first link-local * address attached to the interface. */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); if (!ifa) return EADDRNOTAVAIL; hostid = IFA_IN6(ifa); /* prefixlen must be <= 64. */ if (64 < iflr->prefixlen) return EINVAL; prefixlen = iflr->prefixlen; /* hostid part must be zero. */ sin6 = (struct sockaddr_in6 *)&iflr->addr; if (sin6->sin6_addr.s6_addr32[2] != 0 || sin6->sin6_addr.s6_addr32[3] != 0) { return EINVAL; } } else prefixlen = iflr->prefixlen; /* copy args to in6_aliasreq, perform ioctl(SIOCAIFADDR_IN6). */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&iflr->addr, &ifra.ifra_addr, ((struct sockaddr *)&iflr->addr)->sa_len); if (hostid) { /* fill in hostid part */ ifra.ifra_addr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; ifra.ifra_addr.sin6_addr.s6_addr32[3] = hostid->s6_addr32[3]; } if (((struct sockaddr *)&iflr->dstaddr)->sa_family) { /* XXX */ bcopy(&iflr->dstaddr, &ifra.ifra_dstaddr, ((struct sockaddr *)&iflr->dstaddr)->sa_len); if (hostid) { ifra.ifra_dstaddr.sin6_addr.s6_addr32[2] = hostid->s6_addr32[2]; ifra.ifra_dstaddr.sin6_addr.s6_addr32[3] = hostid->s6_addr32[3]; } } ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); ifra.ifra_flags = iflr->flags & ~IFLR_PREFIX; return in6_control(so, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, td); } case SIOCGLIFADDR: case SIOCDLIFADDR: { struct in6_ifaddr *ia; struct in6_addr mask, candidate, match; struct sockaddr_in6 *sin6; int cmp; bzero(&mask, sizeof(mask)); if (iflr->flags & IFLR_PREFIX) { /* lookup a prefix rather than address. */ in6_prefixlen2mask(&mask, iflr->prefixlen); sin6 = (struct sockaddr_in6 *)&iflr->addr; bcopy(&sin6->sin6_addr, &match, sizeof(match)); match.s6_addr32[0] &= mask.s6_addr32[0]; match.s6_addr32[1] &= mask.s6_addr32[1]; match.s6_addr32[2] &= mask.s6_addr32[2]; match.s6_addr32[3] &= mask.s6_addr32[3]; /* if you set extra bits, that's wrong */ if (bcmp(&match, &sin6->sin6_addr, sizeof(match))) return EINVAL; cmp = 1; } else { if (cmd == SIOCGLIFADDR) { /* on getting an address, take the 1st match */ cmp = 0; /* XXX */ } else { /* on deleting an address, do exact match */ in6_prefixlen2mask(&mask, 128); sin6 = (struct sockaddr_in6 *)&iflr->addr; bcopy(&sin6->sin6_addr, &match, sizeof(match)); cmp = 1; } } TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (!cmp) break; /* * XXX: this is adhoc, but is necessary to allow * a user to specify fe80::/64 (not /10) for a * link-local address. */ bcopy(IFA_IN6(ifa), &candidate, sizeof(candidate)); in6_clearscope(&candidate); candidate.s6_addr32[0] &= mask.s6_addr32[0]; candidate.s6_addr32[1] &= mask.s6_addr32[1]; candidate.s6_addr32[2] &= mask.s6_addr32[2]; candidate.s6_addr32[3] &= mask.s6_addr32[3]; if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) break; } if (!ifa) return EADDRNOTAVAIL; ia = ifa2ia6(ifa); if (cmd == SIOCGLIFADDR) { int error; /* fill in the if_laddrreq structure */ bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); error = sa6_recoverscope( (struct sockaddr_in6 *)&iflr->addr); if (error != 0) return (error); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin6_len); error = sa6_recoverscope( (struct sockaddr_in6 *)&iflr->dstaddr); if (error != 0) return (error); } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); iflr->prefixlen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); iflr->flags = ia->ia6_flags; /* XXX */ return 0; } else { struct in6_aliasreq ifra; /* fill in6_aliasreq and do ioctl(SIOCDIFADDR_IN6) */ bzero(&ifra, sizeof(ifra)); bcopy(iflr->iflr_name, ifra.ifra_name, sizeof(ifra.ifra_name)); bcopy(&ia->ia_addr, &ifra.ifra_addr, ia->ia_addr.sin6_len); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, ia->ia_dstaddr.sin6_len); } else { bzero(&ifra.ifra_dstaddr, sizeof(ifra.ifra_dstaddr)); } bcopy(&ia->ia_prefixmask, &ifra.ifra_dstaddr, ia->ia_prefixmask.sin6_len); ifra.ifra_flags = ia->ia6_flags; return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, ifp, td); } } } return EOPNOTSUPP; /* just for safety */ } /* * Initialize an interface's intetnet6 address * and routing table entry. */ static int in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, struct sockaddr_in6 *sin6, int newhost) { int error = 0, plen, ifacount = 0; int s = splimp(); struct ifaddr *ifa; /* * Give the interface a chance to initialize * if this is its first address, * and to validate the address if necessary. */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifacount++; } ia->ia_addr = *sin6; if (ifacount <= 1 && ifp->if_ioctl) { IFF_LOCKGIANT(ifp); error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); IFF_UNLOCKGIANT(ifp); if (error) { splx(s); return (error); } } splx(s); ia->ia_ifa.ifa_metric = ifp->if_metric; /* we could do in(6)_socktrim here, but just omit it at this moment. */ /* * Special case: * If a new destination address is specified for a point-to-point * interface, install a route to the destination as an interface * direct route. * XXX: the logic below rejects assigning multiple addresses on a p2p * interface that share the same destination. */ #if 0 /* QL - verify */ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { int rtflags = RTF_UP | RTF_HOST; struct rtentry *rt = NULL, **rtp = NULL; if (nd6_need_cache(ifp) != 0) { rtp = &rt; } error = rtrequest(RTM_ADD, (struct sockaddr *)&ia->ia_dstaddr, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&ia->ia_prefixmask, ia->ia_flags | rtflags, rtp); if (error != 0) return (error); if (rt != NULL) { struct llinfo_nd6 *ln; RT_LOCK(rt); ln = (struct llinfo_nd6 *)rt->rt_llinfo; if (ln != NULL) { /* * Set the state to STALE because we don't * have to perform address resolution on this * link. */ ln->ln_state = ND6_LLINFO_STALE; } RT_REMREF(rt); RT_UNLOCK(rt); } ia->ia_flags |= IFA_ROUTE; } #else plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { if ((error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, RTF_UP | RTF_HOST)) != 0) return (error); ia->ia_flags |= IFA_ROUTE; } #endif /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */ if (newhost) { struct llentry *ln; IF_AFDATA_LOCK(ifp); ia->ia_ifa.ifa_rtrequest = NULL; /* XXX QL * we need to report rt_newaddrmsg */ ln = lla_lookup(LLTABLE6(ifp), (LLE_CREATE | LLE_IFADDR | LLE_EXCLUSIVE), (struct sockaddr *)&ia->ia_addr); IF_AFDATA_UNLOCK(ifp); if (ln != NULL) { ln->la_expire = 0; /* for IPv6 this means permanent */ ln->ln_state = ND6_LLINFO_REACHABLE; LLE_WUNLOCK(ln); } } return (error); } struct in6_multi_mship * in6_joingroup(struct ifnet *ifp, struct in6_addr *addr, int *errorp, int delay) { struct in6_multi_mship *imm; imm = malloc(sizeof(*imm), M_IP6MADDR, M_NOWAIT); if (!imm) { *errorp = ENOBUFS; return NULL; } imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, delay); if (!imm->i6mm_maddr) { /* *errorp is alrady set */ free(imm, M_IP6MADDR); return NULL; } return imm; } int in6_leavegroup(struct in6_multi_mship *imm) { if (imm->i6mm_maddr) in6_delmulti(imm->i6mm_maddr); free(imm, M_IP6MADDR); return 0; } /* * Find an IPv6 interface link-local address specific to an interface. */ struct in6_ifaddr * in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) { struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_IS_ADDR_LINKLOCAL(IFA_IN6(ifa))) { if ((((struct in6_ifaddr *)ifa)->ia6_flags & ignoreflags) != 0) continue; break; } } return ((struct in6_ifaddr *)ifa); } /* * find the internet address corresponding to a given interface and address. */ struct in6_ifaddr * in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr) { struct ifaddr *ifa; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) break; } return ((struct in6_ifaddr *)ifa); } /* * Convert IP6 address to printable (loggable) representation. Caller * has to make sure that ip6buf is at least INET6_ADDRSTRLEN long. */ static char digits[] = "0123456789abcdef"; char * ip6_sprintf(char *ip6buf, const struct in6_addr *addr) { int i; char *cp; const u_int16_t *a = (const u_int16_t *)addr; const u_int8_t *d; int dcolon = 0, zero = 0; cp = ip6buf; for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { if (i == 7) *cp++ = ':'; a++; continue; } else dcolon = 2; } if (*a == 0) { if (dcolon == 0 && *(a + 1) == 0) { if (i == 0) *cp++ = ':'; *cp++ = ':'; dcolon = 1; } else { *cp++ = '0'; *cp++ = ':'; } a++; continue; } d = (const u_char *)a; /* Try to eliminate leading zeros in printout like in :0001. */ zero = 1; *cp = digits[*d >> 4]; if (*cp != '0') { zero = 0; cp++; } *cp = digits[*d++ & 0xf]; if (zero == 0 || (*cp != '0')) { zero = 0; cp++; } *cp = digits[*d >> 4]; if (zero == 0 || (*cp != '0')) { zero = 0; cp++; } *cp++ = digits[*d & 0xf]; *cp++ = ':'; a++; } *--cp = '\0'; return (ip6buf); } int in6_localaddr(struct in6_addr *in6) { INIT_VNET_INET6(curvnet); struct in6_ifaddr *ia; if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) return 1; for (ia = V_in6_ifaddr; ia; ia = ia->ia_next) { if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, &ia->ia_prefixmask.sin6_addr)) { return 1; } } return (0); } int in6_is_addr_deprecated(struct sockaddr_in6 *sa6) { INIT_VNET_INET6(curvnet); struct in6_ifaddr *ia; for (ia = V_in6_ifaddr; ia; ia = ia->ia_next) { if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &sa6->sin6_addr) && (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) return (1); /* true */ /* XXX: do we still have to go thru the rest of the list? */ } return (0); /* false */ } /* * return length of part which dst and src are equal * hard coding... */ int in6_matchlen(struct in6_addr *src, struct in6_addr *dst) { int match = 0; u_char *s = (u_char *)src, *d = (u_char *)dst; u_char *lim = s + 16, r; while (s < lim) if ((r = (*d++ ^ *s++)) != 0) { while (r < 128) { match++; r <<= 1; } break; } else match += 8; return match; } /* XXX: to be scope conscious */ int in6_are_prefix_equal(struct in6_addr *p1, struct in6_addr *p2, int len) { int bytelen, bitlen; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_are_prefix_equal: invalid prefix length(%d)\n", len); return (0); } bytelen = len / 8; bitlen = len % 8; if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) return (0); if (bitlen != 0 && p1->s6_addr[bytelen] >> (8 - bitlen) != p2->s6_addr[bytelen] >> (8 - bitlen)) return (0); return (1); } void in6_prefixlen2mask(struct in6_addr *maskp, int len) { u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; int bytelen, bitlen, i; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "in6_prefixlen2mask: invalid prefix length(%d)\n", len); return; } bzero(maskp, sizeof(*maskp)); bytelen = len / 8; bitlen = len % 8; for (i = 0; i < bytelen; i++) maskp->s6_addr[i] = 0xff; if (bitlen) maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; } /* * return the best address out of the same scope. if no address was * found, return the first valid address from designated IF. */ struct in6_ifaddr * in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) { INIT_VNET_INET6(curvnet); int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; struct in6_ifaddr *besta = 0; struct in6_ifaddr *dep[2]; /* last-resort: deprecated */ dep[0] = dep[1] = NULL; /* * We first look for addresses in the same scope. * If there is one, return it. * If two or more, return one which matches the dst longest. * If none, return one of global addresses assigned other ifs. */ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (V_ip6_use_deprecated) dep[0] = (struct in6_ifaddr *)ifa; continue; } if (dst_scope == in6_addrscope(IFA_IN6(ifa))) { /* * call in6_matchlen() as few as possible */ if (besta) { if (blen == -1) blen = in6_matchlen(&besta->ia_addr.sin6_addr, dst); tlen = in6_matchlen(IFA_IN6(ifa), dst); if (tlen > blen) { blen = tlen; besta = (struct in6_ifaddr *)ifa; } } else besta = (struct in6_ifaddr *)ifa; } } if (besta) return (besta); TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST) continue; /* XXX: is there any case to allow anycast? */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_NOTREADY) continue; /* don't use this interface */ if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DETACHED) continue; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (V_ip6_use_deprecated) dep[1] = (struct in6_ifaddr *)ifa; continue; } return (struct in6_ifaddr *)ifa; } /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) return dep[0]; if (dep[1]) return dep[1]; return NULL; } /* * perform DAD when interface becomes IFF_UP. */ void in6_if_up(struct ifnet *ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; if (ia->ia6_flags & IN6_IFF_TENTATIVE) { /* * The TENTATIVE flag was likely set by hand * beforehand, implicitly indicating the need for DAD. * We may be able to skip the random delay in this * case, but we impose delays just in case. */ nd6_dad_start(ifa, arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); } } /* * special cases, like 6to4, are handled in in6_ifattach */ in6_ifattach(ifp, NULL); } int in6if_do_dad(struct ifnet *ifp) { if ((ifp->if_flags & IFF_LOOPBACK) != 0) return (0); switch (ifp->if_type) { #ifdef IFT_DUMMY case IFT_DUMMY: #endif case IFT_FAITH: /* * These interfaces do not have the IFF_LOOPBACK flag, * but loop packets back. We do not have to do DAD on such * interfaces. We should even omit it, because loop-backed * NS would confuse the DAD procedure. */ return (0); default: /* * Our DAD routine requires the interface up and running. * However, some interfaces can be up before the RUNNING * status. Additionaly, users may try to assign addresses * before the interface becomes up (or running). * We simply skip DAD in such a case as a work around. * XXX: we should rather mark "tentative" on such addresses, * and do DAD after the interface becomes ready. */ if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) return (0); return (1); } } /* * Calculate max IPv6 MTU through all the interfaces and store it * to in6_maxmtu. */ void in6_setmaxmtu(void) { INIT_VNET_NET(curvnet); INIT_VNET_INET6(curvnet); unsigned long maxmtu = 0; struct ifnet *ifp; IFNET_RLOCK(); for (ifp = TAILQ_FIRST(&V_ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) continue; if ((ifp->if_flags & IFF_LOOPBACK) == 0 && IN6_LINKMTU(ifp) > maxmtu) maxmtu = IN6_LINKMTU(ifp); } IFNET_RUNLOCK(); if (maxmtu) /* update only when maxmtu is positive */ V_in6_maxmtu = maxmtu; } /* * Provide the length of interface identifiers to be used for the link attached * to the given interface. The length should be defined in "IPv6 over * xxx-link" document. Note that address architecture might also define * the length for a particular set of address prefixes, regardless of the * link type. As clarified in rfc2462bis, those two definitions should be * consistent, and those really are as of August 2004. */ int in6_if2idlen(struct ifnet *ifp) { switch (ifp->if_type) { case IFT_ETHER: /* RFC2464 */ #ifdef IFT_PROPVIRTUAL case IFT_PROPVIRTUAL: /* XXX: no RFC. treat it as ether */ #endif #ifdef IFT_L2VLAN case IFT_L2VLAN: /* ditto */ #endif #ifdef IFT_IEEE80211 case IFT_IEEE80211: /* ditto */ #endif #ifdef IFT_MIP case IFT_MIP: /* ditto */ #endif return (64); case IFT_FDDI: /* RFC2467 */ return (64); case IFT_ISO88025: /* RFC2470 (IPv6 over Token Ring) */ return (64); case IFT_PPP: /* RFC2472 */ return (64); case IFT_ARCNET: /* RFC2497 */ return (64); case IFT_FRELAY: /* RFC2590 */ return (64); case IFT_IEEE1394: /* RFC3146 */ return (64); case IFT_GIF: return (64); /* draft-ietf-v6ops-mech-v2-07 */ case IFT_LOOP: return (64); /* XXX: is this really correct? */ default: /* * Unknown link type: * It might be controversial to use the today's common constant * of 64 for these cases unconditionally. For full compliance, * we should return an error in this case. On the other hand, * if we simply miss the standard for the link type or a new * standard is defined for a new link type, the IFID length * is very likely to be the common constant. As a compromise, * we always use the constant, but make an explicit notice * indicating the "unknown" case. */ printf("in6_if2idlen: unknown link type (%d)\n", ifp->if_type); return (64); } } #include struct in6_llentry { struct llentry base; struct sockaddr_in6 l3_addr6; }; static struct llentry * in6_lltable_new(const struct sockaddr *l3addr, u_int flags) { struct in6_llentry *lle; lle = malloc(sizeof(struct in6_llentry), M_LLTABLE, M_DONTWAIT | M_ZERO); if (lle == NULL) /* NB: caller generates msg */ return NULL; callout_init(&lle->base.ln_timer_ch, CALLOUT_MPSAFE); lle->l3_addr6 = *(const struct sockaddr_in6 *)l3addr; lle->base.lle_refcnt = 1; LLE_LOCK_INIT(&lle->base); return &lle->base; } /* * Deletes an address from the address table. * This function is called by the timer functions * such as arptimer() and nd6_llinfo_timer(), and * the caller does the locking. */ static void in6_lltable_free(struct lltable *llt, struct llentry *lle) { LLE_WUNLOCK(lle); LLE_LOCK_DESTROY(lle); free(lle, M_LLTABLE); } static int in6_lltable_rtcheck(struct ifnet *ifp, const struct sockaddr *l3addr) { struct rtentry *rt; char ip6buf[INET6_ADDRSTRLEN]; KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); /* XXX rtalloc1 should take a const param */ rt = rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0); if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) { struct ifaddr *ifa; /* * Create an ND6 cache for an IPv6 neighbor * that is not covered by our own prefix. */ /* XXX ifaof_ifpforaddr should take a const param */ ifa = ifaof_ifpforaddr(__DECONST(struct sockaddr *, l3addr), ifp); if (ifa != NULL) { if (rt != NULL) rtfree(rt); return 0; } log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n", ip6_sprintf(ip6buf, &((const struct sockaddr_in6 *)l3addr)->sin6_addr)); if (rt != NULL) rtfree(rt); return EINVAL; } rtfree(rt); return 0; } static struct llentry * in6_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3addr) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)l3addr; struct ifnet *ifp = llt->llt_ifp; struct llentry *lle; struct llentries *lleh; u_int hashkey; IF_AFDATA_LOCK_ASSERT(ifp); KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); hashkey = sin6->sin6_addr.s6_addr32[3]; lleh = &llt->lle_head[LLATBL_HASH(hashkey, LLTBL_HASHMASK)]; LIST_FOREACH(lle, lleh, lle_next) { if (lle->la_flags & LLE_DELETED) continue; if (bcmp(L3_ADDR(lle), l3addr, l3addr->sa_len) == 0) break; } if (lle == NULL) { if (!(flags & LLE_CREATE)) return (NULL); /* * A route that covers the given address must have * been installed 1st because we are doing a resolution, * verify this. */ if (!(flags & LLE_IFADDR) && in6_lltable_rtcheck(ifp, l3addr) != 0) return NULL; lle = in6_lltable_new(l3addr, flags); if (lle == NULL) { log(LOG_INFO, "lla_lookup: new lle malloc failed\n"); return NULL; } lle->la_flags = flags & ~LLE_CREATE; if ((flags & (LLE_CREATE | LLE_IFADDR)) == (LLE_CREATE | LLE_IFADDR)) { bcopy(IF_LLADDR(ifp), &lle->ll_addr, ifp->if_addrlen); lle->la_flags |= (LLE_VALID | LLE_STATIC); } lle->lle_tbl = llt; lle->lle_head = lleh; LIST_INSERT_HEAD(lleh, lle, lle_next); } else if (flags & LLE_DELETE) { - LLE_WLOCK(lle); - lle->la_flags = LLE_DELETED; - LLE_WUNLOCK(lle); + if (!(lle->la_flags & LLE_IFADDR) || (flags & LLE_IFADDR)) { + LLE_WLOCK(lle); + lle->la_flags = LLE_DELETED; + LLE_WUNLOCK(lle); #ifdef DIAGNOSTICS - log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); + log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif + } lle = (void *)-1; } if (LLE_IS_VALID(lle)) { if (flags & LLE_EXCLUSIVE) LLE_WLOCK(lle); else LLE_RLOCK(lle); } return (lle); } static int in6_lltable_dump(struct lltable *llt, struct sysctl_req *wr) { struct ifnet *ifp = llt->llt_ifp; struct llentry *lle; /* XXX stack use */ struct { struct rt_msghdr rtm; struct sockaddr_in6 sin6; /* * ndp.c assumes that sdl is word aligned */ #ifdef __LP64__ uint32_t pad; #endif struct sockaddr_dl sdl; } ndpc; int i, error; /* XXXXX * current IFNET_RLOCK() is mapped to IFNET_WLOCK() * so it is okay to use this ASSERT, change it when * IFNET lock is finalized */ IFNET_WLOCK_ASSERT(); error = 0; for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) { LIST_FOREACH(lle, &llt->lle_head[i], lle_next) { struct sockaddr_dl *sdl; /* skip deleted or invalid entries */ if ((lle->la_flags & (LLE_DELETED|LLE_VALID)) != LLE_VALID) continue; /* * produce a msg made of: * struct rt_msghdr; * struct sockaddr_in6 (IPv6) * struct sockaddr_dl; */ bzero(&ndpc, sizeof(ndpc)); ndpc.rtm.rtm_msglen = sizeof(ndpc); ndpc.sin6.sin6_family = AF_INET6; ndpc.sin6.sin6_len = sizeof(ndpc.sin6); bcopy(L3_ADDR(lle), &ndpc.sin6, L3_ADDR_LEN(lle)); /* publish */ if (lle->la_flags & LLE_PUB) ndpc.rtm.rtm_flags |= RTF_ANNOUNCE; sdl = &ndpc.sdl; sdl->sdl_family = AF_LINK; sdl->sdl_len = sizeof(*sdl); sdl->sdl_alen = ifp->if_addrlen; sdl->sdl_index = ifp->if_index; sdl->sdl_type = ifp->if_type; bcopy(&lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); ndpc.rtm.rtm_rmx.rmx_expire = lle->la_flags & LLE_STATIC ? 0 : lle->la_expire; ndpc.rtm.rtm_flags |= RTF_HOST; if (lle->la_flags & LLE_STATIC) ndpc.rtm.rtm_flags |= RTF_STATIC; ndpc.rtm.rtm_index = ifp->if_index; error = SYSCTL_OUT(wr, &ndpc, sizeof(ndpc)); if (error) break; } } return error; } void * in6_domifattach(struct ifnet *ifp) { struct in6_ifextra *ext; ext = (struct in6_ifextra *)malloc(sizeof(*ext), M_IFADDR, M_WAITOK); bzero(ext, sizeof(*ext)); ext->in6_ifstat = (struct in6_ifstat *)malloc(sizeof(struct in6_ifstat), M_IFADDR, M_WAITOK); bzero(ext->in6_ifstat, sizeof(*ext->in6_ifstat)); ext->icmp6_ifstat = (struct icmp6_ifstat *)malloc(sizeof(struct icmp6_ifstat), M_IFADDR, M_WAITOK); bzero(ext->icmp6_ifstat, sizeof(*ext->icmp6_ifstat)); ext->nd_ifinfo = nd6_ifattach(ifp); ext->scope6_id = scope6_ifattach(ifp); ext->lltable = lltable_init(ifp, AF_INET6); if (ext->lltable != NULL) { ext->lltable->llt_new = in6_lltable_new; ext->lltable->llt_free = in6_lltable_free; ext->lltable->llt_rtcheck = in6_lltable_rtcheck; ext->lltable->llt_lookup = in6_lltable_lookup; ext->lltable->llt_dump = in6_lltable_dump; } return ext; } void in6_domifdetach(struct ifnet *ifp, void *aux) { struct in6_ifextra *ext = (struct in6_ifextra *)aux; scope6_ifdetach(ext->scope6_id); nd6_ifdetach(ext->nd_ifinfo); lltable_free(ext->lltable); free(ext->in6_ifstat, M_IFADDR); free(ext->icmp6_ifstat, M_IFADDR); free(ext, M_IFADDR); } /* * Convert sockaddr_in6 to sockaddr_in. Original sockaddr_in6 must be * v4 mapped addr or v4 compat addr */ void in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin, sizeof(*sin)); sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = sin6->sin6_port; sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; } /* Convert sockaddr_in to sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { bzero(sin6, sizeof(*sin6)); sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_port = sin->sin_port; sin6->sin6_addr.s6_addr32[0] = 0; sin6->sin6_addr.s6_addr32[1] = 0; sin6->sin6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; } /* Convert sockaddr_in6 into sockaddr_in. */ void in6_sin6_2_sin_in_sock(struct sockaddr *nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 sin6; /* * Save original sockaddr_in6 addr and convert it * to sockaddr_in. */ sin6 = *(struct sockaddr_in6 *)nam; sin_p = (struct sockaddr_in *)nam; in6_sin6_2_sin(sin_p, &sin6); } /* Convert sockaddr_in into sockaddr_in6 in v4 mapped addr format. */ void in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) { struct sockaddr_in *sin_p; struct sockaddr_in6 *sin6_p; sin6_p = malloc(sizeof *sin6_p, M_SONAME, M_WAITOK); sin_p = (struct sockaddr_in *)*nam; in6_sin_2_v4mapsin6(sin_p, sin6_p); free(*nam, M_SONAME); *nam = (struct sockaddr *)sin6_p; } Index: projects/cambria/sys/netinet6/ip6_input.c =================================================================== --- projects/cambria/sys/netinet6/ip6_input.c (revision 186459) +++ projects/cambria/sys/netinet6/ip6_input.c (revision 186460) @@ -1,1703 +1,1703 @@ /*- * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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. Neither the name of the project 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 PROJECT 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 PROJECT 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. * * $KAME: ip6_input.c,v 1.259 2002/01/21 04:58:09 jinmei Exp $ */ /*- * Copyright (c) 1982, 1986, 1988, 1993 * 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. * 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. * * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ipsec.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #endif /* INET */ #include #include #include #include #include #include #include #include #include #ifdef IPSEC #include #include #include #endif /* IPSEC */ #include extern struct domain inet6domain; u_char ip6_protox[IPPROTO_MAX]; static struct ifqueue ip6intrq; #ifndef VIMAGE #ifndef VIMAGE_GLOBALS struct vnet_inet6 vnet_inet6_0; #endif #endif #ifdef VIMAGE_GLOBALS static int ip6qmaxlen; struct in6_ifaddr *in6_ifaddr; struct ip6stat ip6stat; extern struct callout in6_tmpaddrtimer_ch; extern int dad_init; extern int pmtu_expire; extern int pmtu_probe; extern u_long rip6_sendspace; extern u_long rip6_recvspace; extern int icmp6errppslim; extern int icmp6_nodeinfo; extern int udp6_sendspace; extern int udp6_recvspace; extern struct route_in6 ip6_forward_rt; int ip6_forward_srcrt; /* XXX */ int ip6_sourcecheck; /* XXX */ int ip6_sourcecheck_interval; /* XXX */ int ip6_ours_check_algorithm; #endif struct pfil_head inet6_pfil_hook; static void ip6_init2(void *); static struct ip6aux *ip6_setdstifaddr(struct mbuf *, struct in6_ifaddr *); static int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *); #ifdef PULLDOWN_TEST static struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int); #endif /* * IP6 initialization: fill in IP6 protocol switch table. * All protocols not implemented in kernel go to raw IP6 protocol handler. */ void ip6_init(void) { INIT_VNET_INET6(curvnet); struct ip6protosw *pr; int i; V_ip6qmaxlen = IFQ_MAXLEN; V_in6_maxmtu = 0; #ifdef IP6_AUTO_LINKLOCAL V_ip6_auto_linklocal = IP6_AUTO_LINKLOCAL; #else V_ip6_auto_linklocal = 1; /* enable by default */ #endif TUNABLE_INT_FETCH("net.inet6.ip6.auto_linklocal", &V_ip6_auto_linklocal); #ifndef IPV6FORWARDING #ifdef GATEWAY6 #define IPV6FORWARDING 1 /* forward IP6 packets not for us */ #else #define IPV6FORWARDING 0 /* don't forward IP6 packets not for us */ #endif /* GATEWAY6 */ #endif /* !IPV6FORWARDING */ #ifndef IPV6_SENDREDIRECTS #define IPV6_SENDREDIRECTS 1 #endif V_ip6_forwarding = IPV6FORWARDING; /* act as router? */ V_ip6_sendredirects = IPV6_SENDREDIRECTS; V_ip6_defhlim = IPV6_DEFHLIM; V_ip6_defmcasthlim = IPV6_DEFAULT_MULTICAST_HOPS; V_ip6_accept_rtadv = 0; /* "IPV6FORWARDING ? 0 : 1" is dangerous */ V_ip6_log_interval = 5; V_ip6_hdrnestlimit = 15; /* How many header options will we process? */ V_ip6_dad_count = 1; /* DupAddrDetectionTransmits */ V_ip6_auto_flowlabel = 1; V_ip6_use_deprecated = 1;/* allow deprecated addr (RFC2462 5.5.4) */ V_ip6_rr_prune = 5; /* router renumbering prefix * walk list every 5 sec. */ V_ip6_mcast_pmtu = 0; /* enable pMTU discovery for multicast? */ V_ip6_v6only = 1; V_ip6_keepfaith = 0; V_ip6_log_time = (time_t)0L; #ifdef IPSTEALTH V_ip6stealth = 0; #endif V_nd6_onlink_ns_rfc4861 = 0; /* allow 'on-link' nd6 NS (RFC 4861) */ V_pmtu_expire = 60*10; V_pmtu_probe = 60*2; /* raw IP6 parameters */ /* * Nominal space allocated to a raw ip socket. */ #define RIPV6SNDQ 8192 #define RIPV6RCVQ 8192 V_rip6_sendspace = RIPV6SNDQ; V_rip6_recvspace = RIPV6RCVQ; /* ICMPV6 parameters */ V_icmp6_rediraccept = 1; /* accept and process redirects */ V_icmp6_redirtimeout = 10 * 60; /* 10 minutes */ V_icmp6errppslim = 100; /* 100pps */ /* control how to respond to NI queries */ V_icmp6_nodeinfo = (ICMP6_NODEINFO_FQDNOK|ICMP6_NODEINFO_NODEADDROK); /* UDP on IP6 parameters */ V_udp6_sendspace = 9216; /* really max datagram size */ V_udp6_recvspace = 40 * (1024 + sizeof(struct sockaddr_in6)); /* 40 1K datagrams */ V_dad_init = 0; #ifdef DIAGNOSTIC if (sizeof(struct protosw) != sizeof(struct ip6protosw)) panic("sizeof(protosw) != sizeof(ip6protosw)"); #endif pr = (struct ip6protosw *)pffindproto(PF_INET6, IPPROTO_RAW, SOCK_RAW); if (pr == 0) panic("ip6_init"); - /* Initialize the entire ip_protox[] array to IPPROTO_RAW. */ + /* Initialize the entire ip6_protox[] array to IPPROTO_RAW. */ for (i = 0; i < IPPROTO_MAX; i++) ip6_protox[i] = pr - inet6sw; /* * Cycle through IP protocols and put them into the appropriate place * in ip6_protox[]. */ for (pr = (struct ip6protosw *)inet6domain.dom_protosw; pr < (struct ip6protosw *)inet6domain.dom_protoswNPROTOSW; pr++) if (pr->pr_domain->dom_family == PF_INET6 && pr->pr_protocol && pr->pr_protocol != IPPROTO_RAW) { /* Be careful to only index valid IP protocols. */ if (pr->pr_protocol < IPPROTO_MAX) ip6_protox[pr->pr_protocol] = pr - inet6sw; } /* Initialize packet filter hooks. */ inet6_pfil_hook.ph_type = PFIL_TYPE_AF; inet6_pfil_hook.ph_af = AF_INET6; if ((i = pfil_head_register(&inet6_pfil_hook)) != 0) printf("%s: WARNING: unable to register pfil hook, " "error %d\n", __func__, i); ip6intrq.ifq_maxlen = V_ip6qmaxlen; mtx_init(&ip6intrq.ifq_mtx, "ip6_inq", NULL, MTX_DEF); netisr_register(NETISR_IPV6, ip6_input, &ip6intrq, 0); scope6_init(); addrsel_policy_init(); nd6_init(); frag6_init(); V_ip6_desync_factor = arc4random() % MAX_TEMP_DESYNC_FACTOR; } static void ip6_init2(void *dummy) { INIT_VNET_INET6(curvnet); /* nd6_timer_init */ callout_init(&V_nd6_timer_ch, 0); callout_reset(&V_nd6_timer_ch, hz, nd6_timer, NULL); /* timer for regeneranation of temporary addresses randomize ID */ callout_init(&V_in6_tmpaddrtimer_ch, 0); callout_reset(&V_in6_tmpaddrtimer_ch, (V_ip6_temp_preferred_lifetime - V_ip6_desync_factor - V_ip6_temp_regen_advance) * hz, in6_tmpaddrtimer, NULL); } /* cheat */ /* This must be after route_init(), which is now SI_ORDER_THIRD */ SYSINIT(netinet6init2, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ip6_init2, NULL); void ip6_input(struct mbuf *m) { INIT_VNET_NET(curvnet); INIT_VNET_INET6(curvnet); struct ip6_hdr *ip6; int off = sizeof(struct ip6_hdr), nest; u_int32_t plen; u_int32_t rtalert = ~0; int nxt, ours = 0; struct ifnet *deliverifp = NULL, *ifp = NULL; struct in6_addr odst; int srcrt = 0; struct llentry *lle = NULL; struct sockaddr_in6 dst6; #ifdef IPSEC /* * should the inner packet be considered authentic? * see comment in ah4_input(). * NB: m cannot be NULL when passed to the input routine */ m->m_flags &= ~M_AUTHIPHDR; m->m_flags &= ~M_AUTHIPDGM; #endif /* IPSEC */ /* * make sure we don't have onion peering information into m_tag. */ ip6_delaux(m); /* * mbuf statistics */ if (m->m_flags & M_EXT) { if (m->m_next) V_ip6stat.ip6s_mext2m++; else V_ip6stat.ip6s_mext1++; } else { #define M2MMAX (sizeof(V_ip6stat.ip6s_m2m)/sizeof(V_ip6stat.ip6s_m2m[0])) if (m->m_next) { if (m->m_flags & M_LOOP) { V_ip6stat.ip6s_m2m[V_loif[0].if_index]++; /* XXX */ } else if (m->m_pkthdr.rcvif->if_index < M2MMAX) V_ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; else V_ip6stat.ip6s_m2m[0]++; } else V_ip6stat.ip6s_m1++; #undef M2MMAX } /* drop the packet if IPv6 operation is disabled on the IF */ if ((ND_IFINFO(m->m_pkthdr.rcvif)->flags & ND6_IFF_IFDISABLED)) { m_freem(m); return; } in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_receive); V_ip6stat.ip6s_total++; #ifndef PULLDOWN_TEST /* * L2 bridge code and some other code can return mbuf chain * that does not conform to KAME requirement. too bad. * XXX: fails to join if interface MTU > MCLBYTES. jumbogram? */ if (m && m->m_next != NULL && m->m_pkthdr.len < MCLBYTES) { struct mbuf *n; MGETHDR(n, M_DONTWAIT, MT_HEADER); if (n) M_MOVE_PKTHDR(n, m); if (n && n->m_pkthdr.len > MHLEN) { MCLGET(n, M_DONTWAIT); if ((n->m_flags & M_EXT) == 0) { m_freem(n); n = NULL; } } if (n == NULL) { m_freem(m); return; /* ENOBUFS */ } m_copydata(m, 0, n->m_pkthdr.len, mtod(n, caddr_t)); n->m_len = n->m_pkthdr.len; m_freem(m); m = n; } IP6_EXTHDR_CHECK(m, 0, sizeof(struct ip6_hdr), /* nothing */); #endif if (m->m_len < sizeof(struct ip6_hdr)) { struct ifnet *inifp; inifp = m->m_pkthdr.rcvif; if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { V_ip6stat.ip6s_toosmall++; in6_ifstat_inc(inifp, ifs6_in_hdrerr); return; } } ip6 = mtod(m, struct ip6_hdr *); if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { V_ip6stat.ip6s_badvers++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); goto bad; } V_ip6stat.ip6s_nxthist[ip6->ip6_nxt]++; /* * Check against address spoofing/corruption. */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst)) { /* * XXX: "badscope" is not very suitable for a multicast source. */ V_ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) && !(m->m_flags & M_LOOP)) { /* * In this case, the packet should come from the loopback * interface. However, we cannot just check the if_flags, * because ip6_mloopback() passes the "actual" interface * as the outgoing/incoming interface. */ V_ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } #ifdef ALTQ if (altq_input != NULL && (*altq_input)(m, AF_INET6) == 0) { /* packet is dropped by traffic conditioner */ return; } #endif /* * The following check is not documented in specs. A malicious * party may be able to use IPv4 mapped addr to confuse tcp/udp stack * and bypass security checks (act as if it was from 127.0.0.1 by using * IPv6 src ::ffff:127.0.0.1). Be cautious. * * This check chokes if we are in an SIIT cloud. As none of BSDs * support IPv4-less kernel compilation, we cannot support SIIT * environment at all. So, it makes more sense for us to reject any * malicious packets for non-SIIT environment, than try to do a * partial support for SIIT environment. */ if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { V_ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } #if 0 /* * Reject packets with IPv4 compatible addresses (auto tunnel). * * The code forbids auto tunnel relay case in RFC1933 (the check is * stronger than RFC1933). We may want to re-enable it if mech-xx * is revised to forbid relaying case. */ if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) || IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { V_ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } #endif /* * Run through list of hooks for input packets. * * NB: Beware of the destination address changing * (e.g. by NAT rewriting). When this happens, * tell ip6_forward to do the right thing. */ odst = ip6->ip6_dst; /* Jump over all PFIL processing if hooks are not active. */ if (!PFIL_HOOKED(&inet6_pfil_hook)) goto passin; if (pfil_run_hooks(&inet6_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, NULL)) return; if (m == NULL) /* consumed by filter */ return; ip6 = mtod(m, struct ip6_hdr *); srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); passin: /* * Disambiguate address scope zones (if there is ambiguity). * We first make sure that the original source or destination address * is not in our internal form for scoped addresses. Such addresses * are not necessarily invalid spec-wise, but we cannot accept them due * to the usage conflict. * in6_setscope() then also checks and rejects the cases where src or * dst are the loopback address and the receiving interface * is not loopback. */ if (in6_clearscope(&ip6->ip6_src) || in6_clearscope(&ip6->ip6_dst)) { V_ip6stat.ip6s_badscope++; /* XXX */ goto bad; } if (in6_setscope(&ip6->ip6_src, m->m_pkthdr.rcvif, NULL) || in6_setscope(&ip6->ip6_dst, m->m_pkthdr.rcvif, NULL)) { V_ip6stat.ip6s_badscope++; goto bad; } /* * Multicast check */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct in6_multi *in6m = 0; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_mcast); /* * See if we belong to the destination multicast group on the * arrival interface. */ IN6_LOOKUP_MULTI(ip6->ip6_dst, m->m_pkthdr.rcvif, in6m); if (in6m) ours = 1; else if (!ip6_mrouter) { V_ip6stat.ip6s_notmember++; V_ip6stat.ip6s_cantforward++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); goto bad; } deliverifp = m->m_pkthdr.rcvif; goto hbhcheck; } /* * Unicast check */ bzero(&dst6, sizeof(dst6)); dst6.sin6_family = AF_INET6; dst6.sin6_len = sizeof(struct sockaddr_in6); dst6.sin6_addr = ip6->ip6_dst; ifp = m->m_pkthdr.rcvif; IF_AFDATA_LOCK(ifp); lle = lla_lookup(LLTABLE6(ifp), 0, (struct sockaddr *)&dst6); IF_AFDATA_UNLOCK(ifp); if ((lle != NULL) && (lle->la_flags & LLE_IFADDR)) { ours = 1; deliverifp = ifp; LLE_RUNLOCK(lle); goto hbhcheck; } if (lle != NULL) LLE_RUNLOCK(lle); if (V_ip6_forward_rt.ro_rt != NULL && (V_ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &((struct sockaddr_in6 *)(&V_ip6_forward_rt.ro_dst))->sin6_addr)) V_ip6stat.ip6s_forward_cachehit++; else { struct sockaddr_in6 *dst6; if (V_ip6_forward_rt.ro_rt) { /* route is down or destination is different */ V_ip6stat.ip6s_forward_cachemiss++; RTFREE(V_ip6_forward_rt.ro_rt); V_ip6_forward_rt.ro_rt = 0; } bzero(&V_ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); dst6 = (struct sockaddr_in6 *)&V_ip6_forward_rt.ro_dst; dst6->sin6_len = sizeof(struct sockaddr_in6); dst6->sin6_family = AF_INET6; dst6->sin6_addr = ip6->ip6_dst; rtalloc((struct route *)&V_ip6_forward_rt); } #define rt6_key(r) ((struct sockaddr_in6 *)((r)->rt_nodes->rn_key)) /* * Accept the packet if the forwarding interface to the destination * according to the routing table is the loopback interface, * unless the associated route has a gateway. * Note that this approach causes to accept a packet if there is a * route to the loopback interface for the destination of the packet. * But we think it's even useful in some situations, e.g. when using * a special daemon which wants to intercept the packet. * * XXX: some OSes automatically make a cloned route for the destination * of an outgoing packet. If the outgoing interface of the packet * is a loopback one, the kernel would consider the packet to be * accepted, even if we have no such address assinged on the interface. * We check the cloned flag of the route entry to reject such cases, * assuming that route entries for our own addresses are not made by * cloning (it should be true because in6_addloop explicitly installs * the host route). However, we might have to do an explicit check * while it would be less efficient. Or, should we rather install a * reject route for such a case? */ if (V_ip6_forward_rt.ro_rt && (V_ip6_forward_rt.ro_rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST && #ifdef RTF_WASCLONED !(V_ip6_forward_rt.ro_rt->rt_flags & RTF_WASCLONED) && #endif #ifdef RTF_CLONED !(V_ip6_forward_rt.ro_rt->rt_flags & RTF_CLONED) && #endif #if 0 /* * The check below is redundant since the comparison of * the destination and the key of the rtentry has * already done through looking up the routing table. */ IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &rt6_key(V_ip6_forward_rt.ro_rt)->sin6_addr) #endif V_ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { struct in6_ifaddr *ia6 = (struct in6_ifaddr *)V_ip6_forward_rt.ro_rt->rt_ifa; /* * record address information into m_tag. */ (void)ip6_setdstifaddr(m, ia6); /* * packets to a tentative, duplicated, or somehow invalid * address must not be accepted. */ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) { /* this address is ready */ ours = 1; deliverifp = ia6->ia_ifp; /* correct? */ /* Count the packet in the ip address stats */ ia6->ia_ifa.if_ipackets++; ia6->ia_ifa.if_ibytes += m->m_pkthdr.len; goto hbhcheck; } else { char ip6bufs[INET6_ADDRSTRLEN]; char ip6bufd[INET6_ADDRSTRLEN]; /* address is not ready, so discard the packet. */ nd6log((LOG_INFO, "ip6_input: packet to an unready address %s->%s\n", ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst))); goto bad; } } /* * FAITH (Firewall Aided Internet Translator) */ if (V_ip6_keepfaith) { if (V_ip6_forward_rt.ro_rt && V_ip6_forward_rt.ro_rt->rt_ifp && V_ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_FAITH) { /* XXX do we need more sanity checks? */ ours = 1; deliverifp = V_ip6_forward_rt.ro_rt->rt_ifp; /* faith */ goto hbhcheck; } } /* * Now there is no reason to process the packet if it's not our own * and we're not a router. */ if (!V_ip6_forwarding) { V_ip6stat.ip6s_cantforward++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); goto bad; } hbhcheck: /* * record address information into m_tag, if we don't have one yet. * note that we are unable to record it, if the address is not listed * as our interface address (e.g. multicast addresses, addresses * within FAITH prefixes and such). */ if (deliverifp && !ip6_getdstifaddr(m)) { struct in6_ifaddr *ia6; ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); if (ia6) { if (!ip6_setdstifaddr(m, ia6)) { /* * XXX maybe we should drop the packet here, * as we could not provide enough information * to the upper layers. */ } } } /* * Process Hop-by-Hop options header if it's contained. * m may be modified in ip6_hopopts_input(). * If a JumboPayload option is included, plen will also be modified. */ plen = (u_int32_t)ntohs(ip6->ip6_plen); if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { struct ip6_hbh *hbh; if (ip6_hopopts_input(&plen, &rtalert, &m, &off)) { #if 0 /*touches NULL pointer*/ in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); #endif return; /* m have already been freed */ } /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); /* * if the payload length field is 0 and the next header field * indicates Hop-by-Hop Options header, then a Jumbo Payload * option MUST be included. */ if (ip6->ip6_plen == 0 && plen == 0) { /* * Note that if a valid jumbo payload option is * contained, ip6_hopopts_input() must set a valid * (non-zero) payload length to the variable plen. */ V_ip6stat.ip6s_badoptions++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); return; } #ifndef PULLDOWN_TEST /* ip6_hopopts_input() ensures that mbuf is contiguous */ hbh = (struct ip6_hbh *)(ip6 + 1); #else IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); if (hbh == NULL) { V_ip6stat.ip6s_tooshort++; return; } #endif nxt = hbh->ip6h_nxt; /* * If we are acting as a router and the packet contains a * router alert option, see if we know the option value. * Currently, we only support the option value for MLD, in which * case we should pass the packet to the multicast routing * daemon. */ if (rtalert != ~0 && V_ip6_forwarding) { switch (rtalert) { case IP6OPT_RTALERT_MLD: ours = 1; break; default: /* * RFC2711 requires unrecognized values must be * silently ignored. */ break; } } } else nxt = ip6->ip6_nxt; /* * Check that the amount of data in the buffers * is as at least much as the IPv6 header would have us expect. * Trim mbufs if longer than we expect. * Drop packet if shorter than we expect. */ if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { V_ip6stat.ip6s_tooshort++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); goto bad; } if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { if (m->m_len == m->m_pkthdr.len) { m->m_len = sizeof(struct ip6_hdr) + plen; m->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; } else m_adj(m, sizeof(struct ip6_hdr) + plen - m->m_pkthdr.len); } /* * Forward if desirable. */ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { /* * If we are acting as a multicast router, all * incoming multicast packets are passed to the * kernel-level multicast forwarding function. * The packet is returned (relatively) intact; if * ip6_mforward() returns a non-zero value, the packet * must be discarded, else it may be accepted below. */ if (ip6_mrouter && ip6_mforward && ip6_mforward(ip6, m->m_pkthdr.rcvif, m)) { V_ip6stat.ip6s_cantforward++; m_freem(m); return; } if (!ours) { m_freem(m); return; } } else if (!ours) { ip6_forward(m, srcrt); return; } ip6 = mtod(m, struct ip6_hdr *); /* * Malicious party may be able to use IPv4 mapped addr to confuse * tcp/udp stack and bypass security checks (act as if it was from * 127.0.0.1 by using IPv6 src ::ffff:127.0.0.1). Be cautious. * * For SIIT end node behavior, you may want to disable the check. * However, you will become vulnerable to attacks using IPv4 mapped * source. */ if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) || IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) { V_ip6stat.ip6s_badscope++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr); goto bad; } /* * Tell launch routine the next header */ V_ip6stat.ip6s_delivered++; in6_ifstat_inc(deliverifp, ifs6_in_deliver); nest = 0; while (nxt != IPPROTO_DONE) { if (V_ip6_hdrnestlimit && (++nest > V_ip6_hdrnestlimit)) { V_ip6stat.ip6s_toomanyhdr++; goto bad; } /* * protection against faulty packet - there should be * more sanity checks in header chain processing. */ if (m->m_pkthdr.len < off) { V_ip6stat.ip6s_tooshort++; in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated); goto bad; } #ifdef IPSEC /* * enforce IPsec policy checking if we are seeing last header. * note that we do not visit this with protocols with pcb layer * code - like udp/tcp/raw ip. */ if (ip6_ipsec_input(m, nxt)) goto bad; #endif /* IPSEC */ nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } return; bad: m_freem(m); } /* * set/grab in6_ifaddr correspond to IPv6 destination address. * XXX backward compatibility wrapper */ static struct ip6aux * ip6_setdstifaddr(struct mbuf *m, struct in6_ifaddr *ia6) { struct ip6aux *ip6a; ip6a = ip6_addaux(m); if (ip6a) ip6a->ip6a_dstia6 = ia6; return ip6a; /* NULL if failed to set */ } struct in6_ifaddr * ip6_getdstifaddr(struct mbuf *m) { struct ip6aux *ip6a; ip6a = ip6_findaux(m); if (ip6a) return ip6a->ip6a_dstia6; else return NULL; } /* * Hop-by-Hop options header processing. If a valid jumbo payload option is * included, the real payload length will be stored in plenp. * * rtalertp - XXX: should be stored more smart way */ static int ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp, struct mbuf **mp, int *offp) { INIT_VNET_INET6(curvnet); struct mbuf *m = *mp; int off = *offp, hbhlen; struct ip6_hbh *hbh; u_int8_t *opt; /* validation of the length of the header */ #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, sizeof(*hbh), -1); hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); hbhlen = (hbh->ip6h_len + 1) << 3; IP6_EXTHDR_CHECK(m, off, hbhlen, -1); hbh = (struct ip6_hbh *)(mtod(m, caddr_t) + off); #else IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); if (hbh == NULL) { V_ip6stat.ip6s_tooshort++; return -1; } hbhlen = (hbh->ip6h_len + 1) << 3; IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), hbhlen); if (hbh == NULL) { V_ip6stat.ip6s_tooshort++; return -1; } #endif off += hbhlen; hbhlen -= sizeof(struct ip6_hbh); opt = (u_int8_t *)hbh + sizeof(struct ip6_hbh); if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh), hbhlen, rtalertp, plenp) < 0) return (-1); *offp = off; *mp = m; return (0); } /* * Search header for all Hop-by-hop options and process each option. * This function is separate from ip6_hopopts_input() in order to * handle a case where the sending node itself process its hop-by-hop * options header. In such a case, the function is called from ip6_output(). * * The function assumes that hbh header is located right after the IPv6 header * (RFC2460 p7), opthead is pointer into data content in m, and opthead to * opthead + hbhlen is located in continuous memory region. */ int ip6_process_hopopts(struct mbuf *m, u_int8_t *opthead, int hbhlen, u_int32_t *rtalertp, u_int32_t *plenp) { INIT_VNET_INET6(curvnet); struct ip6_hdr *ip6; int optlen = 0; u_int8_t *opt = opthead; u_int16_t rtalert_val; u_int32_t jumboplen; const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { switch (*opt) { case IP6OPT_PAD1: optlen = 1; break; case IP6OPT_PADN: if (hbhlen < IP6OPT_MINLEN) { V_ip6stat.ip6s_toosmall++; goto bad; } optlen = *(opt + 1) + 2; break; case IP6OPT_ROUTER_ALERT: /* XXX may need check for alignment */ if (hbhlen < IP6OPT_RTALERT_LEN) { V_ip6stat.ip6s_toosmall++; goto bad; } if (*(opt + 1) != IP6OPT_RTALERT_LEN - 2) { /* XXX stat */ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, erroff + opt + 1 - opthead); return (-1); } optlen = IP6OPT_RTALERT_LEN; bcopy((caddr_t)(opt + 2), (caddr_t)&rtalert_val, 2); *rtalertp = ntohs(rtalert_val); break; case IP6OPT_JUMBO: /* XXX may need check for alignment */ if (hbhlen < IP6OPT_JUMBO_LEN) { V_ip6stat.ip6s_toosmall++; goto bad; } if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) { /* XXX stat */ icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, erroff + opt + 1 - opthead); return (-1); } optlen = IP6OPT_JUMBO_LEN; /* * IPv6 packets that have non 0 payload length * must not contain a jumbo payload option. */ ip6 = mtod(m, struct ip6_hdr *); if (ip6->ip6_plen) { V_ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, erroff + opt - opthead); return (-1); } /* * We may see jumbolen in unaligned location, so * we'd need to perform bcopy(). */ bcopy(opt + 2, &jumboplen, sizeof(jumboplen)); jumboplen = (u_int32_t)htonl(jumboplen); #if 1 /* * if there are multiple jumbo payload options, * *plenp will be non-zero and the packet will be * rejected. * the behavior may need some debate in ipngwg - * multiple options does not make sense, however, * there's no explicit mention in specification. */ if (*plenp != 0) { V_ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, erroff + opt + 2 - opthead); return (-1); } #endif /* * jumbo payload length must be larger than 65535. */ if (jumboplen <= IPV6_MAXPACKET) { V_ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, erroff + opt + 2 - opthead); return (-1); } *plenp = jumboplen; break; default: /* unknown option */ if (hbhlen < IP6OPT_MINLEN) { V_ip6stat.ip6s_toosmall++; goto bad; } optlen = ip6_unknown_opt(opt, m, erroff + opt - opthead); if (optlen == -1) return (-1); optlen += 2; break; } } return (0); bad: m_freem(m); return (-1); } /* * Unknown option processing. * The third argument `off' is the offset from the IPv6 header to the option, * which is necessary if the IPv6 header the and option header and IPv6 header * is not continuous in order to return an ICMPv6 error. */ int ip6_unknown_opt(u_int8_t *optp, struct mbuf *m, int off) { INIT_VNET_INET6(curvnet); struct ip6_hdr *ip6; switch (IP6OPT_TYPE(*optp)) { case IP6OPT_TYPE_SKIP: /* ignore the option */ return ((int)*(optp + 1)); case IP6OPT_TYPE_DISCARD: /* silently discard */ m_freem(m); return (-1); case IP6OPT_TYPE_FORCEICMP: /* send ICMP even if multicasted */ V_ip6stat.ip6s_badoptions++; icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); return (-1); case IP6OPT_TYPE_ICMP: /* send ICMP if not multicasted */ V_ip6stat.ip6s_badoptions++; ip6 = mtod(m, struct ip6_hdr *); if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || (m->m_flags & (M_BCAST|M_MCAST))) m_freem(m); else icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, off); return (-1); } m_freem(m); /* XXX: NOTREACHED */ return (-1); } /* * Create the "control" list for this pcb. * These functions will not modify mbuf chain at all. * * With KAME mbuf chain restriction: * The routine will be called from upper layer handlers like tcp6_input(). * Thus the routine assumes that the caller (tcp6_input) have already * called IP6_EXTHDR_CHECK() and all the extension headers are located in the * very first mbuf on the mbuf chain. * * ip6_savecontrol_v4 will handle those options that are possible to be * set on a v4-mapped socket. * ip6_savecontrol will directly call ip6_savecontrol_v4 to handle those * options and handle the v6-only ones itself. */ struct mbuf ** ip6_savecontrol_v4(struct inpcb *inp, struct mbuf *m, struct mbuf **mp, int *v4only) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); #ifdef SO_TIMESTAMP if ((inp->inp_socket->so_options & SO_TIMESTAMP) != 0) { struct timeval tv; microtime(&tv); *mp = sbcreatecontrol((caddr_t) &tv, sizeof(tv), SCM_TIMESTAMP, SOL_SOCKET); if (*mp) mp = &(*mp)->m_next; } #endif if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { if (v4only != NULL) *v4only = 1; return (mp); } #define IS2292(inp, x, y) (((inp)->inp_flags & IN6P_RFC2292) ? (x) : (y)) /* RFC 2292 sec. 5 */ if ((inp->inp_flags & IN6P_PKTINFO) != 0) { struct in6_pktinfo pi6; bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); in6_clearscope(&pi6.ipi6_addr); /* XXX */ pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif) ? m->m_pkthdr.rcvif->if_index : 0; *mp = sbcreatecontrol((caddr_t) &pi6, sizeof(struct in6_pktinfo), IS2292(inp, IPV6_2292PKTINFO, IPV6_PKTINFO), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } if ((inp->inp_flags & IN6P_HOPLIMIT) != 0) { int hlim = ip6->ip6_hlim & 0xff; *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), IS2292(inp, IPV6_2292HOPLIMIT, IPV6_HOPLIMIT), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } if (v4only != NULL) *v4only = 0; return (mp); } void ip6_savecontrol(struct inpcb *in6p, struct mbuf *m, struct mbuf **mp) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); int v4only = 0; mp = ip6_savecontrol_v4(in6p, m, mp, &v4only); if (v4only) return; if ((in6p->inp_flags & IN6P_TCLASS) != 0) { u_int32_t flowinfo; int tclass; flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK); flowinfo >>= 20; tclass = flowinfo & 0xff; *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass), IPV6_TCLASS, IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; } /* * IPV6_HOPOPTS socket option. Recall that we required super-user * privilege for the option (see ip6_ctloutput), but it might be too * strict, since there might be some hop-by-hop options which can be * returned to normal user. * See also RFC 2292 section 6 (or RFC 3542 section 8). */ if ((in6p->inp_flags & IN6P_HOPOPTS) != 0) { /* * Check if a hop-by-hop options header is contatined in the * received packet, and if so, store the options as ancillary * data. Note that a hop-by-hop options header must be * just after the IPv6 header, which is assured through the * IPv6 input processing. */ if (ip6->ip6_nxt == IPPROTO_HOPOPTS) { struct ip6_hbh *hbh; int hbhlen = 0; #ifdef PULLDOWN_TEST struct mbuf *ext; #endif #ifndef PULLDOWN_TEST hbh = (struct ip6_hbh *)(ip6 + 1); hbhlen = (hbh->ip6h_len + 1) << 3; #else ext = ip6_pullexthdr(m, sizeof(struct ip6_hdr), ip6->ip6_nxt); if (ext == NULL) { V_ip6stat.ip6s_tooshort++; return; } hbh = mtod(ext, struct ip6_hbh *); hbhlen = (hbh->ip6h_len + 1) << 3; if (hbhlen != ext->m_len) { m_freem(ext); V_ip6stat.ip6s_tooshort++; return; } #endif /* * XXX: We copy the whole header even if a * jumbo payload option is included, the option which * is to be removed before returning according to * RFC2292. * Note: this constraint is removed in RFC3542 */ *mp = sbcreatecontrol((caddr_t)hbh, hbhlen, IS2292(in6p, IPV6_2292HOPOPTS, IPV6_HOPOPTS), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; #ifdef PULLDOWN_TEST m_freem(ext); #endif } } if ((in6p->inp_flags & (IN6P_RTHDR | IN6P_DSTOPTS)) != 0) { int nxt = ip6->ip6_nxt, off = sizeof(struct ip6_hdr); /* * Search for destination options headers or routing * header(s) through the header chain, and stores each * header as ancillary data. * Note that the order of the headers remains in * the chain of ancillary data. */ while (1) { /* is explicit loop prevention necessary? */ struct ip6_ext *ip6e = NULL; int elen; #ifdef PULLDOWN_TEST struct mbuf *ext = NULL; #endif /* * if it is not an extension header, don't try to * pull it from the chain. */ switch (nxt) { case IPPROTO_DSTOPTS: case IPPROTO_ROUTING: case IPPROTO_HOPOPTS: case IPPROTO_AH: /* is it possible? */ break; default: goto loopend; } #ifndef PULLDOWN_TEST if (off + sizeof(*ip6e) > m->m_len) goto loopend; ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off); if (nxt == IPPROTO_AH) elen = (ip6e->ip6e_len + 2) << 2; else elen = (ip6e->ip6e_len + 1) << 3; if (off + elen > m->m_len) goto loopend; #else ext = ip6_pullexthdr(m, off, nxt); if (ext == NULL) { V_ip6stat.ip6s_tooshort++; return; } ip6e = mtod(ext, struct ip6_ext *); if (nxt == IPPROTO_AH) elen = (ip6e->ip6e_len + 2) << 2; else elen = (ip6e->ip6e_len + 1) << 3; if (elen != ext->m_len) { m_freem(ext); V_ip6stat.ip6s_tooshort++; return; } #endif switch (nxt) { case IPPROTO_DSTOPTS: if (!(in6p->inp_flags & IN6P_DSTOPTS)) break; *mp = sbcreatecontrol((caddr_t)ip6e, elen, IS2292(in6p, IPV6_2292DSTOPTS, IPV6_DSTOPTS), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; break; case IPPROTO_ROUTING: if (!in6p->inp_flags & IN6P_RTHDR) break; *mp = sbcreatecontrol((caddr_t)ip6e, elen, IS2292(in6p, IPV6_2292RTHDR, IPV6_RTHDR), IPPROTO_IPV6); if (*mp) mp = &(*mp)->m_next; break; case IPPROTO_HOPOPTS: case IPPROTO_AH: /* is it possible? */ break; default: /* * other cases have been filtered in the above. * none will visit this case. here we supply * the code just in case (nxt overwritten or * other cases). */ #ifdef PULLDOWN_TEST m_freem(ext); #endif goto loopend; } /* proceed with the next header. */ off += elen; nxt = ip6e->ip6e_nxt; ip6e = NULL; #ifdef PULLDOWN_TEST m_freem(ext); ext = NULL; #endif } loopend: ; } } #undef IS2292 void ip6_notify_pmtu(struct inpcb *in6p, struct sockaddr_in6 *dst, u_int32_t *mtu) { struct socket *so; struct mbuf *m_mtu; struct ip6_mtuinfo mtuctl; so = in6p->inp_socket; if (mtu == NULL) return; #ifdef DIAGNOSTIC if (so == NULL) /* I believe this is impossible */ panic("ip6_notify_pmtu: socket is NULL"); #endif bzero(&mtuctl, sizeof(mtuctl)); /* zero-clear for safety */ mtuctl.ip6m_mtu = *mtu; mtuctl.ip6m_addr = *dst; if (sa6_recoverscope(&mtuctl.ip6m_addr)) return; if ((m_mtu = sbcreatecontrol((caddr_t)&mtuctl, sizeof(mtuctl), IPV6_PATHMTU, IPPROTO_IPV6)) == NULL) return; if (sbappendaddr(&so->so_rcv, (struct sockaddr *)dst, NULL, m_mtu) == 0) { m_freem(m_mtu); /* XXX: should count statistics */ } else sorwakeup(so); return; } #ifdef PULLDOWN_TEST /* * pull single extension header from mbuf chain. returns single mbuf that * contains the result, or NULL on error. */ static struct mbuf * ip6_pullexthdr(struct mbuf *m, size_t off, int nxt) { struct ip6_ext ip6e; size_t elen; struct mbuf *n; #ifdef DIAGNOSTIC switch (nxt) { case IPPROTO_DSTOPTS: case IPPROTO_ROUTING: case IPPROTO_HOPOPTS: case IPPROTO_AH: /* is it possible? */ break; default: printf("ip6_pullexthdr: invalid nxt=%d\n", nxt); } #endif m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); if (nxt == IPPROTO_AH) elen = (ip6e.ip6e_len + 2) << 2; else elen = (ip6e.ip6e_len + 1) << 3; MGET(n, M_DONTWAIT, MT_DATA); if (n && elen >= MLEN) { MCLGET(n, M_DONTWAIT); if ((n->m_flags & M_EXT) == 0) { m_free(n); n = NULL; } } if (!n) return NULL; n->m_len = 0; if (elen >= M_TRAILINGSPACE(n)) { m_free(n); return NULL; } m_copydata(m, off, elen, mtod(n, caddr_t)); n->m_len = elen; return n; } #endif /* * Get pointer to the previous header followed by the header * currently processed. * XXX: This function supposes that * M includes all headers, * the next header field and the header length field of each header * are valid, and * the sum of each header length equals to OFF. * Because of these assumptions, this function must be called very * carefully. Moreover, it will not be used in the near future when * we develop `neater' mechanism to process extension headers. */ char * ip6_get_prevhdr(struct mbuf *m, int off) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); if (off == sizeof(struct ip6_hdr)) return (&ip6->ip6_nxt); else { int len, nxt; struct ip6_ext *ip6e = NULL; nxt = ip6->ip6_nxt; len = sizeof(struct ip6_hdr); while (len < off) { ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + len); switch (nxt) { case IPPROTO_FRAGMENT: len += sizeof(struct ip6_frag); break; case IPPROTO_AH: len += (ip6e->ip6e_len + 2) << 2; break; default: len += (ip6e->ip6e_len + 1) << 3; break; } nxt = ip6e->ip6e_nxt; } if (ip6e) return (&ip6e->ip6e_nxt); else return NULL; } } /* * get next header offset. m will be retained. */ int ip6_nexthdr(struct mbuf *m, int off, int proto, int *nxtp) { struct ip6_hdr ip6; struct ip6_ext ip6e; struct ip6_frag fh; /* just in case */ if (m == NULL) panic("ip6_nexthdr: m == NULL"); if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off) return -1; switch (proto) { case IPPROTO_IPV6: if (m->m_pkthdr.len < off + sizeof(ip6)) return -1; m_copydata(m, off, sizeof(ip6), (caddr_t)&ip6); if (nxtp) *nxtp = ip6.ip6_nxt; off += sizeof(ip6); return off; case IPPROTO_FRAGMENT: /* * terminate parsing if it is not the first fragment, * it does not make sense to parse through it. */ if (m->m_pkthdr.len < off + sizeof(fh)) return -1; m_copydata(m, off, sizeof(fh), (caddr_t)&fh); /* IP6F_OFF_MASK = 0xfff8(BigEndian), 0xf8ff(LittleEndian) */ if (fh.ip6f_offlg & IP6F_OFF_MASK) return -1; if (nxtp) *nxtp = fh.ip6f_nxt; off += sizeof(struct ip6_frag); return off; case IPPROTO_AH: if (m->m_pkthdr.len < off + sizeof(ip6e)) return -1; m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); if (nxtp) *nxtp = ip6e.ip6e_nxt; off += (ip6e.ip6e_len + 2) << 2; return off; case IPPROTO_HOPOPTS: case IPPROTO_ROUTING: case IPPROTO_DSTOPTS: if (m->m_pkthdr.len < off + sizeof(ip6e)) return -1; m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e); if (nxtp) *nxtp = ip6e.ip6e_nxt; off += (ip6e.ip6e_len + 1) << 3; return off; case IPPROTO_NONE: case IPPROTO_ESP: case IPPROTO_IPCOMP: /* give up */ return -1; default: return -1; } return -1; } /* * get offset for the last header in the chain. m will be kept untainted. */ int ip6_lasthdr(struct mbuf *m, int off, int proto, int *nxtp) { int newoff; int nxt; if (!nxtp) { nxt = -1; nxtp = &nxt; } while (1) { newoff = ip6_nexthdr(m, off, proto, nxtp); if (newoff < 0) return off; else if (newoff < off) return -1; /* invalid */ else if (newoff == off) return newoff; off = newoff; proto = *nxtp; } } struct ip6aux * ip6_addaux(struct mbuf *m) { struct m_tag *mtag; mtag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL); if (!mtag) { mtag = m_tag_get(PACKET_TAG_IPV6_INPUT, sizeof(struct ip6aux), M_NOWAIT); if (mtag) { m_tag_prepend(m, mtag); bzero(mtag + 1, sizeof(struct ip6aux)); } } return mtag ? (struct ip6aux *)(mtag + 1) : NULL; } struct ip6aux * ip6_findaux(struct mbuf *m) { struct m_tag *mtag; mtag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL); return mtag ? (struct ip6aux *)(mtag + 1) : NULL; } void ip6_delaux(struct mbuf *m) { struct m_tag *mtag; mtag = m_tag_find(m, PACKET_TAG_IPV6_INPUT, NULL); if (mtag) m_tag_delete(m, mtag); } /* * System control for IP6 */ u_char inet6ctlerrmap[PRC_NCMDS] = { 0, 0, 0, 0, 0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH, EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, EMSGSIZE, EHOSTUNREACH, 0, 0, 0, 0, 0, 0, ENOPROTOOPT }; Index: projects/cambria/sys/netipsec/vipsec.h =================================================================== --- projects/cambria/sys/netipsec/vipsec.h (revision 186459) +++ projects/cambria/sys/netipsec/vipsec.h (revision 186460) @@ -1,179 +1,177 @@ /* * Copyright (c) 2007-2008 University of Zagreb * Copyright (c) 2007-2008 FreeBSD Foundation * * This software was developed by the University of Zagreb and the * FreeBSD Foundation under sponsorship by the Stichting NLnet and the * FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _NETIPSEC_VIPSEC_H_ #define _NETIPSEC_VIPSEC_H_ #include #include #include -#include - #include #include #include #include #include #include struct vnet_ipsec { int _ipsec_debug; struct ipsecstat _ipsec4stat; struct secpolicy _ip4_def_policy; int _ip4_esp_trans_deflev; int _ip4_esp_net_deflev; int _ip4_ah_trans_deflev; int _ip4_ah_net_deflev; int _ip4_ah_offsetmask; int _ip4_ipsec_dfbit; int _ip4_ipsec_ecn; int _ip4_esp_randpad; int _ipsec_replay; int _ipsec_integrity; int _crypto_support; u_int32_t _key_debug_level; u_int _key_spi_trycnt; u_int32_t _key_spi_minval; u_int32_t _key_spi_maxval; u_int32_t _policy_id; u_int _key_int_random; u_int _key_larval_lifetime; int _key_blockacq_count; int _key_blockacq_lifetime; int _key_preferred_oldsa; u_int32_t _acq_seq; int _esp_enable; struct espstat _espstat; int _esp_max_ivlen; int _ipsec_esp_keymin; int _ipsec_esp_auth; int _ipsec_ah_keymin; int _ipip_allow; struct ipipstat _ipipstat; struct ipsecstat _ipsec6stat; int _ip6_esp_trans_deflev; int _ip6_esp_net_deflev; int _ip6_ah_trans_deflev; int _ip6_ah_net_deflev; int _ip6_ipsec_ecn; int _ah_enable; int _ah_cleartos; struct ahstat _ahstat; int _ipcomp_enable; struct ipcompstat _ipcompstat; struct pfkeystat _pfkeystat; struct key_cb _key_cb; LIST_HEAD(, secpolicy) _sptree[IPSEC_DIR_MAX]; LIST_HEAD(, secashead) _sahtree; LIST_HEAD(, secreg) _regtree[SADB_SATYPE_MAX + 1]; LIST_HEAD(, secacq) _acqtree; LIST_HEAD(, secspacq) _spacqtree; }; #ifndef VIMAGE #ifndef VIMAGE_GLOBALS extern struct vnet_ipsec vnet_ipsec_0; #endif #endif /* * Symbol translation macros */ #define INIT_VNET_IPSEC(vnet) \ INIT_FROM_VNET(vnet, VNET_MOD_IPSEC, struct vnet_ipsec, vnet_ipsec) #define VNET_IPSEC(sym) VSYM(vnet_ipsec, sym) #define V_acq_seq VNET_IPSEC(acq_seq) #define V_acqtree VNET_IPSEC(acqtree) #define V_ah_cleartos VNET_IPSEC(ah_cleartos) #define V_ah_enable VNET_IPSEC(ah_enable) #define V_ahstat VNET_IPSEC(ahstat) #define V_crypto_support VNET_IPSEC(crypto_support) #define V_esp_enable VNET_IPSEC(esp_enable) #define V_esp_max_ivlen VNET_IPSEC(esp_max_ivlen) #define V_espstat VNET_IPSEC(espstat) #define V_ip4_ah_net_deflev VNET_IPSEC(ip4_ah_net_deflev) #define V_ip4_ah_offsetmask VNET_IPSEC(ip4_ah_offsetmask) #define V_ip4_ah_trans_deflev VNET_IPSEC(ip4_ah_trans_deflev) #define V_ip4_def_policy VNET_IPSEC(ip4_def_policy) #define V_ip4_esp_net_deflev VNET_IPSEC(ip4_esp_net_deflev) #define V_ip4_esp_randpad VNET_IPSEC(ip4_esp_randpad) #define V_ip4_esp_trans_deflev VNET_IPSEC(ip4_esp_trans_deflev) #define V_ip4_ipsec_dfbit VNET_IPSEC(ip4_ipsec_dfbit) #define V_ip4_ipsec_ecn VNET_IPSEC(ip4_ipsec_ecn) #define V_ip6_ah_net_deflev VNET_IPSEC(ip6_ah_net_deflev) #define V_ip6_ah_trans_deflev VNET_IPSEC(ip6_ah_trans_deflev) #define V_ip6_esp_net_deflev VNET_IPSEC(ip6_esp_net_deflev) #define V_ip6_esp_randpad VNET_IPSEC(ip6_esp_randpad) #define V_ip6_esp_trans_deflev VNET_IPSEC(ip6_esp_trans_deflev) #define V_ip6_ipsec_ecn VNET_IPSEC(ip6_ipsec_ecn) #define V_ipcomp_enable VNET_IPSEC(ipcomp_enable) #define V_ipcompstat VNET_IPSEC(ipcompstat) #define V_ipip_allow VNET_IPSEC(ipip_allow) #define V_ipipstat VNET_IPSEC(ipipstat) #define V_ipsec4stat VNET_IPSEC(ipsec4stat) #define V_ipsec6stat VNET_IPSEC(ipsec6stat) #define V_ipsec_ah_keymin VNET_IPSEC(ipsec_ah_keymin) #define V_ipsec_debug VNET_IPSEC(ipsec_debug) #define V_ipsec_esp_auth VNET_IPSEC(ipsec_esp_auth) #define V_ipsec_esp_keymin VNET_IPSEC(ipsec_esp_keymin) #define V_ipsec_integrity VNET_IPSEC(ipsec_integrity) #define V_ipsec_replay VNET_IPSEC(ipsec_replay) #define V_key_blockacq_count VNET_IPSEC(key_blockacq_count) #define V_key_blockacq_lifetime VNET_IPSEC(key_blockacq_lifetime) #define V_key_cb VNET_IPSEC(key_cb) #define V_key_debug_level VNET_IPSEC(key_debug_level) #define V_key_int_random VNET_IPSEC(key_int_random) #define V_key_larval_lifetime VNET_IPSEC(key_larval_lifetime) #define V_key_preferred_oldsa VNET_IPSEC(key_preferred_oldsa) #define V_key_spi_maxval VNET_IPSEC(key_spi_maxval) #define V_key_spi_minval VNET_IPSEC(key_spi_minval) #define V_key_spi_trycnt VNET_IPSEC(key_spi_trycnt) #define V_pfkeystat VNET_IPSEC(pfkeystat) #define V_policy_id VNET_IPSEC(policy_id) #define V_regtree VNET_IPSEC(regtree) #define V_sahtree VNET_IPSEC(sahtree) #define V_spacqtree VNET_IPSEC(spacqtree) #define V_sptree VNET_IPSEC(sptree) #endif /* !_NETIPSEC_VIPSEC_H_ */ Index: projects/cambria/sys/pc98/conf/GENERIC =================================================================== --- projects/cambria/sys/pc98/conf/GENERIC (revision 186459) +++ projects/cambria/sys/pc98/conf/GENERIC (revision 186460) @@ -1,287 +1,287 @@ # # GENERIC -- Generic kernel configuration file for FreeBSD/pc98 # # For more information on this file, please read the handbook section on # Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ cpu I486_CPU cpu I586_CPU cpu I686_CPU ident GENERIC # To statically compile in device wiring instead of /boot/device.hints #hints "GENERIC.hints" # Default places to look for devices. makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols options SCHED_4BSD # 4BSD scheduler #options PREEMPTION # Enable kernel thread preemption options INET # InterNETworking options INET6 # IPv6 communications protocols -options SCTP # Stream Control Transmission Protocol +#options SCTP # Stream Control Transmission Protocol options FFS # Berkeley Fast Filesystem options SOFTUPDATES # Enable FFS soft updates support options UFS_ACL # Support for access control lists options UFS_DIRHASH # Improve performance on big directories options UFS_GJOURNAL # Enable gjournal-based UFS journaling options MD_ROOT # MD is a potential root device options NFSCLIENT # Network Filesystem Client -options NFSSERVER # Network Filesystem Server -options NFSLOCKD # Network Lock Manager +#options NFSSERVER # Network Filesystem Server +#options NFSLOCKD # Network Lock Manager options NFS_ROOT # NFS usable as /, requires NFSCLIENT options MSDOSFS # MSDOS Filesystem options CD9660 # ISO 9660 Filesystem -options PROCFS # Process filesystem (requires PSEUDOFS) -options PSEUDOFS # Pseudo-filesystem framework +#options PROCFS # Process filesystem (requires PSEUDOFS) +#options PSEUDOFS # Pseudo-filesystem framework options GEOM_PART_GPT # GUID Partition Tables. options GEOM_LABEL # Provides labelization options COMPAT_43TTY # BSD 4.3 TTY compat (sgtty) options COMPAT_FREEBSD4 # Compatible with FreeBSD4 options COMPAT_FREEBSD5 # Compatible with FreeBSD5 options COMPAT_FREEBSD6 # Compatible with FreeBSD6 options COMPAT_FREEBSD7 # Compatible with FreeBSD7 options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI options EPSON_BOUNCEDMA # use bounce buffer for 15-16M #options EPSON_MEMWIN # EPSON memory window support #options LINE30 options KTRACE # ktrace(1) support options STACK # stack(9) support -options SYSVSHM # SYSV-style shared memory -options SYSVMSG # SYSV-style message queues -options SYSVSEM # SYSV-style semaphores +#options SYSVSHM # SYSV-style shared memory +#options SYSVMSG # SYSV-style message queues +#options SYSVSEM # SYSV-style semaphores options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions options KBD_INSTALL_CDEV # install a CDEV entry in /dev options HWPMC_HOOKS # Necessary kernel hooks for hwpmc(4) options AUDIT # Security event auditing # Debugging for use in -current options KDB # Enable kernel debugger support. options DDB # Support DDB. options GDB # Support remote GDB. options INVARIANTS # Enable calls of extra sanity checking options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed # To make an SMP kernel, the next two lines are needed #options SMP # Symmetric MultiProcessor Kernel #device apic # I/O APIC # Bus support. device pci # Floppy drives device fdc # ATA and ATAPI devices device ata device atadisk # ATA disk drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives options ATA_STATIC_ID # Static device numbering # SCSI Controllers device adv # Advansys SCSI adapters device ahc # AHA2940 and onboard AIC7xxx devices device amd # AMD 53C974 (Tekram DC-390(T)) device isp # Qlogic family #device ncr # NCR/Symbios Logic device sym # NCR/Symbios Logic (newer chipsets + those of `ncr') device aic # PC-9801-100 device ct # host adapter using WD33C93[ABC] chip (C bus) -device ncv # NCR 53C500 -device nsp # Workbit Ninja SCSI-3 -device stg # TMC 18C30/18C50 +#device ncv # NCR 53C500 +#device nsp # Workbit Ninja SCSI-3 +#device stg # TMC 18C30/18C50 # SCSI peripherals device scbus # SCSI bus (required for SCSI) device ch # SCSI media changers device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct SCSI access) device ses # SCSI Environmental Services (and SAF-TE) # keyboard driver device pckbd # PC98 keyboard device gdc # GDC screen device splash # Splash screen and screen saver support # syscons is the default console driver, resembling an SCO console device sc #device agp # support several AGP chipsets # Power management support (see NOTES for more options) #device apm #device pmc #device canbus #device canbepm # Add suspend/resume support for the i8254. #device pmtimer # Audio support #device sound # Generic sound driver #device snd_mss # Microsoft Sound System #device "snd_sb16" # Sound Blaster 16 #device snd_sbc # Sound Blaster # PCCARD (PCMCIA) support # PCMCIA and cardbus bridge support -device cbb # cardbus (yenta) bridge -device pccard # PC Card (16-bit) bus -device cardbus # CardBus (32-bit) bus +#device cbb # cardbus (yenta) bridge +#device pccard # PC Card (16-bit) bus +#device cardbus # CardBus (32-bit) bus # Serial (COM) ports #options COM_MULTIPORT #options COM_ESP # ESP98 #device sio # 8250, 16[45]50, 8251 based serial ports device uart # Generic UART driver device mse #device joy # NEW Parallel port -device ppc -device ppbus # Parallel port bus (required) -device lpt # Printer -device plip # TCP/IP over parallel -device ppi # Parallel port interface device +#device ppc +#device ppbus # Parallel port bus (required) +#device lpt # Printer +#device plip # TCP/IP over parallel +#device ppi # Parallel port interface device #device vpo # Requires scbus and da # OLD Parallel port # Please stay olpt driver after ppc driver #device olpt # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') -device em # Intel PRO/1000 adapter Gigabit Ethernet Card +#device em # Intel PRO/1000 adapter Gigabit Ethernet Card device le # AMD Am7900 LANCE and Am79C9xx PCnet -device ti # Alteon Networks Tigon I/II gigabit Ethernet +#device ti # Alteon Networks Tigon I/II gigabit Ethernet device txp # 3Com 3cR990 (``Typhoon'') device vx # 3Com 3c590, 3c595 (``Vortex'') # PCI Ethernet NICs that use the common MII bus controller code. # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! device miibus # MII bus support device bfe # Broadcom BCM440x 10/100 Ethernet -device bge # Broadcom BCM570xx Gigabit Ethernet +#device bge # Broadcom BCM570xx Gigabit Ethernet device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) -device lge # Level 1 LXT1001 gigabit Ethernet -device nge # NatSemi DP83820 gigabit Ethernet +#device lge # Level 1 LXT1001 gigabit Ethernet +#device nge # NatSemi DP83820 gigabit Ethernet device pcn # AMD Am79C97x PCI 10/100 (precedence over 'le') device re # RealTek 8139C+/8169/8169S/8110S device rl # RealTek 8129/8139 device sf # Adaptec AIC-6915 (``Starfire'') device sis # Silicon Integrated Systems SiS 900/SiS 7016 -device sk # SysKonnect SK-984x & SK-982x gigabit Ethernet +#device sk # SysKonnect SK-984x & SK-982x gigabit Ethernet device ste # Sundance ST201 (D-Link DFE-550TX) device tl # Texas Instruments ThunderLAN device tx # SMC EtherPower II (83c170 ``EPIC'') -device vge # VIA VT612x gigabit Ethernet +#device vge # VIA VT612x gigabit Ethernet device vr # VIA Rhine, Rhine II device wb # Winbond W89C840F device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'') # ISA Ethernet NICs. pccard NICs included. # 'device ed' requires 'device miibus' device ed # NE[12]000, SMC Ultra, 3c503, DS8390 cards device ep # Etherlink III based cards device fe # Fujitsu MB8696x based cards device sn # SMC's 9000 series of Ethernet chips device snc device xe # Xircom pccard Ethernet # Wireless NIC cards -device wlan # 802.11 support -options IEEE80211_DEBUG # enable debug msgs -options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's -device wlan_wep # 802.11 WEP support -device wlan_ccmp # 802.11 CCMP support -device wlan_tkip # 802.11 TKIP support -device wlan_amrr # AMRR transmit rate control algorithm -device an # Aironet 4500/4800 802.11 wireless NICs. -device ath # Atheros pci/cardbus NIC's -device ath_hal # pci/cardbus chip support -options AH_SUPPORT_AR5416 # enable AR5416 tx/rx descriptors -device ath_rate_sample # SampleRate tx rate control for ath -device ral # Ralink Technology RT2500 wireless NICs. -device wi # WaveLAN/Intersil/Symbol 802.11 wireless NICs. +#device wlan # 802.11 support +#options IEEE80211_DEBUG # enable debug msgs +#options IEEE80211_AMPDU_AGE # age frames in AMPDU reorder q's +#device wlan_wep # 802.11 WEP support +#device wlan_ccmp # 802.11 CCMP support +#device wlan_tkip # 802.11 TKIP support +#device wlan_amrr # AMRR transmit rate control algorithm +#device an # Aironet 4500/4800 802.11 wireless NICs. +#device ath # Atheros pci/cardbus NIC's +#device ath_hal # pci/cardbus chip support +#options AH_SUPPORT_AR5416 # enable AR5416 tx/rx descriptors +#device ath_rate_sample # SampleRate tx rate control for ath +#device ral # Ralink Technology RT2500 wireless NICs. +#device wi # WaveLAN/Intersil/Symbol 802.11 wireless NICs. #device wl # Older non 802.11 Wavelan wireless NIC. # Pseudo devices. device loop # Network loopback device random # Entropy device device ether # Ethernet support device tun # Packet tunnel. device pty # BSD-style compatibility pseudo ttys device md # Memory "disks" device gif # IPv6 and IPv4 tunneling device faith # IPv6-to-IPv4 relaying (translation) device firmware # firmware assist module # The `bpf' device enables the Berkeley Packet Filter. # Be aware of the administrative consequences of enabling this! # Note that 'bpf' is required for DHCP. device bpf # Berkeley packet filter # USB support #device uhci # UHCI PCI->USB interface #device ohci # OHCI PCI->USB interface #device ehci # EHCI PCI->USB interface (USB 2.0) #device usb # USB Bus (required) #device udbp # USB Double Bulk Pipe devices #device ugen # Generic #device uhid # "Human Interface Devices" #device ukbd # Keyboard #device ulpt # Printer #device umass # Disks/Mass storage - Requires scbus and da #device ums # Mouse #device ural # Ralink Technology RT2500USB wireless NICs #device rum # Ralink Technology RT2501USB wireless NICs #device zyd # ZyDAS zb1211/zb1211b wireless NICs #device urio # Diamond Rio 500 MP3 player #device uscanner # Scanners # USB Serial devices #device ucom # Generic com ttys #device uark # Technologies ARK3116 based serial adapters #device ubsa # Belkin F5U103 and compatible serial adapters #device ubser # BWCT console serial adapters #device uftdi # For FTDI usb serial adapters #device uipaq # Some WinCE based devices #device uplcom # Prolific PL-2303 serial adapters #device uslcom # SI Labs CP2101/CP2102 serial adapters #device uvisor # Visor and Palm devices #device uvscom # USB serial support for DDI pocket's PHS # USB Ethernet, requires miibus #device aue # ADMtek USB Ethernet #device axe # ASIX Electronics USB Ethernet #device cdce # Generic USB over Ethernet #device cue # CATC USB Ethernet #device kue # Kawasaki LSI USB Ethernet #device rue # RealTek RTL8150 USB Ethernet #device udav # Davicom DM9601E USB # FireWire support #device firewire # FireWire bus code #device sbp # SCSI over FireWire (Requires scbus and da) #device fwe # Ethernet over FireWire (non-standard!) Index: projects/cambria/sys/pci/if_rl.c =================================================================== --- projects/cambria/sys/pci/if_rl.c (revision 186459) +++ projects/cambria/sys/pci/if_rl.c (revision 186460) @@ -1,2109 +1,2111 @@ /*- * Copyright (c) 1997, 1998 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. */ #include __FBSDID("$FreeBSD$"); /* * RealTek 8129/8139 PCI NIC driver * * Supports several extremely cheap PCI 10/100 adapters based on * the RealTek chipset. Datasheets can be obtained from * www.realtek.com.tw. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The RealTek 8139 PCI NIC redefines the meaning of 'low end.' This is * probably the worst PCI ethernet controller ever made, with the possible * exception of the FEAST chip made by SMC. The 8139 supports bus-master * DMA, but it has a terrible interface that nullifies any performance * gains that bus-master DMA usually offers. * * For transmission, the chip offers a series of four TX descriptor * registers. Each transmit frame must be in a contiguous buffer, aligned * on a longword (32-bit) boundary. This means we almost always have to * do mbuf copies in order to transmit a frame, except in the unlikely * case where a) the packet fits into a single mbuf, and b) the packet * is 32-bit aligned within the mbuf's data area. The presence of only * four descriptor registers means that we can never have more than four * packets queued for transmission at any one time. * * Reception is not much better. The driver has to allocate a single large * buffer area (up to 64K in size) into which the chip will DMA received * frames. Because we don't know where within this region received packets * will begin or end, we have no choice but to copy data from the buffer * area into mbufs in order to pass the packets up to the higher protocol * levels. * * It's impossible given this rotten design to really achieve decent * performance at 100Mbps, unless you happen to have a 400Mhz PII or * some equally overmuscled CPU to drive it. * * On the bright side, the 8139 does have a built-in PHY, although * rather than using an MDIO serial interface like most other NICs, the * PHY registers are directly accessible through the 8139's register * space. The 8139 supports autonegotiation, as well as a 64-bit multicast * filter. * * The 8129 chip is an older version of the 8139 that uses an external PHY * chip. The 8129 has a serial MDIO interface for accessing the MII where * the 8139 lets you directly access the on-board PHY registers. We need * to select which interface to use depending on the chip type. */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(rl, pci, 1, 1, 1); MODULE_DEPEND(rl, ether, 1, 1, 1); MODULE_DEPEND(rl, miibus, 1, 1, 1); /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Default to using PIO access for this driver. On SMP systems, * there appear to be problems with memory mapped mode: it looks like * doing too many memory mapped access back to back in rapid succession * can hang the bus. I'm inclined to blame this on crummy design/construction * on the part of RealTek. Memory mapped mode does appear to work on * uniprocessor systems though. */ #define RL_USEIOSPACE #include /* * Various supported device vendors/types and their names. */ static struct rl_type rl_devs[] = { { RT_VENDORID, RT_DEVICEID_8129, RL_8129, "RealTek 8129 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8139, RL_8139, "RealTek 8139 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8139D, RL_8139, "RealTek 8139 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8138, RL_8139, "RealTek 8139 10/100BaseTX CardBus" }, { RT_VENDORID, RT_DEVICEID_8100, RL_8139, "RealTek 8100 10/100BaseTX" }, { ACCTON_VENDORID, ACCTON_DEVICEID_5030, RL_8139, "Accton MPX 5030/5038 10/100BaseTX" }, { DELTA_VENDORID, DELTA_DEVICEID_8139, RL_8139, "Delta Electronics 8139 10/100BaseTX" }, { ADDTRON_VENDORID, ADDTRON_DEVICEID_8139, RL_8139, "Addtron Technology 8139 10/100BaseTX" }, { DLINK_VENDORID, DLINK_DEVICEID_530TXPLUS, RL_8139, "D-Link DFE-530TX+ 10/100BaseTX" }, { DLINK_VENDORID, DLINK_DEVICEID_690TXD, RL_8139, "D-Link DFE-690TXD 10/100BaseTX" }, { NORTEL_VENDORID, ACCTON_DEVICEID_5030, RL_8139, "Nortel Networks 10/100BaseTX" }, { COREGA_VENDORID, COREGA_DEVICEID_FETHERCBTXD, RL_8139, "Corega FEther CB-TXD" }, { COREGA_VENDORID, COREGA_DEVICEID_FETHERIICBTXD, RL_8139, "Corega FEtherII CB-TXD" }, { PEPPERCON_VENDORID, PEPPERCON_DEVICEID_ROLF, RL_8139, "Peppercon AG ROL-F" }, { PLANEX_VENDORID, PLANEX_DEVICEID_FNW3603TX, RL_8139, "Planex FNW-3603-TX" }, { PLANEX_VENDORID, PLANEX_DEVICEID_FNW3800TX, RL_8139, "Planex FNW-3800-TX" }, { CP_VENDORID, RT_DEVICEID_8139, RL_8139, "Compaq HNE-300" }, { LEVEL1_VENDORID, LEVEL1_DEVICEID_FPC0106TX, RL_8139, "LevelOne FPC-0106TX" }, { EDIMAX_VENDORID, EDIMAX_DEVICEID_EP4103DL, RL_8139, "Edimax EP-4103DL CardBus" } }; static int rl_attach(device_t); static int rl_detach(device_t); static void rl_dmamap_cb(void *, bus_dma_segment_t *, int, int); static int rl_dma_alloc(struct rl_softc *); static void rl_dma_free(struct rl_softc *); static void rl_eeprom_putbyte(struct rl_softc *, int); static void rl_eeprom_getword(struct rl_softc *, int, uint16_t *); static int rl_encap(struct rl_softc *, struct mbuf **); static int rl_list_tx_init(struct rl_softc *); static int rl_list_rx_init(struct rl_softc *); static int rl_ifmedia_upd(struct ifnet *); static void rl_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int rl_ioctl(struct ifnet *, u_long, caddr_t); static void rl_intr(void *); static void rl_init(void *); static void rl_init_locked(struct rl_softc *sc); static void rl_mii_send(struct rl_softc *, uint32_t, int); static void rl_mii_sync(struct rl_softc *); static int rl_mii_readreg(struct rl_softc *, struct rl_mii_frame *); static int rl_mii_writereg(struct rl_softc *, struct rl_mii_frame *); static int rl_miibus_readreg(device_t, int, int); static void rl_miibus_statchg(device_t); static int rl_miibus_writereg(device_t, int, int, int); #ifdef DEVICE_POLLING static void rl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count); static void rl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count); #endif static int rl_probe(device_t); static void rl_read_eeprom(struct rl_softc *, uint8_t *, int, int, int); static void rl_reset(struct rl_softc *); static int rl_resume(device_t); static void rl_rxeof(struct rl_softc *); static void rl_setmulti(struct rl_softc *); static int rl_shutdown(device_t); static void rl_start(struct ifnet *); static void rl_start_locked(struct ifnet *); static void rl_stop(struct rl_softc *); static int rl_suspend(device_t); static void rl_tick(void *); static void rl_txeof(struct rl_softc *); static void rl_watchdog(struct rl_softc *); #ifdef RL_USEIOSPACE #define RL_RES SYS_RES_IOPORT #define RL_RID RL_PCI_LOIO #else #define RL_RES SYS_RES_MEMORY #define RL_RID RL_PCI_LOMEM #endif static device_method_t rl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rl_probe), DEVMETHOD(device_attach, rl_attach), DEVMETHOD(device_detach, rl_detach), DEVMETHOD(device_suspend, rl_suspend), DEVMETHOD(device_resume, rl_resume), DEVMETHOD(device_shutdown, rl_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, rl_miibus_readreg), DEVMETHOD(miibus_writereg, rl_miibus_writereg), DEVMETHOD(miibus_statchg, rl_miibus_statchg), { 0, 0 } }; static driver_t rl_driver = { "rl", rl_methods, sizeof(struct rl_softc) }; static devclass_t rl_devclass; DRIVER_MODULE(rl, pci, rl_driver, rl_devclass, 0, 0); DRIVER_MODULE(rl, cardbus, rl_driver, rl_devclass, 0, 0); DRIVER_MODULE(miibus, rl, miibus_driver, miibus_devclass, 0, 0); #define EE_SET(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) | x) #define EE_CLR(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) & ~x) /* * Send a read command and address to the EEPROM, check for ACK. */ static void rl_eeprom_putbyte(struct rl_softc *sc, int addr) { register int d, i; d = addr | sc->rl_eecmd_read; /* * Feed in each bit and strobe the clock. */ for (i = 0x400; i; i >>= 1) { if (d & i) { EE_SET(RL_EE_DATAIN); } else { EE_CLR(RL_EE_DATAIN); } DELAY(100); EE_SET(RL_EE_CLK); DELAY(150); EE_CLR(RL_EE_CLK); DELAY(100); } } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void rl_eeprom_getword(struct rl_softc *sc, int addr, uint16_t *dest) { register int i; uint16_t word = 0; /* Enter EEPROM access mode. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); /* * Send address of word we want to read. */ rl_eeprom_putbyte(sc, addr); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { EE_SET(RL_EE_CLK); DELAY(100); if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) word |= i; EE_CLR(RL_EE_CLK); DELAY(100); } /* Turn off EEPROM access mode. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); *dest = word; } /* * Read a sequence of words from the EEPROM. */ static void rl_read_eeprom(struct rl_softc *sc, uint8_t *dest, int off, int cnt, int swap) { int i; uint16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { rl_eeprom_getword(sc, off + i, &word); ptr = (uint16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } } /* * MII access routines are provided for the 8129, which * doesn't have a built-in PHY. For the 8139, we fake things * up by diverting rl_phy_readreg()/rl_phy_writereg() to the * direct access PHY registers. */ #define MII_SET(x) \ CSR_WRITE_1(sc, RL_MII, \ CSR_READ_1(sc, RL_MII) | (x)) #define MII_CLR(x) \ CSR_WRITE_1(sc, RL_MII, \ CSR_READ_1(sc, RL_MII) & ~(x)) /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void rl_mii_sync(struct rl_softc *sc) { register int i; MII_SET(RL_MII_DIR|RL_MII_DATAOUT); for (i = 0; i < 32; i++) { MII_SET(RL_MII_CLK); DELAY(1); MII_CLR(RL_MII_CLK); DELAY(1); } } /* * Clock a series of bits through the MII. */ static void rl_mii_send(struct rl_softc *sc, uint32_t bits, int cnt) { int i; MII_CLR(RL_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { MII_SET(RL_MII_DATAOUT); } else { MII_CLR(RL_MII_DATAOUT); } DELAY(1); MII_CLR(RL_MII_CLK); DELAY(1); MII_SET(RL_MII_CLK); } } /* * Read an PHY register through the MII. */ static int rl_mii_readreg(struct rl_softc *sc, struct rl_mii_frame *frame) { int i, ack; /* Set up frame for RX. */ frame->mii_stdelim = RL_MII_STARTDELIM; frame->mii_opcode = RL_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; CSR_WRITE_2(sc, RL_MII, 0); /* Turn on data xmit. */ MII_SET(RL_MII_DIR); rl_mii_sync(sc); /* Send command/address info. */ rl_mii_send(sc, frame->mii_stdelim, 2); rl_mii_send(sc, frame->mii_opcode, 2); rl_mii_send(sc, frame->mii_phyaddr, 5); rl_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ MII_CLR((RL_MII_CLK|RL_MII_DATAOUT)); DELAY(1); MII_SET(RL_MII_CLK); DELAY(1); /* Turn off xmit. */ MII_CLR(RL_MII_DIR); /* Check for ack */ MII_CLR(RL_MII_CLK); DELAY(1); ack = CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN; MII_SET(RL_MII_CLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { MII_CLR(RL_MII_CLK); DELAY(1); MII_SET(RL_MII_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { MII_CLR(RL_MII_CLK); DELAY(1); if (!ack) { if (CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN) frame->mii_data |= i; DELAY(1); } MII_SET(RL_MII_CLK); DELAY(1); } fail: MII_CLR(RL_MII_CLK); DELAY(1); MII_SET(RL_MII_CLK); DELAY(1); return (ack ? 1 : 0); } /* * Write to a PHY register through the MII. */ static int rl_mii_writereg(struct rl_softc *sc, struct rl_mii_frame *frame) { /* Set up frame for TX. */ frame->mii_stdelim = RL_MII_STARTDELIM; frame->mii_opcode = RL_MII_WRITEOP; frame->mii_turnaround = RL_MII_TURNAROUND; /* Turn on data output. */ MII_SET(RL_MII_DIR); rl_mii_sync(sc); rl_mii_send(sc, frame->mii_stdelim, 2); rl_mii_send(sc, frame->mii_opcode, 2); rl_mii_send(sc, frame->mii_phyaddr, 5); rl_mii_send(sc, frame->mii_regaddr, 5); rl_mii_send(sc, frame->mii_turnaround, 2); rl_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ MII_SET(RL_MII_CLK); DELAY(1); MII_CLR(RL_MII_CLK); DELAY(1); /* Turn off xmit. */ MII_CLR(RL_MII_DIR); return (0); } static int rl_miibus_readreg(device_t dev, int phy, int reg) { struct rl_softc *sc; struct rl_mii_frame frame; uint16_t rval = 0; uint16_t rl8139_reg = 0; sc = device_get_softc(dev); if (sc->rl_type == RL_8139) { /* Pretend the internal PHY is only at address 0 */ if (phy) { return (0); } switch (reg) { case MII_BMCR: rl8139_reg = RL_BMCR; break; case MII_BMSR: rl8139_reg = RL_BMSR; break; case MII_ANAR: rl8139_reg = RL_ANAR; break; case MII_ANER: rl8139_reg = RL_ANER; break; case MII_ANLPAR: rl8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); /* * Allow the rlphy driver to read the media status * register. If we have a link partner which does not * support NWAY, this is the register which will tell * us the results of parallel detection. */ case RL_MEDIASTAT: rval = CSR_READ_1(sc, RL_MEDIASTAT); return (rval); default: device_printf(sc->rl_dev, "bad phy register\n"); return (0); } rval = CSR_READ_2(sc, rl8139_reg); return (rval); } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; rl_mii_readreg(sc, &frame); return (frame.mii_data); } static int rl_miibus_writereg(device_t dev, int phy, int reg, int data) { struct rl_softc *sc; struct rl_mii_frame frame; uint16_t rl8139_reg = 0; sc = device_get_softc(dev); if (sc->rl_type == RL_8139) { /* Pretend the internal PHY is only at address 0 */ if (phy) { return (0); } switch (reg) { case MII_BMCR: rl8139_reg = RL_BMCR; break; case MII_BMSR: rl8139_reg = RL_BMSR; break; case MII_ANAR: rl8139_reg = RL_ANAR; break; case MII_ANER: rl8139_reg = RL_ANER; break; case MII_ANLPAR: rl8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: return (0); break; default: device_printf(sc->rl_dev, "bad phy register\n"); return (0); } CSR_WRITE_2(sc, rl8139_reg, data); return (0); } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; rl_mii_writereg(sc, &frame); return (0); } static void rl_miibus_statchg(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->rl_miibus); ifp = sc->rl_ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; sc->rl_flags &= ~RL_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->rl_flags |= RL_FLAG_LINK; break; default: break; } } /* * RealTek controllers do not provide any interface to * Tx/Rx MACs for resolved speed, duplex and flow-control * parameters. */ } /* * Program the 64-bit multicast hash filter. */ static void rl_setmulti(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; int h = 0; uint32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; uint32_t rxfilt; int mcnt = 0; RL_LOCK_ASSERT(sc); rxfilt = CSR_READ_4(sc, RL_RXCFG); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxfilt |= RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, RL_MAR0, 0); CSR_WRITE_4(sc, RL_MAR4, 0); /* now program new ones */ IF_ADDR_LOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } IF_ADDR_UNLOCK(ifp); if (mcnt) rxfilt |= RL_RXCFG_RX_MULTI; else rxfilt &= ~RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); CSR_WRITE_4(sc, RL_MAR0, hashes[0]); CSR_WRITE_4(sc, RL_MAR4, hashes[1]); } static void rl_reset(struct rl_softc *sc) { register int i; RL_LOCK_ASSERT(sc); CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); for (i = 0; i < RL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) break; } if (i == RL_TIMEOUT) device_printf(sc->rl_dev, "reset never completed!\n"); } /* * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int rl_probe(device_t dev) { struct rl_type *t; uint16_t devid, revid, vendor; int i; vendor = pci_get_vendor(dev); devid = pci_get_device(dev); revid = pci_get_revid(dev); if (vendor == RT_VENDORID && devid == RT_DEVICEID_8139) { if (revid == 0x20) { /* 8139C+, let re(4) take care of this device. */ return (ENXIO); } } t = rl_devs; for (i = 0; i < sizeof(rl_devs) / sizeof(rl_devs[0]); i++, t++) { if (vendor == t->rl_vid && devid == t->rl_did) { device_set_desc(dev, t->rl_name); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } struct rl_dmamap_arg { bus_addr_t rl_busaddr; }; static void rl_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct rl_dmamap_arg *ctx; if (error != 0) return; KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); ctx = (struct rl_dmamap_arg *)arg; ctx->rl_busaddr = segs[0].ds_addr; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int rl_attach(device_t dev) { uint8_t eaddr[ETHER_ADDR_LEN]; uint16_t as[3]; struct ifnet *ifp; struct rl_softc *sc; struct rl_type *t; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *children; int error = 0, i, rid; int unit; uint16_t rl_did = 0; char tn[32]; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->rl_dev = dev; sc->rl_twister_enable = 0; snprintf(tn, sizeof(tn), "dev.rl.%d.twister_enable", unit); TUNABLE_INT_FETCH(tn, &sc->rl_twister_enable); ctx = device_get_sysctl_ctx(sc->rl_dev); children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev)); SYSCTL_ADD_INT(ctx, children, OID_AUTO, "twister_enable", CTLFLAG_RD, &sc->rl_twister_enable, 0, ""); mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->rl_stat_callout, &sc->rl_mtx, 0); pci_enable_busmaster(dev); /* Map control/status registers. */ rid = RL_RID; sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid, RF_ACTIVE); if (sc->rl_res == NULL) { device_printf(dev, "couldn't map ports/memory\n"); error = ENXIO; goto fail; } #ifdef notdef /* * Detect the Realtek 8139B. For some reason, this chip is very * unstable when left to autoselect the media * The best workaround is to set the device to the required * media type or to set it to the 10 Meg speed. */ if ((rman_get_end(sc->rl_res) - rman_get_start(sc->rl_res)) == 0xFF) device_printf(dev, "Realtek 8139B detected. Warning, this may be unstable in autoselect mode\n"); #endif sc->rl_btag = rman_get_bustag(sc->rl_res); sc->rl_bhandle = rman_get_bushandle(sc->rl_res); /* Allocate interrupt */ rid = 0; sc->rl_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->rl_irq[0] == NULL) { device_printf(dev, "couldn't map interrupt\n"); error = ENXIO; goto fail; } /* * Reset the adapter. Only take the lock here as it's needed in * order to call rl_reset(). */ RL_LOCK(sc); rl_reset(sc); RL_UNLOCK(sc); sc->rl_eecmd_read = RL_EECMD_READ_6BIT; rl_read_eeprom(sc, (uint8_t *)&rl_did, 0, 1, 0); if (rl_did != 0x8129) sc->rl_eecmd_read = RL_EECMD_READ_8BIT; /* * Get station address from the EEPROM. */ rl_read_eeprom(sc, (uint8_t *)as, RL_EE_EADDR, 3, 0); for (i = 0; i < 3; i++) { eaddr[(i * 2) + 0] = as[i] & 0xff; eaddr[(i * 2) + 1] = as[i] >> 8; } /* * Now read the exact device type from the EEPROM to find * out if it's an 8129 or 8139. */ rl_read_eeprom(sc, (uint8_t *)&rl_did, RL_EE_PCI_DID, 1, 0); t = rl_devs; sc->rl_type = 0; while(t->rl_name != NULL) { if (rl_did == t->rl_did) { sc->rl_type = t->rl_basetype; break; } t++; } if (sc->rl_type == 0) { device_printf(dev, "unknown device ID: %x assuming 8139\n", rl_did); sc->rl_type = RL_8139; /* * Read RL_IDR register to get ethernet address as accessing * EEPROM may not extract correct address. */ for (i = 0; i < ETHER_ADDR_LEN; i++) eaddr[i] = CSR_READ_1(sc, RL_IDR0 + i); } if ((error = rl_dma_alloc(sc)) != 0) goto fail; ifp = sc->rl_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } /* Do MII setup */ if (mii_phy_probe(dev, &sc->rl_miibus, rl_ifmedia_upd, rl_ifmedia_sts)) { device_printf(dev, "MII without any phy!\n"); error = ENXIO; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = rl_ioctl; ifp->if_start = rl_start; ifp->if_init = rl_init; ifp->if_capabilities = IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->rl_irq[0], INTR_TYPE_NET | INTR_MPSAFE, NULL, rl_intr, sc, &sc->rl_intrhand[0]); if (error) { device_printf(sc->rl_dev, "couldn't set up irq\n"); ether_ifdetach(ifp); } fail: if (error) rl_detach(dev); return (error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int rl_detach(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->rl_ifp; KASSERT(mtx_initialized(&sc->rl_mtx), ("rl mutex not initialized")); #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { RL_LOCK(sc); rl_stop(sc); RL_UNLOCK(sc); callout_drain(&sc->rl_stat_callout); ether_ifdetach(ifp); } #if 0 sc->suspended = 1; #endif if (sc->rl_miibus) device_delete_child(dev, sc->rl_miibus); bus_generic_detach(dev); if (sc->rl_intrhand[0]) bus_teardown_intr(dev, sc->rl_irq[0], sc->rl_intrhand[0]); if (sc->rl_irq[0]) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq[0]); if (sc->rl_res) bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); if (ifp) if_free(ifp); rl_dma_free(sc); mtx_destroy(&sc->rl_mtx); return (0); } static int rl_dma_alloc(struct rl_softc *sc) { struct rl_dmamap_arg ctx; int error, i; /* * Allocate the parent bus DMA tag appropriate for PCI. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->rl_dev), /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT, 0, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rl_parent_tag); if (error) { device_printf(sc->rl_dev, "failed to create parent DMA tag.\n"); goto fail; } /* Create DMA tag for Rx memory block. */ error = bus_dma_tag_create(sc->rl_parent_tag, /* parent */ RL_RX_8139_BUF_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, 1, /* maxsize,nsegments */ RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rl_cdata.rl_rx_tag); if (error) { device_printf(sc->rl_dev, "failed to create Rx memory block DMA tag.\n"); goto fail; } /* Create DMA tag for Tx buffer. */ error = bus_dma_tag_create(sc->rl_parent_tag, /* parent */ RL_TX_8139_BUF_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, 1, /* maxsize, nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rl_cdata.rl_tx_tag); if (error) { device_printf(sc->rl_dev, "failed to create Tx DMA tag.\n"); goto fail; } /* * Allocate DMA'able memory and load DMA map for Rx memory block. */ error = bus_dmamem_alloc(sc->rl_cdata.rl_rx_tag, (void **)&sc->rl_cdata.rl_rx_buf, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->rl_cdata.rl_rx_dmamap); if (error != 0) { device_printf(sc->rl_dev, "failed to allocate Rx DMA memory block.\n"); goto fail; } ctx.rl_busaddr = 0; error = bus_dmamap_load(sc->rl_cdata.rl_rx_tag, sc->rl_cdata.rl_rx_dmamap, sc->rl_cdata.rl_rx_buf, RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ, rl_dmamap_cb, &ctx, BUS_DMA_NOWAIT); if (error != 0 || ctx.rl_busaddr == 0) { device_printf(sc->rl_dev, "could not load Rx DMA memory block.\n"); goto fail; } sc->rl_cdata.rl_rx_buf_paddr = ctx.rl_busaddr; /* Create DMA maps for Tx buffers. */ for (i = 0; i < RL_TX_LIST_CNT; i++) { sc->rl_cdata.rl_tx_chain[i] = NULL; sc->rl_cdata.rl_tx_dmamap[i] = NULL; error = bus_dmamap_create(sc->rl_cdata.rl_tx_tag, 0, &sc->rl_cdata.rl_tx_dmamap[i]); if (error != 0) { device_printf(sc->rl_dev, "could not create Tx dmamap.\n"); goto fail; } } /* Leave a few bytes before the start of the RX ring buffer. */ sc->rl_cdata.rl_rx_buf_ptr = sc->rl_cdata.rl_rx_buf; sc->rl_cdata.rl_rx_buf += RL_RX_8139_BUF_RESERVE; fail: return (error); } static void rl_dma_free(struct rl_softc *sc) { int i; /* Rx memory block. */ if (sc->rl_cdata.rl_rx_tag != NULL) { if (sc->rl_cdata.rl_rx_dmamap != NULL) bus_dmamap_unload(sc->rl_cdata.rl_rx_tag, sc->rl_cdata.rl_rx_dmamap); if (sc->rl_cdata.rl_rx_dmamap != NULL && sc->rl_cdata.rl_rx_buf_ptr != NULL) bus_dmamem_free(sc->rl_cdata.rl_rx_tag, sc->rl_cdata.rl_rx_buf_ptr, sc->rl_cdata.rl_rx_dmamap); sc->rl_cdata.rl_rx_buf_ptr = NULL; sc->rl_cdata.rl_rx_buf = NULL; sc->rl_cdata.rl_rx_dmamap = NULL; bus_dma_tag_destroy(sc->rl_cdata.rl_rx_tag); sc->rl_cdata.rl_tx_tag = NULL; } /* Tx buffers. */ if (sc->rl_cdata.rl_tx_tag != NULL) { for (i = 0; i < RL_TX_LIST_CNT; i++) { if (sc->rl_cdata.rl_tx_dmamap[i] != NULL) { bus_dmamap_destroy( sc->rl_cdata.rl_tx_tag, sc->rl_cdata.rl_tx_dmamap[i]); sc->rl_cdata.rl_tx_dmamap[i] = NULL; } bus_dma_tag_destroy(sc->rl_cdata.rl_tx_tag); sc->rl_cdata.rl_tx_tag = NULL; } } if (sc->rl_parent_tag != NULL) { bus_dma_tag_destroy(sc->rl_parent_tag); sc->rl_parent_tag = NULL; } } /* * Initialize the transmit descriptors. */ static int rl_list_tx_init(struct rl_softc *sc) { struct rl_chain_data *cd; int i; RL_LOCK_ASSERT(sc); cd = &sc->rl_cdata; for (i = 0; i < RL_TX_LIST_CNT; i++) { cd->rl_tx_chain[i] = NULL; CSR_WRITE_4(sc, RL_TXADDR0 + (i * sizeof(uint32_t)), 0x0000000); } sc->rl_cdata.cur_tx = 0; sc->rl_cdata.last_tx = 0; return (0); } static int rl_list_rx_init(struct rl_softc *sc) { RL_LOCK_ASSERT(sc); bzero(sc->rl_cdata.rl_rx_buf_ptr, RL_RXBUFLEN + RL_RX_8139_BUF_GUARD_SZ); bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, sc->rl_cdata.rl_rx_dmamap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. * * You know there's something wrong with a PCI bus-master chip design * when you have to use m_devget(). * * The receive operation is badly documented in the datasheet, so I'll * attempt to document it here. The driver provides a buffer area and * places its base address in the RX buffer start address register. * The chip then begins copying frames into the RX buffer. Each frame * is preceded by a 32-bit RX status word which specifies the length * of the frame and certain other status bits. Each frame (starting with * the status word) is also 32-bit aligned. The frame length is in the * first 16 bits of the status word; the lower 15 bits correspond with * the 'rx status register' mentioned in the datasheet. * * Note: to make the Alpha happy, the frame payload needs to be aligned * on a 32-bit boundary. To achieve this, we pass RL_ETHER_ALIGN (2 bytes) * as the offset argument to m_devget(). */ static void rl_rxeof(struct rl_softc *sc) { struct mbuf *m; struct ifnet *ifp = sc->rl_ifp; uint8_t *rxbufpos; int total_len = 0; int wrap = 0; uint32_t rxstat; uint16_t cur_rx; uint16_t limit; uint16_t max_bytes, rx_bytes = 0; RL_LOCK_ASSERT(sc); bus_dmamap_sync(sc->rl_cdata.rl_rx_tag, sc->rl_cdata.rl_rx_dmamap, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); cur_rx = (CSR_READ_2(sc, RL_CURRXADDR) + 16) % RL_RXBUFLEN; /* Do not try to read past this point. */ limit = CSR_READ_2(sc, RL_CURRXBUF) % RL_RXBUFLEN; if (limit < cur_rx) max_bytes = (RL_RXBUFLEN - cur_rx) + limit; else max_bytes = limit - cur_rx; while((CSR_READ_1(sc, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) { #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif rxbufpos = sc->rl_cdata.rl_rx_buf + cur_rx; rxstat = le32toh(*(uint32_t *)rxbufpos); /* * Here's a totally undocumented fact for you. When the * RealTek chip is in the process of copying a packet into * RAM for you, the length will be 0xfff0. If you spot a * packet header with this value, you need to stop. The * datasheet makes absolutely no mention of this and * RealTek should be shot for this. */ total_len = rxstat >> 16; if (total_len == RL_RXSTAT_UNFINISHED) break; if (!(rxstat & RL_RXSTAT_RXOK) || total_len < ETHER_MIN_LEN || total_len > ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN) { ifp->if_ierrors++; rl_init_locked(sc); return; } /* No errors; receive the packet. */ rx_bytes += total_len + 4; /* * XXX The RealTek chip includes the CRC with every * received frame, and there's no way to turn this * behavior off (at least, I can't find anything in * the manual that explains how to do it) so we have * to trim off the CRC manually. */ total_len -= ETHER_CRC_LEN; /* * Avoid trying to read more bytes than we know * the chip has prepared for us. */ if (rx_bytes > max_bytes) break; rxbufpos = sc->rl_cdata.rl_rx_buf + ((cur_rx + sizeof(uint32_t)) % RL_RXBUFLEN); if (rxbufpos == (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN)) rxbufpos = sc->rl_cdata.rl_rx_buf; wrap = (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN) - rxbufpos; if (total_len > wrap) { m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, NULL); if (m != NULL) m_copyback(m, wrap, total_len - wrap, sc->rl_cdata.rl_rx_buf); cur_rx = (total_len - wrap + ETHER_CRC_LEN); } else { m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, NULL); cur_rx += total_len + 4 + ETHER_CRC_LEN; } /* Round up to 32-bit boundary. */ cur_rx = (cur_rx + 3) & ~3; CSR_WRITE_2(sc, RL_CURRXADDR, cur_rx - 16); if (m == NULL) { ifp->if_iqdrops++; continue; } ifp->if_ipackets++; RL_UNLOCK(sc); (*ifp->if_input)(ifp, m); RL_LOCK(sc); } /* No need to sync Rx memory block as we didn't modify it. */ } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void rl_txeof(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; uint32_t txstat; RL_LOCK_ASSERT(sc); /* * Go through our tx list and free mbufs for those * frames that have been uploaded. */ do { if (RL_LAST_TXMBUF(sc) == NULL) break; txstat = CSR_READ_4(sc, RL_LAST_TXSTAT(sc)); if (!(txstat & (RL_TXSTAT_TX_OK| RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT))) break; ifp->if_collisions += (txstat & RL_TXSTAT_COLLCNT) >> 24; bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, RL_LAST_DMAMAP(sc), BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->rl_cdata.rl_tx_tag, RL_LAST_DMAMAP(sc)); m_freem(RL_LAST_TXMBUF(sc)); RL_LAST_TXMBUF(sc) = NULL; /* * If there was a transmit underrun, bump the TX threshold. * Make sure not to overflow the 63 * 32byte we can address * with the 6 available bit. */ if ((txstat & RL_TXSTAT_TX_UNDERRUN) && (sc->rl_txthresh < 2016)) sc->rl_txthresh += 32; if (txstat & RL_TXSTAT_TX_OK) ifp->if_opackets++; else { int oldthresh; ifp->if_oerrors++; if ((txstat & RL_TXSTAT_TXABRT) || (txstat & RL_TXSTAT_OUTOFWIN)) CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); oldthresh = sc->rl_txthresh; /* error recovery */ rl_init_locked(sc); /* restore original threshold */ sc->rl_txthresh = oldthresh; return; } RL_INC(sc->rl_cdata.last_tx); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } while (sc->rl_cdata.last_tx != sc->rl_cdata.cur_tx); if (RL_LAST_TXMBUF(sc) == NULL) sc->rl_watchdog_timer = 0; } static void rl_twister_update(struct rl_softc *sc) { uint16_t linktest; /* * Table provided by RealTek (Kinston ) for * Linux driver. Values undocumented otherwise. */ static const uint32_t param[4][4] = { {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} }; /* * Tune the so-called twister registers of the RTL8139. These * are used to compensate for impedance mismatches. The * method for tuning these registers is undocumented and the * following procedure is collected from public sources. */ switch (sc->rl_twister) { case CHK_LINK: /* * If we have a sufficient link, then we can proceed in * the state machine to the next stage. If not, then * disable further tuning after writing sane defaults. */ if (CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_LINK_OK) { CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_OFF_CMD); sc->rl_twister = FIND_ROW; } else { CSR_WRITE_2(sc, RL_CSCFG, RL_CSCFG_LINK_DOWN_CMD); CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST); CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF); CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF); sc->rl_twister = DONE; } break; case FIND_ROW: /* * Read how long it took to see the echo to find the tuning * row to use. */ linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS; if (linktest == RL_CSCFG_ROW3) sc->rl_twist_row = 3; else if (linktest == RL_CSCFG_ROW2) sc->rl_twist_row = 2; else if (linktest == RL_CSCFG_ROW1) sc->rl_twist_row = 1; else sc->rl_twist_row = 0; sc->rl_twist_col = 0; sc->rl_twister = SET_PARAM; break; case SET_PARAM: if (sc->rl_twist_col == 0) CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET); CSR_WRITE_4(sc, RL_PARA7C, param[sc->rl_twist_row][sc->rl_twist_col]); if (++sc->rl_twist_col == 4) { if (sc->rl_twist_row == 3) sc->rl_twister = RECHK_LONG; else sc->rl_twister = DONE; } break; case RECHK_LONG: /* * For long cables, we have to double check to make sure we * don't mistune. */ linktest = CSR_READ_2(sc, RL_CSCFG) & RL_CSCFG_STATUS; if (linktest == RL_CSCFG_ROW3) sc->rl_twister = DONE; else { CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_RETUNE); sc->rl_twister = RETUNE; } break; case RETUNE: /* Retune for a shorter cable (try column 2) */ CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_CBL_TEST); CSR_WRITE_4(sc, RL_PARA78, RL_PARA78_DEF); CSR_WRITE_4(sc, RL_PARA7C, RL_PARA7C_DEF); CSR_WRITE_4(sc, RL_NWAYTST, RL_NWAYTST_RESET); sc->rl_twist_row--; sc->rl_twist_col = 0; sc->rl_twister = SET_PARAM; break; case DONE: break; } } static void rl_tick(void *xsc) { struct rl_softc *sc = xsc; struct mii_data *mii; int ticks; RL_LOCK_ASSERT(sc); /* * If we're doing the twister cable calibration, then we need to defer * watchdog timeouts. This is a no-op in normal operations, but * can falsely trigger when the cable calibration takes a while and * there was traffic ready to go when rl was started. * * We don't defer mii_tick since that updates the mii status, which * helps the twister process, at least according to similar patches * for the Linux driver I found online while doing the fixes. Worst * case is a few extra mii reads during calibration. */ mii = device_get_softc(sc->rl_miibus); mii_tick(mii); + if ((sc->rl_flags & RL_FLAG_LINK) == 0) + rl_miibus_statchg(sc->rl_dev); if (sc->rl_twister_enable) { if (sc->rl_twister == DONE) rl_watchdog(sc); else rl_twister_update(sc); if (sc->rl_twister == DONE) ticks = hz; else ticks = hz / 10; } else { rl_watchdog(sc); ticks = hz; } callout_reset(&sc->rl_stat_callout, ticks, rl_tick, sc); } #ifdef DEVICE_POLLING static void rl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; RL_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) rl_poll_locked(ifp, cmd, count); RL_UNLOCK(sc); } static void rl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; RL_LOCK_ASSERT(sc); sc->rxcycles = count; rl_rxeof(sc); rl_txeof(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) rl_start_locked(ifp); if (cmd == POLL_AND_CHECK_STATUS) { uint16_t status; /* We should also check the status register. */ status = CSR_READ_2(sc, RL_ISR); if (status == 0xffff) return; if (status != 0) CSR_WRITE_2(sc, RL_ISR, status); /* XXX We should check behaviour on receiver stalls. */ if (status & RL_ISR_SYSTEM_ERR) rl_init_locked(sc); } } #endif /* DEVICE_POLLING */ static void rl_intr(void *arg) { struct rl_softc *sc = arg; struct ifnet *ifp = sc->rl_ifp; uint16_t status; RL_LOCK(sc); if (sc->suspended) goto done_locked; #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) goto done_locked; #endif for (;;) { status = CSR_READ_2(sc, RL_ISR); /* If the card has gone away, the read returns 0xffff. */ if (status == 0xffff) break; if (status != 0) CSR_WRITE_2(sc, RL_ISR, status); if ((status & RL_INTRS) == 0) break; if (status & RL_ISR_RX_OK) rl_rxeof(sc); if (status & RL_ISR_RX_ERR) rl_rxeof(sc); if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR)) rl_txeof(sc); if (status & RL_ISR_SYSTEM_ERR) rl_init_locked(sc); } if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) rl_start_locked(ifp); done_locked: RL_UNLOCK(sc); } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int rl_encap(struct rl_softc *sc, struct mbuf **m_head) { struct mbuf *m; bus_dma_segment_t txsegs[1]; int error, nsegs, padlen; RL_LOCK_ASSERT(sc); m = *m_head; padlen = 0; /* * Hardware doesn't auto-pad, so we have to make sure * pad short frames out to the minimum frame length. */ if (m->m_pkthdr.len < RL_MIN_FRAMELEN) padlen = RL_MIN_FRAMELEN - m->m_pkthdr.len; /* * The RealTek is brain damaged and wants longword-aligned * TX buffers, plus we can only have one fragment buffer * per packet. We have to copy pretty much all the time. */ if (m->m_next != NULL || (mtod(m, uintptr_t) & 3) != 0 || (padlen > 0 && M_TRAILINGSPACE(m) < padlen)) { m = m_defrag(*m_head, M_DONTWAIT); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOMEM); } } *m_head = m; if (padlen > 0) { /* * Make security-conscious people happy: zero out the * bytes in the pad area, since we don't know what * this mbuf cluster buffer's previous user might * have left in it. */ bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); m->m_pkthdr.len += padlen; m->m_len = m->m_pkthdr.len; } error = bus_dmamap_load_mbuf_sg(sc->rl_cdata.rl_tx_tag, RL_CUR_DMAMAP(sc), m, txsegs, &nsegs, 0); if (error != 0) return (error); if (nsegs == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } RL_CUR_TXMBUF(sc) = m; bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, RL_CUR_DMAMAP(sc), BUS_DMASYNC_PREWRITE); CSR_WRITE_4(sc, RL_CUR_TXADDR(sc), RL_ADDR_LO(txsegs[0].ds_addr)); return (0); } /* * Main transmit routine. */ static void rl_start(struct ifnet *ifp) { struct rl_softc *sc = ifp->if_softc; RL_LOCK(sc); rl_start_locked(ifp); RL_UNLOCK(sc); } static void rl_start_locked(struct ifnet *ifp) { struct rl_softc *sc = ifp->if_softc; struct mbuf *m_head = NULL; RL_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || (sc->rl_flags & RL_FLAG_LINK) == 0) return; while (RL_CUR_TXMBUF(sc) == NULL) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (rl_encap(sc, &m_head)) { if (m_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } /* Pass a copy of this mbuf chain to the bpf subsystem. */ BPF_MTAP(ifp, RL_CUR_TXMBUF(sc)); /* Transmit the frame. */ CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc), RL_TXTHRESH(sc->rl_txthresh) | RL_CUR_TXMBUF(sc)->m_pkthdr.len); RL_INC(sc->rl_cdata.cur_tx); /* Set a timeout in case the chip goes out to lunch. */ sc->rl_watchdog_timer = 5; } /* * We broke out of the loop because all our TX slots are * full. Mark the NIC as busy until it drains some of the * packets from the queue. */ if (RL_CUR_TXMBUF(sc) != NULL) ifp->if_drv_flags |= IFF_DRV_OACTIVE; } static void rl_init(void *xsc) { struct rl_softc *sc = xsc; RL_LOCK(sc); rl_init_locked(sc); RL_UNLOCK(sc); } static void rl_init_locked(struct rl_softc *sc) { struct ifnet *ifp = sc->rl_ifp; struct mii_data *mii; uint32_t rxcfg = 0; uint32_t eaddr[2]; RL_LOCK_ASSERT(sc); mii = device_get_softc(sc->rl_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ rl_stop(sc); rl_reset(sc); if (sc->rl_twister_enable) { /* * Reset twister register tuning state. The twister * registers and their tuning are undocumented, but * are necessary to cope with bad links. rl_twister = * DONE here will disable this entirely. */ sc->rl_twister = CHK_LINK; } /* * Init our MAC address. Even though the chipset * documentation doesn't mention it, we need to enter "Config * register write enable" mode to modify the ID registers. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG); bzero(eaddr, sizeof(eaddr)); bcopy(IF_LLADDR(sc->rl_ifp), eaddr, ETHER_ADDR_LEN); CSR_WRITE_STREAM_4(sc, RL_IDR0, eaddr[0]); CSR_WRITE_STREAM_4(sc, RL_IDR4, eaddr[1]); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); /* Init the RX memory block pointer register. */ CSR_WRITE_4(sc, RL_RXADDR, sc->rl_cdata.rl_rx_buf_paddr + RL_RX_8139_BUF_RESERVE); /* Init TX descriptors. */ rl_list_tx_init(sc); /* Init Rx memory block. */ rl_list_rx_init(sc); /* * Enable transmit and receive. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); /* * Set the initial TX and RX configuration. */ CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG); /* Set the individual bit to receive frames for this host only. */ rxcfg = CSR_READ_4(sc, RL_RXCFG); rxcfg |= RL_RXCFG_RX_INDIV; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { rxcfg |= RL_RXCFG_RX_ALLPHYS; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } else { rxcfg &= ~RL_RXCFG_RX_ALLPHYS; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } /* Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { rxcfg |= RL_RXCFG_RX_BROAD; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } else { rxcfg &= ~RL_RXCFG_RX_BROAD; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } /* Program the multicast filter, if necessary. */ rl_setmulti(sc); #ifdef DEVICE_POLLING /* Disable interrupts if we are polling. */ if (ifp->if_capenable & IFCAP_POLLING) CSR_WRITE_2(sc, RL_IMR, 0); else #endif /* Enable interrupts. */ CSR_WRITE_2(sc, RL_IMR, RL_INTRS); /* Set initial TX threshold */ sc->rl_txthresh = RL_TX_THRESH_INIT; /* Start RX/TX process. */ CSR_WRITE_4(sc, RL_MISSEDPKT, 0); /* Enable receiver and transmitter. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); sc->rl_flags &= ~RL_FLAG_LINK; mii_mediachg(mii); CSR_WRITE_1(sc, RL_CFG1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; callout_reset(&sc->rl_stat_callout, hz, rl_tick, sc); } /* * Set media options. */ static int rl_ifmedia_upd(struct ifnet *ifp) { struct rl_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->rl_miibus); RL_LOCK(sc); mii_mediachg(mii); RL_UNLOCK(sc); return (0); } /* * Report current media status. */ static void rl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct rl_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->rl_miibus); RL_LOCK(sc); mii_pollstat(mii); RL_UNLOCK(sc); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int rl_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; struct rl_softc *sc = ifp->if_softc; int error = 0; switch (command) { case SIOCSIFFLAGS: RL_LOCK(sc); if (ifp->if_flags & IFF_UP) { rl_init_locked(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) rl_stop(sc); } RL_UNLOCK(sc); error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: RL_LOCK(sc); rl_setmulti(sc); RL_UNLOCK(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->rl_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: #ifdef DEVICE_POLLING if (ifr->ifr_reqcap & IFCAP_POLLING && !(ifp->if_capenable & IFCAP_POLLING)) { error = ether_poll_register(rl_poll, ifp); if (error) return(error); RL_LOCK(sc); /* Disable interrupts */ CSR_WRITE_2(sc, RL_IMR, 0x0000); ifp->if_capenable |= IFCAP_POLLING; RL_UNLOCK(sc); return (error); } if (!(ifr->ifr_reqcap & IFCAP_POLLING) && ifp->if_capenable & IFCAP_POLLING) { error = ether_poll_deregister(ifp); /* Enable interrupts. */ RL_LOCK(sc); CSR_WRITE_2(sc, RL_IMR, RL_INTRS); ifp->if_capenable &= ~IFCAP_POLLING; RL_UNLOCK(sc); return (error); } #endif /* DEVICE_POLLING */ break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void rl_watchdog(struct rl_softc *sc) { RL_LOCK_ASSERT(sc); if (sc->rl_watchdog_timer == 0 || --sc->rl_watchdog_timer >0) return; device_printf(sc->rl_dev, "watchdog timeout\n"); sc->rl_ifp->if_oerrors++; rl_txeof(sc); rl_rxeof(sc); rl_init_locked(sc); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void rl_stop(struct rl_softc *sc) { register int i; struct ifnet *ifp = sc->rl_ifp; RL_LOCK_ASSERT(sc); sc->rl_watchdog_timer = 0; callout_stop(&sc->rl_stat_callout); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->rl_flags &= ~RL_FLAG_LINK; CSR_WRITE_1(sc, RL_COMMAND, 0x00); CSR_WRITE_2(sc, RL_IMR, 0x0000); for (i = 0; i < RL_TIMEOUT; i++) { DELAY(10); if ((CSR_READ_1(sc, RL_COMMAND) & (RL_CMD_RX_ENB | RL_CMD_TX_ENB)) == 0) break; } if (i == RL_TIMEOUT) device_printf(sc->rl_dev, "Unable to stop Tx/Rx MAC\n"); /* * Free the TX list buffers. */ for (i = 0; i < RL_TX_LIST_CNT; i++) { if (sc->rl_cdata.rl_tx_chain[i] != NULL) { if (sc->rl_cdata.rl_tx_chain[i] != NULL) { bus_dmamap_sync(sc->rl_cdata.rl_tx_tag, sc->rl_cdata.rl_tx_dmamap[i], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->rl_cdata.rl_tx_tag, sc->rl_cdata.rl_tx_dmamap[i]); m_freem(sc->rl_cdata.rl_tx_chain[i]); sc->rl_cdata.rl_tx_chain[i] = NULL; } CSR_WRITE_4(sc, RL_TXADDR0 + (i * sizeof(uint32_t)), 0x0000000); } } } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int rl_suspend(device_t dev) { struct rl_softc *sc; sc = device_get_softc(dev); RL_LOCK(sc); rl_stop(sc); sc->suspended = 1; RL_UNLOCK(sc); return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int rl_resume(device_t dev) { struct rl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->rl_ifp; RL_LOCK(sc); /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) rl_init_locked(sc); sc->suspended = 0; RL_UNLOCK(sc); return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static int rl_shutdown(device_t dev) { struct rl_softc *sc; sc = device_get_softc(dev); RL_LOCK(sc); rl_stop(sc); RL_UNLOCK(sc); return (0); } Index: projects/cambria/sys/security/mac/mac_process.c =================================================================== --- projects/cambria/sys/security/mac/mac_process.c (revision 186459) +++ projects/cambria/sys/security/mac/mac_process.c (revision 186460) @@ -1,533 +1,531 @@ /*- * Copyright (c) 1999-2002, 2008 Robert N. M. Watson * Copyright (c) 2001 Ilmar S. Habibulin * Copyright (c) 2001-2003 Networks Associates Technology, Inc. * Copyright (c) 2005 Samy Al Bahra * Copyright (c) 2006 SPARTA, Inc. * Copyright (c) 2008 Apple Inc. * All rights reserved. * * This software was developed by Robert Watson and Ilmar Habibulin for the * TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * This software was enhanced by SPARTA ISSO under SPAWAR contract * N66001-04-C-6019 ("SEFOS"). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_mac.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int mac_mmap_revocation = 1; SYSCTL_INT(_security_mac, OID_AUTO, mmap_revocation, CTLFLAG_RW, &mac_mmap_revocation, 0, "Revoke mmap access to files on subject " "relabel"); static int mac_mmap_revocation_via_cow = 0; SYSCTL_INT(_security_mac, OID_AUTO, mmap_revocation_via_cow, CTLFLAG_RW, &mac_mmap_revocation_via_cow, 0, "Revoke mmap access to files via " "copy-on-write semantics, or by removing all write access"); static void mac_proc_vm_revoke_recurse(struct thread *td, struct ucred *cred, struct vm_map *map); static struct label * mac_proc_label_alloc(void) { struct label *label; label = mac_labelzone_alloc(M_WAITOK); MAC_PERFORM(proc_init_label, label); return (label); } void mac_proc_init(struct proc *p) { if (mac_labeled & MPC_OBJECT_PROC) p->p_label = mac_proc_label_alloc(); else p->p_label = NULL; } static void mac_proc_label_free(struct label *label) { MAC_PERFORM(proc_destroy_label, label); mac_labelzone_free(label); } void mac_proc_destroy(struct proc *p) { if (p->p_label != NULL) { mac_proc_label_free(p->p_label); p->p_label = NULL; } } void mac_thread_userret(struct thread *td) { MAC_PERFORM(thread_userret, td); } int mac_execve_enter(struct image_params *imgp, struct mac *mac_p) { struct label *label; struct mac mac; char *buffer; int error; if (mac_p == NULL) return (0); if (!(mac_labeled & MPC_OBJECT_CRED)) return (EINVAL); error = copyin(mac_p, &mac, sizeof(mac)); if (error) return (error); error = mac_check_structmac_consistent(&mac); if (error) return (error); buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK); error = copyinstr(mac.m_string, buffer, mac.m_buflen, NULL); if (error) { free(buffer, M_MACTEMP); return (error); } label = mac_cred_label_alloc(); error = mac_cred_internalize_label(label, buffer); free(buffer, M_MACTEMP); if (error) { mac_cred_label_free(label); return (error); } imgp->execlabel = label; return (0); } void mac_execve_exit(struct image_params *imgp) { if (imgp->execlabel != NULL) { mac_cred_label_free(imgp->execlabel); imgp->execlabel = NULL; } } void mac_execve_interpreter_enter(struct vnode *interpvp, struct label **interpvplabel) { if (mac_labeled & MPC_OBJECT_VNODE) { *interpvplabel = mac_vnode_label_alloc(); mac_vnode_copy_label(interpvp->v_label, *interpvplabel); } else *interpvplabel = NULL; } void mac_execve_interpreter_exit(struct label *interpvplabel) { if (interpvplabel != NULL) mac_vnode_label_free(interpvplabel); } /* * When relabeling a process, call out to the policies for the maximum * permission allowed for each object type we know about in its memory space, * and revoke access (in the least surprising ways we know) when necessary. * The process lock is not held here. */ void mac_proc_vm_revoke(struct thread *td) { struct ucred *cred; PROC_LOCK(td->td_proc); cred = crhold(td->td_proc->p_ucred); PROC_UNLOCK(td->td_proc); /* XXX freeze all other threads */ mac_proc_vm_revoke_recurse(td, cred, &td->td_proc->p_vmspace->vm_map); /* XXX allow other threads to continue */ crfree(cred); } static __inline const char * prot2str(vm_prot_t prot) { switch (prot & VM_PROT_ALL) { case VM_PROT_READ: return ("r--"); case VM_PROT_READ | VM_PROT_WRITE: return ("rw-"); case VM_PROT_READ | VM_PROT_EXECUTE: return ("r-x"); case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE: return ("rwx"); case VM_PROT_WRITE: return ("-w-"); case VM_PROT_EXECUTE: return ("--x"); case VM_PROT_WRITE | VM_PROT_EXECUTE: return ("-wx"); default: return ("---"); } } static void mac_proc_vm_revoke_recurse(struct thread *td, struct ucred *cred, struct vm_map *map) { struct vm_map_entry *vme; int vfslocked, result; vm_prot_t revokeperms; vm_object_t backing_object, object; vm_ooffset_t offset; struct vnode *vp; struct mount *mp; if (!mac_mmap_revocation) return; - vm_map_lock_read(map); + vm_map_lock(map); for (vme = map->header.next; vme != &map->header; vme = vme->next) { if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { mac_proc_vm_revoke_recurse(td, cred, vme->object.sub_map); continue; } /* * Skip over entries that obviously are not shared. */ if (vme->eflags & (MAP_ENTRY_COW | MAP_ENTRY_NOSYNC) || !vme->max_protection) continue; /* * Drill down to the deepest backing object. */ offset = vme->offset; object = vme->object.vm_object; if (object == NULL) continue; VM_OBJECT_LOCK(object); while ((backing_object = object->backing_object) != NULL) { VM_OBJECT_LOCK(backing_object); offset += object->backing_object_offset; VM_OBJECT_UNLOCK(object); object = backing_object; } VM_OBJECT_UNLOCK(object); /* * At the moment, vm_maps and objects aren't considered by * the MAC system, so only things with backing by a normal * object (read: vnodes) are checked. */ if (object->type != OBJT_VNODE) continue; vp = (struct vnode *)object->handle; vfslocked = VFS_LOCK_GIANT(vp->v_mount); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); result = vme->max_protection; mac_vnode_check_mmap_downgrade(cred, vp, &result); VOP_UNLOCK(vp, 0); /* * Find out what maximum protection we may be allowing now * but a policy needs to get removed. */ revokeperms = vme->max_protection & ~result; if (!revokeperms) { VFS_UNLOCK_GIANT(vfslocked); continue; } printf("pid %ld: revoking %s perms from %#lx:%ld " "(max %s/cur %s)\n", (long)td->td_proc->p_pid, prot2str(revokeperms), (u_long)vme->start, (long)(vme->end - vme->start), prot2str(vme->max_protection), prot2str(vme->protection)); - vm_map_lock_upgrade(map); /* * This is the really simple case: if a map has more * max_protection than is allowed, but it's not being * actually used (that is, the current protection is still * allowed), we can just wipe it out and do nothing more. */ if ((vme->protection & revokeperms) == 0) { vme->max_protection -= revokeperms; } else { if (revokeperms & VM_PROT_WRITE) { /* * In the more complicated case, flush out all * pending changes to the object then turn it * copy-on-write. */ vm_object_reference(object); (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); VM_OBJECT_LOCK(object); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + vme->end - vme->start + PAGE_MASK), OBJPC_SYNC); VM_OBJECT_UNLOCK(object); VOP_UNLOCK(vp, 0); vn_finished_write(mp); vm_object_deallocate(object); /* * Why bother if there's no read permissions * anymore? For the rest, we need to leave * the write permissions on for COW, or * remove them entirely if configured to. */ if (!mac_mmap_revocation_via_cow) { vme->max_protection &= ~VM_PROT_WRITE; vme->protection &= ~VM_PROT_WRITE; } if ((revokeperms & VM_PROT_READ) == 0) vme->eflags |= MAP_ENTRY_COW | MAP_ENTRY_NEEDS_COPY; } if (revokeperms & VM_PROT_EXECUTE) { vme->max_protection &= ~VM_PROT_EXECUTE; vme->protection &= ~VM_PROT_EXECUTE; } if (revokeperms & VM_PROT_READ) { vme->max_protection = 0; vme->protection = 0; } pmap_protect(map->pmap, vme->start, vme->end, vme->protection & ~revokeperms); vm_map_simplify_entry(map, vme); } - vm_map_lock_downgrade(map); VFS_UNLOCK_GIANT(vfslocked); } - vm_map_unlock_read(map); + vm_map_unlock(map); } int mac_proc_check_debug(struct ucred *cred, struct proc *p) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_debug, cred, p); return (error); } int mac_proc_check_sched(struct ucred *cred, struct proc *p) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_sched, cred, p); return (error); } int mac_proc_check_signal(struct ucred *cred, struct proc *p, int signum) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_signal, cred, p, signum); return (error); } int mac_proc_check_setuid(struct proc *p, struct ucred *cred, uid_t uid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setuid, cred, uid); return (error); } int mac_proc_check_seteuid(struct proc *p, struct ucred *cred, uid_t euid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_seteuid, cred, euid); return (error); } int mac_proc_check_setgid(struct proc *p, struct ucred *cred, gid_t gid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setgid, cred, gid); return (error); } int mac_proc_check_setegid(struct proc *p, struct ucred *cred, gid_t egid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setegid, cred, egid); return (error); } int mac_proc_check_setgroups(struct proc *p, struct ucred *cred, int ngroups, gid_t *gidset) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setgroups, cred, ngroups, gidset); return (error); } int mac_proc_check_setreuid(struct proc *p, struct ucred *cred, uid_t ruid, uid_t euid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setreuid, cred, ruid, euid); return (error); } int mac_proc_check_setregid(struct proc *proc, struct ucred *cred, gid_t rgid, gid_t egid) { int error; PROC_LOCK_ASSERT(proc, MA_OWNED); MAC_CHECK(proc_check_setregid, cred, rgid, egid); return (error); } int mac_proc_check_setresuid(struct proc *p, struct ucred *cred, uid_t ruid, uid_t euid, uid_t suid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setresuid, cred, ruid, euid, suid); return (error); } int mac_proc_check_setresgid(struct proc *p, struct ucred *cred, gid_t rgid, gid_t egid, gid_t sgid) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_setresgid, cred, rgid, egid, sgid); return (error); } int mac_proc_check_wait(struct ucred *cred, struct proc *p) { int error; PROC_LOCK_ASSERT(p, MA_OWNED); MAC_CHECK(proc_check_wait, cred, p); return (error); } Index: projects/cambria/sys/sparc64/sparc64/mp_machdep.c =================================================================== --- projects/cambria/sys/sparc64/sparc64/mp_machdep.c (revision 186459) +++ projects/cambria/sys/sparc64/sparc64/mp_machdep.c (revision 186460) @@ -1,610 +1,594 @@ /*- * Copyright (c) 1997 Berkeley Software Design, Inc. 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. Berkeley Software Design Inc's name may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``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 BERKELEY SOFTWARE DESIGN INC 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. * * from BSDI: locore.s,v 1.36.2.15 1999/08/23 22:34:41 cp Exp */ /*- * Copyright (c) 2002 Jake Burkholder. * Copyright (c) 2007 Marius Strobl * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SUNW_STARTCPU "SUNW,start-cpu" #define SUNW_STOPSELF "SUNW,stop-self" static ih_func_t cpu_ipi_ast; static ih_func_t cpu_ipi_preempt; static ih_func_t cpu_ipi_stop; /* * Argument area used to pass data to non-boot processors as they start up. * This must be statically initialized with a known invalid CPU module ID, * since the other processors will use it before the boot CPU enters the * kernel. */ struct cpu_start_args cpu_start_args = { 0, -1, -1, 0, 0, 0 }; struct ipi_cache_args ipi_cache_args; struct ipi_tlb_args ipi_tlb_args; struct pcb stoppcbs[MAXCPU]; struct mtx ipi_mtx; cpu_ipi_selected_t *cpu_ipi_selected; static vm_offset_t mp_tramp; static u_int cpuid_to_mid[MAXCPU]; -static int has_stopself; static int isjbus; static volatile u_int shutdown_cpus; static void cpu_mp_unleash(void *v); static void spitfire_ipi_send(u_int mid, u_long d0, u_long d1, u_long d2); static void sun4u_startcpu(phandle_t cpu, void *func, u_long arg); -static void sun4u_stopself(void); static cpu_ipi_selected_t cheetah_ipi_selected; static cpu_ipi_selected_t spitfire_ipi_selected; SYSINIT(cpu_mp_unleash, SI_SUB_SMP, SI_ORDER_FIRST, cpu_mp_unleash, NULL); CTASSERT(MAXCPU <= IDR_CHEETAH_MAX_BN_PAIRS); CTASSERT(MAXCPU <= sizeof(u_int) * NBBY); CTASSERT(MAXCPU <= sizeof(int) * NBBY); void mp_init(void) { struct tte *tp; int i; mp_tramp = (vm_offset_t)OF_claim(NULL, PAGE_SIZE, PAGE_SIZE); if (mp_tramp == (vm_offset_t)-1) panic("%s", __func__); bcopy(mp_tramp_code, (void *)mp_tramp, mp_tramp_code_len); *(vm_offset_t *)(mp_tramp + mp_tramp_tlb_slots) = kernel_tlb_slots; *(vm_offset_t *)(mp_tramp + mp_tramp_func) = (vm_offset_t)mp_startup; tp = (struct tte *)(mp_tramp + mp_tramp_code_len); for (i = 0; i < kernel_tlb_slots; i++) { tp[i].tte_vpn = TV_VPN(kernel_tlbs[i].te_va, TS_4M); tp[i].tte_data = TD_V | TD_4M | TD_PA(kernel_tlbs[i].te_pa) | TD_L | TD_CP | TD_CV | TD_P | TD_W; } for (i = 0; i < PAGE_SIZE; i += sizeof(vm_offset_t)) flush(mp_tramp + i); /* * On UP systems cpu_ipi_selected() can be called while * cpu_mp_start() wasn't so initialize these here. */ if (cpu_impl == CPU_IMPL_ULTRASPARCIIIi || cpu_impl == CPU_IMPL_ULTRASPARCIIIip) isjbus = 1; if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) cpu_ipi_selected = cheetah_ipi_selected; else cpu_ipi_selected = spitfire_ipi_selected; } /* * Probe for other CPUs. */ void cpu_mp_setmaxid(void) { char buf[128]; phandle_t child; u_int cpus; all_cpus = 1 << curcpu; mp_ncpus = 1; cpus = 0; for (child = OF_child(OF_peer(0)); child != 0; child = OF_peer(child)) if (OF_getprop(child, "device_type", buf, sizeof(buf)) > 0 && strcmp(buf, "cpu") == 0) cpus++; mp_maxid = cpus - 1; } int cpu_mp_probe(void) { return (mp_maxid > 0); } struct cpu_group * cpu_topo(void) { return (smp_topo_none()); } static void sun4u_startcpu(phandle_t cpu, void *func, u_long arg) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t cpu; cell_t func; cell_t arg; } args = { (cell_t)SUNW_STARTCPU, 3, }; args.cpu = cpu; args.func = (cell_t)func; args.arg = (cell_t)arg; ofw_entry(&args); } /* - * Stop the calling CPU. - */ -static void -sun4u_stopself(void) -{ - static struct { - cell_t name; - cell_t nargs; - cell_t nreturns; - } args = { - (cell_t)SUNW_STOPSELF, - }; - - ofw_exit(&args); - panic("%s: failed.", __func__); -} - -/* * Fire up any non-boot processors. */ void cpu_mp_start(void) { char buf[128]; volatile struct cpu_start_args *csa; struct pcpu *pc; register_t s; vm_offset_t va; phandle_t child; u_int mid; u_int clock; u_int cpuid; mtx_init(&ipi_mtx, "ipi", NULL, MTX_SPIN); - if (OF_test(SUNW_STOPSELF) == 0) - has_stopself = 1; - intr_setup(PIL_AST, cpu_ipi_ast, -1, NULL, NULL); intr_setup(PIL_RENDEZVOUS, (ih_func_t *)smp_rendezvous_action, -1, NULL, NULL); intr_setup(PIL_STOP, cpu_ipi_stop, -1, NULL, NULL); intr_setup(PIL_PREEMPT, cpu_ipi_preempt, -1, NULL, NULL); cpuid_to_mid[curcpu] = PCPU_GET(mid); csa = &cpu_start_args; for (child = OF_child(OF_peer(0)); child != 0 && mp_ncpus <= MAXCPU; child = OF_peer(child)) { if (OF_getprop(child, "device_type", buf, sizeof(buf)) <= 0 || strcmp(buf, "cpu") != 0) continue; if (OF_getprop(child, cpu_impl < CPU_IMPL_ULTRASPARCIII ? "upa-portid" : "portid", &mid, sizeof(mid)) <= 0) panic("%s: can't get module ID", __func__); if (mid == PCPU_GET(mid)) continue; if (OF_getprop(child, "clock-frequency", &clock, sizeof(clock)) <= 0) panic("%s: can't get clock", __func__); if (clock != PCPU_GET(clock)) hardclock_use_stick = 1; csa->csa_state = 0; sun4u_startcpu(child, (void *)mp_tramp, 0); s = intr_disable(); while (csa->csa_state != CPU_TICKSYNC) ; membar(StoreLoad); csa->csa_tick = rd(tick); if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) { while (csa->csa_state != CPU_STICKSYNC) ; membar(StoreLoad); csa->csa_stick = rdstick(); } while (csa->csa_state != CPU_INIT) ; csa->csa_tick = csa->csa_stick = 0; intr_restore(s); cpuid = mp_ncpus++; cpuid_to_mid[cpuid] = mid; cpu_identify(csa->csa_ver, clock, cpuid); va = kmem_alloc(kernel_map, PCPU_PAGES * PAGE_SIZE); pc = (struct pcpu *)(va + (PCPU_PAGES * PAGE_SIZE)) - 1; pcpu_init(pc, cpuid, sizeof(*pc)); pc->pc_addr = va; pc->pc_clock = clock; pc->pc_mid = mid; pc->pc_node = child; cache_init(pc); all_cpus |= 1 << cpuid; intr_add_cpu(cpuid); } KASSERT(!isjbus || mp_ncpus <= IDR_JALAPENO_MAX_BN_PAIRS, ("%s: can only IPI a maximum of %d JBus-CPUs", __func__, IDR_JALAPENO_MAX_BN_PAIRS)); PCPU_SET(other_cpus, all_cpus & ~(1 << curcpu)); smp_active = 1; } void cpu_mp_announce(void) { } static void cpu_mp_unleash(void *v) { volatile struct cpu_start_args *csa; struct pcpu *pc; register_t s; vm_offset_t va; vm_paddr_t pa; u_int ctx_inc; u_int ctx_min; int i; ctx_min = TLB_CTX_USER_MIN; ctx_inc = (TLB_CTX_USER_MAX - 1) / mp_ncpus; csa = &cpu_start_args; csa->csa_count = mp_ncpus; SLIST_FOREACH(pc, &cpuhead, pc_allcpu) { pc->pc_tlb_ctx = ctx_min; pc->pc_tlb_ctx_min = ctx_min; pc->pc_tlb_ctx_max = ctx_min + ctx_inc; ctx_min += ctx_inc; if (pc->pc_cpuid == curcpu) continue; KASSERT(pc->pc_idlethread != NULL, ("%s: idlethread", __func__)); pc->pc_curthread = pc->pc_idlethread; pc->pc_curpcb = pc->pc_curthread->td_pcb; for (i = 0; i < PCPU_PAGES; i++) { va = pc->pc_addr + i * PAGE_SIZE; pa = pmap_kextract(va); if (pa == 0) panic("%s: pmap_kextract", __func__); csa->csa_ttes[i].tte_vpn = TV_VPN(va, TS_8K); csa->csa_ttes[i].tte_data = TD_V | TD_8K | TD_PA(pa) | TD_L | TD_CP | TD_CV | TD_P | TD_W; } csa->csa_state = 0; csa->csa_pcpu = pc->pc_addr; csa->csa_mid = pc->pc_mid; s = intr_disable(); while (csa->csa_state != CPU_BOOTSTRAP) ; intr_restore(s); } membar(StoreLoad); csa->csa_count = 0; smp_started = 1; } void cpu_mp_bootstrap(struct pcpu *pc) { volatile struct cpu_start_args *csa; csa = &cpu_start_args; if (cpu_impl >= CPU_IMPL_ULTRASPARCIII) cheetah_init(); cache_enable(); pmap_map_tsb(); /* * Flush all non-locked TLB entries possibly left over by the * firmware. */ tlb_flush_nonlocked(); cpu_setregs(pc); tick_start(); smp_cpus++; KASSERT(curthread != NULL, ("%s: curthread", __func__)); PCPU_SET(other_cpus, all_cpus & ~(1 << curcpu)); printf("SMP: AP CPU #%d Launched!\n", curcpu); csa->csa_count--; membar(StoreLoad); csa->csa_state = CPU_BOOTSTRAP; while (csa->csa_count != 0) ; /* Ok, now enter the scheduler. */ sched_throw(NULL); } void cpu_mp_shutdown(void) { int i; critical_enter(); shutdown_cpus = PCPU_GET(other_cpus); if (stopped_cpus != PCPU_GET(other_cpus)) /* XXX */ stop_cpus(stopped_cpus ^ PCPU_GET(other_cpus)); i = 0; while (shutdown_cpus != 0) { if (i++ > 100000) { printf("timeout shutting down CPUs.\n"); break; } } /* XXX: delay a bit to allow the CPUs to actually enter the PROM. */ DELAY(100000); critical_exit(); } static void cpu_ipi_ast(struct trapframe *tf) { } static void cpu_ipi_stop(struct trapframe *tf) { CTR2(KTR_SMP, "%s: stopped %d", __func__, curcpu); savectx(&stoppcbs[curcpu]); atomic_set_acq_int(&stopped_cpus, PCPU_GET(cpumask)); while ((started_cpus & PCPU_GET(cpumask)) == 0) { if ((shutdown_cpus & PCPU_GET(cpumask)) != 0) { atomic_clear_int(&shutdown_cpus, PCPU_GET(cpumask)); - if (has_stopself != 0) - sun4u_stopself(); (void)intr_disable(); for (;;) ; } } atomic_clear_rel_int(&started_cpus, PCPU_GET(cpumask)); atomic_clear_rel_int(&stopped_cpus, PCPU_GET(cpumask)); CTR2(KTR_SMP, "%s: restarted %d", __func__, curcpu); } static void cpu_ipi_preempt(struct trapframe *tf) { sched_preempt(curthread); } static void spitfire_ipi_selected(u_int cpus, u_long d0, u_long d1, u_long d2) { u_int cpu; KASSERT((cpus & (1 << curcpu)) == 0, ("%s: CPU can't IPI itself", __func__)); while (cpus) { cpu = ffs(cpus) - 1; cpus &= ~(1 << cpu); spitfire_ipi_send(cpuid_to_mid[cpu], d0, d1, d2); } } static void spitfire_ipi_send(u_int mid, u_long d0, u_long d1, u_long d2) { register_t s; u_long ids; int i; KASSERT((ldxa(0, ASI_INTR_DISPATCH_STATUS) & IDR_BUSY) == 0, ("%s: outstanding dispatch", __func__)); for (i = 0; i < IPI_RETRIES; i++) { s = intr_disable(); stxa(AA_SDB_INTR_D0, ASI_SDB_INTR_W, d0); stxa(AA_SDB_INTR_D1, ASI_SDB_INTR_W, d1); stxa(AA_SDB_INTR_D2, ASI_SDB_INTR_W, d2); membar(Sync); stxa(AA_INTR_SEND | (mid << IDC_ITID_SHIFT), ASI_SDB_INTR_W, 0); /* * Workaround for SpitFire erratum #54; do a dummy read * from a SDB internal register before the MEMBAR #Sync * for the write to ASI_SDB_INTR_W (requiring another * MEMBAR #Sync in order to make sure the write has * occurred before the load). */ membar(Sync); (void)ldxa(AA_SDB_CNTL_HIGH, ASI_SDB_CONTROL_R); membar(Sync); while (((ids = ldxa(0, ASI_INTR_DISPATCH_STATUS)) & IDR_BUSY) != 0) ; intr_restore(s); if ((ids & (IDR_BUSY | IDR_NACK)) == 0) return; /* * Leave interrupts enabled for a bit before retrying * in order to avoid deadlocks if the other CPU is also * trying to send an IPI. */ DELAY(2); } if ( #ifdef KDB kdb_active || #endif panicstr != NULL) printf("%s: couldn't send IPI to module 0x%u\n", __func__, mid); else - panic("%s: couldn't send IPI", __func__); + panic("%s: couldn't send IPI to module 0x%u", + __func__, mid); } static void cheetah_ipi_selected(u_int cpus, u_long d0, u_long d1, u_long d2) { register_t s; u_long ids; u_int bnp; u_int cpu; int i; KASSERT((cpus & (1 << curcpu)) == 0, ("%s: CPU can't IPI itself", __func__)); KASSERT((ldxa(0, ASI_INTR_DISPATCH_STATUS) & IDR_CHEETAH_ALL_BUSY) == 0, ("%s: outstanding dispatch", __func__)); if (cpus == 0) return; ids = 0; for (i = 0; i < IPI_RETRIES * mp_ncpus; i++) { s = intr_disable(); stxa(AA_SDB_INTR_D0, ASI_SDB_INTR_W, d0); stxa(AA_SDB_INTR_D1, ASI_SDB_INTR_W, d1); stxa(AA_SDB_INTR_D2, ASI_SDB_INTR_W, d2); membar(Sync); bnp = 0; for (cpu = 0; cpu < mp_ncpus; cpu++) { if ((cpus & (1 << cpu)) != 0) { stxa(AA_INTR_SEND | (cpuid_to_mid[cpu] << IDC_ITID_SHIFT) | (isjbus ? 0 : bnp << IDC_BN_SHIFT), ASI_SDB_INTR_W, 0); membar(Sync); bnp++; } } while (((ids = ldxa(0, ASI_INTR_DISPATCH_STATUS)) & IDR_CHEETAH_ALL_BUSY) != 0) ; intr_restore(s); if ((ids & (IDR_CHEETAH_ALL_BUSY | IDR_CHEETAH_ALL_NACK)) == 0) return; bnp = 0; for (cpu = 0; cpu < mp_ncpus; cpu++) { if ((cpus & (1 << cpu)) != 0) { if ((ids & (IDR_NACK << (isjbus ? (2 * cpuid_to_mid[cpu]) : (2 * bnp)))) == 0) cpus &= ~(1 << cpu); bnp++; } } /* + * On at least Fire V880 we may receive IDR_NACKs for + * CPUs we actually haven't tried to send an IPI to, + * but which apparently can be safely ignored. + */ + if (cpus == 0) + return; + /* * Leave interrupts enabled for a bit before retrying * in order to avoid deadlocks if the other CPUs are * also trying to send IPIs. */ - DELAY(2 * bnp); + DELAY(2 * mp_ncpus); } if ( #ifdef KDB kdb_active || #endif panicstr != NULL) printf("%s: couldn't send IPI (cpus=0x%u ids=0x%lu)\n", __func__, cpus, ids); else - panic("%s: couldn't send IPI", __func__); + panic("%s: couldn't send IPI (cpus=0x%u ids=0x%lu)", + __func__, cpus, ids); } Index: projects/cambria/sys/sun4v/include/ofw_machdep.h =================================================================== --- projects/cambria/sys/sun4v/include/ofw_machdep.h (revision 186459) +++ projects/cambria/sys/sun4v/include/ofw_machdep.h (revision 186460) @@ -1,38 +1,43 @@ /*- * Copyright (c) 2001 by Thomas Moestl . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _MACHINE_OFW_MACHDEP_H_ #define _MACHINE_OFW_MACHDEP_H_ #include +#include +#include +typedef uint64_t cell_t; + int OF_decode_addr(phandle_t, int, int *, bus_addr_t *); void OF_getetheraddr(device_t, u_char *); void cpu_shutdown(void *); -void openfirmware_exit(void *); +int ofw_entry(void *); +void ofw_exit(void *); #endif /* _MACHINE_OFW_MACHDEP_H_ */ Index: projects/cambria/sys/vm/vm_object.c =================================================================== --- projects/cambria/sys/vm/vm_object.c (revision 186459) +++ projects/cambria/sys/vm/vm_object.c (revision 186460) @@ -1,2277 +1,2277 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. * * 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. * 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. * * from: @(#)vm_object.c 8.5 (Berkeley) 3/22/94 * * * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * * Authors: Avadis Tevanian, Jr., Michael Wayne Young * * 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 the * rights to redistribute these changes. */ /* * Virtual memory object module. */ #include __FBSDID("$FreeBSD$"); #include "opt_vm.h" #include #include #include #include #include #include #include #include #include /* for curproc, pageproc */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EASY_SCAN_FACTOR 8 #define MSYNC_FLUSH_HARDSEQ 0x01 #define MSYNC_FLUSH_SOFTSEQ 0x02 /* * msync / VM object flushing optimizations */ static int msync_flush_flags = MSYNC_FLUSH_HARDSEQ | MSYNC_FLUSH_SOFTSEQ; SYSCTL_INT(_vm, OID_AUTO, msync_flush_flags, CTLFLAG_RW, &msync_flush_flags, 0, "Enable sequential iteration optimization"); static int old_msync; SYSCTL_INT(_vm, OID_AUTO, old_msync, CTLFLAG_RW, &old_msync, 0, "Use old (insecure) msync behavior"); static void vm_object_qcollapse(vm_object_t object); static int vm_object_page_collect_flush(vm_object_t object, vm_page_t p, int curgeneration, int pagerflags); static void vm_object_vndeallocate(vm_object_t object); /* * Virtual memory objects maintain the actual data * associated with allocated virtual memory. A given * page of memory exists within exactly one object. * * An object is only deallocated when all "references" * are given up. Only one "reference" to a given * region of an object should be writeable. * * Associated with each object is a list of all resident * memory pages belonging to that object; this list is * maintained by the "vm_page" module, and locked by the object's * lock. * * Each object also records a "pager" routine which is * used to retrieve (and store) pages to the proper backing * storage. In addition, objects may be backed by other * objects from which they were virtual-copied. * * The only items within the object structure which are * modified after time of creation are: * reference count locked by object's lock * pager routine locked by object's lock * */ struct object_q vm_object_list; struct mtx vm_object_list_mtx; /* lock for object list and count */ struct vm_object kernel_object_store; struct vm_object kmem_object_store; SYSCTL_NODE(_vm_stats, OID_AUTO, object, CTLFLAG_RD, 0, "VM object stats"); static long object_collapses; SYSCTL_LONG(_vm_stats_object, OID_AUTO, collapses, CTLFLAG_RD, &object_collapses, 0, "VM object collapses"); static long object_bypasses; SYSCTL_LONG(_vm_stats_object, OID_AUTO, bypasses, CTLFLAG_RD, &object_bypasses, 0, "VM object bypasses"); static uma_zone_t obj_zone; static int vm_object_zinit(void *mem, int size, int flags); #ifdef INVARIANTS static void vm_object_zdtor(void *mem, int size, void *arg); static void vm_object_zdtor(void *mem, int size, void *arg) { vm_object_t object; object = (vm_object_t)mem; KASSERT(TAILQ_EMPTY(&object->memq), ("object %p has resident pages", object)); #if VM_NRESERVLEVEL > 0 KASSERT(LIST_EMPTY(&object->rvq), ("object %p has reservations", object)); #endif KASSERT(object->cache == NULL, ("object %p has cached pages", object)); KASSERT(object->paging_in_progress == 0, ("object %p paging_in_progress = %d", object, object->paging_in_progress)); KASSERT(object->resident_page_count == 0, ("object %p resident_page_count = %d", object, object->resident_page_count)); KASSERT(object->shadow_count == 0, ("object %p shadow_count = %d", object, object->shadow_count)); } #endif static int vm_object_zinit(void *mem, int size, int flags) { vm_object_t object; object = (vm_object_t)mem; bzero(&object->mtx, sizeof(object->mtx)); VM_OBJECT_LOCK_INIT(object, "standard object"); /* These are true for any object that has been freed */ object->paging_in_progress = 0; object->resident_page_count = 0; object->shadow_count = 0; return (0); } void _vm_object_allocate(objtype_t type, vm_pindex_t size, vm_object_t object) { TAILQ_INIT(&object->memq); LIST_INIT(&object->shadow_head); object->root = NULL; object->type = type; object->size = size; object->generation = 1; object->ref_count = 1; object->flags = 0; if ((object->type == OBJT_DEFAULT) || (object->type == OBJT_SWAP)) object->flags = OBJ_ONEMAPPING; object->pg_color = 0; object->handle = NULL; object->backing_object = NULL; object->backing_object_offset = (vm_ooffset_t) 0; #if VM_NRESERVLEVEL > 0 LIST_INIT(&object->rvq); #endif object->cache = NULL; mtx_lock(&vm_object_list_mtx); TAILQ_INSERT_TAIL(&vm_object_list, object, object_list); mtx_unlock(&vm_object_list_mtx); } /* * vm_object_init: * * Initialize the VM objects module. */ void vm_object_init(void) { TAILQ_INIT(&vm_object_list); mtx_init(&vm_object_list_mtx, "vm object_list", NULL, MTX_DEF); VM_OBJECT_LOCK_INIT(&kernel_object_store, "kernel object"); _vm_object_allocate(OBJT_PHYS, OFF_TO_IDX(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS), kernel_object); #if VM_NRESERVLEVEL > 0 kernel_object->flags |= OBJ_COLORED; kernel_object->pg_color = (u_short)atop(VM_MIN_KERNEL_ADDRESS); #endif VM_OBJECT_LOCK_INIT(&kmem_object_store, "kmem object"); _vm_object_allocate(OBJT_PHYS, OFF_TO_IDX(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS), kmem_object); #if VM_NRESERVLEVEL > 0 kmem_object->flags |= OBJ_COLORED; kmem_object->pg_color = (u_short)atop(VM_MIN_KERNEL_ADDRESS); #endif /* * The lock portion of struct vm_object must be type stable due * to vm_pageout_fallback_object_lock locking a vm object * without holding any references to it. */ obj_zone = uma_zcreate("VM OBJECT", sizeof (struct vm_object), NULL, #ifdef INVARIANTS vm_object_zdtor, #else NULL, #endif vm_object_zinit, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM|UMA_ZONE_NOFREE); } void vm_object_clear_flag(vm_object_t object, u_short bits) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); object->flags &= ~bits; } void vm_object_pip_add(vm_object_t object, short i) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); object->paging_in_progress += i; } void vm_object_pip_subtract(vm_object_t object, short i) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); object->paging_in_progress -= i; } void vm_object_pip_wakeup(vm_object_t object) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); object->paging_in_progress--; if ((object->flags & OBJ_PIPWNT) && object->paging_in_progress == 0) { vm_object_clear_flag(object, OBJ_PIPWNT); wakeup(object); } } void vm_object_pip_wakeupn(vm_object_t object, short i) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); if (i) object->paging_in_progress -= i; if ((object->flags & OBJ_PIPWNT) && object->paging_in_progress == 0) { vm_object_clear_flag(object, OBJ_PIPWNT); wakeup(object); } } void vm_object_pip_wait(vm_object_t object, char *waitid) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); while (object->paging_in_progress) { object->flags |= OBJ_PIPWNT; msleep(object, VM_OBJECT_MTX(object), PVM, waitid, 0); } } /* * vm_object_allocate: * * Returns a new object with the given size. */ vm_object_t vm_object_allocate(objtype_t type, vm_pindex_t size) { vm_object_t object; object = (vm_object_t)uma_zalloc(obj_zone, M_WAITOK); _vm_object_allocate(type, size, object); return (object); } /* * vm_object_reference: * * Gets another reference to the given object. Note: OBJ_DEAD * objects can be referenced during final cleaning. */ void vm_object_reference(vm_object_t object) { if (object == NULL) return; VM_OBJECT_LOCK(object); vm_object_reference_locked(object); VM_OBJECT_UNLOCK(object); } /* * vm_object_reference_locked: * * Gets another reference to the given object. * * The object must be locked. */ void vm_object_reference_locked(vm_object_t object) { struct vnode *vp; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); object->ref_count++; if (object->type == OBJT_VNODE) { vp = object->handle; vref(vp); } } /* * Handle deallocating an object of type OBJT_VNODE. */ static void vm_object_vndeallocate(vm_object_t object) { struct vnode *vp = (struct vnode *) object->handle; VFS_ASSERT_GIANT(vp->v_mount); VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); KASSERT(object->type == OBJT_VNODE, ("vm_object_vndeallocate: not a vnode object")); KASSERT(vp != NULL, ("vm_object_vndeallocate: missing vp")); #ifdef INVARIANTS if (object->ref_count == 0) { vprint("vm_object_vndeallocate", vp); panic("vm_object_vndeallocate: bad object reference count"); } #endif object->ref_count--; if (object->ref_count == 0) { mp_fixme("Unlocked vflag access."); vp->v_vflag &= ~VV_TEXT; } VM_OBJECT_UNLOCK(object); /* * vrele may need a vop lock */ vrele(vp); } /* * vm_object_deallocate: * * Release a reference to the specified object, * gained either through a vm_object_allocate * or a vm_object_reference call. When all references * are gone, storage associated with this object * may be relinquished. * * No object may be locked. */ void vm_object_deallocate(vm_object_t object) { vm_object_t temp; while (object != NULL) { int vfslocked; vfslocked = 0; restart: VM_OBJECT_LOCK(object); if (object->type == OBJT_VNODE) { struct vnode *vp = (struct vnode *) object->handle; /* * Conditionally acquire Giant for a vnode-backed * object. We have to be careful since the type of * a vnode object can change while the object is * unlocked. */ if (VFS_NEEDSGIANT(vp->v_mount) && !vfslocked) { vfslocked = 1; if (!mtx_trylock(&Giant)) { VM_OBJECT_UNLOCK(object); mtx_lock(&Giant); goto restart; } } vm_object_vndeallocate(object); VFS_UNLOCK_GIANT(vfslocked); return; } else /* * This is to handle the case that the object * changed type while we dropped its lock to * obtain Giant. */ VFS_UNLOCK_GIANT(vfslocked); KASSERT(object->ref_count != 0, ("vm_object_deallocate: object deallocated too many times: %d", object->type)); /* * If the reference count goes to 0 we start calling * vm_object_terminate() on the object chain. * A ref count of 1 may be a special case depending on the * shadow count being 0 or 1. */ object->ref_count--; if (object->ref_count > 1) { VM_OBJECT_UNLOCK(object); return; } else if (object->ref_count == 1) { if (object->shadow_count == 0 && object->handle == NULL && (object->type == OBJT_DEFAULT || object->type == OBJT_SWAP)) { vm_object_set_flag(object, OBJ_ONEMAPPING); } else if ((object->shadow_count == 1) && (object->handle == NULL) && (object->type == OBJT_DEFAULT || object->type == OBJT_SWAP)) { vm_object_t robject; robject = LIST_FIRST(&object->shadow_head); KASSERT(robject != NULL, ("vm_object_deallocate: ref_count: %d, shadow_count: %d", object->ref_count, object->shadow_count)); if (!VM_OBJECT_TRYLOCK(robject)) { /* * Avoid a potential deadlock. */ object->ref_count++; VM_OBJECT_UNLOCK(object); /* * More likely than not the thread * holding robject's lock has lower * priority than the current thread. * Let the lower priority thread run. */ pause("vmo_de", 1); continue; } /* * Collapse object into its shadow unless its * shadow is dead. In that case, object will * be deallocated by the thread that is * deallocating its shadow. */ if ((robject->flags & OBJ_DEAD) == 0 && (robject->handle == NULL) && (robject->type == OBJT_DEFAULT || robject->type == OBJT_SWAP)) { robject->ref_count++; retry: if (robject->paging_in_progress) { VM_OBJECT_UNLOCK(object); vm_object_pip_wait(robject, "objde1"); temp = robject->backing_object; if (object == temp) { VM_OBJECT_LOCK(object); goto retry; } } else if (object->paging_in_progress) { VM_OBJECT_UNLOCK(robject); object->flags |= OBJ_PIPWNT; msleep(object, VM_OBJECT_MTX(object), PDROP | PVM, "objde2", 0); VM_OBJECT_LOCK(robject); temp = robject->backing_object; if (object == temp) { VM_OBJECT_LOCK(object); goto retry; } } else VM_OBJECT_UNLOCK(object); if (robject->ref_count == 1) { robject->ref_count--; object = robject; goto doterm; } object = robject; vm_object_collapse(object); VM_OBJECT_UNLOCK(object); continue; } VM_OBJECT_UNLOCK(robject); } VM_OBJECT_UNLOCK(object); return; } doterm: temp = object->backing_object; if (temp != NULL) { VM_OBJECT_LOCK(temp); LIST_REMOVE(object, shadow_list); temp->shadow_count--; temp->generation++; VM_OBJECT_UNLOCK(temp); object->backing_object = NULL; } /* * Don't double-terminate, we could be in a termination * recursion due to the terminate having to sync data * to disk. */ if ((object->flags & OBJ_DEAD) == 0) vm_object_terminate(object); else VM_OBJECT_UNLOCK(object); object = temp; } } /* * vm_object_destroy removes the object from the global object list * and frees the space for the object. */ void vm_object_destroy(vm_object_t object) { /* * Remove the object from the global object list. */ mtx_lock(&vm_object_list_mtx); TAILQ_REMOVE(&vm_object_list, object, object_list); mtx_unlock(&vm_object_list_mtx); /* * Free the space for the object. */ uma_zfree(obj_zone, object); } /* * vm_object_terminate actually destroys the specified object, freeing * up all previously used resources. * * The object must be locked. * This routine may block. */ void vm_object_terminate(vm_object_t object) { vm_page_t p; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); /* * Make sure no one uses us. */ vm_object_set_flag(object, OBJ_DEAD); /* * wait for the pageout daemon to be done with the object */ vm_object_pip_wait(object, "objtrm"); KASSERT(!object->paging_in_progress, ("vm_object_terminate: pageout in progress")); /* * Clean and free the pages, as appropriate. All references to the * object are gone, so we don't need to lock it. */ if (object->type == OBJT_VNODE) { struct vnode *vp = (struct vnode *)object->handle; /* * Clean pages and flush buffers. */ vm_object_page_clean(object, 0, 0, OBJPC_SYNC); VM_OBJECT_UNLOCK(object); vinvalbuf(vp, V_SAVE, 0, 0); VM_OBJECT_LOCK(object); } KASSERT(object->ref_count == 0, ("vm_object_terminate: object with references, ref_count=%d", object->ref_count)); /* * Now free any remaining pages. For internal objects, this also * removes them from paging queues. Don't free wired pages, just * remove them from the object. */ vm_page_lock_queues(); while ((p = TAILQ_FIRST(&object->memq)) != NULL) { KASSERT(!p->busy && (p->oflags & VPO_BUSY) == 0, ("vm_object_terminate: freeing busy page %p " - "p->busy = %d, p->flags %x\n", p, p->busy, p->flags)); + "p->busy = %d, p->oflags %x\n", p, p->busy, p->oflags)); if (p->wire_count == 0) { vm_page_free(p); cnt.v_pfree++; } else { vm_page_remove(p); } } vm_page_unlock_queues(); #if VM_NRESERVLEVEL > 0 if (__predict_false(!LIST_EMPTY(&object->rvq))) vm_reserv_break_all(object); #endif if (__predict_false(object->cache != NULL)) vm_page_cache_free(object, 0, 0); /* * Let the pager know object is dead. */ vm_pager_deallocate(object); VM_OBJECT_UNLOCK(object); vm_object_destroy(object); } /* * vm_object_page_clean * * Clean all dirty pages in the specified range of object. Leaves page * on whatever queue it is currently on. If NOSYNC is set then do not * write out pages with VPO_NOSYNC set (originally comes from MAP_NOSYNC), * leaving the object dirty. * * When stuffing pages asynchronously, allow clustering. XXX we need a * synchronous clustering mode implementation. * * Odd semantics: if start == end, we clean everything. * * The object must be locked. */ void vm_object_page_clean(vm_object_t object, vm_pindex_t start, vm_pindex_t end, int flags) { vm_page_t p, np; vm_pindex_t tstart, tend; vm_pindex_t pi; int clearobjflags; int pagerflags; int curgeneration; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); if (object->type != OBJT_VNODE || (object->flags & OBJ_MIGHTBEDIRTY) == 0) return; pagerflags = (flags & (OBJPC_SYNC | OBJPC_INVAL)) ? VM_PAGER_PUT_SYNC : VM_PAGER_CLUSTER_OK; pagerflags |= (flags & OBJPC_INVAL) ? VM_PAGER_PUT_INVAL : 0; vm_object_set_flag(object, OBJ_CLEANING); tstart = start; if (end == 0) { tend = object->size; } else { tend = end; } vm_page_lock_queues(); /* * If the caller is smart and only msync()s a range he knows is * dirty, we may be able to avoid an object scan. This results in * a phenominal improvement in performance. We cannot do this * as a matter of course because the object may be huge - e.g. * the size might be in the gigabytes or terrabytes. */ if (msync_flush_flags & MSYNC_FLUSH_HARDSEQ) { vm_pindex_t tscan; int scanlimit; int scanreset; scanreset = object->resident_page_count / EASY_SCAN_FACTOR; if (scanreset < 16) scanreset = 16; pagerflags |= VM_PAGER_IGNORE_CLEANCHK; scanlimit = scanreset; tscan = tstart; while (tscan < tend) { curgeneration = object->generation; p = vm_page_lookup(object, tscan); if (p == NULL || p->valid == 0) { if (--scanlimit == 0) break; ++tscan; continue; } vm_page_test_dirty(p); if ((p->dirty & p->valid) == 0) { if (--scanlimit == 0) break; ++tscan; continue; } /* * If we have been asked to skip nosync pages and * this is a nosync page, we can't continue. */ if ((flags & OBJPC_NOSYNC) && (p->oflags & VPO_NOSYNC)) { if (--scanlimit == 0) break; ++tscan; continue; } scanlimit = scanreset; /* * This returns 0 if it was unable to busy the first * page (i.e. had to sleep). */ tscan += vm_object_page_collect_flush(object, p, curgeneration, pagerflags); } /* * If everything was dirty and we flushed it successfully, * and the requested range is not the entire object, we * don't have to mess with CLEANCHK or MIGHTBEDIRTY and can * return immediately. */ if (tscan >= tend && (tstart || tend < object->size)) { vm_page_unlock_queues(); vm_object_clear_flag(object, OBJ_CLEANING); return; } pagerflags &= ~VM_PAGER_IGNORE_CLEANCHK; } /* * Generally set CLEANCHK interlock and make the page read-only so * we can then clear the object flags. * * However, if this is a nosync mmap then the object is likely to * stay dirty so do not mess with the page and do not clear the * object flags. */ clearobjflags = 1; TAILQ_FOREACH(p, &object->memq, listq) { p->oflags |= VPO_CLEANCHK; if ((flags & OBJPC_NOSYNC) && (p->oflags & VPO_NOSYNC)) clearobjflags = 0; else pmap_remove_write(p); } if (clearobjflags && (tstart == 0) && (tend == object->size)) { struct vnode *vp; vm_object_clear_flag(object, OBJ_MIGHTBEDIRTY); if (object->type == OBJT_VNODE && (vp = (struct vnode *)object->handle) != NULL) { VI_LOCK(vp); if (vp->v_iflag & VI_OBJDIRTY) vp->v_iflag &= ~VI_OBJDIRTY; VI_UNLOCK(vp); } } rescan: curgeneration = object->generation; for (p = TAILQ_FIRST(&object->memq); p; p = np) { int n; np = TAILQ_NEXT(p, listq); again: pi = p->pindex; if ((p->oflags & VPO_CLEANCHK) == 0 || (pi < tstart) || (pi >= tend) || p->valid == 0) { p->oflags &= ~VPO_CLEANCHK; continue; } vm_page_test_dirty(p); if ((p->dirty & p->valid) == 0) { p->oflags &= ~VPO_CLEANCHK; continue; } /* * If we have been asked to skip nosync pages and this is a * nosync page, skip it. Note that the object flags were * not cleared in this case so we do not have to set them. */ if ((flags & OBJPC_NOSYNC) && (p->oflags & VPO_NOSYNC)) { p->oflags &= ~VPO_CLEANCHK; continue; } n = vm_object_page_collect_flush(object, p, curgeneration, pagerflags); if (n == 0) goto rescan; if (object->generation != curgeneration) goto rescan; /* * Try to optimize the next page. If we can't we pick up * our (random) scan where we left off. */ if (msync_flush_flags & MSYNC_FLUSH_SOFTSEQ) { if ((p = vm_page_lookup(object, pi + n)) != NULL) goto again; } } vm_page_unlock_queues(); #if 0 VOP_FSYNC(vp, (pagerflags & VM_PAGER_PUT_SYNC)?MNT_WAIT:0, curproc); #endif vm_object_clear_flag(object, OBJ_CLEANING); return; } static int vm_object_page_collect_flush(vm_object_t object, vm_page_t p, int curgeneration, int pagerflags) { int runlen; int maxf; int chkb; int maxb; int i; vm_pindex_t pi; vm_page_t maf[vm_pageout_page_count]; vm_page_t mab[vm_pageout_page_count]; vm_page_t ma[vm_pageout_page_count]; mtx_assert(&vm_page_queue_mtx, MA_OWNED); pi = p->pindex; while (vm_page_sleep_if_busy(p, TRUE, "vpcwai")) { vm_page_lock_queues(); if (object->generation != curgeneration) { return(0); } } maxf = 0; for(i = 1; i < vm_pageout_page_count; i++) { vm_page_t tp; if ((tp = vm_page_lookup(object, pi + i)) != NULL) { if ((tp->oflags & VPO_BUSY) || ((pagerflags & VM_PAGER_IGNORE_CLEANCHK) == 0 && (tp->oflags & VPO_CLEANCHK) == 0) || (tp->busy != 0)) break; vm_page_test_dirty(tp); if ((tp->dirty & tp->valid) == 0) { tp->oflags &= ~VPO_CLEANCHK; break; } maf[ i - 1 ] = tp; maxf++; continue; } break; } maxb = 0; chkb = vm_pageout_page_count - maxf; if (chkb) { for(i = 1; i < chkb;i++) { vm_page_t tp; if ((tp = vm_page_lookup(object, pi - i)) != NULL) { if ((tp->oflags & VPO_BUSY) || ((pagerflags & VM_PAGER_IGNORE_CLEANCHK) == 0 && (tp->oflags & VPO_CLEANCHK) == 0) || (tp->busy != 0)) break; vm_page_test_dirty(tp); if ((tp->dirty & tp->valid) == 0) { tp->oflags &= ~VPO_CLEANCHK; break; } mab[ i - 1 ] = tp; maxb++; continue; } break; } } for(i = 0; i < maxb; i++) { int index = (maxb - i) - 1; ma[index] = mab[i]; ma[index]->oflags &= ~VPO_CLEANCHK; } p->oflags &= ~VPO_CLEANCHK; ma[maxb] = p; for(i = 0; i < maxf; i++) { int index = (maxb + i) + 1; ma[index] = maf[i]; ma[index]->oflags &= ~VPO_CLEANCHK; } runlen = maxb + maxf + 1; vm_pageout_flush(ma, runlen, pagerflags); for (i = 0; i < runlen; i++) { if (ma[i]->valid & ma[i]->dirty) { pmap_remove_write(ma[i]); ma[i]->oflags |= VPO_CLEANCHK; /* * maxf will end up being the actual number of pages * we wrote out contiguously, non-inclusive of the * first page. We do not count look-behind pages. */ if (i >= maxb + 1 && (maxf > i - maxb - 1)) maxf = i - maxb - 1; } } return(maxf + 1); } /* * Note that there is absolutely no sense in writing out * anonymous objects, so we track down the vnode object * to write out. * We invalidate (remove) all pages from the address space * for semantic correctness. * * Note: certain anonymous maps, such as MAP_NOSYNC maps, * may start out with a NULL object. */ void vm_object_sync(vm_object_t object, vm_ooffset_t offset, vm_size_t size, boolean_t syncio, boolean_t invalidate) { vm_object_t backing_object; struct vnode *vp; struct mount *mp; int flags; if (object == NULL) return; VM_OBJECT_LOCK(object); while ((backing_object = object->backing_object) != NULL) { VM_OBJECT_LOCK(backing_object); offset += object->backing_object_offset; VM_OBJECT_UNLOCK(object); object = backing_object; if (object->size < OFF_TO_IDX(offset + size)) size = IDX_TO_OFF(object->size) - offset; } /* * Flush pages if writing is allowed, invalidate them * if invalidation requested. Pages undergoing I/O * will be ignored by vm_object_page_remove(). * * We cannot lock the vnode and then wait for paging * to complete without deadlocking against vm_fault. * Instead we simply call vm_object_page_remove() and * allow it to block internally on a page-by-page * basis when it encounters pages undergoing async * I/O. */ if (object->type == OBJT_VNODE && (object->flags & OBJ_MIGHTBEDIRTY) != 0) { int vfslocked; vp = object->handle; VM_OBJECT_UNLOCK(object); (void) vn_start_write(vp, &mp, V_WAIT); vfslocked = VFS_LOCK_GIANT(vp->v_mount); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); flags = (syncio || invalidate) ? OBJPC_SYNC : 0; flags |= invalidate ? OBJPC_INVAL : 0; VM_OBJECT_LOCK(object); vm_object_page_clean(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + size + PAGE_MASK), flags); VM_OBJECT_UNLOCK(object); VOP_UNLOCK(vp, 0); VFS_UNLOCK_GIANT(vfslocked); vn_finished_write(mp); VM_OBJECT_LOCK(object); } if ((object->type == OBJT_VNODE || object->type == OBJT_DEVICE) && invalidate) { boolean_t purge; purge = old_msync || (object->type == OBJT_DEVICE); vm_object_page_remove(object, OFF_TO_IDX(offset), OFF_TO_IDX(offset + size + PAGE_MASK), purge ? FALSE : TRUE); } VM_OBJECT_UNLOCK(object); } /* * vm_object_madvise: * * Implements the madvise function at the object/page level. * * MADV_WILLNEED (any object) * * Activate the specified pages if they are resident. * * MADV_DONTNEED (any object) * * Deactivate the specified pages if they are resident. * * MADV_FREE (OBJT_DEFAULT/OBJT_SWAP objects, * OBJ_ONEMAPPING only) * * Deactivate and clean the specified pages if they are * resident. This permits the process to reuse the pages * without faulting or the kernel to reclaim the pages * without I/O. */ void vm_object_madvise(vm_object_t object, vm_pindex_t pindex, int count, int advise) { vm_pindex_t end, tpindex; vm_object_t backing_object, tobject; vm_page_t m; if (object == NULL) return; VM_OBJECT_LOCK(object); end = pindex + count; /* * Locate and adjust resident pages */ for (; pindex < end; pindex += 1) { relookup: tobject = object; tpindex = pindex; shadowlookup: /* * MADV_FREE only operates on OBJT_DEFAULT or OBJT_SWAP pages * and those pages must be OBJ_ONEMAPPING. */ if (advise == MADV_FREE) { if ((tobject->type != OBJT_DEFAULT && tobject->type != OBJT_SWAP) || (tobject->flags & OBJ_ONEMAPPING) == 0) { goto unlock_tobject; } } m = vm_page_lookup(tobject, tpindex); if (m == NULL && advise == MADV_WILLNEED) { /* * If the page is cached, reactivate it. */ m = vm_page_alloc(tobject, tpindex, VM_ALLOC_IFCACHED | VM_ALLOC_NOBUSY); } if (m == NULL) { /* * There may be swap even if there is no backing page */ if (advise == MADV_FREE && tobject->type == OBJT_SWAP) swap_pager_freespace(tobject, tpindex, 1); /* * next object */ backing_object = tobject->backing_object; if (backing_object == NULL) goto unlock_tobject; VM_OBJECT_LOCK(backing_object); tpindex += OFF_TO_IDX(tobject->backing_object_offset); if (tobject != object) VM_OBJECT_UNLOCK(tobject); tobject = backing_object; goto shadowlookup; } /* * If the page is busy or not in a normal active state, * we skip it. If the page is not managed there are no * page queues to mess with. Things can break if we mess * with pages in any of the below states. */ vm_page_lock_queues(); if (m->hold_count || m->wire_count || (m->flags & PG_UNMANAGED) || m->valid != VM_PAGE_BITS_ALL) { vm_page_unlock_queues(); goto unlock_tobject; } if ((m->oflags & VPO_BUSY) || m->busy) { vm_page_flag_set(m, PG_REFERENCED); vm_page_unlock_queues(); if (object != tobject) VM_OBJECT_UNLOCK(object); m->oflags |= VPO_WANTED; msleep(m, VM_OBJECT_MTX(tobject), PDROP | PVM, "madvpo", 0); VM_OBJECT_LOCK(object); goto relookup; } if (advise == MADV_WILLNEED) { vm_page_activate(m); } else if (advise == MADV_DONTNEED) { vm_page_dontneed(m); } else if (advise == MADV_FREE) { /* * Mark the page clean. This will allow the page * to be freed up by the system. However, such pages * are often reused quickly by malloc()/free() * so we do not do anything that would cause * a page fault if we can help it. * * Specifically, we do not try to actually free * the page now nor do we try to put it in the * cache (which would cause a page fault on reuse). * * But we do make the page is freeable as we * can without actually taking the step of unmapping * it. */ pmap_clear_modify(m); m->dirty = 0; m->act_count = 0; vm_page_dontneed(m); } vm_page_unlock_queues(); if (advise == MADV_FREE && tobject->type == OBJT_SWAP) swap_pager_freespace(tobject, tpindex, 1); unlock_tobject: if (tobject != object) VM_OBJECT_UNLOCK(tobject); } VM_OBJECT_UNLOCK(object); } /* * vm_object_shadow: * * Create a new object which is backed by the * specified existing object range. The source * object reference is deallocated. * * The new object and offset into that object * are returned in the source parameters. */ void vm_object_shadow( vm_object_t *object, /* IN/OUT */ vm_ooffset_t *offset, /* IN/OUT */ vm_size_t length) { vm_object_t source; vm_object_t result; source = *object; /* * Don't create the new object if the old object isn't shared. */ if (source != NULL) { VM_OBJECT_LOCK(source); if (source->ref_count == 1 && source->handle == NULL && (source->type == OBJT_DEFAULT || source->type == OBJT_SWAP)) { VM_OBJECT_UNLOCK(source); return; } VM_OBJECT_UNLOCK(source); } /* * Allocate a new object with the given length. */ result = vm_object_allocate(OBJT_DEFAULT, length); /* * The new object shadows the source object, adding a reference to it. * Our caller changes his reference to point to the new object, * removing a reference to the source object. Net result: no change * of reference count. * * Try to optimize the result object's page color when shadowing * in order to maintain page coloring consistency in the combined * shadowed object. */ result->backing_object = source; /* * Store the offset into the source object, and fix up the offset into * the new object. */ result->backing_object_offset = *offset; if (source != NULL) { VM_OBJECT_LOCK(source); LIST_INSERT_HEAD(&source->shadow_head, result, shadow_list); source->shadow_count++; source->generation++; #if VM_NRESERVLEVEL > 0 result->flags |= source->flags & (OBJ_NEEDGIANT | OBJ_COLORED); result->pg_color = (source->pg_color + OFF_TO_IDX(*offset)) & ((1 << (VM_NFREEORDER - 1)) - 1); #else result->flags |= source->flags & OBJ_NEEDGIANT; #endif VM_OBJECT_UNLOCK(source); } /* * Return the new things */ *offset = 0; *object = result; } /* * vm_object_split: * * Split the pages in a map entry into a new object. This affords * easier removal of unused pages, and keeps object inheritance from * being a negative impact on memory usage. */ void vm_object_split(vm_map_entry_t entry) { vm_page_t m, m_next; vm_object_t orig_object, new_object, source; vm_pindex_t idx, offidxstart; vm_size_t size; orig_object = entry->object.vm_object; if (orig_object->type != OBJT_DEFAULT && orig_object->type != OBJT_SWAP) return; if (orig_object->ref_count <= 1) return; VM_OBJECT_UNLOCK(orig_object); offidxstart = OFF_TO_IDX(entry->offset); size = atop(entry->end - entry->start); /* * If swap_pager_copy() is later called, it will convert new_object * into a swap object. */ new_object = vm_object_allocate(OBJT_DEFAULT, size); /* * At this point, the new object is still private, so the order in * which the original and new objects are locked does not matter. */ VM_OBJECT_LOCK(new_object); VM_OBJECT_LOCK(orig_object); source = orig_object->backing_object; if (source != NULL) { VM_OBJECT_LOCK(source); if ((source->flags & OBJ_DEAD) != 0) { VM_OBJECT_UNLOCK(source); VM_OBJECT_UNLOCK(orig_object); VM_OBJECT_UNLOCK(new_object); vm_object_deallocate(new_object); VM_OBJECT_LOCK(orig_object); return; } LIST_INSERT_HEAD(&source->shadow_head, new_object, shadow_list); source->shadow_count++; source->generation++; vm_object_reference_locked(source); /* for new_object */ vm_object_clear_flag(source, OBJ_ONEMAPPING); VM_OBJECT_UNLOCK(source); new_object->backing_object_offset = orig_object->backing_object_offset + entry->offset; new_object->backing_object = source; } new_object->flags |= orig_object->flags & OBJ_NEEDGIANT; retry: if ((m = TAILQ_FIRST(&orig_object->memq)) != NULL) { if (m->pindex < offidxstart) { m = vm_page_splay(offidxstart, orig_object->root); if ((orig_object->root = m)->pindex < offidxstart) m = TAILQ_NEXT(m, listq); } } vm_page_lock_queues(); for (; m != NULL && (idx = m->pindex - offidxstart) < size; m = m_next) { m_next = TAILQ_NEXT(m, listq); /* * We must wait for pending I/O to complete before we can * rename the page. * * We do not have to VM_PROT_NONE the page as mappings should * not be changed by this operation. */ if ((m->oflags & VPO_BUSY) || m->busy) { vm_page_flag_set(m, PG_REFERENCED); vm_page_unlock_queues(); VM_OBJECT_UNLOCK(new_object); m->oflags |= VPO_WANTED; msleep(m, VM_OBJECT_MTX(orig_object), PVM, "spltwt", 0); VM_OBJECT_LOCK(new_object); goto retry; } vm_page_rename(m, new_object, idx); /* page automatically made dirty by rename and cache handled */ vm_page_busy(m); } vm_page_unlock_queues(); if (orig_object->type == OBJT_SWAP) { /* * swap_pager_copy() can sleep, in which case the orig_object's * and new_object's locks are released and reacquired. */ swap_pager_copy(orig_object, new_object, offidxstart, 0); /* * Transfer any cached pages from orig_object to new_object. */ if (__predict_false(orig_object->cache != NULL)) vm_page_cache_transfer(orig_object, offidxstart, new_object); } VM_OBJECT_UNLOCK(orig_object); TAILQ_FOREACH(m, &new_object->memq, listq) vm_page_wakeup(m); VM_OBJECT_UNLOCK(new_object); entry->object.vm_object = new_object; entry->offset = 0LL; vm_object_deallocate(orig_object); VM_OBJECT_LOCK(new_object); } #define OBSC_TEST_ALL_SHADOWED 0x0001 #define OBSC_COLLAPSE_NOWAIT 0x0002 #define OBSC_COLLAPSE_WAIT 0x0004 static int vm_object_backing_scan(vm_object_t object, int op) { int r = 1; vm_page_t p; vm_object_t backing_object; vm_pindex_t backing_offset_index; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); VM_OBJECT_LOCK_ASSERT(object->backing_object, MA_OWNED); backing_object = object->backing_object; backing_offset_index = OFF_TO_IDX(object->backing_object_offset); /* * Initial conditions */ if (op & OBSC_TEST_ALL_SHADOWED) { /* * We do not want to have to test for the existence of cache * or swap pages in the backing object. XXX but with the * new swapper this would be pretty easy to do. * * XXX what about anonymous MAP_SHARED memory that hasn't * been ZFOD faulted yet? If we do not test for this, the * shadow test may succeed! XXX */ if (backing_object->type != OBJT_DEFAULT) { return (0); } } if (op & OBSC_COLLAPSE_WAIT) { vm_object_set_flag(backing_object, OBJ_DEAD); } /* * Our scan */ p = TAILQ_FIRST(&backing_object->memq); while (p) { vm_page_t next = TAILQ_NEXT(p, listq); vm_pindex_t new_pindex = p->pindex - backing_offset_index; if (op & OBSC_TEST_ALL_SHADOWED) { vm_page_t pp; /* * Ignore pages outside the parent object's range * and outside the parent object's mapping of the * backing object. * * note that we do not busy the backing object's * page. */ if ( p->pindex < backing_offset_index || new_pindex >= object->size ) { p = next; continue; } /* * See if the parent has the page or if the parent's * object pager has the page. If the parent has the * page but the page is not valid, the parent's * object pager must have the page. * * If this fails, the parent does not completely shadow * the object and we might as well give up now. */ pp = vm_page_lookup(object, new_pindex); if ( (pp == NULL || pp->valid == 0) && !vm_pager_has_page(object, new_pindex, NULL, NULL) ) { r = 0; break; } } /* * Check for busy page */ if (op & (OBSC_COLLAPSE_WAIT | OBSC_COLLAPSE_NOWAIT)) { vm_page_t pp; if (op & OBSC_COLLAPSE_NOWAIT) { if ((p->oflags & VPO_BUSY) || !p->valid || p->busy) { p = next; continue; } } else if (op & OBSC_COLLAPSE_WAIT) { if ((p->oflags & VPO_BUSY) || p->busy) { vm_page_lock_queues(); vm_page_flag_set(p, PG_REFERENCED); vm_page_unlock_queues(); VM_OBJECT_UNLOCK(object); p->oflags |= VPO_WANTED; msleep(p, VM_OBJECT_MTX(backing_object), PDROP | PVM, "vmocol", 0); VM_OBJECT_LOCK(object); VM_OBJECT_LOCK(backing_object); /* * If we slept, anything could have * happened. Since the object is * marked dead, the backing offset * should not have changed so we * just restart our scan. */ p = TAILQ_FIRST(&backing_object->memq); continue; } } KASSERT( p->object == backing_object, ("vm_object_backing_scan: object mismatch") ); /* * Destroy any associated swap */ if (backing_object->type == OBJT_SWAP) { swap_pager_freespace( backing_object, p->pindex, 1 ); } if ( p->pindex < backing_offset_index || new_pindex >= object->size ) { /* * Page is out of the parent object's range, we * can simply destroy it. */ vm_page_lock_queues(); KASSERT(!pmap_page_is_mapped(p), ("freeing mapped page %p", p)); if (p->wire_count == 0) vm_page_free(p); else vm_page_remove(p); vm_page_unlock_queues(); p = next; continue; } pp = vm_page_lookup(object, new_pindex); if ( pp != NULL || vm_pager_has_page(object, new_pindex, NULL, NULL) ) { /* * page already exists in parent OR swap exists * for this location in the parent. Destroy * the original page from the backing object. * * Leave the parent's page alone */ vm_page_lock_queues(); KASSERT(!pmap_page_is_mapped(p), ("freeing mapped page %p", p)); if (p->wire_count == 0) vm_page_free(p); else vm_page_remove(p); vm_page_unlock_queues(); p = next; continue; } #if VM_NRESERVLEVEL > 0 /* * Rename the reservation. */ vm_reserv_rename(p, object, backing_object, backing_offset_index); #endif /* * Page does not exist in parent, rename the * page from the backing object to the main object. * * If the page was mapped to a process, it can remain * mapped through the rename. */ vm_page_lock_queues(); vm_page_rename(p, object, new_pindex); vm_page_unlock_queues(); /* page automatically made dirty by rename */ } p = next; } return (r); } /* * this version of collapse allows the operation to occur earlier and * when paging_in_progress is true for an object... This is not a complete * operation, but should plug 99.9% of the rest of the leaks. */ static void vm_object_qcollapse(vm_object_t object) { vm_object_t backing_object = object->backing_object; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); VM_OBJECT_LOCK_ASSERT(backing_object, MA_OWNED); if (backing_object->ref_count != 1) return; vm_object_backing_scan(object, OBSC_COLLAPSE_NOWAIT); } /* * vm_object_collapse: * * Collapse an object with the object backing it. * Pages in the backing object are moved into the * parent, and the backing object is deallocated. */ void vm_object_collapse(vm_object_t object) { VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); while (TRUE) { vm_object_t backing_object; /* * Verify that the conditions are right for collapse: * * The object exists and the backing object exists. */ if ((backing_object = object->backing_object) == NULL) break; /* * we check the backing object first, because it is most likely * not collapsable. */ VM_OBJECT_LOCK(backing_object); if (backing_object->handle != NULL || (backing_object->type != OBJT_DEFAULT && backing_object->type != OBJT_SWAP) || (backing_object->flags & OBJ_DEAD) || object->handle != NULL || (object->type != OBJT_DEFAULT && object->type != OBJT_SWAP) || (object->flags & OBJ_DEAD)) { VM_OBJECT_UNLOCK(backing_object); break; } if ( object->paging_in_progress != 0 || backing_object->paging_in_progress != 0 ) { vm_object_qcollapse(object); VM_OBJECT_UNLOCK(backing_object); break; } /* * We know that we can either collapse the backing object (if * the parent is the only reference to it) or (perhaps) have * the parent bypass the object if the parent happens to shadow * all the resident pages in the entire backing object. * * This is ignoring pager-backed pages such as swap pages. * vm_object_backing_scan fails the shadowing test in this * case. */ if (backing_object->ref_count == 1) { /* * If there is exactly one reference to the backing * object, we can collapse it into the parent. */ vm_object_backing_scan(object, OBSC_COLLAPSE_WAIT); #if VM_NRESERVLEVEL > 0 /* * Break any reservations from backing_object. */ if (__predict_false(!LIST_EMPTY(&backing_object->rvq))) vm_reserv_break_all(backing_object); #endif /* * Move the pager from backing_object to object. */ if (backing_object->type == OBJT_SWAP) { /* * swap_pager_copy() can sleep, in which case * the backing_object's and object's locks are * released and reacquired. */ swap_pager_copy( backing_object, object, OFF_TO_IDX(object->backing_object_offset), TRUE); /* * Free any cached pages from backing_object. */ if (__predict_false(backing_object->cache != NULL)) vm_page_cache_free(backing_object, 0, 0); } /* * Object now shadows whatever backing_object did. * Note that the reference to * backing_object->backing_object moves from within * backing_object to within object. */ LIST_REMOVE(object, shadow_list); backing_object->shadow_count--; backing_object->generation++; if (backing_object->backing_object) { VM_OBJECT_LOCK(backing_object->backing_object); LIST_REMOVE(backing_object, shadow_list); LIST_INSERT_HEAD( &backing_object->backing_object->shadow_head, object, shadow_list); /* * The shadow_count has not changed. */ backing_object->backing_object->generation++; VM_OBJECT_UNLOCK(backing_object->backing_object); } object->backing_object = backing_object->backing_object; object->backing_object_offset += backing_object->backing_object_offset; /* * Discard backing_object. * * Since the backing object has no pages, no pager left, * and no object references within it, all that is * necessary is to dispose of it. */ KASSERT(backing_object->ref_count == 1, ("backing_object %p was somehow re-referenced during collapse!", backing_object)); VM_OBJECT_UNLOCK(backing_object); mtx_lock(&vm_object_list_mtx); TAILQ_REMOVE( &vm_object_list, backing_object, object_list ); mtx_unlock(&vm_object_list_mtx); uma_zfree(obj_zone, backing_object); object_collapses++; } else { vm_object_t new_backing_object; /* * If we do not entirely shadow the backing object, * there is nothing we can do so we give up. */ if (object->resident_page_count != object->size && vm_object_backing_scan(object, OBSC_TEST_ALL_SHADOWED) == 0) { VM_OBJECT_UNLOCK(backing_object); break; } /* * Make the parent shadow the next object in the * chain. Deallocating backing_object will not remove * it, since its reference count is at least 2. */ LIST_REMOVE(object, shadow_list); backing_object->shadow_count--; backing_object->generation++; new_backing_object = backing_object->backing_object; if ((object->backing_object = new_backing_object) != NULL) { VM_OBJECT_LOCK(new_backing_object); LIST_INSERT_HEAD( &new_backing_object->shadow_head, object, shadow_list ); new_backing_object->shadow_count++; new_backing_object->generation++; vm_object_reference_locked(new_backing_object); VM_OBJECT_UNLOCK(new_backing_object); object->backing_object_offset += backing_object->backing_object_offset; } /* * Drop the reference count on backing_object. Since * its ref_count was at least 2, it will not vanish. */ backing_object->ref_count--; VM_OBJECT_UNLOCK(backing_object); object_bypasses++; } /* * Try again with this object's new backing object. */ } } /* * vm_object_page_remove: * * For the given object, either frees or invalidates each of the * specified pages. In general, a page is freed. However, if a * page is wired for any reason other than the existence of a * managed, wired mapping, then it may be invalidated but not * removed from the object. Pages are specified by the given * range ["start", "end") and Boolean "clean_only". As a * special case, if "end" is zero, then the range extends from * "start" to the end of the object. If "clean_only" is TRUE, * then only the non-dirty pages within the specified range are * affected. * * In general, this operation should only be performed on objects * that contain managed pages. There are two exceptions. First, * it may be performed on the kernel and kmem objects. Second, * it may be used by msync(..., MS_INVALIDATE) to invalidate * device-backed pages. * * The object must be locked. */ void vm_object_page_remove(vm_object_t object, vm_pindex_t start, vm_pindex_t end, boolean_t clean_only) { vm_page_t p, next; int wirings; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); if (object->resident_page_count == 0) goto skipmemq; /* * Since physically-backed objects do not use managed pages, we can't * remove pages from the object (we must instead remove the page * references, and then destroy the object). */ KASSERT(object->type != OBJT_PHYS || object == kernel_object || object == kmem_object, ("attempt to remove pages from a physical object")); vm_object_pip_add(object, 1); again: vm_page_lock_queues(); if ((p = TAILQ_FIRST(&object->memq)) != NULL) { if (p->pindex < start) { p = vm_page_splay(start, object->root); if ((object->root = p)->pindex < start) p = TAILQ_NEXT(p, listq); } } /* * Assert: the variable p is either (1) the page with the * least pindex greater than or equal to the parameter pindex * or (2) NULL. */ for (; p != NULL && (p->pindex < end || end == 0); p = next) { next = TAILQ_NEXT(p, listq); /* * If the page is wired for any reason besides the * existence of managed, wired mappings, then it cannot * be freed. For example, fictitious pages, which * represent device memory, are inherently wired and * cannot be freed. They can, however, be invalidated * if "clean_only" is FALSE. */ if ((wirings = p->wire_count) != 0 && (wirings = pmap_page_wired_mappings(p)) != p->wire_count) { /* Fictitious pages do not have managed mappings. */ if ((p->flags & PG_FICTITIOUS) == 0) pmap_remove_all(p); /* Account for removal of managed, wired mappings. */ p->wire_count -= wirings; if (!clean_only) p->valid = 0; continue; } if (vm_page_sleep_if_busy(p, TRUE, "vmopar")) goto again; KASSERT((p->flags & PG_FICTITIOUS) == 0, ("vm_object_page_remove: page %p is fictitious", p)); if (clean_only && p->valid) { pmap_remove_write(p); if (p->valid & p->dirty) continue; } pmap_remove_all(p); /* Account for removal of managed, wired mappings. */ if (wirings != 0) p->wire_count -= wirings; vm_page_free(p); } vm_page_unlock_queues(); vm_object_pip_wakeup(object); skipmemq: if (__predict_false(object->cache != NULL)) vm_page_cache_free(object, start, end); } /* * Routine: vm_object_coalesce * Function: Coalesces two objects backing up adjoining * regions of memory into a single object. * * returns TRUE if objects were combined. * * NOTE: Only works at the moment if the second object is NULL - * if it's not, which object do we lock first? * * Parameters: * prev_object First object to coalesce * prev_offset Offset into prev_object * prev_size Size of reference to prev_object * next_size Size of reference to the second object * * Conditions: * The object must *not* be locked. */ boolean_t vm_object_coalesce(vm_object_t prev_object, vm_ooffset_t prev_offset, vm_size_t prev_size, vm_size_t next_size) { vm_pindex_t next_pindex; if (prev_object == NULL) return (TRUE); VM_OBJECT_LOCK(prev_object); if (prev_object->type != OBJT_DEFAULT && prev_object->type != OBJT_SWAP) { VM_OBJECT_UNLOCK(prev_object); return (FALSE); } /* * Try to collapse the object first */ vm_object_collapse(prev_object); /* * Can't coalesce if: . more than one reference . paged out . shadows * another object . has a copy elsewhere (any of which mean that the * pages not mapped to prev_entry may be in use anyway) */ if (prev_object->backing_object != NULL) { VM_OBJECT_UNLOCK(prev_object); return (FALSE); } prev_size >>= PAGE_SHIFT; next_size >>= PAGE_SHIFT; next_pindex = OFF_TO_IDX(prev_offset) + prev_size; if ((prev_object->ref_count > 1) && (prev_object->size != next_pindex)) { VM_OBJECT_UNLOCK(prev_object); return (FALSE); } /* * Remove any pages that may still be in the object from a previous * deallocation. */ if (next_pindex < prev_object->size) { vm_object_page_remove(prev_object, next_pindex, next_pindex + next_size, FALSE); if (prev_object->type == OBJT_SWAP) swap_pager_freespace(prev_object, next_pindex, next_size); } /* * Extend the object if necessary. */ if (next_pindex + next_size > prev_object->size) prev_object->size = next_pindex + next_size; VM_OBJECT_UNLOCK(prev_object); return (TRUE); } void vm_object_set_writeable_dirty(vm_object_t object) { struct vnode *vp; VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); if ((object->flags & OBJ_MIGHTBEDIRTY) != 0) return; vm_object_set_flag(object, OBJ_MIGHTBEDIRTY); if (object->type == OBJT_VNODE && (vp = (struct vnode *)object->handle) != NULL) { VI_LOCK(vp); vp->v_iflag |= VI_OBJDIRTY; VI_UNLOCK(vp); } } #include "opt_ddb.h" #ifdef DDB #include #include #include static int _vm_object_in_map(vm_map_t map, vm_object_t object, vm_map_entry_t entry) { vm_map_t tmpm; vm_map_entry_t tmpe; vm_object_t obj; int entcount; if (map == 0) return 0; if (entry == 0) { tmpe = map->header.next; entcount = map->nentries; while (entcount-- && (tmpe != &map->header)) { if (_vm_object_in_map(map, object, tmpe)) { return 1; } tmpe = tmpe->next; } } else if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) { tmpm = entry->object.sub_map; tmpe = tmpm->header.next; entcount = tmpm->nentries; while (entcount-- && tmpe != &tmpm->header) { if (_vm_object_in_map(tmpm, object, tmpe)) { return 1; } tmpe = tmpe->next; } } else if ((obj = entry->object.vm_object) != NULL) { for (; obj; obj = obj->backing_object) if (obj == object) { return 1; } } return 0; } static int vm_object_in_map(vm_object_t object) { struct proc *p; /* sx_slock(&allproc_lock); */ FOREACH_PROC_IN_SYSTEM(p) { if (!p->p_vmspace /* || (p->p_flag & (P_SYSTEM|P_WEXIT)) */) continue; if (_vm_object_in_map(&p->p_vmspace->vm_map, object, 0)) { /* sx_sunlock(&allproc_lock); */ return 1; } } /* sx_sunlock(&allproc_lock); */ if (_vm_object_in_map(kernel_map, object, 0)) return 1; if (_vm_object_in_map(kmem_map, object, 0)) return 1; if (_vm_object_in_map(pager_map, object, 0)) return 1; if (_vm_object_in_map(buffer_map, object, 0)) return 1; return 0; } DB_SHOW_COMMAND(vmochk, vm_object_check) { vm_object_t object; /* * make sure that internal objs are in a map somewhere * and none have zero ref counts. */ TAILQ_FOREACH(object, &vm_object_list, object_list) { if (object->handle == NULL && (object->type == OBJT_DEFAULT || object->type == OBJT_SWAP)) { if (object->ref_count == 0) { db_printf("vmochk: internal obj has zero ref count: %ld\n", (long)object->size); } if (!vm_object_in_map(object)) { db_printf( "vmochk: internal obj is not in a map: " "ref: %d, size: %lu: 0x%lx, backing_object: %p\n", object->ref_count, (u_long)object->size, (u_long)object->size, (void *)object->backing_object); } } } } /* * vm_object_print: [ debug ] */ DB_SHOW_COMMAND(object, vm_object_print_static) { /* XXX convert args. */ vm_object_t object = (vm_object_t)addr; boolean_t full = have_addr; vm_page_t p; /* XXX count is an (unused) arg. Avoid shadowing it. */ #define count was_count int count; if (object == NULL) return; db_iprintf( "Object %p: type=%d, size=0x%jx, res=%d, ref=%d, flags=0x%x\n", object, (int)object->type, (uintmax_t)object->size, object->resident_page_count, object->ref_count, object->flags); db_iprintf(" sref=%d, backing_object(%d)=(%p)+0x%jx\n", object->shadow_count, object->backing_object ? object->backing_object->ref_count : 0, object->backing_object, (uintmax_t)object->backing_object_offset); if (!full) return; db_indent += 2; count = 0; TAILQ_FOREACH(p, &object->memq, listq) { if (count == 0) db_iprintf("memory:="); else if (count == 6) { db_printf("\n"); db_iprintf(" ..."); count = 0; } else db_printf(","); count++; db_printf("(off=0x%jx,page=0x%jx)", (uintmax_t)p->pindex, (uintmax_t)VM_PAGE_TO_PHYS(p)); } if (count != 0) db_printf("\n"); db_indent -= 2; } /* XXX. */ #undef count /* XXX need this non-static entry for calling from vm_map_print. */ void vm_object_print( /* db_expr_t */ long addr, boolean_t have_addr, /* db_expr_t */ long count, char *modif) { vm_object_print_static(addr, have_addr, count, modif); } DB_SHOW_COMMAND(vmopag, vm_object_print_pages) { vm_object_t object; int nl = 0; int c; TAILQ_FOREACH(object, &vm_object_list, object_list) { vm_pindex_t idx, fidx; vm_pindex_t osize; vm_paddr_t pa = -1; int rcount; vm_page_t m; db_printf("new object: %p\n", (void *)object); if (nl > 18) { c = cngetc(); if (c != ' ') return; nl = 0; } nl++; rcount = 0; fidx = 0; osize = object->size; if (osize > 128) osize = 128; for (idx = 0; idx < osize; idx++) { m = vm_page_lookup(object, idx); if (m == NULL) { if (rcount) { db_printf(" index(%ld)run(%d)pa(0x%lx)\n", (long)fidx, rcount, (long)pa); if (nl > 18) { c = cngetc(); if (c != ' ') return; nl = 0; } nl++; rcount = 0; } continue; } if (rcount && (VM_PAGE_TO_PHYS(m) == pa + rcount * PAGE_SIZE)) { ++rcount; continue; } if (rcount) { db_printf(" index(%ld)run(%d)pa(0x%lx)\n", (long)fidx, rcount, (long)pa); if (nl > 18) { c = cngetc(); if (c != ' ') return; nl = 0; } nl++; } fidx = idx; pa = VM_PAGE_TO_PHYS(m); rcount = 1; } if (rcount) { db_printf(" index(%ld)run(%d)pa(0x%lx)\n", (long)fidx, rcount, (long)pa); if (nl > 18) { c = cngetc(); if (c != ' ') return; nl = 0; } nl++; } } } #endif /* DDB */ Index: projects/cambria/tools/regression/bin/sh/builtins/type1.0.stderr =================================================================== --- projects/cambria/tools/regression/bin/sh/builtins/type1.0.stderr (revision 186459) +++ projects/cambria/tools/regression/bin/sh/builtins/type1.0.stderr (revision 186460) Property changes on: projects/cambria/tools/regression/bin/sh/builtins/type1.0.stderr ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/tools/regression/bin/sh/builtins/type1.0.stderr:r186349-186457 Index: projects/cambria/tools/tools/README =================================================================== --- projects/cambria/tools/tools/README (revision 186459) +++ projects/cambria/tools/tools/README (revision 186460) @@ -1,59 +1,61 @@ # $FreeBSD$ This directory is for tools. A tool is something which is sometimes useful, and doesn't fit any of the other categories. Please make a subdir per program, and add a brief description to this file. ansify Convert K&R-style function definitions to ANSI style ath Tools specific to the Atheros 802.11 support backout_commit A tool for reading in a commit message and generating a script that will backout the commit. commitsdb A tool for reconstructing commit history using md5 checksums of the commit logs. crypto Test and exercise tools related to the crypto framework diffburst OBSOLETE: equivalent functionality is available via split -p. For example: "split -p ^diff < patchfile". See split(1). editing Editor modes and the like to help editing FreeBSD code. epfe Extract printing filter examples from printing.sgml. +ether_reflect An Ethernet packet reflector for low level testing. find-sb Scan a disk for possible filesystem superblocks. gdb_regofs A simple tool that prints out a register offset table for mapping gdb(1) register numbers to struct reg and struct fpreg offsets. The tool is useful on selected platforms only. genericize Turn a kernel config into something that can more easily be diffed against the appropriate GENERIC. hcomp Compress header files by removing comments and whitespace. html-mv Rename HTML generated filenames to human readable filenames. ifinfo Uses the interface MIB to print out all the information an interface exports in an ugly form. iwi Tools specific to the Intel PRO/Wireless 2200BG/2225BG/2915ABG support. kdrv KernelDriver; add/list/remove third-party kernel driver source to/in/from a kernel source tree. kernelcruft Shellscript to find orphaned *.c files in /sys kerninclude Shellscript to find unused #includes in the kernel. kernxref Shellscript to cross reference symbols in the LINT kernel. kttcp An in-kernel version of the ttcp network performance tool +mctest A multicast test program mfc Merge a directory from HEAD to a branch where it does not already exist and other MFC related script(s). mid Create a Message-ID database for mailing lists. ncpus Count the number of processors nxge A diagnostic tool for the nxge(4) driver pciid Generate src/share/misc/pci_vendors. pciroms A tool for dumping PCI ROM images. WARNING: alpha quality. pirtool A tool for dumping the $PIR table on i386 machines at runtime. portsinfo Generate list of new ports for last two weeks. prstats Generate statistics about the PR database. recoverdisk Copy as much data as possible from a defective disk. scsi-defects Get at the primary or grown defect list of a SCSI disk. sysdoc Build a manual page with available sysctls for a specific kernel configuration. tinybsd Script to build FreeBSD embedded systems. track Track the progress of a world / kernel build vop_table Generates a HTML document that shows all the VOP's in the kernel. whereintheworld Summarizes "make world" output. Index: projects/cambria/tools/tools/ether_reflect/Makefile =================================================================== --- projects/cambria/tools/tools/ether_reflect/Makefile (nonexistent) +++ projects/cambria/tools/tools/ether_reflect/Makefile (revision 186460) @@ -0,0 +1,10 @@ +# +# $FreeBSD$ +# +# A Makefile that builds both the ether_reflect program and its manual page. + +PROG= ether_reflect + +LDADD+= -lpcap + +.include Property changes on: projects/cambria/tools/tools/ether_reflect/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/cambria/tools/tools/ether_reflect/ether_reflect.1 =================================================================== --- projects/cambria/tools/tools/ether_reflect/ether_reflect.1 (nonexistent) +++ projects/cambria/tools/tools/ether_reflect/ether_reflect.1 (revision 186460) @@ -0,0 +1,108 @@ +.\" Copyright (c) 2008 George V. Neville-Neil +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd December 23, 2008 +.Dt ether_reflect 1 +.Os +.Sh NAME +.Nm ether_reflect +.Nd "reflect ethernet packets" +.Sh SYNOPSIS +.Nm +.Op Fl a Ar ethernet address +.Op Fl e Ar ethertype +.Op Fl i Ar interface +.Op Fl t Ar timeout +.Op Fl p +.Op Fl d +.Sh DESCRIPTION +The +.Nm +command implements a simple ethernet packet reflector using the +.Xr PCAP 3 +library and +.Xr bpf 4 , +the Berkeley Packet Filter. The program is useful primarily to test +the low level round trip time of packets through an Ethernet interface +and/or a switch. Network protocols, such as IP, and the network stack +in general are never invoked, only the device driver that implements +the particular interface is executed. As the +.Nm +command uses the +.Xr bpf 4 +device the user must have root privileges to execute this program. +.Pp +The options are as follows: +.Bl -tag -width ".Fl d Ar argument" +.It Fl a Ar address +Instead of reversing the ethernet destination and source addresses +supply a different destination ethernet address for each packet +received. +.It Fl e Ar ether type +Use a different ethertype than the default, 0x8822, which is the IEEE +ether type for driver testing. +.It Fl i Ar interface +Network interface, which can be found with ifconfig(1). +.It Fl t Ar timeout +The time, in milliseconds, to wait for a packet. Lower times decrease +latency at the cost of CPU. +.It Fl p +Set the device into promiscuous mode before testing. This is not +usually necessary. +.It Fl d +Debug output. Print various small pieces of debug information. +.El +.Sh EXAMPLES +The following is an example of a typical usage +of the +.Nm +command: +.Pp +.Dl "ether_reflect -i em0 -t 1" +.Pp +Reflect all test packets, those with an ether type of 0x8822, which +are seen on ineterface em0. The timeout is 1 millisecond. +.Pp +.Dl "ether_reflect -i em0 -a 00:00:00:aa:bb:cc -t 1" +.Pp +Rewrite the destination address in each packet to 00:00:00:aa:bb:cc +before reflecting the packet. +.Sh SEE ALSO +.Xr ifconfig 8 , +.Xr tcpdump 1 , +.Xr pcap 4 , +.Xr bpf 2 . +.Sh HISTORY +The +.Nm +program first appeared in +.Fx 8.0 . +.Sh AUTHORS +This +manual page was written by +.An George V. Neville-Neil Aq gnn@FreeBSD.org . +.Sh BUGS +Should be reported to the author or to net@freebsd.org. Property changes on: projects/cambria/tools/tools/ether_reflect/ether_reflect.1 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/cambria/tools/tools/ether_reflect/ether_reflect.c =================================================================== --- projects/cambria/tools/tools/ether_reflect/ether_reflect.c (nonexistent) +++ projects/cambria/tools/tools/ether_reflect/ether_reflect.c (revision 186460) @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2008, Neville-Neil Consulting + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + * + * Author: George V. Neville-Neil + * + * Purpose: This program uses libpcap to read packets from the network + * of a specific ethertype (default is 0x8822) and reflects them back + * out the same interface with their destination and source mac + * addresses reversed. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +#define ETHER_TYPE_TEST "0x8822" +#define SNAPLEN 96 +#define MAXPROG 128 + +char errbuf[PCAP_ERRBUF_SIZE]; + +void usage(char* message) { + if (message != NULL) + printf ("error: %s\n", message); + printf("usage: ether_reflect -i interface -e ethertype " + "-a address -t timeout -p -d\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int ch; + int debug = 0, promisc = 0; + int timeout = 100; + bpf_u_int32 localnet=0, netmask=0; + unsigned int error = 0; + char *interface = NULL; + char *proto = ETHER_TYPE_TEST; + char in_string[MAXPROG]; + char tmp[ETHER_ADDR_LEN]; + char addr[ETHER_ADDR_LEN]; + char *user_addr = NULL; + pcap_t *capture; + struct bpf_program program; + struct pcap_pkthdr *header; + unsigned char *packet = NULL; + + while ((ch = getopt(argc, argv, "a:e:i:t:pd")) != -1) { + switch (ch) { + case 'a': + user_addr = optarg; + break; + case 'e': + proto = optarg; + break; + case 'i': + interface = optarg; + break; + case 'p': + promisc = 1; + break; + case 't': + timeout = atoi(optarg); + break; + case 'd': + debug = 1; + break; + case '?': + default: + usage("invalid arguments"); + } + } + argc -= optind; + argv += optind; + + if (interface == NULL) + usage("You must specify an interface"); + + if (user_addr != NULL) + ether_aton_r(user_addr, (struct ether_addr *)&tmp); + + if ((capture = pcap_open_live(interface, SNAPLEN, promisc, timeout, + &errbuf[0])) == NULL) + usage(errbuf); + + snprintf(&in_string[0], MAXPROG, "ether proto %s\n", proto); + + if (pcap_lookupnet(interface, &localnet, &netmask, errbuf) < 0) + usage(errbuf); + + if (pcap_compile(capture, &program, in_string, 1, netmask) < 0) + usage(errbuf); + + if (pcap_setfilter(capture, &program) < 0) + usage(errbuf); + + if (pcap_setdirection(capture, PCAP_D_IN) < 0) + usage(errbuf); + + while (1) { + error = pcap_next_ex(capture, &header, + (const unsigned char **)&packet); + if (error == 0) + continue; + if (error == -1) + usage("packet read error"); + if (error == -2) + usage("savefile? invalid!"); + + if (debug) { + printf ("got packet of %d length\n", header->len); + printf ("header %s\n", + ether_ntoa((const struct ether_addr*) + &packet[0])); + printf ("header %s\n", + ether_ntoa((const struct ether_addr*) + &packet[ETHER_ADDR_LEN])); + } + + /* + * If the user did not supply an address then we simply + * reverse the source and destination addresses. + */ + if (user_addr == NULL) { + bcopy(packet, &tmp, ETHER_ADDR_LEN); + bcopy(&packet[ETHER_ADDR_LEN], packet, ETHER_ADDR_LEN); + bcopy(&tmp, &packet[ETHER_ADDR_LEN], ETHER_ADDR_LEN); + } else { + bcopy(&tmp, packet, ETHER_ADDR_LEN); + } + if (pcap_inject(capture, packet, header->len) < 0) + if (debug) + pcap_perror(capture, "pcap_inject"); + } +} Property changes on: projects/cambria/tools/tools/ether_reflect/ether_reflect.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/cambria/tools/tools/mctest/mctest.1 =================================================================== --- projects/cambria/tools/tools/mctest/mctest.1 (revision 186459) +++ projects/cambria/tools/tools/mctest/mctest.1 (revision 186460) @@ -1,113 +1,113 @@ .\" Copyright (c) 2008 George V. Neville-Neil .\" 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. .\" .\" $FreeBSD$ .\" .Dd April 3, 2008 .Dt mctest 1 .Os .Sh NAME .Nm mctest .Nd "multicast test" .Sh SYNOPSIS .Nm .Op Fl i Ar interface .Op Fl g Ar group .Op Fl b Ar base port .Op Fl n Ar number .Op Fl s Ar size .Op Fl t Ar inter-packet gap .Op Fl M Ar number of clients (source only) .Op Fl m Ar my client number (sink only) .Op Fl r .Sh DESCRIPTION The .Nm command implements a multicast test which involved a source and a sink. The source sends packets to a pre-configured multicast address over the interface given as a command line argument. The sink listens for multicast packets, records the time at which they're received and then reflects them back over unicast to the source. When the source has captured all the reflected packets it prints out the round trip time of each. .Pp The source prints out the round trip time of packets sent to the sinks. The sink prints out the time between the packets received. .Pp The options are as follows: .Bl -tag -width ".Fl d Ar argument" .It Fl i Ar interface Network interface, which can be found with ifconfig(1). -.It Fl i Ar group +.It Fl g Ar group Multicast group -.It Fl i Ar base port +.It Fl b Ar base port Port on which to listen .It Fl s Ar size Packet size in bytes. .It Fl n Ar number Number of packets. .It Fl t Ar gap Inter-packet gap in nanoseconds. .It Fl M Ar clients The number of clients that are listening .It Fl m Ar my number The client's number 0, 1, etc. .It Fl r This version of .Nm is the receiver aka the sink. This option MUST only be used with one copy of the program at a time. .El .Sh EXAMPLES The following is an example of a typical usage of the .Nm command: .Pp Source .Dl "mctest -i em0 -M 1 -s 1024 -n 100 -t 1" Sink .Dl "mctest -i em0 -m 0 -s 1024 -n 100 -r" .Pp Send 100 packets of 1024 bytes, with an inter-packet gap of 1 nanosecond. .Pp Gaps are measured with .Xr nanosleep 2 , and so are not accurate down to nanoseconds but depend on the setting of kern.hz. .Sh SEE ALSO .Xr ifconfig 8 , .Xr netstat 1 , .Xr nanosleep 2 . .Sh HISTORY The .Nm program first appeared in .Fx 7.0 . .Sh AUTHORS This manual page was written by .An George V. Neville-Neil Aq gnn@FreeBSD.org . .Sh BUGS Should be reported to the author or to net@freebsd.org. Index: projects/cambria/usr.bin/elf2aout/Makefile =================================================================== --- projects/cambria/usr.bin/elf2aout/Makefile (revision 186459) +++ projects/cambria/usr.bin/elf2aout/Makefile (revision 186460) @@ -1,8 +1,8 @@ # $FreeBSD$ PROG= elf2aout -NO_MAN= + NO_WERROR= WARNS?= 5 .include Index: projects/cambria/usr.bin/elf2aout/elf2aout.1 =================================================================== --- projects/cambria/usr.bin/elf2aout/elf2aout.1 (nonexistent) +++ projects/cambria/usr.bin/elf2aout/elf2aout.1 (revision 186460) @@ -0,0 +1,64 @@ +.\" Copyright (c) 2008 Tom Rhodes +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd December 23, 2008 +.Dt ELF2AOUT 1 +.Os +.Sh NAME +.Nm elf2aout +.Nd "Convert ELF binary to a.out format" +.Sh SYNOPSIS +.Nm +.Op Fl o outfile +.Ar infile +.Sh DESCRIPTION +The +.Nm +utility is used to convert an ELF formatted binary, +namely a kernel, to an a.out formatted one. +Most +.Tn OpenBoot +firmware require an a.out format or FCode boot image +and this utility is designed to accommodate. +If +.Ar infile +is not in ELF format, an error message will be presented. +.Sh SEE ALSO +.Xr elf 3 , +.Xr a.out 5 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 4.6 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility was written by +.An Jake Burkholder Aq jake@FreeBSD.org . +This manual page was written by +.An Tom Rhodes Aq trhodes@FreeBSD.org . Property changes on: projects/cambria/usr.bin/elf2aout/elf2aout.1 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/cambria/usr.bin/ncal/ncal.1 =================================================================== --- projects/cambria/usr.bin/ncal/ncal.1 (revision 186459) +++ projects/cambria/usr.bin/ncal/ncal.1 (revision 186460) @@ -1,140 +1,148 @@ .\" Copyright (c) 1997 Wolfgang Helbig .\" 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. .\" .\" $FreeBSD$ .\" .Dd October 23, 2005 .Dt CAL 1 .Os .Sh NAME .Nm cal , .Nm ncal -.Nd displays a calendar and the date of easter +.Nd displays a calendar and the date of Easter .Sh SYNOPSIS .Nm .Op Fl jy .Oo .Op Ar month .Ar year .Oc .Nm .Op Fl j .Fl m Ar month .Op Ar year .Nm ncal .Op Fl jJpwy .Op Fl s Ar country_code .Oo .Op Ar month .Ar year .Oc .Nm ncal .Op Fl Jeo .Op Ar year .Sh DESCRIPTION The .Nm utility displays a simple calendar in traditional format and .Nm ncal -offers an alternative layout, more options and the date of easter. +offers an alternative layout, more options and the date of Easter. The new format is a little cramped but it makes a year fit on a 25x80 terminal. If arguments are not specified, the current month is displayed. .Pp The options are as follows: .Bl -tag -width indent .It Fl J Display Julian Calendar, if combined with the .Fl e -option, display date of easter according to the Julian Calendar. +option, display date of Easter according to the Julian Calendar. .It Fl e -Display date of easter (for western churches). +Display date of Easter (for western churches). .It Fl j Display Julian days (days one-based, numbered from January 1). .It Fl m Ar month Display the specified .Ar month . +If +.Ar month +is specified as a decimal number, it may be followed by the letter +.Ql f +or +.Ql p +to indicate the following or preceding month of that number, +respectively. .It Fl o -Display date of orthodox easter (Greek and Russian +Display date of Orthodox Easter (Greek and Russian Orthodox Churches). .It Fl p Print the country codes and switching days from Julian to Gregorian Calendar as they are assumed by .Nm ncal . The country code as determined from the local environment is marked with an asterisk. .It Fl s Ar country_code Assume the switch from Julian to Gregorian Calendar at the date associated with the .Ar country_code . If not specified, .Nm ncal tries to guess the switch date from the local environment or falls back to September 2, 1752. This was when Great Britain and her colonies switched to the Gregorian Calendar. .It Fl w Print the number of the week below each week column. .It Fl y Display a calendar for the specified year. .El .Pp -A single parameter specifies the year (1 - 9999) to be displayed; +A single parameter specifies the year (1\(en9999) to be displayed; note the year must be fully specified: .Dq Li cal 89 will .Em not display a calendar for 1989. Two parameters denote the month and year; the month is either a number between 1 and 12, or a full or abbreviated name as specified by the current locale. Month and year default to those of the current system clock and time zone (so .Dq Li cal -m 8 will display a calendar for the month of August in the current year). .Pp -A year starts on Jan 1. +A year starts on January 1. .Sh SEE ALSO .Xr calendar 3 , .Xr strftime 3 .Sh HISTORY A .Nm command appeared in .At v5 . The .Nm ncal command appeared in .Fx 2.2.6 . .Sh AUTHORS The .Nm ncal command and manual were written by .An Wolfgang Helbig Aq helbig@FreeBSD.org . .Sh BUGS -The assignment of Julian\(emGregorian switching dates to +The assignment of Julian\(enGregorian switching dates to country codes is historically naive for many countries. .Pp The .Nm utility does not recognize multibyte characters. Index: projects/cambria/usr.bin/ncal/ncal.c =================================================================== --- projects/cambria/usr.bin/ncal/ncal.c (revision 186459) +++ projects/cambria/usr.bin/ncal/ncal.c (revision 186460) @@ -1,876 +1,892 @@ /*- * Copyright (c) 1997 Wolfgang Helbig * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include /* Width of one month with backward compatibility */ #define MONTH_WIDTH_B_J 27 #define MONTH_WIDTH_B 20 #define MONTH_WIDTH_J 24 #define MONTH_WIDTH 18 #define MAX_WIDTH 28 typedef struct date date; struct monthlines { char name[MAX_WIDTH + 1]; char lines[7][MAX_WIDTH + 1]; char weeks[MAX_WIDTH + 1]; }; struct weekdays { char names[7][4]; }; /* The switches from Julian to Gregorian in some countries */ static struct djswitch { const char *cc; /* Country code according to ISO 3166 */ const char *nm; /* Name of country */ date dt; /* Last day of Julian calendar */ } switches[] = { {"AL", "Albania", {1912, 11, 30}}, {"AT", "Austria", {1583, 10, 5}}, {"AU", "Australia", {1752, 9, 2}}, {"BE", "Belgium", {1582, 12, 14}}, {"BG", "Bulgaria", {1916, 3, 18}}, {"CA", "Canada", {1752, 9, 2}}, {"CH", "Switzerland", {1655, 2, 28}}, {"CN", "China", {1911, 12, 18}}, {"CZ", "Czech Republic",{1584, 1, 6}}, {"DE", "Germany", {1700, 2, 18}}, {"DK", "Denmark", {1700, 2, 18}}, {"ES", "Spain", {1582, 10, 4}}, {"FI", "Finland", {1753, 2, 17}}, {"FR", "France", {1582, 12, 9}}, {"GB", "United Kingdom",{1752, 9, 2}}, {"GR", "Greece", {1924, 3, 9}}, {"HU", "Hungary", {1587, 10, 21}}, {"IS", "Iceland", {1700, 11, 16}}, {"IT", "Italy", {1582, 10, 4}}, {"JP", "Japan", {1918, 12, 18}}, {"LI", "Lithuania", {1918, 2, 1}}, {"LN", "Latin", {9999, 05, 31}}, {"LU", "Luxembourg", {1582, 12, 14}}, {"LV", "Latvia", {1918, 2, 1}}, {"NL", "Netherlands", {1582, 12, 14}}, {"NO", "Norway", {1700, 2, 18}}, {"PL", "Poland", {1582, 10, 4}}, {"PT", "Portugal", {1582, 10, 4}}, {"RO", "Romania", {1919, 3, 31}}, {"RU", "Russia", {1918, 1, 31}}, {"SI", "Slovenia", {1919, 3, 4}}, {"SW", "Sweden", {1753, 2, 17}}, {"TR", "Turkey", {1926, 12, 18}}, {"US", "United States", {1752, 9, 2}}, {"YU", "Yugoslavia", {1919, 3, 4}} }; struct djswitch *dftswitch = switches + sizeof(switches) / sizeof(struct djswitch) - 2; /* default switch (should be "US") */ /* Table used to print day of month and week numbers */ char daystr[] = " 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" " 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" " 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47" " 48 49 50 51 52 53"; /* Table used to print day of year and week numbers */ char jdaystr[] = " 1 2 3 4 5 6 7 8 9" " 10 11 12 13 14 15 16 17 18 19" " 20 21 22 23 24 25 26 27 28 29" " 30 31 32 33 34 35 36 37 38 39" " 40 41 42 43 44 45 46 47 48 49" " 50 51 52 53 54 55 56 57 58 59" " 60 61 62 63 64 65 66 67 68 69" " 70 71 72 73 74 75 76 77 78 79" " 80 81 82 83 84 85 86 87 88 89" " 90 91 92 93 94 95 96 97 98 99" " 100 101 102 103 104 105 106 107 108 109" " 110 111 112 113 114 115 116 117 118 119" " 120 121 122 123 124 125 126 127 128 129" " 130 131 132 133 134 135 136 137 138 139" " 140 141 142 143 144 145 146 147 148 149" " 150 151 152 153 154 155 156 157 158 159" " 160 161 162 163 164 165 166 167 168 169" " 170 171 172 173 174 175 176 177 178 179" " 180 181 182 183 184 185 186 187 188 189" " 190 191 192 193 194 195 196 197 198 199" " 200 201 202 203 204 205 206 207 208 209" " 210 211 212 213 214 215 216 217 218 219" " 220 221 222 223 224 225 226 227 228 229" " 230 231 232 233 234 235 236 237 238 239" " 240 241 242 243 244 245 246 247 248 249" " 250 251 252 253 254 255 256 257 258 259" " 260 261 262 263 264 265 266 267 268 269" " 270 271 272 273 274 275 276 277 278 279" " 280 281 282 283 284 285 286 287 288 289" " 290 291 292 293 294 295 296 297 298 299" " 300 301 302 303 304 305 306 307 308 309" " 310 311 312 313 314 315 316 317 318 319" " 320 321 322 323 324 325 326 327 328 329" " 330 331 332 333 334 335 336 337 338 339" " 340 341 342 343 344 345 346 347 348 349" " 350 351 352 353 354 355 356 357 358 359" " 360 361 362 363 364 365 366"; int flag_weeks; /* user wants number of week */ int nswitch; /* user defined switch date */ int nswitchb; /* switch date for backward compatibility */ char *center(char *s, char *t, int w); void mkmonth(int year, int month, int jd_flag, struct monthlines * monthl); void mkmonthb(int year, int month, int jd_flag, struct monthlines * monthl); void mkweekdays(struct weekdays * wds); -int parsemonth(const char *s); +int parsemonth(const char *s, int *m, int *y); void printcc(void); void printeaster(int year, int julian, int orthodox); void printmonth(int year, int month, int jd_flag); void printmonthb(int year, int month, int jd_flag); void printyear(int year, int jd_flag); void printyearb(int year, int jd_flag); int firstday(int y, int m); date *sdate(int ndays, struct date * d); date *sdateb(int ndays, struct date * d); int sndays(struct date * d); int sndaysb(struct date * d); static void usage(void); int weekdayb(int nd); int main(int argc, char *argv[]) { struct djswitch *p, *q; /* to search user defined switch date */ date never = {10000, 1, 1}; /* outside valid range of dates */ date ukswitch = {1752, 9, 2};/* switch date for Great Britain */ int ch; /* holds the option character */ int m = 0; /* month */ int y = 0; /* year */ int flag_backward = 0; /* user called cal--backward compat. */ int flag_hole_year = 0; /* user wants the whole year */ int flag_julian_cal = 0; /* user wants Julian Calendar */ int flag_julian_day = 0; /* user wants the Julian day * numbers */ int flag_orthodox = 0; /* use wants Orthodox easter */ int flag_easter = 0; /* use wants easter date */ char *cp; /* character pointer */ char *flag_month = NULL; /* requested month as string */ const char *locale; /* locale to get country code */ /* * Use locale to determine the country code, * and use the country code to determine the default * switchdate and date format from the switches table. */ if (setlocale(LC_ALL, "") == NULL) warn("setlocale"); locale = setlocale(LC_TIME, NULL); if (locale == NULL || strcmp(locale, "C") == 0 || strcmp(locale, "POSIX") == 0 || strcmp(locale, "ASCII") == 0 || strcmp(locale, "US-ASCII") == 0) locale = "_US"; q = switches + sizeof(switches) / sizeof(struct djswitch); for (p = switches; p != q; p++) if ((cp = strstr(locale, p->cc)) != NULL && *(cp - 1) == '_') break; if (p == q) { nswitch = ndaysj(&dftswitch->dt); } else { nswitch = ndaysj(&p->dt); dftswitch = p; } /* * Get the filename portion of argv[0] and set flag_backward if * this program is called "cal". */ cp = strrchr(argv[0], '/'); cp = (cp == NULL) ? argv[0] : cp + 1; if (strcmp("cal", cp) == 0) flag_backward = 1; /* Set the switch date to United Kingdom if backwards compatible */ if (flag_backward) nswitchb = ndaysj(&ukswitch); while ((ch = getopt(argc, argv, "Jejm:ops:wy")) != -1) switch (ch) { case 'J': if (flag_backward) usage(); nswitch = ndaysj(&never); flag_julian_cal = 1; break; case 'e': if (flag_backward) usage(); flag_easter = 1; break; case 'j': flag_julian_day = 1; break; case 'm': flag_month = optarg; break; case 'o': if (flag_backward) usage(); flag_orthodox = 1; flag_easter = 1; break; case 'p': if (flag_backward) usage(); printcc(); return (0); break; case 's': if (flag_backward) usage(); q = switches + sizeof(switches) / sizeof(struct djswitch); for (p = switches; p != q && strcmp(p->cc, optarg) != 0; p++) ; if (p == q) errx(EX_USAGE, "%s: invalid country code", optarg); nswitch = ndaysj(&(p->dt)); break; case 'w': if (flag_backward) usage(); flag_weeks = 1; break; case 'y': flag_hole_year = 1; break; default: usage(); } argc -= optind; argv += optind; switch (argc) { case 2: if (flag_easter) usage(); flag_month = *argv++; /* FALLTHROUGH */ case 1: y = atoi(*argv++); if (y < 1 || y > 9999) errx(EX_USAGE, "year %d not in range 1..9999", y); break; case 0: { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); y = tm->tm_year + 1900; m = tm->tm_mon + 1; } break; default: usage(); } if (flag_month != NULL) { - m = parsemonth(flag_month); - if (m < 1 || m > 12) + if (parsemonth(flag_month, &m, &y)) { errx(EX_USAGE, "%s is neither a month number (1..12) nor a name", flag_month); + } } if (flag_easter) printeaster(y, flag_julian_cal, flag_orthodox); else if (argc == 1 || flag_hole_year) if (flag_backward) printyearb(y, flag_julian_day); else printyear(y, flag_julian_day); else if (flag_backward) printmonthb(y, m, flag_julian_day); else printmonth(y, m, flag_julian_day); return (0); } static void usage(void) { fputs( "usage: cal [-jy] [[month] year]\n" " cal [-j] [-m month] [year]\n" " ncal [-Jjpwy] [-s country_code] [[month] year]\n" " ncal [-Jeo] [year]\n", stderr); exit(EX_USAGE); } /* print the assumed switches for all countries */ void printcc(void) { struct djswitch *p; int n; /* number of lines to print */ int m; /* offset from left to right table entry on the same line */ #define FSTR "%c%s %-15s%4d-%02d-%02d" #define DFLT(p) ((p) == dftswitch ? '*' : ' ') #define FSTRARG(p) DFLT(p), (p)->cc, (p)->nm, (p)->dt.y, (p)->dt.m, (p)->dt.d n = sizeof(switches) / sizeof(struct djswitch); m = (n + 1) / 2; n /= 2; for (p = switches; p != switches + n; p++) printf(FSTR" "FSTR"\n", FSTRARG(p), FSTRARG(p+m)); if (m != n) printf(FSTR"\n", FSTRARG(p)); } /* print the date of easter sunday */ void printeaster(int y, int julian, int orthodox) { date dt; struct tm tm; char buf[80]; static int d_first = -1; if (d_first < 0) d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); /* force orthodox easter for years before 1583 */ if (y < 1583) orthodox = 1; if (orthodox) if (julian) easteroj(y, &dt); else easterog(y, &dt); else easterg(y, &dt); memset(&tm, 0, sizeof(tm)); tm.tm_year = dt.y - 1900; tm.tm_mon = dt.m - 1; tm.tm_mday = dt.d; strftime(buf, sizeof(buf), d_first ? "%e %B %Y" : "%B %e %Y", &tm); printf("%s\n", buf); } void printmonth(int y, int m, int jd_flag) { struct monthlines month; struct weekdays wds; int i; mkmonth(y, m - 1, jd_flag, &month); mkweekdays(&wds); printf(" %s %d\n", month.name, y); for (i = 0; i != 7; i++) printf("%.2s%s\n", wds.names[i], month.lines[i]); if (flag_weeks) printf(" %s\n", month.weeks); } void printmonthb(int y, int m, int jd_flag) { struct monthlines month; struct weekdays wds; char s[MAX_WIDTH], t[MAX_WIDTH]; int i; int mw; mkmonthb(y, m - 1, jd_flag, &month); mkweekdays(&wds); mw = jd_flag ? MONTH_WIDTH_B_J : MONTH_WIDTH_B; sprintf(s, "%s %d", month.name, y); printf("%s\n", center(t, s, mw)); if (jd_flag) printf(" %s %s %s %s %s %s %.2s\n", wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5]); else printf("%s%s%s%s%s%s%.2s\n", wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5]); for (i = 0; i != 6; i++) printf("%s\n", month.lines[i]+1); } void printyear(int y, int jd_flag) { struct monthlines year[12]; struct weekdays wds; char s[80], t[80]; int i, j; int mpl; int mw; for (i = 0; i != 12; i++) mkmonth(y, i, jd_flag, year + i); mkweekdays(&wds); mpl = jd_flag ? 3 : 4; mw = jd_flag ? MONTH_WIDTH_J : MONTH_WIDTH; sprintf(s, "%d", y); printf("%s\n", center(t, s, mpl * mw)); for (j = 0; j != 12; j += mpl) { printf(" %-*s%-*s", mw, year[j].name, mw, year[j + 1].name); if (mpl == 3) printf("%s\n", year[j + 2].name); else printf("%-*s%s\n", mw, year[j + 2].name, year[j + 3].name); for (i = 0; i != 7; i++) { printf("%.2s%-*s%-*s", wds.names[i], mw, year[j].lines[i], mw, year[j + 1].lines[i]); if (mpl == 3) printf("%s\n", year[j + 2].lines[i]); else printf("%-*s%s\n", mw, year[j + 2].lines[i], year[j + 3].lines[i]); } if (flag_weeks) { if (mpl == 3) printf(" %-*s%-*s%-s\n", mw, year[j].weeks, mw, year[j + 1].weeks, year[j + 2].weeks); else printf(" %-*s%-*s%-*s%-s\n", mw, year[j].weeks, mw, year[j + 1].weeks, mw, year[j + 2].weeks, year[j + 3].weeks); } } } void printyearb(int y, int jd_flag) { struct monthlines year[12]; struct weekdays wds; char s[80], t[80]; int i, j; int mpl; int mw; for (i = 0; i != 12; i++) mkmonthb(y, i, jd_flag, year + i); mkweekdays(&wds); mpl = jd_flag ? 2 : 3; mw = jd_flag ? MONTH_WIDTH_B_J : MONTH_WIDTH_B; sprintf(s, "%d", y); printf("%s\n\n", center(t, s, mw * mpl + mpl)); for (j = 0; j != 12; j += mpl) { printf("%-*s ", mw, center(s, year[j].name, mw)); if (mpl == 2) printf("%s\n", center(s, year[j + 1].name, mw)); else printf("%-*s %s\n", mw, center(s, year[j + 1].name, mw), center(t, year[j + 2].name, mw)); if (mpl == 2) printf(" %s %s %s %s %s %s %s " " %s %s %s %s %s %s %.2s\n", wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5], wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5]); else printf("%s%s%s%s%s%s%s " "%s%s%s%s%s%s%s " "%s%s%s%s%s%s%.2s\n", wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5], wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5], wds.names[6], wds.names[0], wds.names[1], wds.names[2], wds.names[3], wds.names[4], wds.names[5]); for (i = 0; i != 6; i++) { if (mpl == 2) printf("%-*s %s\n", mw, year[j].lines[i]+1, year[j + 1].lines[i]+1); else printf("%-*s %-*s %s\n", mw, year[j].lines[i]+1, mw, year[j + 1].lines[i]+1, year[j + 2].lines[i]+1); } } } void mkmonth(int y, int m, int jd_flag, struct monthlines *mlines) { struct tm tm; /* for strftime printing local names of * months */ date dt; /* handy date */ int dw; /* width of numbers */ int first; /* first day of month */ int firstm; /* first day of first week of month */ int i, j, k; /* just indices */ int last; /* the first day of next month */ int jan1 = 0; /* the first day of this year */ char *ds; /* pointer to day strings (daystr or * jdaystr) */ /* Set name of month. */ memset(&tm, 0, sizeof(tm)); tm.tm_mon = m; strftime(mlines->name, sizeof(mlines->name), "%OB", &tm); mlines->name[0] = toupper((unsigned char)mlines->name[0]); /* * Set first and last to the day number of the first day of this * month and the first day of next month respectively. Set jan1 to * the day number of the first day of this year. */ first = firstday(y, m + 1); if (m == 11) last = firstday(y + 1, 1); else last = firstday(y, m + 2); if (jd_flag) jan1 = firstday(y, 1); /* * Set firstm to the day number of monday of the first week of * this month. (This might be in the last month) */ firstm = first - weekday(first); /* Set ds (daystring) and dw (daywidth) according to the jd_flag */ if (jd_flag) { ds = jdaystr; dw = 4; } else { ds = daystr; dw = 3; } /* * Fill the lines with day of month or day of year (julian day) * line index: i, each line is one weekday. column index: j, each * column is one day number. print column index: k. */ for (i = 0; i != 7; i++) { for (j = firstm + i, k = 0; j < last; j += 7, k += dw) if (j >= first) { if (jd_flag) dt.d = j - jan1 + 1; else sdate(j, &dt); memcpy(mlines->lines[i] + k, ds + dt.d * dw, dw); } else memcpy(mlines->lines[i] + k, " ", dw); mlines->lines[i][k] = '\0'; } /* fill the weeknumbers */ if (flag_weeks) { for (j = firstm, k = 0; j < last; k += dw, j += 7) if (j <= nswitch) memset(mlines->weeks + k, ' ', dw); else memcpy(mlines->weeks + k, ds + week(j, &i)*dw, dw); mlines->weeks[k] = '\0'; } } void mkmonthb(int y, int m, int jd_flag, struct monthlines *mlines) { struct tm tm; /* for strftime printing local names of * months */ date dt; /* handy date */ int dw; /* width of numbers */ int first; /* first day of month */ int firsts; /* sunday of first week of month */ int i, j, k; /* just indices */ int jan1 = 0; /* the first day of this year */ int last; /* the first day of next month */ char *ds; /* pointer to day strings (daystr or * jdaystr) */ /* Set ds (daystring) and dw (daywidth) according to the jd_flag */ if (jd_flag) { ds = jdaystr; dw = 4; } else { ds = daystr; dw = 3; } /* Set name of month centered */ memset(&tm, 0, sizeof(tm)); tm.tm_mon = m; strftime(mlines->name, sizeof(mlines->name), "%OB", &tm); mlines->name[0] = toupper((unsigned char)mlines->name[0]); /* * Set first and last to the day number of the first day of this * month and the first day of next month respectively. Set jan1 to * the day number of Jan 1st of this year. */ dt.y = y; dt.m = m + 1; dt.d = 1; first = sndaysb(&dt); if (m == 11) { dt.y = y + 1; dt.m = 1; dt.d = 1; } else { dt.y = y; dt.m = m + 2; dt.d = 1; } last = sndaysb(&dt); if (jd_flag) { dt.y = y; dt.m = 1; dt.d = 1; jan1 = sndaysb(&dt); } /* * Set firsts to the day number of sunday of the first week of * this month. (This might be in the last month) */ firsts = first - (weekday(first)+1) % 7; /* * Fill the lines with day of month or day of year (Julian day) * line index: i, each line is one week. column index: j, each * column is one day number. print column index: k. */ for (i = 0; i != 6; i++) { for (j = firsts + 7 * i, k = 0; j < last && k != dw * 7; j++, k += dw) if (j >= first) { if (jd_flag) dt.d = j - jan1 + 1; else sdateb(j, &dt); memcpy(mlines->lines[i] + k, ds + dt.d * dw, dw); } else memcpy(mlines->lines[i] + k, " ", dw); if (k == 0) mlines->lines[i][1] = '\0'; else mlines->lines[i][k] = '\0'; } } /* Put the local names of weekdays into the wds */ void mkweekdays(struct weekdays *wds) { int i, len; struct tm tm; char buf[20]; memset(&tm, 0, sizeof(tm)); for (i = 0; i != 7; i++) { tm.tm_wday = (i+1) % 7; strftime(buf, sizeof(buf), "%a", &tm); len = strlen(buf); if (len > 2) len = 2; strcpy(wds->names[i], " "); strncpy(wds->names[i] + 2 - len, buf, len); } } /* * Compute the day number of the first * existing date after the first day in month. * (the first day in month and even the month might not exist!) */ int firstday(int y, int m) { date dt; int nd; dt.y = y; dt.m = m; dt.d = 1; nd = sndays(&dt); for (;;) { sdate(nd, &dt); if ((dt.m >= m && dt.y == y) || dt.y > y) return (nd); else nd++; } /* NEVER REACHED */ } /* * Compute the number of days from date, obey the local switch from * Julian to Gregorian if specified by the user. */ int sndays(struct date *d) { if (nswitch != 0) if (nswitch < ndaysj(d)) return (ndaysg(d)); else return (ndaysj(d)); else return ndaysg(d); } /* * Compute the number of days from date, obey the switch from * Julian to Gregorian as used by UK and her colonies. */ int sndaysb(struct date *d) { if (nswitchb < ndaysj(d)) return (ndaysg(d)); else return (ndaysj(d)); } /* Inverse of sndays */ struct date * sdate(int nd, struct date *d) { if (nswitch < nd) return (gdate(nd, d)); else return (jdate(nd, d)); } /* Inverse of sndaysb */ struct date * sdateb(int nd, struct date *d) { if (nswitchb < nd) return (gdate(nd, d)); else return (jdate(nd, d)); } /* Center string t in string s of length w by putting enough leading blanks */ char * center(char *s, char *t, int w) { char blanks[80]; memset(blanks, ' ', sizeof(blanks)); sprintf(s, "%.*s%s", (int)(w - strlen(t)) / 2, blanks, t); return (s); } int -parsemonth(const char *s) +parsemonth(const char *s, int *m, int *y) { - int v; + int nm, ny; char *cp; struct tm tm; - v = (int)strtol(s, &cp, 10); - if (cp != s) - return (v); - if (strptime(s, "%B", &tm) != NULL) - return (tm.tm_mon + 1); - if (strptime(s, "%b", &tm) != NULL) - return (tm.tm_mon + 1); - return (0); + nm = (int)strtol(s, &cp, 10); + if (cp != s) { + ny = *y; + if (*cp == '\0') { + ; /* no special action */ + } else if (*cp == 'f' || *cp == 'F') { + if (nm <= *m) + ny++; + } else if (*cp == 'p' || *cp == 'P') { + if (nm >= *m) + ny--; + } else + return (1); + if (nm < 1 || nm > 12) + return 1; + *m = nm; + *y = ny; + return (0); + } + if (strptime(s, "%B", &tm) != NULL || strptime(s, "%b", &tm) != NULL) { + *m = tm.tm_mon + 1; + return (0); + } + return (1); } Index: projects/cambria/usr.sbin/burncd/burncd.c =================================================================== --- projects/cambria/usr.sbin/burncd/burncd.c (revision 186459) +++ projects/cambria/usr.sbin/burncd/burncd.c (revision 186460) @@ -1,733 +1,735 @@ /*- * Copyright (c) 2000,2001,2002 Søren Schmidt * 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, * without modification, immediately at the beginning of the file. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLOCKS 16 struct track_info { int file; char file_name[MAXPATHLEN + 1]; off_t file_size; int block_size; int block_type; int pregap; int addr; }; static struct track_info tracks[100]; -static int global_fd_for_cleanup, quiet, verbose, saved_block_size, notracks; +static int quiet, verbose, saved_block_size, notracks; +static volatile int global_fd_for_cleanup; void add_track(char *, int, int, int); void do_DAO(int fd, int, int); void do_TAO(int fd, int, int, int); void do_format(int, int, char *); int write_file(int fd, struct track_info *); int roundup_blocks(struct track_info *); void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int); void cleanup(int); void cleanup_flush(void); void cleanup_signal(int); void usage(void); int main(int argc, char **argv) { int arg, addr, ch, fd; int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0; int nogap = 0, speed = 4 * 177, test_write = 0, force = 0; int block_size = 0, block_type = 0, cdopen = 0, dvdrw = 0; const char *dev; if ((dev = getenv("CDROM")) == NULL) dev = "/dev/acd0"; while ((ch = getopt(argc, argv, "def:Flmnpqs:tv")) != -1) { switch (ch) { case 'd': dao = 1; break; case 'e': eject = 1; break; case 'f': dev = optarg; break; case 'F': force = 1; break; case 'l': list = 1; break; case 'm': multi = 1; break; case 'n': nogap = 1; break; case 'p': preemp = 1; break; case 'q': quiet = 1; break; case 's': if (strcasecmp("max", optarg) == 0) speed = CDR_MAX_SPEED; else speed = atoi(optarg) * 177; if (speed <= 0) errx(EX_USAGE, "Invalid speed: %s", optarg); break; case 't': test_write = 1; break; case 'v': verbose = 1; break; default: usage(); } } argc -= optind; argv += optind; if (argc == 0) usage(); if ((fd = open(dev, O_RDWR, 0)) < 0) err(EX_NOINPUT, "open(%s)", dev); if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0) err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)"); if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0) err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)"); global_fd_for_cleanup = fd; err_set_exit(cleanup); signal(SIGHUP, cleanup_signal); signal(SIGINT, cleanup_signal); signal(SIGTERM, cleanup_signal); for (arg = 0; arg < argc; arg++) { if (!strcasecmp(argv[arg], "fixate")) { fixate = 1; continue; } if (!strcasecmp(argv[arg], "eject")) { eject = 1; break; } if (!strcasecmp(argv[arg], "msinfo")) { struct ioc_read_toc_single_entry entry; struct ioc_toc_header header; if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0) err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)"); bzero(&entry, sizeof(struct ioc_read_toc_single_entry)); entry.address_format = CD_LBA_FORMAT; entry.track = header.ending_track; if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0) err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)"); if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); fprintf(stdout, "%d,%d\n", ntohl(entry.entry.addr.lba), addr); break; } if ((!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")) && !test_write) { int blank, pct, last = 0; if (!strcasecmp(argv[arg], "erase")) blank = CDR_B_ALL; else blank = CDR_B_MIN; if (!quiet) fprintf(stderr, "%sing CD, please wait..\r", blank == CDR_B_ALL ? "eras" : "blank"); if (ioctl(fd, CDRIOCBLANK, &blank) < 0) err(EX_IOERR, "ioctl(CDRIOCBLANK)"); while (1) { sleep(1); if (ioctl(fd, CDRIOCGETPROGRESS, &pct) == -1) err(EX_IOERR,"ioctl(CDRIOGETPROGRESS)"); if (pct > 0 && !quiet) fprintf(stderr, "%sing CD - %d %% done \r", blank == CDR_B_ALL ? "eras" : "blank", pct); if (pct == 100 || (pct == 0 && last > 90)) break; last = pct; } if (!quiet) printf("\n"); continue; } if (!strcasecmp(argv[arg], "format") && !test_write) { if (arg + 1 < argc && (!strcasecmp(argv[arg + 1], "dvd+rw") || !strcasecmp(argv[arg + 1], "dvd-rw"))) do_format(fd, force, argv[arg + 1]); else errx(EX_NOINPUT, "format media type invalid"); arg++; continue; } if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) { block_type = CDR_DB_RAW; block_size = 2352; continue; } if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) { block_type = CDR_DB_ROM_MODE1; block_size = 2048; continue; } if (!strcasecmp(argv[arg], "mode2")) { block_type = CDR_DB_ROM_MODE2; block_size = 2336; continue; } if (!strcasecmp(argv[arg], "xamode1")) { block_type = CDR_DB_XA_MODE1; block_size = 2048; continue; } if (!strcasecmp(argv[arg], "xamode2")) { block_type = CDR_DB_XA_MODE2_F2; block_size = 2324; continue; } if (!strcasecmp(argv[arg], "vcd")) { block_type = CDR_DB_XA_MODE2_F2; block_size = 2352; dao = 1; nogap = 1; continue; } if (!strcasecmp(argv[arg], "dvdrw")) { block_type = CDR_DB_ROM_MODE1; block_size = 2048; dvdrw = 1; continue; } if (!block_size) errx(EX_NOINPUT, "no data format selected"); if (list) { char file_buf[MAXPATHLEN + 1], *eol; FILE *fp; if ((fp = fopen(argv[arg], "r")) == NULL) err(EX_NOINPUT, "fopen(%s)", argv[arg]); while (fgets(file_buf, sizeof(file_buf), fp) != NULL) { if (*file_buf == '#' || *file_buf == '\n') continue; if ((eol = strchr(file_buf, '\n'))) *eol = '\0'; add_track(file_buf, block_size, block_type, nogap); } if (feof(fp)) fclose(fp); else err(EX_IOERR, "fgets(%s)", file_buf); } else add_track(argv[arg], block_size, block_type, nogap); } if (notracks) { if (dvdrw && notracks > 1) errx(EX_USAGE, "DVD's only have 1 track"); if (ioctl(fd, CDIOCSTART, 0) < 0) err(EX_IOERR, "ioctl(CDIOCSTART)"); if (!cdopen) { if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0) err(EX_IOERR, "ioctl(CDRIOCINITWRITER)"); cdopen = 1; } if (dao) do_DAO(fd, test_write, multi); else do_TAO(fd, test_write, preemp, dvdrw); } if (!test_write && fixate && !dao && !dvdrw) { if (!quiet) fprintf(stderr, "fixating CD, please wait..\n"); if (ioctl(fd, CDRIOCFIXATE, &multi) < 0) err(EX_IOERR, "ioctl(CDRIOCFIXATE)"); } if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) { err_set_exit(NULL); err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); } if (eject) if (ioctl(fd, CDIOCEJECT) < 0) err(EX_IOERR, "ioctl(CDIOCEJECT)"); signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); close(fd); exit(EX_OK); } void add_track(char *name, int block_size, int block_type, int nogap) { struct stat sb; int file; static int done_stdin = 0; if (!strcmp(name, "-")) { if (done_stdin) { warn("skipping multiple usages of stdin"); return; } file = STDIN_FILENO; done_stdin = 1; } else if ((file = open(name, O_RDONLY, 0)) < 0) err(EX_NOINPUT, "open(%s)", name); if (fstat(file, &sb) < 0) err(EX_IOERR, "fstat(%s)", name); tracks[notracks].file = file; strncpy(tracks[notracks].file_name, name, MAXPATHLEN); if (file == STDIN_FILENO) tracks[notracks].file_size = -1; else tracks[notracks].file_size = sb.st_size; tracks[notracks].block_size = block_size; tracks[notracks].block_type = block_type; if (nogap && notracks) tracks[notracks].pregap = 0; else { if (tracks[notracks - (notracks > 0)].block_type == block_type) tracks[notracks].pregap = 150; else tracks[notracks].pregap = 255; } if (verbose) { int pad = 0; if (tracks[notracks].file_size / tracks[notracks].block_size != roundup_blocks(&tracks[notracks])) pad = 1; fprintf(stderr, "adding type 0x%02x file %s size %jd KB %d blocks %s\n", tracks[notracks].block_type, name, (intmax_t)sb.st_size/1024, roundup_blocks(&tracks[notracks]), pad ? "(0 padded)" : ""); } notracks++; } void do_DAO(int fd, int test_write, int multi) { struct cdr_cuesheet sheet; struct cdr_cue_entry cue[100]; int format = CDR_SESS_CDROM; int addr, i, j = 0; int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 }; int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1, 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 }; if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); if (verbose) fprintf(stderr, "next writeable LBA %d\n", addr); cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0, (bt2df[tracks[0].block_type] & 0xf0) | (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr); for (i = 0; i < notracks; i++) { if (bt2ctl[tracks[i].block_type] < 0 || bt2df[tracks[i].block_type] < 0) errx(EX_IOERR, "track type not supported in DAO mode"); if (tracks[i].block_type >= CDR_DB_XA_MODE1) format = CDR_SESS_CDROM_XA; if (i == 0) { addr += tracks[i].pregap; tracks[i].addr = addr; cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 0x01, i+1, 0x1, bt2df[tracks[i].block_type], 0x00, addr); } else { if (tracks[i].pregap) { if (tracks[i].block_type > 0x7) { cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 0x01, i+1, 0x0, (bt2df[tracks[i].block_type] & 0xf0) | (tracks[i].block_type < 8 ? 0x01 :0x04), 0x00, addr); } else cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 0x01, i+1, 0x0, bt2df[tracks[i].block_type], 0x00, addr); } tracks[i].addr = tracks[i - 1].addr + roundup_blocks(&tracks[i - 1]); cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 0x01, i+1, 0x1, bt2df[tracks[i].block_type], 0x00, addr + tracks[i].pregap); if (tracks[i].block_type > 0x7) addr += tracks[i].pregap; } addr += roundup_blocks(&tracks[i]); } cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01, (bt2df[tracks[i - 1].block_type] & 0xf0) | (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr); sheet.len = j * 8; sheet.entries = cue; sheet.test_write = test_write; sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE; sheet.session_format = format; if (verbose) { u_int8_t *ptr = (u_int8_t *)sheet.entries; fprintf(stderr,"CUE sheet:"); for (i = 0; i < sheet.len; i++) if (i % 8) fprintf(stderr," %02x", ptr[i]); else fprintf(stderr,"\n%02x", ptr[i]); fprintf(stderr,"\n"); } if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0) err(EX_IOERR, "ioctl(CDRIOCSENDCUE)"); for (i = 0; i < notracks; i++) { if (write_file(fd, &tracks[i])) { cleanup_flush(); err(EX_IOERR, "write_file"); } } ioctl(fd, CDRIOCFLUSH); } void do_TAO(int fd, int test_write, int preemp, int dvdrw) { struct cdr_track track; int i; for (i = 0; i < notracks; i++) { track.test_write = test_write; track.datablock_type = tracks[i].block_type; track.preemp = preemp; if (ioctl(fd, CDRIOCINITTRACK, &track) < 0) err(EX_IOERR, "ioctl(CDRIOCINITTRACK)"); if (dvdrw) tracks[i].addr = 0; else if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0) err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); if (!quiet) fprintf(stderr, "next writeable LBA %d\n", tracks[i].addr); if (write_file(fd, &tracks[i])) { cleanup_flush(); err(EX_IOERR, "write_file"); } if (ioctl(fd, CDRIOCFLUSH) < 0) err(EX_IOERR, "ioctl(CDRIOCFLUSH)"); } } #define NTOH3B(x) ((x&0x0000ff)<<16) | (x&0x00ff00) | ((x&0xff0000)>>16) void do_format(int the_fd, int force, char *type) { struct cdr_format_capacities capacities; struct cdr_format_params format_params; int count, i, pct, last = 0; if (ioctl(the_fd, CDRIOCREADFORMATCAPS, &capacities) == -1) err(EX_IOERR, "ioctl(CDRIOCREADFORMATCAPS)"); if (verbose) { fprintf(stderr, "format list entries=%zd\n", capacities.length / sizeof(struct cdr_format_capacity)); fprintf(stderr, "current format: blocks=%u type=0x%x block_size=%u\n", ntohl(capacities.blocks), capacities.type, NTOH3B(capacities.block_size)); } count = capacities.length / sizeof(struct cdr_format_capacity); if (verbose) { for (i = 0; i < count; ++i) fprintf(stderr, "format %d: blocks=%u type=0x%x param=%u\n", i, ntohl(capacities.format[i].blocks), capacities.format[i].type, NTOH3B(capacities.format[i].param)); } for (i = 0; i < count; ++i) { if (!strcasecmp(type, "dvd+rw")) { if (capacities.format[i].type == 0x26) { break; } } if (!strcasecmp(type, "dvd-rw")) { if (capacities.format[i].type == 0x0) { break; } } } if (i == count) errx(EX_IOERR, "could not find a valid format capacity"); if (!quiet) fprintf(stderr,"formatting with blocks=%u type=0x%x param=%u\n", ntohl(capacities.format[i].blocks), capacities.format[i].type, NTOH3B(capacities.format[i].param)); if (!force && capacities.type == 2) errx(EX_IOERR, "media already formatted (use -F to override)"); memset(&format_params, 0, sizeof(struct cdr_format_params)); format_params.fov = 1; format_params.immed = 1; format_params.length = ntohs(sizeof(struct cdr_format_capacity)); memcpy(&format_params.format, &capacities.format[i], sizeof(struct cdr_format_capacity)); if(ioctl(the_fd, CDRIOCFORMAT, &format_params) == -1) err(EX_IOERR, "ioctl(CDRIOCFORMAT)"); while (1) { sleep(1); if (ioctl(the_fd, CDRIOCGETPROGRESS, &pct) == -1) err(EX_IOERR, "ioctl(CDRIOGETPROGRESS)"); if (pct > 0 && !quiet) fprintf(stderr, "formatting DVD - %d %% done \r", pct); if (pct == 100 || (pct == 0 && last > 90)) break; last = pct; } if (!quiet) fprintf(stderr, "\n"); } int write_file(int fd, struct track_info *track_info) { off_t size, count, filesize; char buf[2352*BLOCKS]; static off_t tot_size = 0; filesize = track_info->file_size / 1024; if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0) err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); if (track_info->addr >= 0) lseek(fd, track_info->addr * track_info->block_size, SEEK_SET); if (verbose) fprintf(stderr, "addr = %d size = %jd blocks = %d\n", track_info->addr, (intmax_t)track_info->file_size, roundup_blocks(track_info)); if (!quiet) { if (track_info->file == STDIN_FILENO) fprintf(stderr, "writing from stdin\n"); else fprintf(stderr, "writing from file %s size %jd KB\n", track_info->file_name, (intmax_t)filesize); } size = 0; while ((count = read(track_info->file, buf, track_info->file_size == -1 ? track_info->block_size * BLOCKS : MIN((track_info->file_size - size), track_info->block_size * BLOCKS))) > 0) { int res; if (count % track_info->block_size) { /* pad file to % block_size */ bzero(&buf[count], (track_info->block_size * BLOCKS) - count); count = ((count / track_info->block_size) + 1) * track_info->block_size; } if ((res = write(fd, buf, count)) != count) { if (res == -1) { fprintf(stderr, "\n"); close(track_info->file); return errno; } else fprintf(stderr, "\nonly wrote %d of %jd" " bytes\n", res, (intmax_t)count); break; } size += count; tot_size += count; if (!quiet) { int pct; fprintf(stderr, "written this track %jd KB", (intmax_t)size/1024); if (track_info->file != STDIN_FILENO && filesize) { pct = (size / 1024) * 100 / filesize; fprintf(stderr, " (%d%%)", pct); } fprintf(stderr, " total %jd KB\r", (intmax_t)tot_size / 1024); } if (track_info->file_size != -1 && size >= track_info->file_size) break; } if (!quiet) fprintf(stderr, "\n"); close(track_info->file); return 0; } int roundup_blocks(struct track_info *track) { return ((track->file_size + track->block_size - 1) / track->block_size); } void cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx, int dataform, int scms, int lba) { cue->adr = adr; cue->ctl = ctl; cue->track = track; cue->index = idx; cue->dataform = dataform; cue->scms = scms; lba += 150; cue->min = lba / (60*75); cue->sec = (lba % (60*75)) / 75; cue->frame = (lba % (60*75)) % 75; } void cleanup(int dummy __unused) { if (ioctl(global_fd_for_cleanup, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); } void cleanup_flush(void) { if (ioctl(global_fd_for_cleanup, CDRIOCFLUSH) < 0) err(EX_IOERR, "ioctl(CDRIOCFLUSH)"); } void -cleanup_signal(int sig __unused) +cleanup_signal(int sig) { - cleanup_flush(); - fprintf(stderr, "\n"); - errx(EXIT_FAILURE, "Aborted"); + signal(sig, SIG_IGN); + ioctl(global_fd_for_cleanup, CDRIOCFLUSH); + write(STDERR_FILENO, "\nAborted\n", 10); + _exit(EXIT_FAILURE); } void usage(void) { fprintf(stderr, "usage: %s [-deFlmnpqtv] [-f device] [-s speed] [command]" " [command file ...]\n", getprogname()); exit(EX_USAGE); } Index: projects/cambria/usr.sbin/makefs/ffs/ffs_bswap.c =================================================================== --- projects/cambria/usr.sbin/makefs/ffs/ffs_bswap.c (revision 186459) +++ projects/cambria/usr.sbin/makefs/ffs/ffs_bswap.c (revision 186460) Property changes on: projects/cambria/usr.sbin/makefs/ffs/ffs_bswap.c ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/usr.sbin/makefs/ffs/ffs_bswap.c:r186349-186457 Index: projects/cambria/usr.sbin/makefs/ffs/ffs_subr.c =================================================================== --- projects/cambria/usr.sbin/makefs/ffs/ffs_subr.c (revision 186459) +++ projects/cambria/usr.sbin/makefs/ffs/ffs_subr.c (revision 186460) Property changes on: projects/cambria/usr.sbin/makefs/ffs/ffs_subr.c ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/usr.sbin/makefs/ffs/ffs_subr.c:r186349-186457 Index: projects/cambria/usr.sbin/makefs/ffs/ufs_bswap.h =================================================================== --- projects/cambria/usr.sbin/makefs/ffs/ufs_bswap.h (revision 186459) +++ projects/cambria/usr.sbin/makefs/ffs/ufs_bswap.h (revision 186460) Property changes on: projects/cambria/usr.sbin/makefs/ffs/ufs_bswap.h ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/usr.sbin/makefs/ffs/ufs_bswap.h:r186349-186457 Index: projects/cambria/usr.sbin/makefs/getid.c =================================================================== --- projects/cambria/usr.sbin/makefs/getid.c (revision 186459) +++ projects/cambria/usr.sbin/makefs/getid.c (revision 186460) Property changes on: projects/cambria/usr.sbin/makefs/getid.c ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/usr.sbin/makefs/getid.c:r186349-186457 Index: projects/cambria/usr.sbin/nscd/nscd.conf.5 =================================================================== --- projects/cambria/usr.sbin/nscd/nscd.conf.5 (revision 186459) +++ projects/cambria/usr.sbin/nscd/nscd.conf.5 (revision 186460) @@ -1,148 +1,148 @@ .\" Copyright (c) 2005 Michael Bushkov .\" 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. .\" .\" $FreeBSD$ .\" .Dd April 30, 2006 .Dt NSCD.CONF 5 .Os .Sh NAME .Nm nscd.conf .Nd "nscd configuration file" .Sh DESCRIPTION The .Nm file is used by the .Xr nscd 8 daemon and is read on its startup. Its syntax is mostly similar to the .Pa nscd.conf syntax in .Tn Linux and .Tn Solaris . It has some differences, though \[em] see them below. .Pp Each line specifies either an attribute and a .Ar value , or an attribute, a .Ar cachename and a .Ar value . Usual cachenames are .Dq Li passwd , -.Dq Li groups , +.Dq Li group , .Dq Li hosts , .Dq Li services , .Dq Li protocols and .Dq Li rpc . You can also use any other .Ar cachename (for example, if some third-party application uses nsswitch). .Bl -tag -width indent .It Va threads Op Ar value Number of threads, which would listen for connections and process requests. The minimum is 1. The default value is 8. .It Va enable-cache Oo Ar cachename Oc Op Cm yes | no Enables or disables the cache for specified .Ar cachename . .It Va positive-time-to-live Oo Ar cachename Oc Op Ar value Sets the TTL (time-to-live) for the specified cache in seconds. Larger values can increase system's performance, but they also can affect the cache coherence. The default value is 3600. .It Va positive-policy Oo Ar cachename Oc Op Cm fifo | lru | lfu The policy that is applied to erase some of the cache elements, when the size limit of the given .Ar cachename is exceeded. Possible policies are: .Cm fifo (first-in-first-out), .Cm lru (least-recently-used), and .Cm lfu (least-frequently-used). The default policy is .Cm lru . .It Va negative-time-to-live Oo Ar cachename Oc Op Ar value The TTL of the negative cached elements in seconds. The larger values can significantly increase system performance in some environments (when dealing with files with UIDs, which are not in system databases, for example). This number should be kept low to avoid the cache coherence problems. The default value is 60. .It Va negative-policy Oo Ar cachename Oc Op Cm fifo | lru | lfu The same as the positive-policy, but this one is applied to the negative elements of the given .Ar cachename . The default policy is fifo. .It Va suggested-size Oo Ar cachename Oc Op Ar value This is the internal hash table size. The value should be a prime number for optimum performance. You should only change this value when the number of cached elements is significantly (in 5-10 times) greater then the default hash table size (255). .It Va keep-hot-count Oo Ar cachename Oc Op Ar value The size limit of the cache with the given .Ar cachename . When it is exceeded, the policy will be applied. The default value is 2048. .It Va perform-actual-lookups Oo Ar cachename Oc Op Cm yes | no If enabled, the .Xr nscd 8 does not simply receive and cache the NSS-requests results, but performs all the lookups by itself and only returns the responses. If this feature is enabled, then for the given .Ar cachename .Xr nscd 8 will act similarly to the NSCD. .Pp .Sy NOTE : this feature is currently experimental \[em] it supports only .Dq Li passwd , -.Dq Li groups +.Dq Li group and .Dq Li services cachenames. .El .Sh NOTES You can use the .Ql # symbol at the beginning of the line for comments. .Sh FILES .Bl -tag -width ".Pa /etc/nscd.conf" -compact .It Pa /etc/nscd.conf .El .Sh SEE ALSO .Xr nscd 8 .Sh AUTHORS .An Michael Bushkov .Aq bushman@freebsd.org .Sh BUGS Please send bug reports and suggestions to .Aq bushman@freebsd.org . Index: projects/cambria/usr.sbin/pmcstat/pmcstat.c =================================================================== --- projects/cambria/usr.sbin/pmcstat/pmcstat.c (revision 186459) +++ projects/cambria/usr.sbin/pmcstat/pmcstat.c (revision 186460) @@ -1,1323 +1,1326 @@ /*- * Copyright (c) 2003-2008, Joseph Koshy * Copyright (c) 2007 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by A. Joseph Koshy under * sponsorship from the FreeBSD Foundation and Google, Inc. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pmcstat.h" /* * A given invocation of pmcstat(8) can manage multiple PMCs of both * the system-wide and per-process variety. Each of these could be in * 'counting mode' or in 'sampling mode'. * * For 'counting mode' PMCs, pmcstat(8) will periodically issue a * pmc_read() at the configured time interval and print out the value * of the requested PMCs. * * For 'sampling mode' PMCs it can log to a file for offline analysis, * or can analyse sampling data "on the fly", either by converting * samples to printed textual form or by creating gprof(1) compatible * profiles, one per program executed. When creating gprof(1) * profiles it can optionally merge entries from multiple processes * for a given executable into a single profile file. * * pmcstat(8) can also execute a command line and attach PMCs to the * resulting child process. The protocol used is as follows: * * - parent creates a socketpair for two way communication and * fork()s. * - subsequently: * * /Parent/ /Child/ * * - Wait for childs token. * - Sends token. * - Awaits signal to start. * - Attaches PMCs to the child's pid * and starts them. Sets up * monitoring for the child. * - Signals child to start. * - Recieves signal, attempts exec(). * * After this point normal processing can happen. */ /* Globals */ int pmcstat_interrupt = 0; int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; int pmcstat_sockpair[NSOCKPAIRFD]; int pmcstat_kq; kvm_t *pmcstat_kvm; struct kinfo_proc *pmcstat_plist; void pmcstat_attach_pmcs(struct pmcstat_args *a) { struct pmcstat_ev *ev; struct pmcstat_target *pt; int count; /* Attach all process PMCs to target processes. */ count = 0; STAILQ_FOREACH(ev, &a->pa_events, ev_next) { if (PMC_IS_SYSTEM_MODE(ev->ev_mode)) continue; SLIST_FOREACH(pt, &a->pa_targets, pt_next) if (pmc_attach(ev->ev_pmcid, pt->pt_pid) == 0) count++; else if (errno != ESRCH) err(EX_OSERR, "ERROR: cannot attach pmc " "\"%s\" to process %d", ev->ev_name, (int) pt->pt_pid); } if (count == 0) errx(EX_DATAERR, "ERROR: No processes were attached to."); } void pmcstat_cleanup(struct pmcstat_args *a) { struct pmcstat_ev *ev, *tmp; /* release allocated PMCs. */ STAILQ_FOREACH_SAFE(ev, &a->pa_events, ev_next, tmp) if (ev->ev_pmcid != PMC_ID_INVALID) { if (pmc_stop(ev->ev_pmcid) < 0) err(EX_OSERR, "ERROR: cannot stop pmc 0x%x " "\"%s\"", ev->ev_pmcid, ev->ev_name); if (pmc_release(ev->ev_pmcid) < 0) err(EX_OSERR, "ERROR: cannot release pmc " "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); free(ev->ev_name); free(ev->ev_spec); STAILQ_REMOVE(&a->pa_events, ev, pmcstat_ev, ev_next); free(ev); } /* de-configure the log file if present. */ if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) (void) pmc_configure_logfile(-1); if (a->pa_logparser) { pmclog_close(a->pa_logparser); a->pa_logparser = NULL; } if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) pmcstat_shutdown_logging(a); } void pmcstat_clone_event_descriptor(struct pmcstat_args *a, struct pmcstat_ev *ev, uint32_t cpumask) { int cpu; struct pmcstat_ev *ev_clone; while ((cpu = ffs(cpumask)) > 0) { cpu--; if ((ev_clone = malloc(sizeof(*ev_clone))) == NULL) errx(EX_SOFTWARE, "ERROR: Out of memory"); (void) memset(ev_clone, 0, sizeof(*ev_clone)); ev_clone->ev_count = ev->ev_count; ev_clone->ev_cpu = cpu; ev_clone->ev_cumulative = ev->ev_cumulative; ev_clone->ev_flags = ev->ev_flags; ev_clone->ev_mode = ev->ev_mode; ev_clone->ev_name = strdup(ev->ev_name); ev_clone->ev_pmcid = ev->ev_pmcid; ev_clone->ev_saved = ev->ev_saved; ev_clone->ev_spec = strdup(ev->ev_spec); STAILQ_INSERT_TAIL(&a->pa_events, ev_clone, ev_next); cpumask &= ~(1 << cpu); } } void pmcstat_create_process(struct pmcstat_args *a) { char token; pid_t pid; struct kevent kev; struct pmcstat_target *pt; if (socketpair(AF_UNIX, SOCK_STREAM, 0, pmcstat_sockpair) < 0) err(EX_OSERR, "ERROR: cannot create socket pair"); switch (pid = fork()) { case -1: err(EX_OSERR, "ERROR: cannot fork"); /*NOTREACHED*/ case 0: /* child */ (void) close(pmcstat_sockpair[PARENTSOCKET]); /* Write a token to tell our parent we've started executing. */ if (write(pmcstat_sockpair[CHILDSOCKET], "+", 1) != 1) err(EX_OSERR, "ERROR (child): cannot write token"); /* Wait for our parent to signal us to start. */ if (read(pmcstat_sockpair[CHILDSOCKET], &token, 1) < 0) err(EX_OSERR, "ERROR (child): cannot read token"); (void) close(pmcstat_sockpair[CHILDSOCKET]); /* exec() the program requested */ execvp(*a->pa_argv, a->pa_argv); /* and if that fails, notify the parent */ kill(getppid(), SIGCHLD); err(EX_OSERR, "ERROR: execvp \"%s\" failed", *a->pa_argv); /*NOTREACHED*/ default: /* parent */ (void) close(pmcstat_sockpair[CHILDSOCKET]); break; } /* Ask to be notified via a kevent when the target process exits. */ EV_SET(&kev, pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: cannot monitor child process %d", pid); if ((pt = malloc(sizeof(*pt))) == NULL) errx(EX_SOFTWARE, "ERROR: Out of memory."); pt->pt_pid = pid; SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next); /* Wait for the child to signal that its ready to go. */ if (read(pmcstat_sockpair[PARENTSOCKET], &token, 1) < 0) err(EX_OSERR, "ERROR (parent): cannot read token"); return; } void pmcstat_find_targets(struct pmcstat_args *a, const char *spec) { int n, nproc, pid, rv; struct pmcstat_target *pt; char errbuf[_POSIX2_LINE_MAX], *end; static struct kinfo_proc *kp; regex_t reg; regmatch_t regmatch; /* First check if we've been given a process id. */ pid = strtol(spec, &end, 0); if (end != spec && pid >= 0) { if ((pt = malloc(sizeof(*pt))) == NULL) goto outofmemory; pt->pt_pid = pid; SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next); return; } /* Otherwise treat arg as a regular expression naming processes. */ if (pmcstat_kvm == NULL) { if ((pmcstat_kvm = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf)) == NULL) err(EX_OSERR, "ERROR: Cannot open kernel \"%s\"", errbuf); if ((pmcstat_plist = kvm_getprocs(pmcstat_kvm, KERN_PROC_PROC, 0, &nproc)) == NULL) err(EX_OSERR, "ERROR: Cannot get process list: %s", kvm_geterr(pmcstat_kvm)); } if ((rv = regcomp(®, spec, REG_EXTENDED|REG_NOSUB)) != 0) { regerror(rv, ®, errbuf, sizeof(errbuf)); err(EX_DATAERR, "ERROR: Failed to compile regex \"%s\": %s", spec, errbuf); } for (n = 0, kp = pmcstat_plist; n < nproc; n++, kp++) { if ((rv = regexec(®, kp->ki_comm, 1, ®match, 0)) == 0) { if ((pt = malloc(sizeof(*pt))) == NULL) goto outofmemory; pt->pt_pid = kp->ki_pid; SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next); } else if (rv != REG_NOMATCH) { regerror(rv, ®, errbuf, sizeof(errbuf)); errx(EX_SOFTWARE, "ERROR: Regex evalation failed: %s", errbuf); } } regfree(®); return; outofmemory: errx(EX_SOFTWARE, "Out of memory."); /*NOTREACHED*/ } uint32_t pmcstat_get_cpumask(const char *cpuspec) { uint32_t cpumask; int cpu; const char *s; char *end; s = cpuspec; cpumask = 0ULL; do { cpu = strtol(s, &end, 0); if (cpu < 0 || end == s) errx(EX_USAGE, "ERROR: Illegal CPU specification " "\"%s\".", cpuspec); cpumask |= (1 << cpu); s = end + strspn(end, ", \t"); } while (*s); return (cpumask); } void pmcstat_kill_process(struct pmcstat_args *a) { struct pmcstat_target *pt; assert(a->pa_flags & FLAG_HAS_COMMANDLINE); /* * If a command line was specified, it would be the very first * in the list, before any other processes specified by -t. */ pt = SLIST_FIRST(&a->pa_targets); assert(pt != NULL); if (kill(pt->pt_pid, SIGINT) != 0) err(EX_OSERR, "ERROR: cannot signal child process"); } void pmcstat_start_pmcs(struct pmcstat_args *a) { struct pmcstat_ev *ev; STAILQ_FOREACH(ev, &args.pa_events, ev_next) { assert(ev->ev_pmcid != PMC_ID_INVALID); if (pmc_start(ev->ev_pmcid) < 0) { warn("ERROR: Cannot start pmc 0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); pmcstat_cleanup(a); exit(EX_OSERR); } } } void pmcstat_print_headers(struct pmcstat_args *a) { struct pmcstat_ev *ev; int c, w; (void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX); STAILQ_FOREACH(ev, &a->pa_events, ev_next) { if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) continue; c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; if (ev->ev_fieldskip != 0) (void) fprintf(a->pa_printfile, "%*s", ev->ev_fieldskip, ""); w = ev->ev_fieldwidth - ev->ev_fieldskip - 2; if (c == 's') (void) fprintf(a->pa_printfile, "s/%02d/%-*s ", ev->ev_cpu, w-3, ev->ev_name); else (void) fprintf(a->pa_printfile, "p/%*s ", w, ev->ev_name); } (void) fflush(a->pa_printfile); } void pmcstat_print_counters(struct pmcstat_args *a) { int extra_width; struct pmcstat_ev *ev; pmc_value_t value; extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; STAILQ_FOREACH(ev, &a->pa_events, ev_next) { /* skip sampling mode counters */ if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) continue; if (pmc_read(ev->ev_pmcid, &value) < 0) err(EX_OSERR, "ERROR: Cannot read pmc " "\"%s\"", ev->ev_name); (void) fprintf(a->pa_printfile, "%*ju ", ev->ev_fieldwidth + extra_width, (uintmax_t) ev->ev_cumulative ? value : (value - ev->ev_saved)); if (ev->ev_cumulative == 0) ev->ev_saved = value; extra_width = 0; } (void) fflush(a->pa_printfile); } /* * Print output */ void pmcstat_print_pmcs(struct pmcstat_args *a) { static int linecount = 0; /* check if we need to print a header line */ if (++linecount > pmcstat_displayheight) { (void) fprintf(a->pa_printfile, "\n"); linecount = 1; } if (linecount == 1) pmcstat_print_headers(a); (void) fprintf(a->pa_printfile, "\n"); pmcstat_print_counters(a); return; } /* * Do process profiling * * If a pid was specified, attach each allocated PMC to the target * process. Otherwise, fork a child and attach the PMCs to the child, * and have the child exec() the target program. */ void pmcstat_start_process(void) { /* Signal the child to proceed. */ if (write(pmcstat_sockpair[PARENTSOCKET], "!", 1) != 1) err(EX_OSERR, "ERROR (parent): write of token failed"); (void) close(pmcstat_sockpair[PARENTSOCKET]); } void pmcstat_show_usage(void) { errx(EX_USAGE, "[options] [commandline]\n" "\t Measure process and/or system performance using hardware\n" "\t performance monitoring counters.\n" "\t Options include:\n" "\t -C\t\t (toggle) show cumulative counts\n" "\t -D path\t create profiles in directory \"path\"\n" "\t -E\t\t (toggle) show counts at process exit\n" "\t -G file\t write a system-wide callgraph to \"file\"\n" "\t -M file\t print executable/gmon file map to \"file\"\n" "\t -N\t\t (toggle) capture callchains\n" "\t -O file\t send log output to \"file\"\n" "\t -P spec\t allocate a process-private sampling PMC\n" "\t -R file\t read events from \"file\"\n" "\t -S spec\t allocate a system-wide sampling PMC\n" "\t -W\t\t (toggle) show counts per context switch\n" "\t -c cpu-list\t set cpus for subsequent system-wide PMCs\n" "\t -d\t\t (toggle) track descendants\n" "\t -g\t\t produce gprof(1) compatible profiles\n" "\t -k dir\t\t set the path to the kernel\n" "\t -n rate\t set sampling rate\n" "\t -o file\t send print output to \"file\"\n" "\t -p spec\t allocate a process-private counting PMC\n" "\t -q\t\t suppress verbosity\n" "\t -r fsroot\t specify FS root directory\n" "\t -s spec\t allocate a system-wide counting PMC\n" "\t -t process-spec attach to running processes matching " "\"process-spec\"\n" "\t -v\t\t increase verbosity\n" "\t -w secs\t set printing time interval\n" "\t -z depth\t limit callchain display depth" ); } /* * Main */ int main(int argc, char **argv) { double interval; int option, npmc, ncpu, haltedcpus; int c, check_driver_stats, current_cpu, current_sampling_count; int do_callchain, do_descendants, do_logproccsw, do_logprocexit; int do_print; size_t dummy; int graphdepth; int pipefd[2]; int use_cumulative_counts; uint32_t cpumask; char *end, *tmp; const char *errmsg, *graphfilename; enum pmcstat_state runstate; struct pmc_driverstats ds_start, ds_end; struct pmcstat_ev *ev; struct sigaction sa; struct kevent kev; struct winsize ws; struct stat sb; char buffer[PATH_MAX]; check_driver_stats = 0; current_cpu = 0; current_sampling_count = DEFAULT_SAMPLE_COUNT; do_callchain = 1; do_descendants = 0; do_logproccsw = 0; do_logprocexit = 0; use_cumulative_counts = 0; graphfilename = "-"; args.pa_required = 0; args.pa_flags = 0; args.pa_verbosity = 1; args.pa_logfd = -1; args.pa_fsroot = ""; args.pa_kernel = strdup("/boot/kernel"); args.pa_samplesdir = "."; args.pa_printfile = stderr; args.pa_graphdepth = DEFAULT_CALLGRAPH_DEPTH; args.pa_graphfile = NULL; args.pa_interval = DEFAULT_WAIT_INTERVAL; args.pa_mapfilename = NULL; args.pa_inputpath = NULL; args.pa_outputpath = NULL; STAILQ_INIT(&args.pa_events); SLIST_INIT(&args.pa_targets); bzero(&ds_start, sizeof(ds_start)); bzero(&ds_end, sizeof(ds_end)); ev = NULL; /* * The initial CPU mask specifies all non-halted CPUS in the * system. */ dummy = sizeof(int); if (sysctlbyname("hw.ncpu", &ncpu, &dummy, NULL, 0) < 0) err(EX_OSERR, "ERROR: Cannot determine the number of CPUs"); cpumask = (1 << ncpu) - 1; haltedcpus = 0; if (ncpu > 1) { if (sysctlbyname("machdep.hlt_cpus", &haltedcpus, &dummy, NULL, 0) < 0) err(EX_OSERR, "ERROR: Cannot determine which CPUs are " "halted"); cpumask &= ~haltedcpus; } while ((option = getopt(argc, argv, "CD:EG:M:NO:P:R:S:Wc:dgk:m:n:o:p:qr:s:t:vw:z:")) != -1) switch (option) { case 'C': /* cumulative values */ use_cumulative_counts = !use_cumulative_counts; args.pa_required |= FLAG_HAS_COUNTING_PMCS; break; case 'c': /* CPU */ if (optarg[0] == '*' && optarg[1] == '\0') cpumask = ((1 << ncpu) - 1) & ~haltedcpus; else cpumask = pmcstat_get_cpumask(optarg); args.pa_required |= FLAG_HAS_SYSTEM_PMCS; break; case 'D': if (stat(optarg, &sb) < 0) err(EX_OSERR, "ERROR: Cannot stat \"%s\"", optarg); if (!S_ISDIR(sb.st_mode)) errx(EX_USAGE, "ERROR: \"%s\" is not a " "directory.", optarg); args.pa_samplesdir = optarg; args.pa_flags |= FLAG_HAS_SAMPLESDIR; args.pa_required |= FLAG_DO_GPROF; break; case 'd': /* toggle descendents */ do_descendants = !do_descendants; args.pa_required |= FLAG_HAS_PROCESS_PMCS; break; case 'G': /* produce a system-wide callgraph */ args.pa_flags |= FLAG_DO_CALLGRAPHS; graphfilename = optarg; break; case 'g': /* produce gprof compatible profiles */ args.pa_flags |= FLAG_DO_GPROF; break; case 'k': /* pathname to the kernel */ free(args.pa_kernel); args.pa_kernel = strdup(optarg); args.pa_required |= FLAG_DO_ANALYSIS; args.pa_flags |= FLAG_HAS_KERNELPATH; break; case 'm': args.pa_flags |= FLAG_WANTS_MAPPINGS; graphfilename = optarg; break; case 'E': /* log process exit */ do_logprocexit = !do_logprocexit; args.pa_required |= (FLAG_HAS_PROCESS_PMCS | FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE); break; case 'M': /* mapfile */ args.pa_mapfilename = optarg; break; case 'N': do_callchain = !do_callchain; args.pa_required |= FLAG_HAS_SAMPLING_PMCS; break; case 'p': /* process virtual counting PMC */ case 's': /* system-wide counting PMC */ case 'P': /* process virtual sampling PMC */ case 'S': /* system-wide sampling PMC */ if ((ev = malloc(sizeof(*ev))) == NULL) errx(EX_SOFTWARE, "ERROR: Out of memory."); switch (option) { case 'p': ev->ev_mode = PMC_MODE_TC; break; case 's': ev->ev_mode = PMC_MODE_SC; break; case 'P': ev->ev_mode = PMC_MODE_TS; break; case 'S': ev->ev_mode = PMC_MODE_SS; break; } if (option == 'P' || option == 'p') { args.pa_flags |= FLAG_HAS_PROCESS_PMCS; args.pa_required |= (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET); } if (option == 'P' || option == 'S') { args.pa_flags |= FLAG_HAS_SAMPLING_PMCS; args.pa_required |= (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE); } if (option == 'p' || option == 's') args.pa_flags |= FLAG_HAS_COUNTING_PMCS; if (option == 's' || option == 'S') args.pa_flags |= FLAG_HAS_SYSTEM_PMCS; ev->ev_spec = strdup(optarg); if (option == 'S' || option == 'P') ev->ev_count = current_sampling_count; else ev->ev_count = -1; if (option == 'S' || option == 's') ev->ev_cpu = ffs(cpumask) - 1; else ev->ev_cpu = PMC_CPU_ANY; ev->ev_flags = 0; if (do_callchain) ev->ev_flags |= PMC_F_CALLCHAIN; if (do_descendants) ev->ev_flags |= PMC_F_DESCENDANTS; if (do_logprocexit) ev->ev_flags |= PMC_F_LOG_PROCEXIT; if (do_logproccsw) ev->ev_flags |= PMC_F_LOG_PROCCSW; ev->ev_cumulative = use_cumulative_counts; ev->ev_saved = 0LL; ev->ev_pmcid = PMC_ID_INVALID; /* extract event name */ c = strcspn(optarg, ", \t"); ev->ev_name = malloc(c + 1); (void) strncpy(ev->ev_name, optarg, c); *(ev->ev_name + c) = '\0'; STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next); if (option == 's' || option == 'S') pmcstat_clone_event_descriptor(&args, ev, cpumask & ~(1 << ev->ev_cpu)); break; case 'n': /* sampling count */ current_sampling_count = strtol(optarg, &end, 0); if (*end != '\0' || current_sampling_count <= 0) errx(EX_USAGE, "ERROR: Illegal count value \"%s\".", optarg); args.pa_required |= FLAG_HAS_SAMPLING_PMCS; break; case 'o': /* outputfile */ if (args.pa_printfile != NULL) (void) fclose(args.pa_printfile); if ((args.pa_printfile = fopen(optarg, "w")) == NULL) errx(EX_OSERR, "ERROR: cannot open \"%s\" for " "writing.", optarg); args.pa_flags |= FLAG_DO_PRINT; break; case 'O': /* sampling output */ if (args.pa_outputpath) errx(EX_USAGE, "ERROR: option -O may only be " "specified once."); args.pa_outputpath = optarg; args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE; break; case 'q': /* quiet mode */ args.pa_verbosity = 0; break; case 'r': /* root FS path */ args.pa_fsroot = optarg; break; case 'R': /* read an existing log file */ if (args.pa_inputpath != NULL) errx(EX_USAGE, "ERROR: option -R may only be " "specified once."); args.pa_inputpath = optarg; if (args.pa_printfile == stderr) args.pa_printfile = stdout; args.pa_flags |= FLAG_READ_LOGFILE; break; case 't': /* target pid or process name */ pmcstat_find_targets(&args, optarg); args.pa_flags |= FLAG_HAS_TARGET; args.pa_required |= FLAG_HAS_PROCESS_PMCS; break; case 'v': /* verbose */ args.pa_verbosity++; break; case 'w': /* wait interval */ interval = strtod(optarg, &end); if (*end != '\0' || interval <= 0) errx(EX_USAGE, "ERROR: Illegal wait interval " "value \"%s\".", optarg); args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; args.pa_required |= FLAG_HAS_COUNTING_PMCS; args.pa_interval = interval; break; case 'W': /* toggle LOG_CSW */ do_logproccsw = !do_logproccsw; args.pa_required |= (FLAG_HAS_PROCESS_PMCS | FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE); break; case 'z': graphdepth = strtod(optarg, &end); if (*end != '\0' || graphdepth <= 0) errx(EX_USAGE, "ERROR: Illegal callchain " "depth \"%s\".", optarg); args.pa_graphdepth = graphdepth; args.pa_required |= FLAG_DO_CALLGRAPHS; break; case '?': default: pmcstat_show_usage(); break; } args.pa_argc = (argc -= optind); args.pa_argv = (argv += optind); args.pa_cpumask = cpumask; /* For selecting CPUs using -R. */ if (argc) /* command line present */ args.pa_flags |= FLAG_HAS_COMMANDLINE; if (args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS | FLAG_WANTS_MAPPINGS)) args.pa_flags |= FLAG_DO_ANALYSIS; /* * Check invocation syntax. */ /* disallow -O and -R together */ if (args.pa_outputpath && args.pa_inputpath) errx(EX_USAGE, "ERROR: options -O and -R are mutually " "exclusive."); /* -m option is allowed with -R only. */ if (args.pa_flags & FLAG_WANTS_MAPPINGS && args.pa_inputpath == NULL) errx(EX_USAGE, "ERROR: option -m requires an input file"); /* -m option is not allowed combined with -g or -G. */ if (args.pa_flags & FLAG_WANTS_MAPPINGS && args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS)) errx(EX_USAGE, "ERROR: option -m and -g | -G are mutually " "exclusive"); if (args.pa_flags & FLAG_READ_LOGFILE) { errmsg = NULL; if (args.pa_flags & FLAG_HAS_COMMANDLINE) errmsg = "a command line specification"; else if (args.pa_flags & FLAG_HAS_TARGET) errmsg = "option -t"; else if (!STAILQ_EMPTY(&args.pa_events)) errmsg = "a PMC event specification"; if (errmsg) errx(EX_USAGE, "ERROR: option -R may not be used with " "%s.", errmsg); } else if (STAILQ_EMPTY(&args.pa_events)) /* All other uses require a PMC spec. */ pmcstat_show_usage(); /* check for -t pid without a process PMC spec */ if ((args.pa_required & FLAG_HAS_TARGET) && (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) errx(EX_USAGE, "ERROR: option -t requires a process mode PMC " "to be specified."); /* check for process-mode options without a command or -t pid */ if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) == 0) errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require " "a command line or target process."); /* check for -p | -P without a target process of some sort */ if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) && (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) == 0) errx(EX_USAGE, "ERROR: options -P and -p require a " "target process or a command line."); /* check for process-mode options without a process-mode PMC */ if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) errx(EX_USAGE, "ERROR: options -d, -E, and -W require a " "process mode PMC to be specified."); /* check for -c cpu with no system mode PMCs or logfile. */ if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) && (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0 && (args.pa_flags & FLAG_READ_LOGFILE) == 0) errx(EX_USAGE, "ERROR: option -c requires at least one " "system mode PMC to be specified."); /* check for counting mode options without a counting PMC */ if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) && (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0) errx(EX_USAGE, "ERROR: options -C, -W, -o and -w require at " "least one counting mode PMC to be specified."); /* check for sampling mode options without a sampling PMC spec */ if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) && (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0) errx(EX_USAGE, "ERROR: options -N, -n and -O require at " "least one sampling mode PMC to be specified."); /* check if -g/-G are being used correctly */ if ((args.pa_flags & FLAG_DO_ANALYSIS) && !(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE))) errx(EX_USAGE, "ERROR: options -g/-G require sampling PMCs " "or -R to be specified."); /* check if -O was spuriously specified */ if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) && (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) errx(EX_USAGE, "ERROR: option -O is used only with options " "-E, -P, -S and -W."); /* -k kernel path require -g/-G or -R */ if ((args.pa_flags & FLAG_HAS_KERNELPATH) && (args.pa_flags & FLAG_DO_ANALYSIS) == 0 && (args.pa_flags & FLAG_READ_LOGFILE) == 0) errx(EX_USAGE, "ERROR: option -k is only used with -g/-R."); /* -D only applies to gprof output mode (-g) */ if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) && (args.pa_flags & FLAG_DO_GPROF) == 0) errx(EX_USAGE, "ERROR: option -D is only used with -g."); /* -M mapfile requires -g or -R */ if (args.pa_mapfilename != NULL && (args.pa_flags & FLAG_DO_GPROF) == 0 && (args.pa_flags & FLAG_READ_LOGFILE) == 0) errx(EX_USAGE, "ERROR: option -M is only used with -g/-R."); /* * Disallow textual output of sampling PMCs if counting PMCs * have also been asked for, mostly because the combined output * is difficult to make sense of. */ if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) && ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) == 0)) errx(EX_USAGE, "ERROR: option -O is required if counting and " "sampling PMCs are specified together."); /* * Check if "-k kerneldir" was specified, and if whether * 'kerneldir' actually refers to a a file. If so, use * `dirname path` to determine the kernel directory. */ if (args.pa_flags & FLAG_HAS_KERNELPATH) { (void) snprintf(buffer, sizeof(buffer), "%s%s", args.pa_fsroot, args.pa_kernel); if (stat(buffer, &sb) < 0) err(EX_OSERR, "ERROR: Cannot locate kernel \"%s\"", buffer); if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) errx(EX_USAGE, "ERROR: \"%s\": Unsupported file type.", buffer); if (!S_ISDIR(sb.st_mode)) { tmp = args.pa_kernel; args.pa_kernel = strdup(dirname(args.pa_kernel)); free(tmp); (void) snprintf(buffer, sizeof(buffer), "%s%s", args.pa_fsroot, args.pa_kernel); if (stat(buffer, &sb) < 0) err(EX_OSERR, "ERROR: Cannot stat \"%s\"", buffer); if (!S_ISDIR(sb.st_mode)) errx(EX_USAGE, "ERROR: \"%s\" is not a " "directory.", buffer); } } /* * If we have a callgraph be created, select the outputfile. */ if (args.pa_flags & FLAG_DO_CALLGRAPHS) { if (strcmp(graphfilename, "-") == 0) args.pa_graphfile = args.pa_printfile; else { args.pa_graphfile = fopen(graphfilename, "w"); if (args.pa_graphfile == NULL) err(EX_OSERR, "ERROR: cannot open \"%s\" " "for writing", graphfilename); } } if (args.pa_flags & FLAG_WANTS_MAPPINGS) { args.pa_graphfile = fopen(graphfilename, "w"); if (args.pa_graphfile == NULL) err(EX_OSERR, "ERROR: cannot open \"%s\" for writing", graphfilename); } /* if we've been asked to process a log file, do that and exit */ if (args.pa_flags & FLAG_READ_LOGFILE) { /* * Print the log in textual form if we haven't been * asked to generate profiling information. */ if ((args.pa_flags & FLAG_DO_ANALYSIS) == 0) args.pa_flags |= FLAG_DO_PRINT; pmcstat_initialize_logging(&args); args.pa_logfd = pmcstat_open_log(args.pa_inputpath, PMCSTAT_OPEN_FOR_READ); if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL) err(EX_OSERR, "ERROR: Cannot create parser"); pmcstat_process_log(&args); pmcstat_shutdown_logging(&args); exit(EX_OK); } /* otherwise, we've been asked to collect data */ if (pmc_init() < 0) err(EX_UNAVAILABLE, "ERROR: Initialization of the pmc(3) library failed"); if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */ err(EX_OSERR, "ERROR: Cannot determine the number of PMCs " "on CPU %d", 0); /* Allocate a kqueue */ if ((pmcstat_kq = kqueue()) < 0) err(EX_OSERR, "ERROR: Cannot allocate kqueue"); /* * Configure the specified log file or setup a default log * consumer via a pipe. */ if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) { if (args.pa_outputpath) args.pa_logfd = pmcstat_open_log(args.pa_outputpath, PMCSTAT_OPEN_FOR_WRITE); else { /* * process the log on the fly by reading it in * through a pipe. */ if (pipe(pipefd) < 0) err(EX_OSERR, "ERROR: pipe(2) failed"); if (fcntl(pipefd[READPIPEFD], F_SETFL, O_NONBLOCK) < 0) err(EX_OSERR, "ERROR: fcntl(2) failed"); EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD, 0, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent"); args.pa_logfd = pipefd[WRITEPIPEFD]; args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT); args.pa_logparser = pmclog_open(pipefd[READPIPEFD]); } if (pmc_configure_logfile(args.pa_logfd) < 0) err(EX_OSERR, "ERROR: Cannot configure log file"); } /* remember to check for driver errors if we are sampling or logging */ check_driver_stats = (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) || (args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE); /* * Allocate PMCs. */ STAILQ_FOREACH(ev, &args.pa_events, ev_next) { if (pmc_allocate(ev->ev_spec, ev->ev_mode, ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0) err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with " "specification \"%s\"", PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process", ev->ev_spec); if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && pmc_set(ev->ev_pmcid, ev->ev_count) < 0) err(EX_OSERR, "ERROR: Cannot set sampling count " "for PMC \"%s\"", ev->ev_name); } /* compute printout widths */ STAILQ_FOREACH(ev, &args.pa_events, ev_next) { int counter_width; int display_width; int header_width; (void) pmc_width(ev->ev_pmcid, &counter_width); header_width = strlen(ev->ev_name) + 2; /* prefix '%c/' */ display_width = (int) floor(counter_width / 3.32193) + 1; if (PMC_IS_SYSTEM_MODE(ev->ev_mode)) header_width += 3; /* 2 digit CPU number + '/' */ if (header_width > display_width) { ev->ev_fieldskip = 0; ev->ev_fieldwidth = header_width; } else { ev->ev_fieldskip = display_width - header_width; ev->ev_fieldwidth = display_width; } } /* * If our output is being set to a terminal, register a handler * for window size changes. */ if (isatty(fileno(args.pa_printfile))) { if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0) err(EX_OSERR, "ERROR: Cannot determine window size"); pmcstat_displayheight = ws.ws_row - 1; EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for " "SIGWINCH"); } EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO"); /* * An exec() failure of a forked child is signalled by the * child sending the parent a SIGCHLD. We don't register an * actual signal handler for SIGCHLD, but instead use our * kqueue to pick up the signal. */ EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD"); /* setup a timer if we have counting mode PMCs needing to be printed */ if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, args.pa_interval * 1000, NULL); if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) err(EX_OSERR, "ERROR: Cannot register kevent for " "timer"); } /* attach PMCs to the target process, starting it if specified */ if (args.pa_flags & FLAG_HAS_COMMANDLINE) pmcstat_create_process(&args); if (check_driver_stats && pmc_get_driver_stats(&ds_start) < 0) err(EX_OSERR, "ERROR: Cannot retrieve driver statistics"); /* Attach process pmcs to the target process. */ if (args.pa_flags & (FLAG_HAS_TARGET | FLAG_HAS_COMMANDLINE)) { if (SLIST_EMPTY(&args.pa_targets)) errx(EX_DATAERR, "ERROR: No matching target " "processes."); if (args.pa_flags & FLAG_HAS_PROCESS_PMCS) pmcstat_attach_pmcs(&args); if (pmcstat_kvm) { kvm_close(pmcstat_kvm); pmcstat_kvm = NULL; } } /* start the pmcs */ pmcstat_start_pmcs(&args); /* start the (commandline) process if needed */ if (args.pa_flags & FLAG_HAS_COMMANDLINE) pmcstat_start_process(); /* initialize logging if printing the configured log */ if ((args.pa_flags & FLAG_DO_PRINT) && (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))) pmcstat_initialize_logging(&args); /* Handle SIGINT using the kqueue loop */ sa.sa_handler = SIG_IGN; sa.sa_flags = 0; (void) sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) < 0) err(EX_OSERR, "ERROR: Cannot install signal handler"); /* * loop till either the target process (if any) exits, or we * are killed by a SIGINT. */ runstate = PMCSTAT_RUNNING; do_print = 0; do { if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) { if (errno != EINTR) err(EX_OSERR, "ERROR: kevent failed"); else continue; } if (kev.flags & EV_ERROR) errc(EX_OSERR, kev.data, "ERROR: kevent failed"); switch (kev.filter) { case EVFILT_PROC: /* target has exited */ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) runstate = pmcstat_close_log(&args); else runstate = PMCSTAT_FINISHED; do_print = 1; break; case EVFILT_READ: /* log file data is present */ runstate = pmcstat_process_log(&args); break; case EVFILT_SIGNAL: if (kev.ident == SIGCHLD) { /* * The child process sends us a * SIGCHLD if its exec() failed. We * wait for it to exit and then exit * ourselves. */ (void) wait(&c); runstate = PMCSTAT_FINISHED; } else if (kev.ident == SIGIO) { /* * We get a SIGIO if a PMC loses all * of its targets, or if logfile * writes encounter an error. */ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) { runstate = pmcstat_close_log(&args); if (args.pa_flags & (FLAG_DO_PRINT|FLAG_DO_ANALYSIS)) pmcstat_process_log(&args); } do_print = 1; /* print PMCs at exit */ runstate = PMCSTAT_FINISHED; } else if (kev.ident == SIGINT) { /* Kill the child process if we started it */ if (args.pa_flags & FLAG_HAS_COMMANDLINE) pmcstat_kill_process(&args); + /* Close the pipe to self, if present. */ + if (args.pa_flags & FLAG_HAS_PIPE) + (void) close(pipefd[READPIPEFD]); runstate = PMCSTAT_FINISHED; } else if (kev.ident == SIGWINCH) { if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0) err(EX_OSERR, "ERROR: Cannot determine " "window size"); pmcstat_displayheight = ws.ws_row - 1; } else assert(0); break; case EVFILT_TIMER: /* print out counting PMCs */ do_print = 1; break; } if (do_print && (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { pmcstat_print_pmcs(&args); if (runstate == PMCSTAT_FINISHED && /* final newline */ (args.pa_flags & FLAG_DO_PRINT) == 0) (void) fprintf(args.pa_printfile, "\n"); do_print = 0; } } while (runstate != PMCSTAT_FINISHED); /* flush any pending log entries */ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) pmc_flush_logfile(); pmcstat_cleanup(&args); free(args.pa_kernel); /* check if the driver lost any samples or events */ if (check_driver_stats) { if (pmc_get_driver_stats(&ds_end) < 0) err(EX_OSERR, "ERROR: Cannot retrieve driver " "statistics"); if (ds_start.pm_intr_bufferfull != ds_end.pm_intr_bufferfull && args.pa_verbosity > 0) warnx("WARNING: some samples were dropped. Please " "consider tuning the \"kern.hwpmc.nsamples\" " "tunable."); if (ds_start.pm_buffer_requests_failed != ds_end.pm_buffer_requests_failed && args.pa_verbosity > 0) warnx("WARNING: some events were discarded. Please " "consider tuning the \"kern.hwpmc.nbuffers\" " "tunable."); } exit(EX_OK); } Index: projects/cambria/usr.sbin/syslogd/syslog.conf.5 =================================================================== --- projects/cambria/usr.sbin/syslogd/syslog.conf.5 (revision 186459) +++ projects/cambria/usr.sbin/syslogd/syslog.conf.5 (revision 186460) @@ -1,517 +1,517 @@ .\" Copyright (c) 1990, 1991, 1993 .\" 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. .\" 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. .\" .\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93 .\" $FreeBSD$ .\" -.Dd June 9, 1993 +.Dd December 23, 2008 .Dt SYSLOG.CONF 5 .Os .Sh NAME .Nm syslog.conf .Nd .Xr syslogd 8 configuration file .Sh DESCRIPTION The .Nm file is the configuration file for the .Xr syslogd 8 program. It consists of blocks of lines separated by .Em program and .Em hostname specifications (separations appear alone on their lines), with each line containing two fields: the .Em selector field which specifies the types of messages and priorities to which the line applies, and an .Em action field which specifies the action to be taken if a message .Xr syslogd 8 receives matches the selection criteria. The .Em selector field is separated from the .Em action field by one or more tab characters or spaces. .Pp Note that if you use spaces as separators, your .Nm might be incompatible with other Unices or Unix-like systems. This functionality was added for ease of configuration (e.g.\& it is possible to cut-and-paste into .Nm ) , and to avoid possible mistakes. This change however preserves backwards compatibility with the old style of .Nm (i.e., tab characters only). .Pp The .Em selectors are encoded as a .Em facility , a period .Pq Dq \&. , an optional set of comparison flags .Pq Oo \&! Oc Op <=> , and a .Em level , with no intervening white-space. Both the .Em facility and the .Em level are case insensitive. .Pp The .Em facility describes the part of the system generating the message, and is one of the following keywords: .Cm auth , authpriv , console , cron , daemon , ftp , kern , lpr , .Cm mail , mark , news , ntp , security , syslog , user , uucp , and .Cm local0 through .Cm local7 . These keywords (with the exception of mark) correspond to similar .Dq Dv LOG_ values specified to the .Xr openlog 3 and .Xr syslog 3 library routines. .Pp The .Em comparison flags may be used to specify exactly what is logged. The default comparison is .Dq => (or, if you prefer, .Dq >= ) , which means that messages from the specified .Em facility list, and of a priority level equal to or greater than .Em level will be logged. Comparison flags beginning with .Dq Li \&! will have their logical sense inverted. Thus .Dq !=info means all levels except info and .Dq !notice has the same meaning as .Dq