From: Felix Fietkau Date: Fri, 8 May 2009 22:48:08 +0000 (+0000) Subject: restore 2.6.23 support for rb532. 2.6.24 is 100% broken and was only added accidentally X-Git-Tag: 8.09.1~73 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=d59cdde45d354265b94dbb9df767a28c507b1893;p=openwrt%2Fsvn-archive%2Fopenwrt.git restore 2.6.23 support for rb532. 2.6.24 is 100% broken and was only added accidentally SVN-Revision: 15718 --- diff --git a/target/linux/generic-2.6/config-2.6.23 b/target/linux/generic-2.6/config-2.6.23 new file mode 100644 index 0000000000..99ef377fee --- /dev/null +++ b/target/linux/generic-2.6/config-2.6.23 @@ -0,0 +1,1759 @@ +# CONFIG_6PACK is not set +# CONFIG_8139CP is not set +# CONFIG_9P_FS is not set +# CONFIG_ACENIC is not set +# CONFIG_ACORN_PARTITION is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADM6996_PHY is not set +# CONFIG_AFFS_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_AF_RXRPC is not set +CONFIG_AIRO=m +CONFIG_AIRO_CS=m +# CONFIG_AMD8111_ETH is not set +# CONFIG_AMIGA_PARTITION is not set +CONFIG_ANON_INODES=y +# CONFIG_APPLICOM is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_ARCNET is not set +CONFIG_ARPD=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_ATA is not set +# CONFIG_ATALK is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_ATA_GENERIC is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_ATL1 is not set +CONFIG_ATM=m +CONFIG_ATMEL=m +# CONFIG_ATM_AMBASSADOR is not set +CONFIG_ATM_BR2684=m +CONFIG_ATM_BR2684_IPFILTER=y +CONFIG_ATM_CLIP=m +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_DRIVERS is not set +CONFIG_ATM_DUMMY=m +# CONFIG_ATM_ENI is not set +# CONFIG_ATM_FIRESTREAM is not set +# CONFIG_ATM_FORE200E_MAYBE is not set +# CONFIG_ATM_HE is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_IA is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_LANAI is not set +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +# CONFIG_ATM_NICSTAR is not set +CONFIG_ATM_TCP=m +# CONFIG_ATM_ZATM is not set +# CONFIG_AUDIT is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_AUTOFS_FS is not set +CONFIG_AX25=m +# CONFIG_AX25_DAMA_SLAVE is not set +# CONFIG_AX88796 is not set +# CONFIG_B44 is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +CONFIG_BASE_FULL=y +# CONFIG_BASLER_EXCITE is not set +# CONFIG_BAYCOM_EPP is not set +# CONFIG_BAYCOM_PAR is not set +# CONFIG_BAYCOM_SER_FDX is not set +# CONFIG_BAYCOM_SER_HDX is not set +CONFIG_BCM43XX=m +CONFIG_BCM43XX_DEBUG=y +CONFIG_BCM43XX_DMA=y +CONFIG_BCM43XX_DMA_AND_PIO_MODE=y +# CONFIG_BCM43XX_DMA_MODE is not set +CONFIG_BCM43XX_PIO=y +# CONFIG_BCM43XX_PIO_MODE is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BLINK is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_CPQ_DA is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_CRYPTOLOOP=m +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_CS5535 is not set +# CONFIG_BLK_DEV_CY82C693 is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_DELKIN is not set +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HPT34X is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDEDMA_FORCED is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_BLK_DEV_IDEPNP is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_IT8213 is not set +# CONFIG_BLK_DEV_IT821X is not set +# CONFIG_BLK_DEV_JMICRON is not set +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_OFFBOARD is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_SC1200 is not set +CONFIG_BLK_DEV_SD=m +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_TC86C001 is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +CONFIG_BLOCK=y +# CONFIG_BNX2 is not set +CONFIG_BONDING=m +# CONFIG_BPQETHER is not set +CONFIG_BRIDGE=y +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_ULOG=m +CONFIG_BRIDGE_EBT_VLAN=m +# CONFIG_BRIDGE_NETFILTER is not set +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BROKEN_ON_SMP=y +CONFIG_BSD_DISKLABEL=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_BT=m +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +# CONFIG_BT_CMTP is not set +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +# CONFIG_BT_HCIBLUECARD is not set +CONFIG_BT_HCIBPA10X=m +# CONFIG_BT_HCIBT3C is not set +# CONFIG_BT_HCIBTUART is not set +# CONFIG_BT_HCIDTL1 is not set +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIVHCI=m +CONFIG_BT_HIDP=m +CONFIG_BT_L2CAP=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_SCO=m +CONFIG_BUG=y +# CONFIG_CAPI_AVM is not set +# CONFIG_CAPI_EICON is not set +# CONFIG_CAPI_TRACE is not set +CONFIG_CARDBUS=y +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_CASSINI is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_CFG80211 is not set +# CONFIG_CHELSIO_T1 is not set +# CONFIG_CHELSIO_T3 is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CICADA_PHY is not set +CONFIG_CIFS=m +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_STATS=y +# CONFIG_CIFS_STATS2 is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +CONFIG_CLS_U32_MARK=y +CONFIG_CLS_U32_PERF=y +CONFIG_CMDLINE="" +# CONFIG_CODA_FS is not set +CONFIG_CONFIGFS_FS=y +# CONFIG_CONNECTOR is not set +# CONFIG_CRAMFS is not set +CONFIG_CRC16=m +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_CRC_CCITT=m +CONFIG_CRC_ITU_T=m +CONFIG_CROSSCOMPILE=y +CONFIG_CRYPTO=y +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_HASH=m +CONFIG_CRYPTO_HMAC=m +# CONFIG_CRYPTO_HW is not set +CONFIG_CRYPTO_KHAZAD=m +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_NULL=m +# CONFIG_CRYPTO_PCBC is not set +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_WP512=m +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_DAB is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DECNET is not set +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_BIC is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_CUBIC is not set +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_HTCP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +# CONFIG_DEFAULT_NOOP is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="vegas" +CONFIG_DEFAULT_VEGAS=y +# CONFIG_DEFAULT_WESTWOOD is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DGRS is not set +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_DL2K is not set +# CONFIG_DLM is not set +# CONFIG_DMA_ENGINE is not set +# CONFIG_DNOTIFY is not set +# CONFIG_DRM is not set +# CONFIG_DS1682 is not set +# CONFIG_DTLK is not set +# CONFIG_DUMMY is not set +# CONFIG_DVB is not set +# CONFIG_DVB_CORE is not set +CONFIG_E100=m +# CONFIG_E1000 is not set +# CONFIG_ECONET is not set +# CONFIG_EEPRO100 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EFI_PARTITION is not set +# CONFIG_EFS_FS is not set +# CONFIG_ELF_CORE is not set +CONFIG_EMBEDDED=y +# CONFIG_ENABLE_MUST_CHECK is not set +# CONFIG_EPIC100 is not set +CONFIG_EPOLL=y +# CONFIG_EQUALIZER is not set +CONFIG_EVENTFD=y +CONFIG_EXPERIMENTAL=y +CONFIG_EXPORTFS=m +CONFIG_EXT2_FS=m +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=m +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_FAT_FS=m +# CONFIG_FB is not set +# CONFIG_FB_IBM_GXT4500 is not set +# CONFIG_FDDI is not set +# CONFIG_FEALNX is not set +CONFIG_FIB_RULES=y +# CONFIG_FIREWIRE is not set +CONFIG_FLATMEM=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_FORCEDETH is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_FTL is not set +# CONFIG_FUSE_FS is not set +# CONFIG_FUSION is not set +# CONFIG_FUSION_FC is not set +# CONFIG_FUSION_SAS is not set +# CONFIG_FUSION_SPI is not set +CONFIG_FUTEX=y +CONFIG_FW_LOADER=y +CONFIG_GACT_PROB=y +# CONFIG_GAMEPORT is not set +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_TIME=y +# CONFIG_GFS2_FS is not set +CONFIG_GPIO_DEVICE=m +# CONFIG_HAMACHI is not set +CONFIG_HAMRADIO=y +# CONFIG_HAPPYMEAL is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_HERMES=m +CONFIG_HFSPLUS_FS=m +CONFIG_HFS_FS=m +# CONFIG_HID is not set +# CONFIG_HID_DEBUG is not set +# CONFIG_HID_FF is not set +# CONFIG_HID_SUPPORT is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_HIPPI is not set +CONFIG_HOSTAP=m +# CONFIG_HOSTAP_CS is not set +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_FIRMWARE_NVRAM=y +CONFIG_HOSTAP_PCI=m +CONFIG_HOSTAP_PLX=m +CONFIG_HOTPLUG=y +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_HP100 is not set +# CONFIG_HPFS_FS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set +CONFIG_HZ=100 +CONFIG_HZ_100=y +# CONFIG_HZ_1000 is not set +# CONFIG_HZ_1024 is not set +# CONFIG_HZ_128 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_256 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_48 is not set +# CONFIG_I2C_ALGOPCA is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_ELEKTOR is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_I810 is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_PROSAVAGE is not set +# CONFIG_I2C_SAVAGE4 is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2O is not set +# CONFIG_I82092 is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDMA_IVB is not set +# CONFIG_IDEDMA_ONLYDISK is not set +# CONFIG_IDEPCI_SHARE_IRQ is not set +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_IDE_PROC_FS=y +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IEEE1394 is not set +CONFIG_IEEE80211=m +CONFIG_IEEE80211_CRYPT_CCMP=m +CONFIG_IEEE80211_CRYPT_TKIP=m +CONFIG_IEEE80211_CRYPT_WEP=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_SOFTMAC=m +# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set +CONFIG_IFB=m +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_IMQ=m +# CONFIG_IMQ_BEHAVIOR_AA is not set +# CONFIG_IMQ_BEHAVIOR_AB is not set +CONFIG_IMQ_BEHAVIOR_BA=y +# CONFIG_IMQ_BEHAVIOR_BB is not set +CONFIG_IMQ_NUM_DEVS=2 +CONFIG_INET=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET_AH=m +CONFIG_INET_DIAG=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_TCP_DIAG=m +CONFIG_INET_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_TUNNEL=m +# CONFIG_INFINIBAND is not set +# CONFIG_INFTL is not set +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_INOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_INPUT is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_EVDEV=m +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_MISC is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_TARGET_IMQ=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_ROUTE=m +# CONFIG_IPC_NS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_IPSEC_NAT_TRAVERSAL=y +CONFIG_IPV6=m +# CONFIG_IPV6_MIP6 is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_IPV6_PRIVACY is not set +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_SIT=m +# CONFIG_IPV6_TUNNEL is not set +CONFIG_IPW2100=m +# CONFIG_IPW2100_DEBUG is not set +CONFIG_IPW2100_MONITOR=y +CONFIG_IPW2200=m +# CONFIG_IPW2200_DEBUG is not set +CONFIG_IPW2200_MONITOR=y +# CONFIG_IPW2200_PROMISCUOUS is not set +# CONFIG_IPW2200_QOS is not set +# CONFIG_IPW2200_RADIOTAP is not set +# CONFIG_IPX is not set +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_DCCP is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +# CONFIG_IP_MROUTE is not set +CONFIG_IP_MULTICAST=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP_NF_CONNTRACK=y +# CONFIG_IP_NF_CONNTRACK_EVENTS is not set +CONFIG_IP_NF_CONNTRACK_MARK=y +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_H323=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_HASHLIMIT=m +CONFIG_IP_NF_MATCH_IPP2P=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LAYER7=m +# CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG is not set +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_SET=m +CONFIG_IP_NF_MATCH_TIME=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_H323=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_NAT_PPTP=m +CONFIG_IP_NF_NAT_SIP=m +# CONFIG_IP_NF_NAT_SNMP_BASIC is not set +CONFIG_IP_NF_NAT_TFTP=m +# CONFIG_IP_NF_NETBIOS_NS is not set +CONFIG_IP_NF_PPTP=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_SET=m +CONFIG_IP_NF_SET_HASHSIZE=1024 +CONFIG_IP_NF_SET_IPHASH=m +CONFIG_IP_NF_SET_IPMAP=m +CONFIG_IP_NF_SET_IPPORTHASH=m +CONFIG_IP_NF_SET_IPTREE=m +CONFIG_IP_NF_SET_IPTREEMAP=m +CONFIG_IP_NF_SET_MACIPMAP=m +CONFIG_IP_NF_SET_MAX=256 +CONFIG_IP_NF_SET_NETHASH=m +CONFIG_IP_NF_SET_PORTMAP=m +CONFIG_IP_NF_SIP=m +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_IMQ=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_ROUTE=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_TARGET_SET=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TFTP=m +# CONFIG_IP_PNP is not set +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_MULTIPATH_CACHED=y +CONFIG_IP_ROUTE_MULTIPATH_DRR=m +CONFIG_IP_ROUTE_MULTIPATH_RANDOM=m +CONFIG_IP_ROUTE_MULTIPATH_RR=m +CONFIG_IP_ROUTE_MULTIPATH_WRANDOM=m +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_SCTP is not set +# CONFIG_IP_VS is not set +# CONFIG_IRDA is not set +# CONFIG_ISCSI_TCP is not set +CONFIG_ISDN=m +CONFIG_ISDN_CAPI=m +CONFIG_ISDN_CAPI_CAPI20=m +CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIFS_BOOL=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +# CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON is not set +# CONFIG_ISDN_I4L is not set +CONFIG_ISO9660_FS=m +# CONFIG_IXGB is not set +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_XATTR is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_JOLIET=y +# CONFIG_KALLSYMS is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_KEXEC is not set +# CONFIG_KEYS is not set +# CONFIG_KMOD is not set +# CONFIG_LAPB is not set +# CONFIG_LASAT is not set +# CONFIG_LBD is not set +# CONFIG_LDM_PARTITION is not set +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_IDE_DISK is not set +# CONFIG_LEDS_TRIGGER_MORSE is not set +CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +# CONFIG_LEDS_TRIGGER_NETDEV is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_LIBCRC32C=m +# CONFIG_LIBERTAS is not set +# CONFIG_LIBERTAS_USB is not set +CONFIG_LLC=y +CONFIG_LLC2=m +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCKD=m +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_LOCKD_V4=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_LSF is not set +# CONFIG_LXT_PHY is not set +# CONFIG_MAC80211 is not set +# CONFIG_MAC80211_DEBUG is not set +# CONFIG_MAC80211_LEDS is not set +# CONFIG_MACVLAN is not set +# CONFIG_MAC_EMUMOUSEBTN is not set +CONFIG_MAC_PARTITION=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_MARKEINS is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_MD is not set +# CONFIG_MEGARAID_LEGACY is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_SAS is not set +# CONFIG_MFD_SM501 is not set +CONFIG_MII=y +CONFIG_MINIX_FS=m +# CONFIG_MINIX_SUBPARTITION is not set +CONFIG_MINI_FO=y +CONFIG_MISC_DEVICES=y +CONFIG_MKISS=m +# CONFIG_MMC is not set +CONFIG_MMU=y +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_INPORT is not set +# CONFIG_MOUSE_LOGIBM is not set +# CONFIG_MOUSE_PC110PAD is not set +CONFIG_MSDOS_FS=m +CONFIG_MSDOS_PARTITION=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +CONFIG_MTD_ROOTFS_ROOT_DEV=y +CONFIG_MTD_ROOTFS_SPLIT=y +# CONFIG_MTD_UBI is not set +# CONFIG_MVSWITCH_PHY is not set +# CONFIG_MWAVE is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NCP_FS is not set +CONFIG_NET=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETDEBUG is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +# CONFIG_NETFILTER_NETLINK is not set +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PORTSCAN=m +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_NETFILTER_XT_TARGET_CHAOS=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_DELUDE=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_TARPIT=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +# CONFIG_NETPOLL is not set +# CONFIG_NETROM is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NET_9P is not set +CONFIG_NET_ACT_GACT=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_POLICE=y +# CONFIG_NET_ACT_SIMP is not set +CONFIG_NET_CLS=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_POLICE=y +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_U32=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_ETHERNET=y +# CONFIG_NET_FC is not set +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_NET_IPIP=m +# CONFIG_NET_ISA is not set +CONFIG_NET_KEY=m +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_NET_PCI=y +# CONFIG_NET_PCMCIA is not set +CONFIG_NET_PKTGEN=m +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_NET_RADIO=y +# CONFIG_NET_SB1000 is not set +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_CBQ=m +# CONFIG_NET_SCH_CLK_CPU is not set +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +CONFIG_NET_SCH_CLK_JIFFIES=y +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_ESFQ=m +CONFIG_NET_SCH_ESFQ_NFCT=y +CONFIG_NET_SCH_FIFO=y +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_RR=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_TEQL=m +# CONFIG_NET_TULIP is not set +CONFIG_NET_WIRELESS=y +CONFIG_NET_WIRELESS_RTNETLINK=y +# CONFIG_NEW_GPIO is not set +CONFIG_NEW_LEDS=y +CONFIG_NFSD=m +CONFIG_NFSD_TCP=y +# CONFIG_NFSD_V2_ACL is not set +CONFIG_NFSD_V3=y +# CONFIG_NFSD_V3_ACL is not set +CONFIG_NFSD_V4=y +# CONFIG_NFS_ACL_SUPPORT is not set +CONFIG_NFS_COMMON=y +# CONFIG_NFS_DIRECTIO is not set +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFTL is not set +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_ENABLED=y +# CONFIG_NF_CONNTRACK_EVENTS is not set +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_NF_CONNTRACK_RTSP=m +# CONFIG_NF_CONNTRACK_SANE is not set +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_SUPPORT=y +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_ACCT=y +CONFIG_NF_CT_PROTO_GRE=m +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +CONFIG_NF_NAT=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_H323=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_RTSP=m +CONFIG_NF_NAT_SIP=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NLS=m +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_CODEPAGE_1250=m +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_CODEPAGE_437=m +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=m +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_ISO8859_1=m +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=m +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_ISO8859_9 is not set +CONFIG_NLS_KOI8_R=m +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=m +CONFIG_NORTEL_HERMES=m +# CONFIG_NO_HZ is not set +# CONFIG_NS83820 is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_OSF_PARTITION is not set +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_PARPORT is not set +# CONFIG_PARPORT_PC is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_ISAPNP is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_LEGACY is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PCMCIA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +CONFIG_PATA_PLATFORM=m +# CONFIG_PATA_QDI is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set +# CONFIG_PATA_WINBOND_VLB is not set +# CONFIG_PC300TOO is not set +# CONFIG_PCCARD is not set +# CONFIG_PCI is not set +# CONFIG_PCI_ATMEL is not set +CONFIG_PCI_HERMES=m +# CONFIG_PCMCIA is not set +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_ATMEL is not set +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_HERMES is not set +# CONFIG_PCMCIA_IOCTL is not set +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_NETWAVE is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_RAYCS is not set +# CONFIG_PCMCIA_SPECTRUM is not set +# CONFIG_PCMCIA_SYM53C500 is not set +# CONFIG_PCMCIA_WAVELAN is not set +# CONFIG_PCMCIA_WL3501 is not set +# CONFIG_PCNET32 is not set +# CONFIG_PD6729 is not set +# CONFIG_PDC_ADMA is not set +# CONFIG_PHANTOM is not set +# CONFIG_PHONE is not set +# CONFIG_PHYLIB is not set +CONFIG_PLIST=y +CONFIG_PLX_HERMES=m +# CONFIG_PM is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_POWER_SUPPLY is not set +CONFIG_PPP=m +CONFIG_PPPOATM=m +CONFIG_PPPOE=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_SYNC_TTY=m +# CONFIG_PPS is not set +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_PRINTK=y +# CONFIG_PRINTK_TIME is not set +CONFIG_PRISM54=m +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +# CONFIG_PROFILING is not set +# CONFIG_QEMU is not set +# CONFIG_QLA3XXX is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_R3964 is not set +# CONFIG_R8169 is not set +# CONFIG_RADIO_ADAPTERS is not set +# CONFIG_RADIO_AZTECH is not set +# CONFIG_RADIO_CADET is not set +# CONFIG_RADIO_GEMTEK is not set +# CONFIG_RADIO_GEMTEK_PCI is not set +# CONFIG_RADIO_MAESTRO is not set +# CONFIG_RADIO_MAXIRADIO is not set +# CONFIG_RADIO_RTRACK is not set +# CONFIG_RADIO_RTRACK2 is not set +# CONFIG_RADIO_SF16FMI is not set +# CONFIG_RADIO_SF16FMR2 is not set +# CONFIG_RADIO_TERRATEC is not set +# CONFIG_RADIO_TRUST is not set +# CONFIG_RADIO_TYPHOON is not set +# CONFIG_RADIO_ZOLTRIX is not set +# CONFIG_RAID_ATTRS is not set +CONFIG_RAMFS=y +# CONFIG_RAW_DRIVER is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_RELAY is not set +# CONFIG_RESOURCES_64BIT is not set +# CONFIG_RFD_FTL is not set +# CONFIG_RFKILL is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_ROSE is not set +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_RTC_CLASS is not set +CONFIG_RTC_DRV_CMOS=y +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTL8187 is not set +CONFIG_RT_MUTEXES=y +# CONFIG_S2IO is not set +# CONFIG_SATA_AHCI is not set +# CONFIG_SATA_INIC162X is not set +# CONFIG_SATA_MV is not set +# CONFIG_SATA_NV is not set +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIL24 is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_SX4 is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set +# CONFIG_SC92031 is not set +CONFIG_SCSI=m +# CONFIG_SCSI_3W_9XXX is not set +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AACRAID is not set +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AIC79XX is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_AIC94XX is not set +# CONFIG_SCSI_ARCMSR is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DEBUG is not set +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_DMX3191D is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_ESP_CORE is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_HPTIOP is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_INIA100 is not set +# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPR is not set +# CONFIG_SCSI_IPS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_LPFC is not set +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_PAS16 is not set +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_PSI240I is not set +# CONFIG_SCSI_QLA_FC is not set +# CONFIG_SCSI_QLA_ISCSI is not set +# CONFIG_SCSI_QLOGIC_1280 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP is not set +# CONFIG_SCSI_STEX is not set +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_SYM53C8XX_2 is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SECCOMP is not set +# CONFIG_SECURITY is not set +CONFIG_SELECT_MEMORY_MODEL=y +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_APPLESMC is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_CORETEMP is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +CONFIG_SERIAL_8250_NR_UARTS=2 +# CONFIG_SERIAL_8250_PCI is not set +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_SERIAL_UARTLITE is not set +# CONFIG_SERIO is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SHAPER is not set +CONFIG_SHMEM=y +CONFIG_SIGNALFD=y +# CONFIG_SIS190 is not set +# CONFIG_SIS900 is not set +# CONFIG_SK98LIN is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +CONFIG_SLAB=y +CONFIG_SLHC=m +# CONFIG_SLIP is not set +# CONFIG_SLOB is not set +# CONFIG_SLUB is not set +# CONFIG_SMB_FS is not set +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_SND=m +# CONFIG_SND_AC97_POWER_SAVE is not set +# CONFIG_SND_AD1816A is not set +# CONFIG_SND_AD1848 is not set +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ADLIB is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ALS100 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AZT2320 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMI8330 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_CS4231 is not set +# CONFIG_SND_CS4232 is not set +# CONFIG_SND_CS4236 is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_DT019X is not set +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1688 is not set +# CONFIG_SND_ES18XX is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_ES968 is not set +# CONFIG_SND_FM801 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_GUSCLASSIC is not set +# CONFIG_SND_GUSEXTREME is not set +# CONFIG_SND_GUSMAX is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +CONFIG_SND_HWDEP=m +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_INTERWAVE is not set +# CONFIG_SND_INTERWAVE_STB is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_MIRO is not set +# CONFIG_SND_MIXART is not set +CONFIG_SND_MIXER_OSS=m +# CONFIG_SND_MONA is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_OPL3SA2 is not set +# CONFIG_SND_OPTI92X_AD1848 is not set +# CONFIG_SND_OPTI92X_CS4231 is not set +# CONFIG_SND_OPTI93X is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_PCM=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_PDAUDIOCF is not set +CONFIG_SND_RAWMIDI=m +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_RTCTIMER is not set +# CONFIG_SND_SB16 is not set +# CONFIG_SND_SB8 is not set +# CONFIG_SND_SBAWE is not set +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_SGALAXY is not set +# CONFIG_SND_SOC is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_SSCAPE is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_TIMER=m +# CONFIG_SND_TRIDENT is not set +CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_WAVEFRONT is not set +# CONFIG_SND_YMFPCI is not set +# CONFIG_SNI_RM is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +CONFIG_SOUND=m +# CONFIG_SOUND_PRIME is not set +# CONFIG_SPARSEMEM_MANUAL is not set +# CONFIG_SPI is not set +# CONFIG_SPI_DEBUG is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_SPI_AT25 is not set +# CONFIG_SPI_TLE62X0 is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_SQUASHFS_VMALLOC is not set +# CONFIG_SSFDC is not set +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_STANDALONE=y +# CONFIG_STRIP is not set +# CONFIG_SUNDANCE is not set +# CONFIG_SUNGEM is not set +CONFIG_SUNRPC=m +# CONFIG_SUNRPC_BIND34 is not set +CONFIG_SUNRPC_GSS=m +# CONFIG_SUN_PARTITION is not set +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SWAP=y +# CONFIG_SYNCLINK_CS is not set +CONFIG_SYN_COOKIES=y +CONFIG_SYSCTL=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_SYSFS=y +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_SYSVIPC=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_TASKSTATS is not set +# CONFIG_TCG_TPM is not set +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=m +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_VEGAS=y +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_YEAH=m +# CONFIG_TCP_MD5SIG is not set +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_TEXTSEARCH_KMP=m +# CONFIG_TIFM_CORE is not set +# CONFIG_TIGON3 is not set +CONFIG_TIMERFD=y +# CONFIG_TINY_SHMEM is not set +# CONFIG_TIPC is not set +# CONFIG_TLAN is not set +CONFIG_TMD_HERMES=m +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_TR is not set +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_TUN=m +# CONFIG_TUNER_3036 is not set +# CONFIG_TUNER_TEA5761 is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y +# CONFIG_UFS_FS is not set +# CONFIG_UIO is not set +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_UNIX=y +CONFIG_UNIX98_PTYS=y +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_USB is not set +# CONFIG_USB_ACECAD is not set +CONFIG_USB_ACM=m +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AIPTEK is not set +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_APPLETOUCH is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set +CONFIG_USB_ATM=m +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_BANDWIDTH is not set +CONFIG_USB_BELKIN=y +# CONFIG_USB_BERRY_CHARGE is not set +CONFIG_USB_CATC=m +# CONFIG_USB_CXACRU is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_DEBUG is not set +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ET61X251 is not set +CONFIG_USB_EZUSB=y +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_GADGET is not set +CONFIG_USB_HID=m +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_HIDINPUT=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_KAWETH=m +# CONFIG_USB_KBD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LIBUSUAL is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_ZAURUS=m +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +# CONFIG_USB_OHCI_HCD is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OV511 is not set +CONFIG_USB_PEGASUS=m +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_POWERMATE is not set +CONFIG_USB_PRINTER=m +# CONFIG_USB_QUICKCAM_MESSENGER is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_SE401 is not set +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_AIRPRIME=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_XIRCOM=m +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_SN9C102 is not set +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_ALAUDA=y +CONFIG_USB_STORAGE_DATAFAB=y +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_FREECOM=y +# CONFIG_USB_STORAGE_ISD200 is not set +CONFIG_USB_STORAGE_JUMPSHOT=y +CONFIG_USB_STORAGE_KARMA=y +# CONFIG_USB_STORAGE_ONETOUCH is not set +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_USBAT=y +# CONFIG_USB_STV680 is not set +CONFIG_USB_SUPPORT=y +# CONFIG_USB_TEST is not set +# CONFIG_USB_TOUCHSCREEN is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_USBNET=m +CONFIG_USB_USBNET_MII=m +# CONFIG_USB_VICAM is not set +# CONFIG_USB_W9968CF is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_XUSBATM is not set +CONFIG_USB_YEALINK=m +# CONFIG_USB_ZC0301 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_UTS_NS is not set +CONFIG_VFAT_FS=m +# CONFIG_VIA_VELOCITY is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_CAFE_CCIC is not set +# CONFIG_VIDEO_CAPTURE_DRIVERS is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_CX2341X is not set +# CONFIG_VIDEO_CX25840 is not set +# CONFIG_VIDEO_CX88 is not set +CONFIG_VIDEO_DEV=m +# CONFIG_VIDEO_DPC is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set +# CONFIG_VIDEO_HEXIUM_GEMINI is not set +# CONFIG_VIDEO_HEXIUM_ORION is not set +# CONFIG_VIDEO_IVTV is not set +# CONFIG_VIDEO_KS0127 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_MXB is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA7111 is not set +# CONFIG_VIDEO_SAA7114 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7134 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_STRADIS is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TDA9875 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_VIDEO_V4L1 is not set +CONFIG_VIDEO_V4L1_COMPAT=y +CONFIG_VIDEO_V4L2=y +# CONFIG_VIDEO_VIVI is not set +# CONFIG_VIDEO_VPX3220 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_ZORAN is not set +CONFIG_VIRT_TO_BUS=y +# CONFIG_VITESSE_PHY is not set +CONFIG_VLAN_8021Q=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_VT is not set +# CONFIG_VXFS_FS is not set +CONFIG_W1=m +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_GPIO is not set +# CONFIG_W1_MASTER_MATROX is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W83627HF_WDT is not set +# CONFIG_W83697HF_WDT is not set +# CONFIG_W83877F_WDT is not set +# CONFIG_W83977F_WDT is not set +# CONFIG_WAN is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_WDTPCI is not set +CONFIG_WIRELESS_EXT=y +CONFIG_WLAN_80211=y +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WR_PPMC is not set +# CONFIG_X25 is not set +CONFIG_XFRM=y +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_USER=m +CONFIG_XFS_FS=m +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_SECURITY is not set +# CONFIG_YAFFS_FS is not set +# CONFIG_YAM is not set +# CONFIG_YELLOWFIN is not set +CONFIG_YENTA=m +# CONFIG_YENTA_O2 is not set +# CONFIG_YENTA_RICOH is not set +# CONFIG_YENTA_TI is not set +# CONFIG_YENTA_TOSHIBA is not set +CONFIG_ZD1211RW=m +# CONFIG_ZD1211RW_DEBUG is not set +CONFIG_ZISOFS=y +CONFIG_ZISOFS_FS=m +CONFIG_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA=y +CONFIG_ZONE_DMA_FLAG=1 diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/Kconfig b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/Kconfig new file mode 100644 index 0000000000..7b6f836cda --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/Kconfig @@ -0,0 +1,175 @@ +# +# YAFFS file system configurations +# + +config YAFFS_FS + tristate "YAFFS2 file system support" + default n + depends on MTD + select YAFFS_YAFFS1 + select YAFFS_YAFFS2 + help + YAFFS2, or Yet Another Flash Filing System, is a filing system + optimised for NAND Flash chips. + + To compile the YAFFS2 file system support as a module, choose M + here: the module will be called yaffs2. + + If unsure, say N. + + Further information on YAFFS2 is available at + . + +config YAFFS_YAFFS1 + bool "512 byte / page devices" + depends on YAFFS_FS + default y + help + Enable YAFFS1 support -- yaffs for 512 byte / page devices + + Not needed for 2K-page devices. + + If unsure, say Y. + +config YAFFS_9BYTE_TAGS + bool "Use older-style on-NAND data format with pageStatus byte" + depends on YAFFS_YAFFS1 + default n + help + + Older-style on-NAND data format has a "pageStatus" byte to record + chunk/page state. This byte is zero when the page is discarded. + Choose this option if you have existing on-NAND data using this + format that you need to continue to support. New data written + also uses the older-style format. Note: Use of this option + generally requires that MTD's oob layout be adjusted to use the + older-style format. See notes on tags formats and MTD versions. + + If unsure, say N. + +config YAFFS_DOES_ECC + bool "Lets Yaffs do its own ECC" + depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS + default n + help + This enables Yaffs to use its own ECC functions instead of using + the ones from the generic MTD-NAND driver. + + If unsure, say N. + +config YAFFS_ECC_WRONG_ORDER + bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" + depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS + default n + help + This makes yaffs_ecc.c use the same ecc byte order as Steven + Hill's nand_ecc.c. If not set, then you get the same ecc byte + order as SmartMedia. + + If unsure, say N. + +config YAFFS_YAFFS2 + bool "2048 byte (or larger) / page devices" + depends on YAFFS_FS + default y + help + Enable YAFFS2 support -- yaffs for >= 2K bytes per page devices + + If unsure, say Y. + +config YAFFS_AUTO_YAFFS2 + bool "Autoselect yaffs2 format" + depends on YAFFS_YAFFS2 + default y + help + Without this, you need to explicitely use yaffs2 as the file + system type. With this, you can say "yaffs" and yaffs or yaffs2 + will be used depending on the device page size (yaffs on + 512-byte page devices, yaffs2 on 2K page devices). + + If unsure, say Y. + +config YAFFS_DISABLE_LAZY_LOAD + bool "Disable lazy loading" + depends on YAFFS_YAFFS2 + default n + help + "Lazy loading" defers loading file details until they are + required. This saves mount time, but makes the first look-up + a bit longer. + + Lazy loading will only happen if enabled by this option being 'n' + and if the appropriate tags are available, else yaffs2 will + automatically fall back to immediate loading and do the right + thing. + + Lazy laoding will be required by checkpointing. + + Setting this to 'y' will disable lazy loading. + + If unsure, say N. + +config YAFFS_CHECKPOINT_RESERVED_BLOCKS + int "Reserved blocks for checkpointing" + depends on YAFFS_YAFFS2 + default 10 + help + Give the number of Blocks to reserve for checkpointing. + Checkpointing saves the state at unmount so that mounting is + much faster as a scan of all the flash to regenerate this state + is not needed. These Blocks are reserved per partition, so if + you have very small partitions the default (10) may be a mess + for you. You can set this value to 0, but that does not mean + checkpointing is disabled at all. There only won't be any + specially reserved blocks for checkpointing, so if there is + enough free space on the filesystem, it will be used for + checkpointing. + + If unsure, leave at default (10), but don't wonder if there are + always 2MB used on your large page device partition (10 x 2k + pagesize). When using small partitions or when being very small + on space, you probably want to set this to zero. + +config YAFFS_DISABLE_WIDE_TNODES + bool "Turn off wide tnodes" + depends on YAFFS_FS + default n + help + Wide tnodes are only used for NAND arrays >=32MB for 512-byte + page devices and >=128MB for 2k page devices. They use slightly + more RAM but are faster since they eliminate chunk group + searching. + + Setting this to 'y' will force tnode width to 16 bits and save + memory but make large arrays slower. + + If unsure, say N. + +config YAFFS_ALWAYS_CHECK_CHUNK_ERASED + bool "Force chunk erase check" + depends on YAFFS_FS + default n + help + Normally YAFFS only checks chunks before writing until an erased + chunk is found. This helps to detect any partially written + chunks that might have happened due to power loss. + + Enabling this forces on the test that chunks are erased in flash + before writing to them. This takes more time but is potentially + a bit more secure. + + Suggest setting Y during development and ironing out driver + issues etc. Suggest setting to N if you want faster writing. + + If unsure, say Y. + +config YAFFS_SHORT_NAMES_IN_RAM + bool "Cache short names in RAM" + depends on YAFFS_FS + default y + help + If this config is set, then short names are stored with the + yaffs_Object. This costs an extra 16 bytes of RAM per object, + but makes look-ups faster. + + If unsure, say Y. diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/Makefile b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/Makefile new file mode 100644 index 0000000000..73f46583f4 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the linux YAFFS filesystem routines. +# + +obj-$(CONFIG_YAFFS_FS) += yaffs.o + +yaffs-y := yaffs_ecc.o yaffs_fs.o yaffs_guts.o yaffs_checkptrw.o +yaffs-y += yaffs_packedtags2.o yaffs_nand.o yaffs_qsort.o +yaffs-y += yaffs_tagscompat.o yaffs_tagsvalidity.o +yaffs-y += yaffs_mtdif1.o yaffs_packedtags1.o +yaffs-y += yaffs_mtdif.o yaffs_mtdif2.o diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/devextras.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/devextras.h new file mode 100644 index 0000000000..fcf2690a56 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/devextras.h @@ -0,0 +1,264 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* + * This file is just holds extra declarations used during development. + * Most of these are from kernel includes placed here so we can use them in + * applications. + * + */ + +#ifndef __EXTRAS_H__ +#define __EXTRAS_H__ + +#if defined WIN32 +#define __inline__ __inline +#define new newHack +#endif + +#if !(defined __KERNEL__) || (defined WIN32) + +/* User space defines */ + +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned __u32; + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +#define prefetch(x) 1 + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *new, + struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head *prev, + struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next, prefetch(pos->next); pos != (head); \ + pos = pos->next, prefetch(pos->next)) + +/** + * list_for_each_safe - iterate over a list safe against removal + * of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/* + * File types + */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +#ifndef WIN32 +#include +#endif + +/* + * Attribute flags. These should be or-ed together to figure out what + * has been changed! + */ +#define ATTR_MODE 1 +#define ATTR_UID 2 +#define ATTR_GID 4 +#define ATTR_SIZE 8 +#define ATTR_ATIME 16 +#define ATTR_MTIME 32 +#define ATTR_CTIME 64 +#define ATTR_ATIME_SET 128 +#define ATTR_MTIME_SET 256 +#define ATTR_FORCE 512 /* Not a change, but a change it */ +#define ATTR_ATTR_FLAG 1024 + +struct iattr { + unsigned int ia_valid; + unsigned ia_mode; + unsigned ia_uid; + unsigned ia_gid; + unsigned ia_size; + unsigned ia_atime; + unsigned ia_mtime; + unsigned ia_ctime; + unsigned int ia_attr_flags; +}; + +#define KERN_DEBUG + +#else + +#ifndef WIN32 +#include +#include +#include +#include +#endif + +#endif + +#if defined WIN32 +#undef new +#endif + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/moduleconfig.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/moduleconfig.h new file mode 100644 index 0000000000..016391ca59 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/moduleconfig.h @@ -0,0 +1,65 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Martin Fouts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_CONFIG_H__ +#define __YAFFS_CONFIG_H__ + +#ifdef YAFFS_OUT_OF_TREE + +/* DO NOT UNSET THESE THREE. YAFFS2 will not compile if you do. */ +#define CONFIG_YAFFS_FS +#define CONFIG_YAFFS_YAFFS1 +#define CONFIG_YAFFS_YAFFS2 + +/* These options are independent of each other. Select those that matter. */ + +/* Default: Not selected */ +/* Meaning: Yaffs does its own ECC, rather than using MTD ECC */ +//#define CONFIG_YAFFS_DOES_ECC + +/* Default: Not selected */ +/* Meaning: ECC byte order is 'wrong'. Only meaningful if */ +/* CONFIG_YAFFS_DOES_ECC is set */ +//#define CONFIG_YAFFS_ECC_WRONG_ORDER + +/* Default: Selected */ +/* Meaning: Disables testing whether chunks are erased before writing to them*/ +#define CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK + +/* Default: Selected */ +/* Meaning: Cache short names, taking more RAM, but faster look-ups */ +#define CONFIG_YAFFS_SHORT_NAMES_IN_RAM + +/* Default: 10 */ +/* Meaning: set the count of blocks to reserve for checkpointing */ +#define CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS 10 + +/* +Older-style on-NAND data format has a "pageStatus" byte to record +chunk/page state. This byte is zeroed when the page is discarded. +Choose this option if you have existing on-NAND data in this format +that you need to continue to support. New data written also uses the +older-style format. +Note: Use of this option generally requires that MTD's oob layout be +adjusted to use the older-style format. See notes on tags formats and +MTD versions. +*/ +/* Default: Not selected */ +/* Meaning: Use older-style on-NAND data format with pageStatus byte */ +#define CONFIG_YAFFS_9BYTE_TAGS + +#endif /* YAFFS_OUT_OF_TREE */ + +#endif /* __YAFFS_CONFIG_H__ */ diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_checkptrw.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_checkptrw.c new file mode 100644 index 0000000000..933a33fb8b --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_checkptrw.c @@ -0,0 +1,404 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +const char *yaffs_checkptrw_c_version = + "$Id: yaffs_checkptrw.c,v 1.14 2007-05-15 20:07:40 charles Exp $"; + + +#include "yaffs_checkptrw.h" + + +static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) +{ + + int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; + + T(YAFFS_TRACE_CHECKPOINT, + (TSTR("checkpt blocks available = %d" TENDSTR), + blocksAvailable)); + + + return (blocksAvailable <= 0) ? 0 : 1; +} + + +static int yaffs_CheckpointErase(yaffs_Device *dev) +{ + + int i; + + + if(!dev->eraseBlockInNAND) + return 0; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR), + dev->internalStartBlock,dev->internalEndBlock)); + + for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i)); + if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){ + bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + dev->nFreeChunks += dev->nChunksPerBlock; + } + else { + dev->markNANDBlockBad(dev,i); + bi->blockState = YAFFS_BLOCK_STATE_DEAD; + } + } + } + + dev->blocksInCheckpoint = 0; + + return 1; +} + + +static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) +{ + int i; + int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; + T(YAFFS_TRACE_CHECKPOINT, + (TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR), + dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock)); + + if(dev->checkpointNextBlock >= 0 && + dev->checkpointNextBlock <= dev->internalEndBlock && + blocksAvailable > 0){ + + for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){ + dev->checkpointNextBlock = i + 1; + dev->checkpointCurrentBlock = i; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i)); + return; + } + } + } + T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR))); + + dev->checkpointNextBlock = -1; + dev->checkpointCurrentBlock = -1; +} + +static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) +{ + int i; + yaffs_ExtendedTags tags; + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR), + dev->blocksInCheckpoint, dev->checkpointNextBlock)); + + if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) + for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ + int chunk = i * dev->nChunksPerBlock; + int realignedChunk = chunk - dev->chunkOffset; + + dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags); + T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), + i, tags.objectId,tags.sequenceNumber,tags.eccResult)); + + if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ + /* Right kind of block */ + dev->checkpointNextBlock = tags.objectId; + dev->checkpointCurrentBlock = i; + dev->checkpointBlockList[dev->blocksInCheckpoint] = i; + dev->blocksInCheckpoint++; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i)); + return; + } + } + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR))); + + dev->checkpointNextBlock = -1; + dev->checkpointCurrentBlock = -1; +} + + +int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) +{ + + /* Got the functions we need? */ + if (!dev->writeChunkWithTagsToNAND || + !dev->readChunkWithTagsFromNAND || + !dev->eraseBlockInNAND || + !dev->markNANDBlockBad) + return 0; + + if(forWriting && !yaffs_CheckpointSpaceOk(dev)) + return 0; + + if(!dev->checkpointBuffer) + dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk); + if(!dev->checkpointBuffer) + return 0; + + + dev->checkpointPageSequence = 0; + + dev->checkpointOpenForWrite = forWriting; + + dev->checkpointByteCount = 0; + dev->checkpointSum = 0; + dev->checkpointXor = 0; + dev->checkpointCurrentBlock = -1; + dev->checkpointCurrentChunk = -1; + dev->checkpointNextBlock = dev->internalStartBlock; + + /* Erase all the blocks in the checkpoint area */ + if(forWriting){ + memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); + dev->checkpointByteOffset = 0; + return yaffs_CheckpointErase(dev); + + + } else { + int i; + /* Set to a value that will kick off a read */ + dev->checkpointByteOffset = dev->nDataBytesPerChunk; + /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) + * going to be way more than we need */ + dev->blocksInCheckpoint = 0; + dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2; + dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks); + for(i = 0; i < dev->checkpointMaxBlocks; i++) + dev->checkpointBlockList[i] = -1; + } + + return 1; +} + +int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) +{ + __u32 compositeSum; + compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); + *sum = compositeSum; + return 1; +} + +static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) +{ + + int chunk; + int realignedChunk; + + yaffs_ExtendedTags tags; + + if(dev->checkpointCurrentBlock < 0){ + yaffs_CheckpointFindNextErasedBlock(dev); + dev->checkpointCurrentChunk = 0; + } + + if(dev->checkpointCurrentBlock < 0) + return 0; + + tags.chunkDeleted = 0; + tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */ + tags.chunkId = dev->checkpointPageSequence + 1; + tags.sequenceNumber = YAFFS_SEQUENCE_CHECKPOINT_DATA; + tags.byteCount = dev->nDataBytesPerChunk; + if(dev->checkpointCurrentChunk == 0){ + /* First chunk we write for the block? Set block state to + checkpoint */ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock); + bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; + dev->blocksInCheckpoint++; + } + + chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk; + + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR), + chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); + + realignedChunk = chunk - dev->chunkOffset; + + dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); + dev->checkpointByteOffset = 0; + dev->checkpointPageSequence++; + dev->checkpointCurrentChunk++; + if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ + dev->checkpointCurrentChunk = 0; + dev->checkpointCurrentBlock = -1; + } + memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); + + return 1; +} + + +int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes) +{ + int i=0; + int ok = 1; + + + __u8 * dataBytes = (__u8 *)data; + + + + if(!dev->checkpointBuffer) + return 0; + + if(!dev->checkpointOpenForWrite) + return -1; + + while(i < nBytes && ok) { + + + + dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; + dev->checkpointSum += *dataBytes; + dev->checkpointXor ^= *dataBytes; + + dev->checkpointByteOffset++; + i++; + dataBytes++; + dev->checkpointByteCount++; + + + if(dev->checkpointByteOffset < 0 || + dev->checkpointByteOffset >= dev->nDataBytesPerChunk) + ok = yaffs_CheckpointFlushBuffer(dev); + + } + + return i; +} + +int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) +{ + int i=0; + int ok = 1; + yaffs_ExtendedTags tags; + + + int chunk; + int realignedChunk; + + __u8 *dataBytes = (__u8 *)data; + + if(!dev->checkpointBuffer) + return 0; + + if(dev->checkpointOpenForWrite) + return -1; + + while(i < nBytes && ok) { + + + if(dev->checkpointByteOffset < 0 || + dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { + + if(dev->checkpointCurrentBlock < 0){ + yaffs_CheckpointFindNextCheckpointBlock(dev); + dev->checkpointCurrentChunk = 0; + } + + if(dev->checkpointCurrentBlock < 0) + ok = 0; + else { + + chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + + dev->checkpointCurrentChunk; + + realignedChunk = chunk - dev->chunkOffset; + + /* read in the next chunk */ + /* printf("read checkpoint page %d\n",dev->checkpointPage); */ + dev->readChunkWithTagsFromNAND(dev, realignedChunk, + dev->checkpointBuffer, + &tags); + + if(tags.chunkId != (dev->checkpointPageSequence + 1) || + tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) + ok = 0; + + dev->checkpointByteOffset = 0; + dev->checkpointPageSequence++; + dev->checkpointCurrentChunk++; + + if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) + dev->checkpointCurrentBlock = -1; + } + } + + if(ok){ + *dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; + dev->checkpointSum += *dataBytes; + dev->checkpointXor ^= *dataBytes; + dev->checkpointByteOffset++; + i++; + dataBytes++; + dev->checkpointByteCount++; + } + } + + return i; +} + +int yaffs_CheckpointClose(yaffs_Device *dev) +{ + + if(dev->checkpointOpenForWrite){ + if(dev->checkpointByteOffset != 0) + yaffs_CheckpointFlushBuffer(dev); + } else { + int i; + for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]); + if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) + bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; + else { + // Todo this looks odd... + } + } + YFREE(dev->checkpointBlockList); + dev->checkpointBlockList = NULL; + } + + dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock; + dev->nErasedBlocks -= dev->blocksInCheckpoint; + + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR), + dev->checkpointByteCount)); + + if(dev->checkpointBuffer){ + /* free the buffer */ + YFREE(dev->checkpointBuffer); + dev->checkpointBuffer = NULL; + return 1; + } + else + return 0; + +} + +int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) +{ + /* Erase the first checksum block */ + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR))); + + if(!yaffs_CheckpointSpaceOk(dev)) + return 0; + + return yaffs_CheckpointErase(dev); +} + + + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_checkptrw.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_checkptrw.h new file mode 100644 index 0000000000..d3ff17405f --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_checkptrw.h @@ -0,0 +1,35 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_CHECKPTRW_H__ +#define __YAFFS_CHECKPTRW_H__ + +#include "yaffs_guts.h" + +int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting); + +int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes); + +int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes); + +int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum); + +int yaffs_CheckpointClose(yaffs_Device *dev); + +int yaffs_CheckpointInvalidateStream(yaffs_Device *dev); + + +#endif + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_ecc.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_ecc.c new file mode 100644 index 0000000000..e2860393d3 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_ecc.c @@ -0,0 +1,331 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC + * blocks are used on a 512-byte NAND page. + * + */ + +/* Table generated by gen-ecc.c + * Using a table means we do not have to calculate p1..p4 and p1'..p4' + * for each byte of data. These are instead provided in a table in bits7..2. + * Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore + * this bytes influence on the line parity. + */ + +const char *yaffs_ecc_c_version = + "$Id: yaffs_ecc.c,v 1.9 2007-02-14 01:09:06 wookey Exp $"; + +#include "yportenv.h" + +#include "yaffs_ecc.h" + +static const unsigned char column_parity_table[] = { + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, + 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, + 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, + 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, + 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, + 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, + 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, + 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, + 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, + 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, + 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, + 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, + 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, + 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, + 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, + 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, +}; + +/* Count the bits in an unsigned char or a U32 */ + +static int yaffs_CountBits(unsigned char x) +{ + int r = 0; + while (x) { + if (x & 1) + r++; + x >>= 1; + } + return r; +} + +static int yaffs_CountBits32(unsigned x) +{ + int r = 0; + while (x) { + if (x & 1) + r++; + x >>= 1; + } + return r; +} + +/* Calculate the ECC for a 256-byte block of data */ +void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc) +{ + unsigned int i; + + unsigned char col_parity = 0; + unsigned char line_parity = 0; + unsigned char line_parity_prime = 0; + unsigned char t; + unsigned char b; + + for (i = 0; i < 256; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) // odd number of bits in the byte + { + line_parity ^= i; + line_parity_prime ^= ~i; + } + + } + + ecc[2] = (~col_parity) | 0x03; + + t = 0; + if (line_parity & 0x80) + t |= 0x80; + if (line_parity_prime & 0x80) + t |= 0x40; + if (line_parity & 0x40) + t |= 0x20; + if (line_parity_prime & 0x40) + t |= 0x10; + if (line_parity & 0x20) + t |= 0x08; + if (line_parity_prime & 0x20) + t |= 0x04; + if (line_parity & 0x10) + t |= 0x02; + if (line_parity_prime & 0x10) + t |= 0x01; + ecc[1] = ~t; + + t = 0; + if (line_parity & 0x08) + t |= 0x80; + if (line_parity_prime & 0x08) + t |= 0x40; + if (line_parity & 0x04) + t |= 0x20; + if (line_parity_prime & 0x04) + t |= 0x10; + if (line_parity & 0x02) + t |= 0x08; + if (line_parity_prime & 0x02) + t |= 0x04; + if (line_parity & 0x01) + t |= 0x02; + if (line_parity_prime & 0x01) + t |= 0x01; + ecc[0] = ~t; + +#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER + // Swap the bytes into the wrong order + t = ecc[0]; + ecc[0] = ecc[1]; + ecc[1] = t; +#endif +} + + +/* Correct the ECC on a 256 byte block of data */ + +int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc) +{ + unsigned char d0, d1, d2; /* deltas */ + + d0 = read_ecc[0] ^ test_ecc[0]; + d1 = read_ecc[1] ^ test_ecc[1]; + d2 = read_ecc[2] ^ test_ecc[2]; + + if ((d0 | d1 | d2) == 0) + return 0; /* no error */ + + if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && + ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && + ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { + /* Single bit (recoverable) error in data */ + + unsigned byte; + unsigned bit; + +#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER + // swap the bytes to correct for the wrong order + unsigned char t; + + t = d0; + d0 = d1; + d1 = t; +#endif + + bit = byte = 0; + + if (d1 & 0x80) + byte |= 0x80; + if (d1 & 0x20) + byte |= 0x40; + if (d1 & 0x08) + byte |= 0x20; + if (d1 & 0x02) + byte |= 0x10; + if (d0 & 0x80) + byte |= 0x08; + if (d0 & 0x20) + byte |= 0x04; + if (d0 & 0x08) + byte |= 0x02; + if (d0 & 0x02) + byte |= 0x01; + + if (d2 & 0x80) + bit |= 0x04; + if (d2 & 0x20) + bit |= 0x02; + if (d2 & 0x08) + bit |= 0x01; + + data[byte] ^= (1 << bit); + + return 1; /* Corrected the error */ + } + + if ((yaffs_CountBits(d0) + + yaffs_CountBits(d1) + + yaffs_CountBits(d2)) == 1) { + /* Reccoverable error in ecc */ + + read_ecc[0] = test_ecc[0]; + read_ecc[1] = test_ecc[1]; + read_ecc[2] = test_ecc[2]; + + return 1; /* Corrected the error */ + } + + /* Unrecoverable error */ + + return -1; + +} + + +/* + * ECCxxxOther does ECC calcs on arbitrary n bytes of data + */ +void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, + yaffs_ECCOther * eccOther) +{ + unsigned int i; + + unsigned char col_parity = 0; + unsigned line_parity = 0; + unsigned line_parity_prime = 0; + unsigned char b; + + for (i = 0; i < nBytes; i++) { + b = column_parity_table[*data++]; + col_parity ^= b; + + if (b & 0x01) { + /* odd number of bits in the byte */ + line_parity ^= i; + line_parity_prime ^= ~i; + } + + } + + eccOther->colParity = (col_parity >> 2) & 0x3f; + eccOther->lineParity = line_parity; + eccOther->lineParityPrime = line_parity_prime; +} + +int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, + yaffs_ECCOther * read_ecc, + const yaffs_ECCOther * test_ecc) +{ + unsigned char cDelta; /* column parity delta */ + unsigned lDelta; /* line parity delta */ + unsigned lDeltaPrime; /* line parity delta */ + unsigned bit; + + cDelta = read_ecc->colParity ^ test_ecc->colParity; + lDelta = read_ecc->lineParity ^ test_ecc->lineParity; + lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime; + + if ((cDelta | lDelta | lDeltaPrime) == 0) + return 0; /* no error */ + + if (lDelta == ~lDeltaPrime && + (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15)) + { + /* Single bit (recoverable) error in data */ + + bit = 0; + + if (cDelta & 0x20) + bit |= 0x04; + if (cDelta & 0x08) + bit |= 0x02; + if (cDelta & 0x02) + bit |= 0x01; + + if(lDelta >= nBytes) + return -1; + + data[lDelta] ^= (1 << bit); + + return 1; /* corrected */ + } + + if ((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) + + yaffs_CountBits(cDelta)) == 1) { + /* Reccoverable error in ecc */ + + *read_ecc = *test_ecc; + return 1; /* corrected */ + } + + /* Unrecoverable error */ + + return -1; + +} + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_ecc.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_ecc.h new file mode 100644 index 0000000000..79bc3d1171 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_ecc.h @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + /* + * This code implements the ECC algorithm used in SmartMedia. + * + * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. + * The two unused bit are set to 1. + * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC + * blocks are used on a 512-byte NAND page. + * + */ + +#ifndef __YAFFS_ECC_H__ +#define __YAFFS_ECC_H__ + +typedef struct { + unsigned char colParity; + unsigned lineParity; + unsigned lineParityPrime; +} yaffs_ECCOther; + +void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc); +int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, + const unsigned char *test_ecc); + +void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes, + yaffs_ECCOther * ecc); +int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, + yaffs_ECCOther * read_ecc, + const yaffs_ECCOther * test_ecc); +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_fs.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_fs.c new file mode 100644 index 0000000000..67001b10e6 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_fs.c @@ -0,0 +1,2299 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * Acknowledgements: + * Luc van OostenRyck for numerous patches. + * Nick Bane for numerous patches. + * Nick Bane for 2.5/2.6 integration. + * Andras Toth for mknod rdev issue. + * Michael Fischer for finding the problem with inode inconsistency. + * Some code bodily lifted from JFFS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * + * This is the file system front-end to YAFFS that hooks it up to + * the VFS. + * + * Special notes: + * >> 2.4: sb->u.generic_sbp points to the yaffs_Device associated with + * this superblock + * >> 2.6: sb->s_fs_info points to the yaffs_Device associated with this + * superblock + * >> inode->u.generic_ip points to the associated yaffs_Object. + */ + +const char *yaffs_fs_c_version = + "$Id: yaffs_fs.c,v 1.63 2007-09-19 20:35:40 imcd Exp $"; +extern const char *yaffs_guts_c_version; + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + +#include /* Added NCB 15-8-2003 */ +#include +#define UnlockPage(p) unlock_page(p) +#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) + +/* FIXME: use sb->s_id instead ? */ +#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) + +#else + +#include +#define BDEVNAME_SIZE 0 +#define yaffs_devname(sb, buf) kdevname(sb->s_dev) + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ +#define __user +#endif + +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +#define WRITE_SIZE_STR "writesize" +#define WRITE_SIZE(mtd) (mtd)->writesize +#else +#define WRITE_SIZE_STR "oobblock" +#define WRITE_SIZE(mtd) (mtd)->oobblock +#endif + +#include + +#include "yportenv.h" +#include "yaffs_guts.h" + +#include +#include "yaffs_mtdif.h" +#include "yaffs_mtdif1.h" +#include "yaffs_mtdif2.h" + +unsigned int yaffs_traceMask = YAFFS_TRACE_BAD_BLOCKS; +unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; + +/* Module Parameters */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +module_param(yaffs_traceMask,uint,0644); +module_param(yaffs_wr_attempts,uint,0644); +#else +MODULE_PARM(yaffs_traceMask,"i"); +MODULE_PARM(yaffs_wr_attempts,"i"); +#endif + +/*#define T(x) printk x */ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) +#define yaffs_InodeToObjectLV(iptr) (iptr)->i_private +#else +#define yaffs_InodeToObjectLV(iptr) (iptr)->u.generic_ip +#endif + +#define yaffs_InodeToObject(iptr) ((yaffs_Object *)(yaffs_InodeToObjectLV(iptr))) +#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode) + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->s_fs_info) +#else +#define yaffs_SuperToDevice(sb) ((yaffs_Device *)sb->u.generic_sbp) +#endif + +static void yaffs_put_super(struct super_block *sb); + +static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, + loff_t * pos); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_file_flush(struct file *file, fl_owner_t id); +#else +static int yaffs_file_flush(struct file *file); +#endif + +static int yaffs_sync_object(struct file *file, struct dentry *dentry, + int datasync); + +static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *n); +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *n); +#else +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode); +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry); +#endif +static int yaffs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry); +static int yaffs_unlink(struct inode *dir, struct dentry *dentry); +static int yaffs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname); +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev); +#else +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + int dev); +#endif +static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +static int yaffs_setattr(struct dentry *dentry, struct iattr *attr); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_sync_fs(struct super_block *sb, int wait); +static void yaffs_write_super(struct super_block *sb); +#else +static int yaffs_sync_fs(struct super_block *sb); +static int yaffs_write_super(struct super_block *sb); +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf); +#else +static int yaffs_statfs(struct super_block *sb, struct statfs *buf); +#endif +static void yaffs_read_inode(struct inode *inode); + +static void yaffs_put_inode(struct inode *inode); +static void yaffs_delete_inode(struct inode *); +static void yaffs_clear_inode(struct inode *); + +static int yaffs_readpage(struct file *file, struct page *page); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_writepage(struct page *page, struct writeback_control *wbc); +#else +static int yaffs_writepage(struct page *page); +#endif +static int yaffs_prepare_write(struct file *f, struct page *pg, + unsigned offset, unsigned to); +static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, + unsigned to); + +static int yaffs_readlink(struct dentry *dentry, char __user * buffer, + int buflen); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); +#else +static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd); +#endif + +static struct address_space_operations yaffs_file_address_operations = { + .readpage = yaffs_readpage, + .writepage = yaffs_writepage, + .prepare_write = yaffs_prepare_write, + .commit_write = yaffs_commit_write, +}; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)) +static struct file_operations yaffs_file_operations = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, +}; + +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) + +static struct file_operations yaffs_file_operations = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, + .sendfile = generic_file_sendfile, +}; + +#else + +static struct file_operations yaffs_file_operations = { + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .flush = yaffs_file_flush, + .fsync = yaffs_sync_object, +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + .sendfile = generic_file_sendfile, +#endif +}; +#endif + +static struct inode_operations yaffs_file_inode_operations = { + .setattr = yaffs_setattr, +}; + +static struct inode_operations yaffs_symlink_inode_operations = { + .readlink = yaffs_readlink, + .follow_link = yaffs_follow_link, + .setattr = yaffs_setattr, +}; + +static struct inode_operations yaffs_dir_inode_operations = { + .create = yaffs_create, + .lookup = yaffs_lookup, + .link = yaffs_link, + .unlink = yaffs_unlink, + .symlink = yaffs_symlink, + .mkdir = yaffs_mkdir, + .rmdir = yaffs_unlink, + .mknod = yaffs_mknod, + .rename = yaffs_rename, + .setattr = yaffs_setattr, +}; + +static struct file_operations yaffs_dir_operations = { + .read = generic_read_dir, + .readdir = yaffs_readdir, + .fsync = yaffs_sync_object, +}; + +static struct super_operations yaffs_super_ops = { + .statfs = yaffs_statfs, + .read_inode = yaffs_read_inode, + .put_inode = yaffs_put_inode, + .put_super = yaffs_put_super, + .delete_inode = yaffs_delete_inode, + .clear_inode = yaffs_clear_inode, + .sync_fs = yaffs_sync_fs, + .write_super = yaffs_write_super, +}; + +static void yaffs_GrossLock(yaffs_Device * dev) +{ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs locking\n")); + + down(&dev->grossLock); +} + +static void yaffs_GrossUnlock(yaffs_Device * dev) +{ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs unlocking\n")); + up(&dev->grossLock); + +} + +static int yaffs_readlink(struct dentry *dentry, char __user * buffer, + int buflen) +{ + unsigned char *alias; + int ret; + + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + + yaffs_GrossLock(dev); + + alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); + + yaffs_GrossUnlock(dev); + + if (!alias) + return -ENOMEM; + + ret = vfs_readlink(dentry, buffer, buflen, alias); + kfree(alias); + return ret; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) +static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +#else +static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) +#endif +{ + unsigned char *alias; + int ret; + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + + yaffs_GrossLock(dev); + + alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry)); + + yaffs_GrossUnlock(dev); + + if (!alias) + { + ret = -ENOMEM; + goto out; + } + + ret = vfs_follow_link(nd, alias); + kfree(alias); +out: +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) + return ERR_PTR (ret); +#else + return ret; +#endif +} + +struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, + yaffs_Object * obj); + +/* + * Lookup is used to find objects in the fs + */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *n) +#else +static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) +#endif +{ + yaffs_Object *obj; + struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ + + yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev; + + yaffs_GrossLock(dev); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_lookup for %d:%s\n", + yaffs_InodeToObject(dir)->objectId, dentry->d_name.name)); + + obj = + yaffs_FindObjectByName(yaffs_InodeToObject(dir), + dentry->d_name.name); + + obj = yaffs_GetEquivalentObject(obj); /* in case it was a hardlink */ + + /* Can't hold gross lock when calling yaffs_get_inode() */ + yaffs_GrossUnlock(dev); + + if (obj) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_lookup found %d\n", obj->objectId)); + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + + if (inode) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_loookup dentry \n")); +/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to + * d_add even if NULL inode */ +#if 0 + /*dget(dentry); // try to solve directory bug */ + d_add(dentry, inode); + + /* return dentry; */ + return NULL; +#endif + } + + } else { + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_lookup not found\n")); + + } + +/* added NCB for 2.5/6 compatability - forces add even if inode is + * NULL which creates dentry hash */ + d_add(dentry, inode); + + return NULL; + /* return (ERR_PTR(-EIO)); */ + +} + +/* For now put inode is just for debugging + * Put inode is called when the inode **structure** is put. + */ +static void yaffs_put_inode(struct inode *inode) +{ + T(YAFFS_TRACE_OS, + ("yaffs_put_inode: ino %d, count %d\n", (int)inode->i_ino, + atomic_read(&inode->i_count))); + +} + +/* clear is called to tell the fs to release any per-inode data it holds */ +static void yaffs_clear_inode(struct inode *inode) +{ + yaffs_Object *obj; + yaffs_Device *dev; + + obj = yaffs_InodeToObject(inode); + + T(YAFFS_TRACE_OS, + ("yaffs_clear_inode: ino %d, count %d %s\n", (int)inode->i_ino, + atomic_read(&inode->i_count), + obj ? "object exists" : "null object")); + + if (obj) { + dev = obj->myDev; + yaffs_GrossLock(dev); + + /* Clear the association between the inode and + * the yaffs_Object. + */ + obj->myInode = NULL; + yaffs_InodeToObjectLV(inode) = NULL; + + /* If the object freeing was deferred, then the real + * free happens now. + * This should fix the inode inconsistency problem. + */ + + yaffs_HandleDeferedFree(obj); + + yaffs_GrossUnlock(dev); + } + +} + +/* delete is called when the link count is zero and the inode + * is put (ie. nobody wants to know about it anymore, time to + * delete the file). + * NB Must call clear_inode() + */ +static void yaffs_delete_inode(struct inode *inode) +{ + yaffs_Object *obj = yaffs_InodeToObject(inode); + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, + ("yaffs_delete_inode: ino %d, count %d %s\n", (int)inode->i_ino, + atomic_read(&inode->i_count), + obj ? "object exists" : "null object")); + + if (obj) { + dev = obj->myDev; + yaffs_GrossLock(dev); + yaffs_DeleteFile(obj); + yaffs_GrossUnlock(dev); + } +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) + truncate_inode_pages (&inode->i_data, 0); +#endif + clear_inode(inode); +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_file_flush(struct file *file, fl_owner_t id) +#else +static int yaffs_file_flush(struct file *file) +#endif +{ + yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry); + + yaffs_Device *dev = obj->myDev; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_file_flush object %d (%s)\n", obj->objectId, + obj->dirty ? "dirty" : "clean")); + + yaffs_GrossLock(dev); + + yaffs_FlushFile(obj, 1); + + yaffs_GrossUnlock(dev); + + return 0; +} + +static int yaffs_readpage_nolock(struct file *f, struct page *pg) +{ + /* Lifted from jffs2 */ + + yaffs_Object *obj; + unsigned char *pg_buf; + int ret; + + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_readpage at %08x, size %08x\n", + (unsigned)(pg->index << PAGE_CACHE_SHIFT), + (unsigned)PAGE_CACHE_SIZE)); + + obj = yaffs_DentryToObject(f->f_dentry); + + dev = obj->myDev; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + BUG_ON(!PageLocked(pg)); +#else + if (!PageLocked(pg)) + PAGE_BUG(pg); +#endif + + pg_buf = kmap(pg); + /* FIXME: Can kmap fail? */ + + yaffs_GrossLock(dev); + + ret = + yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, + PAGE_CACHE_SIZE); + + yaffs_GrossUnlock(dev); + + if (ret >= 0) + ret = 0; + + if (ret) { + ClearPageUptodate(pg); + SetPageError(pg); + } else { + SetPageUptodate(pg); + ClearPageError(pg); + } + + flush_dcache_page(pg); + kunmap(pg); + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_readpage done\n")); + return ret; +} + +static int yaffs_readpage_unlock(struct file *f, struct page *pg) +{ + int ret = yaffs_readpage_nolock(f, pg); + UnlockPage(pg); + return ret; +} + +static int yaffs_readpage(struct file *f, struct page *pg) +{ + return yaffs_readpage_unlock(f, pg); +} + +/* writepage inspired by/stolen from smbfs */ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_writepage(struct page *page, struct writeback_control *wbc) +#else +static int yaffs_writepage(struct page *page) +#endif +{ + struct address_space *mapping = page->mapping; + loff_t offset = (loff_t) page->index << PAGE_CACHE_SHIFT; + struct inode *inode; + unsigned long end_index; + char *buffer; + yaffs_Object *obj; + int nWritten = 0; + unsigned nBytes; + + if (!mapping) + BUG(); + inode = mapping->host; + if (!inode) + BUG(); + + if (offset > inode->i_size) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_writepage at %08x, inode size = %08x!!!\n", + (unsigned)(page->index << PAGE_CACHE_SHIFT), + (unsigned)inode->i_size)); + T(YAFFS_TRACE_OS, + (KERN_DEBUG " -> don't care!!\n")); + unlock_page(page); + return 0; + } + + end_index = inode->i_size >> PAGE_CACHE_SHIFT; + + /* easy case */ + if (page->index < end_index) { + nBytes = PAGE_CACHE_SIZE; + } else { + nBytes = inode->i_size & (PAGE_CACHE_SIZE - 1); + } + + get_page(page); + + buffer = kmap(page); + + obj = yaffs_InodeToObject(inode); + yaffs_GrossLock(obj->myDev); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_writepage at %08x, size %08x\n", + (unsigned)(page->index << PAGE_CACHE_SHIFT), nBytes)); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "writepag0: obj = %05x, ino = %05x\n", + (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); + + nWritten = + yaffs_WriteDataToFile(obj, buffer, page->index << PAGE_CACHE_SHIFT, + nBytes, 0); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "writepag1: obj = %05x, ino = %05x\n", + (int)obj->variant.fileVariant.fileSize, (int)inode->i_size)); + + yaffs_GrossUnlock(obj->myDev); + + kunmap(page); + SetPageUptodate(page); + UnlockPage(page); + put_page(page); + + return (nWritten == nBytes) ? 0 : -ENOSPC; +} + +static int yaffs_prepare_write(struct file *f, struct page *pg, + unsigned offset, unsigned to) +{ + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_prepair_write\n")); + if (!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE)) + return yaffs_readpage_nolock(f, pg); + + return 0; + +} + +static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, + unsigned to) +{ + + void *addr = page_address(pg) + offset; + loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; + int nBytes = to - offset; + int nWritten; + + unsigned spos = pos; + unsigned saddr = (unsigned)addr; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_commit_write addr %x pos %x nBytes %d\n", saddr, + spos, nBytes)); + + nWritten = yaffs_file_write(f, addr, nBytes, &pos); + + if (nWritten != nBytes) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_commit_write not same size nWritten %d nBytes %d\n", + nWritten, nBytes)); + SetPageError(pg); + ClearPageUptodate(pg); + } else { + SetPageUptodate(pg); + } + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_commit_write returning %d\n", + nWritten == nBytes ? 0 : nWritten)); + + return nWritten == nBytes ? 0 : nWritten; + +} + +static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object * obj) +{ + if (inode && obj) { + + + /* Check mode against the variant type and attempt to repair if broken. */ + __u32 mode = obj->yst_mode; + switch( obj->variantType ){ + case YAFFS_OBJECT_TYPE_FILE : + if( ! S_ISREG(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFREG; + } + + break; + case YAFFS_OBJECT_TYPE_SYMLINK : + if( ! S_ISLNK(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFLNK; + } + + break; + case YAFFS_OBJECT_TYPE_DIRECTORY : + if( ! S_ISDIR(mode) ){ + obj->yst_mode &= ~S_IFMT; + obj->yst_mode |= S_IFDIR; + } + + break; + case YAFFS_OBJECT_TYPE_UNKNOWN : + case YAFFS_OBJECT_TYPE_HARDLINK : + case YAFFS_OBJECT_TYPE_SPECIAL : + default: + /* TODO? */ + break; + } + + inode->i_ino = obj->objectId; + inode->i_mode = obj->yst_mode; + inode->i_uid = obj->yst_uid; + inode->i_gid = obj->yst_gid; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + inode->i_blksize = inode->i_sb->s_blocksize; +#endif +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + + inode->i_rdev = old_decode_dev(obj->yst_rdev); + inode->i_atime.tv_sec = (time_t) (obj->yst_atime); + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; + inode->i_ctime.tv_nsec = 0; +#else + inode->i_rdev = obj->yst_rdev; + inode->i_atime = obj->yst_atime; + inode->i_mtime = obj->yst_mtime; + inode->i_ctime = obj->yst_ctime; +#endif + inode->i_size = yaffs_GetObjectFileLength(obj); + inode->i_blocks = (inode->i_size + 511) >> 9; + + inode->i_nlink = yaffs_GetObjectLinkCount(obj); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_FillInode mode %x uid %d gid %d size %d count %d\n", + inode->i_mode, inode->i_uid, inode->i_gid, + (int)inode->i_size, atomic_read(&inode->i_count))); + + switch (obj->yst_mode & S_IFMT) { + default: /* fifo, device or socket */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + init_special_inode(inode, obj->yst_mode, + old_decode_dev(obj->yst_rdev)); +#else + init_special_inode(inode, obj->yst_mode, + (dev_t) (obj->yst_rdev)); +#endif + break; + case S_IFREG: /* file */ + inode->i_op = &yaffs_file_inode_operations; + inode->i_fop = &yaffs_file_operations; + inode->i_mapping->a_ops = + &yaffs_file_address_operations; + break; + case S_IFDIR: /* directory */ + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + break; + case S_IFLNK: /* symlink */ + inode->i_op = &yaffs_symlink_inode_operations; + break; + } + + yaffs_InodeToObjectLV(inode) = obj; + + obj->myInode = inode; + + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_FileInode invalid parameters\n")); + } + +} + +struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, + yaffs_Object * obj) +{ + struct inode *inode; + + if (!sb) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for NULL super_block!!\n")); + return NULL; + + } + + if (!obj) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for NULL object!!\n")); + return NULL; + + } + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_get_inode for object %d\n", obj->objectId)); + + inode = iget(sb, obj->objectId); + + /* NB Side effect: iget calls back to yaffs_read_inode(). */ + /* iget also increments the inode's i_count */ + /* NB You can't be holding grossLock or deadlock will happen! */ + + return inode; +} + +static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, + loff_t * pos) +{ + yaffs_Object *obj; + int nWritten, ipos; + struct inode *inode; + yaffs_Device *dev; + + obj = yaffs_DentryToObject(f->f_dentry); + + dev = obj->myDev; + + yaffs_GrossLock(dev); + + inode = f->f_dentry->d_inode; + + if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) { + ipos = inode->i_size; + } else { + ipos = *pos; + } + + if (!obj) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_file_write: hey obj is null!\n")); + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_file_write about to write writing %d bytes" + "to object %d at %d\n", + n, obj->objectId, ipos)); + } + + nWritten = yaffs_WriteDataToFile(obj, buf, ipos, n, 0); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_file_write writing %d bytes, %d written at %d\n", + n, nWritten, ipos)); + if (nWritten > 0) { + ipos += nWritten; + *pos = ipos; + if (ipos > inode->i_size) { + inode->i_size = ipos; + inode->i_blocks = (ipos + 511) >> 9; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG + "yaffs_file_write size updated to %d bytes, " + "%d blocks\n", + ipos, (int)(inode->i_blocks))); + } + + } + yaffs_GrossUnlock(dev); + return nWritten == 0 ? -ENOSPC : nWritten; +} + +static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) +{ + yaffs_Object *obj; + yaffs_Device *dev; + struct inode *inode = f->f_dentry->d_inode; + unsigned long offset, curoffs; + struct list_head *i; + yaffs_Object *l; + + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + obj = yaffs_DentryToObject(f->f_dentry); + dev = obj->myDev; + + yaffs_GrossLock(dev); + + offset = f->f_pos; + + T(YAFFS_TRACE_OS, ("yaffs_readdir: starting at %d\n", (int)offset)); + + if (offset == 0) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_readdir: entry . ino %d \n", + (int)inode->i_ino)); + if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) + < 0) { + goto out; + } + offset++; + f->f_pos++; + } + if (offset == 1) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_readdir: entry .. ino %d \n", + (int)f->f_dentry->d_parent->d_inode->i_ino)); + if (filldir + (dirent, "..", 2, offset, + f->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) { + goto out; + } + offset++; + f->f_pos++; + } + + curoffs = 1; + + /* If the directory has changed since the open or last call to + readdir, rewind to after the 2 canned entries. */ + + if (f->f_version != inode->i_version) { + offset = 2; + f->f_pos = offset; + f->f_version = inode->i_version; + } + + list_for_each(i, &obj->variant.directoryVariant.children) { + curoffs++; + if (curoffs >= offset) { + l = list_entry(i, yaffs_Object, siblings); + + yaffs_GetObjectName(l, name, + YAFFS_MAX_NAME_LENGTH + 1); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_readdir: %s inode %d\n", name, + yaffs_GetObjectInode(l))); + + if (filldir(dirent, + name, + strlen(name), + offset, + yaffs_GetObjectInode(l), + yaffs_GetObjectType(l)) + < 0) { + goto up_and_out; + } + + offset++; + f->f_pos++; + } + } + + up_and_out: + out: + + yaffs_GrossUnlock(dev); + + return 0; +} + +/* + * File creation. Allocate an inode, and we're done.. + */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t rdev) +#else +static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, + int rdev) +#endif +{ + struct inode *inode; + + yaffs_Object *obj = NULL; + yaffs_Device *dev; + + yaffs_Object *parent = yaffs_InodeToObject(dir); + + int error = -ENOSPC; + uid_t uid = current->fsuid; + gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + + if((dir->i_mode & S_ISGID) && S_ISDIR(mode)) + mode |= S_ISGID; + + if (parent) { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod: parent object %d type %d\n", + parent->objectId, parent->variantType)); + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod: could not get parent object\n")); + return -EPERM; + } + + T(YAFFS_TRACE_OS, ("yaffs_mknod: making oject for %s, " + "mode %x dev %x\n", + dentry->d_name.name, mode, rdev)); + + dev = parent->myDev; + + yaffs_GrossLock(dev); + + switch (mode & S_IFMT) { + default: + /* Special (socket, fifo, device...) */ + T(YAFFS_TRACE_OS, (KERN_DEBUG + "yaffs_mknod: making special\n")); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + obj = + yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid, + gid, old_encode_dev(rdev)); +#else + obj = + yaffs_MknodSpecial(parent, dentry->d_name.name, mode, uid, + gid, rdev); +#endif + break; + case S_IFREG: /* file */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n")); + obj = + yaffs_MknodFile(parent, dentry->d_name.name, mode, uid, + gid); + break; + case S_IFDIR: /* directory */ + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod: making directory\n")); + obj = + yaffs_MknodDirectory(parent, dentry->d_name.name, mode, + uid, gid); + break; + case S_IFLNK: /* symlink */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mknod: making file\n")); + obj = NULL; /* Do we ever get here? */ + break; + } + + /* Can not call yaffs_get_inode() with gross lock held */ + yaffs_GrossUnlock(dev); + + if (obj) { + inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); + d_instantiate(dentry, inode); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod created object %d count = %d\n", + obj->objectId, atomic_read(&inode->i_count))); + error = 0; + } else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_mknod failed making object\n")); + error = -ENOMEM; + } + + return error; +} + +static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int retVal; + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_mkdir\n")); + retVal = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); +#if 0 + /* attempt to fix dir bug - didn't work */ + if (!retVal) { + dget(dentry); + } +#endif + return retVal; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *n) +#else +static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_create\n")); + return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); +} + +static int yaffs_unlink(struct inode *dir, struct dentry *dentry) +{ + int retVal; + + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_unlink %d:%s\n", (int)(dir->i_ino), + dentry->d_name.name)); + + dev = yaffs_InodeToObject(dir)->myDev; + + yaffs_GrossLock(dev); + + retVal = yaffs_Unlink(yaffs_InodeToObject(dir), dentry->d_name.name); + + if (retVal == YAFFS_OK) { + dentry->d_inode->i_nlink--; + dir->i_version++; + yaffs_GrossUnlock(dev); + mark_inode_dirty(dentry->d_inode); + return 0; + } + yaffs_GrossUnlock(dev); + return -ENOTEMPTY; +} + +/* + * Create a link... + */ +static int yaffs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + yaffs_Object *obj = NULL; + yaffs_Object *link = NULL; + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_link\n")); + + obj = yaffs_InodeToObject(inode); + dev = obj->myDev; + + yaffs_GrossLock(dev); + + if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ + { + link = + yaffs_Link(yaffs_InodeToObject(dir), dentry->d_name.name, + obj); + } + + if (link) { + old_dentry->d_inode->i_nlink = yaffs_GetObjectLinkCount(obj); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_link link count %d i_count %d\n", + old_dentry->d_inode->i_nlink, + atomic_read(&old_dentry->d_inode->i_count))); + + } + + yaffs_GrossUnlock(dev); + + if (link) { + + return 0; + } + + return -EPERM; +} + +static int yaffs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + yaffs_Object *obj; + yaffs_Device *dev; + uid_t uid = current->fsuid; + gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_symlink\n")); + + dev = yaffs_InodeToObject(dir)->myDev; + yaffs_GrossLock(dev); + obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, + S_IFLNK | S_IRWXUGO, uid, gid, symname); + yaffs_GrossUnlock(dev); + + if (obj) { + + struct inode *inode; + + inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); + d_instantiate(dentry, inode); + T(YAFFS_TRACE_OS, (KERN_DEBUG "symlink created OK\n")); + return 0; + } else { + T(YAFFS_TRACE_OS, (KERN_DEBUG "symlink not created\n")); + + } + + return -ENOMEM; +} + +static int yaffs_sync_object(struct file *file, struct dentry *dentry, + int datasync) +{ + + yaffs_Object *obj; + yaffs_Device *dev; + + obj = yaffs_DentryToObject(dentry); + + dev = obj->myDev; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_object\n")); + yaffs_GrossLock(dev); + yaffs_FlushFile(obj, 1); + yaffs_GrossUnlock(dev); + return 0; +} + +/* + * The VFS layer already does all the dentry stuff for rename. + * + * NB: POSIX says you can rename an object over an old object of the same name + */ +static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + yaffs_Device *dev; + int retVal = YAFFS_FAIL; + yaffs_Object *target; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_rename\n")); + dev = yaffs_InodeToObject(old_dir)->myDev; + + yaffs_GrossLock(dev); + + /* Check if the target is an existing directory that is not empty. */ + target = + yaffs_FindObjectByName(yaffs_InodeToObject(new_dir), + new_dentry->d_name.name); + + + + if (target && + target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&target->variant.directoryVariant.children)) { + + T(YAFFS_TRACE_OS, (KERN_DEBUG "target is non-empty dir\n")); + + retVal = YAFFS_FAIL; + } else { + + /* Now does unlinking internally using shadowing mechanism */ + T(YAFFS_TRACE_OS, (KERN_DEBUG "calling yaffs_RenameObject\n")); + + retVal = + yaffs_RenameObject(yaffs_InodeToObject(old_dir), + old_dentry->d_name.name, + yaffs_InodeToObject(new_dir), + new_dentry->d_name.name); + + } + yaffs_GrossUnlock(dev); + + if (retVal == YAFFS_OK) { + if(target) { + new_dentry->d_inode->i_nlink--; + mark_inode_dirty(new_dentry->d_inode); + } + + return 0; + } else { + return -ENOTEMPTY; + } + +} + +static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + yaffs_Device *dev; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_setattr of object %d\n", + yaffs_InodeToObject(inode)->objectId)); + + if ((error = inode_change_ok(inode, attr)) == 0) { + + dev = yaffs_InodeToObject(inode)->myDev; + yaffs_GrossLock(dev); + if (yaffs_SetAttributes(yaffs_InodeToObject(inode), attr) == + YAFFS_OK) { + error = 0; + } else { + error = -EPERM; + } + yaffs_GrossUnlock(dev); + if (!error) + error = inode_setattr(inode, attr); + } + return error; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev; + struct super_block *sb = dentry->d_sb; +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); +#else +static int yaffs_statfs(struct super_block *sb, struct statfs *buf) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); +#endif + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_statfs\n")); + + yaffs_GrossLock(dev); + + buf->f_type = YAFFS_MAGIC; + buf->f_bsize = sb->s_blocksize; + buf->f_namelen = 255; + if (sb->s_blocksize > dev->nDataBytesPerChunk) { + + buf->f_blocks = + (dev->endBlock - dev->startBlock + + 1) * dev->nChunksPerBlock / (sb->s_blocksize / + dev->nDataBytesPerChunk); + buf->f_bfree = + yaffs_GetNumberOfFreeChunks(dev) / (sb->s_blocksize / + dev->nDataBytesPerChunk); + } else { + + buf->f_blocks = + (dev->endBlock - dev->startBlock + + 1) * dev->nChunksPerBlock * (dev->nDataBytesPerChunk / + sb->s_blocksize); + buf->f_bfree = + yaffs_GetNumberOfFreeChunks(dev) * (dev->nDataBytesPerChunk / + sb->s_blocksize); + } + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_bavail = buf->f_bfree; + + yaffs_GrossUnlock(dev); + return 0; +} + + +/** +static int yaffs_do_sync_fs(struct super_block *sb) +{ + + yaffs_Device *dev = yaffs_SuperToDevice(sb); + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_do_sync_fs\n")); + + if(sb->s_dirt) { + yaffs_GrossLock(dev); + + if(dev) + yaffs_CheckpointSave(dev); + + yaffs_GrossUnlock(dev); + + sb->s_dirt = 0; + } + return 0; +} +**/ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static void yaffs_write_super(struct super_block *sb) +#else +static int yaffs_write_super(struct super_block *sb) +#endif +{ + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_write_super\n")); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) + return 0; /* yaffs_do_sync_fs(sb);*/ +#endif +} + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_sync_fs(struct super_block *sb, int wait) +#else +static int yaffs_sync_fs(struct super_block *sb) +#endif +{ + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_sync_fs\n")); + + return 0; /* yaffs_do_sync_fs(sb);*/ + +} + + +static void yaffs_read_inode(struct inode *inode) +{ + /* NB This is called as a side effect of other functions, but + * we had to release the lock to prevent deadlocks, so + * need to lock again. + */ + + yaffs_Object *obj; + yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb); + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_read_inode for %d\n", (int)inode->i_ino)); + + yaffs_GrossLock(dev); + + obj = yaffs_FindObjectByNumber(dev, inode->i_ino); + + yaffs_FillInodeFromObject(inode, obj); + + yaffs_GrossUnlock(dev); +} + +static LIST_HEAD(yaffs_dev_list); + +#if 0 // not used +static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); + + if( *flags & MS_RDONLY ) { + struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; + + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_remount_fs: %s: RO\n", dev->name )); + + yaffs_GrossLock(dev); + + yaffs_FlushEntireDeviceCache(dev); + + yaffs_CheckpointSave(dev); + + if (mtd->sync) + mtd->sync(mtd); + + yaffs_GrossUnlock(dev); + } + else { + T(YAFFS_TRACE_OS, + (KERN_DEBUG "yaffs_remount_fs: %s: RW\n", dev->name )); + } + + return 0; +} +#endif + +static void yaffs_put_super(struct super_block *sb) +{ + yaffs_Device *dev = yaffs_SuperToDevice(sb); + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_put_super\n")); + + yaffs_GrossLock(dev); + + yaffs_FlushEntireDeviceCache(dev); + + yaffs_CheckpointSave(dev); + + if (dev->putSuperFunc) { + dev->putSuperFunc(sb); + } + + yaffs_Deinitialise(dev); + + yaffs_GrossUnlock(dev); + + /* we assume this is protected by lock_kernel() in mount/umount */ + list_del(&dev->devList); + + if(dev->spareBuffer){ + YFREE(dev->spareBuffer); + dev->spareBuffer = NULL; + } + + kfree(dev); +} + + +static void yaffs_MTDPutSuper(struct super_block *sb) +{ + + struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice; + + if (mtd->sync) { + mtd->sync(mtd); + } + + put_mtd_device(mtd); +} + + +static void yaffs_MarkSuperBlockDirty(void *vsb) +{ + struct super_block *sb = (struct super_block *)vsb; + + T(YAFFS_TRACE_OS, (KERN_DEBUG "yaffs_MarkSuperBlockDirty() sb = %p\n",sb)); +// if(sb) +// sb->s_dirt = 1; +} + +typedef struct { + int inband_tags; + int skip_checkpoint_read; + int skip_checkpoint_write; + int no_cache; +} yaffs_options; + +#define MAX_OPT_LEN 20 +static int yaffs_parse_options(yaffs_options *options, const char *options_str) +{ + char cur_opt[MAX_OPT_LEN+1]; + int p; + int error = 0; + + /* Parse through the options which is a comma seperated list */ + + while(options_str && *options_str && !error){ + memset(cur_opt,0,MAX_OPT_LEN+1); + p = 0; + + while(*options_str && *options_str != ','){ + if(p < MAX_OPT_LEN){ + cur_opt[p] = *options_str; + p++; + } + options_str++; + } + + if(!strcmp(cur_opt,"inband-tags")) + options->inband_tags = 1; + else if(!strcmp(cur_opt,"no-cache")) + options->no_cache = 1; + else if(!strcmp(cur_opt,"no-checkpoint-read")) + options->skip_checkpoint_read = 1; + else if(!strcmp(cur_opt,"no-checkpoint-write")) + options->skip_checkpoint_write = 1; + else if(!strcmp(cur_opt,"no-checkpoint")){ + options->skip_checkpoint_read = 1; + options->skip_checkpoint_write = 1; + } else { + printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n",cur_opt); + error = 1; + } + + } + + return error; +} + +static struct super_block *yaffs_internal_read_super(int yaffsVersion, + struct super_block *sb, + void *data, int silent) +{ + int nBlocks; + struct inode *inode = NULL; + struct dentry *root; + yaffs_Device *dev = 0; + char devname_buf[BDEVNAME_SIZE + 1]; + struct mtd_info *mtd; + int err; + char *data_str = (char *)data; + + yaffs_options options; + + sb->s_magic = YAFFS_MAGIC; + sb->s_op = &yaffs_super_ops; + + if (!sb) + printk(KERN_INFO "yaffs: sb is NULL\n"); + else if (!sb->s_dev) + printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); + else if (!yaffs_devname(sb, devname_buf)) + printk(KERN_INFO "yaffs: devname is NULL\n"); + else + printk(KERN_INFO "yaffs: dev is %d name is \"%s\"\n", + sb->s_dev, + yaffs_devname(sb, devname_buf)); + + if(!data_str) + data_str = ""; + + printk(KERN_INFO "yaffs: passed flags \"%s\"\n",data_str); + + memset(&options,0,sizeof(options)); + + if(yaffs_parse_options(&options,data_str)){ + /* Option parsing failed */ + return NULL; + } + + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + T(YAFFS_TRACE_OS, ("yaffs_read_super: Using yaffs%d\n", yaffsVersion)); + T(YAFFS_TRACE_OS, + ("yaffs_read_super: block size %d\n", (int)(sb->s_blocksize))); + +#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY + T(YAFFS_TRACE_OS, + ("yaffs: Write verification disabled. All guarantees " + "null and void\n")); +#endif + + T(YAFFS_TRACE_ALWAYS, ("yaffs: Attempting MTD mount on %u.%u, " + "\"%s\"\n", + MAJOR(sb->s_dev), MINOR(sb->s_dev), + yaffs_devname(sb, devname_buf))); + + /* Check it's an mtd device..... */ + if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { + return NULL; /* This isn't an mtd device */ + } + /* Get the device */ + mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); + if (!mtd) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device #%u doesn't appear to exist\n", + MINOR(sb->s_dev))); + return NULL; + } + /* Check it's NAND */ + if (mtd->type != MTD_NANDFLASH) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device is not NAND it's type %d\n", mtd->type)); + return NULL; + } + + T(YAFFS_TRACE_OS, (" erase %p\n", mtd->erase)); + T(YAFFS_TRACE_OS, (" read %p\n", mtd->read)); + T(YAFFS_TRACE_OS, (" write %p\n", mtd->write)); + T(YAFFS_TRACE_OS, (" readoob %p\n", mtd->read_oob)); + T(YAFFS_TRACE_OS, (" writeoob %p\n", mtd->write_oob)); + T(YAFFS_TRACE_OS, (" block_isbad %p\n", mtd->block_isbad)); + T(YAFFS_TRACE_OS, (" block_markbad %p\n", mtd->block_markbad)); + T(YAFFS_TRACE_OS, (" %s %d\n", WRITE_SIZE_STR, WRITE_SIZE(mtd))); + T(YAFFS_TRACE_OS, (" oobsize %d\n", mtd->oobsize)); + T(YAFFS_TRACE_OS, (" erasesize %d\n", mtd->erasesize)); + T(YAFFS_TRACE_OS, (" size %d\n", mtd->size)); + +#ifdef CONFIG_YAFFS_AUTO_YAFFS2 + + if (yaffsVersion == 1 && +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + mtd->writesize >= 2048) { +#else + mtd->oobblock >= 2048) { +#endif + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs2\n")); + yaffsVersion = 2; + } + + /* Added NCB 26/5/2006 for completeness */ + if (yaffsVersion == 2 && +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + mtd->writesize == 512) { +#else + mtd->oobblock == 512) { +#endif + T(YAFFS_TRACE_ALWAYS,("yaffs: auto selecting yaffs1\n")); + yaffsVersion = 1; + } + +#endif + + if (yaffsVersion == 2) { + /* Check for version 2 style functions */ + if (!mtd->erase || + !mtd->block_isbad || + !mtd->block_markbad || + !mtd->read || + !mtd->write || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + !mtd->read_oob || !mtd->write_oob) { +#else + !mtd->write_ecc || + !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { +#endif + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support required " + "functions\n"));; + return NULL; + } + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + if (mtd->writesize < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +#else + if (mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE || +#endif + mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not have the " + "right page sizes\n")); + return NULL; + } + } else { + /* Check for V1 style functions */ + if (!mtd->erase || + !mtd->read || + !mtd->write || +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + !mtd->read_oob || !mtd->write_oob) { +#else + !mtd->write_ecc || + !mtd->read_ecc || !mtd->read_oob || !mtd->write_oob) { +#endif + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support required " + "functions\n"));; + return NULL; + } + + if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || + mtd->oobsize != YAFFS_BYTES_PER_SPARE) { + T(YAFFS_TRACE_ALWAYS, + ("yaffs: MTD device does not support have the " + "right page sizes\n")); + return NULL; + } + } + + /* OK, so if we got here, we have an MTD that's NAND and looks + * like it has the right capabilities + * Set the yaffs_Device up for mtd + */ + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) + sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); +#else + sb->u.generic_sbp = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL); +#endif + if (!dev) { + /* Deep shit could not allocate device structure */ + T(YAFFS_TRACE_ALWAYS, + ("yaffs_read_super: Failed trying to allocate " + "yaffs_Device. \n")); + return NULL; + } + + memset(dev, 0, sizeof(yaffs_Device)); + dev->genericDevice = mtd; + dev->name = mtd->name; + + /* Set up the memory size parameters.... */ + + nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); + dev->startBlock = 0; + dev->endBlock = nBlocks - 1; + dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK; + dev->nDataBytesPerChunk = YAFFS_BYTES_PER_CHUNK; + dev->nReservedBlocks = 5; + dev->nShortOpCaches = (options.no_cache) ? 0 : 10; + + /* ... and the functions. */ + if (yaffsVersion == 2) { + dev->writeChunkWithTagsToNAND = + nandmtd2_WriteChunkWithTagsToNAND; + dev->readChunkWithTagsFromNAND = + nandmtd2_ReadChunkWithTagsFromNAND; + dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad; + dev->queryNANDBlock = nandmtd2_QueryNANDBlock; + dev->spareBuffer = YMALLOC(mtd->oobsize); + dev->isYaffs2 = 1; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + dev->nDataBytesPerChunk = mtd->writesize; + dev->nChunksPerBlock = mtd->erasesize / mtd->writesize; +#else + dev->nDataBytesPerChunk = mtd->oobblock; + dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock; +#endif + nBlocks = mtd->size / mtd->erasesize; + + dev->nCheckpointReservedBlocks = CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS; + dev->startBlock = 0; + dev->endBlock = nBlocks - 1; + } else { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + /* use the MTD interface in yaffs_mtdif1.c */ + dev->writeChunkWithTagsToNAND = + nandmtd1_WriteChunkWithTagsToNAND; + dev->readChunkWithTagsFromNAND = + nandmtd1_ReadChunkWithTagsFromNAND; + dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad; + dev->queryNANDBlock = nandmtd1_QueryNANDBlock; +#else + dev->writeChunkToNAND = nandmtd_WriteChunkToNAND; + dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND; +#endif + dev->isYaffs2 = 0; + } + /* ... and common functions */ + dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND; + dev->initialiseNAND = nandmtd_InitialiseNAND; + + dev->putSuperFunc = yaffs_MTDPutSuper; + + dev->superBlock = (void *)sb; + dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty; + + +#ifndef CONFIG_YAFFS_DOES_ECC + dev->useNANDECC = 1; +#endif + +#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES + dev->wideTnodesDisabled = 1; +#endif + + dev->skipCheckpointRead = options.skip_checkpoint_read; + dev->skipCheckpointWrite = options.skip_checkpoint_write; + + /* we assume this is protected by lock_kernel() in mount/umount */ + list_add_tail(&dev->devList, &yaffs_dev_list); + + init_MUTEX(&dev->grossLock); + + yaffs_GrossLock(dev); + + err = yaffs_GutsInitialise(dev); + + T(YAFFS_TRACE_OS, + ("yaffs_read_super: guts initialised %s\n", + (err == YAFFS_OK) ? "OK" : "FAILED")); + + /* Release lock before yaffs_get_inode() */ + yaffs_GrossUnlock(dev); + + /* Create root inode */ + if (err == YAFFS_OK) + inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, + yaffs_Root(dev)); + + if (!inode) + return NULL; + + inode->i_op = &yaffs_dir_inode_operations; + inode->i_fop = &yaffs_dir_operations; + + T(YAFFS_TRACE_OS, ("yaffs_read_super: got root inode\n")); + + root = d_alloc_root(inode); + + T(YAFFS_TRACE_OS, ("yaffs_read_super: d_alloc_root done\n")); + + if (!root) { + iput(inode); + return NULL; + } + sb->s_root = root; + + T(YAFFS_TRACE_OS, ("yaffs_read_super: done\n")); + return sb; +} + + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd, mnt); +} +#else +static struct super_block *yaffs_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs_internal_read_super_mtd); +} +#endif + +static struct file_system_type yaffs_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs", + .get_sb = yaffs_read_super, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#else +static struct super_block *yaffs_read_super(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(1, sb, data, silent); +} + +static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, + FS_REQUIRES_DEV); +#endif + + +#ifdef CONFIG_YAFFS_YAFFS2 + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, + int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static int yaffs2_read_super(struct file_system_type *fs, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd, mnt); +} +#else +static struct super_block *yaffs2_read_super(struct file_system_type *fs, + int flags, const char *dev_name, + void *data) +{ + + return get_sb_bdev(fs, flags, dev_name, data, + yaffs2_internal_read_super_mtd); +} +#endif + +static struct file_system_type yaffs2_fs_type = { + .owner = THIS_MODULE, + .name = "yaffs2", + .get_sb = yaffs2_read_super, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#else +static struct super_block *yaffs2_read_super(struct super_block *sb, + void *data, int silent) +{ + return yaffs_internal_read_super(2, sb, data, silent); +} + +static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, + FS_REQUIRES_DEV); +#endif + +#endif /* CONFIG_YAFFS_YAFFS2 */ + +static struct proc_dir_entry *my_proc_entry; + +static char *yaffs_dump_dev(char *buf, yaffs_Device * dev) +{ + buf += sprintf(buf, "startBlock......... %d\n", dev->startBlock); + buf += sprintf(buf, "endBlock........... %d\n", dev->endBlock); + buf += sprintf(buf, "nDataBytesPerChunk. %d\n", dev->nDataBytesPerChunk); + buf += sprintf(buf, "chunkGroupBits..... %d\n", dev->chunkGroupBits); + buf += sprintf(buf, "chunkGroupSize..... %d\n", dev->chunkGroupSize); + buf += sprintf(buf, "nErasedBlocks...... %d\n", dev->nErasedBlocks); + buf += sprintf(buf, "nReservedBlocks.... %d\n", dev->nReservedBlocks); + buf += sprintf(buf, "nCheckptResBlocks.. %d\n", dev->nCheckpointReservedBlocks); + buf += sprintf(buf, "blocksInCheckpoint. %d\n", dev->blocksInCheckpoint); + buf += sprintf(buf, "nTnodesCreated..... %d\n", dev->nTnodesCreated); + buf += sprintf(buf, "nFreeTnodes........ %d\n", dev->nFreeTnodes); + buf += sprintf(buf, "nObjectsCreated.... %d\n", dev->nObjectsCreated); + buf += sprintf(buf, "nFreeObjects....... %d\n", dev->nFreeObjects); + buf += sprintf(buf, "nFreeChunks........ %d\n", dev->nFreeChunks); + buf += sprintf(buf, "nPageWrites........ %d\n", dev->nPageWrites); + buf += sprintf(buf, "nPageReads......... %d\n", dev->nPageReads); + buf += sprintf(buf, "nBlockErasures..... %d\n", dev->nBlockErasures); + buf += sprintf(buf, "nGCCopies.......... %d\n", dev->nGCCopies); + buf += + sprintf(buf, "garbageCollections. %d\n", dev->garbageCollections); + buf += + sprintf(buf, "passiveGCs......... %d\n", + dev->passiveGarbageCollections); + buf += sprintf(buf, "nRetriedWrites..... %d\n", dev->nRetriedWrites); + buf += sprintf(buf, "nShortOpCaches..... %d\n", dev->nShortOpCaches); + buf += sprintf(buf, "nRetireBlocks...... %d\n", dev->nRetiredBlocks); + buf += sprintf(buf, "eccFixed........... %d\n", dev->eccFixed); + buf += sprintf(buf, "eccUnfixed......... %d\n", dev->eccUnfixed); + buf += sprintf(buf, "tagsEccFixed....... %d\n", dev->tagsEccFixed); + buf += sprintf(buf, "tagsEccUnfixed..... %d\n", dev->tagsEccUnfixed); + buf += sprintf(buf, "cacheHits.......... %d\n", dev->cacheHits); + buf += sprintf(buf, "nDeletedFiles...... %d\n", dev->nDeletedFiles); + buf += sprintf(buf, "nUnlinkedFiles..... %d\n", dev->nUnlinkedFiles); + buf += + sprintf(buf, "nBackgroudDeletions %d\n", dev->nBackgroundDeletions); + buf += sprintf(buf, "useNANDECC......... %d\n", dev->useNANDECC); + buf += sprintf(buf, "isYaffs2........... %d\n", dev->isYaffs2); + + return buf; +} + +static int yaffs_proc_read(char *page, + char **start, + off_t offset, int count, int *eof, void *data) +{ + struct list_head *item; + char *buf = page; + int step = offset; + int n = 0; + + /* Get proc_file_read() to step 'offset' by one on each sucessive call. + * We use 'offset' (*ppos) to indicate where we are in devList. + * This also assumes the user has posted a read buffer large + * enough to hold the complete output; but that's life in /proc. + */ + + *(int *)start = 1; + + /* Print header first */ + if (step == 0) { + buf += sprintf(buf, "YAFFS built:" __DATE__ " " __TIME__ + "\n%s\n%s\n", yaffs_fs_c_version, + yaffs_guts_c_version); + } + + /* hold lock_kernel while traversing yaffs_dev_list */ + lock_kernel(); + + /* Locate and print the Nth entry. Order N-squared but N is small. */ + list_for_each(item, &yaffs_dev_list) { + yaffs_Device *dev = list_entry(item, yaffs_Device, devList); + if (n < step) { + n++; + continue; + } + buf += sprintf(buf, "\nDevice %d \"%s\"\n", n, dev->name); + buf = yaffs_dump_dev(buf, dev); + break; + } + unlock_kernel(); + + return buf - page < count ? buf - page : count; +} + +/** + * Set the verbosity of the warnings and error messages. + * + * Note that the names can only be a..z or _ with the current code. + */ + +static struct { + char *mask_name; + unsigned mask_bitfield; +} mask_flags[] = { + {"allocate", YAFFS_TRACE_ALLOCATE}, + {"always", YAFFS_TRACE_ALWAYS}, + {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, + {"buffers", YAFFS_TRACE_BUFFERS}, + {"bug", YAFFS_TRACE_BUG}, + {"checkpt", YAFFS_TRACE_CHECKPOINT}, + {"deletion", YAFFS_TRACE_DELETION}, + {"erase", YAFFS_TRACE_ERASE}, + {"error", YAFFS_TRACE_ERROR}, + {"gc_detail", YAFFS_TRACE_GC_DETAIL}, + {"gc", YAFFS_TRACE_GC}, + {"mtd", YAFFS_TRACE_MTD}, + {"nandaccess", YAFFS_TRACE_NANDACCESS}, + {"os", YAFFS_TRACE_OS}, + {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, + {"scan", YAFFS_TRACE_SCAN}, + {"tracing", YAFFS_TRACE_TRACING}, + + {"verify", YAFFS_TRACE_VERIFY}, + {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, + {"verify_full", YAFFS_TRACE_VERIFY_FULL}, + {"verify_all", YAFFS_TRACE_VERIFY_ALL}, + + {"write", YAFFS_TRACE_WRITE}, + {"all", 0xffffffff}, + {"none", 0}, + {NULL, 0}, +}; + +#define MAX_MASK_NAME_LENGTH 40 +static int yaffs_proc_write(struct file *file, const char *buf, + unsigned long count, void *data) +{ + unsigned rg = 0, mask_bitfield; + char *end; + char *mask_name; + const char *x; + char substring[MAX_MASK_NAME_LENGTH+1]; + int i; + int done = 0; + int add, len = 0; + int pos = 0; + + rg = yaffs_traceMask; + + while (!done && (pos < count)) { + done = 1; + while ((pos < count) && isspace(buf[pos])) { + pos++; + } + + switch (buf[pos]) { + case '+': + case '-': + case '=': + add = buf[pos]; + pos++; + break; + + default: + add = ' '; + break; + } + mask_name = NULL; + + mask_bitfield = simple_strtoul(buf + pos, &end, 0); + if (end > buf + pos) { + mask_name = "numeral"; + len = end - (buf + pos); + pos += len; + done = 0; + } else { + for(x = buf + pos, i = 0; + (*x == '_' || (*x >='a' && *x <= 'z')) && + i write_proc = yaffs_proc_write; + my_proc_entry->read_proc = yaffs_proc_read; + my_proc_entry->data = NULL; + } else { + return -ENOMEM; + } + + /* Now add the file system entries */ + + fsinst = fs_to_install; + + while (fsinst->fst && !error) { + error = register_filesystem(fsinst->fst); + if (!error) { + fsinst->installed = 1; + } + fsinst++; + } + + /* Any errors? uninstall */ + if (error) { + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } + } + + return error; +} + +static void __exit exit_yaffs_fs(void) +{ + + struct file_system_to_install *fsinst; + + T(YAFFS_TRACE_ALWAYS, ("yaffs " __DATE__ " " __TIME__ + " removing. \n")); + + remove_proc_entry("yaffs", &proc_root); + + fsinst = fs_to_install; + + while (fsinst->fst) { + if (fsinst->installed) { + unregister_filesystem(fsinst->fst); + fsinst->installed = 0; + } + fsinst++; + } + +} + +module_init(init_yaffs_fs) +module_exit(exit_yaffs_fs) + +MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); +MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2006"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_guts.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_guts.c new file mode 100644 index 0000000000..2ab8146913 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_guts.c @@ -0,0 +1,7469 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +const char *yaffs_guts_c_version = + "$Id: yaffs_guts.c,v 1.49 2007-05-15 20:07:40 charles Exp $"; + +#include "yportenv.h" + +#include "yaffsinterface.h" +#include "yaffs_guts.h" +#include "yaffs_tagsvalidity.h" + +#include "yaffs_tagscompat.h" +#ifndef CONFIG_YAFFS_USE_OWN_SORT +#include "yaffs_qsort.h" +#endif +#include "yaffs_nand.h" + +#include "yaffs_checkptrw.h" + +#include "yaffs_nand.h" +#include "yaffs_packedtags2.h" + + +#ifdef CONFIG_YAFFS_WINCE +void yfsd_LockYAFFS(BOOL fsLockOnly); +void yfsd_UnlockYAFFS(BOOL fsLockOnly); +#endif + +#define YAFFS_PASSIVE_GC_CHUNKS 2 + +#include "yaffs_ecc.h" + + +/* Robustification (if it ever comes about...) */ +static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND); +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk); +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags); +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_ExtendedTags * tags); + +/* Other local prototypes */ +static int yaffs_UnlinkObject( yaffs_Object *obj); +static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj); + +static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList); + +static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device * dev, + const __u8 * buffer, + yaffs_ExtendedTags * tags, + int useReserve); +static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, + int chunkInNAND, int inScan); + +static yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, + yaffs_ObjectType type); +static void yaffs_AddObjectToDirectory(yaffs_Object * directory, + yaffs_Object * obj); +static int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, + int force, int isShrink, int shadows); +static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj); +static int yaffs_CheckStructures(void); +static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, + int chunkOffset, int *limit); +static int yaffs_DoGenericObjectDeletion(yaffs_Object * in); + +static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blockNo); + +static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo); +static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, + int lineNo); + +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, + int chunkInNAND); + +static int yaffs_UnlinkWorker(yaffs_Object * obj); +static void yaffs_DestroyObject(yaffs_Object * obj); + +static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, + int chunkInObject); + +loff_t yaffs_GetFileSize(yaffs_Object * obj); + +static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr); + +static void yaffs_VerifyFreeChunks(yaffs_Device * dev); + +static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in); + +#ifdef YAFFS_PARANOID +static int yaffs_CheckFileSanity(yaffs_Object * in); +#else +#define yaffs_CheckFileSanity(in) +#endif + +static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in); +static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId); + +static void yaffs_InvalidateCheckpoint(yaffs_Device *dev); + +static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags); + +static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos); +static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId); + + +/* Function to calculate chunk and offset */ + +static void yaffs_AddrToChunk(yaffs_Device *dev, loff_t addr, __u32 *chunk, __u32 *offset) +{ + if(dev->chunkShift){ + /* Easy-peasy power of 2 case */ + *chunk = (__u32)(addr >> dev->chunkShift); + *offset = (__u32)(addr & dev->chunkMask); + } + else if(dev->crumbsPerChunk) + { + /* Case where we're using "crumbs" */ + *offset = (__u32)(addr & dev->crumbMask); + addr >>= dev->crumbShift; + *chunk = ((__u32)addr)/dev->crumbsPerChunk; + *offset += ((addr - (*chunk * dev->crumbsPerChunk)) << dev->crumbShift); + } + else + YBUG(); +} + +/* Function to return the number of shifts for a power of 2 greater than or equal + * to the given number + * Note we don't try to cater for all possible numbers and this does not have to + * be hellishly efficient. + */ + +static __u32 ShiftsGE(__u32 x) +{ + int extraBits; + int nShifts; + + nShifts = extraBits = 0; + + while(x>1){ + if(x & 1) extraBits++; + x>>=1; + nShifts++; + } + + if(extraBits) + nShifts++; + + return nShifts; +} + +/* Function to return the number of shifts to get a 1 in bit 0 + */ + +static __u32 ShiftDiv(__u32 x) +{ + int nShifts; + + nShifts = 0; + + if(!x) return 0; + + while( !(x&1)){ + x>>=1; + nShifts++; + } + + return nShifts; +} + + + +/* + * Temporary buffer manipulations. + */ + +static int yaffs_InitialiseTempBuffers(yaffs_Device *dev) +{ + int i; + __u8 *buf = (__u8 *)1; + + memset(dev->tempBuffer,0,sizeof(dev->tempBuffer)); + + for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { + dev->tempBuffer[i].line = 0; /* not in use */ + dev->tempBuffer[i].buffer = buf = + YMALLOC_DMA(dev->nDataBytesPerChunk); + } + + return buf ? YAFFS_OK : YAFFS_FAIL; + +} + +static __u8 *yaffs_GetTempBuffer(yaffs_Device * dev, int lineNo) +{ + int i, j; + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->tempBuffer[i].line == 0) { + dev->tempBuffer[i].line = lineNo; + if ((i + 1) > dev->maxTemp) { + dev->maxTemp = i + 1; + for (j = 0; j <= i; j++) + dev->tempBuffer[j].maxLine = + dev->tempBuffer[j].line; + } + + return dev->tempBuffer[i].buffer; + } + } + + T(YAFFS_TRACE_BUFFERS, + (TSTR("Out of temp buffers at line %d, other held by lines:"), + lineNo)); + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + T(YAFFS_TRACE_BUFFERS, (TSTR(" %d "), dev->tempBuffer[i].line)); + } + T(YAFFS_TRACE_BUFFERS, (TSTR(" " TENDSTR))); + + /* + * If we got here then we have to allocate an unmanaged one + * This is not good. + */ + + dev->unmanagedTempAllocations++; + return YMALLOC(dev->nDataBytesPerChunk); + +} + +static void yaffs_ReleaseTempBuffer(yaffs_Device * dev, __u8 * buffer, + int lineNo) +{ + int i; + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->tempBuffer[i].buffer == buffer) { + dev->tempBuffer[i].line = 0; + return; + } + } + + if (buffer) { + /* assume it is an unmanaged one. */ + T(YAFFS_TRACE_BUFFERS, + (TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR), + lineNo)); + YFREE(buffer); + dev->unmanagedTempDeallocations++; + } + +} + +/* + * Determine if we have a managed buffer. + */ +int yaffs_IsManagedTempBuffer(yaffs_Device * dev, const __u8 * buffer) +{ + int i; + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + if (dev->tempBuffer[i].buffer == buffer) + return 1; + + } + + for (i = 0; i < dev->nShortOpCaches; i++) { + if( dev->srCache[i].data == buffer ) + return 1; + + } + + if (buffer == dev->checkpointBuffer) + return 1; + + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: unmaged buffer detected.\n" TENDSTR))); + return 0; +} + + + +/* + * Chunk bitmap manipulations + */ + +static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device * dev, int blk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR), + blk)); + YBUG(); + } + return dev->chunkBits + + (dev->chunkBitmapStride * (blk - dev->internalStartBlock)); +} + +static Y_INLINE void yaffs_VerifyChunkBitId(yaffs_Device *dev, int blk, int chunk) +{ + if(blk < dev->internalStartBlock || blk > dev->internalEndBlock || + chunk < 0 || chunk >= dev->nChunksPerBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs: Chunk Id (%d:%d) invalid"TENDSTR),blk,chunk)); + YBUG(); + } +} + +static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device * dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + memset(blkBits, 0, dev->chunkBitmapStride); +} + +static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device * dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + yaffs_VerifyChunkBitId(dev,blk,chunk); + + blkBits[chunk / 8] &= ~(1 << (chunk & 7)); +} + +static Y_INLINE void yaffs_SetChunkBit(yaffs_Device * dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + + yaffs_VerifyChunkBitId(dev,blk,chunk); + + blkBits[chunk / 8] |= (1 << (chunk & 7)); +} + +static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device * dev, int blk, int chunk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + yaffs_VerifyChunkBitId(dev,blk,chunk); + + return (blkBits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; +} + +static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device * dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + int i; + for (i = 0; i < dev->chunkBitmapStride; i++) { + if (*blkBits) + return 1; + blkBits++; + } + return 0; +} + +static int yaffs_CountChunkBits(yaffs_Device * dev, int blk) +{ + __u8 *blkBits = yaffs_BlockBits(dev, blk); + int i; + int n = 0; + for (i = 0; i < dev->chunkBitmapStride; i++) { + __u8 x = *blkBits; + while(x){ + if(x & 1) + n++; + x >>=1; + } + + blkBits++; + } + return n; +} + +/* + * Verification code + */ + +static int yaffs_SkipVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipFullVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_FULL)); +} + +static int yaffs_SkipNANDVerification(yaffs_Device *dev) +{ + return !(yaffs_traceMask & (YAFFS_TRACE_VERIFY_NAND)); +} + +static const char * blockStateName[] = { +"Unknown", +"Needs scanning", +"Scanning", +"Empty", +"Allocating", +"Full", +"Dirty", +"Checkpoint", +"Collecting", +"Dead" +}; + +static void yaffs_VerifyBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +{ + int actuallyUsed; + int inUse; + + if(yaffs_SkipVerification(dev)) + return; + + /* Report illegal runtime states */ + if(bi->blockState <0 || bi->blockState >= YAFFS_NUMBER_OF_BLOCK_STATES) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has undefined state %d"TENDSTR),n,bi->blockState)); + + switch(bi->blockState){ + case YAFFS_BLOCK_STATE_UNKNOWN: + case YAFFS_BLOCK_STATE_SCANNING: + case YAFFS_BLOCK_STATE_NEEDS_SCANNING: + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has bad run-state %s"TENDSTR), + n,blockStateName[bi->blockState])); + } + + /* Check pages in use and soft deletions are legal */ + + actuallyUsed = bi->pagesInUse - bi->softDeletions; + + if(bi->pagesInUse < 0 || bi->pagesInUse > dev->nChunksPerBlock || + bi->softDeletions < 0 || bi->softDeletions > dev->nChunksPerBlock || + actuallyUsed < 0 || actuallyUsed > dev->nChunksPerBlock) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has illegal values pagesInUsed %d softDeletions %d"TENDSTR), + n,bi->pagesInUse,bi->softDeletions)); + + + /* Check chunk bitmap legal */ + inUse = yaffs_CountChunkBits(dev,n); + if(inUse != bi->pagesInUse) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has inconsistent values pagesInUse %d counted chunk bits %d"TENDSTR), + n,bi->pagesInUse,inUse)); + + /* Check that the sequence number is valid. + * Ten million is legal, but is very unlikely + */ + if(dev->isYaffs2 && + (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || bi->blockState == YAFFS_BLOCK_STATE_FULL) && + (bi->sequenceNumber < YAFFS_LOWEST_SEQUENCE_NUMBER || bi->sequenceNumber > 10000000 )) + T(YAFFS_TRACE_VERIFY,(TSTR("Block %d has suspect sequence number of %d"TENDSTR), + n,bi->sequenceNumber)); + +} + +static void yaffs_VerifyCollectedBlock(yaffs_Device *dev,yaffs_BlockInfo *bi,int n) +{ + yaffs_VerifyBlock(dev,bi,n); + + /* After collection the block should be in the erased state */ + /* TODO: This will need to change if we do partial gc */ + + if(bi->blockState != YAFFS_BLOCK_STATE_EMPTY){ + T(YAFFS_TRACE_ERROR,(TSTR("Block %d is in state %d after gc, should be erased"TENDSTR), + n,bi->blockState)); + } +} + +static void yaffs_VerifyBlocks(yaffs_Device *dev) +{ + int i; + int nBlocksPerState[YAFFS_NUMBER_OF_BLOCK_STATES]; + int nIllegalBlockStates = 0; + + + if(yaffs_SkipVerification(dev)) + return; + + memset(nBlocksPerState,0,sizeof(nBlocksPerState)); + + + for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++){ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); + yaffs_VerifyBlock(dev,bi,i); + + if(bi->blockState >=0 && bi->blockState < YAFFS_NUMBER_OF_BLOCK_STATES) + nBlocksPerState[bi->blockState]++; + else + nIllegalBlockStates++; + + } + + T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); + T(YAFFS_TRACE_VERIFY,(TSTR("Block summary"TENDSTR))); + + T(YAFFS_TRACE_VERIFY,(TSTR("%d blocks have illegal states"TENDSTR),nIllegalBlockStates)); + if(nBlocksPerState[YAFFS_BLOCK_STATE_ALLOCATING] > 1) + T(YAFFS_TRACE_VERIFY,(TSTR("Too many allocating blocks"TENDSTR))); + + for(i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) + T(YAFFS_TRACE_VERIFY, + (TSTR("%s %d blocks"TENDSTR), + blockStateName[i],nBlocksPerState[i])); + + if(dev->blocksInCheckpoint != nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT]) + T(YAFFS_TRACE_VERIFY, + (TSTR("Checkpoint block count wrong dev %d count %d"TENDSTR), + dev->blocksInCheckpoint, nBlocksPerState[YAFFS_BLOCK_STATE_CHECKPOINT])); + + if(dev->nErasedBlocks != nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY]) + T(YAFFS_TRACE_VERIFY, + (TSTR("Erased block count wrong dev %d count %d"TENDSTR), + dev->nErasedBlocks, nBlocksPerState[YAFFS_BLOCK_STATE_EMPTY])); + + if(nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING] > 1) + T(YAFFS_TRACE_VERIFY, + (TSTR("Too many collecting blocks %d (max is 1)"TENDSTR), + nBlocksPerState[YAFFS_BLOCK_STATE_COLLECTING])); + + T(YAFFS_TRACE_VERIFY,(TSTR(""TENDSTR))); + +} + +/* + * Verify the object header. oh must be valid, but obj and tags may be NULL in which + * case those tests will not be performed. + */ +static void yaffs_VerifyObjectHeader(yaffs_Object *obj, yaffs_ObjectHeader *oh, yaffs_ExtendedTags *tags, int parentCheck) +{ + if(yaffs_SkipVerification(obj->myDev)) + return; + + if(!(tags && obj && oh)){ + T(YAFFS_TRACE_VERIFY, + (TSTR("Verifying object header tags %x obj %x oh %x"TENDSTR), + (__u32)tags,(__u32)obj,(__u32)oh)); + return; + } + + if(oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || + oh->type > YAFFS_OBJECT_TYPE_MAX) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header type is illegal value 0x%x"TENDSTR), + tags->objectId, oh->type)); + + if(tags->objectId != obj->objectId) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch objectId %d"TENDSTR), + tags->objectId, obj->objectId)); + + + /* + * Check that the object's parent ids match if parentCheck requested. + * + * Tests do not apply to the root object. + */ + + if(parentCheck && tags->objectId > 1 && !obj->parent) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch parentId %d obj->parent is NULL"TENDSTR), + tags->objectId, oh->parentObjectId)); + + + if(parentCheck && obj->parent && + oh->parentObjectId != obj->parent->objectId && + (oh->parentObjectId != YAFFS_OBJECTID_UNLINKED || + obj->parent->objectId != YAFFS_OBJECTID_DELETED)) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header mismatch parentId %d parentObjectId %d"TENDSTR), + tags->objectId, oh->parentObjectId, obj->parent->objectId)); + + + if(tags->objectId > 1 && oh->name[0] == 0) /* Null name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is NULL"TENDSTR), + obj->objectId)); + + if(tags->objectId > 1 && ((__u8)(oh->name[0])) == 0xff) /* Trashed name */ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d header name is 0xFF"TENDSTR), + obj->objectId)); +} + + + +static int yaffs_VerifyTnodeWorker(yaffs_Object * obj, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = obj->myDev; + int ok = 1; + int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + + if (tn) { + if (level > 0) { + + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ + if (tn->internal[i]) { + ok = yaffs_VerifyTnodeWorker(obj, + tn->internal[i], + level - 1, + (chunkOffset<objectId; + + chunkOffset <<= YAFFS_TNODES_LEVEL0_BITS; + + for(i = 0; i < YAFFS_NTNODES_LEVEL0; i++){ + __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + + if(theChunk > 0){ + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),tags.objectId,tags.chunkId,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); + if(tags.objectId != objectId || tags.chunkId != chunkOffset){ + T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, chunkOffset, theChunk, + tags.objectId, tags.chunkId)); + } + } + chunkOffset++; + } + } + } + + return ok; + +} + + +static void yaffs_VerifyFile(yaffs_Object *obj) +{ + int requiredTallness; + int actualTallness; + __u32 lastChunk; + __u32 x; + __u32 i; + int ok; + yaffs_Device *dev; + yaffs_ExtendedTags tags; + yaffs_Tnode *tn; + __u32 objectId; + + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + dev = obj->myDev; + objectId = obj->objectId; + + /* Check file size is consistent with tnode depth */ + lastChunk = obj->variant.fileVariant.fileSize / dev->nDataBytesPerChunk + 1; + x = lastChunk >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (x> 0) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + actualTallness = obj->variant.fileVariant.topLevel; + + if(requiredTallness > actualTallness ) + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d had tnode tallness %d, needs to be %d"TENDSTR), + obj->objectId,actualTallness, requiredTallness)); + + + /* Check that the chunks in the tnode tree are all correct. + * We do this by scanning through the tnode tree and + * checking the tags for every chunk match. + */ + + if(yaffs_SkipNANDVerification(dev)) + return; + + for(i = 1; i <= lastChunk; i++){ + tn = yaffs_FindLevel0Tnode(dev, &obj->variant.fileVariant,i); + + if (tn) { + __u32 theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if(theChunk > 0){ + /* T(~0,(TSTR("verifying (%d:%d) %d"TENDSTR),objectId,i,theChunk)); */ + yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL, &tags); + if(tags.objectId != objectId || tags.chunkId != i){ + T(~0,(TSTR("Object %d chunkId %d NAND mismatch chunk %d tags (%d:%d)"TENDSTR), + objectId, i, theChunk, + tags.objectId, tags.chunkId)); + } + } + } + + } + +} + +static void yaffs_VerifyDirectory(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + +} + +static void yaffs_VerifyHardLink(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + /* Verify sane equivalent object */ +} + +static void yaffs_VerifySymlink(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; + + /* Verify symlink string */ +} + +static void yaffs_VerifySpecial(yaffs_Object *obj) +{ + if(obj && yaffs_SkipVerification(obj->myDev)) + return; +} + +static void yaffs_VerifyObject(yaffs_Object *obj) +{ + yaffs_Device *dev; + + __u32 chunkMin; + __u32 chunkMax; + + __u32 chunkIdOk; + __u32 chunkIsLive; + + if(!obj) + return; + + dev = obj->myDev; + + if(yaffs_SkipVerification(dev)) + return; + + /* Check sane object header chunk */ + + chunkMin = dev->internalStartBlock * dev->nChunksPerBlock; + chunkMax = (dev->internalEndBlock+1) * dev->nChunksPerBlock - 1; + + chunkIdOk = (obj->chunkId >= chunkMin && obj->chunkId <= chunkMax); + chunkIsLive = chunkIdOk && + yaffs_CheckChunkBit(dev, + obj->chunkId / dev->nChunksPerBlock, + obj->chunkId % dev->nChunksPerBlock); + if(!obj->fake && + (!chunkIdOk || !chunkIsLive)) { + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has chunkId %d %s %s"TENDSTR), + obj->objectId,obj->chunkId, + chunkIdOk ? "" : ",out of range", + chunkIsLive || !chunkIdOk ? "" : ",marked as deleted")); + } + + if(chunkIdOk && chunkIsLive &&!yaffs_SkipNANDVerification(dev)) { + yaffs_ExtendedTags tags; + yaffs_ObjectHeader *oh; + __u8 *buffer = yaffs_GetTempBuffer(dev,__LINE__); + + oh = (yaffs_ObjectHeader *)buffer; + + yaffs_ReadChunkWithTagsFromNAND(dev, obj->chunkId,buffer, &tags); + + yaffs_VerifyObjectHeader(obj,oh,&tags,1); + + yaffs_ReleaseTempBuffer(dev,buffer,__LINE__); + } + + /* Verify it has a parent */ + if(obj && !obj->fake && + (!obj->parent || obj->parent->myDev != dev)){ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has parent pointer %p which does not look like an object"TENDSTR), + obj->objectId,obj->parent)); + } + + /* Verify parent is a directory */ + if(obj->parent && obj->parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY){ + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d's parent is not a directory (type %d)"TENDSTR), + obj->objectId,obj->parent->variantType)); + } + + switch(obj->variantType){ + case YAFFS_OBJECT_TYPE_FILE: + yaffs_VerifyFile(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_VerifySymlink(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_VerifyDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_VerifyHardLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_VerifySpecial(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + T(YAFFS_TRACE_VERIFY, + (TSTR("Obj %d has illegaltype %d"TENDSTR), + obj->objectId,obj->variantType)); + break; + } + + +} + +static void yaffs_VerifyObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + int i; + struct list_head *lh; + + if(yaffs_SkipVerification(dev)) + return; + + /* Iterate through the objects in each hash entry */ + + for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++){ + list_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { + obj = list_entry(lh, yaffs_Object, hashLink); + yaffs_VerifyObject(obj); + } + } + } + +} + + +/* + * Simple hash function. Needs to have a reasonable spread + */ + +static Y_INLINE int yaffs_HashFunction(int n) +{ + n = abs(n); + return (n % YAFFS_NOBJECT_BUCKETS); +} + +/* + * Access functions to useful fake objects + */ + +yaffs_Object *yaffs_Root(yaffs_Device * dev) +{ + return dev->rootDir; +} + +yaffs_Object *yaffs_LostNFound(yaffs_Device * dev) +{ + return dev->lostNFoundDir; +} + + +/* + * Erased NAND checking functions + */ + +int yaffs_CheckFF(__u8 * buffer, int nBytes) +{ + /* Horrible, slow implementation */ + while (nBytes--) { + if (*buffer != 0xFF) + return 0; + buffer++; + } + return 1; +} + +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, + int chunkInNAND) +{ + + int retval = YAFFS_OK; + __u8 *data = yaffs_GetTempBuffer(dev, __LINE__); + yaffs_ExtendedTags tags; + int result; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunkInNAND, data, &tags); + + if(tags.eccResult > YAFFS_ECC_RESULT_NO_ERROR) + retval = YAFFS_FAIL; + + + if (!yaffs_CheckFF(data, dev->nDataBytesPerChunk) || tags.chunkUsed) { + T(YAFFS_TRACE_NANDACCESS, + (TSTR("Chunk %d not erased" TENDSTR), chunkInNAND)); + retval = YAFFS_FAIL; + } + + yaffs_ReleaseTempBuffer(dev, data, __LINE__); + + return retval; + +} + + +static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, + const __u8 * data, + yaffs_ExtendedTags * tags, + int useReserve) +{ + int attempts = 0; + int writeOk = 0; + int chunk; + + yaffs_InvalidateCheckpoint(dev); + + do { + yaffs_BlockInfo *bi = 0; + int erasedOk = 0; + + chunk = yaffs_AllocateChunk(dev, useReserve, &bi); + if (chunk < 0) { + /* no space */ + break; + } + + /* First check this chunk is erased, if it needs + * checking. The checking policy (unless forced + * always on) is as follows: + * + * Check the first page we try to write in a block. + * If the check passes then we don't need to check any + * more. If the check fails, we check again... + * If the block has been erased, we don't need to check. + * + * However, if the block has been prioritised for gc, + * then we think there might be something odd about + * this block and stop using it. + * + * Rationale: We should only ever see chunks that have + * not been erased if there was a partially written + * chunk due to power loss. This checking policy should + * catch that case with very few checks and thus save a + * lot of checks that are most likely not needed. + */ + if (bi->gcPrioritise) { + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + /* try another chunk */ + continue; + } + + /* let's give it a try */ + attempts++; + +#ifdef CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED + bi->skipErasedCheck = 0; +#endif + if (!bi->skipErasedCheck) { + erasedOk = yaffs_CheckChunkErased(dev, chunk); + if (erasedOk != YAFFS_OK) { + T(YAFFS_TRACE_ERROR, + (TSTR ("**>> yaffs chunk %d was not erased" + TENDSTR), chunk)); + + /* try another chunk */ + continue; + } + bi->skipErasedCheck = 1; + } + + writeOk = yaffs_WriteChunkWithTagsToNAND(dev, chunk, + data, tags); + if (writeOk != YAFFS_OK) { + yaffs_HandleWriteChunkError(dev, chunk, erasedOk); + /* try another chunk */ + continue; + } + + /* Copy the data into the robustification buffer */ + yaffs_HandleWriteChunkOk(dev, chunk, data, tags); + + } while (writeOk != YAFFS_OK && attempts < yaffs_wr_attempts); + + if (attempts > 1) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs write required %d attempts" TENDSTR), + attempts)); + + dev->nRetriedWrites += (attempts - 1); + } + + return chunk; +} + +/* + * Block retiring for handling a broken block. + */ + +static void yaffs_RetireBlock(yaffs_Device * dev, int blockInNAND) +{ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); + + yaffs_InvalidateCheckpoint(dev); + + yaffs_MarkBlockBad(dev, blockInNAND); + + bi->blockState = YAFFS_BLOCK_STATE_DEAD; + bi->gcPrioritise = 0; + bi->needsRetiring = 0; + + dev->nRetiredBlocks++; +} + +/* + * Functions for robustisizing TODO + * + */ + +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags) +{ +} + +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_ExtendedTags * tags) +{ +} + +void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi) +{ + if(!bi->gcPrioritise){ + bi->gcPrioritise = 1; + dev->hasPendingPrioritisedGCs = 1; + bi->chunkErrorStrikes ++; + + if(bi->chunkErrorStrikes > 3){ + bi->needsRetiring = 1; /* Too many stikes, so retire this */ + T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Block struck out" TENDSTR))); + + } + + } +} + +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND, int erasedOk) +{ + + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockInNAND); + + yaffs_HandleChunkError(dev,bi); + + + if(erasedOk ) { + /* Was an actual write failure, so mark the block for retirement */ + bi->needsRetiring = 1; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Block %d needs retiring" TENDSTR), blockInNAND)); + + + } + + /* Delete the chunk */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); +} + + +/*---------------- Name handling functions ------------*/ + +static __u16 yaffs_CalcNameSum(const YCHAR * name) +{ + __u16 sum = 0; + __u16 i = 1; + + YUCHAR *bname = (YUCHAR *) name; + if (bname) { + while ((*bname) && (i < (YAFFS_MAX_NAME_LENGTH/2))) { + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE + sum += yaffs_toupper(*bname) * i; +#else + sum += (*bname) * i; +#endif + i++; + bname++; + } + } + return sum; +} + +static void yaffs_SetObjectName(yaffs_Object * obj, const YCHAR * name) +{ +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + if (name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH) { + yaffs_strcpy(obj->shortName, name); + } else { + obj->shortName[0] = _Y('\0'); + } +#endif + obj->sum = yaffs_CalcNameSum(name); +} + +/*-------------------- TNODES ------------------- + + * List of spare tnodes + * The list is hooked together using the first pointer + * in the tnode. + */ + +/* yaffs_CreateTnodes creates a bunch more tnodes and + * adds them to the tnode free list. + * Don't use this function directly + */ + +static int yaffs_CreateTnodes(yaffs_Device * dev, int nTnodes) +{ + int i; + int tnodeSize; + yaffs_Tnode *newTnodes; + __u8 *mem; + yaffs_Tnode *curr; + yaffs_Tnode *next; + yaffs_TnodeList *tnl; + + if (nTnodes < 1) + return YAFFS_OK; + + /* Calculate the tnode size in bytes for variable width tnode support. + * Must be a multiple of 32-bits */ + tnodeSize = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + + /* make these things */ + + newTnodes = YMALLOC(nTnodes * tnodeSize); + mem = (__u8 *)newTnodes; + + if (!newTnodes) { + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs: Could not allocate Tnodes" TENDSTR))); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ +#if 0 + for (i = 0; i < nTnodes - 1; i++) { + newTnodes[i].internal[0] = &newTnodes[i + 1]; +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; +#endif + } + + newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes; +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1; +#endif + dev->freeTnodes = newTnodes; +#else + /* New hookup for wide tnodes */ + for(i = 0; i < nTnodes -1; i++) { + curr = (yaffs_Tnode *) &mem[i * tnodeSize]; + next = (yaffs_Tnode *) &mem[(i+1) * tnodeSize]; + curr->internal[0] = next; + } + + curr = (yaffs_Tnode *) &mem[(nTnodes - 1) * tnodeSize]; + curr->internal[0] = dev->freeTnodes; + dev->freeTnodes = (yaffs_Tnode *)mem; + +#endif + + + dev->nFreeTnodes += nTnodes; + dev->nTnodesCreated += nTnodes; + + /* Now add this bunch of tnodes to a list for freeing up. + * NB If we can't add this to the management list it isn't fatal + * but it just means we can't free this bunch of tnodes later. + */ + + tnl = YMALLOC(sizeof(yaffs_TnodeList)); + if (!tnl) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs: Could not add tnodes to management list" TENDSTR))); + return YAFFS_FAIL; + + } else { + tnl->tnodes = newTnodes; + tnl->next = dev->allocatedTnodeList; + dev->allocatedTnodeList = tnl; + } + + T(YAFFS_TRACE_ALLOCATE, (TSTR("yaffs: Tnodes added" TENDSTR))); + + return YAFFS_OK; +} + +/* GetTnode gets us a clean tnode. Tries to make allocate more if we run out */ + +static yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev) +{ + yaffs_Tnode *tn = NULL; + + /* If there are none left make more */ + if (!dev->freeTnodes) { + yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); + } + + if (dev->freeTnodes) { + tn = dev->freeTnodes; +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + if (tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1) { + /* Hoosterman, this thing looks like it isn't in the list */ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: Tnode list bug 1" TENDSTR))); + } +#endif + dev->freeTnodes = dev->freeTnodes->internal[0]; + dev->nFreeTnodes--; + } + + return tn; +} + +static yaffs_Tnode *yaffs_GetTnode(yaffs_Device * dev) +{ + yaffs_Tnode *tn = yaffs_GetTnodeRaw(dev); + + if(tn) + memset(tn, 0, (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + + return tn; +} + +/* FreeTnode frees up a tnode and puts it back on the free list */ +static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn) +{ + if (tn) { +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + if (tn->internal[YAFFS_NTNODES_INTERNAL] != 0) { + /* Hoosterman, this thing looks like it is already in the list */ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: Tnode list bug 2" TENDSTR))); + } + tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1; +#endif + tn->internal[0] = dev->freeTnodes; + dev->freeTnodes = tn; + dev->nFreeTnodes++; + } +} + +static void yaffs_DeinitialiseTnodes(yaffs_Device * dev) +{ + /* Free the list of allocated tnodes */ + yaffs_TnodeList *tmp; + + while (dev->allocatedTnodeList) { + tmp = dev->allocatedTnodeList->next; + + YFREE(dev->allocatedTnodeList->tnodes); + YFREE(dev->allocatedTnodeList); + dev->allocatedTnodeList = tmp; + + } + + dev->freeTnodes = NULL; + dev->nFreeTnodes = 0; +} + +static void yaffs_InitialiseTnodes(yaffs_Device * dev) +{ + dev->allocatedTnodeList = NULL; + dev->freeTnodes = NULL; + dev->nFreeTnodes = 0; + dev->nTnodesCreated = 0; + +} + + +void yaffs_PutLevel0Tnode(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos, unsigned val) +{ + __u32 *map = (__u32 *)tn; + __u32 bitInMap; + __u32 bitInWord; + __u32 wordInMap; + __u32 mask; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + val >>= dev->chunkGroupBits; + + bitInMap = pos * dev->tnodeWidth; + wordInMap = bitInMap /32; + bitInWord = bitInMap & (32 -1); + + mask = dev->tnodeMask << bitInWord; + + map[wordInMap] &= ~mask; + map[wordInMap] |= (mask & (val << bitInWord)); + + if(dev->tnodeWidth > (32-bitInWord)) { + bitInWord = (32 - bitInWord); + wordInMap++;; + mask = dev->tnodeMask >> (/*dev->tnodeWidth -*/ bitInWord); + map[wordInMap] &= ~mask; + map[wordInMap] |= (mask & (val >> bitInWord)); + } +} + +static __u32 yaffs_GetChunkGroupBase(yaffs_Device *dev, yaffs_Tnode *tn, unsigned pos) +{ + __u32 *map = (__u32 *)tn; + __u32 bitInMap; + __u32 bitInWord; + __u32 wordInMap; + __u32 val; + + pos &= YAFFS_TNODES_LEVEL0_MASK; + + bitInMap = pos * dev->tnodeWidth; + wordInMap = bitInMap /32; + bitInWord = bitInMap & (32 -1); + + val = map[wordInMap] >> bitInWord; + + if(dev->tnodeWidth > (32-bitInWord)) { + bitInWord = (32 - bitInWord); + wordInMap++;; + val |= (map[wordInMap] << bitInWord); + } + + val &= dev->tnodeMask; + val <<= dev->chunkGroupBits; + + return val; +} + +/* ------------------- End of individual tnode manipulation -----------------*/ + +/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ + * The look up tree is represented by the top tnode and the number of topLevel + * in the tree. 0 means only the level 0 tnode is in the tree. + */ + +/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ +static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId) +{ + + yaffs_Tnode *tn = fStruct->top; + __u32 i; + int requiredTallness; + int level = fStruct->topLevel; + + /* Check sane level and chunk Id */ + if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) { + return NULL; + } + + if (chunkId > YAFFS_MAX_CHUNK_ID) { + return NULL; + } + + /* First check we're tall enough (ie enough topLevel) */ + + i = chunkId >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (i) { + i >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + if (requiredTallness > fStruct->topLevel) { + /* Not tall enough, so we can't find it, return NULL. */ + return NULL; + } + + /* Traverse down to level 0 */ + while (level > 0 && tn) { + tn = tn-> + internal[(chunkId >> + ( YAFFS_TNODES_LEVEL0_BITS + + (level - 1) * + YAFFS_TNODES_INTERNAL_BITS) + ) & + YAFFS_TNODES_INTERNAL_MASK]; + level--; + + } + + return tn; +} + +/* AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree. + * This happens in two steps: + * 1. If the tree isn't tall enough, then make it taller. + * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. + * + * Used when modifying the tree. + * + * If the tn argument is NULL, then a fresh tnode will be added otherwise the specified tn will + * be plugged into the ttree. + */ + +static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device * dev, + yaffs_FileStructure * fStruct, + __u32 chunkId, + yaffs_Tnode *passedTn) +{ + + int requiredTallness; + int i; + int l; + yaffs_Tnode *tn; + + __u32 x; + + + /* Check sane level and page Id */ + if (fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL) { + return NULL; + } + + if (chunkId > YAFFS_MAX_CHUNK_ID) { + return NULL; + } + + /* First check we're tall enough (ie enough topLevel) */ + + x = chunkId >> YAFFS_TNODES_LEVEL0_BITS; + requiredTallness = 0; + while (x) { + x >>= YAFFS_TNODES_INTERNAL_BITS; + requiredTallness++; + } + + + if (requiredTallness > fStruct->topLevel) { + /* Not tall enough,gotta make the tree taller */ + for (i = fStruct->topLevel; i < requiredTallness; i++) { + + tn = yaffs_GetTnode(dev); + + if (tn) { + tn->internal[0] = fStruct->top; + fStruct->top = tn; + } else { + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs: no more tnodes" TENDSTR))); + } + } + + fStruct->topLevel = requiredTallness; + } + + /* Traverse down to level 0, adding anything we need */ + + l = fStruct->topLevel; + tn = fStruct->top; + + if(l > 0) { + while (l > 0 && tn) { + x = (chunkId >> + ( YAFFS_TNODES_LEVEL0_BITS + + (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & + YAFFS_TNODES_INTERNAL_MASK; + + + if((l>1) && !tn->internal[x]){ + /* Add missing non-level-zero tnode */ + tn->internal[x] = yaffs_GetTnode(dev); + + } else if(l == 1) { + /* Looking from level 1 at level 0 */ + if (passedTn) { + /* If we already have one, then release it.*/ + if(tn->internal[x]) + yaffs_FreeTnode(dev,tn->internal[x]); + tn->internal[x] = passedTn; + + } else if(!tn->internal[x]) { + /* Don't have one, none passed in */ + tn->internal[x] = yaffs_GetTnode(dev); + } + } + + tn = tn->internal[x]; + l--; + } + } else { + /* top is level 0 */ + if(passedTn) { + memcpy(tn,passedTn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + yaffs_FreeTnode(dev,passedTn); + } + } + + return tn; +} + +static int yaffs_FindChunkInGroup(yaffs_Device * dev, int theChunk, + yaffs_ExtendedTags * tags, int objectId, + int chunkInInode) +{ + int j; + + for (j = 0; theChunk && j < dev->chunkGroupSize; j++) { + if (yaffs_CheckChunkBit + (dev, theChunk / dev->nChunksPerBlock, + theChunk % dev->nChunksPerBlock)) { + yaffs_ReadChunkWithTagsFromNAND(dev, theChunk, NULL, + tags); + if (yaffs_TagsMatch(tags, objectId, chunkInInode)) { + /* found it; */ + return theChunk; + + } + } + theChunk++; + } + return -1; +} + + +/* DeleteWorker scans backwards through the tnode tree and deletes all the + * chunks and tnodes in the file + * Returns 1 if the tree was deleted. + * Returns 0 if it stopped early due to hitting the limit and the delete is incomplete. + */ + +static int yaffs_DeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, __u32 level, + int chunkOffset, int *limit) +{ + int i; + int chunkInInode; + int theChunk; + yaffs_ExtendedTags tags; + int foundChunk; + yaffs_Device *dev = in->myDev; + + int allDone = 1; + + if (tn) { + if (level > 0) { + + for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; + i--) { + if (tn->internal[i]) { + if (limit && (*limit) < 0) { + allDone = 0; + } else { + allDone = + yaffs_DeleteWorker(in, + tn-> + internal + [i], + level - + 1, + (chunkOffset + << + YAFFS_TNODES_INTERNAL_BITS) + + i, + limit); + } + if (allDone) { + yaffs_FreeTnode(dev, + tn-> + internal[i]); + tn->internal[i] = NULL; + } + } + + } + return (allDone) ? 1 : 0; + } else if (level == 0) { + int hitLimit = 0; + + for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0 && !hitLimit; + i--) { + theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if (theChunk) { + + chunkInInode = + (chunkOffset << + YAFFS_TNODES_LEVEL0_BITS) + i; + + foundChunk = + yaffs_FindChunkInGroup(dev, + theChunk, + &tags, + in->objectId, + chunkInInode); + + if (foundChunk > 0) { + yaffs_DeleteChunk(dev, + foundChunk, 1, + __LINE__); + in->nDataChunks--; + if (limit) { + *limit = *limit - 1; + if (*limit <= 0) { + hitLimit = 1; + } + } + + } + + yaffs_PutLevel0Tnode(dev,tn,i,0); + } + + } + return (i < 0) ? 1 : 0; + + } + + } + + return 1; + +} + +static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) +{ + + yaffs_BlockInfo *theBlock; + + T(YAFFS_TRACE_DELETION, (TSTR("soft delete chunk %d" TENDSTR), chunk)); + + theBlock = yaffs_GetBlockInfo(dev, chunk / dev->nChunksPerBlock); + if (theBlock) { + theBlock->softDeletions++; + dev->nFreeChunks++; + } +} + +/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file. + * All soft deleting does is increment the block's softdelete count and pulls the chunk out + * of the tnode. + * Thus, essentially this is the same as DeleteWorker except that the chunks are soft deleted. + */ + +static int yaffs_SoftDeleteWorker(yaffs_Object * in, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + int theChunk; + int allDone = 1; + yaffs_Device *dev = in->myDev; + + if (tn) { + if (level > 0) { + + for (i = YAFFS_NTNODES_INTERNAL - 1; allDone && i >= 0; + i--) { + if (tn->internal[i]) { + allDone = + yaffs_SoftDeleteWorker(in, + tn-> + internal[i], + level - 1, + (chunkOffset + << + YAFFS_TNODES_INTERNAL_BITS) + + i); + if (allDone) { + yaffs_FreeTnode(dev, + tn-> + internal[i]); + tn->internal[i] = NULL; + } else { + /* Hoosterman... how could this happen? */ + } + } + } + return (allDone) ? 1 : 0; + } else if (level == 0) { + + for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { + theChunk = yaffs_GetChunkGroupBase(dev,tn,i); + if (theChunk) { + /* Note this does not find the real chunk, only the chunk group. + * We make an assumption that a chunk group is not larger than + * a block. + */ + yaffs_SoftDeleteChunk(dev, theChunk); + yaffs_PutLevel0Tnode(dev,tn,i,0); + } + + } + return 1; + + } + + } + + return 1; + +} + +static void yaffs_SoftDeleteFile(yaffs_Object * obj) +{ + if (obj->deleted && + obj->variantType == YAFFS_OBJECT_TYPE_FILE && !obj->softDeleted) { + if (obj->nDataChunks <= 0) { + /* Empty file with no duplicate object headers, just delete it immediately */ + yaffs_FreeTnode(obj->myDev, + obj->variant.fileVariant.top); + obj->variant.fileVariant.top = NULL; + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: Deleting empty file %d" TENDSTR), + obj->objectId)); + yaffs_DoGenericObjectDeletion(obj); + } else { + yaffs_SoftDeleteWorker(obj, + obj->variant.fileVariant.top, + obj->variant.fileVariant. + topLevel, 0); + obj->softDeleted = 1; + } + } +} + +/* Pruning removes any part of the file structure tree that is beyond the + * bounds of the file (ie that does not point to chunks). + * + * A file should only get pruned when its size is reduced. + * + * Before pruning, the chunks must be pulled from the tree and the + * level 0 tnode entries must be zeroed out. + * Could also use this for file deletion, but that's probably better handled + * by a special case. + */ + +static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device * dev, yaffs_Tnode * tn, + __u32 level, int del0) +{ + int i; + int hasData; + + if (tn) { + hasData = 0; + + for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i] && level > 0) { + tn->internal[i] = + yaffs_PruneWorker(dev, tn->internal[i], + level - 1, + (i == 0) ? del0 : 1); + } + + if (tn->internal[i]) { + hasData++; + } + } + + if (hasData == 0 && del0) { + /* Free and return NULL */ + + yaffs_FreeTnode(dev, tn); + tn = NULL; + } + + } + + return tn; + +} + +static int yaffs_PruneFileStructure(yaffs_Device * dev, + yaffs_FileStructure * fStruct) +{ + int i; + int hasData; + int done = 0; + yaffs_Tnode *tn; + + if (fStruct->topLevel > 0) { + fStruct->top = + yaffs_PruneWorker(dev, fStruct->top, fStruct->topLevel, 0); + + /* Now we have a tree with all the non-zero branches NULL but the height + * is the same as it was. + * Let's see if we can trim internal tnodes to shorten the tree. + * We can do this if only the 0th element in the tnode is in use + * (ie all the non-zero are NULL) + */ + + while (fStruct->topLevel && !done) { + tn = fStruct->top; + + hasData = 0; + for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { + if (tn->internal[i]) { + hasData++; + } + } + + if (!hasData) { + fStruct->top = tn->internal[0]; + fStruct->topLevel--; + yaffs_FreeTnode(dev, tn); + } else { + done = 1; + } + } + } + + return YAFFS_OK; +} + +/*-------------------- End of File Structure functions.-------------------*/ + +/* yaffs_CreateFreeObjects creates a bunch more objects and + * adds them to the object free list. + */ +static int yaffs_CreateFreeObjects(yaffs_Device * dev, int nObjects) +{ + int i; + yaffs_Object *newObjects; + yaffs_ObjectList *list; + + if (nObjects < 1) + return YAFFS_OK; + + /* make these things */ + newObjects = YMALLOC(nObjects * sizeof(yaffs_Object)); + list = YMALLOC(sizeof(yaffs_ObjectList)); + + if (!newObjects || !list) { + if(newObjects) + YFREE(newObjects); + if(list) + YFREE(list); + T(YAFFS_TRACE_ALLOCATE, + (TSTR("yaffs: Could not allocate more objects" TENDSTR))); + return YAFFS_FAIL; + } + + /* Hook them into the free list */ + for (i = 0; i < nObjects - 1; i++) { + newObjects[i].siblings.next = + (struct list_head *)(&newObjects[i + 1]); + } + + newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects; + dev->freeObjects = newObjects; + dev->nFreeObjects += nObjects; + dev->nObjectsCreated += nObjects; + + /* Now add this bunch of Objects to a list for freeing up. */ + + list->objects = newObjects; + list->next = dev->allocatedObjectList; + dev->allocatedObjectList = list; + + return YAFFS_OK; +} + + +/* AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out */ +static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device * dev) +{ + yaffs_Object *tn = NULL; + + /* If there are none left make more */ + if (!dev->freeObjects) { + yaffs_CreateFreeObjects(dev, YAFFS_ALLOCATION_NOBJECTS); + } + + if (dev->freeObjects) { + tn = dev->freeObjects; + dev->freeObjects = + (yaffs_Object *) (dev->freeObjects->siblings.next); + dev->nFreeObjects--; + + /* Now sweeten it up... */ + + memset(tn, 0, sizeof(yaffs_Object)); + tn->myDev = dev; + tn->chunkId = -1; + tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN; + INIT_LIST_HEAD(&(tn->hardLinks)); + INIT_LIST_HEAD(&(tn->hashLink)); + INIT_LIST_HEAD(&tn->siblings); + + /* Add it to the lost and found directory. + * NB Can't put root or lostNFound in lostNFound so + * check if lostNFound exists first + */ + if (dev->lostNFoundDir) { + yaffs_AddObjectToDirectory(dev->lostNFoundDir, tn); + } + } + + return tn; +} + +static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device * dev, int number, + __u32 mode) +{ + + yaffs_Object *obj = + yaffs_CreateNewObject(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); + if (obj) { + obj->fake = 1; /* it is fake so it has no NAND presence... */ + obj->renameAllowed = 0; /* ... and we're not allowed to rename it... */ + obj->unlinkAllowed = 0; /* ... or unlink it */ + obj->deleted = 0; + obj->unlinked = 0; + obj->yst_mode = mode; + obj->myDev = dev; + obj->chunkId = 0; /* Not a valid chunk. */ + } + + return obj; + +} + +static void yaffs_UnhashObject(yaffs_Object * tn) +{ + int bucket; + yaffs_Device *dev = tn->myDev; + + /* If it is still linked into the bucket list, free from the list */ + if (!list_empty(&tn->hashLink)) { + list_del_init(&tn->hashLink); + bucket = yaffs_HashFunction(tn->objectId); + dev->objectBucket[bucket].count--; + } + +} + +/* FreeObject frees up a Object and puts it back on the free list */ +static void yaffs_FreeObject(yaffs_Object * tn) +{ + + yaffs_Device *dev = tn->myDev; + +#ifdef __KERNEL__ + if (tn->myInode) { + /* We're still hooked up to a cached inode. + * Don't delete now, but mark for later deletion + */ + tn->deferedFree = 1; + return; + } +#endif + + yaffs_UnhashObject(tn); + + /* Link into the free list. */ + tn->siblings.next = (struct list_head *)(dev->freeObjects); + dev->freeObjects = tn; + dev->nFreeObjects++; +} + +#ifdef __KERNEL__ + +void yaffs_HandleDeferedFree(yaffs_Object * obj) +{ + if (obj->deferedFree) { + yaffs_FreeObject(obj); + } +} + +#endif + +static void yaffs_DeinitialiseObjects(yaffs_Device * dev) +{ + /* Free the list of allocated Objects */ + + yaffs_ObjectList *tmp; + + while (dev->allocatedObjectList) { + tmp = dev->allocatedObjectList->next; + YFREE(dev->allocatedObjectList->objects); + YFREE(dev->allocatedObjectList); + + dev->allocatedObjectList = tmp; + } + + dev->freeObjects = NULL; + dev->nFreeObjects = 0; +} + +static void yaffs_InitialiseObjects(yaffs_Device * dev) +{ + int i; + + dev->allocatedObjectList = NULL; + dev->freeObjects = NULL; + dev->nFreeObjects = 0; + + for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { + INIT_LIST_HEAD(&dev->objectBucket[i].list); + dev->objectBucket[i].count = 0; + } + +} + +static int yaffs_FindNiceObjectBucket(yaffs_Device * dev) +{ + static int x = 0; + int i; + int l = 999; + int lowest = 999999; + + /* First let's see if we can find one that's empty. */ + + for (i = 0; i < 10 && lowest > 0; i++) { + x++; + x %= YAFFS_NOBJECT_BUCKETS; + if (dev->objectBucket[x].count < lowest) { + lowest = dev->objectBucket[x].count; + l = x; + } + + } + + /* If we didn't find an empty list, then try + * looking a bit further for a short one + */ + + for (i = 0; i < 10 && lowest > 3; i++) { + x++; + x %= YAFFS_NOBJECT_BUCKETS; + if (dev->objectBucket[x].count < lowest) { + lowest = dev->objectBucket[x].count; + l = x; + } + + } + + return l; +} + +static int yaffs_CreateNewObjectNumber(yaffs_Device * dev) +{ + int bucket = yaffs_FindNiceObjectBucket(dev); + + /* Now find an object value that has not already been taken + * by scanning the list. + */ + + int found = 0; + struct list_head *i; + + __u32 n = (__u32) bucket; + + /* yaffs_CheckObjectHashSanity(); */ + + while (!found) { + found = 1; + n += YAFFS_NOBJECT_BUCKETS; + if (1 || dev->objectBucket[bucket].count > 0) { + list_for_each(i, &dev->objectBucket[bucket].list) { + /* If there is already one in the list */ + if (i + && list_entry(i, yaffs_Object, + hashLink)->objectId == n) { + found = 0; + } + } + } + } + + + return n; +} + +static void yaffs_HashObject(yaffs_Object * in) +{ + int bucket = yaffs_HashFunction(in->objectId); + yaffs_Device *dev = in->myDev; + + list_add(&in->hashLink, &dev->objectBucket[bucket].list); + dev->objectBucket[bucket].count++; + +} + +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number) +{ + int bucket = yaffs_HashFunction(number); + struct list_head *i; + yaffs_Object *in; + + list_for_each(i, &dev->objectBucket[bucket].list) { + /* Look if it is in the list */ + if (i) { + in = list_entry(i, yaffs_Object, hashLink); + if (in->objectId == number) { +#ifdef __KERNEL__ + /* Don't tell the VFS about this one if it is defered free */ + if (in->deferedFree) + return NULL; +#endif + + return in; + } + } + } + + return NULL; +} + +yaffs_Object *yaffs_CreateNewObject(yaffs_Device * dev, int number, + yaffs_ObjectType type) +{ + + yaffs_Object *theObject; + yaffs_Tnode *tn; + + if (number < 0) { + number = yaffs_CreateNewObjectNumber(dev); + } + + theObject = yaffs_AllocateEmptyObject(dev); + if(!theObject) + return NULL; + + if(type == YAFFS_OBJECT_TYPE_FILE){ + tn = yaffs_GetTnode(dev); + if(!tn){ + yaffs_FreeObject(theObject); + return NULL; + } + } + + + + if (theObject) { + theObject->fake = 0; + theObject->renameAllowed = 1; + theObject->unlinkAllowed = 1; + theObject->objectId = number; + yaffs_HashObject(theObject); + theObject->variantType = type; +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(theObject->win_atime); + theObject->win_ctime[0] = theObject->win_mtime[0] = + theObject->win_atime[0]; + theObject->win_ctime[1] = theObject->win_mtime[1] = + theObject->win_atime[1]; + +#else + + theObject->yst_atime = theObject->yst_mtime = + theObject->yst_ctime = Y_CURRENT_TIME; +#endif + switch (type) { + case YAFFS_OBJECT_TYPE_FILE: + theObject->variant.fileVariant.fileSize = 0; + theObject->variant.fileVariant.scannedFileSize = 0; + theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; /* max __u32 */ + theObject->variant.fileVariant.topLevel = 0; + theObject->variant.fileVariant.top = tn; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + INIT_LIST_HEAD(&theObject->variant.directoryVariant. + children); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_SPECIAL: + /* No action required */ + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* todo this should not happen */ + break; + } + } + + return theObject; +} + +static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device * dev, + int number, + yaffs_ObjectType type) +{ + yaffs_Object *theObject = NULL; + + if (number > 0) { + theObject = yaffs_FindObjectByNumber(dev, number); + } + + if (!theObject) { + theObject = yaffs_CreateNewObject(dev, number, type); + } + + return theObject; + +} + + +static YCHAR *yaffs_CloneString(const YCHAR * str) +{ + YCHAR *newStr = NULL; + + if (str && *str) { + newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR)); + if(newStr) + yaffs_strcpy(newStr, str); + } + + return newStr; + +} + +/* + * Mknod (create) a new object. + * equivalentObject only has meaning for a hard link; + * aliasString only has meaning for a sumlink. + * rdev only has meaning for devices (a subset of special objects) + */ + +static yaffs_Object *yaffs_MknodObject(yaffs_ObjectType type, + yaffs_Object * parent, + const YCHAR * name, + __u32 mode, + __u32 uid, + __u32 gid, + yaffs_Object * equivalentObject, + const YCHAR * aliasString, __u32 rdev) +{ + yaffs_Object *in; + YCHAR *str; + + yaffs_Device *dev = parent->myDev; + + /* Check if the entry exists. If it does then fail the call since we don't want a dup.*/ + if (yaffs_FindObjectByName(parent, name)) { + return NULL; + } + + in = yaffs_CreateNewObject(dev, -1, type); + + if(type == YAFFS_OBJECT_TYPE_SYMLINK){ + str = yaffs_CloneString(aliasString); + if(!str){ + yaffs_FreeObject(in); + return NULL; + } + } + + + + if (in) { + in->chunkId = -1; + in->valid = 1; + in->variantType = type; + + in->yst_mode = mode; + +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(in->win_atime); + in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0]; + in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1]; + +#else + in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME; + + in->yst_rdev = rdev; + in->yst_uid = uid; + in->yst_gid = gid; +#endif + in->nDataChunks = 0; + + yaffs_SetObjectName(in, name); + in->dirty = 1; + + yaffs_AddObjectToDirectory(parent, in); + + in->myDev = parent->myDev; + + switch (type) { + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symLinkVariant.alias = str; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant.equivalentObject = + equivalentObject; + in->variant.hardLinkVariant.equivalentObjectId = + equivalentObject->objectId; + list_add(&in->hardLinks, &equivalentObject->hardLinks); + break; + case YAFFS_OBJECT_TYPE_FILE: + case YAFFS_OBJECT_TYPE_DIRECTORY: + case YAFFS_OBJECT_TYPE_SPECIAL: + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* do nothing */ + break; + } + + if (yaffs_UpdateObjectHeader(in, name, 0, 0, 0) < 0) { + /* Could not create the object header, fail the creation */ + yaffs_DestroyObject(in); + in = NULL; + } + + } + + return in; +} + +yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, + uid, gid, NULL, NULL, 0); +} + +yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, + mode, uid, gid, NULL, NULL, 0); +} + +yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, __u32 rdev) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, + uid, gid, NULL, NULL, rdev); +} + +yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, + const YCHAR * alias) +{ + return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, + uid, gid, NULL, alias, 0); +} + +/* yaffs_Link returns the object id of the equivalent object.*/ +yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, + yaffs_Object * equivalentObject) +{ + /* Get the real object in case we were fed a hard link as an equivalent object */ + equivalentObject = yaffs_GetEquivalentObject(equivalentObject); + + if (yaffs_MknodObject + (YAFFS_OBJECT_TYPE_HARDLINK, parent, name, 0, 0, 0, + equivalentObject, NULL, 0)) { + return equivalentObject; + } else { + return NULL; + } + +} + +static int yaffs_ChangeObjectName(yaffs_Object * obj, yaffs_Object * newDir, + const YCHAR * newName, int force, int shadows) +{ + int unlinkOp; + int deleteOp; + + yaffs_Object *existingTarget; + + if (newDir == NULL) { + newDir = obj->parent; /* use the old directory */ + } + + if (newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragendy: yaffs_ChangeObjectName: newDir is not a directory" + TENDSTR))); + YBUG(); + } + + /* TODO: Do we need this different handling for YAFFS2 and YAFFS1?? */ + if (obj->myDev->isYaffs2) { + unlinkOp = (newDir == obj->myDev->unlinkedDir); + } else { + unlinkOp = (newDir == obj->myDev->unlinkedDir + && obj->variantType == YAFFS_OBJECT_TYPE_FILE); + } + + deleteOp = (newDir == obj->myDev->deletedDir); + + existingTarget = yaffs_FindObjectByName(newDir, newName); + + /* If the object is a file going into the unlinked directory, + * then it is OK to just stuff it in since duplicate names are allowed. + * else only proceed if the new name does not exist and if we're putting + * it into a directory. + */ + if ((unlinkOp || + deleteOp || + force || + (shadows > 0) || + !existingTarget) && + newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY) { + yaffs_SetObjectName(obj, newName); + obj->dirty = 1; + + yaffs_AddObjectToDirectory(newDir, obj); + + if (unlinkOp) + obj->unlinked = 1; + + /* If it is a deletion then we mark it as a shrink for gc purposes. */ + if (yaffs_UpdateObjectHeader(obj, newName, 0, deleteOp, shadows)>= 0) + return YAFFS_OK; + } + + return YAFFS_FAIL; +} + +int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, + yaffs_Object * newDir, const YCHAR * newName) +{ + yaffs_Object *obj; + yaffs_Object *existingTarget; + int force = 0; + +#ifdef CONFIG_YAFFS_CASE_INSENSITIVE + /* Special case for case insemsitive systems (eg. WinCE). + * While look-up is case insensitive, the name isn't. + * Therefore we might want to change x.txt to X.txt + */ + if (oldDir == newDir && yaffs_strcmp(oldName, newName) == 0) { + force = 1; + } +#endif + + obj = yaffs_FindObjectByName(oldDir, oldName); + /* Check new name to long. */ + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK && + yaffs_strlen(newName) > YAFFS_MAX_ALIAS_LENGTH) + /* ENAMETOOLONG */ + return YAFFS_FAIL; + else if (obj->variantType != YAFFS_OBJECT_TYPE_SYMLINK && + yaffs_strlen(newName) > YAFFS_MAX_NAME_LENGTH) + /* ENAMETOOLONG */ + return YAFFS_FAIL; + + if (obj && obj->renameAllowed) { + + /* Now do the handling for an existing target, if there is one */ + + existingTarget = yaffs_FindObjectByName(newDir, newName); + if (existingTarget && + existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY && + !list_empty(&existingTarget->variant.directoryVariant.children)) { + /* There is a target that is a non-empty directory, so we fail */ + return YAFFS_FAIL; /* EEXIST or ENOTEMPTY */ + } else if (existingTarget && existingTarget != obj) { + /* Nuke the target first, using shadowing, + * but only if it isn't the same object + */ + yaffs_ChangeObjectName(obj, newDir, newName, force, + existingTarget->objectId); + yaffs_UnlinkObject(existingTarget); + } + + return yaffs_ChangeObjectName(obj, newDir, newName, 1, 0); + } + return YAFFS_FAIL; +} + +/*------------------------- Block Management and Page Allocation ----------------*/ + +static int yaffs_InitialiseBlocks(yaffs_Device * dev) +{ + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + + dev->blockInfo = NULL; + dev->chunkBits = NULL; + + dev->allocationBlock = -1; /* force it to get a new one */ + + /* If the first allocation strategy fails, thry the alternate one */ + dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo)); + if(!dev->blockInfo){ + dev->blockInfo = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockInfo)); + dev->blockInfoAlt = 1; + } + else + dev->blockInfoAlt = 0; + + if(dev->blockInfo){ + + /* Set up dynamic blockinfo stuff. */ + dev->chunkBitmapStride = (dev->nChunksPerBlock + 7) / 8; /* round up bytes */ + dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks); + if(!dev->chunkBits){ + dev->chunkBits = YMALLOC_ALT(dev->chunkBitmapStride * nBlocks); + dev->chunkBitsAlt = 1; + } + else + dev->chunkBitsAlt = 0; + } + + if (dev->blockInfo && dev->chunkBits) { + memset(dev->blockInfo, 0, nBlocks * sizeof(yaffs_BlockInfo)); + memset(dev->chunkBits, 0, dev->chunkBitmapStride * nBlocks); + return YAFFS_OK; + } + + return YAFFS_FAIL; + +} + +static void yaffs_DeinitialiseBlocks(yaffs_Device * dev) +{ + if(dev->blockInfoAlt && dev->blockInfo) + YFREE_ALT(dev->blockInfo); + else if(dev->blockInfo) + YFREE(dev->blockInfo); + + dev->blockInfoAlt = 0; + + dev->blockInfo = NULL; + + if(dev->chunkBitsAlt && dev->chunkBits) + YFREE_ALT(dev->chunkBits); + else if(dev->chunkBits) + YFREE(dev->chunkBits); + dev->chunkBitsAlt = 0; + dev->chunkBits = NULL; +} + +static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device * dev, + yaffs_BlockInfo * bi) +{ + int i; + __u32 seq; + yaffs_BlockInfo *b; + + if (!dev->isYaffs2) + return 1; /* disqualification only applies to yaffs2. */ + + if (!bi->hasShrinkHeader) + return 1; /* can gc */ + + /* Find the oldest dirty sequence number if we don't know it and save it + * so we don't have to keep recomputing it. + */ + if (!dev->oldestDirtySequence) { + seq = dev->sequenceNumber; + + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; + i++) { + b = yaffs_GetBlockInfo(dev, i); + if (b->blockState == YAFFS_BLOCK_STATE_FULL && + (b->pagesInUse - b->softDeletions) < + dev->nChunksPerBlock && b->sequenceNumber < seq) { + seq = b->sequenceNumber; + } + } + dev->oldestDirtySequence = seq; + } + + /* Can't do gc of this block if there are any blocks older than this one that have + * discarded pages. + */ + return (bi->sequenceNumber <= dev->oldestDirtySequence); + +} + +/* FindDiretiestBlock is used to select the dirtiest block (or close enough) + * for garbage collection. + */ + +static int yaffs_FindBlockForGarbageCollection(yaffs_Device * dev, + int aggressive) +{ + + int b = dev->currentDirtyChecker; + + int i; + int iterations; + int dirtiest = -1; + int pagesInUse = 0; + int prioritised=0; + yaffs_BlockInfo *bi; + int pendingPrioritisedExist = 0; + + /* First let's see if we need to grab a prioritised block */ + if(dev->hasPendingPrioritisedGCs){ + for(i = dev->internalStartBlock; i < dev->internalEndBlock && !prioritised; i++){ + + bi = yaffs_GetBlockInfo(dev, i); + //yaffs_VerifyBlock(dev,bi,i); + + if(bi->gcPrioritise) { + pendingPrioritisedExist = 1; + if(bi->blockState == YAFFS_BLOCK_STATE_FULL && + yaffs_BlockNotDisqualifiedFromGC(dev, bi)){ + pagesInUse = (bi->pagesInUse - bi->softDeletions); + dirtiest = i; + prioritised = 1; + aggressive = 1; /* Fool the non-aggressive skip logiv below */ + } + } + } + + if(!pendingPrioritisedExist) /* None found, so we can clear this */ + dev->hasPendingPrioritisedGCs = 0; + } + + /* If we're doing aggressive GC then we are happy to take a less-dirty block, and + * search harder. + * else (we're doing a leasurely gc), then we only bother to do this if the + * block has only a few pages in use. + */ + + dev->nonAggressiveSkip--; + + if (!aggressive && (dev->nonAggressiveSkip > 0)) { + return -1; + } + + if(!prioritised) + pagesInUse = + (aggressive) ? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1; + + if (aggressive) { + iterations = + dev->internalEndBlock - dev->internalStartBlock + 1; + } else { + iterations = + dev->internalEndBlock - dev->internalStartBlock + 1; + iterations = iterations / 16; + if (iterations > 200) { + iterations = 200; + } + } + + for (i = 0; i <= iterations && pagesInUse > 0 && !prioritised; i++) { + b++; + if (b < dev->internalStartBlock || b > dev->internalEndBlock) { + b = dev->internalStartBlock; + } + + if (b < dev->internalStartBlock || b > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> Block %d is not valid" TENDSTR), b)); + YBUG(); + } + + bi = yaffs_GetBlockInfo(dev, b); + +#if 0 + if (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT) { + dirtiest = b; + pagesInUse = 0; + } + else +#endif + + if (bi->blockState == YAFFS_BLOCK_STATE_FULL && + (bi->pagesInUse - bi->softDeletions) < pagesInUse && + yaffs_BlockNotDisqualifiedFromGC(dev, bi)) { + dirtiest = b; + pagesInUse = (bi->pagesInUse - bi->softDeletions); + } + } + + dev->currentDirtyChecker = b; + + if (dirtiest > 0) { + T(YAFFS_TRACE_GC, + (TSTR("GC Selected block %d with %d free, prioritised:%d" TENDSTR), dirtiest, + dev->nChunksPerBlock - pagesInUse,prioritised)); + } + + dev->oldestDirtySequence = 0; + + if (dirtiest > 0) { + dev->nonAggressiveSkip = 4; + } + + return dirtiest; +} + +static void yaffs_BlockBecameDirty(yaffs_Device * dev, int blockNo) +{ + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, blockNo); + + int erasedOk = 0; + + /* If the block is still healthy erase it and mark as clean. + * If the block has had a data failure, then retire it. + */ + + T(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, + (TSTR("yaffs_BlockBecameDirty block %d state %d %s"TENDSTR), + blockNo, bi->blockState, (bi->needsRetiring) ? "needs retiring" : "")); + + bi->blockState = YAFFS_BLOCK_STATE_DIRTY; + + if (!bi->needsRetiring) { + yaffs_InvalidateCheckpoint(dev); + erasedOk = yaffs_EraseBlockInNAND(dev, blockNo); + if (!erasedOk) { + dev->nErasureFailures++; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Erasure failed %d" TENDSTR), blockNo)); + } + } + + if (erasedOk && + ((yaffs_traceMask & YAFFS_TRACE_ERASE) || !yaffs_SkipVerification(dev))) { + int i; + for (i = 0; i < dev->nChunksPerBlock; i++) { + if (!yaffs_CheckChunkErased + (dev, blockNo * dev->nChunksPerBlock + i)) { + T(YAFFS_TRACE_ERROR, + (TSTR + (">>Block %d erasure supposedly OK, but chunk %d not erased" + TENDSTR), blockNo, i)); + } + } + } + + if (erasedOk) { + /* Clean it up... */ + bi->blockState = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + bi->pagesInUse = 0; + bi->softDeletions = 0; + bi->hasShrinkHeader = 0; + bi->skipErasedCheck = 1; /* This is clean, so no need to check */ + bi->gcPrioritise = 0; + yaffs_ClearChunkBits(dev, blockNo); + + T(YAFFS_TRACE_ERASE, + (TSTR("Erased block %d" TENDSTR), blockNo)); + } else { + dev->nFreeChunks -= dev->nChunksPerBlock; /* We lost a block of free space */ + + yaffs_RetireBlock(dev, blockNo); + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>> Block %d retired" TENDSTR), blockNo)); + } +} + +static int yaffs_FindBlockForAllocation(yaffs_Device * dev) +{ + int i; + + yaffs_BlockInfo *bi; + + if (dev->nErasedBlocks < 1) { + /* Hoosterman we've got a problem. + * Can't get space to gc + */ + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR))); + + return -1; + } + + /* Find an empty block. */ + + for (i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { + dev->allocationBlockFinder++; + if (dev->allocationBlockFinder < dev->internalStartBlock + || dev->allocationBlockFinder > dev->internalEndBlock) { + dev->allocationBlockFinder = dev->internalStartBlock; + } + + bi = yaffs_GetBlockInfo(dev, dev->allocationBlockFinder); + + if (bi->blockState == YAFFS_BLOCK_STATE_EMPTY) { + bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING; + dev->sequenceNumber++; + bi->sequenceNumber = dev->sequenceNumber; + dev->nErasedBlocks--; + T(YAFFS_TRACE_ALLOCATE, + (TSTR("Allocated block %d, seq %d, %d left" TENDSTR), + dev->allocationBlockFinder, dev->sequenceNumber, + dev->nErasedBlocks)); + return dev->allocationBlockFinder; + } + } + + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs tragedy: no more eraased blocks, but there should have been %d" + TENDSTR), dev->nErasedBlocks)); + + return -1; +} + + +// Check if there's space to allocate... +// Thinks.... do we need top make this ths same as yaffs_GetFreeChunks()? +static int yaffs_CheckSpaceForAllocation(yaffs_Device * dev) +{ + int reservedChunks; + int reservedBlocks = dev->nReservedBlocks; + int checkpointBlocks; + + checkpointBlocks = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; + if(checkpointBlocks < 0) + checkpointBlocks = 0; + + reservedChunks = ((reservedBlocks + checkpointBlocks) * dev->nChunksPerBlock); + + return (dev->nFreeChunks > reservedChunks); +} + +static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo **blockUsedPtr) +{ + int retVal; + yaffs_BlockInfo *bi; + + if (dev->allocationBlock < 0) { + /* Get next block to allocate off */ + dev->allocationBlock = yaffs_FindBlockForAllocation(dev); + dev->allocationPage = 0; + } + + if (!useReserve && !yaffs_CheckSpaceForAllocation(dev)) { + /* Not enough space to allocate unless we're allowed to use the reserve. */ + return -1; + } + + if (dev->nErasedBlocks < dev->nReservedBlocks + && dev->allocationPage == 0) { + T(YAFFS_TRACE_ALLOCATE, (TSTR("Allocating reserve" TENDSTR))); + } + + /* Next page please.... */ + if (dev->allocationBlock >= 0) { + bi = yaffs_GetBlockInfo(dev, dev->allocationBlock); + + retVal = (dev->allocationBlock * dev->nChunksPerBlock) + + dev->allocationPage; + bi->pagesInUse++; + yaffs_SetChunkBit(dev, dev->allocationBlock, + dev->allocationPage); + + dev->allocationPage++; + + dev->nFreeChunks--; + + /* If the block is full set the state to full */ + if (dev->allocationPage >= dev->nChunksPerBlock) { + bi->blockState = YAFFS_BLOCK_STATE_FULL; + dev->allocationBlock = -1; + } + + if(blockUsedPtr) + *blockUsedPtr = bi; + + return retVal; + } + + T(YAFFS_TRACE_ERROR, + (TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR))); + + return -1; +} + +static int yaffs_GetErasedChunks(yaffs_Device * dev) +{ + int n; + + n = dev->nErasedBlocks * dev->nChunksPerBlock; + + if (dev->allocationBlock > 0) { + n += (dev->nChunksPerBlock - dev->allocationPage); + } + + return n; + +} + +static int yaffs_GarbageCollectBlock(yaffs_Device * dev, int block) +{ + int oldChunk; + int newChunk; + int chunkInBlock; + int markNAND; + int retVal = YAFFS_OK; + int cleanups = 0; + int i; + int isCheckpointBlock; + int matchingChunk; + + int chunksBefore = yaffs_GetErasedChunks(dev); + int chunksAfter; + + yaffs_ExtendedTags tags; + + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, block); + + yaffs_Object *object; + + isCheckpointBlock = (bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT); + + bi->blockState = YAFFS_BLOCK_STATE_COLLECTING; + + T(YAFFS_TRACE_TRACING, + (TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR), block, + bi->pagesInUse, bi->hasShrinkHeader)); + + /*yaffs_VerifyFreeChunks(dev); */ + + bi->hasShrinkHeader = 0; /* clear the flag so that the block can erase */ + + /* Take off the number of soft deleted entries because + * they're going to get really deleted during GC. + */ + dev->nFreeChunks -= bi->softDeletions; + + dev->isDoingGC = 1; + + if (isCheckpointBlock || + !yaffs_StillSomeChunkBits(dev, block)) { + T(YAFFS_TRACE_TRACING, + (TSTR + ("Collecting block %d that has no chunks in use" TENDSTR), + block)); + yaffs_BlockBecameDirty(dev, block); + } else { + + __u8 *buffer = yaffs_GetTempBuffer(dev, __LINE__); + + yaffs_VerifyBlock(dev,bi,block); + + for (chunkInBlock = 0, oldChunk = block * dev->nChunksPerBlock; + chunkInBlock < dev->nChunksPerBlock + && yaffs_StillSomeChunkBits(dev, block); + chunkInBlock++, oldChunk++) { + if (yaffs_CheckChunkBit(dev, block, chunkInBlock)) { + + /* This page is in use and might need to be copied off */ + + markNAND = 1; + + yaffs_InitialiseTags(&tags); + + yaffs_ReadChunkWithTagsFromNAND(dev, oldChunk, + buffer, &tags); + + object = + yaffs_FindObjectByNumber(dev, + tags.objectId); + + T(YAFFS_TRACE_GC_DETAIL, + (TSTR + ("Collecting page %d, %d %d %d " TENDSTR), + chunkInBlock, tags.objectId, tags.chunkId, + tags.byteCount)); + + if(object && !yaffs_SkipVerification(dev)){ + if(tags.chunkId == 0) + matchingChunk = object->chunkId; + else if(object->softDeleted) + matchingChunk = oldChunk; /* Defeat the test */ + else + matchingChunk = yaffs_FindChunkInFile(object,tags.chunkId,NULL); + + if(oldChunk != matchingChunk) + T(YAFFS_TRACE_ERROR, + (TSTR("gc: page in gc mismatch: %d %d %d %d"TENDSTR), + oldChunk,matchingChunk,tags.objectId, tags.chunkId)); + + } + + if (!object) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("page %d in gc has no object: %d %d %d " + TENDSTR), oldChunk, + tags.objectId, tags.chunkId, tags.byteCount)); + } + + if (object && object->deleted + && tags.chunkId != 0) { + /* Data chunk in a deleted file, throw it away + * It's a soft deleted data chunk, + * No need to copy this, just forget about it and + * fix up the object. + */ + + object->nDataChunks--; + + if (object->nDataChunks <= 0) { + /* remeber to clean up the object */ + dev->gcCleanupList[cleanups] = + tags.objectId; + cleanups++; + } + markNAND = 0; + } else if (0 + /* Todo object && object->deleted && object->nDataChunks == 0 */ + ) { + /* Deleted object header with no data chunks. + * Can be discarded and the file deleted. + */ + object->chunkId = 0; + yaffs_FreeTnode(object->myDev, + object->variant. + fileVariant.top); + object->variant.fileVariant.top = NULL; + yaffs_DoGenericObjectDeletion(object); + + } else if (object) { + /* It's either a data chunk in a live file or + * an ObjectHeader, so we're interested in it. + * NB Need to keep the ObjectHeaders of deleted files + * until the whole file has been deleted off + */ + tags.serialNumber++; + + dev->nGCCopies++; + + if (tags.chunkId == 0) { + /* It is an object Id, + * We need to nuke the shrinkheader flags first + * We no longer want the shrinkHeader flag since its work is done + * and if it is left in place it will mess up scanning. + * Also, clear out any shadowing stuff + */ + + yaffs_ObjectHeader *oh; + oh = (yaffs_ObjectHeader *)buffer; + oh->isShrink = 0; + oh->shadowsObject = -1; + tags.extraShadows = 0; + tags.extraIsShrinkHeader = 0; + + yaffs_VerifyObjectHeader(object,oh,&tags,1); + } + + newChunk = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags, 1); + + if (newChunk < 0) { + retVal = YAFFS_FAIL; + } else { + + /* Ok, now fix up the Tnodes etc. */ + + if (tags.chunkId == 0) { + /* It's a header */ + object->chunkId = newChunk; + object->serial = tags.serialNumber; + } else { + /* It's a data chunk */ + yaffs_PutChunkIntoFile + (object, + tags.chunkId, + newChunk, 0); + } + } + } + + yaffs_DeleteChunk(dev, oldChunk, markNAND, __LINE__); + + } + } + + yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + + + /* Do any required cleanups */ + for (i = 0; i < cleanups; i++) { + /* Time to delete the file too */ + object = + yaffs_FindObjectByNumber(dev, + dev->gcCleanupList[i]); + if (object) { + yaffs_FreeTnode(dev, + object->variant.fileVariant. + top); + object->variant.fileVariant.top = NULL; + T(YAFFS_TRACE_GC, + (TSTR + ("yaffs: About to finally delete object %d" + TENDSTR), object->objectId)); + yaffs_DoGenericObjectDeletion(object); + object->myDev->nDeletedFiles--; + } + + } + + } + + yaffs_VerifyCollectedBlock(dev,bi,block); + + if (chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev))) { + T(YAFFS_TRACE_GC, + (TSTR + ("gc did not increase free chunks before %d after %d" + TENDSTR), chunksBefore, chunksAfter)); + } + + dev->isDoingGC = 0; + + return YAFFS_OK; +} + +/* New garbage collector + * If we're very low on erased blocks then we do aggressive garbage collection + * otherwise we do "leasurely" garbage collection. + * Aggressive gc looks further (whole array) and will accept less dirty blocks. + * Passive gc only inspects smaller areas and will only accept more dirty blocks. + * + * The idea is to help clear out space in a more spread-out manner. + * Dunno if it really does anything useful. + */ +static int yaffs_CheckGarbageCollection(yaffs_Device * dev) +{ + int block; + int aggressive; + int gcOk = YAFFS_OK; + int maxTries = 0; + + int checkpointBlockAdjust; + + if (dev->isDoingGC) { + /* Bail out so we don't get recursive gc */ + return YAFFS_OK; + } + + /* This loop should pass the first time. + * We'll only see looping here if the erase of the collected block fails. + */ + + do { + maxTries++; + + checkpointBlockAdjust = (dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint); + if(checkpointBlockAdjust < 0) + checkpointBlockAdjust = 0; + + if (dev->nErasedBlocks < (dev->nReservedBlocks + checkpointBlockAdjust + 2)) { + /* We need a block soon...*/ + aggressive = 1; + } else { + /* We're in no hurry */ + aggressive = 0; + } + + block = yaffs_FindBlockForGarbageCollection(dev, aggressive); + + if (block > 0) { + dev->garbageCollections++; + if (!aggressive) { + dev->passiveGarbageCollections++; + } + + T(YAFFS_TRACE_GC, + (TSTR + ("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR), + dev->nErasedBlocks, aggressive)); + + gcOk = yaffs_GarbageCollectBlock(dev, block); + } + + if (dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) { + T(YAFFS_TRACE_GC, + (TSTR + ("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" + TENDSTR), dev->nErasedBlocks, maxTries, block)); + } + } while ((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) + && (maxTries < 2)); + + return aggressive ? gcOk : YAFFS_OK; +} + +/*------------------------- TAGS --------------------------------*/ + +static int yaffs_TagsMatch(const yaffs_ExtendedTags * tags, int objectId, + int chunkInObject) +{ + return (tags->chunkId == chunkInObject && + tags->objectId == objectId && !tags->chunkDeleted) ? 1 : 0; + +} + + +/*-------------------- Data file manipulation -----------------*/ + +static int yaffs_FindChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags) +{ + /*Get the Tnode, then get the level 0 offset chunk offset */ + yaffs_Tnode *tn; + int theChunk = -1; + yaffs_ExtendedTags localTags; + int retVal = -1; + + yaffs_Device *dev = in->myDev; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &localTags; + } + + tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); + + if (tn) { + theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); + + retVal = + yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, + chunkInInode); + } + return retVal; +} + +static int yaffs_FindAndDeleteChunkInFile(yaffs_Object * in, int chunkInInode, + yaffs_ExtendedTags * tags) +{ + /* Get the Tnode, then get the level 0 offset chunk offset */ + yaffs_Tnode *tn; + int theChunk = -1; + yaffs_ExtendedTags localTags; + + yaffs_Device *dev = in->myDev; + int retVal = -1; + + if (!tags) { + /* Passed a NULL, so use our own tags space */ + tags = &localTags; + } + + tn = yaffs_FindLevel0Tnode(dev, &in->variant.fileVariant, chunkInInode); + + if (tn) { + + theChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); + + retVal = + yaffs_FindChunkInGroup(dev, theChunk, tags, in->objectId, + chunkInInode); + + /* Delete the entry in the filestructure (if found) */ + if (retVal != -1) { + yaffs_PutLevel0Tnode(dev,tn,chunkInInode,0); + } + } else { + /*T(("No level 0 found for %d\n", chunkInInode)); */ + } + + if (retVal == -1) { + /* T(("Could not find %d to delete\n",chunkInInode)); */ + } + return retVal; +} + +#ifdef YAFFS_PARANOID + +static int yaffs_CheckFileSanity(yaffs_Object * in) +{ + int chunk; + int nChunks; + int fSize; + int failed = 0; + int objId; + yaffs_Tnode *tn; + yaffs_Tags localTags; + yaffs_Tags *tags = &localTags; + int theChunk; + int chunkDeleted; + + if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { + /* T(("Object not a file\n")); */ + return YAFFS_FAIL; + } + + objId = in->objectId; + fSize = in->variant.fileVariant.fileSize; + nChunks = + (fSize + in->myDev->nDataBytesPerChunk - 1) / in->myDev->nDataBytesPerChunk; + + for (chunk = 1; chunk <= nChunks; chunk++) { + tn = yaffs_FindLevel0Tnode(in->myDev, &in->variant.fileVariant, + chunk); + + if (tn) { + + theChunk = yaffs_GetChunkGroupBase(dev,tn,chunk); + + if (yaffs_CheckChunkBits + (dev, theChunk / dev->nChunksPerBlock, + theChunk % dev->nChunksPerBlock)) { + + yaffs_ReadChunkTagsFromNAND(in->myDev, theChunk, + tags, + &chunkDeleted); + if (yaffs_TagsMatch + (tags, in->objectId, chunk, chunkDeleted)) { + /* found it; */ + + } + } else { + + failed = 1; + } + + } else { + /* T(("No level 0 found for %d\n", chunk)); */ + } + } + + return failed ? YAFFS_FAIL : YAFFS_OK; +} + +#endif + +static int yaffs_PutChunkIntoFile(yaffs_Object * in, int chunkInInode, + int chunkInNAND, int inScan) +{ + /* NB inScan is zero unless scanning. + * For forward scanning, inScan is > 0; + * for backward scanning inScan is < 0 + */ + + yaffs_Tnode *tn; + yaffs_Device *dev = in->myDev; + int existingChunk; + yaffs_ExtendedTags existingTags; + yaffs_ExtendedTags newTags; + unsigned existingSerial, newSerial; + + if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { + /* Just ignore an attempt at putting a chunk into a non-file during scanning + * If it is not during Scanning then something went wrong! + */ + if (!inScan) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy:attempt to put data chunk into a non-file" + TENDSTR))); + YBUG(); + } + + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); + return YAFFS_OK; + } + + tn = yaffs_AddOrFindLevel0Tnode(dev, + &in->variant.fileVariant, + chunkInInode, + NULL); + if (!tn) { + return YAFFS_FAIL; + } + + existingChunk = yaffs_GetChunkGroupBase(dev,tn,chunkInInode); + + if (inScan != 0) { + /* If we're scanning then we need to test for duplicates + * NB This does not need to be efficient since it should only ever + * happen when the power fails during a write, then only one + * chunk should ever be affected. + * + * Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO + * Update: For backward scanning we don't need to re-read tags so this is quite cheap. + */ + + if (existingChunk != 0) { + /* NB Right now existing chunk will not be real chunkId if the device >= 32MB + * thus we have to do a FindChunkInFile to get the real chunk id. + * + * We have a duplicate now we need to decide which one to use: + * + * Backwards scanning YAFFS2: The old one is what we use, dump the new one. + * Forward scanning YAFFS2: The new one is what we use, dump the old one. + * YAFFS1: Get both sets of tags and compare serial numbers. + */ + + if (inScan > 0) { + /* Only do this for forward scanning */ + yaffs_ReadChunkWithTagsFromNAND(dev, + chunkInNAND, + NULL, &newTags); + + /* Do a proper find */ + existingChunk = + yaffs_FindChunkInFile(in, chunkInInode, + &existingTags); + } + + if (existingChunk <= 0) { + /*Hoosterman - how did this happen? */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: existing chunk < 0 in scan" + TENDSTR))); + + } + + /* NB The deleted flags should be false, otherwise the chunks will + * not be loaded during a scan + */ + + newSerial = newTags.serialNumber; + existingSerial = existingTags.serialNumber; + + if ((inScan > 0) && + (in->myDev->isYaffs2 || + existingChunk <= 0 || + ((existingSerial + 1) & 3) == newSerial)) { + /* Forward scanning. + * Use new + * Delete the old one and drop through to update the tnode + */ + yaffs_DeleteChunk(dev, existingChunk, 1, + __LINE__); + } else { + /* Backward scanning or we want to use the existing one + * Use existing. + * Delete the new one and return early so that the tnode isn't changed + */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, + __LINE__); + return YAFFS_OK; + } + } + + } + + if (existingChunk == 0) { + in->nDataChunks++; + } + + yaffs_PutLevel0Tnode(dev,tn,chunkInInode,chunkInNAND); + + return YAFFS_OK; +} + +static int yaffs_ReadChunkDataFromObject(yaffs_Object * in, int chunkInInode, + __u8 * buffer) +{ + int chunkInNAND = yaffs_FindChunkInFile(in, chunkInInode, NULL); + + if (chunkInNAND >= 0) { + return yaffs_ReadChunkWithTagsFromNAND(in->myDev, chunkInNAND, + buffer,NULL); + } else { + T(YAFFS_TRACE_NANDACCESS, + (TSTR("Chunk %d not found zero instead" TENDSTR), + chunkInNAND)); + /* get sane (zero) data if you read a hole */ + memset(buffer, 0, in->myDev->nDataBytesPerChunk); + return 0; + } + +} + +void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn) +{ + int block; + int page; + yaffs_ExtendedTags tags; + yaffs_BlockInfo *bi; + + if (chunkId <= 0) + return; + + + dev->nDeletions++; + block = chunkId / dev->nChunksPerBlock; + page = chunkId % dev->nChunksPerBlock; + + + if(!yaffs_CheckChunkBit(dev,block,page)) + T(YAFFS_TRACE_VERIFY, + (TSTR("Deleting invalid chunk %d"TENDSTR), + chunkId)); + + bi = yaffs_GetBlockInfo(dev, block); + + T(YAFFS_TRACE_DELETION, + (TSTR("line %d delete of chunk %d" TENDSTR), lyn, chunkId)); + + if (markNAND && + bi->blockState != YAFFS_BLOCK_STATE_COLLECTING && !dev->isYaffs2) { + + yaffs_InitialiseTags(&tags); + + tags.chunkDeleted = 1; + + yaffs_WriteChunkWithTagsToNAND(dev, chunkId, NULL, &tags); + yaffs_HandleUpdateChunk(dev, chunkId, &tags); + } else { + dev->nUnmarkedDeletions++; + } + + /* Pull out of the management area. + * If the whole block became dirty, this will kick off an erasure. + */ + if (bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING || + bi->blockState == YAFFS_BLOCK_STATE_FULL || + bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + bi->blockState == YAFFS_BLOCK_STATE_COLLECTING) { + dev->nFreeChunks++; + + yaffs_ClearChunkBit(dev, block, page); + + bi->pagesInUse--; + + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING && + bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + yaffs_BlockBecameDirty(dev, block); + } + + } else { + /* T(("Bad news deleting chunk %d\n",chunkId)); */ + } + +} + +static int yaffs_WriteChunkDataToObject(yaffs_Object * in, int chunkInInode, + const __u8 * buffer, int nBytes, + int useReserve) +{ + /* Find old chunk Need to do this to get serial number + * Write new one and patch into tree. + * Invalidate old tags. + */ + + int prevChunkId; + yaffs_ExtendedTags prevTags; + + int newChunkId; + yaffs_ExtendedTags newTags; + + yaffs_Device *dev = in->myDev; + + yaffs_CheckGarbageCollection(dev); + + /* Get the previous chunk at this location in the file if it exists */ + prevChunkId = yaffs_FindChunkInFile(in, chunkInInode, &prevTags); + + /* Set up new tags */ + yaffs_InitialiseTags(&newTags); + + newTags.chunkId = chunkInInode; + newTags.objectId = in->objectId; + newTags.serialNumber = + (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1; + newTags.byteCount = nBytes; + + newChunkId = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, + useReserve); + + if (newChunkId >= 0) { + yaffs_PutChunkIntoFile(in, chunkInInode, newChunkId, 0); + + if (prevChunkId >= 0) { + yaffs_DeleteChunk(dev, prevChunkId, 1, __LINE__); + + } + + yaffs_CheckFileSanity(in); + } + return newChunkId; + +} + +/* UpdateObjectHeader updates the header on NAND for an object. + * If name is not NULL, then that new name is used. + */ +int yaffs_UpdateObjectHeader(yaffs_Object * in, const YCHAR * name, int force, + int isShrink, int shadows) +{ + + yaffs_BlockInfo *bi; + + yaffs_Device *dev = in->myDev; + + int prevChunkId; + int retVal = 0; + int result = 0; + + int newChunkId; + yaffs_ExtendedTags newTags; + yaffs_ExtendedTags oldTags; + + __u8 *buffer = NULL; + YCHAR oldName[YAFFS_MAX_NAME_LENGTH + 1]; + + yaffs_ObjectHeader *oh = NULL; + + yaffs_strcpy(oldName,"silly old name"); + + if (!in->fake || force) { + + yaffs_CheckGarbageCollection(dev); + yaffs_CheckObjectDetailsLoaded(in); + + buffer = yaffs_GetTempBuffer(in->myDev, __LINE__); + oh = (yaffs_ObjectHeader *) buffer; + + prevChunkId = in->chunkId; + + if (prevChunkId >= 0) { + result = yaffs_ReadChunkWithTagsFromNAND(dev, prevChunkId, + buffer, &oldTags); + + yaffs_VerifyObjectHeader(in,oh,&oldTags,0); + + memcpy(oldName, oh->name, sizeof(oh->name)); + } + + memset(buffer, 0xFF, dev->nDataBytesPerChunk); + + oh->type = in->variantType; + oh->yst_mode = in->yst_mode; + oh->shadowsObject = shadows; + +#ifdef CONFIG_YAFFS_WINCE + oh->win_atime[0] = in->win_atime[0]; + oh->win_ctime[0] = in->win_ctime[0]; + oh->win_mtime[0] = in->win_mtime[0]; + oh->win_atime[1] = in->win_atime[1]; + oh->win_ctime[1] = in->win_ctime[1]; + oh->win_mtime[1] = in->win_mtime[1]; +#else + oh->yst_uid = in->yst_uid; + oh->yst_gid = in->yst_gid; + oh->yst_atime = in->yst_atime; + oh->yst_mtime = in->yst_mtime; + oh->yst_ctime = in->yst_ctime; + oh->yst_rdev = in->yst_rdev; +#endif + if (in->parent) { + oh->parentObjectId = in->parent->objectId; + } else { + oh->parentObjectId = 0; + } + + if (name && *name) { + memset(oh->name, 0, sizeof(oh->name)); + yaffs_strncpy(oh->name, name, YAFFS_MAX_NAME_LENGTH); + } else if (prevChunkId>=0) { + memcpy(oh->name, oldName, sizeof(oh->name)); + } else { + memset(oh->name, 0, sizeof(oh->name)); + } + + oh->isShrink = isShrink; + + switch (in->variantType) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Should not happen */ + break; + case YAFFS_OBJECT_TYPE_FILE: + oh->fileSize = + (oh->parentObjectId == YAFFS_OBJECTID_DELETED + || oh->parentObjectId == + YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant. + fileVariant.fileSize; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + oh->equivalentObjectId = + in->variant.hardLinkVariant.equivalentObjectId; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_strncpy(oh->alias, + in->variant.symLinkVariant.alias, + YAFFS_MAX_ALIAS_LENGTH); + oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; + break; + } + + /* Tags */ + yaffs_InitialiseTags(&newTags); + in->serial++; + newTags.chunkId = 0; + newTags.objectId = in->objectId; + newTags.serialNumber = in->serial; + + /* Add extra info for file header */ + + newTags.extraHeaderInfoAvailable = 1; + newTags.extraParentObjectId = oh->parentObjectId; + newTags.extraFileLength = oh->fileSize; + newTags.extraIsShrinkHeader = oh->isShrink; + newTags.extraEquivalentObjectId = oh->equivalentObjectId; + newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0; + newTags.extraObjectType = in->variantType; + + yaffs_VerifyObjectHeader(in,oh,&newTags,1); + + /* Create new chunk in NAND */ + newChunkId = + yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &newTags, + (prevChunkId >= 0) ? 1 : 0); + + if (newChunkId >= 0) { + + in->chunkId = newChunkId; + + if (prevChunkId >= 0) { + yaffs_DeleteChunk(dev, prevChunkId, 1, + __LINE__); + } + + if(!yaffs_ObjectHasCachedWriteData(in)) + in->dirty = 0; + + /* If this was a shrink, then mark the block that the chunk lives on */ + if (isShrink) { + bi = yaffs_GetBlockInfo(in->myDev, + newChunkId /in->myDev-> nChunksPerBlock); + bi->hasShrinkHeader = 1; + } + + } + + retVal = newChunkId; + + } + + if (buffer) + yaffs_ReleaseTempBuffer(dev, buffer, __LINE__); + + return retVal; +} + +/*------------------------ Short Operations Cache ---------------------------------------- + * In many situations where there is no high level buffering (eg WinCE) a lot of + * reads might be short sequential reads, and a lot of writes may be short + * sequential writes. eg. scanning/writing a jpeg file. + * In these cases, a short read/write cache can provide a huge perfomance benefit + * with dumb-as-a-rock code. + * In Linux, the page cache provides read buffering aand the short op cache provides write + * buffering. + * + * There are a limited number (~10) of cache chunks per device so that we don't + * need a very intelligent search. + */ + +static int yaffs_ObjectHasCachedWriteData(yaffs_Object *obj) +{ + yaffs_Device *dev = obj->myDev; + int i; + yaffs_ChunkCache *cache; + int nCaches = obj->myDev->nShortOpCaches; + + for(i = 0; i < nCaches; i++){ + cache = &dev->srCache[i]; + if (cache->object == obj && + cache->dirty) + return 1; + } + + return 0; +} + + +static void yaffs_FlushFilesChunkCache(yaffs_Object * obj) +{ + yaffs_Device *dev = obj->myDev; + int lowest = -99; /* Stop compiler whining. */ + int i; + yaffs_ChunkCache *cache; + int chunkWritten = 0; + int nCaches = obj->myDev->nShortOpCaches; + + if (nCaches > 0) { + do { + cache = NULL; + + /* Find the dirty cache for this object with the lowest chunk id. */ + for (i = 0; i < nCaches; i++) { + if (dev->srCache[i].object == obj && + dev->srCache[i].dirty) { + if (!cache + || dev->srCache[i].chunkId < + lowest) { + cache = &dev->srCache[i]; + lowest = cache->chunkId; + } + } + } + + if (cache && !cache->locked) { + /* Write it out and free it up */ + + chunkWritten = + yaffs_WriteChunkDataToObject(cache->object, + cache->chunkId, + cache->data, + cache->nBytes, + 1); + cache->dirty = 0; + cache->object = NULL; + } + + } while (cache && chunkWritten > 0); + + if (cache) { + /* Hoosterman, disk full while writing cache out. */ + T(YAFFS_TRACE_ERROR, + (TSTR("yaffs tragedy: no space during cache write" TENDSTR))); + + } + } + +} + +/*yaffs_FlushEntireDeviceCache(dev) + * + * + */ + +void yaffs_FlushEntireDeviceCache(yaffs_Device *dev) +{ + yaffs_Object *obj; + int nCaches = dev->nShortOpCaches; + int i; + + /* Find a dirty object in the cache and flush it... + * until there are no further dirty objects. + */ + do { + obj = NULL; + for( i = 0; i < nCaches && !obj; i++) { + if (dev->srCache[i].object && + dev->srCache[i].dirty) + obj = dev->srCache[i].object; + + } + if(obj) + yaffs_FlushFilesChunkCache(obj); + + } while(obj); + +} + + +/* Grab us a cache chunk for use. + * First look for an empty one. + * Then look for the least recently used non-dirty one. + * Then look for the least recently used dirty one...., flush and look again. + */ +static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device * dev) +{ + int i; + int usage; + int theOne; + + if (dev->nShortOpCaches > 0) { + for (i = 0; i < dev->nShortOpCaches; i++) { + if (!dev->srCache[i].object) + return &dev->srCache[i]; + } + + return NULL; + + theOne = -1; + usage = 0; /* just to stop the compiler grizzling */ + + for (i = 0; i < dev->nShortOpCaches; i++) { + if (!dev->srCache[i].dirty && + ((dev->srCache[i].lastUse < usage && theOne >= 0) || + theOne < 0)) { + usage = dev->srCache[i].lastUse; + theOne = i; + } + } + + + return theOne >= 0 ? &dev->srCache[theOne] : NULL; + } else { + return NULL; + } + +} + +static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device * dev) +{ + yaffs_ChunkCache *cache; + yaffs_Object *theObj; + int usage; + int i; + int pushout; + + if (dev->nShortOpCaches > 0) { + /* Try find a non-dirty one... */ + + cache = yaffs_GrabChunkCacheWorker(dev); + + if (!cache) { + /* They were all dirty, find the last recently used object and flush + * its cache, then find again. + * NB what's here is not very accurate, we actually flush the object + * the last recently used page. + */ + + /* With locking we can't assume we can use entry zero */ + + theObj = NULL; + usage = -1; + cache = NULL; + pushout = -1; + + for (i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].object && + !dev->srCache[i].locked && + (dev->srCache[i].lastUse < usage || !cache)) + { + usage = dev->srCache[i].lastUse; + theObj = dev->srCache[i].object; + cache = &dev->srCache[i]; + pushout = i; + } + } + + if (!cache || cache->dirty) { + /* Flush and try again */ + yaffs_FlushFilesChunkCache(theObj); + cache = yaffs_GrabChunkCacheWorker(dev); + } + + } + return cache; + } else + return NULL; + +} + +/* Find a cached chunk */ +static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object * obj, + int chunkId) +{ + yaffs_Device *dev = obj->myDev; + int i; + if (dev->nShortOpCaches > 0) { + for (i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].object == obj && + dev->srCache[i].chunkId == chunkId) { + dev->cacheHits++; + + return &dev->srCache[i]; + } + } + } + return NULL; +} + +/* Mark the chunk for the least recently used algorithym */ +static void yaffs_UseChunkCache(yaffs_Device * dev, yaffs_ChunkCache * cache, + int isAWrite) +{ + + if (dev->nShortOpCaches > 0) { + if (dev->srLastUse < 0 || dev->srLastUse > 100000000) { + /* Reset the cache usages */ + int i; + for (i = 1; i < dev->nShortOpCaches; i++) { + dev->srCache[i].lastUse = 0; + } + dev->srLastUse = 0; + } + + dev->srLastUse++; + + cache->lastUse = dev->srLastUse; + + if (isAWrite) { + cache->dirty = 1; + } + } +} + +/* Invalidate a single cache page. + * Do this when a whole page gets written, + * ie the short cache for this page is no longer valid. + */ +static void yaffs_InvalidateChunkCache(yaffs_Object * object, int chunkId) +{ + if (object->myDev->nShortOpCaches > 0) { + yaffs_ChunkCache *cache = yaffs_FindChunkCache(object, chunkId); + + if (cache) { + cache->object = NULL; + } + } +} + +/* Invalidate all the cache pages associated with this object + * Do this whenever ther file is deleted or resized. + */ +static void yaffs_InvalidateWholeChunkCache(yaffs_Object * in) +{ + int i; + yaffs_Device *dev = in->myDev; + + if (dev->nShortOpCaches > 0) { + /* Invalidate it. */ + for (i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].object == in) { + dev->srCache[i].object = NULL; + } + } + } +} + +/*--------------------- Checkpointing --------------------*/ + + +static int yaffs_WriteCheckpointValidityMarker(yaffs_Device *dev,int head) +{ + yaffs_CheckpointValidity cp; + + memset(&cp,0,sizeof(cp)); + + cp.structType = sizeof(cp); + cp.magic = YAFFS_MAGIC; + cp.version = YAFFS_CHECKPOINT_VERSION; + cp.head = (head) ? 1 : 0; + + return (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp))? + 1 : 0; +} + +static int yaffs_ReadCheckpointValidityMarker(yaffs_Device *dev, int head) +{ + yaffs_CheckpointValidity cp; + int ok; + + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + + if(ok) + ok = (cp.structType == sizeof(cp)) && + (cp.magic == YAFFS_MAGIC) && + (cp.version == YAFFS_CHECKPOINT_VERSION) && + (cp.head == ((head) ? 1 : 0)); + return ok ? 1 : 0; +} + +static void yaffs_DeviceToCheckpointDevice(yaffs_CheckpointDevice *cp, + yaffs_Device *dev) +{ + cp->nErasedBlocks = dev->nErasedBlocks; + cp->allocationBlock = dev->allocationBlock; + cp->allocationPage = dev->allocationPage; + cp->nFreeChunks = dev->nFreeChunks; + + cp->nDeletedFiles = dev->nDeletedFiles; + cp->nUnlinkedFiles = dev->nUnlinkedFiles; + cp->nBackgroundDeletions = dev->nBackgroundDeletions; + cp->sequenceNumber = dev->sequenceNumber; + cp->oldestDirtySequence = dev->oldestDirtySequence; + +} + +static void yaffs_CheckpointDeviceToDevice(yaffs_Device *dev, + yaffs_CheckpointDevice *cp) +{ + dev->nErasedBlocks = cp->nErasedBlocks; + dev->allocationBlock = cp->allocationBlock; + dev->allocationPage = cp->allocationPage; + dev->nFreeChunks = cp->nFreeChunks; + + dev->nDeletedFiles = cp->nDeletedFiles; + dev->nUnlinkedFiles = cp->nUnlinkedFiles; + dev->nBackgroundDeletions = cp->nBackgroundDeletions; + dev->sequenceNumber = cp->sequenceNumber; + dev->oldestDirtySequence = cp->oldestDirtySequence; +} + + +static int yaffs_WriteCheckpointDevice(yaffs_Device *dev) +{ + yaffs_CheckpointDevice cp; + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; + + /* Write device runtime values*/ + yaffs_DeviceToCheckpointDevice(&cp,dev); + cp.structType = sizeof(cp); + + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); + + /* Write block info */ + if(ok) { + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + ok = (yaffs_CheckpointWrite(dev,dev->blockInfo,nBytes) == nBytes); + } + + /* Write chunk bits */ + if(ok) { + nBytes = nBlocks * dev->chunkBitmapStride; + ok = (yaffs_CheckpointWrite(dev,dev->chunkBits,nBytes) == nBytes); + } + return ok ? 1 : 0; + +} + +static int yaffs_ReadCheckpointDevice(yaffs_Device *dev) +{ + yaffs_CheckpointDevice cp; + __u32 nBytes; + __u32 nBlocks = (dev->internalEndBlock - dev->internalStartBlock + 1); + + int ok; + + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + if(!ok) + return 0; + + if(cp.structType != sizeof(cp)) + return 0; + + + yaffs_CheckpointDeviceToDevice(dev,&cp); + + nBytes = nBlocks * sizeof(yaffs_BlockInfo); + + ok = (yaffs_CheckpointRead(dev,dev->blockInfo,nBytes) == nBytes); + + if(!ok) + return 0; + nBytes = nBlocks * dev->chunkBitmapStride; + + ok = (yaffs_CheckpointRead(dev,dev->chunkBits,nBytes) == nBytes); + + return ok ? 1 : 0; +} + +static void yaffs_ObjectToCheckpointObject(yaffs_CheckpointObject *cp, + yaffs_Object *obj) +{ + + cp->objectId = obj->objectId; + cp->parentId = (obj->parent) ? obj->parent->objectId : 0; + cp->chunkId = obj->chunkId; + cp->variantType = obj->variantType; + cp->deleted = obj->deleted; + cp->softDeleted = obj->softDeleted; + cp->unlinked = obj->unlinked; + cp->fake = obj->fake; + cp->renameAllowed = obj->renameAllowed; + cp->unlinkAllowed = obj->unlinkAllowed; + cp->serial = obj->serial; + cp->nDataChunks = obj->nDataChunks; + + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + cp->fileSizeOrEquivalentObjectId = obj->variant.fileVariant.fileSize; + else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + cp->fileSizeOrEquivalentObjectId = obj->variant.hardLinkVariant.equivalentObjectId; +} + +static void yaffs_CheckpointObjectToObject( yaffs_Object *obj,yaffs_CheckpointObject *cp) +{ + + yaffs_Object *parent; + + obj->objectId = cp->objectId; + + if(cp->parentId) + parent = yaffs_FindOrCreateObjectByNumber( + obj->myDev, + cp->parentId, + YAFFS_OBJECT_TYPE_DIRECTORY); + else + parent = NULL; + + if(parent) + yaffs_AddObjectToDirectory(parent, obj); + + obj->chunkId = cp->chunkId; + obj->variantType = cp->variantType; + obj->deleted = cp->deleted; + obj->softDeleted = cp->softDeleted; + obj->unlinked = cp->unlinked; + obj->fake = cp->fake; + obj->renameAllowed = cp->renameAllowed; + obj->unlinkAllowed = cp->unlinkAllowed; + obj->serial = cp->serial; + obj->nDataChunks = cp->nDataChunks; + + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) + obj->variant.fileVariant.fileSize = cp->fileSizeOrEquivalentObjectId; + else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) + obj->variant.hardLinkVariant.equivalentObjectId = cp->fileSizeOrEquivalentObjectId; + + if(obj->objectId >= YAFFS_NOBJECT_BUCKETS) + obj->lazyLoaded = 1; +} + + + +static int yaffs_CheckpointTnodeWorker(yaffs_Object * in, yaffs_Tnode * tn, + __u32 level, int chunkOffset) +{ + int i; + yaffs_Device *dev = in->myDev; + int ok = 1; + int nTnodeBytes = (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; + + if (tn) { + if (level > 0) { + + for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++){ + if (tn->internal[i]) { + ok = yaffs_CheckpointTnodeWorker(in, + tn->internal[i], + level - 1, + (chunkOffset<variantType == YAFFS_OBJECT_TYPE_FILE){ + ok = yaffs_CheckpointTnodeWorker(obj, + obj->variant.fileVariant.top, + obj->variant.fileVariant.topLevel, + 0); + if(ok) + ok = (yaffs_CheckpointWrite(obj->myDev,&endMarker,sizeof(endMarker)) == + sizeof(endMarker)); + } + + return ok ? 1 : 0; +} + +static int yaffs_ReadCheckpointTnodes(yaffs_Object *obj) +{ + __u32 baseChunk; + int ok = 1; + yaffs_Device *dev = obj->myDev; + yaffs_FileStructure *fileStructPtr = &obj->variant.fileVariant; + yaffs_Tnode *tn; + int nread = 0; + + ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); + + while(ok && (~baseChunk)){ + nread++; + /* Read level 0 tnode */ + + + /* printf("read tnode at %d\n",baseChunk); */ + tn = yaffs_GetTnodeRaw(dev); + if(tn) + ok = (yaffs_CheckpointRead(dev,tn,(dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8) == + (dev->tnodeWidth * YAFFS_NTNODES_LEVEL0)/8); + else + ok = 0; + + if(tn && ok){ + ok = yaffs_AddOrFindLevel0Tnode(dev, + fileStructPtr, + baseChunk, + tn) ? 1 : 0; + + } + + if(ok) + ok = (yaffs_CheckpointRead(dev,&baseChunk,sizeof(baseChunk)) == sizeof(baseChunk)); + + } + + T(YAFFS_TRACE_CHECKPOINT,( + TSTR("Checkpoint read tnodes %d records, last %d. ok %d" TENDSTR), + nread,baseChunk,ok)); + + return ok ? 1 : 0; +} + + +static int yaffs_WriteCheckpointObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_CheckpointObject cp; + int i; + int ok = 1; + struct list_head *lh; + + + /* Iterate through the objects in each hash entry, + * dumping them to the checkpointing stream. + */ + + for(i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++){ + list_for_each(lh, &dev->objectBucket[i].list) { + if (lh) { + obj = list_entry(lh, yaffs_Object, hashLink); + if (!obj->deferedFree) { + yaffs_ObjectToCheckpointObject(&cp,obj); + cp.structType = sizeof(cp); + + T(YAFFS_TRACE_CHECKPOINT,( + TSTR("Checkpoint write object %d parent %d type %d chunk %d obj addr %x" TENDSTR), + cp.objectId,cp.parentId,cp.variantType,cp.chunkId,(unsigned) obj)); + + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); + + if(ok && obj->variantType == YAFFS_OBJECT_TYPE_FILE){ + ok = yaffs_WriteCheckpointTnodes(obj); + } + } + } + } + } + + /* Dump end of list */ + memset(&cp,0xFF,sizeof(yaffs_CheckpointObject)); + cp.structType = sizeof(cp); + + if(ok) + ok = (yaffs_CheckpointWrite(dev,&cp,sizeof(cp)) == sizeof(cp)); + + return ok ? 1 : 0; +} + +static int yaffs_ReadCheckpointObjects(yaffs_Device *dev) +{ + yaffs_Object *obj; + yaffs_CheckpointObject cp; + int ok = 1; + int done = 0; + yaffs_Object *hardList = NULL; + + while(ok && !done) { + ok = (yaffs_CheckpointRead(dev,&cp,sizeof(cp)) == sizeof(cp)); + if(cp.structType != sizeof(cp)) { + T(YAFFS_TRACE_CHECKPOINT,(TSTR("struct size %d instead of %d ok %d"TENDSTR), + cp.structType,sizeof(cp),ok)); + ok = 0; + } + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("Checkpoint read object %d parent %d type %d chunk %d " TENDSTR), + cp.objectId,cp.parentId,cp.variantType,cp.chunkId)); + + if(ok && cp.objectId == ~0) + done = 1; + else if(ok){ + obj = yaffs_FindOrCreateObjectByNumber(dev,cp.objectId, cp.variantType); + if(obj) { + yaffs_CheckpointObjectToObject(obj,&cp); + if(obj->variantType == YAFFS_OBJECT_TYPE_FILE) { + ok = yaffs_ReadCheckpointTnodes(obj); + } else if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + obj->hardLinks.next = + (struct list_head *) + hardList; + hardList = obj; + } + + } + } + } + + if(ok) + yaffs_HardlinkFixup(dev,hardList); + + return ok ? 1 : 0; +} + +static int yaffs_WriteCheckpointSum(yaffs_Device *dev) +{ + __u32 checkpointSum; + int ok; + + yaffs_GetCheckpointSum(dev,&checkpointSum); + + ok = (yaffs_CheckpointWrite(dev,&checkpointSum,sizeof(checkpointSum)) == sizeof(checkpointSum)); + + if(!ok) + return 0; + + return 1; +} + +static int yaffs_ReadCheckpointSum(yaffs_Device *dev) +{ + __u32 checkpointSum0; + __u32 checkpointSum1; + int ok; + + yaffs_GetCheckpointSum(dev,&checkpointSum0); + + ok = (yaffs_CheckpointRead(dev,&checkpointSum1,sizeof(checkpointSum1)) == sizeof(checkpointSum1)); + + if(!ok) + return 0; + + if(checkpointSum0 != checkpointSum1) + return 0; + + return 1; +} + + +static int yaffs_WriteCheckpointData(yaffs_Device *dev) +{ + + int ok = 1; + + if(dev->skipCheckpointWrite || !dev->isYaffs2){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint write" TENDSTR))); + ok = 0; + } + + if(ok) + ok = yaffs_CheckpointOpen(dev,1); + + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs_WriteCheckpointValidityMarker(dev,1); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint device" TENDSTR))); + ok = yaffs_WriteCheckpointDevice(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint objects" TENDSTR))); + ok = yaffs_WriteCheckpointObjects(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("write checkpoint validity" TENDSTR))); + ok = yaffs_WriteCheckpointValidityMarker(dev,0); + } + + if(ok){ + ok = yaffs_WriteCheckpointSum(dev); + } + + + if(!yaffs_CheckpointClose(dev)) + ok = 0; + + if(ok) + dev->isCheckpointed = 1; + else + dev->isCheckpointed = 0; + + return dev->isCheckpointed; +} + +static int yaffs_ReadCheckpointData(yaffs_Device *dev) +{ + int ok = 1; + + if(dev->skipCheckpointRead || !dev->isYaffs2){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("skipping checkpoint read" TENDSTR))); + ok = 0; + } + + if(ok) + ok = yaffs_CheckpointOpen(dev,0); /* open for read */ + + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs_ReadCheckpointValidityMarker(dev,1); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint device" TENDSTR))); + ok = yaffs_ReadCheckpointDevice(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint objects" TENDSTR))); + ok = yaffs_ReadCheckpointObjects(dev); + } + if(ok){ + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint validity" TENDSTR))); + ok = yaffs_ReadCheckpointValidityMarker(dev,0); + } + + if(ok){ + ok = yaffs_ReadCheckpointSum(dev); + T(YAFFS_TRACE_CHECKPOINT,(TSTR("read checkpoint checksum %d" TENDSTR),ok)); + } + + if(!yaffs_CheckpointClose(dev)) + ok = 0; + + if(ok) + dev->isCheckpointed = 1; + else + dev->isCheckpointed = 0; + + return ok ? 1 : 0; + +} + +static void yaffs_InvalidateCheckpoint(yaffs_Device *dev) +{ + if(dev->isCheckpointed || + dev->blocksInCheckpoint > 0){ + dev->isCheckpointed = 0; + yaffs_CheckpointInvalidateStream(dev); + if(dev->superBlock && dev->markSuperBlockDirty) + dev->markSuperBlockDirty(dev->superBlock); + } +} + + +int yaffs_CheckpointSave(yaffs_Device *dev) +{ + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("save entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + + if(!dev->isCheckpointed) { + yaffs_InvalidateCheckpoint(dev); + yaffs_WriteCheckpointData(dev); + } + + T(YAFFS_TRACE_ALWAYS,(TSTR("save exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + return dev->isCheckpointed; +} + +int yaffs_CheckpointRestore(yaffs_Device *dev) +{ + int retval; + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore entry: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + retval = yaffs_ReadCheckpointData(dev); + + if(dev->isCheckpointed){ + yaffs_VerifyObjects(dev); + yaffs_VerifyBlocks(dev); + yaffs_VerifyFreeChunks(dev); + } + + T(YAFFS_TRACE_CHECKPOINT,(TSTR("restore exit: isCheckpointed %d"TENDSTR),dev->isCheckpointed)); + + return retval; +} + +/*--------------------- File read/write ------------------------ + * Read and write have very similar structures. + * In general the read/write has three parts to it + * An incomplete chunk to start with (if the read/write is not chunk-aligned) + * Some complete chunks + * An incomplete chunk to end off with + * + * Curve-balls: the first chunk might also be the last chunk. + */ + +int yaffs_ReadDataFromFile(yaffs_Object * in, __u8 * buffer, loff_t offset, + int nBytes) +{ + + int chunk; + int start; + int nToCopy; + int n = nBytes; + int nDone = 0; + yaffs_ChunkCache *cache; + + yaffs_Device *dev; + + dev = in->myDev; + + while (n > 0) { + //chunk = offset / dev->nDataBytesPerChunk + 1; + //start = offset % dev->nDataBytesPerChunk; + yaffs_AddrToChunk(dev,offset,&chunk,&start); + chunk++; + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + if ((start + n) < dev->nDataBytesPerChunk) { + nToCopy = n; + } else { + nToCopy = dev->nDataBytesPerChunk - start; + } + + cache = yaffs_FindChunkCache(in, chunk); + + /* If the chunk is already in the cache or it is less than a whole chunk + * then use the cache (if there is caching) + * else bypass the cache. + */ + if (cache || nToCopy != dev->nDataBytesPerChunk) { + if (dev->nShortOpCaches > 0) { + + /* If we can't find the data in the cache, then load it up. */ + + if (!cache) { + cache = yaffs_GrabChunkCache(in->myDev); + cache->object = in; + cache->chunkId = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_ReadChunkDataFromObject(in, chunk, + cache-> + data); + cache->nBytes = 0; + } + + yaffs_UseChunkCache(dev, cache, 0); + + cache->locked = 1; + +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(buffer, &cache->data[start], nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + cache->locked = 0; + } else { + /* Read into the local buffer then copy..*/ + + __u8 *localBuffer = + yaffs_GetTempBuffer(dev, __LINE__); + yaffs_ReadChunkDataFromObject(in, chunk, + localBuffer); +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(buffer, &localBuffer[start], nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + yaffs_ReleaseTempBuffer(dev, localBuffer, + __LINE__); + } + + } else { +#ifdef CONFIG_YAFFS_WINCE + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); + + /* Under WinCE can't do direct transfer. Need to use a local buffer. + * This is because we otherwise screw up WinCE's memory mapper + */ + yaffs_ReadChunkDataFromObject(in, chunk, localBuffer); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(buffer, localBuffer, dev->nDataBytesPerChunk); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); +#endif + +#else + /* A full chunk. Read directly into the supplied buffer. */ + yaffs_ReadChunkDataFromObject(in, chunk, buffer); +#endif + } + + n -= nToCopy; + offset += nToCopy; + buffer += nToCopy; + nDone += nToCopy; + + } + + return nDone; +} + +int yaffs_WriteDataToFile(yaffs_Object * in, const __u8 * buffer, loff_t offset, + int nBytes, int writeThrough) +{ + + int chunk; + int start; + int nToCopy; + int n = nBytes; + int nDone = 0; + int nToWriteBack; + int startOfWrite = offset; + int chunkWritten = 0; + int nBytesRead; + + yaffs_Device *dev; + + dev = in->myDev; + + while (n > 0 && chunkWritten >= 0) { + //chunk = offset / dev->nDataBytesPerChunk + 1; + //start = offset % dev->nDataBytesPerChunk; + yaffs_AddrToChunk(dev,offset,&chunk,&start); + chunk++; + + /* OK now check for the curveball where the start and end are in + * the same chunk. + */ + + if ((start + n) < dev->nDataBytesPerChunk) { + nToCopy = n; + + /* Now folks, to calculate how many bytes to write back.... + * If we're overwriting and not writing to then end of file then + * we need to write back as much as was there before. + */ + + nBytesRead = + in->variant.fileVariant.fileSize - + ((chunk - 1) * dev->nDataBytesPerChunk); + + if (nBytesRead > dev->nDataBytesPerChunk) { + nBytesRead = dev->nDataBytesPerChunk; + } + + nToWriteBack = + (nBytesRead > + (start + n)) ? nBytesRead : (start + n); + + } else { + nToCopy = dev->nDataBytesPerChunk - start; + nToWriteBack = dev->nDataBytesPerChunk; + } + + if (nToCopy != dev->nDataBytesPerChunk) { + /* An incomplete start or end chunk (or maybe both start and end chunk) */ + if (dev->nShortOpCaches > 0) { + yaffs_ChunkCache *cache; + /* If we can't find the data in the cache, then load the cache */ + cache = yaffs_FindChunkCache(in, chunk); + + if (!cache + && yaffs_CheckSpaceForAllocation(in-> + myDev)) { + cache = yaffs_GrabChunkCache(in->myDev); + cache->object = in; + cache->chunkId = chunk; + cache->dirty = 0; + cache->locked = 0; + yaffs_ReadChunkDataFromObject(in, chunk, + cache-> + data); + } + else if(cache && + !cache->dirty && + !yaffs_CheckSpaceForAllocation(in->myDev)){ + /* Drop the cache if it was a read cache item and + * no space check has been made for it. + */ + cache = NULL; + } + + if (cache) { + yaffs_UseChunkCache(dev, cache, 1); + cache->locked = 1; +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + + memcpy(&cache->data[start], buffer, + nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + cache->locked = 0; + cache->nBytes = nToWriteBack; + + if (writeThrough) { + chunkWritten = + yaffs_WriteChunkDataToObject + (cache->object, + cache->chunkId, + cache->data, cache->nBytes, + 1); + cache->dirty = 0; + } + + } else { + chunkWritten = -1; /* fail the write */ + } + } else { + /* An incomplete start or end chunk (or maybe both start and end chunk) + * Read into the local buffer then copy, then copy over and write back. + */ + + __u8 *localBuffer = + yaffs_GetTempBuffer(dev, __LINE__); + + yaffs_ReadChunkDataFromObject(in, chunk, + localBuffer); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + + memcpy(&localBuffer[start], buffer, nToCopy); + +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + chunkWritten = + yaffs_WriteChunkDataToObject(in, chunk, + localBuffer, + nToWriteBack, + 0); + + yaffs_ReleaseTempBuffer(dev, localBuffer, + __LINE__); + + } + + } else { + +#ifdef CONFIG_YAFFS_WINCE + /* Under WinCE can't do direct transfer. Need to use a local buffer. + * This is because we otherwise screw up WinCE's memory mapper + */ + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); +#ifdef CONFIG_YAFFS_WINCE + yfsd_UnlockYAFFS(TRUE); +#endif + memcpy(localBuffer, buffer, dev->nDataBytesPerChunk); +#ifdef CONFIG_YAFFS_WINCE + yfsd_LockYAFFS(TRUE); +#endif + chunkWritten = + yaffs_WriteChunkDataToObject(in, chunk, localBuffer, + dev->nDataBytesPerChunk, + 0); + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); +#else + /* A full chunk. Write directly from the supplied buffer. */ + chunkWritten = + yaffs_WriteChunkDataToObject(in, chunk, buffer, + dev->nDataBytesPerChunk, + 0); +#endif + /* Since we've overwritten the cached data, we better invalidate it. */ + yaffs_InvalidateChunkCache(in, chunk); + } + + if (chunkWritten >= 0) { + n -= nToCopy; + offset += nToCopy; + buffer += nToCopy; + nDone += nToCopy; + } + + } + + /* Update file object */ + + if ((startOfWrite + nDone) > in->variant.fileVariant.fileSize) { + in->variant.fileVariant.fileSize = (startOfWrite + nDone); + } + + in->dirty = 1; + + return nDone; +} + + +/* ---------------------- File resizing stuff ------------------ */ + +static void yaffs_PruneResizedChunks(yaffs_Object * in, int newSize) +{ + + yaffs_Device *dev = in->myDev; + int oldFileSize = in->variant.fileVariant.fileSize; + + int lastDel = 1 + (oldFileSize - 1) / dev->nDataBytesPerChunk; + + int startDel = 1 + (newSize + dev->nDataBytesPerChunk - 1) / + dev->nDataBytesPerChunk; + int i; + int chunkId; + + /* Delete backwards so that we don't end up with holes if + * power is lost part-way through the operation. + */ + for (i = lastDel; i >= startDel; i--) { + /* NB this could be optimised somewhat, + * eg. could retrieve the tags and write them without + * using yaffs_DeleteChunk + */ + + chunkId = yaffs_FindAndDeleteChunkInFile(in, i, NULL); + if (chunkId > 0) { + if (chunkId < + (dev->internalStartBlock * dev->nChunksPerBlock) + || chunkId >= + ((dev->internalEndBlock + + 1) * dev->nChunksPerBlock)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("Found daft chunkId %d for %d" TENDSTR), + chunkId, i)); + } else { + in->nDataChunks--; + yaffs_DeleteChunk(dev, chunkId, 1, __LINE__); + } + } + } + +} + +int yaffs_ResizeFile(yaffs_Object * in, loff_t newSize) +{ + + int oldFileSize = in->variant.fileVariant.fileSize; + int newSizeOfPartialChunk; + int newFullChunks; + + yaffs_Device *dev = in->myDev; + + yaffs_AddrToChunk(dev, newSize, &newFullChunks, &newSizeOfPartialChunk); + + yaffs_FlushFilesChunkCache(in); + yaffs_InvalidateWholeChunkCache(in); + + yaffs_CheckGarbageCollection(dev); + + if (in->variantType != YAFFS_OBJECT_TYPE_FILE) { + return yaffs_GetFileSize(in); + } + + if (newSize == oldFileSize) { + return oldFileSize; + } + + if (newSize < oldFileSize) { + + yaffs_PruneResizedChunks(in, newSize); + + if (newSizeOfPartialChunk != 0) { + int lastChunk = 1 + newFullChunks; + + __u8 *localBuffer = yaffs_GetTempBuffer(dev, __LINE__); + + /* Got to read and rewrite the last chunk with its new size and zero pad */ + yaffs_ReadChunkDataFromObject(in, lastChunk, + localBuffer); + + memset(localBuffer + newSizeOfPartialChunk, 0, + dev->nDataBytesPerChunk - newSizeOfPartialChunk); + + yaffs_WriteChunkDataToObject(in, lastChunk, localBuffer, + newSizeOfPartialChunk, 1); + + yaffs_ReleaseTempBuffer(dev, localBuffer, __LINE__); + } + + in->variant.fileVariant.fileSize = newSize; + + yaffs_PruneFileStructure(dev, &in->variant.fileVariant); + } else { + /* newsSize > oldFileSize */ + in->variant.fileVariant.fileSize = newSize; + } + + + + /* Write a new object header. + * show we've shrunk the file, if need be + * Do this only if the file is not in the deleted directories. + */ + if (in->parent->objectId != YAFFS_OBJECTID_UNLINKED && + in->parent->objectId != YAFFS_OBJECTID_DELETED) { + yaffs_UpdateObjectHeader(in, NULL, 0, + (newSize < oldFileSize) ? 1 : 0, 0); + } + + return newSize; +} + +loff_t yaffs_GetFileSize(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + return obj->variant.fileVariant.fileSize; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_strlen(obj->variant.symLinkVariant.alias); + default: + return 0; + } +} + + + +int yaffs_FlushFile(yaffs_Object * in, int updateTime) +{ + int retVal; + if (in->dirty) { + yaffs_FlushFilesChunkCache(in); + if (updateTime) { +#ifdef CONFIG_YAFFS_WINCE + yfsd_WinFileTimeNow(in->win_mtime); +#else + + in->yst_mtime = Y_CURRENT_TIME; + +#endif + } + + retVal = + (yaffs_UpdateObjectHeader(in, NULL, 0, 0, 0) >= + 0) ? YAFFS_OK : YAFFS_FAIL; + } else { + retVal = YAFFS_OK; + } + + return retVal; + +} + +static int yaffs_DoGenericObjectDeletion(yaffs_Object * in) +{ + + /* First off, invalidate the file's data in the cache, without flushing. */ + yaffs_InvalidateWholeChunkCache(in); + + if (in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir)) { + /* Move to the unlinked directory so we have a record that it was deleted. */ + yaffs_ChangeObjectName(in, in->myDev->deletedDir,"deleted", 0, 0); + + } + + yaffs_RemoveObjectFromDirectory(in); + yaffs_DeleteChunk(in->myDev, in->chunkId, 1, __LINE__); + in->chunkId = -1; + + yaffs_FreeObject(in); + return YAFFS_OK; + +} + +/* yaffs_DeleteFile deletes the whole file data + * and the inode associated with the file. + * It does not delete the links associated with the file. + */ +static int yaffs_UnlinkFile(yaffs_Object * in) +{ + + int retVal; + int immediateDeletion = 0; + + if (1) { +#ifdef __KERNEL__ + if (!in->myInode) { + immediateDeletion = 1; + + } +#else + if (in->inUse <= 0) { + immediateDeletion = 1; + + } +#endif + if (immediateDeletion) { + retVal = + yaffs_ChangeObjectName(in, in->myDev->deletedDir, + "deleted", 0, 0); + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: immediate deletion of file %d" TENDSTR), + in->objectId)); + in->deleted = 1; + in->myDev->nDeletedFiles++; + if (0 && in->myDev->isYaffs2) { + yaffs_ResizeFile(in, 0); + } + yaffs_SoftDeleteFile(in); + } else { + retVal = + yaffs_ChangeObjectName(in, in->myDev->unlinkedDir, + "unlinked", 0, 0); + } + + } + return retVal; +} + +int yaffs_DeleteFile(yaffs_Object * in) +{ + int retVal = YAFFS_OK; + + if (in->nDataChunks > 0) { + /* Use soft deletion if there is data in the file */ + if (!in->unlinked) { + retVal = yaffs_UnlinkFile(in); + } + if (retVal == YAFFS_OK && in->unlinked && !in->deleted) { + in->deleted = 1; + in->myDev->nDeletedFiles++; + yaffs_SoftDeleteFile(in); + } + return in->deleted ? YAFFS_OK : YAFFS_FAIL; + } else { + /* The file has no data chunks so we toss it immediately */ + yaffs_FreeTnode(in->myDev, in->variant.fileVariant.top); + in->variant.fileVariant.top = NULL; + yaffs_DoGenericObjectDeletion(in); + + return YAFFS_OK; + } +} + +static int yaffs_DeleteDirectory(yaffs_Object * in) +{ + /* First check that the directory is empty. */ + if (list_empty(&in->variant.directoryVariant.children)) { + return yaffs_DoGenericObjectDeletion(in); + } + + return YAFFS_FAIL; + +} + +static int yaffs_DeleteSymLink(yaffs_Object * in) +{ + YFREE(in->variant.symLinkVariant.alias); + + return yaffs_DoGenericObjectDeletion(in); +} + +static int yaffs_DeleteHardLink(yaffs_Object * in) +{ + /* remove this hardlink from the list assocaited with the equivalent + * object + */ + list_del(&in->hardLinks); + return yaffs_DoGenericObjectDeletion(in); +} + +static void yaffs_DestroyObject(yaffs_Object * obj) +{ + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + yaffs_DeleteFile(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + yaffs_DeleteDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + yaffs_DeleteSymLink(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + yaffs_DeleteHardLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + yaffs_DoGenericObjectDeletion(obj); + break; + case YAFFS_OBJECT_TYPE_UNKNOWN: + break; /* should not happen. */ + } +} + +static int yaffs_UnlinkWorker(yaffs_Object * obj) +{ + + if (obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + return yaffs_DeleteHardLink(obj); + } else if (!list_empty(&obj->hardLinks)) { + /* Curve ball: We're unlinking an object that has a hardlink. + * + * This problem arises because we are not strictly following + * The Linux link/inode model. + * + * We can't really delete the object. + * Instead, we do the following: + * - Select a hardlink. + * - Unhook it from the hard links + * - Unhook it from its parent directory (so that the rename can work) + * - Rename the object to the hardlink's name. + * - Delete the hardlink + */ + + yaffs_Object *hl; + int retVal; + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + hl = list_entry(obj->hardLinks.next, yaffs_Object, hardLinks); + + list_del_init(&hl->hardLinks); + list_del_init(&hl->siblings); + + yaffs_GetObjectName(hl, name, YAFFS_MAX_NAME_LENGTH + 1); + + retVal = yaffs_ChangeObjectName(obj, hl->parent, name, 0, 0); + + if (retVal == YAFFS_OK) { + retVal = yaffs_DoGenericObjectDeletion(hl); + } + return retVal; + + } else { + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + return yaffs_UnlinkFile(obj); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return yaffs_DeleteDirectory(obj); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return yaffs_DeleteSymLink(obj); + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + return yaffs_DoGenericObjectDeletion(obj); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + case YAFFS_OBJECT_TYPE_UNKNOWN: + default: + return YAFFS_FAIL; + } + } +} + + +static int yaffs_UnlinkObject( yaffs_Object *obj) +{ + + if (obj && obj->unlinkAllowed) { + return yaffs_UnlinkWorker(obj); + } + + return YAFFS_FAIL; + +} +int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name) +{ + yaffs_Object *obj; + + obj = yaffs_FindObjectByName(dir, name); + return yaffs_UnlinkObject(obj); +} + +/*----------------------- Initialisation Scanning ---------------------- */ + +static void yaffs_HandleShadowedObject(yaffs_Device * dev, int objId, + int backwardScanning) +{ + yaffs_Object *obj; + + if (!backwardScanning) { + /* Handle YAFFS1 forward scanning case + * For YAFFS1 we always do the deletion + */ + + } else { + /* Handle YAFFS2 case (backward scanning) + * If the shadowed object exists then ignore. + */ + if (yaffs_FindObjectByNumber(dev, objId)) { + return; + } + } + + /* Let's create it (if it does not exist) assuming it is a file so that it can do shrinking etc. + * We put it in unlinked dir to be cleaned up after the scanning + */ + obj = + yaffs_FindOrCreateObjectByNumber(dev, objId, + YAFFS_OBJECT_TYPE_FILE); + yaffs_AddObjectToDirectory(dev->unlinkedDir, obj); + obj->variant.fileVariant.shrinkSize = 0; + obj->valid = 1; /* So that we don't read any other info for this file */ + +} + +typedef struct { + int seq; + int block; +} yaffs_BlockIndex; + + +static void yaffs_HardlinkFixup(yaffs_Device *dev, yaffs_Object *hardList) +{ + yaffs_Object *hl; + yaffs_Object *in; + + while (hardList) { + hl = hardList; + hardList = (yaffs_Object *) (hardList->hardLinks.next); + + in = yaffs_FindObjectByNumber(dev, + hl->variant.hardLinkVariant. + equivalentObjectId); + + if (in) { + /* Add the hardlink pointers */ + hl->variant.hardLinkVariant.equivalentObject = in; + list_add(&hl->hardLinks, &in->hardLinks); + } else { + /* Todo Need to report/handle this better. + * Got a problem... hardlink to a non-existant object + */ + hl->variant.hardLinkVariant.equivalentObject = NULL; + INIT_LIST_HEAD(&hl->hardLinks); + + } + + } + +} + + + + + +static int ybicmp(const void *a, const void *b){ + register int aseq = ((yaffs_BlockIndex *)a)->seq; + register int bseq = ((yaffs_BlockIndex *)b)->seq; + register int ablock = ((yaffs_BlockIndex *)a)->block; + register int bblock = ((yaffs_BlockIndex *)b)->block; + if( aseq == bseq ) + return ablock - bblock; + else + return aseq - bseq; + +} + +static int yaffs_Scan(yaffs_Device * dev) +{ + yaffs_ExtendedTags tags; + int blk; + int blockIterator; + int startIterator; + int endIterator; + int nBlocksToScan = 0; + int result; + + int chunk; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_BlockInfo *bi; + int sequenceNumber; + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + + int alloc_failed = 0; + + + __u8 *chunkData; + + yaffs_BlockIndex *blockIndex = NULL; + + if (dev->isYaffs2) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan is not for YAFFS2!" TENDSTR))); + return YAFFS_FAIL; + } + + //TODO Throw all the yaffs2 stuuf out of yaffs_Scan since it is only for yaffs1 format. + + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan starts intstartblk %d intendblk %d..." TENDSTR), + dev->internalStartBlock, dev->internalEndBlock)); + + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + if (dev->isYaffs2) { + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); + if(!blockIndex) + return YAFFS_FAIL; + } + + /* Scan all the blocks to determine their state */ + for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { + bi = yaffs_GetBlockInfo(dev, blk); + yaffs_ClearChunkBits(dev, blk); + bi->pagesInUse = 0; + bi->softDeletions = 0; + + yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); + + bi->blockState = state; + bi->sequenceNumber = sequenceNumber; + + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + + if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block empty " TENDSTR))); + dev->nErasedBlocks++; + dev->nFreeChunks += dev->nChunksPerBlock; + } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + + /* Determine the highest sequence number */ + if (dev->isYaffs2 && + sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && + sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + + blockIndex[nBlocksToScan].seq = sequenceNumber; + blockIndex[nBlocksToScan].block = blk; + + nBlocksToScan++; + + if (sequenceNumber >= dev->sequenceNumber) { + dev->sequenceNumber = sequenceNumber; + } + } else if (dev->isYaffs2) { + /* TODO: Nasty sequence number! */ + T(YAFFS_TRACE_SCAN, + (TSTR + ("Block scanning block %d has bad sequence number %d" + TENDSTR), blk, sequenceNumber)); + + } + } + } + + /* Sort the blocks + * Dungy old bubble sort for now... + */ + if (dev->isYaffs2) { + yaffs_BlockIndex temp; + int i; + int j; + + for (i = 0; i < nBlocksToScan; i++) + for (j = i + 1; j < nBlocksToScan; j++) + if (blockIndex[i].seq > blockIndex[j].seq) { + temp = blockIndex[j]; + blockIndex[j] = blockIndex[i]; + blockIndex[i] = temp; + } + } + + /* Now scan the blocks looking at the data. */ + if (dev->isYaffs2) { + startIterator = 0; + endIterator = nBlocksToScan - 1; + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); + } else { + startIterator = dev->internalStartBlock; + endIterator = dev->internalEndBlock; + } + + /* For each block.... */ + for (blockIterator = startIterator; !alloc_failed && blockIterator <= endIterator; + blockIterator++) { + + if (dev->isYaffs2) { + /* get the block to scan in the correct order */ + blk = blockIndex[blockIterator].block; + } else { + blk = blockIterator; + } + + bi = yaffs_GetBlockInfo(dev, blk); + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning....*/ + for (c = 0; !alloc_failed && c < dev->nChunksPerBlock && + state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++) { + /* Read the tags and decide what to do */ + chunk = blk * dev->nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (!dev->isYaffs2 && tags.chunkDeleted) { + /* YAFFS1 only... + * A deleted chunk + */ + deleted++; + dev->nFreeChunks++; + /*T((" %d %d deleted\n",blk,c)); */ + } else if (!tags.chunkUsed) { + /* An unassigned chunk in the block + * This means that either the block is empty or + * this is the one being allocated from + */ + + if (c == 0) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } else { + /* this is the block being allocated from */ + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" TENDSTR), + blk, c)); + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + dev->allocationBlockFinder = blk; + /* Set it to here to encourage the allocator to go forth from here. */ + + /* Yaffs2 sanity check: + * This should be the one with the highest sequence number + */ + if (dev->isYaffs2 + && (dev->sequenceNumber != + bi->sequenceNumber)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs: Allocation block %d was not highest sequence id:" + " block seq = %d, dev seq = %d" + TENDSTR), blk,bi->sequenceNumber,dev->sequenceNumber)); + } + } + + dev->nFreeChunks += (dev->nChunksPerBlock - c); + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + YAFFS_OBJECT_TYPE_FILE); + /* PutChunkIntoFile checks for a clash (two data chunks with + * the same chunkId). + */ + + if(!in) + alloc_failed = 1; + + if(in){ + if(!yaffs_PutChunkIntoFile(in, tags.chunkId, chunk,1)) + alloc_failed = 1; + } + + endpos = + (tags.chunkId - 1) * dev->nDataBytesPerChunk + + tags.byteCount; + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE + && in->variant.fileVariant.scannedFileSize < + endpos) { + in->variant.fileVariant. + scannedFileSize = endpos; + if (!dev->useHeaderFileSize) { + in->variant.fileVariant. + fileSize = + in->variant.fileVariant. + scannedFileSize; + } + + } + /* T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId)); */ + } else { + /* chunkId == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make the object + */ + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, + chunkData, + NULL); + + oh = (yaffs_ObjectHeader *) chunkData; + + in = yaffs_FindObjectByNumber(dev, + tags.objectId); + if (in && in->variantType != oh->type) { + /* This should not happen, but somehow + * Wev'e ended up with an objectId that has been reused but not yet + * deleted, and worse still it has changed type. Delete the old object. + */ + + yaffs_DestroyObject(in); + + in = 0; + } + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + oh->type); + + if(!in) + alloc_failed = 1; + + if (in && oh->shadowsObject > 0) { + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 0); + } + + if (in && in->valid) { + /* We have already filled this one. We have a duplicate and need to resolve it. */ + + unsigned existingSerial = in->serial; + unsigned newSerial = tags.serialNumber; + + if (dev->isYaffs2 || + ((existingSerial + 1) & 3) == + newSerial) { + /* Use new one - destroy the exisiting one */ + yaffs_DeleteChunk(dev, + in->chunkId, + 1, __LINE__); + in->valid = 0; + } else { + /* Use existing - destroy this one. */ + yaffs_DeleteChunk(dev, chunk, 1, + __LINE__); + } + } + + if (in && !in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle with directory structure */ + in->valid = 1; + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + in->chunkId = chunk; + + } else if (in && !in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + in->chunkId = chunk; + + yaffs_SetObjectName(in, oh->name); + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + parent = + yaffs_FindOrCreateObjectByNumber + (dev, oh->parentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + if (parent->variantType == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variantType = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent->variant. + directoryVariant. + children); + } else if (parent->variantType != + YAFFS_OBJECT_TYPE_DIRECTORY) + { + /* Hoosterman, another problem.... + * We're trying to use a non-directory as a directory + */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: attempting to use non-directory as" + " a directory in scan. Put in lost+found." + TENDSTR))); + parent = dev->lostNFoundDir; + } + + yaffs_AddObjectToDirectory(parent, in); + + if (0 && (parent == dev->deletedDir || + parent == dev->unlinkedDir)) { + in->deleted = 1; /* If it is unlinked at start up then it wants deleting */ + dev->nDeletedFiles++; + } + /* Note re hardlinks. + * Since we might scan a hardlink before its equivalent object is scanned + * we put them all in a list. + * After scanning is complete, we should have all the objects, so we run through this + * list and fix up all the chains. + */ + + switch (in->variantType) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + if (dev->isYaffs2 + && oh->isShrink) { + /* Prune back the shrunken chunks */ + yaffs_PruneResizedChunks + (in, oh->fileSize); + /* Mark the block as having a shrinkHeader */ + bi->hasShrinkHeader = 1; + } + + if (dev->useHeaderFileSize) + + in->variant.fileVariant. + fileSize = + oh->fileSize; + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + in->variant.hardLinkVariant. + equivalentObjectId = + oh->equivalentObjectId; + in->hardLinks.next = + (struct list_head *) + hardList; + hardList = in; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; + break; + } + + if (parent == dev->deletedDir) { + yaffs_DestroyObject(in); + bi->hasShrinkHeader = 1; + } + } + } + } + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + /* If we got this far while scanning, then the block is fully allocated.*/ + state = YAFFS_BLOCK_STATE_FULL; + } + + bi->blockState = state; + + /* Now let's see if it was dirty */ + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { + yaffs_BlockBecameDirty(dev, blk); + } + + } + + if (blockIndex) { + YFREE(blockIndex); + } + + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + + yaffs_HardlinkFixup(dev,hardList); + + /* Handle the unlinked files. Since they were left in an unlinked state we should + * just delete them. + */ + { + struct list_head *i; + struct list_head *n; + + yaffs_Object *l; + /* Soft delete all the unlinked files */ + list_for_each_safe(i, n, + &dev->unlinkedDir->variant.directoryVariant. + children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + yaffs_DestroyObject(l); + } + } + } + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if(alloc_failed){ + return YAFFS_FAIL; + } + + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_Scan ends" TENDSTR))); + + + return YAFFS_OK; +} + +static void yaffs_CheckObjectDetailsLoaded(yaffs_Object *in) +{ + __u8 *chunkData; + yaffs_ObjectHeader *oh; + yaffs_Device *dev = in->myDev; + yaffs_ExtendedTags tags; + int result; + int alloc_failed = 0; + + if(!in) + return; + +#if 0 + T(YAFFS_TRACE_SCAN,(TSTR("details for object %d %s loaded" TENDSTR), + in->objectId, + in->lazyLoaded ? "not yet" : "already")); +#endif + + if(in->lazyLoaded){ + in->lazyLoaded = 0; + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + result = yaffs_ReadChunkWithTagsFromNAND(dev,in->chunkId,chunkData,&tags); + oh = (yaffs_ObjectHeader *) chunkData; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; + +#endif + yaffs_SetObjectName(in, oh->name); + + if(in->variantType == YAFFS_OBJECT_TYPE_SYMLINK){ + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh->alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; /* Not returned to caller */ + } + + yaffs_ReleaseTempBuffer(dev,chunkData, __LINE__); + } +} + +static int yaffs_ScanBackwards(yaffs_Device * dev) +{ + yaffs_ExtendedTags tags; + int blk; + int blockIterator; + int startIterator; + int endIterator; + int nBlocksToScan = 0; + + int chunk; + int result; + int c; + int deleted; + yaffs_BlockState state; + yaffs_Object *hardList = NULL; + yaffs_BlockInfo *bi; + int sequenceNumber; + yaffs_ObjectHeader *oh; + yaffs_Object *in; + yaffs_Object *parent; + int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1; + int itsUnlinked; + __u8 *chunkData; + + int fileSize; + int isShrink; + int foundChunksInBlock; + int equivalentObjectId; + int alloc_failed = 0; + + + yaffs_BlockIndex *blockIndex = NULL; + int altBlockIndex = 0; + + if (!dev->isYaffs2) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR))); + return YAFFS_FAIL; + } + + T(YAFFS_TRACE_SCAN, + (TSTR + ("yaffs_ScanBackwards starts intstartblk %d intendblk %d..." + TENDSTR), dev->internalStartBlock, dev->internalEndBlock)); + + + dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER; + + blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex)); + + if(!blockIndex) { + blockIndex = YMALLOC_ALT(nBlocks * sizeof(yaffs_BlockIndex)); + altBlockIndex = 1; + } + + if(!blockIndex) { + T(YAFFS_TRACE_SCAN, + (TSTR("yaffs_Scan() could not allocate block index!" TENDSTR))); + return YAFFS_FAIL; + } + + dev->blocksInCheckpoint = 0; + + chunkData = yaffs_GetTempBuffer(dev, __LINE__); + + /* Scan all the blocks to determine their state */ + for (blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++) { + bi = yaffs_GetBlockInfo(dev, blk); + yaffs_ClearChunkBits(dev, blk); + bi->pagesInUse = 0; + bi->softDeletions = 0; + + yaffs_QueryInitialBlockState(dev, blk, &state, &sequenceNumber); + + bi->blockState = state; + bi->sequenceNumber = sequenceNumber; + + if(bi->sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA) + bi->blockState = state = YAFFS_BLOCK_STATE_CHECKPOINT; + + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block scanning block %d state %d seq %d" TENDSTR), blk, + state, sequenceNumber)); + + + if(state == YAFFS_BLOCK_STATE_CHECKPOINT){ + dev->blocksInCheckpoint++; + + } else if (state == YAFFS_BLOCK_STATE_DEAD) { + T(YAFFS_TRACE_BAD_BLOCKS, + (TSTR("block %d is bad" TENDSTR), blk)); + } else if (state == YAFFS_BLOCK_STATE_EMPTY) { + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("Block empty " TENDSTR))); + dev->nErasedBlocks++; + dev->nFreeChunks += dev->nChunksPerBlock; + } else if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + + /* Determine the highest sequence number */ + if (dev->isYaffs2 && + sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER && + sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER) { + + blockIndex[nBlocksToScan].seq = sequenceNumber; + blockIndex[nBlocksToScan].block = blk; + + nBlocksToScan++; + + if (sequenceNumber >= dev->sequenceNumber) { + dev->sequenceNumber = sequenceNumber; + } + } else if (dev->isYaffs2) { + /* TODO: Nasty sequence number! */ + T(YAFFS_TRACE_SCAN, + (TSTR + ("Block scanning block %d has bad sequence number %d" + TENDSTR), blk, sequenceNumber)); + + } + } + } + + T(YAFFS_TRACE_SCAN, + (TSTR("%d blocks to be sorted..." TENDSTR), nBlocksToScan)); + + + + YYIELD(); + + /* Sort the blocks */ +#ifndef CONFIG_YAFFS_USE_OWN_SORT + yaffs_qsort(blockIndex, nBlocksToScan, + sizeof(yaffs_BlockIndex), ybicmp); +#else + { + /* Dungy old bubble sort... */ + + yaffs_BlockIndex temp; + int i; + int j; + + for (i = 0; i < nBlocksToScan; i++) + for (j = i + 1; j < nBlocksToScan; j++) + if (blockIndex[i].seq > blockIndex[j].seq) { + temp = blockIndex[j]; + blockIndex[j] = blockIndex[i]; + blockIndex[i] = temp; + } + } +#endif + + YYIELD(); + + T(YAFFS_TRACE_SCAN, (TSTR("...done" TENDSTR))); + + /* Now scan the blocks looking at the data. */ + startIterator = 0; + endIterator = nBlocksToScan - 1; + T(YAFFS_TRACE_SCAN_DEBUG, + (TSTR("%d blocks to be scanned" TENDSTR), nBlocksToScan)); + + /* For each block.... backwards */ + for (blockIterator = endIterator; !alloc_failed && blockIterator >= startIterator; + blockIterator--) { + /* Cooperative multitasking! This loop can run for so + long that watchdog timers expire. */ + YYIELD(); + + /* get the block to scan in the correct order */ + blk = blockIndex[blockIterator].block; + + bi = yaffs_GetBlockInfo(dev, blk); + + + state = bi->blockState; + + deleted = 0; + + /* For each chunk in each block that needs scanning.... */ + foundChunksInBlock = 0; + for (c = dev->nChunksPerBlock - 1; + !alloc_failed && c >= 0 && + (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING); c--) { + /* Scan backwards... + * Read the tags and decide what to do + */ + + chunk = blk * dev->nChunksPerBlock + c; + + result = yaffs_ReadChunkWithTagsFromNAND(dev, chunk, NULL, + &tags); + + /* Let's have a good look at this chunk... */ + + if (!tags.chunkUsed) { + /* An unassigned chunk in the block. + * If there are used chunks after this one, then + * it is a chunk that was skipped due to failing the erased + * check. Just skip it so that it can be deleted. + * But, more typically, We get here when this is an unallocated + * chunk and his means that either the block is empty or + * this is the one being allocated from + */ + + if(foundChunksInBlock) + { + /* This is a chunk that was skipped due to failing the erased check */ + + } else if (c == 0) { + /* We're looking at the first chunk in the block so the block is unused */ + state = YAFFS_BLOCK_STATE_EMPTY; + dev->nErasedBlocks++; + } else { + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING || + state == YAFFS_BLOCK_STATE_ALLOCATING) { + if(dev->sequenceNumber == bi->sequenceNumber) { + /* this is the block being allocated from */ + + T(YAFFS_TRACE_SCAN, + (TSTR + (" Allocating from %d %d" + TENDSTR), blk, c)); + + state = YAFFS_BLOCK_STATE_ALLOCATING; + dev->allocationBlock = blk; + dev->allocationPage = c; + dev->allocationBlockFinder = blk; + } + else { + /* This is a partially written block that is not + * the current allocation block. This block must have + * had a write failure, so set up for retirement. + */ + + bi->needsRetiring = 1; + bi->gcPrioritise = 1; + + T(YAFFS_TRACE_ALWAYS, + (TSTR("Partially written block %d being set for retirement" TENDSTR), + blk)); + } + + } + + } + + dev->nFreeChunks++; + + } else if (tags.chunkId > 0) { + /* chunkId > 0 so it is a data chunk... */ + unsigned int endpos; + __u32 chunkBase = + (tags.chunkId - 1) * dev->nDataBytesPerChunk; + + foundChunksInBlock = 1; + + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + in = yaffs_FindOrCreateObjectByNumber(dev, + tags. + objectId, + YAFFS_OBJECT_TYPE_FILE); + if(!in){ + /* Out of memory */ + alloc_failed = 1; + } + + if (in && + in->variantType == YAFFS_OBJECT_TYPE_FILE + && chunkBase < + in->variant.fileVariant.shrinkSize) { + /* This has not been invalidated by a resize */ + if(!yaffs_PutChunkIntoFile(in, tags.chunkId, + chunk, -1)){ + alloc_failed = 1; + } + + /* File size is calculated by looking at the data chunks if we have not + * seen an object header yet. Stop this practice once we find an object header. + */ + endpos = + (tags.chunkId - + 1) * dev->nDataBytesPerChunk + + tags.byteCount; + + if (!in->valid && /* have not got an object header yet */ + in->variant.fileVariant. + scannedFileSize < endpos) { + in->variant.fileVariant. + scannedFileSize = endpos; + in->variant.fileVariant. + fileSize = + in->variant.fileVariant. + scannedFileSize; + } + + } else if(in) { + /* This chunk has been invalidated by a resize, so delete */ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + + } + } else { + /* chunkId == 0, so it is an ObjectHeader. + * Thus, we read in the object header and make the object + */ + foundChunksInBlock = 1; + + yaffs_SetChunkBit(dev, blk, c); + bi->pagesInUse++; + + oh = NULL; + in = NULL; + + if (tags.extraHeaderInfoAvailable) { + in = yaffs_FindOrCreateObjectByNumber + (dev, tags.objectId, + tags.extraObjectType); + } + + if (!in || +#ifdef CONFIG_YAFFS_DISABLE_LAZY_LOAD + !in->valid || +#endif + tags.extraShadows || + (!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == YAFFS_OBJECTID_LOSTNFOUND)) + ) { + + /* If we don't have valid info then we need to read the chunk + * TODO In future we can probably defer reading the chunk and + * living with invalid data until needed. + */ + + result = yaffs_ReadChunkWithTagsFromNAND(dev, + chunk, + chunkData, + NULL); + + oh = (yaffs_ObjectHeader *) chunkData; + + if (!in) + in = yaffs_FindOrCreateObjectByNumber(dev, tags.objectId, oh->type); + + } + + if (!in) { + /* TODO Hoosterman we have a problem! */ + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: Could not make object for object %d " + "at chunk %d during scan" + TENDSTR), tags.objectId, chunk)); + + } + + if (in->valid) { + /* We have already filled this one. + * We have a duplicate that will be discarded, but + * we first have to suck out resize info if it is a file. + */ + + if ((in->variantType == YAFFS_OBJECT_TYPE_FILE) && + ((oh && + oh-> type == YAFFS_OBJECT_TYPE_FILE)|| + (tags.extraHeaderInfoAvailable && + tags.extraObjectType == YAFFS_OBJECT_TYPE_FILE)) + ) { + __u32 thisSize = + (oh) ? oh->fileSize : tags. + extraFileLength; + __u32 parentObjectId = + (oh) ? oh-> + parentObjectId : tags. + extraParentObjectId; + unsigned isShrink = + (oh) ? oh->isShrink : tags. + extraIsShrinkHeader; + + /* If it is deleted (unlinked at start also means deleted) + * we treat the file size as being zeroed at this point. + */ + if (parentObjectId == + YAFFS_OBJECTID_DELETED + || parentObjectId == + YAFFS_OBJECTID_UNLINKED) { + thisSize = 0; + isShrink = 1; + } + + if (isShrink && + in->variant.fileVariant. + shrinkSize > thisSize) { + in->variant.fileVariant. + shrinkSize = + thisSize; + } + + if (isShrink) { + bi->hasShrinkHeader = 1; + } + + } + /* Use existing - destroy this one. */ + yaffs_DeleteChunk(dev, chunk, 1, __LINE__); + + } + + if (!in->valid && + (tags.objectId == YAFFS_OBJECTID_ROOT || + tags.objectId == + YAFFS_OBJECTID_LOSTNFOUND)) { + /* We only load some info, don't fiddle with directory structure */ + in->valid = 1; + + if(oh) { + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; + +#endif + } else { + in->variantType = tags.extraObjectType; + in->lazyLoaded = 1; + } + + in->chunkId = chunk; + + } else if (!in->valid) { + /* we need to load this info */ + + in->valid = 1; + in->chunkId = chunk; + + if(oh) { + in->variantType = oh->type; + + in->yst_mode = oh->yst_mode; +#ifdef CONFIG_YAFFS_WINCE + in->win_atime[0] = oh->win_atime[0]; + in->win_ctime[0] = oh->win_ctime[0]; + in->win_mtime[0] = oh->win_mtime[0]; + in->win_atime[1] = oh->win_atime[1]; + in->win_ctime[1] = oh->win_ctime[1]; + in->win_mtime[1] = oh->win_mtime[1]; +#else + in->yst_uid = oh->yst_uid; + in->yst_gid = oh->yst_gid; + in->yst_atime = oh->yst_atime; + in->yst_mtime = oh->yst_mtime; + in->yst_ctime = oh->yst_ctime; + in->yst_rdev = oh->yst_rdev; +#endif + + if (oh->shadowsObject > 0) + yaffs_HandleShadowedObject(dev, + oh-> + shadowsObject, + 1); + + + yaffs_SetObjectName(in, oh->name); + parent = + yaffs_FindOrCreateObjectByNumber + (dev, oh->parentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + + fileSize = oh->fileSize; + isShrink = oh->isShrink; + equivalentObjectId = oh->equivalentObjectId; + + } + else { + in->variantType = tags.extraObjectType; + parent = + yaffs_FindOrCreateObjectByNumber + (dev, tags.extraParentObjectId, + YAFFS_OBJECT_TYPE_DIRECTORY); + fileSize = tags.extraFileLength; + isShrink = tags.extraIsShrinkHeader; + equivalentObjectId = tags.extraEquivalentObjectId; + in->lazyLoaded = 1; + + } + in->dirty = 0; + + /* directory stuff... + * hook up to parent + */ + + if (parent->variantType == + YAFFS_OBJECT_TYPE_UNKNOWN) { + /* Set up as a directory */ + parent->variantType = + YAFFS_OBJECT_TYPE_DIRECTORY; + INIT_LIST_HEAD(&parent->variant. + directoryVariant. + children); + } else if (parent->variantType != + YAFFS_OBJECT_TYPE_DIRECTORY) + { + /* Hoosterman, another problem.... + * We're trying to use a non-directory as a directory + */ + + T(YAFFS_TRACE_ERROR, + (TSTR + ("yaffs tragedy: attempting to use non-directory as" + " a directory in scan. Put in lost+found." + TENDSTR))); + parent = dev->lostNFoundDir; + } + + yaffs_AddObjectToDirectory(parent, in); + + itsUnlinked = (parent == dev->deletedDir) || + (parent == dev->unlinkedDir); + + if (isShrink) { + /* Mark the block as having a shrinkHeader */ + bi->hasShrinkHeader = 1; + } + + /* Note re hardlinks. + * Since we might scan a hardlink before its equivalent object is scanned + * we put them all in a list. + * After scanning is complete, we should have all the objects, so we run + * through this list and fix up all the chains. + */ + + switch (in->variantType) { + case YAFFS_OBJECT_TYPE_UNKNOWN: + /* Todo got a problem */ + break; + case YAFFS_OBJECT_TYPE_FILE: + + if (in->variant.fileVariant. + scannedFileSize < fileSize) { + /* This covers the case where the file size is greater + * than where the data is + * This will happen if the file is resized to be larger + * than its current data extents. + */ + in->variant.fileVariant.fileSize = fileSize; + in->variant.fileVariant.scannedFileSize = + in->variant.fileVariant.fileSize; + } + + if (isShrink && + in->variant.fileVariant.shrinkSize > fileSize) { + in->variant.fileVariant.shrinkSize = fileSize; + } + + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + if(!itsUnlinked) { + in->variant.hardLinkVariant.equivalentObjectId = + equivalentObjectId; + in->hardLinks.next = + (struct list_head *) hardList; + hardList = in; + } + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + /* Do nothing */ + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + if(oh){ + in->variant.symLinkVariant.alias = + yaffs_CloneString(oh-> + alias); + if(!in->variant.symLinkVariant.alias) + alloc_failed = 1; + } + break; + } + + } + + } + + } /* End of scanning for each chunk */ + + if (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING) { + /* If we got this far while scanning, then the block is fully allocated. */ + state = YAFFS_BLOCK_STATE_FULL; + } + + bi->blockState = state; + + /* Now let's see if it was dirty */ + if (bi->pagesInUse == 0 && + !bi->hasShrinkHeader && + bi->blockState == YAFFS_BLOCK_STATE_FULL) { + yaffs_BlockBecameDirty(dev, blk); + } + + } + + if (altBlockIndex) + YFREE_ALT(blockIndex); + else + YFREE(blockIndex); + + /* Ok, we've done all the scanning. + * Fix up the hard link chains. + * We should now have scanned all the objects, now it's time to add these + * hardlinks. + */ + yaffs_HardlinkFixup(dev,hardList); + + + /* + * Sort out state of unlinked and deleted objects. + */ + { + struct list_head *i; + struct list_head *n; + + yaffs_Object *l; + + /* Soft delete all the unlinked files */ + list_for_each_safe(i, n, + &dev->unlinkedDir->variant.directoryVariant. + children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + yaffs_DestroyObject(l); + } + } + + /* Soft delete all the deletedDir files */ + list_for_each_safe(i, n, + &dev->deletedDir->variant.directoryVariant. + children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + yaffs_DestroyObject(l); + + } + } + } + + yaffs_ReleaseTempBuffer(dev, chunkData, __LINE__); + + if(alloc_failed){ + return YAFFS_FAIL; + } + + T(YAFFS_TRACE_SCAN, (TSTR("yaffs_ScanBackwards ends" TENDSTR))); + + return YAFFS_OK; +} + +/*------------------------------ Directory Functions ----------------------------- */ + +static void yaffs_RemoveObjectFromDirectory(yaffs_Object * obj) +{ + yaffs_Device *dev = obj->myDev; + + if(dev && dev->removeObjectCallback) + dev->removeObjectCallback(obj); + + list_del_init(&obj->siblings); + obj->parent = NULL; +} + + +static void yaffs_AddObjectToDirectory(yaffs_Object * directory, + yaffs_Object * obj) +{ + + if (!directory) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: Trying to add an object to a null pointer directory" + TENDSTR))); + YBUG(); + } + if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: Trying to add an object to a non-directory" + TENDSTR))); + YBUG(); + } + + if (obj->siblings.prev == NULL) { + /* Not initialised */ + INIT_LIST_HEAD(&obj->siblings); + + } else if (!list_empty(&obj->siblings)) { + /* If it is holed up somewhere else, un hook it */ + yaffs_RemoveObjectFromDirectory(obj); + } + /* Now add it */ + list_add(&obj->siblings, &directory->variant.directoryVariant.children); + obj->parent = directory; + + if (directory == obj->myDev->unlinkedDir + || directory == obj->myDev->deletedDir) { + obj->unlinked = 1; + obj->myDev->nUnlinkedFiles++; + obj->renameAllowed = 0; + } +} + +yaffs_Object *yaffs_FindObjectByName(yaffs_Object * directory, + const YCHAR * name) +{ + int sum; + + struct list_head *i; + YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; + + yaffs_Object *l; + + if (!name) { + return NULL; + } + + if (!directory) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: null pointer directory" + TENDSTR))); + YBUG(); + } + if (directory->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); + YBUG(); + } + + sum = yaffs_CalcNameSum(name); + + list_for_each(i, &directory->variant.directoryVariant.children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + + yaffs_CheckObjectDetailsLoaded(l); + + /* Special case for lost-n-found */ + if (l->objectId == YAFFS_OBJECTID_LOSTNFOUND) { + if (yaffs_strcmp(name, YAFFS_LOSTNFOUND_NAME) == 0) { + return l; + } + } else if (yaffs_SumCompare(l->sum, sum) || l->chunkId <= 0) + { + /* LostnFound cunk called Objxxx + * Do a real check + */ + yaffs_GetObjectName(l, buffer, + YAFFS_MAX_NAME_LENGTH); + if (yaffs_strncmp(name, buffer,YAFFS_MAX_NAME_LENGTH) == 0) { + return l; + } + + } + } + } + + return NULL; +} + + +#if 0 +int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, + int (*fn) (yaffs_Object *)) +{ + struct list_head *i; + yaffs_Object *l; + + if (!theDir) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: null pointer directory" + TENDSTR))); + YBUG(); + } + if (theDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("tragedy: yaffs_FindObjectByName: non-directory" TENDSTR))); + YBUG(); + } + + list_for_each(i, &theDir->variant.directoryVariant.children) { + if (i) { + l = list_entry(i, yaffs_Object, siblings); + if (l && !fn(l)) { + return YAFFS_FAIL; + } + } + } + + return YAFFS_OK; + +} +#endif + +/* GetEquivalentObject dereferences any hard links to get to the + * actual object. + */ + +yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj) +{ + if (obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK) { + /* We want the object id of the equivalent object, not this one */ + obj = obj->variant.hardLinkVariant.equivalentObject; + yaffs_CheckObjectDetailsLoaded(obj); + } + return obj; + +} + +int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize) +{ + memset(name, 0, buffSize * sizeof(YCHAR)); + + yaffs_CheckObjectDetailsLoaded(obj); + + if (obj->objectId == YAFFS_OBJECTID_LOSTNFOUND) { + yaffs_strncpy(name, YAFFS_LOSTNFOUND_NAME, buffSize - 1); + } else if (obj->chunkId <= 0) { + YCHAR locName[20]; + /* make up a name */ + yaffs_sprintf(locName, _Y("%s%d"), YAFFS_LOSTNFOUND_PREFIX, + obj->objectId); + yaffs_strncpy(name, locName, buffSize - 1); + + } +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + else if (obj->shortName[0]) { + yaffs_strcpy(name, obj->shortName); + } +#endif + else { + int result; + __u8 *buffer = yaffs_GetTempBuffer(obj->myDev, __LINE__); + + yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *) buffer; + + memset(buffer, 0, obj->myDev->nDataBytesPerChunk); + + if (obj->chunkId >= 0) { + result = yaffs_ReadChunkWithTagsFromNAND(obj->myDev, + obj->chunkId, buffer, + NULL); + } + yaffs_strncpy(name, oh->name, buffSize - 1); + + yaffs_ReleaseTempBuffer(obj->myDev, buffer, __LINE__); + } + + return yaffs_strlen(name); +} + +int yaffs_GetObjectFileLength(yaffs_Object * obj) +{ + + /* Dereference any hard linking */ + obj = yaffs_GetEquivalentObject(obj); + + if (obj->variantType == YAFFS_OBJECT_TYPE_FILE) { + return obj->variant.fileVariant.fileSize; + } + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { + return yaffs_strlen(obj->variant.symLinkVariant.alias); + } else { + /* Only a directory should drop through to here */ + return obj->myDev->nDataBytesPerChunk; + } +} + +int yaffs_GetObjectLinkCount(yaffs_Object * obj) +{ + int count = 0; + struct list_head *i; + + if (!obj->unlinked) { + count++; /* the object itself */ + } + list_for_each(i, &obj->hardLinks) { + count++; /* add the hard links; */ + } + return count; + +} + +int yaffs_GetObjectInode(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + return obj->objectId; +} + +unsigned yaffs_GetObjectType(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + + switch (obj->variantType) { + case YAFFS_OBJECT_TYPE_FILE: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + return DT_DIR; + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + return DT_LNK; + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + return DT_REG; + break; + case YAFFS_OBJECT_TYPE_SPECIAL: + if (S_ISFIFO(obj->yst_mode)) + return DT_FIFO; + if (S_ISCHR(obj->yst_mode)) + return DT_CHR; + if (S_ISBLK(obj->yst_mode)) + return DT_BLK; + if (S_ISSOCK(obj->yst_mode)) + return DT_SOCK; + default: + return DT_REG; + break; + } +} + +YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj) +{ + obj = yaffs_GetEquivalentObject(obj); + if (obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK) { + return yaffs_CloneString(obj->variant.symLinkVariant.alias); + } else { + return yaffs_CloneString(_Y("")); + } +} + +#ifndef CONFIG_YAFFS_WINCE + +int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr) +{ + unsigned int valid = attr->ia_valid; + + if (valid & ATTR_MODE) + obj->yst_mode = attr->ia_mode; + if (valid & ATTR_UID) + obj->yst_uid = attr->ia_uid; + if (valid & ATTR_GID) + obj->yst_gid = attr->ia_gid; + + if (valid & ATTR_ATIME) + obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); + if (valid & ATTR_CTIME) + obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); + if (valid & ATTR_MTIME) + obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); + + if (valid & ATTR_SIZE) + yaffs_ResizeFile(obj, attr->ia_size); + + yaffs_UpdateObjectHeader(obj, NULL, 1, 0, 0); + + return YAFFS_OK; + +} +int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr) +{ + unsigned int valid = 0; + + attr->ia_mode = obj->yst_mode; + valid |= ATTR_MODE; + attr->ia_uid = obj->yst_uid; + valid |= ATTR_UID; + attr->ia_gid = obj->yst_gid; + valid |= ATTR_GID; + + Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; + valid |= ATTR_ATIME; + Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; + valid |= ATTR_CTIME; + Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; + valid |= ATTR_MTIME; + + attr->ia_size = yaffs_GetFileSize(obj); + valid |= ATTR_SIZE; + + attr->ia_valid = valid; + + return YAFFS_OK; + +} + +#endif + +#if 0 +int yaffs_DumpObject(yaffs_Object * obj) +{ + YCHAR name[257]; + + yaffs_GetObjectName(obj, name, 256); + + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d" + " chunk %d type %d size %d\n" + TENDSTR), obj->objectId, yaffs_GetObjectInode(obj), name, + obj->dirty, obj->valid, obj->serial, obj->sum, obj->chunkId, + yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj))); + + return YAFFS_OK; +} +#endif + +/*---------------------------- Initialisation code -------------------------------------- */ + +static int yaffs_CheckDevFunctions(const yaffs_Device * dev) +{ + + /* Common functions, gotta have */ + if (!dev->eraseBlockInNAND || !dev->initialiseNAND) + return 0; + +#ifdef CONFIG_YAFFS_YAFFS2 + + /* Can use the "with tags" style interface for yaffs1 or yaffs2 */ + if (dev->writeChunkWithTagsToNAND && + dev->readChunkWithTagsFromNAND && + !dev->writeChunkToNAND && + !dev->readChunkFromNAND && + dev->markNANDBlockBad && dev->queryNANDBlock) + return 1; +#endif + + /* Can use the "spare" style interface for yaffs1 */ + if (!dev->isYaffs2 && + !dev->writeChunkWithTagsToNAND && + !dev->readChunkWithTagsFromNAND && + dev->writeChunkToNAND && + dev->readChunkFromNAND && + !dev->markNANDBlockBad && !dev->queryNANDBlock) + return 1; + + return 0; /* bad */ +} + + +static int yaffs_CreateInitialDirectories(yaffs_Device *dev) +{ + /* Initialise the unlinked, deleted, root and lost and found directories */ + + dev->lostNFoundDir = dev->rootDir = NULL; + dev->unlinkedDir = dev->deletedDir = NULL; + + dev->unlinkedDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); + + dev->deletedDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); + + dev->rootDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_ROOT, + YAFFS_ROOT_MODE | S_IFDIR); + dev->lostNFoundDir = + yaffs_CreateFakeDirectory(dev, YAFFS_OBJECTID_LOSTNFOUND, + YAFFS_LOSTNFOUND_MODE | S_IFDIR); + + if(dev->lostNFoundDir && dev->rootDir && dev->unlinkedDir && dev->deletedDir){ + yaffs_AddObjectToDirectory(dev->rootDir, dev->lostNFoundDir); + return YAFFS_OK; + } + + return YAFFS_FAIL; +} + +int yaffs_GutsInitialise(yaffs_Device * dev) +{ + int init_failed = 0; + unsigned x; + int bits; + + T(YAFFS_TRACE_TRACING, (TSTR("yaffs: yaffs_GutsInitialise()" TENDSTR))); + + /* Check stuff that must be set */ + + if (!dev) { + T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs: Need a device" TENDSTR))); + return YAFFS_FAIL; + } + + dev->internalStartBlock = dev->startBlock; + dev->internalEndBlock = dev->endBlock; + dev->blockOffset = 0; + dev->chunkOffset = 0; + dev->nFreeChunks = 0; + + if (dev->startBlock == 0) { + dev->internalStartBlock = dev->startBlock + 1; + dev->internalEndBlock = dev->endBlock + 1; + dev->blockOffset = 1; + dev->chunkOffset = dev->nChunksPerBlock; + } + + /* Check geometry parameters. */ + + if ((dev->isYaffs2 && dev->nDataBytesPerChunk < 1024) || + (!dev->isYaffs2 && dev->nDataBytesPerChunk != 512) || + dev->nChunksPerBlock < 2 || + dev->nReservedBlocks < 2 || + dev->internalStartBlock <= 0 || + dev->internalEndBlock <= 0 || + dev->internalEndBlock <= (dev->internalStartBlock + dev->nReservedBlocks + 2) // otherwise it is too small + ) { + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs: NAND geometry problems: chunk size %d, type is yaffs%s " + TENDSTR), dev->nDataBytesPerChunk, dev->isYaffs2 ? "2" : "")); + return YAFFS_FAIL; + } + + if (yaffs_InitialiseNAND(dev) != YAFFS_OK) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: InitialiseNAND failed" TENDSTR))); + return YAFFS_FAIL; + } + + /* Got the right mix of functions? */ + if (!yaffs_CheckDevFunctions(dev)) { + /* Function missing */ + T(YAFFS_TRACE_ALWAYS, + (TSTR + ("yaffs: device function(s) missing or wrong\n" TENDSTR))); + + return YAFFS_FAIL; + } + + /* This is really a compilation check. */ + if (!yaffs_CheckStructures()) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs_CheckStructures failed\n" TENDSTR))); + return YAFFS_FAIL; + } + + if (dev->isMounted) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: device already mounted\n" TENDSTR))); + return YAFFS_FAIL; + } + + /* Finished with most checks. One or two more checks happen later on too. */ + + dev->isMounted = 1; + + + + /* OK now calculate a few things for the device */ + + /* + * Calculate all the chunk size manipulation numbers: + */ + /* Start off assuming it is a power of 2 */ + dev->chunkShift = ShiftDiv(dev->nDataBytesPerChunk); + dev->chunkMask = (1<chunkShift) - 1; + + if(dev->nDataBytesPerChunk == (dev->chunkMask + 1)){ + /* Yes it is a power of 2, disable crumbs */ + dev->crumbMask = 0; + dev->crumbShift = 0; + dev->crumbsPerChunk = 0; + } else { + /* Not a power of 2, use crumbs instead */ + dev->crumbShift = ShiftDiv(sizeof(yaffs_PackedTags2TagsPart)); + dev->crumbMask = (1<crumbShift)-1; + dev->crumbsPerChunk = dev->nDataBytesPerChunk/(1 << dev->crumbShift); + dev->chunkShift = 0; + dev->chunkMask = 0; + } + + + /* + * Calculate chunkGroupBits. + * We need to find the next power of 2 > than internalEndBlock + */ + + x = dev->nChunksPerBlock * (dev->internalEndBlock + 1); + + bits = ShiftsGE(x); + + /* Set up tnode width if wide tnodes are enabled. */ + if(!dev->wideTnodesDisabled){ + /* bits must be even so that we end up with 32-bit words */ + if(bits & 1) + bits++; + if(bits < 16) + dev->tnodeWidth = 16; + else + dev->tnodeWidth = bits; + } + else + dev->tnodeWidth = 16; + + dev->tnodeMask = (1<tnodeWidth)-1; + + /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), + * so if the bitwidth of the + * chunk range we're using is greater than 16 we need + * to figure out chunk shift and chunkGroupSize + */ + + if (bits <= dev->tnodeWidth) + dev->chunkGroupBits = 0; + else + dev->chunkGroupBits = bits - dev->tnodeWidth; + + + dev->chunkGroupSize = 1 << dev->chunkGroupBits; + + if (dev->nChunksPerBlock < dev->chunkGroupSize) { + /* We have a problem because the soft delete won't work if + * the chunk group size > chunks per block. + * This can be remedied by using larger "virtual blocks". + */ + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: chunk group too large\n" TENDSTR))); + + return YAFFS_FAIL; + } + + /* OK, we've finished verifying the device, lets continue with initialisation */ + + /* More device initialisation */ + dev->garbageCollections = 0; + dev->passiveGarbageCollections = 0; + dev->currentDirtyChecker = 0; + dev->bufferedBlock = -1; + dev->doingBufferedBlockRewrite = 0; + dev->nDeletedFiles = 0; + dev->nBackgroundDeletions = 0; + dev->nUnlinkedFiles = 0; + dev->eccFixed = 0; + dev->eccUnfixed = 0; + dev->tagsEccFixed = 0; + dev->tagsEccUnfixed = 0; + dev->nErasureFailures = 0; + dev->nErasedBlocks = 0; + dev->isDoingGC = 0; + dev->hasPendingPrioritisedGCs = 1; /* Assume the worst for now, will get fixed on first GC */ + + /* Initialise temporary buffers and caches. */ + if(!yaffs_InitialiseTempBuffers(dev)) + init_failed = 1; + + dev->srCache = NULL; + dev->gcCleanupList = NULL; + + + if (!init_failed && + dev->nShortOpCaches > 0) { + int i; + __u8 *buf; + int srCacheBytes = dev->nShortOpCaches * sizeof(yaffs_ChunkCache); + + if (dev->nShortOpCaches > YAFFS_MAX_SHORT_OP_CACHES) { + dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES; + } + + buf = dev->srCache = YMALLOC(srCacheBytes); + + if(dev->srCache) + memset(dev->srCache,0,srCacheBytes); + + for (i = 0; i < dev->nShortOpCaches && buf; i++) { + dev->srCache[i].object = NULL; + dev->srCache[i].lastUse = 0; + dev->srCache[i].dirty = 0; + dev->srCache[i].data = buf = YMALLOC_DMA(dev->nDataBytesPerChunk); + } + if(!buf) + init_failed = 1; + + dev->srLastUse = 0; + } + + dev->cacheHits = 0; + + if(!init_failed){ + dev->gcCleanupList = YMALLOC(dev->nChunksPerBlock * sizeof(__u32)); + if(!dev->gcCleanupList) + init_failed = 1; + } + + if (dev->isYaffs2) { + dev->useHeaderFileSize = 1; + } + if(!init_failed && !yaffs_InitialiseBlocks(dev)) + init_failed = 1; + + yaffs_InitialiseTnodes(dev); + yaffs_InitialiseObjects(dev); + + if(!init_failed && !yaffs_CreateInitialDirectories(dev)) + init_failed = 1; + + + if(!init_failed){ + /* Now scan the flash. */ + if (dev->isYaffs2) { + if(yaffs_CheckpointRestore(dev)) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("yaffs: restored from checkpoint" TENDSTR))); + } else { + + /* Clean up the mess caused by an aborted checkpoint load + * and scan backwards. + */ + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(dev); + + + dev->nErasedBlocks = 0; + dev->nFreeChunks = 0; + dev->allocationBlock = -1; + dev->allocationPage = -1; + dev->nDeletedFiles = 0; + dev->nUnlinkedFiles = 0; + dev->nBackgroundDeletions = 0; + dev->oldestDirtySequence = 0; + + if(!init_failed && !yaffs_InitialiseBlocks(dev)) + init_failed = 1; + + yaffs_InitialiseTnodes(dev); + yaffs_InitialiseObjects(dev); + + if(!init_failed && !yaffs_CreateInitialDirectories(dev)) + init_failed = 1; + + if(!init_failed && !yaffs_ScanBackwards(dev)) + init_failed = 1; + } + }else + if(!yaffs_Scan(dev)) + init_failed = 1; + } + + if(init_failed){ + /* Clean up the mess */ + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: yaffs_GutsInitialise() aborted.\n" TENDSTR))); + + yaffs_Deinitialise(dev); + return YAFFS_FAIL; + } + + /* Zero out stats */ + dev->nPageReads = 0; + dev->nPageWrites = 0; + dev->nBlockErasures = 0; + dev->nGCCopies = 0; + dev->nRetriedWrites = 0; + + dev->nRetiredBlocks = 0; + + yaffs_VerifyFreeChunks(dev); + yaffs_VerifyBlocks(dev); + + + T(YAFFS_TRACE_TRACING, + (TSTR("yaffs: yaffs_GutsInitialise() done.\n" TENDSTR))); + return YAFFS_OK; + +} + +void yaffs_Deinitialise(yaffs_Device * dev) +{ + if (dev->isMounted) { + int i; + + yaffs_DeinitialiseBlocks(dev); + yaffs_DeinitialiseTnodes(dev); + yaffs_DeinitialiseObjects(dev); + if (dev->nShortOpCaches > 0 && + dev->srCache) { + + for (i = 0; i < dev->nShortOpCaches; i++) { + if(dev->srCache[i].data) + YFREE(dev->srCache[i].data); + dev->srCache[i].data = NULL; + } + + YFREE(dev->srCache); + dev->srCache = NULL; + } + + YFREE(dev->gcCleanupList); + + for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { + YFREE(dev->tempBuffer[i].buffer); + } + + dev->isMounted = 0; + } + +} + +static int yaffs_CountFreeChunks(yaffs_Device * dev) +{ + int nFree; + int b; + + yaffs_BlockInfo *blk; + + for (nFree = 0, b = dev->internalStartBlock; b <= dev->internalEndBlock; + b++) { + blk = yaffs_GetBlockInfo(dev, b); + + switch (blk->blockState) { + case YAFFS_BLOCK_STATE_EMPTY: + case YAFFS_BLOCK_STATE_ALLOCATING: + case YAFFS_BLOCK_STATE_COLLECTING: + case YAFFS_BLOCK_STATE_FULL: + nFree += + (dev->nChunksPerBlock - blk->pagesInUse + + blk->softDeletions); + break; + default: + break; + } + + } + + return nFree; +} + +int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev) +{ + /* This is what we report to the outside world */ + + int nFree; + int nDirtyCacheChunks; + int blocksForCheckpoint; + +#if 1 + nFree = dev->nFreeChunks; +#else + nFree = yaffs_CountFreeChunks(dev); +#endif + + nFree += dev->nDeletedFiles; + + /* Now count the number of dirty chunks in the cache and subtract those */ + + { + int i; + for (nDirtyCacheChunks = 0, i = 0; i < dev->nShortOpCaches; i++) { + if (dev->srCache[i].dirty) + nDirtyCacheChunks++; + } + } + + nFree -= nDirtyCacheChunks; + + nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock); + + /* Now we figure out how much to reserve for the checkpoint and report that... */ + blocksForCheckpoint = dev->nCheckpointReservedBlocks - dev->blocksInCheckpoint; + if(blocksForCheckpoint < 0) + blocksForCheckpoint = 0; + + nFree -= (blocksForCheckpoint * dev->nChunksPerBlock); + + if (nFree < 0) + nFree = 0; + + return nFree; + +} + +static int yaffs_freeVerificationFailures; + +static void yaffs_VerifyFreeChunks(yaffs_Device * dev) +{ + int counted; + int difference; + + if(yaffs_SkipVerification(dev)) + return; + + counted = yaffs_CountFreeChunks(dev); + + difference = dev->nFreeChunks - counted; + + if (difference) { + T(YAFFS_TRACE_ALWAYS, + (TSTR("Freechunks verification failure %d %d %d" TENDSTR), + dev->nFreeChunks, counted, difference)); + yaffs_freeVerificationFailures++; + } +} + +/*---------------------------------------- YAFFS test code ----------------------*/ + +#define yaffs_CheckStruct(structure,syze, name) \ + if(sizeof(structure) != syze) \ + { \ + T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),\ + name,syze,sizeof(structure))); \ + return YAFFS_FAIL; \ + } + +static int yaffs_CheckStructures(void) +{ +/* yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags") */ +/* yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion") */ +/* yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare") */ +#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG + yaffs_CheckStruct(yaffs_Tnode, 2 * YAFFS_NTNODES_LEVEL0, "yaffs_Tnode") +#endif + yaffs_CheckStruct(yaffs_ObjectHeader, 512, "yaffs_ObjectHeader") + + return YAFFS_OK; +} diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_guts.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_guts.h new file mode 100644 index 0000000000..ea06c1a38e --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_guts.h @@ -0,0 +1,902 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_GUTS_H__ +#define __YAFFS_GUTS_H__ + +#include "devextras.h" +#include "yportenv.h" + +#define YAFFS_OK 1 +#define YAFFS_FAIL 0 + +/* Give us a Y=0x59, + * Give us an A=0x41, + * Give us an FF=0xFF + * Give us an S=0x53 + * And what have we got... + */ +#define YAFFS_MAGIC 0x5941FF53 + +#define YAFFS_NTNODES_LEVEL0 16 +#define YAFFS_TNODES_LEVEL0_BITS 4 +#define YAFFS_TNODES_LEVEL0_MASK 0xf + +#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) +#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) +#define YAFFS_TNODES_INTERNAL_MASK 0x7 +#define YAFFS_TNODES_MAX_LEVEL 6 + +#ifndef CONFIG_YAFFS_NO_YAFFS1 +#define YAFFS_BYTES_PER_SPARE 16 +#define YAFFS_BYTES_PER_CHUNK 512 +#define YAFFS_CHUNK_SIZE_SHIFT 9 +#define YAFFS_CHUNKS_PER_BLOCK 32 +#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) +#endif + +#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 +#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 + +#define YAFFS_MAX_CHUNK_ID 0x000FFFFF + +#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF + +#define YAFFS_ALLOCATION_NOBJECTS 100 +#define YAFFS_ALLOCATION_NTNODES 100 +#define YAFFS_ALLOCATION_NLINKS 100 + +#define YAFFS_NOBJECT_BUCKETS 256 + + +#define YAFFS_OBJECT_SPACE 0x40000 + +#define YAFFS_CHECKPOINT_VERSION 3 + +#ifdef CONFIG_YAFFS_UNICODE +#define YAFFS_MAX_NAME_LENGTH 127 +#define YAFFS_MAX_ALIAS_LENGTH 79 +#else +#define YAFFS_MAX_NAME_LENGTH 255 +#define YAFFS_MAX_ALIAS_LENGTH 159 +#endif + +#define YAFFS_SHORT_NAME_LENGTH 15 + +/* Some special object ids for pseudo objects */ +#define YAFFS_OBJECTID_ROOT 1 +#define YAFFS_OBJECTID_LOSTNFOUND 2 +#define YAFFS_OBJECTID_UNLINKED 3 +#define YAFFS_OBJECTID_DELETED 4 + +/* Sseudo object ids for checkpointing */ +#define YAFFS_OBJECTID_SB_HEADER 0x10 +#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 +#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 + +/* */ + +#define YAFFS_MAX_SHORT_OP_CACHES 20 + +#define YAFFS_N_TEMP_BUFFERS 4 + +/* We limit the number attempts at sucessfully saving a chunk of data. + * Small-page devices have 32 pages per block; large-page devices have 64. + * Default to something in the order of 5 to 10 blocks worth of chunks. + */ +#define YAFFS_WR_ATTEMPTS (5*64) + +/* Sequence numbers are used in YAFFS2 to determine block allocation order. + * The range is limited slightly to help distinguish bad numbers from good. + * This also allows us to perhaps in the future use special numbers for + * special purposes. + * EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years, + * and is a larger number than the lifetime of a 2GB device. + */ +#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 +#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00 + +/* ChunkCache is used for short read/write operations.*/ +typedef struct { + struct yaffs_ObjectStruct *object; + int chunkId; + int lastUse; + int dirty; + int nBytes; /* Only valid if the cache is dirty */ + int locked; /* Can't push out or flush while locked. */ +#ifdef CONFIG_YAFFS_YAFFS2 + __u8 *data; +#else + __u8 data[YAFFS_BYTES_PER_CHUNK]; +#endif +} yaffs_ChunkCache; + + + +/* Tags structures in RAM + * NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise + * the structure size will get blown out. + */ + +#ifndef CONFIG_YAFFS_NO_YAFFS1 +typedef struct { + unsigned chunkId:20; + unsigned serialNumber:2; + unsigned byteCount:10; + unsigned objectId:18; + unsigned ecc:12; + unsigned unusedStuff:2; + +} yaffs_Tags; + +typedef union { + yaffs_Tags asTags; + __u8 asBytes[8]; +} yaffs_TagsUnion; + +#endif + +/* Stuff used for extended tags in YAFFS2 */ + +typedef enum { + YAFFS_ECC_RESULT_UNKNOWN, + YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_ECC_RESULT_FIXED, + YAFFS_ECC_RESULT_UNFIXED +} yaffs_ECCResult; + +typedef enum { + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +} yaffs_ObjectType; + +#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL + +typedef struct { + + unsigned validMarker0; + unsigned chunkUsed; /* Status of the chunk: used or unused */ + unsigned objectId; /* If 0 then this is not part of an object (unused) */ + unsigned chunkId; /* If 0 then this is a header, else a data chunk */ + unsigned byteCount; /* Only valid for data chunks */ + + /* The following stuff only has meaning when we read */ + yaffs_ECCResult eccResult; + unsigned blockBad; + + /* YAFFS 1 stuff */ + unsigned chunkDeleted; /* The chunk is marked deleted */ + unsigned serialNumber; /* Yaffs1 2-bit serial number */ + + /* YAFFS2 stuff */ + unsigned sequenceNumber; /* The sequence number of this block */ + + /* Extra info if this is an object header (YAFFS2 only) */ + + unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */ + unsigned extraParentObjectId; /* The parent object */ + unsigned extraIsShrinkHeader; /* Is it a shrink header? */ + unsigned extraShadows; /* Does this shadow another object? */ + + yaffs_ObjectType extraObjectType; /* What object type? */ + + unsigned extraFileLength; /* Length if it is a file */ + unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */ + + unsigned validMarker1; + +} yaffs_ExtendedTags; + +/* Spare structure for YAFFS1 */ +typedef struct { + __u8 tagByte0; + __u8 tagByte1; + __u8 tagByte2; + __u8 tagByte3; + __u8 pageStatus; /* set to 0 to delete the chunk */ + __u8 blockStatus; + __u8 tagByte4; + __u8 tagByte5; + __u8 ecc1[3]; + __u8 tagByte6; + __u8 tagByte7; + __u8 ecc2[3]; +} yaffs_Spare; + +/*Special structure for passing through to mtd */ +struct yaffs_NANDSpare { + yaffs_Spare spare; + int eccres1; + int eccres2; +}; + +/* Block data in RAM */ + +typedef enum { + YAFFS_BLOCK_STATE_UNKNOWN = 0, + + YAFFS_BLOCK_STATE_SCANNING, + YAFFS_BLOCK_STATE_NEEDS_SCANNING, + /* The block might have something on it (ie it is allocating or full, perhaps empty) + * but it needs to be scanned to determine its true state. + * This state is only valid during yaffs_Scan. + * NB We tolerate empty because the pre-scanner might be incapable of deciding + * However, if this state is returned on a YAFFS2 device, then we expect a sequence number + */ + + YAFFS_BLOCK_STATE_EMPTY, + /* This block is empty */ + + YAFFS_BLOCK_STATE_ALLOCATING, + /* This block is partially allocated. + * At least one page holds valid data. + * This is the one currently being used for page + * allocation. Should never be more than one of these + */ + + YAFFS_BLOCK_STATE_FULL, + /* All the pages in this block have been allocated. + */ + + YAFFS_BLOCK_STATE_DIRTY, + /* All pages have been allocated and deleted. + * Erase me, reuse me. + */ + + YAFFS_BLOCK_STATE_CHECKPOINT, + /* This block is assigned to holding checkpoint data. + */ + + YAFFS_BLOCK_STATE_COLLECTING, + /* This block is being garbage collected */ + + YAFFS_BLOCK_STATE_DEAD + /* This block has failed and is not in use */ +} yaffs_BlockState; + +#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) + + +typedef struct { + + int softDeletions:10; /* number of soft deleted pages */ + int pagesInUse:10; /* number of pages in use */ + yaffs_BlockState blockState:4; /* One of the above block states */ + __u32 needsRetiring:1; /* Data has failed on this block, need to get valid data off */ + /* and retire the block. */ + __u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */ + __u32 gcPrioritise: 1; /* An ECC check or blank check has failed on this block. + It should be prioritised for GC */ + __u32 chunkErrorStrikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */ + +#ifdef CONFIG_YAFFS_YAFFS2 + __u32 hasShrinkHeader:1; /* This block has at least one shrink object header */ + __u32 sequenceNumber; /* block sequence number for yaffs2 */ +#endif + +} yaffs_BlockInfo; + +/* -------------------------- Object structure -------------------------------*/ +/* This is the object structure as stored on NAND */ + +typedef struct { + yaffs_ObjectType type; + + /* Apply to everything */ + int parentObjectId; + __u16 sum__NoLongerUsed; /* checksum of name. No longer used */ + YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; + + /* Thes following apply to directories, files, symlinks - not hard links */ + __u32 yst_mode; /* protection */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 notForWinCE[5]; +#else + __u32 yst_uid; + __u32 yst_gid; + __u32 yst_atime; + __u32 yst_mtime; + __u32 yst_ctime; +#endif + + /* File size applies to files only */ + int fileSize; + + /* Equivalent object id applies to hard links only. */ + int equivalentObjectId; + + /* Alias is for symlinks only. */ + YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + __u32 yst_rdev; /* device stuff for block and char devices (major/min) */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 win_ctime[2]; + __u32 win_atime[2]; + __u32 win_mtime[2]; + __u32 roomToGrow[4]; +#else + __u32 roomToGrow[10]; +#endif + + int shadowsObject; /* This object header shadows the specified object if > 0 */ + + /* isShrink applies to object headers written when we shrink the file (ie resize) */ + __u32 isShrink; + +} yaffs_ObjectHeader; + +/*--------------------------- Tnode -------------------------- */ + +union yaffs_Tnode_union { +#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG + union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL + 1]; +#else + union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL]; +#endif +/* __u16 level0[YAFFS_NTNODES_LEVEL0]; */ + +}; + +typedef union yaffs_Tnode_union yaffs_Tnode; + +struct yaffs_TnodeList_struct { + struct yaffs_TnodeList_struct *next; + yaffs_Tnode *tnodes; +}; + +typedef struct yaffs_TnodeList_struct yaffs_TnodeList; + +/*------------------------ Object -----------------------------*/ +/* An object can be one of: + * - a directory (no data, has children links + * - a regular file (data.... not prunes :->). + * - a symlink [symbolic link] (the alias). + * - a hard link + */ + +typedef struct { + __u32 fileSize; + __u32 scannedFileSize; + __u32 shrinkSize; + int topLevel; + yaffs_Tnode *top; +} yaffs_FileStructure; + +typedef struct { + struct list_head children; /* list of child links */ +} yaffs_DirectoryStructure; + +typedef struct { + YCHAR *alias; +} yaffs_SymLinkStructure; + +typedef struct { + struct yaffs_ObjectStruct *equivalentObject; + __u32 equivalentObjectId; +} yaffs_HardLinkStructure; + +typedef union { + yaffs_FileStructure fileVariant; + yaffs_DirectoryStructure directoryVariant; + yaffs_SymLinkStructure symLinkVariant; + yaffs_HardLinkStructure hardLinkVariant; +} yaffs_ObjectVariant; + +struct yaffs_ObjectStruct { + __u8 deleted:1; /* This should only apply to unlinked files. */ + __u8 softDeleted:1; /* it has also been soft deleted */ + __u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory.*/ + __u8 fake:1; /* A fake object has no presence on NAND. */ + __u8 renameAllowed:1; /* Some objects are not allowed to be renamed. */ + __u8 unlinkAllowed:1; + __u8 dirty:1; /* the object needs to be written to flash */ + __u8 valid:1; /* When the file system is being loaded up, this + * object might be created before the data + * is available (ie. file data records appear before the header). + */ + __u8 lazyLoaded:1; /* This object has been lazy loaded and is missing some detail */ + + __u8 deferedFree:1; /* For Linux kernel. Object is removed from NAND, but is + * still in the inode cache. Free of object is defered. + * until the inode is released. + */ + + __u8 serial; /* serial number of chunk in NAND. Cached here */ + __u16 sum; /* sum of the name to speed searching */ + + struct yaffs_DeviceStruct *myDev; /* The device I'm on */ + + struct list_head hashLink; /* list of objects in this hash bucket */ + + struct list_head hardLinks; /* all the equivalent hard linked objects */ + + /* directory structure stuff */ + /* also used for linking up the free list */ + struct yaffs_ObjectStruct *parent; + struct list_head siblings; + + /* Where's my object header in NAND? */ + int chunkId; + + int nDataChunks; /* Number of data chunks attached to the file. */ + + __u32 objectId; /* the object id value */ + + __u32 yst_mode; + +#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM + YCHAR shortName[YAFFS_SHORT_NAME_LENGTH + 1]; +#endif + +#ifndef __KERNEL__ + __u32 inUse; +#endif + +#ifdef CONFIG_YAFFS_WINCE + __u32 win_ctime[2]; + __u32 win_mtime[2]; + __u32 win_atime[2]; +#else + __u32 yst_uid; + __u32 yst_gid; + __u32 yst_atime; + __u32 yst_mtime; + __u32 yst_ctime; +#endif + + __u32 yst_rdev; + +#ifdef __KERNEL__ + struct inode *myInode; + +#endif + + yaffs_ObjectType variantType; + + yaffs_ObjectVariant variant; + +}; + +typedef struct yaffs_ObjectStruct yaffs_Object; + +struct yaffs_ObjectList_struct { + yaffs_Object *objects; + struct yaffs_ObjectList_struct *next; +}; + +typedef struct yaffs_ObjectList_struct yaffs_ObjectList; + +typedef struct { + struct list_head list; + int count; +} yaffs_ObjectBucket; + + +/* yaffs_CheckpointObject holds the definition of an object as dumped + * by checkpointing. + */ + +typedef struct { + int structType; + __u32 objectId; + __u32 parentId; + int chunkId; + + yaffs_ObjectType variantType:3; + __u8 deleted:1; + __u8 softDeleted:1; + __u8 unlinked:1; + __u8 fake:1; + __u8 renameAllowed:1; + __u8 unlinkAllowed:1; + __u8 serial; + + int nDataChunks; + __u32 fileSizeOrEquivalentObjectId; + +}yaffs_CheckpointObject; + +/*--------------------- Temporary buffers ---------------- + * + * These are chunk-sized working buffers. Each device has a few + */ + +typedef struct { + __u8 *buffer; + int line; /* track from whence this buffer was allocated */ + int maxLine; +} yaffs_TempBuffer; + +/*----------------- Device ---------------------------------*/ + +struct yaffs_DeviceStruct { + struct list_head devList; + const char *name; + + /* Entry parameters set up way early. Yaffs sets up the rest.*/ + int nDataBytesPerChunk; /* Should be a power of 2 >= 512 */ + int nChunksPerBlock; /* does not need to be a power of 2 */ + int nBytesPerSpare; /* spare area size */ + int startBlock; /* Start block we're allowed to use */ + int endBlock; /* End block we're allowed to use */ + int nReservedBlocks; /* We want this tuneable so that we can reduce */ + /* reserved blocks on NOR and RAM. */ + + + /* Stuff used by the shared space checkpointing mechanism */ + /* If this value is zero, then this mechanism is disabled */ + + int nCheckpointReservedBlocks; /* Blocks to reserve for checkpoint data */ + + + + + int nShortOpCaches; /* If <= 0, then short op caching is disabled, else + * the number of short op caches (don't use too many) + */ + + int useHeaderFileSize; /* Flag to determine if we should use file sizes from the header */ + + int useNANDECC; /* Flag to decide whether or not to use NANDECC */ + + void *genericDevice; /* Pointer to device context + * On an mtd this holds the mtd pointer. + */ + void *superBlock; + + /* NAND access functions (Must be set before calling YAFFS)*/ + + int (*writeChunkToNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, const __u8 * data, + const yaffs_Spare * spare); + int (*readChunkFromNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, __u8 * data, + yaffs_Spare * spare); + int (*eraseBlockInNAND) (struct yaffs_DeviceStruct * dev, + int blockInNAND); + int (*initialiseNAND) (struct yaffs_DeviceStruct * dev); + +#ifdef CONFIG_YAFFS_YAFFS2 + int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, const __u8 * data, + const yaffs_ExtendedTags * tags); + int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev, + int chunkInNAND, __u8 * data, + yaffs_ExtendedTags * tags); + int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo); + int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); +#endif + + int isYaffs2; + + /* The removeObjectCallback function must be supplied by OS flavours that + * need it. The Linux kernel does not use this, but yaffs direct does use + * it to implement the faster readdir + */ + void (*removeObjectCallback)(struct yaffs_ObjectStruct *obj); + + /* Callback to mark the superblock dirsty */ + void (*markSuperBlockDirty)(void * superblock); + + int wideTnodesDisabled; /* Set to disable wide tnodes */ + + + /* End of stuff that must be set before initialisation. */ + + /* Checkpoint control. Can be set before or after initialisation */ + __u8 skipCheckpointRead; + __u8 skipCheckpointWrite; + + /* Runtime parameters. Set up by YAFFS. */ + + __u16 chunkGroupBits; /* 0 for devices <= 32MB. else log2(nchunks) - 16 */ + __u16 chunkGroupSize; /* == 2^^chunkGroupBits */ + + /* Stuff to support wide tnodes */ + __u32 tnodeWidth; + __u32 tnodeMask; + + /* Stuff to support various file offses to chunk/offset translations */ + /* "Crumbs" for nDataBytesPerChunk not being a power of 2 */ + __u32 crumbMask; + __u32 crumbShift; + __u32 crumbsPerChunk; + + /* Straight shifting for nDataBytesPerChunk being a power of 2 */ + __u32 chunkShift; + __u32 chunkMask; + + +#ifdef __KERNEL__ + + struct semaphore sem; /* Semaphore for waiting on erasure.*/ + struct semaphore grossLock; /* Gross locking semaphore */ + __u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer + * at compile time so we have to allocate it. + */ + void (*putSuperFunc) (struct super_block * sb); +#endif + + int isMounted; + + int isCheckpointed; + + + /* Stuff to support block offsetting to support start block zero */ + int internalStartBlock; + int internalEndBlock; + int blockOffset; + int chunkOffset; + + + /* Runtime checkpointing stuff */ + int checkpointPageSequence; /* running sequence number of checkpoint pages */ + int checkpointByteCount; + int checkpointByteOffset; + __u8 *checkpointBuffer; + int checkpointOpenForWrite; + int blocksInCheckpoint; + int checkpointCurrentChunk; + int checkpointCurrentBlock; + int checkpointNextBlock; + int *checkpointBlockList; + int checkpointMaxBlocks; + __u32 checkpointSum; + __u32 checkpointXor; + + /* Block Info */ + yaffs_BlockInfo *blockInfo; + __u8 *chunkBits; /* bitmap of chunks in use */ + unsigned blockInfoAlt:1; /* was allocated using alternative strategy */ + unsigned chunkBitsAlt:1; /* was allocated using alternative strategy */ + int chunkBitmapStride; /* Number of bytes of chunkBits per block. + * Must be consistent with nChunksPerBlock. + */ + + int nErasedBlocks; + int allocationBlock; /* Current block being allocated off */ + __u32 allocationPage; + int allocationBlockFinder; /* Used to search for next allocation block */ + + /* Runtime state */ + int nTnodesCreated; + yaffs_Tnode *freeTnodes; + int nFreeTnodes; + yaffs_TnodeList *allocatedTnodeList; + + int isDoingGC; + + int nObjectsCreated; + yaffs_Object *freeObjects; + int nFreeObjects; + + yaffs_ObjectList *allocatedObjectList; + + yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS]; + + int nFreeChunks; + + int currentDirtyChecker; /* Used to find current dirtiest block */ + + __u32 *gcCleanupList; /* objects to delete at the end of a GC. */ + int nonAggressiveSkip; /* GC state/mode */ + + /* Statistcs */ + int nPageWrites; + int nPageReads; + int nBlockErasures; + int nErasureFailures; + int nGCCopies; + int garbageCollections; + int passiveGarbageCollections; + int nRetriedWrites; + int nRetiredBlocks; + int eccFixed; + int eccUnfixed; + int tagsEccFixed; + int tagsEccUnfixed; + int nDeletions; + int nUnmarkedDeletions; + + int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */ + + /* Special directories */ + yaffs_Object *rootDir; + yaffs_Object *lostNFoundDir; + + /* Buffer areas for storing data to recover from write failures TODO + * __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK]; + * yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK]; + */ + + int bufferedBlock; /* Which block is buffered here? */ + int doingBufferedBlockRewrite; + + yaffs_ChunkCache *srCache; + int srLastUse; + + int cacheHits; + + /* Stuff for background deletion and unlinked files.*/ + yaffs_Object *unlinkedDir; /* Directory where unlinked and deleted files live. */ + yaffs_Object *deletedDir; /* Directory where deleted objects are sent to disappear. */ + yaffs_Object *unlinkedDeletion; /* Current file being background deleted.*/ + int nDeletedFiles; /* Count of files awaiting deletion;*/ + int nUnlinkedFiles; /* Count of unlinked files. */ + int nBackgroundDeletions; /* Count of background deletions. */ + + + yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS]; + int maxTemp; + int unmanagedTempAllocations; + int unmanagedTempDeallocations; + + /* yaffs2 runtime stuff */ + unsigned sequenceNumber; /* Sequence number of currently allocating block */ + unsigned oldestDirtySequence; + +}; + +typedef struct yaffs_DeviceStruct yaffs_Device; + +/* The static layout of bllock usage etc is stored in the super block header */ +typedef struct { + int StructType; + int version; + int checkpointStartBlock; + int checkpointEndBlock; + int startBlock; + int endBlock; + int rfu[100]; +} yaffs_SuperBlockHeader; + +/* The CheckpointDevice structure holds the device information that changes at runtime and + * must be preserved over unmount/mount cycles. + */ +typedef struct { + int structType; + int nErasedBlocks; + int allocationBlock; /* Current block being allocated off */ + __u32 allocationPage; + int nFreeChunks; + + int nDeletedFiles; /* Count of files awaiting deletion;*/ + int nUnlinkedFiles; /* Count of unlinked files. */ + int nBackgroundDeletions; /* Count of background deletions. */ + + /* yaffs2 runtime stuff */ + unsigned sequenceNumber; /* Sequence number of currently allocating block */ + unsigned oldestDirtySequence; + +} yaffs_CheckpointDevice; + + +typedef struct { + int structType; + __u32 magic; + __u32 version; + __u32 head; +} yaffs_CheckpointValidity; + +/* Function to manipulate block info */ +static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk) +{ + if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR), + blk)); + YBUG(); + } + return &dev->blockInfo[blk - dev->internalStartBlock]; +} + +/*----------------------- YAFFS Functions -----------------------*/ + +int yaffs_GutsInitialise(yaffs_Device * dev); +void yaffs_Deinitialise(yaffs_Device * dev); + +int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev); + +int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName, + yaffs_Object * newDir, const YCHAR * newName); + +int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name); +int yaffs_DeleteFile(yaffs_Object * obj); + +int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize); +int yaffs_GetObjectFileLength(yaffs_Object * obj); +int yaffs_GetObjectInode(yaffs_Object * obj); +unsigned yaffs_GetObjectType(yaffs_Object * obj); +int yaffs_GetObjectLinkCount(yaffs_Object * obj); + +int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr); +int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr); + +/* File operations */ +int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, loff_t offset, + int nBytes); +int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, loff_t offset, + int nBytes, int writeThrough); +int yaffs_ResizeFile(yaffs_Object * obj, loff_t newSize); + +yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid); +int yaffs_FlushFile(yaffs_Object * obj, int updateTime); + +/* Flushing and checkpointing */ +void yaffs_FlushEntireDeviceCache(yaffs_Device *dev); + +int yaffs_CheckpointSave(yaffs_Device *dev); +int yaffs_CheckpointRestore(yaffs_Device *dev); + +/* Directory operations */ +yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid); +yaffs_Object *yaffs_FindObjectByName(yaffs_Object * theDir, const YCHAR * name); +int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir, + int (*fn) (yaffs_Object *)); + +yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number); + +/* Link operations */ +yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name, + yaffs_Object * equivalentObject); + +yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj); + +/* Symlink operations */ +yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, + const YCHAR * alias); +YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj); + +/* Special inodes (fifos, sockets and devices) */ +yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name, + __u32 mode, __u32 uid, __u32 gid, __u32 rdev); + +/* Special directories */ +yaffs_Object *yaffs_Root(yaffs_Device * dev); +yaffs_Object *yaffs_LostNFound(yaffs_Device * dev); + +#ifdef CONFIG_YAFFS_WINCE +/* CONFIG_YAFFS_WINCE special stuff */ +void yfsd_WinFileTimeNow(__u32 target[2]); +#endif + +#ifdef __KERNEL__ + +void yaffs_HandleDeferedFree(yaffs_Object * obj); +#endif + +/* Debug dump */ +int yaffs_DumpObject(yaffs_Object * obj); + +void yaffs_GutsTest(yaffs_Device * dev); + +/* A few useful functions */ +void yaffs_InitialiseTags(yaffs_ExtendedTags * tags); +void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn); +int yaffs_CheckFF(__u8 * buffer, int nBytes); +void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif.c new file mode 100644 index 0000000000..466e5a44c6 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif.c @@ -0,0 +1,241 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +const char *yaffs_mtdif_c_version = + "$Id: yaffs_mtdif.c,v 1.19 2007-02-14 01:09:06 wookey Exp $"; + +#include "yportenv.h" + + +#include "yaffs_mtdif.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" +#include "linux/mtd/nand.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) +static struct nand_oobinfo yaffs_oobinfo = { + .useecc = 1, + .eccbytes = 6, + .eccpos = {8, 9, 10, 13, 14, 15} +}; + +static struct nand_oobinfo yaffs_noeccinfo = { + .useecc = 0, +}; +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) +static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob) +{ + oob[0] = spare->tagByte0; + oob[1] = spare->tagByte1; + oob[2] = spare->tagByte2; + oob[3] = spare->tagByte3; + oob[4] = spare->tagByte4; + oob[5] = spare->tagByte5 & 0x3f; + oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80; + oob[5] |= spare->pageStatus == 0 ? 0: 0x40; + oob[6] = spare->tagByte6; + oob[7] = spare->tagByte7; +} + +static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob) +{ + struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare; + spare->tagByte0 = oob[0]; + spare->tagByte1 = oob[1]; + spare->tagByte2 = oob[2]; + spare->tagByte3 = oob[3]; + spare->tagByte4 = oob[4]; + spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f; + spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y'; + spare->pageStatus = oob[5] & 0x40 ? 0xff : 0; + spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff; + spare->tagByte6 = oob[6]; + spare->tagByte7 = oob[7]; + spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff; + + nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */ +} +#endif + +int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, const yaffs_Spare * spare) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#endif + size_t dummy; + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + __u8 spareAsBytes[8]; /* OOB */ + + if (data && !spare) + retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data); + else if (spare) { + if (dev->useNANDECC) { + translate_spare2oob(spare, spareAsBytes); + ops.mode = MTD_OOB_AUTO; + ops.ooblen = 8; /* temp hack */ + } else { + ops.mode = MTD_OOB_RAW; + ops.ooblen = YAFFS_BYTES_PER_SPARE; + } + ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; + ops.datbuf = (u8 *)data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; + retval = mtd->write_oob(mtd, addr, &ops); + } +#else + __u8 *spareAsBytes = (__u8 *) spare; + + if (data && spare) { + if (dev->useNANDECC) + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_oobinfo); + else + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_noeccinfo); + } else { + if (data) + retval = + mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (spare) + retval = + mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, + &dummy, spareAsBytes); + } +#endif + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, + yaffs_Spare * spare) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#endif + size_t dummy; + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + __u8 spareAsBytes[8]; /* OOB */ + + if (data && !spare) + retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data); + else if (spare) { + if (dev->useNANDECC) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = 8; /* temp hack */ + } else { + ops.mode = MTD_OOB_RAW; + ops.ooblen = YAFFS_BYTES_PER_SPARE; + } + ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; + ops.datbuf = data; + ops.ooboffs = 0; + ops.oobbuf = spareAsBytes; + retval = mtd->read_oob(mtd, addr, &ops); + if (dev->useNANDECC) + translate_oob2spare(spare, spareAsBytes); + } +#else + __u8 *spareAsBytes = (__u8 *) spare; + + if (data && spare) { + if (dev->useNANDECC) { + /* Careful, this call adds 2 ints */ + /* to the end of the spare data. Calling function */ + /* should allocate enough memory for spare, */ + /* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */ + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_oobinfo); + } else { + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, spareAsBytes, + &yaffs_noeccinfo); + } + } else { + if (data) + retval = + mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (spare) + retval = + mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, + &dummy, spareAsBytes); + } +#endif + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + __u32 addr = + ((loff_t) blockNumber) * dev->nDataBytesPerChunk + * dev->nChunksPerBlock; + struct erase_info ei; + int retval = 0; + + ei.mtd = mtd; + ei.addr = addr; + ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock; + ei.time = 1000; + ei.retries = 2; + ei.callback = NULL; + ei.priv = (u_long) dev; + + /* Todo finish off the ei if required */ + + sema_init(&dev->sem, 0); + + retval = mtd->erase(mtd, &ei); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd_InitialiseNAND(yaffs_Device * dev) +{ + return YAFFS_OK; +} + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif.h new file mode 100644 index 0000000000..317600cac9 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif.h @@ -0,0 +1,27 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF_H__ +#define __YAFFS_MTDIF_H__ + +#include "yaffs_guts.h" + +int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, const yaffs_Spare * spare); +int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, + yaffs_Spare * spare); +int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber); +int nandmtd_InitialiseNAND(yaffs_Device * dev); +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1-compat.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1-compat.c new file mode 100644 index 0000000000..6a376f1f72 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1-compat.c @@ -0,0 +1,434 @@ +From ian@brightstareng.com Fri May 18 15:06:49 2007 +From ian@brightstareng.com Fri May 18 15:08:21 2007 +Received: from 206.173.66.57.ptr.us.xo.net ([206.173.66.57] helo=zebra.brightstareng.com) + by apollo.linkchoose.co.uk with esmtp (Exim 4.60) + (envelope-from ) + id 1Hp380-00011e-T6 + for david.goodenough@linkchoose.co.uk; Fri, 18 May 2007 15:08:21 +0100 +Received: from localhost (localhost.localdomain [127.0.0.1]) + by zebra.brightstareng.com (Postfix) with ESMTP + id 4819F28C004; Fri, 18 May 2007 10:07:49 -0400 (EDT) +Received: from zebra.brightstareng.com ([127.0.0.1]) + by localhost (zebra [127.0.0.1]) (amavisd-new, port 10024) with ESMTP + id 05328-06; Fri, 18 May 2007 10:07:16 -0400 (EDT) +Received: from pippin (unknown [192.168.1.25]) + by zebra.brightstareng.com (Postfix) with ESMTP + id 8BEF528C1BC; Fri, 18 May 2007 10:06:53 -0400 (EDT) +From: Ian McDonnell +To: David Goodenough +Subject: Re: something tested this time -- yaffs_mtdif1-compat.c +Date: Fri, 18 May 2007 10:06:49 -0400 +User-Agent: KMail/1.9.1 +References: <200705142207.06909.ian@brightstareng.com> <200705171131.53536.ian@brightstareng.com> <200705181334.32166.david.goodenough@linkchoose.co.uk> +In-Reply-To: <200705181334.32166.david.goodenough@linkchoose.co.uk> +Cc: Andrea Conti , + Charles Manning +MIME-Version: 1.0 +Content-Type: Multipart/Mixed; + boundary="Boundary-00=_5LbTGmt62YoutxM" +Message-Id: <200705181006.49860.ian@brightstareng.com> +X-Virus-Scanned: by amavisd-new at brightstareng.com +Status: R +X-Status: NT +X-KMail-EncryptionState: +X-KMail-SignatureState: +X-KMail-MDN-Sent: + +--Boundary-00=_5LbTGmt62YoutxM +Content-Type: text/plain; + charset="iso-8859-15" +Content-Transfer-Encoding: 7bit +Content-Disposition: inline + +David, Andrea, + +On Friday 18 May 2007 08:34, you wrote: +> Yea team. With this fix in place (I put it in the wrong place +> at first) I can now mount and ls the Yaffs partition without +> an error messages! + +Good news! + +Attached is a newer yaffs_mtdif1.c with a bandaid to help the +2.6.18 and 2.6.19 versions of MTD not trip on the oob read. +See the LINUX_VERSION_CODE conditional in +nandmtd1_ReadChunkWithTagsFromNAND. + +-imcd + +--Boundary-00=_5LbTGmt62YoutxM +Content-Type: text/x-csrc; + charset="iso-8859-15"; + name="yaffs_mtdif1.c" +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; + filename="yaffs_mtdif1.c" + +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. + * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. + * + * Copyright (C) 2002 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This module provides the interface between yaffs_nand.c and the + * MTD API. This version is used when the MTD interface supports the + * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, + * and we have small-page NAND device. + * + * These functions are invoked via function pointers in yaffs_nand.c. + * This replaces functionality provided by functions in yaffs_mtdif.c + * and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are + * called in yaffs_mtdif.c when the function pointers are NULL. + * We assume the MTD layer is performing ECC (useNANDECC is true). + */ + +#include "yportenv.h" +#include "yaffs_guts.h" +#include "yaffs_packedtags1.h" +#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC + +#include "linux/kernel.h" +#include "linux/version.h" +#include "linux/types.h" +#include "linux/mtd/mtd.h" + +/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + +const char *yaffs_mtdif1_c_version = "$Id$"; + +#ifndef CONFIG_YAFFS_9BYTE_TAGS +# define YTAG1_SIZE 8 +#else +# define YTAG1_SIZE 9 +#endif + +#if 0 +/* Use the following nand_ecclayout with MTD when using + * CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. + * If you have existing Yaffs images and the byte order differs from this, + * adjust 'oobfree' to match your existing Yaffs data. + * + * This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the + * pageStatus byte (at NAND spare offset 4) scattered/gathered from/to + * the 9th byte. + * + * Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 + * We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P + * where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus + * byte and B is the small-page bad-block indicator byte. + */ +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +}; +#endif + +/* Write a chunk (page) of data to NAND. + * + * Caller always provides ExtendedTags data which are converted to a more + * compact (packed) form for storage in NAND. A mini-ECC runs over the + * contents of the tags meta-data; used to valid the tags when read. + * + * - Pack ExtendedTags to PackedTags1 form + * - Compute mini-ECC for PackedTags1 + * - Write data and packed tags to NAND. + * + * Note: Due to the use of the PackedTags1 meta-data which does not include + * a full sequence number (as found in the larger PackedTags2 form) it is + * necessary for Yaffs to re-write a chunk/page (just once) to mark it as + * discarded and dirty. This is not ideal: newer NAND parts are supposed + * to be written just once. When Yaffs performs this operation, this + * function is called with a NULL data pointer -- calling MTD write_oob + * without data is valid usage (2.6.17). + * + * Any underlying MTD error results in YAFFS_FAIL. + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, + int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + + /* we assume that PackedTags1 and yaffs_Tags are compatible */ + compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); + compile_time_assertion(sizeof(yaffs_Tags) == 8); + + yaffs_PackTags1(&pt1, etags); + yaffs_CalcTagsECC((yaffs_Tags *)&pt1); + + /* When deleting a chunk, the upper layer provides only skeletal + * etags, one with chunkDeleted set. However, we need to update the + * tags, not erase them completely. So we use the NAND write property + * that only zeroed-bits stick and set tag bytes to all-ones and + * zero just the (not) deleted bit. + */ +#ifndef CONFIG_YAFFS_9BYTE_TAGS + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* clear delete status bit to indicate deleted */ + pt1.deleted = 0; + } +#else + ((__u8 *)&pt1)[8] = 0xff; + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* zero pageStatus byte to indicate deleted */ + ((__u8 *)&pt1)[8] = 0; + } +#endif + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (__u8 *)&pt1; + + retval = mtd->write_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "write_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + return retval ? YAFFS_FAIL : YAFFS_OK; +} + +/* Return with empty ExtendedTags but add eccResult. + */ +static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) +{ + if (etags) { + memset(etags, 0, sizeof(*etags)); + etags->eccResult = eccResult; + } + return retval; +} + +/* Read a chunk (page) from NAND. + * + * Caller expects ExtendedTags data to be usable even on error; that is, + * all members except eccResult and blockBad are zeroed. + * + * - Check ECC results for data (if applicable) + * - Check for blank/erased block (return empty ExtendedTags if blank) + * - Check the PackedTags1 mini-ECC (correct if necessary/possible) + * - Convert PackedTags1 to ExtendedTags + * - Update eccResult and blockBad members to refect state. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, + int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + int eccres = YAFFS_ECC_RESULT_NO_ERROR; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + int deleted; + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = data; + ops.oobbuf = (__u8 *)&pt1; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; + * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. + */ + ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +#endif + /* Read page and oob using MTD. + * Check status and determine ECC result. + */ + retval = mtd->read_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "read_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + + switch (retval) { + case 0: + /* no error */ + break; + + case -EUCLEAN: + /* MTD's ECC fixed the data */ + eccres = YAFFS_ECC_RESULT_FIXED; + dev->eccFixed++; + break; + + case -EBADMSG: + /* MTD's ECC could not fix the data */ + dev->eccUnfixed++; + /* fall into... */ + default: + rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); + etags->blockBad = (mtd->block_isbad)(mtd, addr); + return YAFFS_FAIL; + } + + /* Check for a blank/erased chunk. + */ + if (yaffs_CheckFF((__u8 *)&pt1, 8)) { + /* when blank, upper layers want eccResult to be <= NO_ERROR */ + return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); + } + +#ifndef CONFIG_YAFFS_9BYTE_TAGS + /* Read deleted status (bit) then return it to it's non-deleted + * state before performing tags mini-ECC check. pt1.deleted is + * inverted. + */ + deleted = !pt1.deleted; + pt1.deleted = 1; +#else + (void) deleted; /* not used */ +#endif + + /* Check the packed tags mini-ECC and correct if necessary/possible. + */ + retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); + switch (retval) { + case 0: + /* no tags error, use MTD result */ + break; + case 1: + /* recovered tags-ECC error */ + dev->tagsEccFixed++; + eccres = YAFFS_ECC_RESULT_FIXED; + break; + default: + /* unrecovered tags-ECC error */ + dev->tagsEccUnfixed++; + return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); + } + + /* Unpack the tags to extended form and set ECC result. + * [set shouldBeFF just to keep yaffs_UnpackTags1 happy] + */ + pt1.shouldBeFF = 0xFFFFFFFF; + yaffs_UnpackTags1(etags, &pt1); + etags->eccResult = eccres; + + /* Set deleted state. + */ +#ifndef CONFIG_YAFFS_9BYTE_TAGS + etags->chunkDeleted = deleted; +#else + etags->chunkDeleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); +#endif + return YAFFS_OK; +} + +/* Mark a block bad. + * + * This is a persistant state. + * Use of this function should be rare. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +{ + struct mtd_info * mtd = dev->genericDevice; + int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); + + retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +/* Check any MTD prerequists. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +static int nandmtd1_TestPrerequists(struct mtd_info * mtd) +{ + /* 2.6.18 has mtd->ecclayout->oobavail */ + /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ + int oobavail = mtd->ecclayout->oobavail; + + if (oobavail < YTAG1_SIZE) { + yaffs_trace(YAFFS_TRACE_ERROR, + "mtd device has only %d bytes for tags, need %d", + oobavail, YTAG1_SIZE); + return YAFFS_FAIL; + } + return YAFFS_OK; +} + +/* Query for the current state of a specific block. + * + * Examine the tags of the first chunk of the block and return the state: + * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad + * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use + * - YAFFS_BLOCK_STATE_EMPTY, the block is clean + * + * Always returns YAFFS_OK. + */ +int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * pState, int *pSequenceNumber) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkNo = blockNo * dev->nChunksPerBlock; + yaffs_ExtendedTags etags; + int state = YAFFS_BLOCK_STATE_DEAD; + int seqnum = 0; + int retval; + + /* We don't yet have a good place to test for MTD config prerequists. + * Do it here as we are called during the initial scan. + */ + if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { + return YAFFS_FAIL; + } + + retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); + if (etags.blockBad) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is marked bad", blockNo); + state = YAFFS_BLOCK_STATE_DEAD; + } + else if (etags.chunkUsed) { + state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + seqnum = etags.sequenceNumber; + } + else { + state = YAFFS_BLOCK_STATE_EMPTY; + } + + *pState = state; + *pSequenceNumber = seqnum; + + /* query always succeeds */ + return YAFFS_OK; +} + +#endif /*KERNEL_VERSION*/ + +--Boundary-00=_5LbTGmt62YoutxM-- + + + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1.c new file mode 100644 index 0000000000..36d5adec5c --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1.c @@ -0,0 +1,363 @@ +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. + * yaffs_mtdif1.c NAND mtd interface functions for small-page NAND. + * + * Copyright (C) 2002 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This module provides the interface between yaffs_nand.c and the + * MTD API. This version is used when the MTD interface supports the + * 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17, + * and we have small-page NAND device. + * + * These functions are invoked via function pointers in yaffs_nand.c. + * This replaces functionality provided by functions in yaffs_mtdif.c + * and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are + * called in yaffs_mtdif.c when the function pointers are NULL. + * We assume the MTD layer is performing ECC (useNANDECC is true). + */ + +#include "yportenv.h" +#include "yaffs_guts.h" +#include "yaffs_packedtags1.h" +#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC + +#include "linux/kernel.h" +#include "linux/version.h" +#include "linux/types.h" +#include "linux/mtd/mtd.h" + +/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + +const char *yaffs_mtdif1_c_version = "$Id: yaffs_mtdif1.c,v 1.3 2007/05/15 20:16:11 ian Exp $"; + +#ifndef CONFIG_YAFFS_9BYTE_TAGS +# define YTAG1_SIZE 8 +#else +# define YTAG1_SIZE 9 +#endif + +#if 0 +/* Use the following nand_ecclayout with MTD when using + * CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout. + * If you have existing Yaffs images and the byte order differs from this, + * adjust 'oobfree' to match your existing Yaffs data. + * + * This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the + * pageStatus byte (at NAND spare offset 4) scattered/gathered from/to + * the 9th byte. + * + * Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5 + * We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P + * where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus + * byte and B is the small-page bad-block indicator byte. + */ +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +}; +#endif + +/* Write a chunk (page) of data to NAND. + * + * Caller always provides ExtendedTags data which are converted to a more + * compact (packed) form for storage in NAND. A mini-ECC runs over the + * contents of the tags meta-data; used to valid the tags when read. + * + * - Pack ExtendedTags to PackedTags1 form + * - Compute mini-ECC for PackedTags1 + * - Write data and packed tags to NAND. + * + * Note: Due to the use of the PackedTags1 meta-data which does not include + * a full sequence number (as found in the larger PackedTags2 form) it is + * necessary for Yaffs to re-write a chunk/page (just once) to mark it as + * discarded and dirty. This is not ideal: newer NAND parts are supposed + * to be written just once. When Yaffs performs this operation, this + * function is called with a NULL data pointer -- calling MTD write_oob + * without data is valid usage (2.6.17). + * + * Any underlying MTD error results in YAFFS_FAIL. + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev, + int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + + /* we assume that PackedTags1 and yaffs_Tags are compatible */ + compile_time_assertion(sizeof(yaffs_PackedTags1) == 12); + compile_time_assertion(sizeof(yaffs_Tags) == 8); + + dev->nPageWrites++; + + yaffs_PackTags1(&pt1, etags); + yaffs_CalcTagsECC((yaffs_Tags *)&pt1); + + /* When deleting a chunk, the upper layer provides only skeletal + * etags, one with chunkDeleted set. However, we need to update the + * tags, not erase them completely. So we use the NAND write property + * that only zeroed-bits stick and set tag bytes to all-ones and + * zero just the (not) deleted bit. + */ +#ifndef CONFIG_YAFFS_9BYTE_TAGS + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* clear delete status bit to indicate deleted */ + pt1.deleted = 0; + } +#else + ((__u8 *)&pt1)[8] = 0xff; + if (etags->chunkDeleted) { + memset(&pt1, 0xff, 8); + /* zero pageStatus byte to indicate deleted */ + ((__u8 *)&pt1)[8] = 0; + } +#endif + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (__u8 *)&pt1; + + retval = mtd->write_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "write_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + return retval ? YAFFS_FAIL : YAFFS_OK; +} + +/* Return with empty ExtendedTags but add eccResult. + */ +static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval) +{ + if (etags) { + memset(etags, 0, sizeof(*etags)); + etags->eccResult = eccResult; + } + return retval; +} + +/* Read a chunk (page) from NAND. + * + * Caller expects ExtendedTags data to be usable even on error; that is, + * all members except eccResult and blockBad are zeroed. + * + * - Check ECC results for data (if applicable) + * - Check for blank/erased block (return empty ExtendedTags if blank) + * - Check the PackedTags1 mini-ECC (correct if necessary/possible) + * - Convert PackedTags1 to ExtendedTags + * - Update eccResult and blockBad members to refect state. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev, + int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkBytes = dev->nDataBytesPerChunk; + loff_t addr = ((loff_t)chunkInNAND) * chunkBytes; + int eccres = YAFFS_ECC_RESULT_NO_ERROR; + struct mtd_oob_ops ops; + yaffs_PackedTags1 pt1; + int retval; + int deleted; + + dev->nPageReads++; + + memset(&ops, 0, sizeof(ops)); + ops.mode = MTD_OOB_AUTO; + ops.len = (data) ? chunkBytes : 0; + ops.ooblen = YTAG1_SIZE; + ops.datbuf = data; + ops.oobbuf = (__u8 *)&pt1; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) + /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; + * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. + */ + ops.len = (ops.datbuf) ? ops.len : ops.ooblen; +#endif + /* Read page and oob using MTD. + * Check status and determine ECC result. + */ + retval = mtd->read_oob(mtd, addr, &ops); + if (retval) { + yaffs_trace(YAFFS_TRACE_MTD, + "read_oob failed, chunk %d, mtd error %d\n", + chunkInNAND, retval); + } + + switch (retval) { + case 0: + /* no error */ + break; + + case -EUCLEAN: + /* MTD's ECC fixed the data */ + eccres = YAFFS_ECC_RESULT_FIXED; + dev->eccFixed++; + break; + + case -EBADMSG: + /* MTD's ECC could not fix the data */ + dev->eccUnfixed++; + /* fall into... */ + default: + rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0); + etags->blockBad = (mtd->block_isbad)(mtd, addr); + return YAFFS_FAIL; + } + + /* Check for a blank/erased chunk. + */ + if (yaffs_CheckFF((__u8 *)&pt1, 8)) { + /* when blank, upper layers want eccResult to be <= NO_ERROR */ + return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK); + } + +#ifndef CONFIG_YAFFS_9BYTE_TAGS + /* Read deleted status (bit) then return it to it's non-deleted + * state before performing tags mini-ECC check. pt1.deleted is + * inverted. + */ + deleted = !pt1.deleted; + pt1.deleted = 1; +#else + deleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7); +#endif + + /* Check the packed tags mini-ECC and correct if necessary/possible. + */ + retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1); + switch (retval) { + case 0: + /* no tags error, use MTD result */ + break; + case 1: + /* recovered tags-ECC error */ + dev->tagsEccFixed++; + if (eccres == YAFFS_ECC_RESULT_NO_ERROR) + eccres = YAFFS_ECC_RESULT_FIXED; + break; + default: + /* unrecovered tags-ECC error */ + dev->tagsEccUnfixed++; + return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL); + } + + /* Unpack the tags to extended form and set ECC result. + * [set shouldBeFF just to keep yaffs_UnpackTags1 happy] + */ + pt1.shouldBeFF = 0xFFFFFFFF; + yaffs_UnpackTags1(etags, &pt1); + etags->eccResult = eccres; + + /* Set deleted state */ + etags->chunkDeleted = deleted; + return YAFFS_OK; +} + +/* Mark a block bad. + * + * This is a persistant state. + * Use of this function should be rare. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +{ + struct mtd_info * mtd = dev->genericDevice; + int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk; + int retval; + + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo); + + retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo); + return (retval) ? YAFFS_FAIL : YAFFS_OK; +} + +/* Check any MTD prerequists. + * + * Returns YAFFS_OK or YAFFS_FAIL. + */ +static int nandmtd1_TestPrerequists(struct mtd_info * mtd) +{ + /* 2.6.18 has mtd->ecclayout->oobavail */ + /* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */ + int oobavail = mtd->ecclayout->oobavail; + + if (oobavail < YTAG1_SIZE) { + yaffs_trace(YAFFS_TRACE_ERROR, + "mtd device has only %d bytes for tags, need %d\n", + oobavail, YTAG1_SIZE); + return YAFFS_FAIL; + } + return YAFFS_OK; +} + +/* Query for the current state of a specific block. + * + * Examine the tags of the first chunk of the block and return the state: + * - YAFFS_BLOCK_STATE_DEAD, the block is marked bad + * - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use + * - YAFFS_BLOCK_STATE_EMPTY, the block is clean + * + * Always returns YAFFS_OK. + */ +int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * pState, int *pSequenceNumber) +{ + struct mtd_info * mtd = dev->genericDevice; + int chunkNo = blockNo * dev->nChunksPerBlock; + yaffs_ExtendedTags etags; + int state = YAFFS_BLOCK_STATE_DEAD; + int seqnum = 0; + int retval; + + /* We don't yet have a good place to test for MTD config prerequists. + * Do it here as we are called during the initial scan. + */ + if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) { + return YAFFS_FAIL; + } + + retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags); + if (etags.blockBad) { + yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, + "block %d is marked bad", blockNo); + state = YAFFS_BLOCK_STATE_DEAD; + } + else if (etags.chunkUsed) { + state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + seqnum = etags.sequenceNumber; + } + else { + state = YAFFS_BLOCK_STATE_EMPTY; + } + + *pState = state; + *pSequenceNumber = seqnum; + + /* query always succeeds */ + return YAFFS_OK; +} + +#endif /*KERNEL_VERSION*/ diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1.h new file mode 100644 index 0000000000..c4f6197d68 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif1.h @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF1_H__ +#define __YAFFS_MTDIF1_H__ + +int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, const yaffs_ExtendedTags * tags); + +int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * data, yaffs_ExtendedTags * tags); + +int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); + +int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif2.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif2.c new file mode 100644 index 0000000000..cdad0734bf --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif2.c @@ -0,0 +1,232 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* mtd interface for YAFFS2 */ + +const char *yaffs_mtdif2_c_version = + "$Id: yaffs_mtdif2.c,v 1.17 2007-02-14 01:09:06 wookey Exp $"; + +#include "yportenv.h" + + +#include "yaffs_mtdif2.h" + +#include "linux/mtd/mtd.h" +#include "linux/types.h" +#include "linux/time.h" + +#include "yaffs_packedtags2.h" + +int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#else + size_t dummy; +#endif + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; + + yaffs_PackedTags2 pt; + + T(YAFFS_TRACE_MTD, + (TSTR + ("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p" + TENDSTR), chunkInNAND, data, tags)); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + if (tags) + yaffs_PackTags2(&pt, tags); + else + BUG(); /* both tags and data should always be present */ + + if (data) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = dev->nDataBytesPerChunk; + ops.ooboffs = 0; + ops.datbuf = (__u8 *)data; + ops.oobbuf = (void *)&pt; + retval = mtd->write_oob(mtd, addr, &ops); + } else + BUG(); /* both tags and data should always be present */ +#else + if (tags) { + yaffs_PackTags2(&pt, tags); + } + + if (data && tags) { + if (dev->useNANDECC) + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, (__u8 *) & pt, NULL); + else + retval = + mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, (__u8 *) & pt, NULL); + } else { + if (data) + retval = + mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (tags) + retval = + mtd->write_oob(mtd, addr, mtd->oobsize, &dummy, + (__u8 *) & pt); + + } +#endif + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * data, yaffs_ExtendedTags * tags) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + struct mtd_oob_ops ops; +#endif + size_t dummy; + int retval = 0; + + loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; + + yaffs_PackedTags2 pt; + + T(YAFFS_TRACE_MTD, + (TSTR + ("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p" + TENDSTR), chunkInNAND, data, tags)); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) + if (data && !tags) + retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data); + else if (tags) { + ops.mode = MTD_OOB_AUTO; + ops.ooblen = sizeof(pt); + ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt); + ops.ooboffs = 0; + ops.datbuf = data; + ops.oobbuf = dev->spareBuffer; + retval = mtd->read_oob(mtd, addr, &ops); + } +#else + if (data && tags) { + if (dev->useNANDECC) { + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, dev->spareBuffer, + NULL); + } else { + retval = + mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, + &dummy, data, dev->spareBuffer, + NULL); + } + } else { + if (data) + retval = + mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, + data); + if (tags) + retval = + mtd->read_oob(mtd, addr, mtd->oobsize, &dummy, + dev->spareBuffer); + } +#endif + + memcpy(&pt, dev->spareBuffer, sizeof(pt)); + + if (tags) + yaffs_UnpackTags2(tags, &pt); + + if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR) + tags->eccResult = YAFFS_ECC_RESULT_UNFIXED; + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + +int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + int retval; + T(YAFFS_TRACE_MTD, + (TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo)); + + retval = + mtd->block_markbad(mtd, + blockNo * dev->nChunksPerBlock * + dev->nDataBytesPerChunk); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; + +} + +int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber) +{ + struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); + int retval; + + T(YAFFS_TRACE_MTD, + (TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo)); + retval = + mtd->block_isbad(mtd, + blockNo * dev->nChunksPerBlock * + dev->nDataBytesPerChunk); + + if (retval) { + T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR))); + + *state = YAFFS_BLOCK_STATE_DEAD; + *sequenceNumber = 0; + } else { + yaffs_ExtendedTags t; + nandmtd2_ReadChunkWithTagsFromNAND(dev, + blockNo * + dev->nChunksPerBlock, NULL, + &t); + + if (t.chunkUsed) { + *sequenceNumber = t.sequenceNumber; + *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + } else { + *sequenceNumber = 0; + *state = YAFFS_BLOCK_STATE_EMPTY; + } + } + T(YAFFS_TRACE_MTD, + (TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber, + *state)); + + if (retval == 0) + return YAFFS_OK; + else + return YAFFS_FAIL; +} + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif2.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif2.h new file mode 100644 index 0000000000..b67ba52aa4 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_mtdif2.h @@ -0,0 +1,29 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_MTDIF2_H__ +#define __YAFFS_MTDIF2_H__ + +#include "yaffs_guts.h" +int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * tags); +int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * data, yaffs_ExtendedTags * tags); +int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); +int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nand.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nand.c new file mode 100644 index 0000000000..4e250338d7 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nand.c @@ -0,0 +1,134 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +const char *yaffs_nand_c_version = + "$Id: yaffs_nand.c,v 1.7 2007-02-14 01:09:06 wookey Exp $"; + +#include "yaffs_nand.h" +#include "yaffs_tagscompat.h" +#include "yaffs_tagsvalidity.h" + + +int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * buffer, + yaffs_ExtendedTags * tags) +{ + int result; + yaffs_ExtendedTags localTags; + + int realignedChunkInNAND = chunkInNAND - dev->chunkOffset; + + /* If there are no tags provided, use local tags to get prioritised gc working */ + if(!tags) + tags = &localTags; + + if (dev->readChunkWithTagsFromNAND) + result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer, + tags); + else + result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev, + realignedChunkInNAND, + buffer, + tags); + if(tags && + tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){ + + yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, chunkInNAND/dev->nChunksPerBlock); + yaffs_HandleChunkError(dev,bi); + } + + return result; +} + +int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * buffer, + yaffs_ExtendedTags * tags) +{ + chunkInNAND -= dev->chunkOffset; + + + if (tags) { + tags->sequenceNumber = dev->sequenceNumber; + tags->chunkUsed = 1; + if (!yaffs_ValidateTags(tags)) { + T(YAFFS_TRACE_ERROR, + (TSTR("Writing uninitialised tags" TENDSTR))); + YBUG(); + } + T(YAFFS_TRACE_WRITE, + (TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND, + tags->objectId, tags->chunkId)); + } else { + T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR))); + YBUG(); + } + + if (dev->writeChunkWithTagsToNAND) + return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer, + tags); + else + return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev, + chunkInNAND, + buffer, + tags); +} + +int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo) +{ + blockNo -= dev->blockOffset; + +; + if (dev->markNANDBlockBad) + return dev->markNANDBlockBad(dev, blockNo); + else + return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo); +} + +int yaffs_QueryInitialBlockState(yaffs_Device * dev, + int blockNo, + yaffs_BlockState * state, + unsigned *sequenceNumber) +{ + blockNo -= dev->blockOffset; + + if (dev->queryNANDBlock) + return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber); + else + return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo, + state, + sequenceNumber); +} + + +int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, + int blockInNAND) +{ + int result; + + blockInNAND -= dev->blockOffset; + + + dev->nBlockErasures++; + result = dev->eraseBlockInNAND(dev, blockInNAND); + + return result; +} + +int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev) +{ + return dev->initialiseNAND(dev); +} + + + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nand.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nand.h new file mode 100644 index 0000000000..5fa334bdc9 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nand.h @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_NAND_H__ +#define __YAFFS_NAND_H__ +#include "yaffs_guts.h" + + + +int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND, + __u8 * buffer, + yaffs_ExtendedTags * tags); + +int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * buffer, + yaffs_ExtendedTags * tags); + +int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo); + +int yaffs_QueryInitialBlockState(yaffs_Device * dev, + int blockNo, + yaffs_BlockState * state, + unsigned *sequenceNumber); + +int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, + int blockInNAND); + +int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev); + +#endif + diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nandemul2k.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nandemul2k.h new file mode 100644 index 0000000000..cd2e96f7ae --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_nandemul2k.h @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* Interface to emulated NAND functions (2k page size) */ + +#ifndef __YAFFS_NANDEMUL2K_H__ +#define __YAFFS_NANDEMUL2K_H__ + +#include "yaffs_guts.h" + +int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, const __u8 * data, + yaffs_ExtendedTags * tags); +int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, __u8 * data, + yaffs_ExtendedTags * tags); +int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo); +int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo, + yaffs_BlockState * state, int *sequenceNumber); +int nandemul2k_EraseBlockInNAND(struct yaffs_DeviceStruct *dev, + int blockInNAND); +int nandemul2k_InitialiseNAND(struct yaffs_DeviceStruct *dev); +int nandemul2k_GetBytesPerChunk(void); +int nandemul2k_GetChunksPerBlock(void); +int nandemul2k_GetNumberOfBlocks(void); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags1.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags1.c new file mode 100644 index 0000000000..f480bf1df1 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags1.c @@ -0,0 +1,52 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_packedtags1.h" +#include "yportenv.h" + +void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t) +{ + pt->chunkId = t->chunkId; + pt->serialNumber = t->serialNumber; + pt->byteCount = t->byteCount; + pt->objectId = t->objectId; + pt->ecc = 0; + pt->deleted = (t->chunkDeleted) ? 0 : 1; + pt->unusedStuff = 0; + pt->shouldBeFF = 0xFFFFFFFF; + +} + +void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt) +{ + static const __u8 allFF[] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff }; + + if (memcmp(allFF, pt, sizeof(yaffs_PackedTags1))) { + t->blockBad = 0; + if (pt->shouldBeFF != 0xFFFFFFFF) { + t->blockBad = 1; + } + t->chunkUsed = 1; + t->objectId = pt->objectId; + t->chunkId = pt->chunkId; + t->byteCount = pt->byteCount; + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + t->chunkDeleted = (pt->deleted) ? 0 : 1; + t->serialNumber = pt->serialNumber; + } else { + memset(t, 0, sizeof(yaffs_ExtendedTags)); + + } +} diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags1.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags1.h new file mode 100644 index 0000000000..776c5c256c --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags1.h @@ -0,0 +1,37 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ + +#ifndef __YAFFS_PACKEDTAGS1_H__ +#define __YAFFS_PACKEDTAGS1_H__ + +#include "yaffs_guts.h" + +typedef struct { + unsigned chunkId:20; + unsigned serialNumber:2; + unsigned byteCount:10; + unsigned objectId:18; + unsigned ecc:12; + unsigned deleted:1; + unsigned unusedStuff:1; + unsigned shouldBeFF; + +} yaffs_PackedTags1; + +void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t); +void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt); +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags2.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags2.c new file mode 100644 index 0000000000..e420f95da1 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags2.c @@ -0,0 +1,182 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_packedtags2.h" +#include "yportenv.h" +#include "yaffs_tagsvalidity.h" + +/* This code packs a set of extended tags into a binary structure for + * NAND storage + */ + +/* Some of the information is "extra" struff which can be packed in to + * speed scanning + * This is defined by having the EXTRA_HEADER_INFO_FLAG set. + */ + +/* Extra flags applied to chunkId */ + +#define EXTRA_HEADER_INFO_FLAG 0x80000000 +#define EXTRA_SHRINK_FLAG 0x40000000 +#define EXTRA_SHADOWS_FLAG 0x20000000 +#define EXTRA_SPARE_FLAGS 0x10000000 + +#define ALL_EXTRA_FLAGS 0xF0000000 + +/* Also, the top 4 bits of the object Id are set to the object type. */ +#define EXTRA_OBJECT_TYPE_SHIFT (28) +#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT) + +static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 * pt) +{ + T(YAFFS_TRACE_MTD, + (TSTR("packed tags obj %d chunk %d byte %d seq %d" TENDSTR), + pt->t.objectId, pt->t.chunkId, pt->t.byteCount, + pt->t.sequenceNumber)); +} + +static void yaffs_DumpTags2(const yaffs_ExtendedTags * t) +{ + T(YAFFS_TRACE_MTD, + (TSTR + ("ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte " + "%d del %d ser %d seq %d" + TENDSTR), t->eccResult, t->blockBad, t->chunkUsed, t->objectId, + t->chunkId, t->byteCount, t->chunkDeleted, t->serialNumber, + t->sequenceNumber)); + +} + +void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t) +{ + pt->t.chunkId = t->chunkId; + pt->t.sequenceNumber = t->sequenceNumber; + pt->t.byteCount = t->byteCount; + pt->t.objectId = t->objectId; + + if (t->chunkId == 0 && t->extraHeaderInfoAvailable) { + /* Store the extra header info instead */ + /* We save the parent object in the chunkId */ + pt->t.chunkId = EXTRA_HEADER_INFO_FLAG + | t->extraParentObjectId; + if (t->extraIsShrinkHeader) { + pt->t.chunkId |= EXTRA_SHRINK_FLAG; + } + if (t->extraShadows) { + pt->t.chunkId |= EXTRA_SHADOWS_FLAG; + } + + pt->t.objectId &= ~EXTRA_OBJECT_TYPE_MASK; + pt->t.objectId |= + (t->extraObjectType << EXTRA_OBJECT_TYPE_SHIFT); + + if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) { + pt->t.byteCount = t->extraEquivalentObjectId; + } else if (t->extraObjectType == YAFFS_OBJECT_TYPE_FILE) { + pt->t.byteCount = t->extraFileLength; + } else { + pt->t.byteCount = 0; + } + } + + yaffs_DumpPackedTags2(pt); + yaffs_DumpTags2(t); + +#ifndef YAFFS_IGNORE_TAGS_ECC + { + yaffs_ECCCalculateOther((unsigned char *)&pt->t, + sizeof(yaffs_PackedTags2TagsPart), + &pt->ecc); + } +#endif +} + +void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt) +{ + + memset(t, 0, sizeof(yaffs_ExtendedTags)); + + yaffs_InitialiseTags(t); + + if (pt->t.sequenceNumber != 0xFFFFFFFF) { + /* Page is in use */ +#ifdef YAFFS_IGNORE_TAGS_ECC + { + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + } +#else + { + yaffs_ECCOther ecc; + int result; + yaffs_ECCCalculateOther((unsigned char *)&pt->t, + sizeof + (yaffs_PackedTags2TagsPart), + &ecc); + result = + yaffs_ECCCorrectOther((unsigned char *)&pt->t, + sizeof + (yaffs_PackedTags2TagsPart), + &pt->ecc, &ecc); + switch(result){ + case 0: + t->eccResult = YAFFS_ECC_RESULT_NO_ERROR; + break; + case 1: + t->eccResult = YAFFS_ECC_RESULT_FIXED; + break; + case -1: + t->eccResult = YAFFS_ECC_RESULT_UNFIXED; + break; + default: + t->eccResult = YAFFS_ECC_RESULT_UNKNOWN; + } + } +#endif + t->blockBad = 0; + t->chunkUsed = 1; + t->objectId = pt->t.objectId; + t->chunkId = pt->t.chunkId; + t->byteCount = pt->t.byteCount; + t->chunkDeleted = 0; + t->serialNumber = 0; + t->sequenceNumber = pt->t.sequenceNumber; + + /* Do extra header info stuff */ + + if (pt->t.chunkId & EXTRA_HEADER_INFO_FLAG) { + t->chunkId = 0; + t->byteCount = 0; + + t->extraHeaderInfoAvailable = 1; + t->extraParentObjectId = + pt->t.chunkId & (~(ALL_EXTRA_FLAGS)); + t->extraIsShrinkHeader = + (pt->t.chunkId & EXTRA_SHRINK_FLAG) ? 1 : 0; + t->extraShadows = + (pt->t.chunkId & EXTRA_SHADOWS_FLAG) ? 1 : 0; + t->extraObjectType = + pt->t.objectId >> EXTRA_OBJECT_TYPE_SHIFT; + t->objectId &= ~EXTRA_OBJECT_TYPE_MASK; + + if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) { + t->extraEquivalentObjectId = pt->t.byteCount; + } else { + t->extraFileLength = pt->t.byteCount; + } + } + } + + yaffs_DumpPackedTags2(pt); + yaffs_DumpTags2(t); + +} diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags2.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags2.h new file mode 100644 index 0000000000..c2242ffe76 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_packedtags2.h @@ -0,0 +1,38 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ + +#ifndef __YAFFS_PACKEDTAGS2_H__ +#define __YAFFS_PACKEDTAGS2_H__ + +#include "yaffs_guts.h" +#include "yaffs_ecc.h" + +typedef struct { + unsigned sequenceNumber; + unsigned objectId; + unsigned chunkId; + unsigned byteCount; +} yaffs_PackedTags2TagsPart; + +typedef struct { + yaffs_PackedTags2TagsPart t; + yaffs_ECCOther ecc; +} yaffs_PackedTags2; + +void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t); +void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt); +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_qsort.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_qsort.c new file mode 100644 index 0000000000..474be9cea3 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_qsort.c @@ -0,0 +1,160 @@ +/* + * 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. 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. + */ + +#include "yportenv.h" +//#include + +/* + * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". + */ +#define swapcode(TYPE, parmi, parmj, n) { \ + long i = (n) / sizeof (TYPE); \ + register TYPE *pi = (TYPE *) (parmi); \ + register TYPE *pj = (TYPE *) (parmj); \ + do { \ + register TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} + +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static __inline void +swapfunc(char *a, char *b, int n, int swaptype) +{ + if (swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b); \ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + +static __inline char * +med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *)) +{ + return cmp(a, b) < 0 ? + (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a )) + :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c )); +} + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +void +yaffs_qsort(void *aa, size_t n, size_t es, + int (*cmp)(const void *, const void *)) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + register char *a = aa; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp); + pm = med3(pm - d, pm, pm + d, cmp); + pn = med3(pn - 2 * d, pn - d, pn, cmp); + } + pm = med3(pl, pm, pn, cmp); + } + swap(a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = cmp(pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min((long)(pd - pc), (long)(pn - pd - es)); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + yaffs_qsort(a, r / es, es, cmp); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* yaffs_qsort(pn - r, r / es, es, cmp);*/ +} diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_qsort.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_qsort.h new file mode 100644 index 0000000000..610b7ec849 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_qsort.h @@ -0,0 +1,23 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YAFFS_QSORT_H__ +#define __YAFFS_QSORT_H__ + +extern void yaffs_qsort (void *const base, size_t total_elems, size_t size, + int (*cmp)(const void *, const void *)); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagscompat.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagscompat.c new file mode 100644 index 0000000000..7622b1af7c --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagscompat.c @@ -0,0 +1,530 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_guts.h" +#include "yaffs_tagscompat.h" +#include "yaffs_ecc.h" + +static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND); +#ifdef NOTYET +static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND); +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_Spare * spare); +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_Spare * spare); +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND); +#endif + +static const char yaffs_countBitsTable[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +int yaffs_CountBits(__u8 x) +{ + int retVal; + retVal = yaffs_countBitsTable[x]; + return retVal; +} + +/********** Tags ECC calculations *********/ + +void yaffs_CalcECC(const __u8 * data, yaffs_Spare * spare) +{ + yaffs_ECCCalculate(data, spare->ecc1); + yaffs_ECCCalculate(&data[256], spare->ecc2); +} + +void yaffs_CalcTagsECC(yaffs_Tags * tags) +{ + /* Calculate an ecc */ + + unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes; + unsigned i, j; + unsigned ecc = 0; + unsigned bit = 0; + + tags->ecc = 0; + + for (i = 0; i < 8; i++) { + for (j = 1; j & 0xff; j <<= 1) { + bit++; + if (b[i] & j) { + ecc ^= bit; + } + } + } + + tags->ecc = ecc; + +} + +int yaffs_CheckECCOnTags(yaffs_Tags * tags) +{ + unsigned ecc = tags->ecc; + + yaffs_CalcTagsECC(tags); + + ecc ^= tags->ecc; + + if (ecc && ecc <= 64) { + /* TODO: Handle the failure better. Retire? */ + unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes; + + ecc--; + + b[ecc / 8] ^= (1 << (ecc & 7)); + + /* Now recvalc the ecc */ + yaffs_CalcTagsECC(tags); + + return 1; /* recovered error */ + } else if (ecc) { + /* Wierd ecc failure value */ + /* TODO Need to do somethiong here */ + return -1; /* unrecovered error */ + } + + return 0; +} + +/********** Tags **********/ + +static void yaffs_LoadTagsIntoSpare(yaffs_Spare * sparePtr, + yaffs_Tags * tagsPtr) +{ + yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr; + + yaffs_CalcTagsECC(tagsPtr); + + sparePtr->tagByte0 = tu->asBytes[0]; + sparePtr->tagByte1 = tu->asBytes[1]; + sparePtr->tagByte2 = tu->asBytes[2]; + sparePtr->tagByte3 = tu->asBytes[3]; + sparePtr->tagByte4 = tu->asBytes[4]; + sparePtr->tagByte5 = tu->asBytes[5]; + sparePtr->tagByte6 = tu->asBytes[6]; + sparePtr->tagByte7 = tu->asBytes[7]; +} + +static void yaffs_GetTagsFromSpare(yaffs_Device * dev, yaffs_Spare * sparePtr, + yaffs_Tags * tagsPtr) +{ + yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr; + int result; + + tu->asBytes[0] = sparePtr->tagByte0; + tu->asBytes[1] = sparePtr->tagByte1; + tu->asBytes[2] = sparePtr->tagByte2; + tu->asBytes[3] = sparePtr->tagByte3; + tu->asBytes[4] = sparePtr->tagByte4; + tu->asBytes[5] = sparePtr->tagByte5; + tu->asBytes[6] = sparePtr->tagByte6; + tu->asBytes[7] = sparePtr->tagByte7; + + result = yaffs_CheckECCOnTags(tagsPtr); + if (result > 0) { + dev->tagsEccFixed++; + } else if (result < 0) { + dev->tagsEccUnfixed++; + } +} + +static void yaffs_SpareInitialise(yaffs_Spare * spare) +{ + memset(spare, 0xFF, sizeof(yaffs_Spare)); +} + +static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, const __u8 * data, + yaffs_Spare * spare) +{ + if (chunkInNAND < dev->startBlock * dev->nChunksPerBlock) { + T(YAFFS_TRACE_ERROR, + (TSTR("**>> yaffs chunk %d is not valid" TENDSTR), + chunkInNAND)); + return YAFFS_FAIL; + } + + dev->nPageWrites++; + return dev->writeChunkToNAND(dev, chunkInNAND, data, spare); +} + +static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev, + int chunkInNAND, + __u8 * data, + yaffs_Spare * spare, + yaffs_ECCResult * eccResult, + int doErrorCorrection) +{ + int retVal; + yaffs_Spare localSpare; + + dev->nPageReads++; + + if (!spare && data) { + /* If we don't have a real spare, then we use a local one. */ + /* Need this for the calculation of the ecc */ + spare = &localSpare; + } + + if (!dev->useNANDECC) { + retVal = dev->readChunkFromNAND(dev, chunkInNAND, data, spare); + if (data && doErrorCorrection) { + /* Do ECC correction */ + /* Todo handle any errors */ + int eccResult1, eccResult2; + __u8 calcEcc[3]; + + yaffs_ECCCalculate(data, calcEcc); + eccResult1 = + yaffs_ECCCorrect(data, spare->ecc1, calcEcc); + yaffs_ECCCalculate(&data[256], calcEcc); + eccResult2 = + yaffs_ECCCorrect(&data[256], spare->ecc2, calcEcc); + + if (eccResult1 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error fix performed on chunk %d:0" + TENDSTR), chunkInNAND)); + dev->eccFixed++; + } else if (eccResult1 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error unfixed on chunk %d:0" + TENDSTR), chunkInNAND)); + dev->eccUnfixed++; + } + + if (eccResult2 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error fix performed on chunk %d:1" + TENDSTR), chunkInNAND)); + dev->eccFixed++; + } else if (eccResult2 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>yaffs ecc error unfixed on chunk %d:1" + TENDSTR), chunkInNAND)); + dev->eccUnfixed++; + } + + if (eccResult1 || eccResult2) { + /* We had a data problem on this page */ + yaffs_HandleReadDataError(dev, chunkInNAND); + } + + if (eccResult1 < 0 || eccResult2 < 0) + *eccResult = YAFFS_ECC_RESULT_UNFIXED; + else if (eccResult1 > 0 || eccResult2 > 0) + *eccResult = YAFFS_ECC_RESULT_FIXED; + else + *eccResult = YAFFS_ECC_RESULT_NO_ERROR; + } + } else { + /* Must allocate enough memory for spare+2*sizeof(int) */ + /* for ecc results from device. */ + struct yaffs_NANDSpare nspare; + retVal = + dev->readChunkFromNAND(dev, chunkInNAND, data, + (yaffs_Spare *) & nspare); + memcpy(spare, &nspare, sizeof(yaffs_Spare)); + if (data && doErrorCorrection) { + if (nspare.eccres1 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error fix performed on chunk %d:0" + TENDSTR), chunkInNAND)); + } else if (nspare.eccres1 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error unfixed on chunk %d:0" + TENDSTR), chunkInNAND)); + } + + if (nspare.eccres2 > 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error fix performed on chunk %d:1" + TENDSTR), chunkInNAND)); + } else if (nspare.eccres2 < 0) { + T(YAFFS_TRACE_ERROR, + (TSTR + ("**>>mtd ecc error unfixed on chunk %d:1" + TENDSTR), chunkInNAND)); + } + + if (nspare.eccres1 || nspare.eccres2) { + /* We had a data problem on this page */ + yaffs_HandleReadDataError(dev, chunkInNAND); + } + + if (nspare.eccres1 < 0 || nspare.eccres2 < 0) + *eccResult = YAFFS_ECC_RESULT_UNFIXED; + else if (nspare.eccres1 > 0 || nspare.eccres2 > 0) + *eccResult = YAFFS_ECC_RESULT_FIXED; + else + *eccResult = YAFFS_ECC_RESULT_NO_ERROR; + + } + } + return retVal; +} + +#ifdef NOTYET +static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev, + int chunkInNAND) +{ + + static int init = 0; + static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK]; + static __u8 data[YAFFS_BYTES_PER_CHUNK]; + /* Might as well always allocate the larger size for */ + /* dev->useNANDECC == true; */ + static __u8 spare[sizeof(struct yaffs_NANDSpare)]; + + dev->readChunkFromNAND(dev, chunkInNAND, data, (yaffs_Spare *) spare); + + if (!init) { + memset(cmpbuf, 0xff, YAFFS_BYTES_PER_CHUNK); + init = 1; + } + + if (memcmp(cmpbuf, data, YAFFS_BYTES_PER_CHUNK)) + return YAFFS_FAIL; + if (memcmp(cmpbuf, spare, 16)) + return YAFFS_FAIL; + + return YAFFS_OK; + +} +#endif + +/* + * Functions for robustisizing + */ + +static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND) +{ + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + + /* Mark the block for retirement */ + yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1; + T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, + (TSTR("**>>Block %d marked for retirement" TENDSTR), blockInNAND)); + + /* TODO: + * Just do a garbage collection on the affected block + * then retire the block + * NB recursion + */ +} + +#ifdef NOTYET +static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND) +{ +} + +static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND, + const __u8 * data, + const yaffs_Spare * spare) +{ +} + +static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND, + const yaffs_Spare * spare) +{ +} + +static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND) +{ + int blockInNAND = chunkInNAND / dev->nChunksPerBlock; + + /* Mark the block for retirement */ + yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1; + /* Delete the chunk */ + yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__); +} + +static int yaffs_VerifyCompare(const __u8 * d0, const __u8 * d1, + const yaffs_Spare * s0, const yaffs_Spare * s1) +{ + + if (memcmp(d0, d1, YAFFS_BYTES_PER_CHUNK) != 0 || + s0->tagByte0 != s1->tagByte0 || + s0->tagByte1 != s1->tagByte1 || + s0->tagByte2 != s1->tagByte2 || + s0->tagByte3 != s1->tagByte3 || + s0->tagByte4 != s1->tagByte4 || + s0->tagByte5 != s1->tagByte5 || + s0->tagByte6 != s1->tagByte6 || + s0->tagByte7 != s1->tagByte7 || + s0->ecc1[0] != s1->ecc1[0] || + s0->ecc1[1] != s1->ecc1[1] || + s0->ecc1[2] != s1->ecc1[2] || + s0->ecc2[0] != s1->ecc2[0] || + s0->ecc2[1] != s1->ecc2[1] || s0->ecc2[2] != s1->ecc2[2]) { + return 0; + } + + return 1; +} +#endif /* NOTYET */ + +int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * + eTags) +{ + yaffs_Spare spare; + yaffs_Tags tags; + + yaffs_SpareInitialise(&spare); + + if (eTags->chunkDeleted) { + spare.pageStatus = 0; + } else { + tags.objectId = eTags->objectId; + tags.chunkId = eTags->chunkId; + tags.byteCount = eTags->byteCount; + tags.serialNumber = eTags->serialNumber; + + if (!dev->useNANDECC && data) { + yaffs_CalcECC(data, &spare); + } + yaffs_LoadTagsIntoSpare(&spare, &tags); + + } + + return yaffs_WriteChunkToNAND(dev, chunkInNAND, data, &spare); +} + +int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev, + int chunkInNAND, + __u8 * data, + yaffs_ExtendedTags * eTags) +{ + + yaffs_Spare spare; + yaffs_Tags tags; + yaffs_ECCResult eccResult; + + static yaffs_Spare spareFF; + static int init; + + if (!init) { + memset(&spareFF, 0xFF, sizeof(spareFF)); + init = 1; + } + + if (yaffs_ReadChunkFromNAND + (dev, chunkInNAND, data, &spare, &eccResult, 1)) { + /* eTags may be NULL */ + if (eTags) { + + int deleted = + (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0; + + eTags->chunkDeleted = deleted; + eTags->eccResult = eccResult; + eTags->blockBad = 0; /* We're reading it */ + /* therefore it is not a bad block */ + eTags->chunkUsed = + (memcmp(&spareFF, &spare, sizeof(spareFF)) != + 0) ? 1 : 0; + + if (eTags->chunkUsed) { + yaffs_GetTagsFromSpare(dev, &spare, &tags); + + eTags->objectId = tags.objectId; + eTags->chunkId = tags.chunkId; + eTags->byteCount = tags.byteCount; + eTags->serialNumber = tags.serialNumber; + } + } + + return YAFFS_OK; + } else { + return YAFFS_FAIL; + } +} + +int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, + int blockInNAND) +{ + + yaffs_Spare spare; + + memset(&spare, 0xff, sizeof(yaffs_Spare)); + + spare.blockStatus = 'Y'; + + yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL, + &spare); + yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, + NULL, &spare); + + return YAFFS_OK; + +} + +int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, + int blockNo, yaffs_BlockState * + state, + int *sequenceNumber) +{ + + yaffs_Spare spare0, spare1; + static yaffs_Spare spareFF; + static int init; + yaffs_ECCResult dummy; + + if (!init) { + memset(&spareFF, 0xFF, sizeof(spareFF)); + init = 1; + } + + *sequenceNumber = 0; + + yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock, NULL, + &spare0, &dummy, 1); + yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock + 1, NULL, + &spare1, &dummy, 1); + + if (yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7) + *state = YAFFS_BLOCK_STATE_DEAD; + else if (memcmp(&spareFF, &spare0, sizeof(spareFF)) == 0) + *state = YAFFS_BLOCK_STATE_EMPTY; + else + *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING; + + return YAFFS_OK; +} diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagscompat.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagscompat.h new file mode 100644 index 0000000000..a61e3ba14f --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagscompat.h @@ -0,0 +1,40 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFS_TAGSCOMPAT_H__ +#define __YAFFS_TAGSCOMPAT_H__ + +#include "yaffs_guts.h" +int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev, + int chunkInNAND, + const __u8 * data, + const yaffs_ExtendedTags * + tags); +int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev, + int chunkInNAND, + __u8 * data, + yaffs_ExtendedTags * + tags); +int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev, + int blockNo); +int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev, + int blockNo, yaffs_BlockState * + state, int *sequenceNumber); + +void yaffs_CalcTagsECC(yaffs_Tags * tags); +int yaffs_CheckECCOnTags(yaffs_Tags * tags); +int yaffs_CountBits(__u8 byte); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagsvalidity.c b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagsvalidity.c new file mode 100644 index 0000000000..9e0bd1cf56 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagsvalidity.c @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yaffs_tagsvalidity.h" + +void yaffs_InitialiseTags(yaffs_ExtendedTags * tags) +{ + memset(tags, 0, sizeof(yaffs_ExtendedTags)); + tags->validMarker0 = 0xAAAAAAAA; + tags->validMarker1 = 0x55555555; +} + +int yaffs_ValidateTags(yaffs_ExtendedTags * tags) +{ + return (tags->validMarker0 == 0xAAAAAAAA && + tags->validMarker1 == 0x55555555); + +} diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagsvalidity.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagsvalidity.h new file mode 100644 index 0000000000..2fd0c24ed2 --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffs_tagsvalidity.h @@ -0,0 +1,24 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YAFFS_TAGS_VALIDITY_H__ +#define __YAFFS_TAGS_VALIDITY_H__ + +#include "yaffs_guts.h" + +void yaffs_InitialiseTags(yaffs_ExtendedTags * tags); +int yaffs_ValidateTags(yaffs_ExtendedTags * tags); +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffsinterface.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffsinterface.h new file mode 100644 index 0000000000..810837a32d --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yaffsinterface.h @@ -0,0 +1,21 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + +#ifndef __YAFFSINTERFACE_H__ +#define __YAFFSINTERFACE_H__ + +int yaffs_Initialise(unsigned nBlocks); + +#endif diff --git a/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yportenv.h b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yportenv.h new file mode 100644 index 0000000000..15ac28121c --- /dev/null +++ b/target/linux/generic-2.6/files-2.6.23/fs/yaffs2/yportenv.h @@ -0,0 +1,187 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. + * + * Copyright (C) 2002-2007 Aleph One Ltd. + * for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. + */ + + +#ifndef __YPORTENV_H__ +#define __YPORTENV_H__ + +#if defined CONFIG_YAFFS_WINCE + +#include "ywinceenv.h" + +#elif defined __KERNEL__ + +#include "moduleconfig.h" + +/* Linux kernel */ +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) +#include +#endif +#include +#include +#include +#include +#include +#include + +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strncmp(a,b,c) strncmp(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) + +#define Y_INLINE inline + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" + +/* #define YPRINTF(x) printk x */ +#define YMALLOC(x) kmalloc(x,GFP_KERNEL) +#define YFREE(x) kfree(x) +#define YMALLOC_ALT(x) vmalloc(x) +#define YFREE_ALT(x) vfree(x) +#define YMALLOC_DMA(x) YMALLOC(x) + +// KR - added for use in scan so processes aren't blocked indefinitely. +#define YYIELD() schedule() + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) +#define Y_CURRENT_TIME CURRENT_TIME.tv_sec +#define Y_TIME_CONVERT(x) (x).tv_sec +#else +#define Y_CURRENT_TIME CURRENT_TIME +#define Y_TIME_CONVERT(x) (x) +#endif + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#define TENDSTR "\n" +#define TSTR(x) KERN_WARNING x +#define TOUT(p) printk p + +#define yaffs_trace(mask, fmt, args...) \ + do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \ + printk(KERN_WARNING "yaffs: " fmt, ## args); \ + } while (0) + +#define compile_time_assertion(assertion) \ + ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) + +#elif defined CONFIG_YAFFS_DIRECT + +/* Direct interface */ +#include "ydirectenv.h" + +#elif defined CONFIG_YAFFS_UTIL + +/* Stuff for YAFFS utilities */ + +#include "stdlib.h" +#include "stdio.h" +#include "string.h" + +#include "devextras.h" + +#define YMALLOC(x) malloc(x) +#define YFREE(x) free(x) +#define YMALLOC_ALT(x) malloc(x) +#define YFREE_ALT(x) free(x) + +#define YCHAR char +#define YUCHAR unsigned char +#define _Y(x) x +#define yaffs_strcpy(a,b) strcpy(a,b) +#define yaffs_strncpy(a,b,c) strncpy(a,b,c) +#define yaffs_strlen(s) strlen(s) +#define yaffs_sprintf sprintf +#define yaffs_toupper(a) toupper(a) + +#define Y_INLINE inline + +/* #define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) */ +/* #define YALERT(s) YINFO(s) */ + +#define TENDSTR "\n" +#define TSTR(x) x +#define TOUT(p) printf p + +#define YAFFS_LOSTNFOUND_NAME "lost+found" +#define YAFFS_LOSTNFOUND_PREFIX "obj" +/* #define YPRINTF(x) printf x */ + +#define YAFFS_ROOT_MODE 0666 +#define YAFFS_LOSTNFOUND_MODE 0666 + +#define yaffs_SumCompare(x,y) ((x) == (y)) +#define yaffs_strcmp(a,b) strcmp(a,b) + +#else +/* Should have specified a configuration type */ +#error Unknown configuration + +#endif + +/* see yaffs_fs.c */ +extern unsigned int yaffs_traceMask; +extern unsigned int yaffs_wr_attempts; + +/* + * Tracing flags. + * The flags masked in YAFFS_TRACE_ALWAYS are always traced. + */ + +#define YAFFS_TRACE_OS 0x00000002 +#define YAFFS_TRACE_ALLOCATE 0x00000004 +#define YAFFS_TRACE_SCAN 0x00000008 +#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 +#define YAFFS_TRACE_ERASE 0x00000020 +#define YAFFS_TRACE_GC 0x00000040 +#define YAFFS_TRACE_WRITE 0x00000080 +#define YAFFS_TRACE_TRACING 0x00000100 +#define YAFFS_TRACE_DELETION 0x00000200 +#define YAFFS_TRACE_BUFFERS 0x00000400 +#define YAFFS_TRACE_NANDACCESS 0x00000800 +#define YAFFS_TRACE_GC_DETAIL 0x00001000 +#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 +#define YAFFS_TRACE_MTD 0x00004000 +#define YAFFS_TRACE_CHECKPOINT 0x00008000 + +#define YAFFS_TRACE_VERIFY 0x00010000 +#define YAFFS_TRACE_VERIFY_NAND 0x00020000 +#define YAFFS_TRACE_VERIFY_FULL 0x00040000 +#define YAFFS_TRACE_VERIFY_ALL 0x000F0000 + + +#define YAFFS_TRACE_ERROR 0x40000000 +#define YAFFS_TRACE_BUG 0x80000000 +#define YAFFS_TRACE_ALWAYS 0xF0000000 + + +#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ALWAYS)) TOUT(p);} while(0) + +#ifndef CONFIG_YAFFS_WINCE +#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__)) +#endif + +#endif diff --git a/target/linux/generic-2.6/patches-2.6.23/001-squashfs.patch b/target/linux/generic-2.6/patches-2.6.23/001-squashfs.patch new file mode 100644 index 0000000000..6fa0f0cc79 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/001-squashfs.patch @@ -0,0 +1,4170 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -1364,6 +1364,71 @@ + + If unsure, say N. + ++config SQUASHFS ++ tristate "SquashFS 3.0 - Squashed file system support" ++ select ZLIB_INFLATE ++ help ++ Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File ++ System). Squashfs is a highly compressed read-only filesystem for Linux. ++ It uses zlib compression to compress both files, inodes and directories. ++ Inodes in the system are very small and all blocks are packed to minimise ++ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. ++ SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full ++ uid/gid information, hard links and timestamps. ++ ++ Squashfs is intended for general read-only filesystem use, for archival ++ use (i.e. in cases where a .tar.gz file may be used), and in embedded ++ systems where low overhead is needed. Further information and filesystem tools ++ are available from http://squashfs.sourceforge.net. ++ ++ If you want to compile this as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read . The module ++ will be called squashfs. Note that the root file system (the one ++ containing the directory /) cannot be compiled as a module. ++ ++ If unsure, say N. ++ ++config SQUASHFS_EMBEDDED ++ ++ bool "Additional options for memory-constrained systems" ++ depends on SQUASHFS ++ default n ++ help ++ Saying Y here allows you to specify cache sizes and how Squashfs ++ allocates memory. This is only intended for memory constrained ++ systems. ++ ++ If unsure, say N. ++ ++config SQUASHFS_FRAGMENT_CACHE_SIZE ++ int "Number of fragments cached" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default "3" ++ help ++ By default SquashFS caches the last 3 fragments read from ++ the filesystem. Increasing this amount may mean SquashFS ++ has to re-read fragments less often from disk, at the expense ++ of extra system memory. Decreasing this amount will mean ++ SquashFS uses less memory at the expense of extra reads from disk. ++ ++ Note there must be at least one cached fragment. Anything ++ much more than three will probably not make much difference. ++ ++config SQUASHFS_VMALLOC ++ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default n ++ help ++ By default SquashFS uses kmalloc to obtain fragment cache memory. ++ Kmalloc memory is the standard kernel allocator, but it can fail ++ on memory constrained systems. Because of the way Vmalloc works, ++ Vmalloc can succeed when kmalloc fails. Specifying this option ++ will make SquashFS always use Vmalloc to allocate the ++ fragment cache memory. ++ ++ If unsure, say N. ++ + config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + depends on BLOCK +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -72,6 +72,7 @@ + obj-$(CONFIG_JBD2) += jbd2/ + obj-$(CONFIG_EXT2_FS) += ext2/ + obj-$(CONFIG_CRAMFS) += cramfs/ ++obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-$(CONFIG_RAMFS) += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ +--- /dev/null ++++ b/fs/squashfs/inode.c +@@ -0,0 +1,2122 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * inode.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs.h" ++ ++static void squashfs_put_super(struct super_block *); ++static int squashfs_statfs(struct dentry *, struct kstatfs *); ++static int squashfs_symlink_readpage(struct file *file, struct page *page); ++static int squashfs_readpage(struct file *file, struct page *page); ++static int squashfs_readpage4K(struct file *file, struct page *page); ++static int squashfs_readdir(struct file *, void *, filldir_t); ++static struct inode *squashfs_alloc_inode(struct super_block *sb); ++static void squashfs_destroy_inode(struct inode *inode); ++static int init_inodecache(void); ++static void destroy_inodecache(void); ++static struct dentry *squashfs_lookup(struct inode *, struct dentry *, ++ struct nameidata *); ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize); ++static int squashfs_get_sb(struct file_system_type *, int, ++ const char *, void *, struct vfsmount *); ++ ++ ++static z_stream stream; ++ ++static struct file_system_type squashfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "squashfs", ++ .get_sb = squashfs_get_sb, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static struct super_operations squashfs_ops = { ++ .alloc_inode = squashfs_alloc_inode, ++ .destroy_inode = squashfs_destroy_inode, ++ .statfs = squashfs_statfs, ++ .put_super = squashfs_put_super, ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { ++ .readpage = squashfs_symlink_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops = { ++ .readpage = squashfs_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { ++ .readpage = squashfs_readpage4K ++}; ++ ++static struct file_operations squashfs_dir_ops = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir ++}; ++ ++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { ++ .lookup = squashfs_lookup ++}; ++ ++ ++static struct buffer_head *get_block_length(struct super_block *s, ++ int *cur_index, int *offset, int *c_byte) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned short temp; ++ struct buffer_head *bh; ++ ++ if (!(bh = sb_bread(s, *cur_index))) ++ goto out; ++ ++ if (msblk->devblksize - *offset == 1) { ++ if (msblk->swap) ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ else ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ if (msblk->swap) ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ bh->b_data); ++ else ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ bh->b_data); ++ *c_byte = temp; ++ *offset = 1; ++ } else { ++ if (msblk->swap) { ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } else { ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } ++ *c_byte = temp; ++ *offset += 2; ++ } ++ ++ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { ++ if (*offset == msblk->devblksize) { ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ *offset = 0; ++ } ++ if (*((unsigned char *) (bh->b_data + *offset)) != ++ SQUASHFS_MARKER_BYTE) { ++ ERROR("Metadata block marker corrupt @ %x\n", ++ *cur_index); ++ brelse(bh); ++ goto out; ++ } ++ (*offset)++; ++ } ++ return bh; ++ ++out: ++ return NULL; ++} ++ ++ ++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> ++ msblk->devblksize_log2) + 2]; ++ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); ++ unsigned int cur_index = index >> msblk->devblksize_log2; ++ int bytes, avail_bytes, b = 0, k; ++ char *c_buffer; ++ unsigned int compressed; ++ unsigned int c_byte = length; ++ ++ if (c_byte) { ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); ++ c_buffer = compressed ? msblk->read_data : buffer; ++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ if (!(bh[0] = sb_getblk(s, cur_index))) ++ goto block_release; ++ ++ for (b = 1; bytes < c_byte; b++) { ++ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b, bh); ++ } else { ++ if (!(bh[0] = get_block_length(s, &cur_index, &offset, ++ &c_byte))) ++ goto read_failure; ++ ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED(c_byte); ++ c_buffer = compressed ? msblk->read_data : buffer; ++ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ for (b = 1; bytes < c_byte; b++) { ++ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b - 1, bh + 1); ++ } ++ ++ if (compressed) ++ down(&msblk->read_data_mutex); ++ ++ for (bytes = 0, k = 0; k < b; k++) { ++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? ++ msblk->devblksize - offset : ++ c_byte - bytes; ++ wait_on_buffer(bh[k]); ++ if (!buffer_uptodate(bh[k])) ++ goto block_release; ++ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); ++ bytes += avail_bytes; ++ offset = 0; ++ brelse(bh[k]); ++ } ++ ++ /* ++ * uncompress block ++ */ ++ if (compressed) { ++ int zlib_err; ++ ++ stream.next_in = c_buffer; ++ stream.avail_in = c_byte; ++ stream.next_out = buffer; ++ stream.avail_out = msblk->read_size; ++ ++ if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || ++ ((zlib_err = zlib_inflate(&stream, Z_FINISH)) ++ != Z_STREAM_END) || ((zlib_err = ++ zlib_inflateEnd(&stream)) != Z_OK)) { ++ ERROR("zlib_fs returned unexpected result 0x%x\n", ++ zlib_err); ++ bytes = 0; ++ } else ++ bytes = stream.total_out; ++ ++ up(&msblk->read_data_mutex); ++ } ++ ++ if (next_index) ++ *next_index = index + c_byte + (length ? 0 : ++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) ++ ? 3 : 2)); ++ return bytes; ++ ++block_release: ++ while (--b >= 0) ++ brelse(bh[b]); ++ ++read_failure: ++ ERROR("sb_bread failed reading block 0x%x\n", cur_index); ++ return 0; ++} ++ ++ ++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ int n, i, bytes, return_length = length; ++ long long next_index; ++ ++ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); ++ ++ while ( 1 ) { ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ if (msblk->block_cache[i].block == block) ++ break; ++ ++ down(&msblk->block_cache_mutex); ++ ++ if (i == SQUASHFS_CACHED_BLKS) { ++ /* read inode header block */ ++ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; ++ n ; n --, i = (i + 1) % ++ SQUASHFS_CACHED_BLKS) ++ if (msblk->block_cache[i].block != ++ SQUASHFS_USED_BLK) ++ break; ++ ++ if (n == 0) { ++ wait_queue_t wait; ++ ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&msblk->waitq, &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ up(&msblk->block_cache_mutex); ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&msblk->waitq, &wait); ++ continue; ++ } ++ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; ++ ++ if (msblk->block_cache[i].block == ++ SQUASHFS_INVALID_BLK) { ++ if (!(msblk->block_cache[i].data = ++ kmalloc(SQUASHFS_METADATA_SIZE, ++ GFP_KERNEL))) { ++ ERROR("Failed to allocate cache" ++ "block\n"); ++ up(&msblk->block_cache_mutex); ++ goto out; ++ } ++ } ++ ++ msblk->block_cache[i].block = SQUASHFS_USED_BLK; ++ up(&msblk->block_cache_mutex); ++ ++ if (!(msblk->block_cache[i].length = ++ squashfs_read_data(s, ++ msblk->block_cache[i].data, ++ block, 0, &next_index))) { ++ ERROR("Unable to read cache block [%llx:%x]\n", ++ block, offset); ++ goto out; ++ } ++ ++ down(&msblk->block_cache_mutex); ++ wake_up(&msblk->waitq); ++ msblk->block_cache[i].block = block; ++ msblk->block_cache[i].next_index = next_index; ++ TRACE("Read cache block [%llx:%x]\n", block, offset); ++ } ++ ++ if (msblk->block_cache[i].block != block) { ++ up(&msblk->block_cache_mutex); ++ continue; ++ } ++ ++ if ((bytes = msblk->block_cache[i].length - offset) >= length) { ++ if (buffer) ++ memcpy(buffer, msblk->block_cache[i].data + ++ offset, length); ++ if (msblk->block_cache[i].length - offset == length) { ++ *next_block = msblk->block_cache[i].next_index; ++ *next_offset = 0; ++ } else { ++ *next_block = block; ++ *next_offset = offset + length; ++ } ++ up(&msblk->block_cache_mutex); ++ goto finish; ++ } else { ++ if (buffer) { ++ memcpy(buffer, msblk->block_cache[i].data + ++ offset, bytes); ++ buffer += bytes; ++ } ++ block = msblk->block_cache[i].next_index; ++ up(&msblk->block_cache_mutex); ++ length -= bytes; ++ offset = 0; ++ } ++ } ++ ++finish: ++ return return_length; ++out: ++ return 0; ++} ++ ++ ++static int get_fragment_location(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); ++ struct squashfs_fragment_entry fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment) ++{ ++ down(&msblk->fragment_mutex); ++ fragment->locked --; ++ wake_up(&msblk->fragment_wait_queue); ++ up(&msblk->fragment_mutex); ++} ++ ++ ++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length) ++{ ++ int i, n; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ ++ while ( 1 ) { ++ down(&msblk->fragment_mutex); ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && ++ msblk->fragment[i].block != start_block; i++); ++ ++ if (i == SQUASHFS_CACHED_FRAGMENTS) { ++ for (i = msblk->next_fragment, n = ++ SQUASHFS_CACHED_FRAGMENTS; n && ++ msblk->fragment[i].locked; n--, i = (i + 1) % ++ SQUASHFS_CACHED_FRAGMENTS); ++ ++ if (n == 0) { ++ wait_queue_t wait; ++ ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&msblk->fragment_wait_queue, ++ &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ up(&msblk->fragment_mutex); ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&msblk->fragment_wait_queue, ++ &wait); ++ continue; ++ } ++ msblk->next_fragment = (msblk->next_fragment + 1) % ++ SQUASHFS_CACHED_FRAGMENTS; ++ ++ if (msblk->fragment[i].data == NULL) ++ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC ++ (SQUASHFS_FILE_MAX_SIZE))) { ++ ERROR("Failed to allocate fragment " ++ "cache block\n"); ++ up(&msblk->fragment_mutex); ++ goto out; ++ } ++ ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].locked = 1; ++ up(&msblk->fragment_mutex); ++ ++ if (!(msblk->fragment[i].length = squashfs_read_data(s, ++ msblk->fragment[i].data, ++ start_block, length, NULL))) { ++ ERROR("Unable to read fragment cache block " ++ "[%llx]\n", start_block); ++ msblk->fragment[i].locked = 0; ++ goto out; ++ } ++ ++ msblk->fragment[i].block = start_block; ++ TRACE("New fragment %d, start block %lld, locked %d\n", ++ i, msblk->fragment[i].block, ++ msblk->fragment[i].locked); ++ break; ++ } ++ ++ msblk->fragment[i].locked++; ++ up(&msblk->fragment_mutex); ++ TRACE("Got fragment %d, start block %lld, locked %d\n", i, ++ msblk->fragment[i].block, ++ msblk->fragment[i].locked); ++ break; ++ } ++ ++ return &msblk->fragment[i]; ++ ++out: ++ return NULL; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++ struct squashfs_base_inode_header *inodeb) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct inode *i = new_inode(s); ++ ++ if (i) { ++ i->i_ino = inodeb->inode_number; ++ i->i_mtime.tv_sec = inodeb->mtime; ++ i->i_atime.tv_sec = inodeb->mtime; ++ i->i_ctime.tv_sec = inodeb->mtime; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++ } ++ ++ return i; ++} ++ ++ ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) ++{ ++ struct inode *i; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header id, sid; ++ struct squashfs_base_inode_header *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_reg_inode_header *inodep = &id.reg; ++ struct squashfs_reg_inode_header *sinodep = &sid.reg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = 1; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_LREG_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_lreg_inode_header *inodep = &id.lreg; ++ struct squashfs_lreg_inode_header *sinodep = &sid.lreg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header *inodep = &id.dir; ++ struct squashfs_dir_inode_header *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header *inodep = &id.dev; ++ struct squashfs_dev_inode_header *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ struct squashfs_ipc_inode_header *inodep = &id.ipc; ++ struct squashfs_ipc_inode_header *sinodep = &sid.ipc; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ insert_inode_hash(i); ++ return i; ++ ++failed_read: ++ ERROR("Unable to read inode [%llx:%x]\n", block, offset); ++ ++failed_read1: ++ return NULL; ++} ++ ++ ++static int read_fragment_index_table(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ /* Allocate fragment index table */ ++ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ long long fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), ++ &msblk->fragment_index[i], 1); ++ msblk->fragment_index[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->iget = squashfs_iget; ++ msblk->read_blocklist = read_blocklist; ++ msblk->read_fragment_index_table = read_fragment_index_table; ++ ++ if (sblk->s_major == 1) { ++ if (!squashfs_1_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with " ++ "Squashfs 1.0 support enabled\n"); ++ return 0; ++ } ++ } else if (sblk->s_major == 2) { ++ if (!squashfs_2_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with " ++ "Squashfs 2.0 support enabled\n"); ++ return 0; ++ } ++ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > ++ SQUASHFS_MINOR) { ++ SERROR("Major/Minor mismatch, trying to mount newer %d.%d " ++ "filesystem\n", sblk->s_major, sblk->s_minor); ++ SERROR("Please update your kernel\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static int squashfs_fill_super(struct super_block *s, void *data, int silent) ++{ ++ struct squashfs_sb_info *msblk; ++ struct squashfs_super_block *sblk; ++ int i; ++ char b[BDEVNAME_SIZE]; ++ struct inode *root; ++ ++ TRACE("Entered squashfs_read_superblock\n"); ++ ++ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), ++ GFP_KERNEL))) { ++ ERROR("Failed to allocate superblock\n"); ++ goto failure; ++ } ++ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); ++ msblk = s->s_fs_info; ++ sblk = &msblk->sblk; ++ ++ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); ++ msblk->devblksize_log2 = ffz(~msblk->devblksize); ++ ++ init_MUTEX(&msblk->read_data_mutex); ++ init_MUTEX(&msblk->read_page_mutex); ++ init_MUTEX(&msblk->block_cache_mutex); ++ init_MUTEX(&msblk->fragment_mutex); ++ init_MUTEX(&msblk->meta_index_mutex); ++ ++ init_waitqueue_head(&msblk->waitq); ++ init_waitqueue_head(&msblk->fragment_wait_queue); ++ ++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, ++ sizeof(struct squashfs_super_block) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ SERROR("unable to read superblock\n"); ++ goto failed_mount; ++ } ++ ++ /* Check it is a SQUASHFS superblock */ ++ msblk->swap = 0; ++ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { ++ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { ++ struct squashfs_super_block ssblk; ++ ++ WARNING("Mounting a different endian SQUASHFS " ++ "filesystem on %s\n", bdevname(s->s_bdev, b)); ++ ++ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); ++ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); ++ msblk->swap = 1; ++ } else { ++ SERROR("Can't find a SQUASHFS superblock on %s\n", ++ bdevname(s->s_bdev, b)); ++ goto failed_mount; ++ } ++ } ++ ++ /* Check the MAJOR & MINOR versions */ ++ if(!supported_squashfs_filesystem(msblk, silent)) ++ goto failed_mount; ++ ++ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); ++ TRACE("Inodes are %scompressed\n", ++ SQUASHFS_UNCOMPRESSED_INODES ++ (sblk->flags) ? "un" : ""); ++ TRACE("Data is %scompressed\n", ++ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) ++ ? "un" : ""); ++ TRACE("Check data is %s present in the filesystem\n", ++ SQUASHFS_CHECK_DATA(sblk->flags) ? ++ "" : "not"); ++ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); ++ TRACE("Block size %d\n", sblk->block_size); ++ TRACE("Number of inodes %d\n", sblk->inodes); ++ if (sblk->s_major > 1) ++ TRACE("Number of fragments %d\n", sblk->fragments); ++ TRACE("Number of uids %d\n", sblk->no_uids); ++ TRACE("Number of gids %d\n", sblk->no_guids); ++ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); ++ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); ++ if (sblk->s_major > 1) ++ TRACE("sblk->fragment_table_start %llx\n", ++ sblk->fragment_table_start); ++ TRACE("sblk->uid_start %llx\n", sblk->uid_start); ++ ++ s->s_flags |= MS_RDONLY; ++ s->s_op = &squashfs_ops; ++ ++ /* Init inode_table block pointer array */ ++ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * ++ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { ++ ERROR("Failed to allocate block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ ++ msblk->next_cache = 0; ++ ++ /* Allocate read_data block */ ++ msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? ++ SQUASHFS_METADATA_SIZE : ++ sblk->block_size; ++ ++ if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { ++ ERROR("Failed to allocate read_data block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate read_page block */ ++ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { ++ ERROR("Failed to allocate read_page block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate uid and gid tables */ ++ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ goto failed_mount; ++ } ++ msblk->guid = msblk->uid + sblk->no_uids; ++ ++ if (msblk->swap) { ++ unsigned int suid[sblk->no_uids + sblk->no_guids]; ++ ++ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + ++ sblk->no_guids), (sizeof(unsigned int) * 8)); ++ } else ++ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ ++ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) ++ goto allocate_root; ++ ++ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * ++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { ++ ERROR("Failed to allocate fragment block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { ++ msblk->fragment[i].locked = 0; ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].data = NULL; ++ } ++ ++ msblk->next_fragment = 0; ++ ++ /* Allocate fragment index table */ ++ if (msblk->read_fragment_index_table(s) == 0) ++ goto failed_mount; ++ ++allocate_root: ++ if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) ++ goto failed_mount; ++ ++ if ((s->s_root = d_alloc_root(root)) == NULL) { ++ ERROR("Root inode create failed\n"); ++ iput(root); ++ goto failed_mount; ++ } ++ ++ TRACE("Leaving squashfs_read_super\n"); ++ return 0; ++ ++failed_mount: ++ kfree(msblk->fragment_index); ++ kfree(msblk->fragment); ++ kfree(msblk->uid); ++ kfree(msblk->read_page); ++ kfree(msblk->read_data); ++ kfree(msblk->block_cache); ++ kfree(msblk->fragment_index_2); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ return -EINVAL; ++ ++failure: ++ return -ENOMEM; ++} ++ ++ ++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct squashfs_sb_info *msblk = dentry->d_inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ TRACE("Entered squashfs_statfs\n"); ++ ++ buf->f_type = SQUASHFS_MAGIC; ++ buf->f_bsize = sblk->block_size; ++ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; ++ buf->f_bfree = buf->f_bavail = 0; ++ buf->f_files = sblk->inodes; ++ buf->f_ffree = 0; ++ buf->f_namelen = SQUASHFS_NAME_LEN; ++ ++ return 0; ++} ++ ++ ++static int squashfs_symlink_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes; ++ long long block = SQUASHFS_I(inode)->start_block; ++ int offset = SQUASHFS_I(inode)->offset; ++ void *pageaddr = kmap(page); ++ ++ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " ++ "%llx, offset %x\n", page->index, ++ SQUASHFS_I(inode)->start_block, ++ SQUASHFS_I(inode)->offset); ++ ++ for (length = 0; length < index; length += bytes) { ++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, ++ block, offset, PAGE_CACHE_SIZE, &block, ++ &offset))) { ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, ++ offset); ++ goto skip_read; ++ } ++ } ++ ++ if (length != index) { ++ ERROR("(squashfs_symlink_readpage) length != index\n"); ++ bytes = 0; ++ goto skip_read; ++ } ++ ++ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : ++ i_size_read(inode) - length; ++ ++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, ++ offset, bytes, &block, &offset))) ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) ++{ ++ struct meta_index *meta = NULL; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ int i; ++ ++ down(&msblk->meta_index_mutex); ++ ++ TRACE("locate_meta_index: index %d, offset %d\n", index, offset); ++ ++ if(msblk->meta_index == NULL) ++ goto not_allocated; ++ ++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) ++ if (msblk->meta_index[i].inode_number == inode->i_ino && ++ msblk->meta_index[i].offset >= offset && ++ msblk->meta_index[i].offset <= index && ++ msblk->meta_index[i].locked == 0) { ++ TRACE("locate_meta_index: entry %d, offset %d\n", i, ++ msblk->meta_index[i].offset); ++ meta = &msblk->meta_index[i]; ++ offset = meta->offset; ++ } ++ ++ if (meta) ++ meta->locked = 1; ++ ++not_allocated: ++ up(&msblk->meta_index_mutex); ++ ++ return meta; ++} ++ ++ ++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct meta_index *meta = NULL; ++ int i; ++ ++ down(&msblk->meta_index_mutex); ++ ++ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); ++ ++ if(msblk->meta_index == NULL) { ++ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * ++ SQUASHFS_META_NUMBER, GFP_KERNEL))) { ++ ERROR("Failed to allocate meta_index\n"); ++ goto failed; ++ } ++ for(i = 0; i < SQUASHFS_META_NUMBER; i++) { ++ msblk->meta_index[i].inode_number = 0; ++ msblk->meta_index[i].locked = 0; ++ } ++ msblk->next_meta_index = 0; ++ } ++ ++ for(i = SQUASHFS_META_NUMBER; i && ++ msblk->meta_index[msblk->next_meta_index].locked; i --) ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ if(i == 0) { ++ TRACE("empty_meta_index: failed!\n"); ++ goto failed; ++ } ++ ++ TRACE("empty_meta_index: returned meta entry %d, %p\n", ++ msblk->next_meta_index, ++ &msblk->meta_index[msblk->next_meta_index]); ++ ++ meta = &msblk->meta_index[msblk->next_meta_index]; ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ meta->inode_number = inode->i_ino; ++ meta->offset = offset; ++ meta->skip = skip; ++ meta->entries = 0; ++ meta->locked = 1; ++ ++failed: ++ up(&msblk->meta_index_mutex); ++ return meta; ++} ++ ++ ++void release_meta_index(struct inode *inode, struct meta_index *meta) ++{ ++ meta->locked = 0; ++} ++ ++ ++static int read_block_index(struct super_block *s, int blocks, char *block_list, ++ long long *start_block, int *offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned int *block_listp; ++ int block = 0; ++ ++ if (msblk->swap) { ++ char sblock_list[blocks << 2]; ++ ++ if (!squashfs_get_cached_block(s, sblock_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Unable to read block list [%llx:%x]\n", ++ *start_block, *offset); ++ goto failure; ++ } ++ SQUASHFS_SWAP_INTS(((unsigned int *)block_list), ++ ((unsigned int *)sblock_list), blocks); ++ } else ++ if (!squashfs_get_cached_block(s, block_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Unable to read block list [%llx:%x]\n", ++ *start_block, *offset); ++ goto failure; ++ } ++ ++ for (block_listp = (unsigned int *) block_list; blocks; ++ block_listp++, blocks --) ++ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); ++ ++ return block; ++ ++failure: ++ return -1; ++} ++ ++ ++#define SIZE 256 ++ ++static inline int calculate_skip(int blocks) { ++ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); ++ return skip >= 7 ? 7 : skip + 1; ++} ++ ++ ++static int get_meta_index(struct inode *inode, int index, ++ long long *index_block, int *index_offset, ++ long long *data_block, char *block_list) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); ++ int offset = 0; ++ struct meta_index *meta; ++ struct meta_entry *meta_entry; ++ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; ++ int cur_offset = SQUASHFS_I(inode)->offset; ++ long long cur_data_block = SQUASHFS_I(inode)->start_block; ++ int i; ++ ++ index /= SQUASHFS_META_INDEXES * skip; ++ ++ while ( offset < index ) { ++ meta = locate_meta_index(inode, index, offset + 1); ++ ++ if (meta == NULL) { ++ if ((meta = empty_meta_index(inode, offset + 1, ++ skip)) == NULL) ++ goto all_done; ++ } else { ++ offset = index < meta->offset + meta->entries ? index : ++ meta->offset + meta->entries - 1; ++ meta_entry = &meta->meta_entry[offset - meta->offset]; ++ cur_index_block = meta_entry->index_block + sblk->inode_table_start; ++ cur_offset = meta_entry->offset; ++ cur_data_block = meta_entry->data_block; ++ TRACE("get_meta_index: offset %d, meta->offset %d, " ++ "meta->entries %d\n", offset, meta->offset, ++ meta->entries); ++ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" ++ " data_block 0x%llx\n", cur_index_block, ++ cur_offset, cur_data_block); ++ } ++ ++ for (i = meta->offset + meta->entries; i <= index && ++ i < meta->offset + SQUASHFS_META_ENTRIES; i++) { ++ int blocks = skip * SQUASHFS_META_INDEXES; ++ ++ while (blocks) { ++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : ++ blocks; ++ int res = read_block_index(inode->i_sb, block, ++ block_list, &cur_index_block, ++ &cur_offset); ++ ++ if (res == -1) ++ goto failed; ++ ++ cur_data_block += res; ++ blocks -= block; ++ } ++ ++ meta_entry = &meta->meta_entry[i - meta->offset]; ++ meta_entry->index_block = cur_index_block - sblk->inode_table_start; ++ meta_entry->offset = cur_offset; ++ meta_entry->data_block = cur_data_block; ++ meta->entries ++; ++ offset ++; ++ } ++ ++ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", ++ meta->offset, meta->entries); ++ ++ release_meta_index(inode, meta); ++ } ++ ++all_done: ++ *index_block = cur_index_block; ++ *index_offset = cur_offset; ++ *data_block = cur_data_block; ++ ++ return offset * SQUASHFS_META_INDEXES * skip; ++ ++failed: ++ release_meta_index(inode, meta); ++ return -1; ++} ++ ++ ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize) ++{ ++ long long block_ptr; ++ int offset; ++ long long block; ++ int res = get_meta_index(inode, index, &block_ptr, &offset, &block, ++ block_list); ++ ++ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" ++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, ++ block); ++ ++ if(res == -1) ++ goto failure; ++ ++ index -= res; ++ ++ while ( index ) { ++ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; ++ int res = read_block_index(inode->i_sb, blocks, block_list, ++ &block_ptr, &offset); ++ if (res == -1) ++ goto failure; ++ block += res; ++ index -= blocks; ++ } ++ ++ if (read_block_index(inode->i_sb, 1, block_list, ++ &block_ptr, &offset) == -1) ++ goto failure; ++ *bsize = *((unsigned int *) block_list); ++ ++ return block; ++ ++failure: ++ return 0; ++} ++ ++ ++static int squashfs_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char block_list[SIZE]; ++ long long block; ++ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; ++ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); ++ void *pageaddr; ++ struct squashfs_fragment_cache *fragment = NULL; ++ char *data_ptr = msblk->read_page; ++ ++ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++ int start_index = page->index & ~mask; ++ int end_index = start_index | mask; ++ ++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", ++ page->index, ++ SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) ++ goto skip_read; ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < (i_size_read(inode) >> ++ sblk->block_log)) { ++ if ((block = (msblk->read_blocklist)(inode, index, 1, ++ block_list, NULL, &bsize)) == 0) ++ goto skip_read; ++ ++ down(&msblk->read_page_mutex); ++ ++ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, ++ block, bsize, NULL))) { ++ ERROR("Unable to read page, block %llx, size %x\n", block, ++ bsize); ++ up(&msblk->read_page_mutex); ++ goto skip_read; ++ } ++ } else { ++ if ((fragment = get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ SQUASHFS_I(inode)->u.s1.fragment_size)) ++ == NULL) { ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ (int) SQUASHFS_I(inode)-> ++ u.s1.fragment_size); ++ goto skip_read; ++ } ++ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + ++ (i_size_read(inode) & (sblk->block_size ++ - 1)); ++ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; ++ data_ptr = fragment->data; ++ } ++ ++ for (i = start_index; i <= end_index && byte_offset < bytes; ++ i++, byte_offset += PAGE_CACHE_SIZE) { ++ struct page *push_page; ++ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? ++ PAGE_CACHE_SIZE : bytes - byte_offset; ++ ++ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", ++ bytes, i, byte_offset, available_bytes); ++ ++ if (i == page->index) { ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memcpy(pageaddr, data_ptr + byte_offset, ++ available_bytes); ++ memset(pageaddr + available_bytes, 0, ++ PAGE_CACHE_SIZE - available_bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ } else if ((push_page = ++ grab_cache_page_nowait(page->mapping, i))) { ++ pageaddr = kmap_atomic(push_page, KM_USER0); ++ ++ memcpy(pageaddr, data_ptr + byte_offset, ++ available_bytes); ++ memset(pageaddr + available_bytes, 0, ++ PAGE_CACHE_SIZE - available_bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(push_page); ++ SetPageUptodate(push_page); ++ unlock_page(push_page); ++ page_cache_release(push_page); ++ } ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < (i_size_read(inode) >> ++ sblk->block_log)) ++ up(&msblk->read_page_mutex); ++ else ++ release_cached_fragment(msblk, fragment); ++ ++ return 0; ++ ++skip_read: ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++static int squashfs_readpage4K(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char block_list[SIZE]; ++ long long block; ++ unsigned int bsize, bytes = 0; ++ void *pageaddr; ++ ++ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", ++ page->index, ++ SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) { ++ pageaddr = kmap_atomic(page, KM_USER0); ++ goto skip_read; ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || page->index < (i_size_read(inode) >> ++ sblk->block_log)) { ++ block = (msblk->read_blocklist)(inode, page->index, 1, ++ block_list, NULL, &bsize); ++ ++ down(&msblk->read_page_mutex); ++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, ++ bsize, NULL); ++ pageaddr = kmap_atomic(page, KM_USER0); ++ if (bytes) ++ memcpy(pageaddr, msblk->read_page, bytes); ++ else ++ ERROR("Unable to read page, block %llx, size %x\n", ++ block, bsize); ++ up(&msblk->read_page_mutex); ++ } else { ++ struct squashfs_fragment_cache *fragment = ++ get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ SQUASHFS_I(inode)-> u.s1.fragment_size); ++ pageaddr = kmap_atomic(page, KM_USER0); ++ if (fragment) { ++ bytes = i_size_read(inode) & (sblk->block_size - 1); ++ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> ++ u.s1.fragment_offset, bytes); ++ release_cached_fragment(msblk, fragment); ++ } else ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, (int) ++ SQUASHFS_I(inode)-> u.s1.fragment_size); ++ } ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ f_pos =- 3; ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length + 3; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; ++ char str[SQUASHFS_NAME_LEN + 1]; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ return length + 3; ++} ++ ++ ++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++ dir_count; ++ struct squashfs_dir_header dirh; ++ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); ++ ++ while(file->f_pos < 3) { ++ char *name; ++ int size, i_ino; ++ ++ if(file->f_pos == 0) { ++ name = "."; ++ size = 1; ++ i_ino = i->i_ino; ++ } else { ++ name = ".."; ++ size = 2; ++ i_ino = SQUASHFS_I(i)->u.s2.parent_inode; ++ } ++ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", ++ (unsigned int) dirent, name, size, (int) ++ file->f_pos, i_ino, ++ squashfs_filetype_table[1]); ++ ++ if (filldir(dirent, name, size, ++ file->f_pos, i_ino, ++ squashfs_filetype_table[1]) < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos += size; ++ dirs_read++; ++ } ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ dirs_read++; ++ } ++ } ++ ++finish: ++ return dirs_read; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header dirh; ++ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; ++ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %d\n", name, ++ dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number); ++ ++ inode = (msblk->iget)(i->i_sb, ino); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++static void squashfs_put_super(struct super_block *s) ++{ ++ int i; ++ ++ if (s->s_fs_info) { ++ struct squashfs_sb_info *sbi = s->s_fs_info; ++ if (sbi->block_cache) ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ if (sbi->block_cache[i].block != ++ SQUASHFS_INVALID_BLK) ++ kfree(sbi->block_cache[i].data); ++ if (sbi->fragment) ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) ++ SQUASHFS_FREE(sbi->fragment[i].data); ++ kfree(sbi->fragment); ++ kfree(sbi->block_cache); ++ kfree(sbi->read_data); ++ kfree(sbi->read_page); ++ kfree(sbi->uid); ++ kfree(sbi->fragment_index); ++ kfree(sbi->fragment_index_2); ++ kfree(sbi->meta_index); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ } ++} ++ ++ ++static int squashfs_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, mnt); ++} ++ ++ ++static int __init init_squashfs_fs(void) ++{ ++ int err = init_inodecache(); ++ if (err) ++ goto out; ++ ++ printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " ++ "Phillip Lougher\n"); ++ ++ if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { ++ ERROR("Failed to allocate zlib workspace\n"); ++ destroy_inodecache(); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if ((err = register_filesystem(&squashfs_fs_type))) { ++ vfree(stream.workspace); ++ destroy_inodecache(); ++ } ++ ++out: ++ return err; ++} ++ ++ ++static void __exit exit_squashfs_fs(void) ++{ ++ vfree(stream.workspace); ++ unregister_filesystem(&squashfs_fs_type); ++ destroy_inodecache(); ++} ++ ++ ++static struct kmem_cache * squashfs_inode_cachep; ++ ++ ++static struct inode *squashfs_alloc_inode(struct super_block *sb) ++{ ++ struct squashfs_inode_info *ei; ++ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); ++ if (!ei) ++ return NULL; ++ return &ei->vfs_inode; ++} ++ ++ ++static void squashfs_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); ++} ++ ++ ++static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) ++{ ++ struct squashfs_inode_info *ei = foo; ++ ++ inode_init_once(&ei->vfs_inode); ++} ++ ++ ++static int __init init_inodecache(void) ++{ ++ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", ++ sizeof(struct squashfs_inode_info), ++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, ++ init_once); ++ if (squashfs_inode_cachep == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++ ++static void destroy_inodecache(void) ++{ ++ kmem_cache_destroy(squashfs_inode_cachep); ++} ++ ++ ++module_init(init_squashfs_fs); ++module_exit(exit_squashfs_fs); ++MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); ++MODULE_AUTHOR("Phillip Lougher "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/fs/squashfs/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux squashfs routines. ++# ++ ++obj-$(CONFIG_SQUASHFS) += squashfs.o ++squashfs-y += inode.o ++squashfs-y += squashfs2_0.o +--- /dev/null ++++ b/fs/squashfs/squashfs2_0.c +@@ -0,0 +1,758 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs2_0.c ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "squashfs.h" ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); ++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, ++ struct nameidata *); ++ ++static struct file_operations squashfs_dir_ops_2 = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir_2 ++}; ++ ++static struct inode_operations squashfs_dir_inode_ops_2 = { ++ .lookup = squashfs_lookup_2 ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static int read_fragment_index_table_2(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index_2, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ unsigned int fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), ++ &msblk->fragment_index_2[i], 1); ++ msblk->fragment_index_2[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int get_fragment_location_2(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); ++ struct squashfs_fragment_entry_2 fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry_2 sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ struct inode *i = new_inode(s); ++ ++ if (i) { ++ i->i_ino = ino; ++ i->i_mtime.tv_sec = sblk->mkfs_time; ++ i->i_atime.tv_sec = sblk->mkfs_time; ++ i->i_ctime.tv_sec = sblk->mkfs_time; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_nlink = 1; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++ } ++ ++ return i; ++} ++ ++ ++static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode) ++{ ++ struct inode *i; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned int block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ unsigned int ino = SQUASHFS_MK_VFS_INODE(block ++ - sblk->inode_table_start, offset); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header_2 id, sid; ++ struct squashfs_base_inode_header_2 *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ struct squashfs_reg_inode_header_2 *inodep = &id.reg; ++ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; ++ long long frag_blk; ++ unsigned int frag_size; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location_2(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ i->i_blksize = PAGE_CACHE_SIZE; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %x, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header_2 *inodep = &id.dir; ++ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header_2 *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header_2 *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header_2 *inodep = &id.dev; ++ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ insert_inode_hash(i); ++ return i; ++ ++failed_read: ++ ERROR("Unable to read inode [%x:%x]\n", block, offset); ++ ++failed_read1: ++ return NULL; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index_2 index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer; ++ char str[SQUASHFS_NAME_LEN + 1]; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index_2), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ return length; ++} ++ ++ ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ ++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, SQUASHFS_MK_VFS_INODE( ++ dirh.start_block, dire->offset), ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ dirs_read++; ++ } ++ } ++ ++finish: ++ return dirs_read; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN]; ++ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (sorted && name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %lld\n", name, ++ dirh.start_block, dire->offset, ino); ++ ++ inode = (msblk->iget)(i->i_sb, ino); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->iget = squashfs_iget_2; ++ msblk->read_fragment_index_table = read_fragment_index_table_2; ++ ++ sblk->bytes_used = sblk->bytes_used_2; ++ sblk->uid_start = sblk->uid_start_2; ++ sblk->guid_start = sblk->guid_start_2; ++ sblk->inode_table_start = sblk->inode_table_start_2; ++ sblk->directory_table_start = sblk->directory_table_start_2; ++ sblk->fragment_table_start = sblk->fragment_table_start_2; ++ ++ return 1; ++} +--- /dev/null ++++ b/fs/squashfs/squashfs.h +@@ -0,0 +1,86 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs.h ++ */ ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#endif ++ ++#ifdef SQUASHFS_TRACE ++#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) ++#else ++#define TRACE(s, args...) {} ++#endif ++ ++#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) ++ ++#define SERROR(s, args...) do { \ ++ if (!silent) \ ++ printk(KERN_ERR "SQUASHFS error: "s, ## args);\ ++ } while(0) ++ ++#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) ++ ++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) ++{ ++ return list_entry(inode, struct squashfs_inode_info, vfs_inode); ++} ++ ++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) ++#define SQSH_EXTERN ++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index); ++extern int squashfs_get_cached_block(struct super_block *s, char *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset); ++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment); ++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length); ++extern struct address_space_operations squashfs_symlink_aops; ++extern struct address_space_operations squashfs_aops; ++extern struct address_space_operations squashfs_aops_4K; ++extern struct inode_operations squashfs_dir_inode_ops; ++#else ++#define SQSH_EXTERN static ++#endif ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif +--- a/include/linux/magic.h ++++ b/include/linux/magic.h +@@ -35,6 +35,9 @@ + #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + ++#define SQUASHFS_MAGIC 0x73717368 ++#define SQUASHFS_MAGIC_SWAP 0x68737173 ++ + #define SMB_SUPER_MAGIC 0x517B + #define USBDEVICE_SUPER_MAGIC 0x9fa2 + +--- /dev/null ++++ b/include/linux/squashfs_fs.h +@@ -0,0 +1,911 @@ ++#ifndef SQUASHFS_FS ++#define SQUASHFS_FS ++ ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs.h ++ */ ++ ++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif ++ ++#ifdef CONFIG_SQUASHFS_VMALLOC ++#define SQUASHFS_ALLOC(a) vmalloc(a) ++#define SQUASHFS_FREE(a) vfree(a) ++#else ++#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL) ++#define SQUASHFS_FREE(a) kfree(a) ++#endif ++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE ++#define SQUASHFS_MAJOR 3 ++#define SQUASHFS_MINOR 0 ++#define SQUASHFS_START 0 ++ ++/* size of metadata (inode and directory) blocks */ ++#define SQUASHFS_METADATA_SIZE 8192 ++#define SQUASHFS_METADATA_LOG 13 ++ ++/* default size of data blocks */ ++#define SQUASHFS_FILE_SIZE 65536 ++#define SQUASHFS_FILE_LOG 16 ++ ++#define SQUASHFS_FILE_MAX_SIZE 65536 ++ ++/* Max number of uids and gids */ ++#define SQUASHFS_UIDS 256 ++#define SQUASHFS_GUIDS 255 ++ ++/* Max length of filename (not 255) */ ++#define SQUASHFS_NAME_LEN 256 ++ ++#define SQUASHFS_INVALID ((long long) 0xffffffffffff) ++#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) ++#define SQUASHFS_INVALID_BLK ((long long) -1) ++#define SQUASHFS_USED_BLK ((long long) -2) ++ ++/* Filesystem flags */ ++#define SQUASHFS_NOI 0 ++#define SQUASHFS_NOD 1 ++#define SQUASHFS_CHECK 2 ++#define SQUASHFS_NOF 3 ++#define SQUASHFS_NO_FRAG 4 ++#define SQUASHFS_ALWAYS_FRAG 5 ++#define SQUASHFS_DUPLICATE 6 ++ ++#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) ++ ++#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOI) ++ ++#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOD) ++ ++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOF) ++ ++#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NO_FRAG) ++ ++#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_ALWAYS_FRAG) ++ ++#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_DUPLICATE) ++ ++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_CHECK) ++ ++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ ++ duplicate_checking) (noi | (nod << 1) | (check_data << 2) \ ++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ ++ (duplicate_checking << 6)) ++ ++/* Max number of types and file types */ ++#define SQUASHFS_DIR_TYPE 1 ++#define SQUASHFS_FILE_TYPE 2 ++#define SQUASHFS_SYMLINK_TYPE 3 ++#define SQUASHFS_BLKDEV_TYPE 4 ++#define SQUASHFS_CHRDEV_TYPE 5 ++#define SQUASHFS_FIFO_TYPE 6 ++#define SQUASHFS_SOCKET_TYPE 7 ++#define SQUASHFS_LDIR_TYPE 8 ++#define SQUASHFS_LREG_TYPE 9 ++ ++/* 1.0 filesystem type definitions */ ++#define SQUASHFS_TYPES 5 ++#define SQUASHFS_IPC_TYPE 0 ++ ++/* Flag whether block is compressed or uncompressed, bit is set if block is ++ * uncompressed */ ++#define SQUASHFS_COMPRESSED_BIT (1 << 15) ++ ++#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ ++ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) ++ ++#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) ++ ++#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) ++ ++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) ++ ++#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) ++ ++/* ++ * Inode number ops. Inodes consist of a compressed block number, and an ++ * uncompressed offset within that block ++ */ ++#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) ++ ++#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) ++ ++#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ ++ << 16) + (B))) ++ ++/* Compute 32 bit VFS inode number from squashfs inode number */ ++#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ ++ ((b) >> 2) + 1)) ++/* XXX */ ++ ++/* Translate between VFS mode and squashfs mode */ ++#define SQUASHFS_MODE(a) ((a) & 0xfff) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry)) ++ ++#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ ++ sizeof(long long)) ++ ++/* cached data constants for filesystem */ ++#define SQUASHFS_CACHED_BLKS 8 ++ ++#define SQUASHFS_MAX_FILE_SIZE_LOG 64 ++ ++#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ ++ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) ++ ++#define SQUASHFS_MARKER_BYTE 0xff ++ ++/* meta index cache */ ++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) ++#define SQUASHFS_META_ENTRIES 31 ++#define SQUASHFS_META_NUMBER 8 ++#define SQUASHFS_SLOTS 4 ++ ++#include ++ ++struct meta_entry { ++ long long data_block; ++ unsigned int index_block; ++ unsigned short offset; ++ unsigned short pad; ++}; ++ ++struct meta_index { ++ unsigned int inode_number; ++ unsigned int offset; ++ unsigned short entries; ++ unsigned short skip; ++ unsigned short locked; ++ unsigned short pad; ++ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; ++}; ++ ++ ++/* ++ * definitions for structures on disk ++ */ ++ ++typedef long long squashfs_block_t; ++typedef long long squashfs_inode_t; ++ ++struct squashfs_super_block { ++ unsigned int s_magic; ++ unsigned int inodes; ++ unsigned int bytes_used_2; ++ unsigned int uid_start_2; ++ unsigned int guid_start_2; ++ unsigned int inode_table_start_2; ++ unsigned int directory_table_start_2; ++ unsigned int s_major:16; ++ unsigned int s_minor:16; ++ unsigned int block_size_1:16; ++ unsigned int block_log:16; ++ unsigned int flags:8; ++ unsigned int no_uids:8; ++ unsigned int no_guids:8; ++ unsigned int mkfs_time /* time of filesystem creation */; ++ squashfs_inode_t root_inode; ++ unsigned int block_size; ++ unsigned int fragments; ++ unsigned int fragment_table_start_2; ++ long long bytes_used; ++ long long uid_start; ++ long long guid_start; ++ long long inode_table_start; ++ long long directory_table_start; ++ long long fragment_table_start; ++ long long unused; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_index { ++ unsigned int index; ++ unsigned int start_block; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_BASE_INODE_HEADER \ ++ unsigned int inode_type:4; \ ++ unsigned int mode:12; \ ++ unsigned int uid:8; \ ++ unsigned int guid:8; \ ++ unsigned int mtime; \ ++ unsigned int inode_number; ++ ++struct squashfs_base_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_lreg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ long long file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int parent_inode; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int i_count:16; ++ unsigned int parent_inode; ++ struct squashfs_dir_index index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header { ++ struct squashfs_base_inode_header base; ++ struct squashfs_dev_inode_header dev; ++ struct squashfs_symlink_inode_header symlink; ++ struct squashfs_reg_inode_header reg; ++ struct squashfs_lreg_inode_header lreg; ++ struct squashfs_dir_inode_header dir; ++ struct squashfs_ldir_inode_header ldir; ++ struct squashfs_ipc_inode_header ipc; ++}; ++ ++struct squashfs_dir_entry { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ int inode_number:16; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_header { ++ unsigned int count:8; ++ unsigned int start_block; ++ unsigned int inode_number; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry { ++ long long start_block; ++ unsigned int size; ++ unsigned int unused; ++} __attribute__ ((packed)); ++ ++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); ++extern int squashfs_uncompress_init(void); ++extern int squashfs_uncompress_exit(void); ++ ++/* ++ * macros to convert each packed bitfield structure from little endian to big ++ * endian and vice versa. These are needed when creating or using a filesystem ++ * on a machine with different byte ordering to the target architecture. ++ * ++ */ ++ ++#define SQUASHFS_SWAP_START \ ++ int bits;\ ++ int b_pos;\ ++ unsigned long long val;\ ++ unsigned char *s;\ ++ unsigned char *d; ++ ++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ ++ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ ++ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ ++ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ ++ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ ++ SQUASHFS_SWAP((s)->flags, d, 288, 8);\ ++ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ ++ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ ++ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ ++ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ ++ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ ++ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ ++ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ ++ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ ++ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ ++ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ ++ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ ++ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ ++ SQUASHFS_SWAP((s)->unused, d, 888, 64);\ ++} ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 64, 32); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header))\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dev_inode_header)); \ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_reg_inode_header));\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_lreg_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 224, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 147, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 155, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ ++ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 8);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 32);\ ++} ++ ++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 2);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 16)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ ++} ++ ++#define SQUASHFS_SWAP_INTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 4);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 32)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 64)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * bits / 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ bits)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++ ++struct squashfs_base_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int type:4; ++ unsigned int offset:4; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 4);\ ++ SQUASHFS_SWAP((s)->guid, d, 20, 4); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header_1));\ ++ SQUASHFS_SWAP((s)->type, d, 24, 4);\ ++ SQUASHFS_SWAP((s)->offset, d, 28, 4);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_1));\ ++ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_1));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_1));\ ++ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_1));\ ++ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 43, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ ++} ++ ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++ ++struct squashfs_dir_index_2 { ++ unsigned int index:27; ++ unsigned int start_block:29; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_base_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++ unsigned int i_count:16; ++ struct squashfs_dir_index_2 index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header_2 { ++ struct squashfs_base_inode_header_2 base; ++ struct squashfs_dev_inode_header_2 dev; ++ struct squashfs_symlink_inode_header_2 symlink; ++ struct squashfs_reg_inode_header_2 reg; ++ struct squashfs_dir_inode_header_2 dir; ++ struct squashfs_ldir_inode_header_2 ldir; ++ struct squashfs_ipc_inode_header_2 ipc; ++}; ++ ++struct squashfs_dir_header_2 { ++ unsigned int count:8; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_entry_2 { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry_2 { ++ unsigned int start_block; ++ unsigned int size; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_2)); \ ++ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_2));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_2));\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 51, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 59, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ ++ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 27);\ ++ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ ++ SQUASHFS_SWAP((s)->size, d, 56, 8);\ ++} ++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 32, 32);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) ++ ++#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ ++ sizeof(int)) ++ ++#endif ++ ++#ifdef __KERNEL__ ++ ++/* ++ * macros used to swap each structure entry, taking into account ++ * bitfields and different bitfield placing conventions on differing ++ * architectures ++ */ ++ ++#include ++ ++#ifdef __BIG_ENDIAN ++ /* convert from little endian to big endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, b_pos) ++#else ++ /* convert from big endian to little endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, 64 - tbits - b_pos) ++#endif ++ ++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ ++ b_pos = pos % 8;\ ++ val = 0;\ ++ s = (unsigned char *)p + (pos / 8);\ ++ d = ((unsigned char *) &val) + 7;\ ++ for(bits = 0; bits < (tbits + b_pos); bits += 8) \ ++ *d-- = *s++;\ ++ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ ++} ++ ++#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); ++ ++#endif ++#endif +--- /dev/null ++++ b/include/linux/squashfs_fs_i.h +@@ -0,0 +1,45 @@ ++#ifndef SQUASHFS_FS_I ++#define SQUASHFS_FS_I ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_i.h ++ */ ++ ++struct squashfs_inode_info { ++ long long start_block; ++ unsigned int offset; ++ union { ++ struct { ++ long long fragment_start_block; ++ unsigned int fragment_size; ++ unsigned int fragment_offset; ++ long long block_list_start; ++ } s1; ++ struct { ++ long long directory_index_start; ++ unsigned int directory_index_offset; ++ unsigned int directory_index_count; ++ unsigned int parent_inode; ++ } s2; ++ } u; ++ struct inode vfs_inode; ++}; ++#endif +--- /dev/null ++++ b/include/linux/squashfs_fs_sb.h +@@ -0,0 +1,74 @@ ++#ifndef SQUASHFS_FS_SB ++#define SQUASHFS_FS_SB ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_sb.h ++ */ ++ ++#include ++ ++struct squashfs_cache { ++ long long block; ++ int length; ++ long long next_index; ++ char *data; ++}; ++ ++struct squashfs_fragment_cache { ++ long long block; ++ int length; ++ unsigned int locked; ++ char *data; ++}; ++ ++struct squashfs_sb_info { ++ struct squashfs_super_block sblk; ++ int devblksize; ++ int devblksize_log2; ++ int swap; ++ struct squashfs_cache *block_cache; ++ struct squashfs_fragment_cache *fragment; ++ int next_cache; ++ int next_fragment; ++ int next_meta_index; ++ unsigned int *uid; ++ unsigned int *guid; ++ long long *fragment_index; ++ unsigned int *fragment_index_2; ++ unsigned int read_size; ++ char *read_data; ++ char *read_page; ++ struct semaphore read_data_mutex; ++ struct semaphore read_page_mutex; ++ struct semaphore block_cache_mutex; ++ struct semaphore fragment_mutex; ++ struct semaphore meta_index_mutex; ++ wait_queue_head_t waitq; ++ wait_queue_head_t fragment_wait_queue; ++ struct meta_index *meta_index; ++ struct inode *(*iget)(struct super_block *s, squashfs_inode_t \ ++ inode); ++ long long (*read_blocklist)(struct inode *inode, int \ ++ index, int readahead_blks, char *block_list, \ ++ unsigned short **block_p, unsigned int *bsize); ++ int (*read_fragment_index_table)(struct super_block *s); ++}; ++#endif +--- a/init/do_mounts_rd.c ++++ b/init/do_mounts_rd.c +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -39,6 +40,7 @@ + * numbers could not be found. + * + * We currently check for the following magic numbers: ++ * squashfs + * minix + * ext2 + * romfs +@@ -53,6 +55,7 @@ + struct ext2_super_block *ext2sb; + struct romfs_super_block *romfsb; + struct cramfs_super *cramfsb; ++ struct squashfs_super_block *squashfsb; + int nblocks = -1; + unsigned char *buf; + +@@ -64,6 +67,7 @@ + ext2sb = (struct ext2_super_block *) buf; + romfsb = (struct romfs_super_block *) buf; + cramfsb = (struct cramfs_super *) buf; ++ squashfsb = (struct squashfs_super_block *) buf; + memset(buf, 0xe5, size); + + /* +@@ -101,6 +105,15 @@ + goto done; + } + ++ /* squashfs is at block zero too */ ++ if (squashfsb->s_magic == SQUASHFS_MAGIC) { ++ printk(KERN_NOTICE ++ "RAMDISK: squashfs filesystem found at block %d\n", ++ start_block); ++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; ++ goto done; ++ } ++ + /* + * Read block 1 to test for minix and ext2 superblock + */ diff --git a/target/linux/generic-2.6/patches-2.6.23/002-lzma_decompress.patch b/target/linux/generic-2.6/patches-2.6.23/002-lzma_decompress.patch new file mode 100644 index 0000000000..d6c7e311ee --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/002-lzma_decompress.patch @@ -0,0 +1,788 @@ +--- /dev/null ++++ b/include/linux/LzmaDecode.h +@@ -0,0 +1,100 @@ ++/* ++ LzmaDecode.h ++ LZMA Decoder interface ++ ++ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) ++ http://www.7-zip.org/ ++ ++ LZMA SDK is licensed under two licenses: ++ 1) GNU Lesser General Public License (GNU LGPL) ++ 2) Common Public License (CPL) ++ It means that you can select one of these two licenses and ++ follow rules of that license. ++ ++ SPECIAL EXCEPTION: ++ Igor Pavlov, as the author of this code, expressly permits you to ++ statically or dynamically link your code (or bind by name) to the ++ interfaces of this file without subjecting your linked code to the ++ terms of the CPL or GNU LGPL. Any modifications or additions ++ to this file, however, are subject to the LGPL or CPL terms. ++*/ ++ ++#ifndef __LZMADECODE_H ++#define __LZMADECODE_H ++ ++/* #define _LZMA_IN_CB */ ++/* Use callback for input data */ ++ ++/* #define _LZMA_OUT_READ */ ++/* Use read function for output data */ ++ ++/* #define _LZMA_PROB32 */ ++/* It can increase speed on some 32-bit CPUs, ++ but memory usage will be doubled in that case */ ++ ++/* #define _LZMA_LOC_OPT */ ++/* Enable local speed optimizations inside code */ ++ ++#ifndef UInt32 ++#ifdef _LZMA_UINT32_IS_ULONG ++#define UInt32 unsigned long ++#else ++#define UInt32 unsigned int ++#endif ++#endif ++ ++#ifdef _LZMA_PROB32 ++#define CProb UInt32 ++#else ++#define CProb unsigned short ++#endif ++ ++#define LZMA_RESULT_OK 0 ++#define LZMA_RESULT_DATA_ERROR 1 ++#define LZMA_RESULT_NOT_ENOUGH_MEM 2 ++ ++#ifdef _LZMA_IN_CB ++typedef struct _ILzmaInCallback ++{ ++ int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize); ++} ILzmaInCallback; ++#endif ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++/* ++bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb) ++bufferSize += 100 in case of _LZMA_OUT_READ ++by default CProb is unsigned short, ++but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int) ++*/ ++ ++#ifdef _LZMA_OUT_READ ++int LzmaDecoderInit( ++ unsigned char *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ unsigned char *dictionary, UInt32 dictionarySize, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ unsigned char *inStream, UInt32 inSize ++ #endif ++); ++#endif ++ ++int LzmaDecode( ++ unsigned char *buffer, ++ #ifndef _LZMA_OUT_READ ++ UInt32 bufferSize, ++ int lc, int lp, int pb, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback, ++ #else ++ unsigned char *inStream, UInt32 inSize, ++ #endif ++ #endif ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed); ++ ++#endif +--- /dev/null ++++ b/lib/LzmaDecode.c +@@ -0,0 +1,663 @@ ++/* ++ LzmaDecode.c ++ LZMA Decoder ++ ++ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) ++ http://www.7-zip.org/ ++ ++ LZMA SDK is licensed under two licenses: ++ 1) GNU Lesser General Public License (GNU LGPL) ++ 2) Common Public License (CPL) ++ It means that you can select one of these two licenses and ++ follow rules of that license. ++ ++ SPECIAL EXCEPTION: ++ Igor Pavlov, as the author of this code, expressly permits you to ++ statically or dynamically link your code (or bind by name) to the ++ interfaces of this file without subjecting your linked code to the ++ terms of the CPL or GNU LGPL. Any modifications or additions ++ to this file, however, are subject to the LGPL or CPL terms. ++*/ ++ ++#include ++ ++#ifndef Byte ++#define Byte unsigned char ++#endif ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++typedef struct _CRangeDecoder ++{ ++ Byte *Buffer; ++ Byte *BufferLim; ++ UInt32 Range; ++ UInt32 Code; ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *InCallback; ++ int Result; ++ #endif ++ int ExtraBytes; ++} CRangeDecoder; ++ ++Byte RangeDecoderReadByte(CRangeDecoder *rd) ++{ ++ if (rd->Buffer == rd->BufferLim) ++ { ++ #ifdef _LZMA_IN_CB ++ UInt32 size; ++ rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size); ++ rd->BufferLim = rd->Buffer + size; ++ if (size == 0) ++ #endif ++ { ++ rd->ExtraBytes = 1; ++ return 0xFF; ++ } ++ } ++ return (*rd->Buffer++); ++} ++ ++/* #define ReadByte (*rd->Buffer++) */ ++#define ReadByte (RangeDecoderReadByte(rd)) ++ ++void RangeDecoderInit(CRangeDecoder *rd, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ Byte *stream, UInt32 bufferSize ++ #endif ++ ) ++{ ++ int i; ++ #ifdef _LZMA_IN_CB ++ rd->InCallback = inCallback; ++ rd->Buffer = rd->BufferLim = 0; ++ #else ++ rd->Buffer = stream; ++ rd->BufferLim = stream + bufferSize; ++ #endif ++ rd->ExtraBytes = 0; ++ rd->Code = 0; ++ rd->Range = (0xFFFFFFFF); ++ for(i = 0; i < 5; i++) ++ rd->Code = (rd->Code << 8) | ReadByte; ++} ++ ++#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code; ++#define RC_FLUSH_VAR rd->Range = range; rd->Code = code; ++#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; } ++ ++UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits) ++{ ++ RC_INIT_VAR ++ UInt32 result = 0; ++ int i; ++ for (i = numTotalBits; i > 0; i--) ++ { ++ /* UInt32 t; */ ++ range >>= 1; ++ ++ result <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ result |= 1; ++ } ++ /* ++ t = (code - range) >> 31; ++ t &= 1; ++ code -= range & (t - 1); ++ result = (result + result) | (1 - t); ++ */ ++ RC_NORMALIZE ++ } ++ RC_FLUSH_VAR ++ return result; ++} ++ ++int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd) ++{ ++ UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob; ++ if (rd->Code < bound) ++ { ++ rd->Range = bound; ++ *prob += (kBitModelTotal - *prob) >> kNumMoveBits; ++ if (rd->Range < kTopValue) ++ { ++ rd->Code = (rd->Code << 8) | ReadByte; ++ rd->Range <<= 8; ++ } ++ return 0; ++ } ++ else ++ { ++ rd->Range -= bound; ++ rd->Code -= bound; ++ *prob -= (*prob) >> kNumMoveBits; ++ if (rd->Range < kTopValue) ++ { ++ rd->Code = (rd->Code << 8) | ReadByte; ++ rd->Range <<= 8; ++ } ++ return 1; ++ } ++} ++ ++#define RC_GET_BIT2(prob, mi, A0, A1) \ ++ UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \ ++ if (code < bound) \ ++ { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \ ++ else \ ++ { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \ ++ RC_NORMALIZE ++ ++#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;) ++ ++int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) ++{ ++ int mi = 1; ++ int i; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ for(i = numLevels; i > 0; i--) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + mi; ++ RC_GET_BIT(prob, mi) ++ #else ++ mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd); ++ #endif ++ } ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return mi - (1 << numLevels); ++} ++ ++int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) ++{ ++ int mi = 1; ++ int i; ++ int symbol = 0; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ for(i = 0; i < numLevels; i++) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + mi; ++ RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i)) ++ #else ++ int bit = RangeDecoderBitDecode(probs + mi, rd); ++ mi = mi + mi + bit; ++ symbol |= (bit << i); ++ #endif ++ } ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd) ++{ ++ int symbol = 1; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ do ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + symbol; ++ RC_GET_BIT(prob, symbol) ++ #else ++ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); ++ #endif ++ } ++ while (symbol < 0x100); ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte) ++{ ++ int symbol = 1; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ do ++ { ++ int bit; ++ int matchBit = (matchByte >> 7) & 1; ++ matchByte <<= 1; ++ #ifdef _LZMA_LOC_OPT ++ { ++ CProb *prob = probs + ((1 + matchBit) << 8) + symbol; ++ RC_GET_BIT2(prob, symbol, bit = 0, bit = 1) ++ } ++ #else ++ bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd); ++ symbol = (symbol << 1) | bit; ++ #endif ++ if (matchBit != bit) ++ { ++ while (symbol < 0x100) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + symbol; ++ RC_GET_BIT(prob, symbol) ++ #else ++ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); ++ #endif ++ } ++ break; ++ } ++ } ++ while (symbol < 0x100); ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState) ++{ ++ if(RangeDecoderBitDecode(p + LenChoice, rd) == 0) ++ return RangeDecoderBitTreeDecode(p + LenLow + ++ (posState << kLenNumLowBits), kLenNumLowBits, rd); ++ if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0) ++ return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid + ++ (posState << kLenNumMidBits), kLenNumMidBits, rd); ++ return kLenNumLowSymbols + kLenNumMidSymbols + ++ RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd); ++} ++ ++#define kNumStates 12 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#ifdef _LZMA_OUT_READ ++ ++typedef struct _LzmaVarState ++{ ++ CRangeDecoder RangeDecoder; ++ Byte *Dictionary; ++ UInt32 DictionarySize; ++ UInt32 DictionaryPos; ++ UInt32 GlobalPos; ++ UInt32 Reps[4]; ++ int lc; ++ int lp; ++ int pb; ++ int State; ++ int PreviousIsMatch; ++ int RemainLen; ++} LzmaVarState; ++ ++int LzmaDecoderInit( ++ unsigned char *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ unsigned char *dictionary, UInt32 dictionarySize, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ unsigned char *inStream, UInt32 inSize ++ #endif ++ ) ++{ ++ LzmaVarState *vs = (LzmaVarState *)buffer; ++ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); ++ UInt32 i; ++ if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState)) ++ return LZMA_RESULT_NOT_ENOUGH_MEM; ++ vs->Dictionary = dictionary; ++ vs->DictionarySize = dictionarySize; ++ vs->DictionaryPos = 0; ++ vs->GlobalPos = 0; ++ vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1; ++ vs->lc = lc; ++ vs->lp = lp; ++ vs->pb = pb; ++ vs->State = 0; ++ vs->PreviousIsMatch = 0; ++ vs->RemainLen = 0; ++ dictionary[dictionarySize - 1] = 0; ++ for (i = 0; i < numProbs; i++) ++ p[i] = kBitModelTotal >> 1; ++ RangeDecoderInit(&vs->RangeDecoder, ++ #ifdef _LZMA_IN_CB ++ inCallback ++ #else ++ inStream, inSize ++ #endif ++ ); ++ return LZMA_RESULT_OK; ++} ++ ++int LzmaDecode(unsigned char *buffer, ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed) ++{ ++ LzmaVarState *vs = (LzmaVarState *)buffer; ++ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); ++ CRangeDecoder rd = vs->RangeDecoder; ++ int state = vs->State; ++ int previousIsMatch = vs->PreviousIsMatch; ++ Byte previousByte; ++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; ++ UInt32 nowPos = 0; ++ UInt32 posStateMask = (1 << (vs->pb)) - 1; ++ UInt32 literalPosMask = (1 << (vs->lp)) - 1; ++ int lc = vs->lc; ++ int len = vs->RemainLen; ++ UInt32 globalPos = vs->GlobalPos; ++ ++ Byte *dictionary = vs->Dictionary; ++ UInt32 dictionarySize = vs->DictionarySize; ++ UInt32 dictionaryPos = vs->DictionaryPos; ++ ++ if (len == -1) ++ { ++ *outSizeProcessed = 0; ++ return LZMA_RESULT_OK; ++ } ++ ++ while(len > 0 && nowPos < outSize) ++ { ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ len--; ++ } ++ if (dictionaryPos == 0) ++ previousByte = dictionary[dictionarySize - 1]; ++ else ++ previousByte = dictionary[dictionaryPos - 1]; ++#else ++ ++int LzmaDecode( ++ Byte *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback, ++ #else ++ unsigned char *inStream, UInt32 inSize, ++ #endif ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); ++ CProb *p = (CProb *)buffer; ++ CRangeDecoder rd; ++ UInt32 i; ++ int state = 0; ++ int previousIsMatch = 0; ++ Byte previousByte = 0; ++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; ++ UInt32 nowPos = 0; ++ UInt32 posStateMask = (1 << pb) - 1; ++ UInt32 literalPosMask = (1 << lp) - 1; ++ int len = 0; ++ if (bufferSize < numProbs * sizeof(CProb)) ++ return LZMA_RESULT_NOT_ENOUGH_MEM; ++ for (i = 0; i < numProbs; i++) ++ p[i] = kBitModelTotal >> 1; ++ RangeDecoderInit(&rd, ++ #ifdef _LZMA_IN_CB ++ inCallback ++ #else ++ inStream, inSize ++ #endif ++ ); ++#endif ++ ++ *outSizeProcessed = 0; ++ while(nowPos < outSize) ++ { ++ int posState = (int)( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ & posStateMask); ++ #ifdef _LZMA_IN_CB ++ if (rd.Result != LZMA_RESULT_OK) ++ return rd.Result; ++ #endif ++ if (rd.ExtraBytes != 0) ++ return LZMA_RESULT_DATA_ERROR; ++ if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0) ++ { ++ CProb *probs = p + Literal + (LZMA_LIT_SIZE * ++ ((( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ & literalPosMask) << lc) + (previousByte >> (8 - lc)))); ++ ++ if (state < 4) state = 0; ++ else if (state < 10) state -= 3; ++ else state -= 6; ++ if (previousIsMatch) ++ { ++ Byte matchByte; ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ matchByte = dictionary[pos]; ++ #else ++ matchByte = outStream[nowPos - rep0]; ++ #endif ++ previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte); ++ previousIsMatch = 0; ++ } ++ else ++ previousByte = LzmaLiteralDecode(probs, &rd); ++ outStream[nowPos++] = previousByte; ++ #ifdef _LZMA_OUT_READ ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #endif ++ } ++ else ++ { ++ previousIsMatch = 1; ++ if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1) ++ { ++ if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0) ++ { ++ if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0) ++ { ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos; ++ #endif ++ if ( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ == 0) ++ return LZMA_RESULT_DATA_ERROR; ++ state = state < 7 ? 9 : 11; ++ #ifdef _LZMA_OUT_READ ++ pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ previousByte = dictionary[pos]; ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #else ++ previousByte = outStream[nowPos - rep0]; ++ #endif ++ outStream[nowPos++] = previousByte; ++ continue; ++ } ++ } ++ else ++ { ++ UInt32 distance; ++ if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0) ++ distance = rep1; ++ else ++ { ++ if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0) ++ distance = rep2; ++ else ++ { ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ len = LzmaLenDecode(p + RepLenCoder, &rd, posState); ++ state = state < 7 ? 8 : 11; ++ } ++ else ++ { ++ int posSlot; ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ state = state < 7 ? 7 : 10; ++ len = LzmaLenDecode(p + LenCoder, &rd, posState); ++ posSlot = RangeDecoderBitTreeDecode(p + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits), kNumPosSlotBits, &rd); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits); ++ if (posSlot < kEndPosModelIndex) ++ { ++ rep0 += RangeDecoderReverseBitTreeDecode( ++ p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd); ++ } ++ else ++ { ++ rep0 += RangeDecoderDecodeDirectBits(&rd, ++ numDirectBits - kNumAlignBits) << kNumAlignBits; ++ rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd); ++ } ++ } ++ else ++ rep0 = posSlot; ++ rep0++; ++ } ++ if (rep0 == (UInt32)(0)) ++ { ++ /* it's for stream version */ ++ len = -1; ++ break; ++ } ++ if (rep0 > nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ { ++ return LZMA_RESULT_DATA_ERROR; ++ } ++ len += kMatchMinLen; ++ do ++ { ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ previousByte = dictionary[pos]; ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #else ++ previousByte = outStream[nowPos - rep0]; ++ #endif ++ outStream[nowPos++] = previousByte; ++ len--; ++ } ++ while(len > 0 && nowPos < outSize); ++ } ++ } ++ ++ #ifdef _LZMA_OUT_READ ++ vs->RangeDecoder = rd; ++ vs->DictionaryPos = dictionaryPos; ++ vs->GlobalPos = globalPos + nowPos; ++ vs->Reps[0] = rep0; ++ vs->Reps[1] = rep1; ++ vs->Reps[2] = rep2; ++ vs->Reps[3] = rep3; ++ vs->State = state; ++ vs->PreviousIsMatch = previousIsMatch; ++ vs->RemainLen = len; ++ #endif ++ ++ *outSizeProcessed = nowPos; ++ return LZMA_RESULT_OK; ++} +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -13,7 +13,7 @@ + lib-y += kobject.o kref.o kobject_uevent.o klist.o + + obj-y += div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ +- bust_spinlocks.o hexdump.o kasprintf.o ++ bust_spinlocks.o hexdump.o kasprintf.o LzmaDecode.o + + ifeq ($(CONFIG_DEBUG_KOBJECT),y) + CFLAGS_kobject.o += -DDEBUG +@@ -62,6 +62,7 @@ + obj-$(CONFIG_AUDIT_GENERIC) += audit.o + + obj-$(CONFIG_SWIOTLB) += swiotlb.o ++ + obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o + + lib-$(CONFIG_GENERIC_BUG) += bug.o diff --git a/target/linux/generic-2.6/patches-2.6.23/003-squashfs_lzma.patch b/target/linux/generic-2.6/patches-2.6.23/003-squashfs_lzma.patch new file mode 100644 index 0000000000..16cc873199 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/003-squashfs_lzma.patch @@ -0,0 +1,107 @@ +--- a/fs/squashfs/inode.c ++++ b/fs/squashfs/inode.c +@@ -4,6 +4,9 @@ + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher + * ++ * LZMA decompressor support added by Oleg I. Vdovikin ++ * Copyright (c) 2005 Oleg I.Vdovikin ++ * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, +@@ -21,6 +24,7 @@ + * inode.c + */ + ++#define SQUASHFS_LZMA + #include + #include + #include +@@ -44,6 +48,19 @@ + + #include "squashfs.h" + ++#ifdef SQUASHFS_LZMA ++#include ++ ++/* default LZMA settings, should be in sync with mksquashfs */ ++#define LZMA_LC 3 ++#define LZMA_LP 0 ++#define LZMA_PB 2 ++ ++#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \ ++ (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb)) ++ ++#endif ++ + static void squashfs_put_super(struct super_block *); + static int squashfs_statfs(struct dentry *, struct kstatfs *); + static int squashfs_symlink_readpage(struct file *file, struct page *page); +@@ -64,7 +81,11 @@ + const char *, void *, struct vfsmount *); + + ++#ifdef SQUASHFS_LZMA ++static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE]; ++#else + static z_stream stream; ++#endif + + static struct file_system_type squashfs_fs_type = { + .owner = THIS_MODULE, +@@ -249,6 +270,15 @@ + if (compressed) { + int zlib_err; + ++#ifdef SQUASHFS_LZMA ++ if ((zlib_err = LzmaDecode(lzma_workspace, ++ LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB, ++ c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK) ++ { ++ ERROR("lzma returned unexpected result 0x%x\n", zlib_err); ++ bytes = 0; ++ } ++#else + stream.next_in = c_buffer; + stream.avail_in = c_byte; + stream.next_out = buffer; +@@ -263,7 +293,7 @@ + bytes = 0; + } else + bytes = stream.total_out; +- ++#endif + up(&msblk->read_data_mutex); + } + +@@ -2045,15 +2075,19 @@ + printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " + "Phillip Lougher\n"); + ++#ifndef SQUASHFS_LZMA + if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { + ERROR("Failed to allocate zlib workspace\n"); + destroy_inodecache(); + err = -ENOMEM; + goto out; + } ++#endif + + if ((err = register_filesystem(&squashfs_fs_type))) { ++#ifndef SQUASHFS_LZMA + vfree(stream.workspace); ++#endif + destroy_inodecache(); + } + +@@ -2064,7 +2098,9 @@ + + static void __exit exit_squashfs_fs(void) + { ++#ifndef SQUASHFS_LZMA + vfree(stream.workspace); ++#endif + unregister_filesystem(&squashfs_fs_type); + destroy_inodecache(); + } diff --git a/target/linux/generic-2.6/patches-2.6.23/004-extra_optimization.patch b/target/linux/generic-2.6/patches-2.6.23/004-extra_optimization.patch new file mode 100644 index 0000000000..7ee86d4bbb --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/004-extra_optimization.patch @@ -0,0 +1,12 @@ +--- a/Makefile ++++ b/Makefile +@@ -508,6 +508,9 @@ + NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) + CHECKFLAGS += $(NOSTDINC_FLAGS) + ++# improve gcc optimization ++CFLAGS += $(call cc-option,-funit-at-a-time,) ++ + # warn about C99 declaration after statement + CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) + diff --git a/target/linux/generic-2.6/patches-2.6.23/006-gcc4_inline_fix.patch b/target/linux/generic-2.6/patches-2.6.23/006-gcc4_inline_fix.patch new file mode 100644 index 0000000000..fac27e8a56 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/006-gcc4_inline_fix.patch @@ -0,0 +1,11 @@ +--- a/include/asm-mips/system.h ++++ b/include/asm-mips/system.h +@@ -181,7 +181,7 @@ + if something tries to do an invalid xchg(). */ + extern void __xchg_called_with_bad_pointer(void); + +-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) ++static __always_inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) + { + switch (size) { + case 4: diff --git a/target/linux/generic-2.6/patches-2.6.23/007-samsung_flash.patch b/target/linux/generic-2.6/patches-2.6.23/007-samsung_flash.patch new file mode 100644 index 0000000000..335ffd9f93 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/007-samsung_flash.patch @@ -0,0 +1,36 @@ +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -51,6 +51,7 @@ + #define SST49LF040B 0x0050 + #define SST49LF008A 0x005a + #define AT49BV6416 0x00d6 ++#define MANUFACTURER_SAMSUNG 0x00ec + + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -294,12 +295,19 @@ + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { +- printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); +- kfree(mtd); +- return NULL; ++ if (cfi->mfr == MANUFACTURER_SAMSUNG && ++ (extp->MajorVersion == '3' && extp->MinorVersion == '3')) { ++ printk(KERN_NOTICE " Newer Samsung flash detected, " ++ "should be compatibile with Amd/Fujitsu.\n"); ++ } ++ else { ++ printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " ++ "version %c.%c.\n", extp->MajorVersion, ++ extp->MinorVersion); ++ kfree(extp); ++ kfree(mtd); ++ return NULL; ++ } + } + + /* Install our own private info structure */ diff --git a/target/linux/generic-2.6/patches-2.6.23/009-revert_intel_flash_breakage.patch b/target/linux/generic-2.6/patches-2.6.23/009-revert_intel_flash_breakage.patch new file mode 100644 index 0000000000..51b84a7459 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/009-revert_intel_flash_breakage.patch @@ -0,0 +1,169 @@ +--- a/drivers/mtd/chips/cfi_cmdset_0001.c ++++ b/drivers/mtd/chips/cfi_cmdset_0001.c +@@ -933,7 +933,7 @@ + + static int __xipram xip_wait_for_operation( + struct map_info *map, struct flchip *chip, +- unsigned long adr, unsigned int chip_op_time ) ++ unsigned long adr, int *chip_op_time ) + { + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; +@@ -942,7 +942,7 @@ + flstate_t oldstate, newstate; + + start = xip_currtime(); +- usec = chip_op_time * 8; ++ usec = *chip_op_time * 8; + if (usec == 0) + usec = 500000; + done = 0; +@@ -1052,8 +1052,8 @@ + #define XIP_INVAL_CACHED_RANGE(map, from, size) \ + INVALIDATE_CACHED_RANGE(map, from, size) + +-#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec) \ +- xip_wait_for_operation(map, chip, cmd_adr, usec) ++#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, p_usec) \ ++ xip_wait_for_operation(map, chip, cmd_adr, p_usec) + + #else + +@@ -1065,65 +1065,65 @@ + static int inval_cache_and_wait_for_operation( + struct map_info *map, struct flchip *chip, + unsigned long cmd_adr, unsigned long inval_adr, int inval_len, +- unsigned int chip_op_time) ++ int *chip_op_time ) + { + struct cfi_private *cfi = map->fldrv_priv; + map_word status, status_OK = CMD(0x80); +- int chip_state = chip->state; +- unsigned int timeo, sleep_time; ++ int z, chip_state = chip->state; ++ unsigned long timeo; + + spin_unlock(chip->mutex); + if (inval_len) + INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); ++ if (*chip_op_time) ++ cfi_udelay(*chip_op_time); + spin_lock(chip->mutex); + +- /* set our timeout to 8 times the expected delay */ +- timeo = chip_op_time * 8; +- if (!timeo) +- timeo = 500000; +- sleep_time = chip_op_time / 2; ++ timeo = *chip_op_time * 8 * HZ / 1000000; ++ if (timeo < HZ/2) ++ timeo = HZ/2; ++ timeo += jiffies; + ++ z = 0; + for (;;) { ++ if (chip->state != chip_state) { ++ /* Someone's suspended the operation: sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ timeo = jiffies + (HZ / 2); /* FIXME */ ++ spin_lock(chip->mutex); ++ continue; ++ } ++ + status = map_read(map, cmd_adr); + if (map_word_andequal(map, status, status_OK, status_OK)) + break; + +- if (!timeo) { ++ /* OK Still waiting */ ++ if (time_after(jiffies, timeo)) { + map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + return -ETIME; + } + +- /* OK Still waiting. Drop the lock, wait a while and retry. */ ++ /* Latency issues. Drop the lock, wait a while and retry */ ++ z++; + spin_unlock(chip->mutex); +- if (sleep_time >= 1000000/HZ) { +- /* +- * Half of the normal delay still remaining +- * can be performed with a sleeping delay instead +- * of busy waiting. +- */ +- msleep(sleep_time/1000); +- timeo -= sleep_time; +- sleep_time = 1000000/HZ; +- } else { +- udelay(1); +- cond_resched(); +- timeo--; +- } ++ cfi_udelay(1); + spin_lock(chip->mutex); +- +- while (chip->state != chip_state) { +- /* Someone's suspended the operation: sleep */ +- DECLARE_WAITQUEUE(wait, current); +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- spin_lock(chip->mutex); +- } + } + ++ if (!z) { ++ if (!--(*chip_op_time)) ++ *chip_op_time = 1; ++ } else if (z > 1) ++ ++(*chip_op_time); ++ + /* Done and happy. */ + chip->state = FL_STATUS; + return 0; +@@ -1132,7 +1132,8 @@ + #endif + + #define WAIT_TIMEOUT(map, chip, adr, udelay) \ +- INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay); ++ ({ int __udelay = (udelay); \ ++ INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, &__udelay); }) + + + static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) +@@ -1356,7 +1357,7 @@ + + ret = INVAL_CACHE_AND_WAIT(map, chip, adr, + adr, map_bankwidth(map), +- chip->word_write_time); ++ &chip->word_write_time); + if (ret) { + xip_enable(map, chip, adr); + printk(KERN_ERR "%s: word write error (status timeout)\n", map->name); +@@ -1593,7 +1594,7 @@ + + ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, + adr, len, +- chip->buffer_write_time); ++ &chip->buffer_write_time); + if (ret) { + map_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; +@@ -1728,7 +1729,7 @@ + + ret = INVAL_CACHE_AND_WAIT(map, chip, adr, + adr, len, +- chip->erase_time); ++ &chip->erase_time); + if (ret) { + map_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; diff --git a/target/linux/generic-2.6/patches-2.6.23/010-disable_old_squashfs_compatibility.patch b/target/linux/generic-2.6/patches-2.6.23/010-disable_old_squashfs_compatibility.patch new file mode 100644 index 0000000000..01e27573bc --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/010-disable_old_squashfs_compatibility.patch @@ -0,0 +1,19 @@ +--- a/fs/squashfs/Makefile ++++ b/fs/squashfs/Makefile +@@ -4,4 +4,3 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += inode.o +-squashfs-y += squashfs2_0.o +--- a/fs/squashfs/squashfs.h ++++ b/fs/squashfs/squashfs.h +@@ -24,6 +24,9 @@ + #ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY + #undef CONFIG_SQUASHFS_1_0_COMPATIBILITY + #endif ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif + + #ifdef SQUASHFS_TRACE + #define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) diff --git a/target/linux/generic-2.6/patches-2.6.23/011-mips_boot.patch b/target/linux/generic-2.6/patches-2.6.23/011-mips_boot.patch new file mode 100644 index 0000000000..1bea326a21 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/011-mips_boot.patch @@ -0,0 +1,11 @@ +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -130,6 +130,8 @@ + #endif + .endm + ++ j kernel_entry ++ nop + #ifndef CONFIG_NO_EXCEPT_FILL + /* + * Reserved space for exception handlers. diff --git a/target/linux/generic-2.6/patches-2.6.23/012-mips_cpu_tlb.patch b/target/linux/generic-2.6/patches-2.6.23/012-mips_cpu_tlb.patch new file mode 100644 index 0000000000..b750df7546 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/012-mips_cpu_tlb.patch @@ -0,0 +1,18 @@ +--- a/arch/mips/mm/tlbex.c ++++ b/arch/mips/mm/tlbex.c +@@ -902,7 +902,6 @@ + case CPU_R10000: + case CPU_R12000: + case CPU_R14000: +- case CPU_4KC: + case CPU_SB1: + case CPU_SB1A: + case CPU_4KSC: +@@ -933,6 +932,7 @@ + tlbw(p); + break; + ++ case CPU_4KC: + case CPU_4KEC: + case CPU_24K: + case CPU_34K: diff --git a/target/linux/generic-2.6/patches-2.6.23/013-mips_gdb_stub.patch b/target/linux/generic-2.6/patches-2.6.23/013-mips_gdb_stub.patch new file mode 100644 index 0000000000..b50a0dedc5 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/013-mips_gdb_stub.patch @@ -0,0 +1,18 @@ +--- a/arch/mips/kernel/gdb-stub.c ++++ b/arch/mips/kernel/gdb-stub.c +@@ -656,6 +656,7 @@ + *epc = (unsigned long)async_breakpoint; + } + ++#ifdef CONFIG_SMP + static void kgdb_wait(void *arg) + { + unsigned flags; +@@ -668,6 +669,7 @@ + + local_irq_restore(flags); + } ++#endif + + /* + * GDB stub needs to call kgdb_wait on all processor with interrupts diff --git a/target/linux/generic-2.6/patches-2.6.23/060-block2mtd_init.patch b/target/linux/generic-2.6/patches-2.6.23/060-block2mtd_init.patch new file mode 100644 index 0000000000..e92499faf0 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/060-block2mtd_init.patch @@ -0,0 +1,112 @@ +--- a/drivers/mtd/devices/block2mtd.c ++++ b/drivers/mtd/devices/block2mtd.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -237,10 +238,11 @@ + + + /* FIXME: ensure that mtd->size % erase_size == 0 */ +-static struct block2mtd_dev *add_device(char *devname, int erase_size) ++static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) + { + struct block_device *bdev; + struct block2mtd_dev *dev; ++ struct mtd_partition *part; + + if (!devname) + return NULL; +@@ -279,14 +281,18 @@ + + /* Setup the MTD structure */ + /* make the name contain the block device in */ +- dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname), +- GFP_KERNEL); ++ ++ if (!mtdname) ++ mtdname = devname; ++ ++ dev->mtd.name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL); ++ + if (!dev->mtd.name) + goto devinit_err; ++ ++ strcpy(dev->mtd.name, mtdname); + +- sprintf(dev->mtd.name, "block2mtd: %s", devname); +- +- dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; ++ dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(erase_size - 1); + dev->mtd.erasesize = erase_size; + dev->mtd.writesize = 1; + dev->mtd.type = MTD_RAM; +@@ -298,15 +304,18 @@ + dev->mtd.read = block2mtd_read; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; +- +- if (add_mtd_device(&dev->mtd)) { ++ ++ part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); ++ part->name = dev->mtd.name; ++ part->offset = 0; ++ part->size = dev->mtd.size; ++ if (add_mtd_partitions(&dev->mtd, part, 1)) { + /* Device didnt get added, so free the entry */ + goto devinit_err; + } + list_add(&dev->list, &blkmtd_device_list); + INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, +- dev->mtd.name + strlen("blkmtd: "), +- dev->mtd.erasesize >> 10, dev->mtd.erasesize); ++ mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize); + return dev; + + devinit_err: +@@ -379,9 +388,9 @@ + + static int block2mtd_setup2(const char *val) + { +- char buf[80 + 12]; /* 80 for device, 12 for erase size */ ++ char buf[80 + 12 + 80]; /* 80 for device, 12 for erase size, 80 for name */ + char *str = buf; +- char *token[2]; ++ char *token[3]; + char *name; + size_t erase_size = PAGE_SIZE; + int i, ret; +@@ -392,7 +401,7 @@ + strcpy(str, val); + kill_final_newline(str); + +- for (i = 0; i < 2; i++) ++ for (i = 0; i < 3; i++) + token[i] = strsep(&str, ","); + + if (str) +@@ -412,8 +421,10 @@ + parse_err("illegal erase size"); + } + } ++ if (token[2] && (strlen(token[2]) + 1 > 80)) ++ parse_err("mtd device name too long"); + +- add_device(name, erase_size); ++ add_device(name, erase_size, token[2]); + + return 0; + } +@@ -447,7 +458,7 @@ + + + module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); +-MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,]\""); ++MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=[,[,]]\""); + + static int __init block2mtd_init(void) + { diff --git a/target/linux/generic-2.6/patches-2.6.23/065-rootfs_split.patch b/target/linux/generic-2.6/patches-2.6.23/065-rootfs_split.patch new file mode 100644 index 0000000000..7be0911c35 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/065-rootfs_split.patch @@ -0,0 +1,944 @@ +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -47,6 +47,16 @@ + devices. Partitioning on NFTL 'devices' is a different - that's the + 'normal' form of partitioning used on a block device. + ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ depends on MTD_PARTITIONS ++ default y ++ ++config MTD_ROOTFS_SPLIT ++ bool "Automatically split 'rootfs' partition for squashfs" ++ depends on MTD_PARTITIONS ++ default y ++ + config MTD_REDBOOT_PARTS + tristate "RedBoot partition table parsing" + depends on MTD_PARTITIONS +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -20,6 +20,8 @@ + #include + #include + #include ++#include ++#include + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -39,7 +41,7 @@ + * the pointer to that structure with this macro. + */ + #define PART(x) ((struct mtd_part *)(x)) +- ++#define IS_PART(mtd) (mtd->read == part_read) + + /* + * MTD methods which simply translate the effective address and pass through +@@ -308,6 +310,312 @@ + return 0; + } + ++static u_int32_t cur_offset = 0; ++static int add_one_partition(struct mtd_info *master, const struct mtd_partition *part, ++ int i, struct mtd_part **slp) ++{ ++ struct mtd_part *slave; ++ ++ /* allocate the partition structure */ ++ slave = kzalloc (sizeof(*slave), GFP_KERNEL); ++ if (!slave) { ++ printk ("memory allocation error while creating partitions for \"%s\"\n", ++ master->name); ++ del_mtd_partitions(master); ++ return -ENOMEM; ++ } ++ list_add(&slave->list, &mtd_partitions); ++ ++ /* set up the MTD object for this partition */ ++ slave->mtd.type = master->type; ++ slave->mtd.flags = master->flags & ~part->mask_flags; ++ slave->mtd.size = part->size; ++ slave->mtd.writesize = master->writesize; ++ slave->mtd.oobsize = master->oobsize; ++ slave->mtd.oobavail = master->oobavail; ++ slave->mtd.subpage_sft = master->subpage_sft; ++ ++ slave->mtd.name = part->name; ++ slave->mtd.owner = master->owner; ++ ++ slave->mtd.read = part_read; ++ slave->mtd.write = part_write; ++ slave->mtd.refresh_device = part->refresh_partition; ++ ++ if(master->point && master->unpoint){ ++ slave->mtd.point = part_point; ++ slave->mtd.unpoint = part_unpoint; ++ } ++ ++ if (master->read_oob) ++ slave->mtd.read_oob = part_read_oob; ++ if (master->write_oob) ++ slave->mtd.write_oob = part_write_oob; ++ if(master->read_user_prot_reg) ++ slave->mtd.read_user_prot_reg = part_read_user_prot_reg; ++ if(master->read_fact_prot_reg) ++ slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; ++ if(master->write_user_prot_reg) ++ slave->mtd.write_user_prot_reg = part_write_user_prot_reg; ++ if(master->lock_user_prot_reg) ++ slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; ++ if(master->get_user_prot_info) ++ slave->mtd.get_user_prot_info = part_get_user_prot_info; ++ if(master->get_fact_prot_info) ++ slave->mtd.get_fact_prot_info = part_get_fact_prot_info; ++ if (master->sync) ++ slave->mtd.sync = part_sync; ++ if (!i && master->suspend && master->resume) { ++ slave->mtd.suspend = part_suspend; ++ slave->mtd.resume = part_resume; ++ } ++ if (master->writev) ++ slave->mtd.writev = part_writev; ++ if (master->lock) ++ slave->mtd.lock = part_lock; ++ if (master->unlock) ++ slave->mtd.unlock = part_unlock; ++ if (master->block_isbad) ++ slave->mtd.block_isbad = part_block_isbad; ++ if (master->block_markbad) ++ slave->mtd.block_markbad = part_block_markbad; ++ slave->mtd.erase = part_erase; ++ slave->master = master; ++ slave->offset = part->offset; ++ slave->index = i; ++ ++ if (slave->offset == MTDPART_OFS_APPEND) ++ slave->offset = cur_offset; ++ if (slave->offset == MTDPART_OFS_NXTBLK) { ++ slave->offset = cur_offset; ++ if ((cur_offset % master->erasesize) != 0) { ++ /* Round up to next erasesize */ ++ slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; ++ printk(KERN_NOTICE "Moving partition %d: " ++ "0x%08x -> 0x%08x\n", i, ++ cur_offset, slave->offset); ++ } ++ } ++ if (slave->mtd.size == MTDPART_SIZ_FULL) ++ slave->mtd.size = master->size - slave->offset; ++ cur_offset = slave->offset + slave->mtd.size; ++ ++ printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, ++ slave->offset + slave->mtd.size, slave->mtd.name); ++ ++ /* let's do some sanity checks */ ++ if (slave->offset >= master->size) { ++ /* let's register it anyway to preserve ordering */ ++ slave->offset = 0; ++ slave->mtd.size = 0; ++ printk ("mtd: partition \"%s\" is out of reach -- disabled\n", ++ part->name); ++ } ++ if (slave->offset + slave->mtd.size > master->size) { ++ slave->mtd.size = master->size - slave->offset; ++ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", ++ part->name, master->name, slave->mtd.size); ++ } ++ if (master->numeraseregions>1) { ++ /* Deal with variable erase size stuff */ ++ int i; ++ struct mtd_erase_region_info *regions = master->eraseregions; ++ ++ /* Find the first erase regions which is part of this partition. */ ++ for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) ++ ; ++ ++ for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { ++ if (slave->mtd.erasesize < regions[i].erasesize) { ++ slave->mtd.erasesize = regions[i].erasesize; ++ } ++ } ++ } else { ++ /* Single erase size */ ++ slave->mtd.erasesize = master->erasesize; ++ } ++ ++ if ((slave->mtd.flags & MTD_WRITEABLE) && ++ (slave->offset % slave->mtd.erasesize)) { ++ /* Doesn't start on a boundary of major erase size */ ++ /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ ++ slave->mtd.flags &= ~MTD_WRITEABLE; ++ printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", ++ part->name); ++ } ++ if ((slave->mtd.flags & MTD_WRITEABLE) && ++ (slave->mtd.size % slave->mtd.erasesize)) { ++ slave->mtd.flags &= ~MTD_WRITEABLE; ++ printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", ++ part->name); ++ } ++ ++ slave->mtd.ecclayout = master->ecclayout; ++ if (master->block_isbad) { ++ uint32_t offs = 0; ++ ++ while(offs < slave->mtd.size) { ++ if (master->block_isbad(master, ++ offs + slave->offset)) ++ slave->mtd.ecc_stats.badblocks++; ++ offs += slave->mtd.erasesize; ++ } ++ } ++ ++ if(part->mtdp) ++ { /* store the object pointer (caller may or may not register it */ ++ *part->mtdp = &slave->mtd; ++ slave->registered = 0; ++ } ++ else ++ { ++ /* register our partition */ ++ add_mtd_device(&slave->mtd); ++ slave->registered = 1; ++ } ++ ++ if (slp) ++ *slp = slave; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++#define ROOTFS_SPLIT_NAME "rootfs_data" ++#define ROOTFS_REMOVED_NAME "" ++static int split_squashfs(struct mtd_info *master, int offset, int *split_offset) ++{ ++ char buf[512]; ++ struct squashfs_super_block *sb = (struct squashfs_super_block *) buf; ++ int len, ret; ++ ++ ret = master->read(master, offset, sizeof(*sb), &len, buf); ++ if (ret || (len != sizeof(*sb))) { ++ printk(KERN_ALERT "split_squashfs: error occured while reading " ++ "from \"%s\"\n", master->name); ++ return -EINVAL; ++ } ++ ++ if (*((u32 *) buf) != SQUASHFS_MAGIC) { ++ printk(KERN_ALERT "split_squashfs: no squashfs found in \"%s\"\n", ++ master->name); ++ *split_offset = 0; ++ return 0; ++ } ++ ++ if (sb->bytes_used <= 0) { ++ printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n", ++ master->name); ++ *split_offset = 0; ++ return 0; ++ } ++ ++ len = (u32) sb->bytes_used; ++ len += (offset & 0x000fffff); ++ len += (master->erasesize - 1); ++ len &= ~(master->erasesize - 1); ++ len -= (offset & 0x000fffff); ++ *split_offset = offset + len; ++ ++ return 0; ++} ++ ++static int split_rootfs_data(struct mtd_info *master, struct mtd_info *rpart, struct mtd_partition *part, ++ int index) ++{ ++ struct mtd_partition *dpart; ++ struct mtd_part *slave = NULL; ++ int split_offset = 0; ++ int ret; ++ ++ ret = split_squashfs(master, part->offset, &split_offset); ++ if (ret) ++ return ret; ++ ++ if (split_offset <= 0) ++ return 0; ++ ++ dpart = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL); ++ if (dpart == NULL) { ++ printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n", ++ ROOTFS_SPLIT_NAME); ++ return -ENOMEM; ++ } ++ ++ memcpy(dpart, part, sizeof(*part)); ++ dpart->name = (unsigned char *)&dpart[1]; ++ strcpy(dpart->name, ROOTFS_SPLIT_NAME); ++ ++ dpart->size -= split_offset - dpart->offset; ++ dpart->offset = split_offset; ++ ++ if (dpart == NULL) ++ return 1; ++ ++ printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%X, len=%X \n", ++ ROOTFS_SPLIT_NAME, dpart->offset, dpart->size); ++ ++ ret = add_one_partition(master, dpart, index, &slave); ++ if (ret) ++ kfree(dpart); ++ else if (slave) ++ rpart->split = &slave->mtd; ++ ++ return ret; ++} ++ ++static int refresh_rootfs_split(struct mtd_info *mtd) ++{ ++ struct mtd_partition tpart; ++ struct mtd_part *part; ++ int index = 0; ++ int offset, size; ++ int ret; ++ ++ part = PART(mtd); ++ ++ /* check for the new squashfs offset first */ ++ ret = split_squashfs(part->master, part->offset, &offset); ++ if (ret) ++ return ret; ++ ++ if ((offset > 0) && !mtd->split) { ++ printk(KERN_INFO "%s: creating new split partition for \"%s\"\n", __func__, mtd->name); ++ /* if we don't have a rootfs split partition, create a new one */ ++ tpart.name = mtd->name; ++ tpart.size = mtd->size; ++ tpart.offset = part->offset; ++ ++ /* find the index of the last partition */ ++ if (!list_empty(&mtd_partitions)) ++ index = list_first_entry(&mtd_partitions, struct mtd_part, list)->index + 1; ++ ++ return split_rootfs_data(part->master, &part->mtd, &tpart, index); ++ } else if ((offset > 0) && mtd->split) { ++ /* update the offsets of the existing partition */ ++ size = mtd->size + part->offset - offset; ++ ++ part = PART(mtd->split); ++ part->offset = offset; ++ part->mtd.size = size; ++ printk(KERN_INFO "%s: %s partition \"" ROOTFS_SPLIT_NAME "\", offset: 0x%06x (0x%06x)\n", ++ __func__, (!strcmp(part->mtd.name, ROOTFS_SPLIT_NAME) ? "updating" : "creating"), ++ part->offset, part->mtd.size); ++ strcpy(part->mtd.name, ROOTFS_SPLIT_NAME); ++ } else if ((offset <= 0) && mtd->split) { ++ printk(KERN_INFO "%s: removing partition \"%s\"\n", __func__, mtd->split->name); ++ ++ /* mark existing partition as removed */ ++ part = PART(mtd->split); ++ strcpy(part->mtd.name, ROOTFS_REMOVED_NAME); ++ part->offset = 0; ++ part->mtd.size = 0; ++ } ++ ++ return 0; ++} ++#endif /* CONFIG_MTD_ROOTFS_SPLIT */ ++ + /* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to +@@ -320,168 +628,31 @@ + int nbparts) + { + struct mtd_part *slave; +- u_int32_t cur_offset = 0; +- int i; ++ struct mtd_partition *part; ++ int i, j, ret = 0; + + printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + +- for (i = 0; i < nbparts; i++) { +- +- /* allocate the partition structure */ +- slave = kzalloc (sizeof(*slave), GFP_KERNEL); +- if (!slave) { +- printk ("memory allocation error while creating partitions for \"%s\"\n", +- master->name); +- del_mtd_partitions(master); +- return -ENOMEM; +- } +- list_add(&slave->list, &mtd_partitions); +- +- /* set up the MTD object for this partition */ +- slave->mtd.type = master->type; +- slave->mtd.flags = master->flags & ~parts[i].mask_flags; +- slave->mtd.size = parts[i].size; +- slave->mtd.writesize = master->writesize; +- slave->mtd.oobsize = master->oobsize; +- slave->mtd.oobavail = master->oobavail; +- slave->mtd.subpage_sft = master->subpage_sft; +- +- slave->mtd.name = parts[i].name; +- slave->mtd.owner = master->owner; +- +- slave->mtd.read = part_read; +- slave->mtd.write = part_write; +- +- if(master->point && master->unpoint){ +- slave->mtd.point = part_point; +- slave->mtd.unpoint = part_unpoint; +- } +- +- if (master->read_oob) +- slave->mtd.read_oob = part_read_oob; +- if (master->write_oob) +- slave->mtd.write_oob = part_write_oob; +- if(master->read_user_prot_reg) +- slave->mtd.read_user_prot_reg = part_read_user_prot_reg; +- if(master->read_fact_prot_reg) +- slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; +- if(master->write_user_prot_reg) +- slave->mtd.write_user_prot_reg = part_write_user_prot_reg; +- if(master->lock_user_prot_reg) +- slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; +- if(master->get_user_prot_info) +- slave->mtd.get_user_prot_info = part_get_user_prot_info; +- if(master->get_fact_prot_info) +- slave->mtd.get_fact_prot_info = part_get_fact_prot_info; +- if (master->sync) +- slave->mtd.sync = part_sync; +- if (!i && master->suspend && master->resume) { +- slave->mtd.suspend = part_suspend; +- slave->mtd.resume = part_resume; +- } +- if (master->writev) +- slave->mtd.writev = part_writev; +- if (master->lock) +- slave->mtd.lock = part_lock; +- if (master->unlock) +- slave->mtd.unlock = part_unlock; +- if (master->block_isbad) +- slave->mtd.block_isbad = part_block_isbad; +- if (master->block_markbad) +- slave->mtd.block_markbad = part_block_markbad; +- slave->mtd.erase = part_erase; +- slave->master = master; +- slave->offset = parts[i].offset; +- slave->index = i; +- +- if (slave->offset == MTDPART_OFS_APPEND) +- slave->offset = cur_offset; +- if (slave->offset == MTDPART_OFS_NXTBLK) { +- slave->offset = cur_offset; +- if ((cur_offset % master->erasesize) != 0) { +- /* Round up to next erasesize */ +- slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; +- printk(KERN_NOTICE "Moving partition %d: " +- "0x%08x -> 0x%08x\n", i, +- cur_offset, slave->offset); +- } +- } +- if (slave->mtd.size == MTDPART_SIZ_FULL) +- slave->mtd.size = master->size - slave->offset; +- cur_offset = slave->offset + slave->mtd.size; +- +- printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, +- slave->offset + slave->mtd.size, slave->mtd.name); +- +- /* let's do some sanity checks */ +- if (slave->offset >= master->size) { +- /* let's register it anyway to preserve ordering */ +- slave->offset = 0; +- slave->mtd.size = 0; +- printk ("mtd: partition \"%s\" is out of reach -- disabled\n", +- parts[i].name); +- } +- if (slave->offset + slave->mtd.size > master->size) { +- slave->mtd.size = master->size - slave->offset; +- printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", +- parts[i].name, master->name, slave->mtd.size); +- } +- if (master->numeraseregions>1) { +- /* Deal with variable erase size stuff */ +- int i; +- struct mtd_erase_region_info *regions = master->eraseregions; +- +- /* Find the first erase regions which is part of this partition. */ +- for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) +- ; +- +- for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { +- if (slave->mtd.erasesize < regions[i].erasesize) { +- slave->mtd.erasesize = regions[i].erasesize; +- } ++ for (i = 0, j = 0; i < nbparts; i++) { ++ part = (struct mtd_partition *) &parts[i]; ++ ret = add_one_partition(master, part, j, &slave); ++ if (ret) ++ return ret; ++ j++; ++ ++ if (strcmp(part->name, "rootfs") == 0 && slave->registered) { ++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV ++ if (ROOT_DEV == 0) { ++ printk(KERN_NOTICE "mtd: partition \"rootfs\" " ++ "set to be root filesystem\n"); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index); + } +- } else { +- /* Single erase size */ +- slave->mtd.erasesize = master->erasesize; +- } +- +- if ((slave->mtd.flags & MTD_WRITEABLE) && +- (slave->offset % slave->mtd.erasesize)) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ +- slave->mtd.flags &= ~MTD_WRITEABLE; +- printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", +- parts[i].name); +- } +- if ((slave->mtd.flags & MTD_WRITEABLE) && +- (slave->mtd.size % slave->mtd.erasesize)) { +- slave->mtd.flags &= ~MTD_WRITEABLE; +- printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", +- parts[i].name); +- } +- +- slave->mtd.ecclayout = master->ecclayout; +- if (master->block_isbad) { +- uint32_t offs = 0; +- +- while(offs < slave->mtd.size) { +- if (master->block_isbad(master, +- offs + slave->offset)) +- slave->mtd.ecc_stats.badblocks++; +- offs += slave->mtd.erasesize; +- } +- } +- +- if(parts[i].mtdp) +- { /* store the object pointer (caller may or may not register it */ +- *parts[i].mtdp = &slave->mtd; +- slave->registered = 0; +- } +- else +- { +- /* register our partition */ +- add_mtd_device(&slave->mtd); +- slave->registered = 1; ++#endif ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++ ret = split_rootfs_data(master, &slave->mtd, part, j); ++ if (ret == 0) ++ j++; ++#endif + } + } + +@@ -557,6 +728,32 @@ + return ret; + } + ++int refresh_mtd_partitions(struct mtd_info *mtd) ++{ ++ int ret = 0; ++ ++ if (IS_PART(mtd)) { ++ struct mtd_part *part; ++ struct mtd_info *master; ++ ++ part = PART(mtd); ++ master = part->master; ++ if (master->refresh_device) ++ ret = master->refresh_device(master); ++ } ++ ++ if (!ret && mtd->refresh_device) ++ ret = mtd->refresh_device(mtd); ++ ++#ifdef CONFIG_MTD_ROOTFS_SPLIT ++ if (!ret && IS_PART(mtd) && !strcmp(mtd->name, "rootfs")) ++ refresh_rootfs_split(mtd); ++#endif ++ ++ return 0; ++} ++ + EXPORT_SYMBOL_GPL(parse_mtd_partitions); ++EXPORT_SYMBOL_GPL(refresh_mtd_partitions); + EXPORT_SYMBOL_GPL(register_mtd_parser); + EXPORT_SYMBOL_GPL(deregister_mtd_parser); +--- a/drivers/mtd/devices/block2mtd.c ++++ b/drivers/mtd/devices/block2mtd.c +@@ -34,6 +34,8 @@ + struct block_device *blkdev; + struct mtd_info mtd; + struct mutex write_mutex; ++ rwlock_t bdev_mutex; ++ char devname[0]; + }; + + +@@ -86,6 +88,12 @@ + size_t len = instr->len; + int err; + ++ read_lock(&dev->bdev_mutex); ++ if (!dev->blkdev) { ++ err = -EINVAL; ++ goto done; ++ } ++ + instr->state = MTD_ERASING; + mutex_lock(&dev->write_mutex); + err = _block2mtd_erase(dev, from, len); +@@ -98,6 +106,10 @@ + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); ++ ++done: ++ read_unlock(&dev->bdev_mutex); ++ + return err; + } + +@@ -109,10 +121,14 @@ + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SIZE-1); +- int cpylen; ++ int cpylen, err = 0; ++ ++ read_lock(&dev->bdev_mutex); ++ if (!dev->blkdev || (from > mtd->size)) { ++ err = -EINVAL; ++ goto done; ++ } + +- if (from > mtd->size) +- return -EINVAL; + if (from + len > mtd->size) + len = mtd->size - from; + +@@ -127,10 +143,14 @@ + len = len - cpylen; + + page = page_read(dev->blkdev->bd_inode->i_mapping, index); +- if (!page) +- return -ENOMEM; +- if (IS_ERR(page)) +- return PTR_ERR(page); ++ if (!page) { ++ err = -ENOMEM; ++ goto done; ++ } ++ if (IS_ERR(page)) { ++ err = PTR_ERR(page); ++ goto done; ++ } + + memcpy(buf, page_address(page) + offset, cpylen); + page_cache_release(page); +@@ -141,7 +161,10 @@ + offset = 0; + index++; + } +- return 0; ++ ++done: ++ read_unlock(&dev->bdev_mutex); ++ return err; + } + + +@@ -193,12 +216,22 @@ + size_t *retlen, const u_char *buf) + { + struct block2mtd_dev *dev = mtd->priv; +- int err; ++ int err = 0; ++ ++ read_lock(&dev->bdev_mutex); ++ if (!dev->blkdev) { ++ err = -EINVAL; ++ goto done; ++ } + + if (!len) +- return 0; +- if (to >= mtd->size) +- return -ENOSPC; ++ goto done; ++ ++ if (to >= mtd->size) { ++ err = -ENOSPC; ++ goto done; ++ } ++ + if (to + len > mtd->size) + len = mtd->size - to; + +@@ -207,6 +240,9 @@ + mutex_unlock(&dev->write_mutex); + if (err > 0) + err = 0; ++ ++done: ++ read_unlock(&dev->bdev_mutex); + return err; + } + +@@ -215,51 +251,29 @@ + static void block2mtd_sync(struct mtd_info *mtd) + { + struct block2mtd_dev *dev = mtd->priv; +- sync_blockdev(dev->blkdev); +- return; +-} +- +- +-static void block2mtd_free_device(struct block2mtd_dev *dev) +-{ +- if (!dev) +- return; +- +- kfree(dev->mtd.name); + +- if (dev->blkdev) { +- invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, +- 0, -1); +- close_bdev_excl(dev->blkdev); +- } ++ read_lock(&dev->bdev_mutex); ++ if (dev->blkdev) ++ sync_blockdev(dev->blkdev); ++ read_unlock(&dev->bdev_mutex); + +- kfree(dev); ++ return; + } + + +-/* FIXME: ensure that mtd->size % erase_size == 0 */ +-static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) ++static int _open_bdev(struct block2mtd_dev *dev) + { + struct block_device *bdev; +- struct block2mtd_dev *dev; +- struct mtd_partition *part; +- +- if (!devname) +- return NULL; +- +- dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL); +- if (!dev) +- return NULL; + + /* Get a handle on the device */ +- bdev = open_bdev_excl(devname, O_RDWR, NULL); ++ bdev = open_bdev_excl(dev->devname, O_RDWR, NULL); + #ifndef MODULE + if (IS_ERR(bdev)) { + + /* We might not have rootfs mounted at this point. Try + to resolve the device name by other means. */ + +- dev_t devt = name_to_dev_t(devname); ++ dev_t devt = name_to_dev_t(dev->devname); + if (devt) { + bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); + } +@@ -267,17 +281,96 @@ + #endif + + if (IS_ERR(bdev)) { +- ERROR("error: cannot open device %s", devname); +- goto devinit_err; ++ ERROR("error: cannot open device %s", dev->devname); ++ return 1; + } + dev->blkdev = bdev; + + if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { + ERROR("attempting to use an MTD device as a block device"); +- goto devinit_err; ++ return 1; + } + ++ return 0; ++} ++ ++static void _close_bdev(struct block2mtd_dev *dev) ++{ ++ struct block_device *bdev; ++ ++ if (!dev->blkdev) ++ return; ++ ++ bdev = dev->blkdev; ++ invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, 0, -1); ++ close_bdev_excl(dev->blkdev); ++ dev->blkdev = NULL; ++} ++ ++static void block2mtd_free_device(struct block2mtd_dev *dev) ++{ ++ if (!dev) ++ return; ++ ++ kfree(dev->mtd.name); ++ _close_bdev(dev); ++ kfree(dev); ++} ++ ++ ++static int block2mtd_refresh(struct mtd_info *mtd) ++{ ++ struct block2mtd_dev *dev = mtd->priv; ++ struct block_device *bdev; ++ dev_t devt; ++ int err = 0; ++ ++ /* no other mtd function can run at this point */ ++ write_lock(&dev->bdev_mutex); ++ ++ /* get the device number for the whole disk */ ++ devt = MKDEV(MAJOR(dev->blkdev->bd_dev), 0); ++ ++ /* close the old block device */ ++ _close_bdev(dev); ++ ++ /* open the whole disk, issue a partition rescan, then */ ++ bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); ++ if (!bdev || !bdev->bd_disk) ++ err = -EINVAL; ++ else { ++ err = rescan_partitions(bdev->bd_disk, bdev); ++ } ++ if (bdev) ++ close_bdev_excl(bdev); ++ ++ /* try to open the partition block device again */ ++ _open_bdev(dev); ++ write_unlock(&dev->bdev_mutex); ++ ++ return err; ++} ++ ++/* FIXME: ensure that mtd->size % erase_size == 0 */ ++static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname) ++{ ++ struct block2mtd_dev *dev; ++ struct mtd_partition *part; ++ ++ if (!devname) ++ return NULL; ++ ++ dev = kzalloc(sizeof(struct block2mtd_dev) + strlen(devname) + 1, GFP_KERNEL); ++ if (!dev) ++ return NULL; ++ ++ strcpy(dev->devname, devname); ++ ++ if (_open_bdev(dev)) ++ goto devinit_err; ++ + mutex_init(&dev->write_mutex); ++ rwlock_init(&dev->bdev_mutex); + + /* Setup the MTD structure */ + /* make the name contain the block device in */ +@@ -304,6 +397,7 @@ + dev->mtd.read = block2mtd_read; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; ++ dev->mtd.refresh_device = block2mtd_refresh; + + part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL); + part->name = dev->mtd.name; +--- a/drivers/mtd/mtdchar.c ++++ b/drivers/mtd/mtdchar.c +@@ -17,6 +17,7 @@ + + #include + #include ++#include + + #include + +@@ -753,6 +754,13 @@ + file->f_pos = 0; + break; + } ++#ifdef CONFIG_MTD_PARTITIONS ++ case MTDREFRESH: ++ { ++ ret = refresh_mtd_partitions(mtd); ++ break; ++ } ++#endif + + default: + ret = -ENOTTY; +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -98,6 +98,7 @@ + uint8_t *oobbuf; + }; + ++struct mtd_info; + struct mtd_info { + u_char type; + u_int32_t flags; +@@ -195,6 +196,9 @@ + struct module *owner; + int usecount; + ++ int (*refresh_device)(struct mtd_info *mtd); ++ struct mtd_info *split; ++ + /* If the driver is something smart, like UBI, it may need to maintain + * its own reference counting. The below functions are only for driver. + * The driver may register its callbacks. These callbacks are not +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -36,6 +36,7 @@ + * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). + */ + ++struct mtd_partition; + struct mtd_partition { + char *name; /* identifier string */ + u_int32_t size; /* partition size */ +@@ -43,6 +44,7 @@ + u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ + struct mtd_info **mtdp; /* pointer to store the MTD object */ ++ int (*refresh_partition)(struct mtd_info *); + }; + + #define MTDPART_OFS_NXTBLK (-2) +@@ -52,6 +54,7 @@ + + int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); + int del_mtd_partitions(struct mtd_info *); ++int refresh_mtd_partitions(struct mtd_info *); + + /* + * Functions dealing with the various ways of partitioning the space +--- a/include/mtd/mtd-abi.h ++++ b/include/mtd/mtd-abi.h +@@ -95,6 +95,7 @@ + #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) + #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) + #define MTDFILEMODE _IO('M', 19) ++#define MTDREFRESH _IO('M', 23) + + /* + * Obsolete legacy interface. Keep it in order not to break userspace diff --git a/target/linux/generic-2.6/patches-2.6.23/070-redboot_space.patch b/target/linux/generic-2.6/patches-2.6.23/070-redboot_space.patch new file mode 100644 index 0000000000..b4d57cb535 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/070-redboot_space.patch @@ -0,0 +1,30 @@ +--- a/drivers/mtd/redboot.c ++++ b/drivers/mtd/redboot.c +@@ -236,14 +236,21 @@ + #endif + names += strlen(names)+1; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { +- i++; +- parts[i].offset = parts[i-1].size + parts[i-1].offset; +- parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = nullname; +- } ++ if (!strcmp(parts[i].name, "rootfs")) { ++ parts[i].size = fl->next->img->flash_base; ++ parts[i].size &= ~(master->erasesize - 1); ++ parts[i].size -= parts[i].offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ nrparts--; ++ } else { ++ i++; ++ parts[i].offset = parts[i-1].size + parts[i-1].offset; ++ parts[i].size = fl->next->img->flash_base - parts[i].offset; ++ parts[i].name = nullname; + #endif ++ } ++ } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); diff --git a/target/linux/generic-2.6/patches-2.6.23/080-mtd_plat_nand_chip_fixup.patch b/target/linux/generic-2.6/patches-2.6.23/080-mtd_plat_nand_chip_fixup.patch new file mode 100644 index 0000000000..4653898cba --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/080-mtd_plat_nand_chip_fixup.patch @@ -0,0 +1,32 @@ +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -572,6 +572,7 @@ + int chip_delay; + unsigned int options; + const char **part_probe_types; ++ int (*chip_fixup)(struct mtd_info *mtd); + void *priv; + }; + +--- a/drivers/mtd/nand/plat_nand.c ++++ b/drivers/mtd/nand/plat_nand.c +@@ -70,7 +70,18 @@ + platform_set_drvdata(pdev, data); + + /* Scan to find existance of the device */ +- if (nand_scan(&data->mtd, 1)) { ++ if (nand_scan_ident(&data->mtd, 1)) { ++ res = -ENXIO; ++ goto out; ++ } ++ ++ if (pdata->chip.chip_fixup) { ++ res = pdata->chip.chip_fixup(&data->mtd); ++ if (res) ++ goto out; ++ } ++ ++ if (nand_scan_tail(&data->mtd)) { + res = -ENXIO; + goto out; + } diff --git a/target/linux/generic-2.6/patches-2.6.23/100-netfilter_layer7_2.17.patch b/target/linux/generic-2.6/patches-2.6.23/100-netfilter_layer7_2.17.patch new file mode 100644 index 0000000000..d94fd3c0c9 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/100-netfilter_layer7_2.17.patch @@ -0,0 +1,2101 @@ +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -633,6 +633,27 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_MATCH_LAYER7 ++ tristate '"layer7" match support' ++ depends on NETFILTER_XTABLES ++ depends on EXPERIMENTAL && (IP_NF_CONNTRACK || NF_CONNTRACK) ++ depends on NF_CT_ACCT ++ help ++ Say Y if you want to be able to classify connections (and their ++ packets) based on regular expression matching of their application ++ layer data. This is one way to classify applications such as ++ peer-to-peer filesharing systems that do not always use the same ++ port. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config NETFILTER_XT_MATCH_LAYER7_DEBUG ++ bool 'Layer 7 debugging output' ++ depends on NETFILTER_XT_MATCH_LAYER7 ++ help ++ Say Y to get lots of debugging output. ++ ++ + config NETFILTER_XT_MATCH_STATISTIC + tristate '"statistic" match support' + depends on NETFILTER_XTABLES +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -71,6 +71,7 @@ + obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o + obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o + obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o + obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o +--- /dev/null ++++ b/net/netfilter/xt_layer7.c +@@ -0,0 +1,634 @@ ++/* ++ Kernel module to match application layer (OSI layer 7) data in connections. ++ ++ http://l7-filter.sf.net ++ ++ (C) 2003, 2004, 2005, 2006, 2007 Matthew Strait and Ethan Sommer. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version ++ 2 of the License, or (at your option) any later version. ++ http://www.gnu.org/licenses/gpl.txt ++ ++ Based on ipt_string.c (C) 2000 Emmanuel Roger , ++ xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait, ++ Ethan Sommer, Justin Levandoski. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "regexp/regexp.c" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matthew Strait , Ethan Sommer "); ++MODULE_DESCRIPTION("iptables application layer match module"); ++MODULE_ALIAS("ipt_layer7"); ++MODULE_VERSION("2.17"); ++ ++static int maxdatalen = 2048; // this is the default ++module_param(maxdatalen, int, 0444); ++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); ++#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG ++ #define DPRINTK(format,args...) printk(format,##args) ++#else ++ #define DPRINTK(format,args...) ++#endif ++ ++#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \ ++ master_conntrack->counters[IP_CT_DIR_REPLY].packets ++ ++/* Number of packets whose data we look at. ++This can be modified through /proc/net/layer7_numpackets */ ++static int num_packets = 10; ++ ++static struct pattern_cache { ++ char * regex_string; ++ regexp * pattern; ++ struct pattern_cache * next; ++} * first_pattern_cache = NULL; ++ ++DEFINE_SPINLOCK(l7_lock); ++ ++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++/* Converts an unfriendly string into a friendly one by ++replacing unprintables with periods and all whitespace with " ". */ ++static char * friendly_print(unsigned char * s) ++{ ++ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!f) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "friendly_print, bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++){ ++ if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; ++ else if(isspace(s[i])) f[i] = ' '; ++ else f[i] = '.'; ++ } ++ f[i] = '\0'; ++ return f; ++} ++ ++static char dec2hex(int i) ++{ ++ switch (i) { ++ case 0 ... 9: ++ return (i + '0'); ++ break; ++ case 10 ... 15: ++ return (i - 10 + 'a'); ++ break; ++ default: ++ if (net_ratelimit()) ++ printk("layer7: Problem in dec2hex\n"); ++ return '\0'; ++ } ++} ++ ++static char * hex_print(unsigned char * s) ++{ ++ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!g) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in hex_print, " ++ "bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++) { ++ g[i*3 ] = dec2hex(s[i]/16); ++ g[i*3 + 1] = dec2hex(s[i]%16); ++ g[i*3 + 2] = ' '; ++ } ++ g[i*3] = '\0'; ++ ++ return g; ++} ++#endif // DEBUG ++ ++/* Use instead of regcomp. As we expect to be seeing the same regexps over and ++over again, it make sense to cache the results. */ ++static regexp * compile_and_cache(const char * regex_string, ++ const char * protocol) ++{ ++ struct pattern_cache * node = first_pattern_cache; ++ struct pattern_cache * last_pattern_cache = first_pattern_cache; ++ struct pattern_cache * tmp; ++ unsigned int len; ++ ++ while (node != NULL) { ++ if (!strcmp(node->regex_string, regex_string)) ++ return node->pattern; ++ ++ last_pattern_cache = node;/* points at the last non-NULL node */ ++ node = node->next; ++ } ++ ++ /* If we reach the end of the list, then we have not yet cached ++ the pattern for this regex. Let's do that now. ++ Be paranoid about running out of memory to avoid list corruption. */ ++ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); ++ ++ if(!tmp) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "compile_and_cache, bailing.\n"); ++ return NULL; ++ } ++ ++ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); ++ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); ++ tmp->next = NULL; ++ ++ if(!tmp->regex_string || !tmp->pattern) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "compile_and_cache, bailing.\n"); ++ kfree(tmp->regex_string); ++ kfree(tmp->pattern); ++ kfree(tmp); ++ return NULL; ++ } ++ ++ /* Ok. The new node is all ready now. */ ++ node = tmp; ++ ++ if(first_pattern_cache == NULL) /* list is empty */ ++ first_pattern_cache = node; /* make node the beginning */ ++ else ++ last_pattern_cache->next = node; /* attach node to the end */ ++ ++ /* copy the string and compile the regex */ ++ len = strlen(regex_string); ++ DPRINTK("About to compile this: \"%s\"\n", regex_string); ++ node->pattern = regcomp((char *)regex_string, &len); ++ if ( !node->pattern ) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: Error compiling regexp " ++ "\"%s\" (%s)\n", ++ regex_string, protocol); ++ /* pattern is now cached as NULL, so we won't try again. */ ++ } ++ ++ strcpy(node->regex_string, regex_string); ++ return node->pattern; ++} ++ ++static int can_handle(const struct sk_buff *skb) ++{ ++ if(!ip_hdr(skb)) /* not IP */ ++ return 0; ++ if(ip_hdr(skb)->protocol != IPPROTO_TCP && ++ ip_hdr(skb)->protocol != IPPROTO_UDP && ++ ip_hdr(skb)->protocol != IPPROTO_ICMP) ++ return 0; ++ return 1; ++} ++ ++/* Returns offset the into the skb->data that the application data starts */ ++static int app_data_offset(const struct sk_buff *skb) ++{ ++ /* In case we are ported somewhere (ebtables?) where ip_hdr(skb) ++ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ ++ int ip_hl = 4*ip_hdr(skb)->ihl; ++ ++ if( ip_hdr(skb)->protocol == IPPROTO_TCP ) { ++ /* 12 == offset into TCP header for the header length field. ++ Can't get this with skb->h.th->doff because the tcphdr ++ struct doesn't get set when routing (this is confirmed to be ++ true in Netfilter as well as QoS.) */ ++ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); ++ ++ return ip_hl + tcp_hl; ++ } else if( ip_hdr(skb)->protocol == IPPROTO_UDP ) { ++ return ip_hl + 8; /* UDP header is always 8 bytes */ ++ } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) { ++ return ip_hl + 8; /* ICMP header is 8 bytes */ ++ } else { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: tried to handle unknown " ++ "protocol!\n"); ++ return ip_hl + 8; /* something reasonable */ ++ } ++} ++ ++/* handles whether there's a match when we aren't appending data anymore */ ++static int match_no_append(struct nf_conn * conntrack, ++ struct nf_conn * master_conntrack, ++ enum ip_conntrack_info ctinfo, ++ enum ip_conntrack_info master_ctinfo, ++ const struct xt_layer7_info * info) ++{ ++ /* If we're in here, throw the app data away */ ++ if(master_conntrack->layer7.app_data != NULL) { ++ ++ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++ if(!master_conntrack->layer7.app_proto) { ++ char * f = ++ friendly_print(master_conntrack->layer7.app_data); ++ char * g = ++ hex_print(master_conntrack->layer7.app_data); ++ DPRINTK("\nl7-filter gave up after %d bytes " ++ "(%d packets):\n%s\n", ++ strlen(f), TOTAL_PACKETS, f); ++ kfree(f); ++ DPRINTK("In hex: %s\n", g); ++ kfree(g); ++ } ++ #endif ++ ++ kfree(master_conntrack->layer7.app_data); ++ master_conntrack->layer7.app_data = NULL; /* don't free again */ ++ } ++ ++ if(master_conntrack->layer7.app_proto){ ++ /* Here child connections set their .app_proto (for /proc) */ ++ if(!conntrack->layer7.app_proto) { ++ conntrack->layer7.app_proto = ++ kmalloc(strlen(master_conntrack->layer7.app_proto)+1, ++ GFP_ATOMIC); ++ if(!conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory " ++ "in match_no_append, " ++ "bailing.\n"); ++ return 1; ++ } ++ strcpy(conntrack->layer7.app_proto, ++ master_conntrack->layer7.app_proto); ++ } ++ ++ return (!strcmp(master_conntrack->layer7.app_proto, ++ info->protocol)); ++ } ++ else { ++ /* If not classified, set to "unknown" to distinguish from ++ connections that are still being tested. */ ++ master_conntrack->layer7.app_proto = ++ kmalloc(strlen("unknown")+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "match_no_append, bailing.\n"); ++ return 1; ++ } ++ strcpy(master_conntrack->layer7.app_proto, "unknown"); ++ return 0; ++ } ++} ++ ++/* add the new app data to the conntrack. Return number of bytes added. */ ++static int add_data(struct nf_conn * master_conntrack, ++ char * app_data, int appdatalen) ++{ ++ int length = 0, i; ++ int oldlength = master_conntrack->layer7.app_data_len; ++ ++ /* This is a fix for a race condition by Deti Fliegl. However, I'm not ++ clear on whether the race condition exists or whether this really ++ fixes it. I might just be being dense... Anyway, if it's not really ++ a fix, all it does is waste a very small amount of time. */ ++ if(!master_conntrack->layer7.app_data) return 0; ++ ++ /* Strip nulls. Make everything lower case (our regex lib doesn't ++ do case insensitivity). Add it to the end of the current data. */ ++ for(i = 0; i < maxdatalen-oldlength-1 && ++ i < appdatalen; i++) { ++ if(app_data[i] != '\0') { ++ /* the kernel version of tolower mungs 'upper ascii' */ ++ master_conntrack->layer7.app_data[length+oldlength] = ++ isascii(app_data[i])? ++ tolower(app_data[i]) : app_data[i]; ++ length++; ++ } ++ } ++ ++ master_conntrack->layer7.app_data[length+oldlength] = '\0'; ++ master_conntrack->layer7.app_data_len = length + oldlength; ++ ++ return length; ++} ++ ++/* taken from drivers/video/modedb.c */ ++static int my_atoi(const char *s) ++{ ++ int val = 0; ++ ++ for (;; s++) { ++ switch (*s) { ++ case '0'...'9': ++ val = 10*val+(*s-'0'); ++ break; ++ default: ++ return val; ++ } ++ } ++} ++ ++/* write out num_packets to userland. */ ++static int layer7_read_proc(char* page, char ** start, off_t off, int count, ++ int* eof, void * data) ++{ ++ if(num_packets > 99 && net_ratelimit()) ++ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n"); ++ ++ page[0] = num_packets/10 + '0'; ++ page[1] = num_packets%10 + '0'; ++ page[2] = '\n'; ++ page[3] = '\0'; ++ ++ *eof=1; ++ ++ return 3; ++} ++ ++/* Read in num_packets from userland */ ++static int layer7_write_proc(struct file* file, const char* buffer, ++ unsigned long count, void *data) ++{ ++ char * foo = kmalloc(count, GFP_ATOMIC); ++ ++ if(!foo){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory, bailing. " ++ "num_packets unchanged.\n"); ++ return count; ++ } ++ ++ if(copy_from_user(foo, buffer, count)) { ++ return -EFAULT; ++ } ++ ++ ++ num_packets = my_atoi(foo); ++ kfree (foo); ++ ++ /* This has an arbitrary limit to make the math easier. I'm lazy. ++ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ ++ if(num_packets > 99) { ++ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n"); ++ num_packets = 99; ++ } else if(num_packets < 1) { ++ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n"); ++ num_packets = 1; ++ } ++ ++ return count; ++} ++ ++static bool ++match(const struct sk_buff *skbin, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct xt_match *match, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ bool *hotdrop) ++{ ++ /* sidestep const without getting a compiler warning... */ ++ struct sk_buff * skb = (struct sk_buff *)skbin; ++ ++ const struct xt_layer7_info * info = matchinfo; ++ enum ip_conntrack_info master_ctinfo, ctinfo; ++ struct nf_conn *master_conntrack, *conntrack; ++ unsigned char * app_data; ++ unsigned int pattern_result, appdatalen; ++ regexp * comppattern; ++ ++ /* Be paranoid/incompetent - lock the entire match function. */ ++ spin_lock_bh(&l7_lock); ++ ++ if(!can_handle(skb)){ ++ DPRINTK("layer7: This is some protocol I can't handle.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ ++ /* Treat parent & all its children together as one connection, except ++ for the purpose of setting conntrack->layer7.app_proto in the actual ++ connection. This makes /proc/net/ip_conntrack more satisfying. */ ++ if(!(conntrack = nf_ct_get(skb, &ctinfo)) || ++ !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){ ++ DPRINTK("layer7: couldn't get conntrack.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ ++ /* Try to get a master conntrack (and its master etc) for FTP, etc. */ ++ while (master_ct(master_conntrack) != NULL) ++ master_conntrack = master_ct(master_conntrack); ++ ++ /* if we've classified it or seen too many packets */ ++ if(TOTAL_PACKETS > num_packets || ++ master_conntrack->layer7.app_proto) { ++ ++ pattern_result = match_no_append(conntrack, master_conntrack, ++ ctinfo, master_ctinfo, info); ++ ++ /* skb->cb[0] == seen. Don't do things twice if there are ++ multiple l7 rules. I'm not sure that using cb for this purpose ++ is correct, even though it says "put your private variables ++ there". But it doesn't look like it is being used for anything ++ else in the skbs that make it here. */ ++ skb->cb[0] = 1; /* marking it seen here's probably irrelevant */ ++ ++ spin_unlock_bh(&l7_lock); ++ return (pattern_result ^ info->invert); ++ } ++ ++ if(skb_is_nonlinear(skb)){ ++ if(skb_linearize(skb) != 0){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: failed to linearize " ++ "packet, bailing.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ } ++ ++ /* now that the skb is linearized, it's safe to set these. */ ++ app_data = skb->data + app_data_offset(skb); ++ appdatalen = skb_tail_pointer(skb) - app_data; ++ ++ /* the return value gets checked later, when we're ready to use it */ ++ comppattern = compile_and_cache(info->pattern, info->protocol); ++ ++ /* On the first packet of a connection, allocate space for app data */ ++ if(TOTAL_PACKETS == 1 && !skb->cb[0] && ++ !master_conntrack->layer7.app_data){ ++ master_conntrack->layer7.app_data = ++ kmalloc(maxdatalen, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "match, bailing.\n"); ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ ++ master_conntrack->layer7.app_data[0] = '\0'; ++ } ++ ++ /* Can be here, but unallocated, if numpackets is increased near ++ the beginning of a connection */ ++ if(master_conntrack->layer7.app_data == NULL){ ++ spin_unlock_bh(&l7_lock); ++ return (info->invert); /* unmatched */ ++ } ++ ++ if(!skb->cb[0]){ ++ int newbytes; ++ newbytes = add_data(master_conntrack, app_data, appdatalen); ++ ++ if(newbytes == 0) { /* didn't add any data */ ++ skb->cb[0] = 1; ++ /* Didn't match before, not going to match now */ ++ spin_unlock_bh(&l7_lock); ++ return info->invert; ++ } ++ } ++ ++ /* If looking for "unknown", then never match. "Unknown" means that ++ we've given up; we're still trying with these packets. */ ++ if(!strcmp(info->protocol, "unknown")) { ++ pattern_result = 0; ++ /* If looking for "unset", then always match. "Unset" means that we ++ haven't yet classified the connection. */ ++ } else if(!strcmp(info->protocol, "unset")) { ++ pattern_result = 2; ++ DPRINTK("layer7: matched unset: not yet classified " ++ "(%d/%d packets)\n", TOTAL_PACKETS, num_packets); ++ /* If the regexp failed to compile, don't bother running it */ ++ } else if(comppattern && ++ regexec(comppattern, master_conntrack->layer7.app_data)){ ++ DPRINTK("layer7: matched %s\n", info->protocol); ++ pattern_result = 1; ++ } else pattern_result = 0; ++ ++ if(pattern_result == 1) { ++ master_conntrack->layer7.app_proto = ++ kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in " ++ "match, bailing.\n"); ++ spin_unlock_bh(&l7_lock); ++ return (pattern_result ^ info->invert); ++ } ++ strcpy(master_conntrack->layer7.app_proto, info->protocol); ++ } else if(pattern_result > 1) { /* cleanup from "unset" */ ++ pattern_result = 1; ++ } ++ ++ /* mark the packet seen */ ++ skb->cb[0] = 1; ++ ++ spin_unlock_bh(&l7_lock); ++ return (pattern_result ^ info->invert); ++} ++ ++static bool check(const char *tablename, ++ const void *inf, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++ ++{ ++ // load nf_conntrack_ipv4 ++ if (nf_ct_l3proto_try_module_get(match->family) < 0) { ++ printk(KERN_WARNING "can't load conntrack support for " ++ "proto=%d\n", match->family); ++ return false; ++ } ++ return true; ++} ++ ++static void ++destroy(const struct xt_match *match, void *matchinfo) ++{ ++ nf_ct_l3proto_module_put(match->family); ++} ++ ++static struct xt_match xt_layer7_match[] = { ++{ ++ .name = "layer7", ++ .family = AF_INET, ++ .checkentry = check, ++ .match = match, ++ .destroy = destroy, ++ .matchsize = sizeof(struct xt_layer7_info), ++ .me = THIS_MODULE ++} ++}; ++ ++static void layer7_cleanup_proc(void) ++{ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) ++ remove_proc_entry("layer7_numpackets", proc_net); ++#else ++ remove_proc_entry("layer7_numpackets", init_net.proc_net); ++#endif ++} ++ ++/* register the proc file */ ++static void layer7_init_proc(void) ++{ ++ struct proc_dir_entry* entry; ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) ++ entry = create_proc_entry("layer7_numpackets", 0644, proc_net); ++#else ++ entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net); ++#endif ++ entry->read_proc = layer7_read_proc; ++ entry->write_proc = layer7_write_proc; ++} ++ ++static int __init xt_layer7_init(void) ++{ ++ need_conntrack(); ++ ++ layer7_init_proc(); ++ if(maxdatalen < 1) { ++ printk(KERN_WARNING "layer7: maxdatalen can't be < 1, " ++ "using 1\n"); ++ maxdatalen = 1; ++ } ++ /* This is not a hard limit. It's just here to prevent people from ++ bringing their slow machines to a grinding halt. */ ++ else if(maxdatalen > 65536) { ++ printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, " ++ "using 65536\n"); ++ maxdatalen = 65536; ++ } ++ return xt_register_matches(xt_layer7_match, ++ ARRAY_SIZE(xt_layer7_match)); ++} ++ ++static void __exit xt_layer7_fini(void) ++{ ++ layer7_cleanup_proc(); ++ xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match)); ++} ++ ++module_init(xt_layer7_init); ++module_exit(xt_layer7_fini); +--- /dev/null ++++ b/net/netfilter/regexp/regexp.c +@@ -0,0 +1,1197 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++struct match_globals { ++char *reginput; /* String-input pointer. */ ++char *regbol; /* Beginning of input, for ^ check. */ ++char **regstartp; /* Pointer to startp array. */ ++char **regendp; /* Ditto for endp. */ ++char *regparse; /* Input-scan pointer. */ ++int regnpar; /* () count. */ ++char regdummy; ++char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++long regsize; /* Code size. */ ++}; ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(struct match_globals *g, int paren,int *flagp); ++STATIC char *regbranch(struct match_globals *g, int *flagp); ++STATIC char *regpiece(struct match_globals *g, int *flagp); ++STATIC char *regatom(struct match_globals *g, int *flagp); ++STATIC char *regnode(struct match_globals *g, char op); ++STATIC char *regnext(struct match_globals *g, char *p); ++STATIC void regc(struct match_globals *g, char b); ++STATIC void reginsert(struct match_globals *g, char op, char *opnd); ++STATIC void regtail(struct match_globals *g, char *p, char *val); ++STATIC void regoptail(struct match_globals *g, char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ struct match_globals g; ++ ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regsize = 0L; ++ g.regcode = &g.regdummy; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (g.regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)g.regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ g.regparse = exp; ++ g.regnpar = 1; ++ g.regcode = r->program; ++ regc(&g, MAGIC); ++ if (reg(&g, 0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(&g, scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(&g, scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (g->regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = g->regnpar; ++ g->regnpar++; ++ ret = regnode(g, OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(g, ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*g->regparse == '|') { ++ g->regparse++; ++ br = regbranch(g, &flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(g, ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode(g, (paren) ? CLOSE+parno : END); ++ regtail(g, ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(g, br)) ++ regoptail(g, br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *g->regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *g->regparse != '\0') { ++ if (*g->regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(g, BRANCH); ++ chain = NULL; ++ while (*g->regparse != '\0' && *g->regparse != '|' && *g->regparse != ')') { ++ latest = regpiece(g, &flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(g, chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(g, NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(g, &flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *g->regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(g, STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regoptail(g, ret, regnode(g, BACK)); /* and loop */ ++ regoptail(g, ret, ret); /* back */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(g, PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(g, BRANCH); /* Either */ ++ regtail(g, ret, next); ++ regtail(g, regnode(g, BACK), ret); /* loop back */ ++ regtail(g, next, regnode(g, BRANCH)); /* or */ ++ regtail(g, ret, regnode(g, NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(g, BRANCH, ret); /* Either x */ ++ regtail(g, ret, regnode(g, BRANCH)); /* or */ ++ next = regnode(g, NOTHING); /* null. */ ++ regtail(g, ret, next); ++ regoptail(g, ret, next); ++ } ++ g->regparse++; ++ if (ISMULT(*g->regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(struct match_globals *g, int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*g->regparse++) { ++ case '^': ++ ret = regnode(g, BOL); ++ break; ++ case '$': ++ ret = regnode(g, EOL); ++ break; ++ case '.': ++ ret = regnode(g, ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*g->regparse == '^') { /* Complement of range. */ ++ ret = regnode(g, ANYBUT); ++ g->regparse++; ++ } else ++ ret = regnode(g, ANYOF); ++ if (*g->regparse == ']' || *g->regparse == '-') ++ regc(g, *g->regparse++); ++ while (*g->regparse != '\0' && *g->regparse != ']') { ++ if (*g->regparse == '-') { ++ g->regparse++; ++ if (*g->regparse == ']' || *g->regparse == '\0') ++ regc(g, '-'); ++ else { ++ class = UCHARAT(g->regparse-2)+1; ++ classend = UCHARAT(g->regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(g, class); ++ g->regparse++; ++ } ++ } else ++ regc(g, *g->regparse++); ++ } ++ regc(g, '\0'); ++ if (*g->regparse != ']') ++ FAIL("unmatched []"); ++ g->regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(g, 1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*g->regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(g, EXACTLY); ++ regc(g, *g->regparse++); ++ regc(g, '\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ g->regparse--; ++ len = my_strcspn((const char *)g->regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(g->regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(g, EXACTLY); ++ while (len > 0) { ++ regc(g, *g->regparse++); ++ len--; ++ } ++ regc(g, '\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(struct match_globals *g, char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = g->regcode; ++ if (ret == &g->regdummy) { ++ g->regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ g->regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(struct match_globals *g, char b) ++{ ++ if (g->regcode != &g->regdummy) ++ *g->regcode++ = b; ++ else ++ g->regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(struct match_globals *g, char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (g->regcode == &g->regdummy) { ++ g->regsize += 3; ++ return; ++ } ++ ++ src = g->regcode; ++ g->regcode += 3; ++ dst = g->regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(struct match_globals *g, char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(g, scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(struct match_globals *g, char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == &g->regdummy || OP(p) != BRANCH) ++ return; ++ regtail(g, OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(struct match_globals *g, regexp *prog, char *string); ++STATIC int regmatch(struct match_globals *g, char *prog); ++STATIC int regrepeat(struct match_globals *g, char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ struct match_globals g; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ g.regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(&g, prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(&g, prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(&g, prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(struct match_globals *g, regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ g->reginput = string; ++ g->regstartp = prog->startp; ++ g->regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(g, prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = g->reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(struct match_globals *g, char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(g, scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (g->reginput != g->regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*g->reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*g->reginput == '\0') ++ return(0); ++ g->reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *g->reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, g->reginput, len) != 0) ++ return(0); ++ g->reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) == NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case ANYBUT: ++ if (*g->reginput == '\0' || strchr(OPERAND(scan), *g->reginput) != NULL) ++ return(0); ++ g->reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regstartp[no] == NULL) ++ g->regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = g->reginput; ++ ++ if (regmatch(g, next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (g->regendp[no] == NULL) ++ g->regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = g->reginput; ++ if (regmatch(g, OPERAND(scan))) ++ return(1); ++ g->reginput = save; ++ scan = regnext(g, scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = g->reginput; ++ no = regrepeat(g, OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *g->reginput == nextch) ++ if (regmatch(g, next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ g->reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(struct match_globals *g, char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = g->reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ g->reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(struct match_globals *g, char *p) ++{ ++ register int offset; ++ ++ if (p == &g->regdummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +--- /dev/null ++++ b/net/netfilter/regexp/regexp.h +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +--- /dev/null ++++ b/net/netfilter/regexp/regmagic.h +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +--- /dev/null ++++ b/net/netfilter/regexp/regsub.c +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -207,6 +207,14 @@ + * too. */ + nf_ct_remove_expectations(ct); + ++ #if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ if(ct->layer7.app_proto) ++ kfree(ct->layer7.app_proto); ++ if(ct->layer7.app_data) ++ kfree(ct->layer7.app_data); ++ #endif ++ ++ + /* We overload first tuple to link into unconfirmed list. */ + if (!nf_ct_is_confirmed(ct)) { + BUG_ON(hlist_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnode)); +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -179,7 +179,12 @@ + return -ENOSPC; + #endif + +- if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ if(conntrack->layer7.app_proto) ++ if(seq_printf(s, "l7proto=%s ", conntrack->layer7.app_proto)) ++ return -ENOSPC; ++#endif ++ if (seq_printf(s, "asdfuse=%u\n", atomic_read(&conntrack->ct_general.use))) + return -ENOSPC; + + return 0; +--- a/include/net/netfilter/nf_conntrack.h ++++ b/include/net/netfilter/nf_conntrack.h +@@ -127,6 +127,22 @@ + u_int32_t secmark; + #endif + ++#if defined(CONFIG_NETFILTER_XT_MATCH_LAYER7) || \ ++ defined(CONFIG_NETFILTER_XT_MATCH_LAYER7_MODULE) ++ struct { ++ /* ++ * e.g. "http". NULL before decision. "unknown" after decision ++ * if no match. ++ */ ++ char *app_proto; ++ /* ++ * application layer data so far. NULL after match decision. ++ */ ++ char *app_data; ++ unsigned int app_data_len; ++ } layer7; ++#endif ++ + /* Storage reserved for other modules: */ + union nf_conntrack_proto proto; + +--- /dev/null ++++ b/include/linux/netfilter/xt_layer7.h +@@ -0,0 +1,13 @@ ++#ifndef _XT_LAYER7_H ++#define _XT_LAYER7_H ++ ++#define MAX_PATTERN_LEN 8192 ++#define MAX_PROTOCOL_LEN 256 ++ ++struct xt_layer7_info { ++ char protocol[MAX_PROTOCOL_LEN]; ++ char pattern[MAX_PATTERN_LEN]; ++ u_int8_t invert; ++}; ++ ++#endif /* _XT_LAYER7_H */ diff --git a/target/linux/generic-2.6/patches-2.6.23/101-netfilter_layer7_pktmatch.patch b/target/linux/generic-2.6/patches-2.6.23/101-netfilter_layer7_pktmatch.patch new file mode 100644 index 0000000000..167d578cf1 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/101-netfilter_layer7_pktmatch.patch @@ -0,0 +1,109 @@ +--- a/include/linux/netfilter/xt_layer7.h ++++ b/include/linux/netfilter/xt_layer7.h +@@ -8,6 +8,7 @@ + char protocol[MAX_PROTOCOL_LEN]; + char pattern[MAX_PATTERN_LEN]; + u_int8_t invert; ++ u_int8_t pkt; + }; + + #endif /* _XT_LAYER7_H */ +--- a/net/netfilter/xt_layer7.c ++++ b/net/netfilter/xt_layer7.c +@@ -297,34 +297,36 @@ + } + + /* add the new app data to the conntrack. Return number of bytes added. */ +-static int add_data(struct nf_conn * master_conntrack, +- char * app_data, int appdatalen) ++static int add_datastr(char *target, int offset, char *app_data, int len) + { + int length = 0, i; +- int oldlength = master_conntrack->layer7.app_data_len; +- +- /* This is a fix for a race condition by Deti Fliegl. However, I'm not +- clear on whether the race condition exists or whether this really +- fixes it. I might just be being dense... Anyway, if it's not really +- a fix, all it does is waste a very small amount of time. */ +- if(!master_conntrack->layer7.app_data) return 0; ++ ++ if (!target) return 0; + + /* Strip nulls. Make everything lower case (our regex lib doesn't + do case insensitivity). Add it to the end of the current data. */ +- for(i = 0; i < maxdatalen-oldlength-1 && +- i < appdatalen; i++) { ++ for(i = 0; i < maxdatalen-offset-1 && i < len; i++) { + if(app_data[i] != '\0') { + /* the kernel version of tolower mungs 'upper ascii' */ +- master_conntrack->layer7.app_data[length+oldlength] = ++ target[length+offset] = + isascii(app_data[i])? + tolower(app_data[i]) : app_data[i]; + length++; + } + } ++ target[length+offset] = '\0'; ++ ++ return length; ++} + +- master_conntrack->layer7.app_data[length+oldlength] = '\0'; +- master_conntrack->layer7.app_data_len = length + oldlength; ++/* add the new app data to the conntrack. Return number of bytes added. */ ++static int add_data(struct nf_conn * master_conntrack, ++ char * app_data, int appdatalen) ++{ ++ int length; + ++ length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen); ++ master_conntrack->layer7.app_data_len += length; + return length; + } + +@@ -411,7 +413,7 @@ + const struct xt_layer7_info * info = matchinfo; + enum ip_conntrack_info master_ctinfo, ctinfo; + struct nf_conn *master_conntrack, *conntrack; +- unsigned char * app_data; ++ unsigned char *app_data, *tmp_data; + unsigned int pattern_result, appdatalen; + regexp * comppattern; + +@@ -439,8 +441,8 @@ + master_conntrack = master_ct(master_conntrack); + + /* if we've classified it or seen too many packets */ +- if(TOTAL_PACKETS > num_packets || +- master_conntrack->layer7.app_proto) { ++ if(!info->pkt && (TOTAL_PACKETS > num_packets || ++ master_conntrack->layer7.app_proto)) { + + pattern_result = match_no_append(conntrack, master_conntrack, + ctinfo, master_ctinfo, info); +@@ -473,6 +475,25 @@ + /* the return value gets checked later, when we're ready to use it */ + comppattern = compile_and_cache(info->pattern, info->protocol); + ++ if (info->pkt) { ++ tmp_data = kmalloc(maxdatalen, GFP_ATOMIC); ++ if(!tmp_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ return info->invert; ++ } ++ ++ tmp_data[0] = '\0'; ++ add_datastr(tmp_data, 0, app_data, appdatalen); ++ pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0); ++ ++ kfree(tmp_data); ++ tmp_data = NULL; ++ spin_unlock_bh(&l7_lock); ++ ++ return (pattern_result ^ info->invert); ++ } ++ + /* On the first packet of a connection, allocate space for app data */ + if(TOTAL_PACKETS == 1 && !skb->cb[0] && + !master_conntrack->layer7.app_data){ diff --git a/target/linux/generic-2.6/patches-2.6.23/110-ipp2p_0.8.1rc1.patch b/target/linux/generic-2.6/patches-2.6.23/110-ipp2p_0.8.1rc1.patch new file mode 100644 index 0000000000..88e27a3d04 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/110-ipp2p_0.8.1rc1.patch @@ -0,0 +1,919 @@ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_ipp2p.h +@@ -0,0 +1,31 @@ ++#ifndef __IPT_IPP2P_H ++#define __IPT_IPP2P_H ++#define IPP2P_VERSION "0.8.1_rc1" ++ ++struct ipt_p2p_info { ++ int cmd; ++ int debug; ++}; ++ ++#endif //__IPT_IPP2P_H ++ ++#define SHORT_HAND_IPP2P 1 /* --ipp2p switch*/ ++//#define SHORT_HAND_DATA 4 /* --ipp2p-data switch*/ ++#define SHORT_HAND_NONE 5 /* no short hand*/ ++ ++#define IPP2P_EDK (1 << 1) ++#define IPP2P_DATA_KAZAA (1 << 2) ++#define IPP2P_DATA_EDK (1 << 3) ++#define IPP2P_DATA_DC (1 << 4) ++#define IPP2P_DC (1 << 5) ++#define IPP2P_DATA_GNU (1 << 6) ++#define IPP2P_GNU (1 << 7) ++#define IPP2P_KAZAA (1 << 8) ++#define IPP2P_BIT (1 << 9) ++#define IPP2P_APPLE (1 << 10) ++#define IPP2P_SOUL (1 << 11) ++#define IPP2P_WINMX (1 << 12) ++#define IPP2P_ARES (1 << 13) ++#define IPP2P_MUTE (1 << 14) ++#define IPP2P_WASTE (1 << 15) ++#define IPP2P_XDCC (1 << 16) +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_ipp2p.c +@@ -0,0 +1,856 @@ ++#if defined(MODVERSIONS) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define get_u8(X,O) (*(__u8 *)(X + O)) ++#define get_u16(X,O) (*(__u16 *)(X + O)) ++#define get_u32(X,O) (*(__u32 *)(X + O)) ++ ++MODULE_AUTHOR("Eicke Friedrich/Klaus Degner "); ++MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); ++MODULE_LICENSE("GPL"); ++ ++ ++/*Search for UDP eDonkey/eMule/Kad commands*/ ++int ++udp_search_edk (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ switch (t[0]) { ++ case 0xe3: ++ { /*edonkey*/ ++ switch (t[1]) ++ { ++ /* client -> server status request */ ++ case 0x96: ++ if (packet_len == 14) return ((IPP2P_EDK * 100) + 50); ++ break; ++ /* server -> client status request */ ++ case 0x97: if (packet_len == 42) return ((IPP2P_EDK * 100) + 51); ++ break; ++ /* server description request */ ++ /* e3 2a ff f0 .. | size == 6 */ ++ case 0xa2: if ( (packet_len == 14) && ( get_u16(t,2) == __constant_htons(0xfff0) ) ) return ((IPP2P_EDK * 100) + 52); ++ break; ++ /* server description response */ ++ /* e3 a3 ff f0 .. | size > 40 && size < 200 */ ++ //case 0xa3: return ((IPP2P_EDK * 100) + 53); ++ // break; ++ case 0x9a: if (packet_len==26) return ((IPP2P_EDK * 100) + 54); ++ break; ++ ++ case 0x92: if (packet_len==18) return ((IPP2P_EDK * 100) + 55); ++ break; ++ } ++ break; ++ } ++ case 0xe4: ++ { ++ switch (t[1]) ++ { ++ /* e4 20 .. | size == 43 */ ++ case 0x20: if ((packet_len == 43) && (t[2] != 0x00) && (t[34] != 0x00)) return ((IPP2P_EDK * 100) + 60); ++ break; ++ /* e4 00 .. 00 | size == 35 ? */ ++ case 0x00: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 61); ++ break; ++ /* e4 10 .. 00 | size == 35 ? */ ++ case 0x10: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 62); ++ break; ++ /* e4 18 .. 00 | size == 35 ? */ ++ case 0x18: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 63); ++ break; ++ /* e4 52 .. | size = 44 */ ++ case 0x52: if (packet_len == 44 ) return ((IPP2P_EDK * 100) + 64); ++ break; ++ /* e4 58 .. | size == 6 */ ++ case 0x58: if (packet_len == 14 ) return ((IPP2P_EDK * 100) + 65); ++ break; ++ /* e4 59 .. | size == 2 */ ++ case 0x59: if (packet_len == 10 )return ((IPP2P_EDK * 100) + 66); ++ break; ++ /* e4 28 .. | packet_len == 52,77,102,127... */ ++ case 0x28: if (((packet_len-52) % 25) == 0) return ((IPP2P_EDK * 100) + 67); ++ break; ++ /* e4 50 xx xx | size == 4 */ ++ case 0x50: if (packet_len == 12) return ((IPP2P_EDK * 100) + 68); ++ break; ++ /* e4 40 xx xx | size == 48 */ ++ case 0x40: if (packet_len == 56) return ((IPP2P_EDK * 100) + 69); ++ break; ++ } ++ break; ++ } ++ } /* end of switch (t[0]) */ ++ return 0; ++}/*udp_search_edk*/ ++ ++ ++/*Search for UDP Gnutella commands*/ ++int ++udp_search_gnu (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ if (memcmp(t, "GND", 3) == 0) return ((IPP2P_GNU * 100) + 51); ++ if (memcmp(t, "GNUTELLA ", 9) == 0) return ((IPP2P_GNU * 100) + 52); ++ return 0; ++}/*udp_search_gnu*/ ++ ++ ++/*Search for UDP KaZaA commands*/ ++int ++udp_search_kazaa (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (t[packet_len-1] == 0x00){ ++ t += (packet_len - 6); ++ if (memcmp(t, "KaZaA", 5) == 0) return (IPP2P_KAZAA * 100 +50); ++ } ++ ++ return 0; ++}/*udp_search_kazaa*/ ++ ++/*Search for UDP DirectConnect commands*/ ++int ++udp_search_directconnect (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ if ((*(t + 8) == 0x24) && (*(t + packet_len - 1) == 0x7c)) { ++ t+=8; ++ if (memcmp(t, "SR ", 3) == 0) return ((IPP2P_DC * 100) + 60); ++ if (memcmp(t, "Ping ", 5) == 0) return ((IPP2P_DC * 100) + 61); ++ } ++ return 0; ++}/*udp_search_directconnect*/ ++ ++ ++ ++/*Search for UDP BitTorrent commands*/ ++int ++udp_search_bit (unsigned char *haystack, int packet_len) ++{ ++ switch(packet_len) ++ { ++ case 24: ++ /* ^ 00 00 04 17 27 10 19 80 */ ++ if ((ntohl(get_u32(haystack, 8)) == 0x00000417) && (ntohl(get_u32(haystack, 12)) == 0x27101980)) ++ return (IPP2P_BIT * 100 + 50); ++ break; ++ case 44: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000400) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 51); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000400)) ++ return (IPP2P_BIT * 100 + 61); ++ break; ++ case 65: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000404) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 52); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000404)) ++ return (IPP2P_BIT * 100 + 62); ++ break; ++ case 67: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000406) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 53); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000406)) ++ return (IPP2P_BIT * 100 + 63); ++ break; ++ case 211: ++ if (get_u32(haystack, 8) == __constant_htonl(0x00000405)) ++ return (IPP2P_BIT * 100 + 54); ++ break; ++ case 29: ++ if ((get_u32(haystack, 8) == __constant_htonl(0x00000401))) ++ return (IPP2P_BIT * 100 + 55); ++ break; ++ case 52: ++ if (get_u32(haystack,8) == __constant_htonl(0x00000827) && ++ get_u32(haystack,12) == __constant_htonl(0x37502950)) ++ return (IPP2P_BIT * 100 + 80); ++ break; ++ default: ++ /* this packet does not have a constant size */ ++ if (packet_len >= 40 && get_u32(haystack, 16) == __constant_htonl(0x00000402) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 56); ++ break; ++ } ++ ++ /* some extra-bitcomet rules: ++ * "d1:" [a|r] "d2:id20:" ++ */ ++ if (packet_len > 30 && get_u8(haystack, 8) == 'd' && get_u8(haystack, 9) == '1' && get_u8(haystack, 10) == ':' ) ++ { ++ if (get_u8(haystack, 11) == 'a' || get_u8(haystack, 11) == 'r') ++ { ++ if (memcmp(haystack+12,"d2:id20:",8)==0) ++ return (IPP2P_BIT * 100 + 57); ++ } ++ } ++ ++#if 0 ++ /* bitlord rules */ ++ /* packetlen must be bigger than 40 */ ++ /* first 4 bytes are zero */ ++ if (packet_len > 40 && get_u32(haystack, 8) == 0x00000000) ++ { ++ /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00*/ ++ if (get_u32(haystack, 12) == 0x00000000 && ++ get_u32(haystack, 16) == 0x00010000 && ++ get_u32(haystack, 24) == 0x00000000 ) ++ return (IPP2P_BIT * 100 + 71); ++ ++ /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00*/ ++ if (get_u32(haystack, 12) == 0x00000001 && ++ get_u32(haystack, 16) == 0x000d0000 && ++ get_u32(haystack, 24) == 0x00000000 ) ++ return (IPP2P_BIT * 100 + 71); ++ ++ ++ } ++#endif ++ ++ return 0; ++}/*udp_search_bit*/ ++ ++ ++ ++/*Search for Ares commands*/ ++//#define IPP2P_DEBUG_ARES ++int ++search_ares (const unsigned char *payload, const u16 plen) ++//int search_ares (unsigned char *haystack, int packet_len, int head_len) ++{ ++// const unsigned char *t = haystack + head_len; ++ ++ /* all ares packets start with */ ++ if (payload[1] == 0 && (plen - payload[0]) == 3) ++ { ++ switch (payload[2]) ++ { ++ case 0x5a: ++ /* ares connect */ ++ if ( plen == 6 && payload[5] == 0x05 ) return ((IPP2P_ARES * 100) + 1); ++ break; ++ case 0x09: ++ /* ares search, min 3 chars --> 14 bytes ++ * lets define a search can be up to 30 chars --> max 34 bytes ++ */ ++ if ( plen >= 14 && plen <= 34 ) return ((IPP2P_ARES * 100) + 1); ++ break; ++#ifdef IPP2P_DEBUG_ARES ++ default: ++ printk(KERN_DEBUG "Unknown Ares command %x recognized, len: %u \n", (unsigned int) payload[2],plen); ++#endif /* IPP2P_DEBUG_ARES */ ++ } ++ } ++ ++#if 0 ++ /* found connect packet: 03 00 5a 04 03 05 */ ++ /* new version ares 1.8: 03 00 5a xx xx 05 */ ++ if ((plen) == 6){ /* possible connect command*/ ++ if ((payload[0] == 0x03) && (payload[1] == 0x00) && (payload[2] == 0x5a) && (payload[5] == 0x05)) ++ return ((IPP2P_ARES * 100) + 1); ++ } ++ if ((plen) == 60){ /* possible download command*/ ++ if ((payload[59] == 0x0a) && (payload[58] == 0x0a)){ ++ if (memcmp(t, "PUSH SHA1:", 10) == 0) /* found download command */ ++ return ((IPP2P_ARES * 100) + 2); ++ } ++ } ++#endif ++ ++ return 0; ++} /*search_ares*/ ++ ++/*Search for SoulSeek commands*/ ++int ++search_soul (const unsigned char *payload, const u16 plen) ++{ ++//#define IPP2P_DEBUG_SOUL ++ /* match: xx xx xx xx | xx = sizeof(payload) - 4 */ ++ if (get_u32(payload, 0) == (plen - 4)){ ++ const __u32 m=get_u32(payload, 4); ++ /* match 00 yy yy 00, yy can be everything */ ++ if ( get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "0: Soulseek command 0x%x recognized\n",get_u32(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 1); ++ } ++ ++ /* next match: 01 yy 00 00 | yy can be everything */ ++ if ( get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "1: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 2); ++ } ++ ++ /* other soulseek commandos are: 1-5,7,9,13-18,22,23,26,28,35-37,40-46,50,51,60,62-69,91,92,1001 */ ++ /* try to do this in an intelligent way */ ++ /* get all small commandos */ ++ switch(m) ++ { ++ case 7: ++ case 9: ++ case 22: ++ case 23: ++ case 26: ++ case 28: ++ case 50: ++ case 51: ++ case 60: ++ case 91: ++ case 92: ++ case 1001: ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "2: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 3); ++ } ++ ++ if (m > 0 && m < 6 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "3: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 4); ++ } ++ if (m > 12 && m < 19 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "4: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 5); ++ } ++ ++ if (m > 34 && m < 38 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "5: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 6); ++ } ++ ++ if (m > 39 && m < 47 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "6: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 7); ++ } ++ ++ if (m > 61 && m < 70 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "7: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 8); ++ } ++ ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first 16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n",get_u32(payload, 4),get_u16(payload, 4) >> 16,get_u8(payload, 4) >> 24); ++#endif /* IPP2P_DEBUG_SOUL */ ++ } ++ ++ /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */ ++ /* without size at the beginning !!! */ ++ if ( get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01 ) ++ { ++ __u32 y=get_u32(payload, 5); ++ /* we need 19 chars + string */ ++ if ( (y + 19) <= (plen) ) ++ { ++ const unsigned char *w=payload+9+y; ++ if (get_u32(w, 0) == 0x01 && ( get_u16(w, 4) == 0x4600 || get_u16(w, 4) == 0x5000) && get_u32(w, 6) == 0x00); ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "Soulssek special client command recognized\n"); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 9); ++ } ++ } ++ return 0; ++} ++ ++ ++/*Search for WinMX commands*/ ++int ++search_winmx (const unsigned char *payload, const u16 plen) ++{ ++//#define IPP2P_DEBUG_WINMX ++ if (((plen) == 4) && (memcmp(payload, "SEND", 4) == 0)) return ((IPP2P_WINMX * 100) + 1); ++ if (((plen) == 3) && (memcmp(payload, "GET", 3) == 0)) return ((IPP2P_WINMX * 100) + 2); ++ //if (packet_len < (head_len + 10)) return 0; ++ if (plen < 10) return 0; ++ ++ if ((memcmp(payload, "SEND", 4) == 0) || (memcmp(payload, "GET", 3) == 0)){ ++ u16 c=4; ++ const u16 end=plen-2; ++ u8 count=0; ++ while (c < end) ++ { ++ if (payload[c]== 0x20 && payload[c+1] == 0x22) ++ { ++ c++; ++ count++; ++ if (count>=2) return ((IPP2P_WINMX * 100) + 3); ++ } ++ c++; ++ } ++ } ++ ++ if ( plen == 149 && payload[0] == '8' ) ++ { ++#ifdef IPP2P_DEBUG_WINMX ++ printk(KERN_INFO "maybe WinMX\n"); ++#endif ++ if (get_u32(payload,17) == 0 && get_u32(payload,21) == 0 && get_u32(payload,25) == 0 && ++// get_u32(payload,33) == __constant_htonl(0x71182b1a) && get_u32(payload,37) == __constant_htonl(0x05050000) && ++// get_u32(payload,133) == __constant_htonl(0x31097edf) && get_u32(payload,145) == __constant_htonl(0xdcb8f792)) ++ get_u16(payload,39) == 0 && get_u16(payload,135) == __constant_htons(0x7edf) && get_u16(payload,147) == __constant_htons(0xf792)) ++ ++ { ++#ifdef IPP2P_DEBUG_WINMX ++ printk(KERN_INFO "got WinMX\n"); ++#endif ++ return ((IPP2P_WINMX * 100) + 4); ++ } ++ } ++ return 0; ++} /*search_winmx*/ ++ ++ ++/*Search for appleJuice commands*/ ++int ++search_apple (const unsigned char *payload, const u16 plen) ++{ ++ if ( (plen > 7) && (payload[6] == 0x0d) && (payload[7] == 0x0a) && (memcmp(payload, "ajprot", 6) == 0)) return (IPP2P_APPLE * 100); ++ ++ return 0; ++} ++ ++ ++/*Search for BitTorrent commands*/ ++int ++search_bittorrent (const unsigned char *payload, const u16 plen) ++{ ++ if (plen > 20) ++ { ++ /* test for match 0x13+"BitTorrent protocol" */ ++ if (payload[0] == 0x13) ++ { ++ if (memcmp(payload+1, "BitTorrent protocol", 19) == 0) return (IPP2P_BIT * 100); ++ } ++ ++ /* get tracker commandos, all starts with GET / ++ * then it can follow: scrape| announce ++ * and then ?hash_info= ++ */ ++ if (memcmp(payload,"GET /",5) == 0) ++ { ++ /* message scrape */ ++ if ( memcmp(payload+5,"scrape?info_hash=",17)==0 ) return (IPP2P_BIT * 100 + 1); ++ /* message announce */ ++ if ( memcmp(payload+5,"announce?info_hash=",19)==0 ) return (IPP2P_BIT * 100 + 2); ++ } ++ } ++ else ++ { ++ /* bitcomet encryptes the first packet, so we have to detect another ++ * one later in the flow */ ++ /* first try failed, too many missdetections */ ++ //if ( size == 5 && get_u32(t,0) == __constant_htonl(1) && t[4] < 3) return (IPP2P_BIT * 100 + 3); ++ ++ /* second try: block request packets */ ++ if ( plen == 17 && get_u32(payload,0) == __constant_htonl(0x0d) && payload[4] == 0x06 && get_u32(payload,13) == __constant_htonl(0x4000) ) return (IPP2P_BIT * 100 + 3); ++ } ++ ++ return 0; ++} ++ ++ ++ ++/*check for Kazaa get command*/ ++int ++search_kazaa (const unsigned char *payload, const u16 plen) ++ ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a) && memcmp(payload, "GET /.hash=", 11) == 0) ++ return (IPP2P_DATA_KAZAA * 100); ++ ++ return 0; ++} ++ ++ ++/*check for gnutella get command*/ ++int ++search_gnu (const unsigned char *payload, const u16 plen) ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ if (memcmp(payload, "GET /get/", 9) == 0) return ((IPP2P_DATA_GNU * 100) + 1); ++ if (memcmp(payload, "GET /uri-res/", 13) == 0) return ((IPP2P_DATA_GNU * 100) + 2); ++ } ++ return 0; ++} ++ ++ ++/*check for gnutella get commands and other typical data*/ ++int ++search_all_gnu (const unsigned char *payload, const u16 plen) ++{ ++ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ ++ if (memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) return ((IPP2P_GNU * 100) + 1); ++ if (memcmp(payload, "GNUTELLA/", 9) == 0) return ((IPP2P_GNU * 100) + 2); ++ ++ ++ if ((memcmp(payload, "GET /get/", 9) == 0) || (memcmp(payload, "GET /uri-res/", 13) == 0)) ++ { ++ u16 c=8; ++ const u16 end=plen-22; ++ while (c < end) { ++ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Gnutella-", 11) == 0) || (memcmp(&payload[c+2], "X-Queue:", 8) == 0))) ++ return ((IPP2P_GNU * 100) + 3); ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++/*check for KaZaA download commands and other typical data*/ ++int ++search_all_kazaa (const unsigned char *payload, const u16 plen) ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ ++ if (memcmp(payload, "GIVE ", 5) == 0) return ((IPP2P_KAZAA * 100) + 1); ++ ++ if (memcmp(payload, "GET /", 5) == 0) { ++ u16 c = 8; ++ const u16 end=plen-22; ++ while (c < end) { ++ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Kazaa-Username: ", 18) == 0) || (memcmp(&payload[c+2], "User-Agent: PeerEnabler/", 24) == 0))) ++ return ((IPP2P_KAZAA * 100) + 2); ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++/*fast check for edonkey file segment transfer command*/ ++int ++search_edk (const unsigned char *payload, const u16 plen) ++{ ++ if (payload[0] != 0xe3) ++ return 0; ++ else { ++ if (payload[5] == 0x47) ++ return (IPP2P_DATA_EDK * 100); ++ else ++ return 0; ++ } ++} ++ ++ ++ ++/*intensive but slower search for some edonkey packets including size-check*/ ++int ++search_all_edk (const unsigned char *payload, const u16 plen) ++{ ++ if (payload[0] != 0xe3) ++ return 0; ++ else { ++ //t += head_len; ++ const u16 cmd = get_u16(payload, 1); ++ if (cmd == (plen - 5)) { ++ switch (payload[5]) { ++ case 0x01: return ((IPP2P_EDK * 100) + 1); /*Client: hello or Server:hello*/ ++ case 0x4c: return ((IPP2P_EDK * 100) + 9); /*Client: Hello-Answer*/ ++ } ++ } ++ return 0; ++ } ++} ++ ++ ++/*fast check for Direct Connect send command*/ ++int ++search_dc (const unsigned char *payload, const u16 plen) ++{ ++ ++ if (payload[0] != 0x24 ) ++ return 0; ++ else { ++ if (memcmp(&payload[1], "Send|", 5) == 0) ++ return (IPP2P_DATA_DC * 100); ++ else ++ return 0; ++ } ++ ++} ++ ++ ++/*intensive but slower check for all direct connect packets*/ ++int ++search_all_dc (const unsigned char *payload, const u16 plen) ++{ ++// unsigned char *t = haystack; ++ ++ if (payload[0] == 0x24 && payload[plen-1] == 0x7c) ++ { ++ const unsigned char *t=&payload[1]; ++ /* Client-Hub-Protocol */ ++ if (memcmp(t, "Lock ", 5) == 0) return ((IPP2P_DC * 100) + 1); ++ /* Client-Client-Protocol, some are already recognized by client-hub (like lock) */ ++ if (memcmp(t, "MyNick ", 7) == 0) return ((IPP2P_DC * 100) + 38); ++ } ++ return 0; ++} ++ ++/*check for mute*/ ++int ++search_mute (const unsigned char *payload, const u16 plen) ++{ ++ if ( plen == 209 || plen == 345 || plen == 473 || plen == 609 || plen == 1121 ) ++ { ++ //printk(KERN_DEBUG "size hit: %u",size); ++ if (memcmp(payload,"PublicKey: ",11) == 0 ) ++ { ++ return ((IPP2P_MUTE * 100) + 0); ++ ++/* if (memcmp(t+size-14,"\x0aEndPublicKey\x0a",14) == 0) ++ { ++ printk(KERN_DEBUG "end pubic key hit: %u",size); ++ ++ }*/ ++ } ++ } ++ return 0; ++} ++ ++ ++/* check for xdcc */ ++int ++search_xdcc (const unsigned char *payload, const u16 plen) ++{ ++ /* search in small packets only */ ++ if (plen > 20 && plen < 200 && payload[plen-1] == 0x0a && payload[plen-2] == 0x0d && memcmp(payload,"PRIVMSG ",8) == 0) ++ { ++ ++ u16 x=10; ++ const u16 end=plen - 13; ++ ++ /* is seems to be a irc private massage, chedck for xdcc command */ ++ while (x < end) ++ { ++ if (payload[x] == ':') ++ { ++ if ( memcmp(&payload[x+1],"xdcc send #",11) == 0 ) ++ return ((IPP2P_XDCC * 100) + 0); ++ } ++ x++; ++ } ++ } ++ return 0; ++} ++ ++/* search for waste */ ++int search_waste(const unsigned char *payload, const u16 plen) ++{ ++ if ( plen >= 8 && memcmp(payload,"GET.sha1:",9) == 0) ++ return ((IPP2P_WASTE * 100) + 0); ++ ++ return 0; ++} ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (const unsigned char *, const u16); ++} matchlist[] = { ++ {IPP2P_EDK,SHORT_HAND_IPP2P,20, &search_all_edk}, ++// {IPP2P_DATA_KAZAA,SHORT_HAND_DATA,200, &search_kazaa}, ++// {IPP2P_DATA_EDK,SHORT_HAND_DATA,60, &search_edk}, ++// {IPP2P_DATA_DC,SHORT_HAND_DATA,26, &search_dc}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,5, search_all_dc}, ++// {IPP2P_DATA_GNU,SHORT_HAND_DATA,40, &search_gnu}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,5, &search_all_gnu}, ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,5, &search_all_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_IPP2P,20, &search_bittorrent}, ++ {IPP2P_APPLE,SHORT_HAND_IPP2P,5, &search_apple}, ++ {IPP2P_SOUL,SHORT_HAND_IPP2P,5, &search_soul}, ++ {IPP2P_WINMX,SHORT_HAND_IPP2P,2, &search_winmx}, ++ {IPP2P_ARES,SHORT_HAND_IPP2P,5, &search_ares}, ++ {IPP2P_MUTE,SHORT_HAND_NONE,200, &search_mute}, ++ {IPP2P_WASTE,SHORT_HAND_NONE,5, &search_waste}, ++ {IPP2P_XDCC,SHORT_HAND_NONE,5, &search_xdcc}, ++ {0,0,0,NULL} ++}; ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (unsigned char *, int); ++} udp_list[] = { ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,14, &udp_search_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_IPP2P,23, &udp_search_bit}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,11, &udp_search_gnu}, ++ {IPP2P_EDK,SHORT_HAND_IPP2P,9, &udp_search_edk}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,12, &udp_search_directconnect}, ++ {0,0,0,NULL} ++}; ++ ++ ++static bool ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct xt_match *match, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ bool *hotdrop) ++{ ++ const struct ipt_p2p_info *info = matchinfo; ++ unsigned char *haystack; ++ struct iphdr *ip = ip_hdr(skb); ++ int p2p_result = 0, i = 0; ++// int head_len; ++ int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/ ++ ++ /*must not be a fragment*/ ++ if (offset) { ++ if (info->debug) printk("IPP2P.match: offset found %i \n",offset); ++ return 0; ++ } ++ ++ /*make sure that skb is linear*/ ++ if(skb_is_nonlinear(skb)){ ++ if (info->debug) printk("IPP2P.match: nonlinear skb found\n"); ++ return 0; ++ } ++ ++ ++ haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/ ++ ++ switch (ip->protocol){ ++ case IPPROTO_TCP: /*what to do with a TCP packet*/ ++ { ++ struct tcphdr *tcph = (void *) ip + ip->ihl * 4; ++ ++ if (tcph->fin) return 0; /*if FIN bit is set bail out*/ ++ if (tcph->syn) return 0; /*if SYN bit is set bail out*/ ++ if (tcph->rst) return 0; /*if RST bit is set bail out*/ ++ ++ haystack += tcph->doff * 4; /*get TCP-Header-Size*/ ++ hlen -= tcph->doff * 4; ++ while (matchlist[i].command) { ++ if ((((info->cmd & matchlist[i].command) == matchlist[i].command) || ++ ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) && ++ (hlen > matchlist[i].packet_len)) { ++ p2p_result = matchlist[i].function_name(haystack, hlen); ++ if (p2p_result) ++ { ++ if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ case IPPROTO_UDP: /*what to do with an UDP packet*/ ++ { ++ struct udphdr *udph = (void *) ip + ip->ihl * 4; ++ ++ while (udp_list[i].command){ ++ if ((((info->cmd & udp_list[i].command) == udp_list[i].command) || ++ ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) && ++ (hlen > udp_list[i].packet_len)) { ++ p2p_result = udp_list[i].function_name(haystack, hlen); ++ if (p2p_result){ ++ if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ default: return 0; ++ } ++} ++ ++ ++ ++static bool ++checkentry(const char *tablename, ++ const void *ip, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++{ ++ /* Must specify -p tcp */ ++/* if (ip->proto != IPPROTO_TCP || (ip->invflags & IPT_INV_PROTO)) { ++ * printk("ipp2p: Only works on TCP packets, use -p tcp\n"); ++ * return 0; ++ * }*/ ++ return 1; ++} ++ ++ ++ ++ ++static struct ipt_match ipp2p_match = { ++ .name = "ipp2p", ++ .match = &match, ++ .family = AF_INET, ++ .matchsize = sizeof(struct ipt_p2p_info), ++ .checkentry = &checkentry, ++ .me = THIS_MODULE, ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_INFO "IPP2P v%s loading\n", IPP2P_VERSION); ++ return xt_register_match(&ipp2p_match); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&ipp2p_match); ++ printk(KERN_INFO "IPP2P v%s unloaded\n", IPP2P_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -63,6 +63,12 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_MATCH_IPP2P ++ tristate "IPP2P" ++ depends on IP_NF_IPTABLES ++ help ++ Module for matching traffic of various Peer-to-Peer applications ++ + config IP_NF_MATCH_TOS + tristate "TOS match support" + depends on IP_NF_IPTABLES +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -50,6 +50,8 @@ + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o + obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o + ++obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o ++ + # targets + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o + obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o diff --git a/target/linux/generic-2.6/patches-2.6.23/120-openswan-2.4.0.kernel-2.6-natt.patch b/target/linux/generic-2.6/patches-2.6.23/120-openswan-2.4.0.kernel-2.6-natt.patch new file mode 100644 index 0000000000..71b7377027 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/120-openswan-2.4.0.kernel-2.6-natt.patch @@ -0,0 +1,91 @@ +--- /dev/null ++++ b/include/net/xfrmudp.h +@@ -0,0 +1,10 @@ ++/* ++ * pointer to function for type that xfrm4_input wants, to permit ++ * decoupling of XFRM from udp.c ++ */ ++#define HAVE_XFRM4_UDP_REGISTER ++ ++typedef int (*xfrm4_rcv_encap_t)(struct sk_buff *skb, __u16 encap_type); ++extern int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func ++ , xfrm4_rcv_encap_t *oldfunc); ++extern int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func); +--- a/net/ipv4/Kconfig ++++ b/net/ipv4/Kconfig +@@ -224,6 +224,12 @@ + Network), but can be distributed all over the Internet. If you want + to do that, say Y here and to "IP multicast routing" below. + ++config IPSEC_NAT_TRAVERSAL ++ bool "IPSEC NAT-Traversal (KLIPS compatible)" ++ depends on INET ++ ---help--- ++ Includes support for RFC3947/RFC3948 NAT-Traversal of ESP over UDP. ++ + config IP_MROUTE + bool "IP: multicast routing" + depends on IP_MULTICAST +--- a/net/ipv4/xfrm4_input.c ++++ b/net/ipv4/xfrm4_input.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq) + { +@@ -161,6 +162,29 @@ + return 0; + } + ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = NULL; ++ ++int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func, ++ xfrm4_rcv_encap_t *oldfunc) ++{ ++ if(oldfunc != NULL) ++ *oldfunc = xfrm4_rcv_encap_func; ++ ++ xfrm4_rcv_encap_func = func; ++ return 0; ++} ++ ++int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func) ++{ ++ if(xfrm4_rcv_encap_func != func) ++ return -1; ++ ++ xfrm4_rcv_encap_func = NULL; ++ return 0; ++} ++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */ ++ + /* If it's a keepalive packet, then just eat it. + * If it's an encapsulated packet, then pass it to the + * IPsec xfrm input. +@@ -251,7 +275,13 @@ + iph->protocol = IPPROTO_ESP; + + /* process ESP */ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++ if(xfrm4_rcv_encap_func == NULL) ++ goto drop; ++ ret = (*xfrm4_rcv_encap_func)(skb, up->encap_type); ++#else + ret = xfrm4_rcv_encap(skb, encap_type); ++#endif + return ret; + + drop: +@@ -265,3 +295,8 @@ + } + + EXPORT_SYMBOL(xfrm4_rcv); ++ ++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL ++EXPORT_SYMBOL(udp4_register_esp_rcvencap); ++EXPORT_SYMBOL(udp4_unregister_esp_rcvencap); ++#endif diff --git a/target/linux/generic-2.6/patches-2.6.23/130-netfilter_ipset.patch b/target/linux/generic-2.6/patches-2.6.23/130-netfilter_ipset.patch new file mode 100644 index 0000000000..25b2172e5e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/130-netfilter_ipset.patch @@ -0,0 +1,7664 @@ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set.h +@@ -0,0 +1,498 @@ ++#ifndef _IP_SET_H ++#define _IP_SET_H ++ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#if 0 ++#define IP_SET_DEBUG ++#endif ++ ++/* ++ * A sockopt of such quality has hardly ever been seen before on the open ++ * market! This little beauty, hardly ever used: above 64, so it's ++ * traditionally used for firewalling, not touched (even once!) by the ++ * 2.0, 2.2 and 2.4 kernels! ++ * ++ * Comes with its own certificate of authenticity, valid anywhere in the ++ * Free world! ++ * ++ * Rusty, 19.4.2000 ++ */ ++#define SO_IP_SET 83 ++ ++/* ++ * Heavily modify by Joakim Axelsson 08.03.2002 ++ * - Made it more modulebased ++ * ++ * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 ++ * - bindings added ++ * - in order to "deal with" backward compatibility, renamed to ipset ++ */ ++ ++/* ++ * Used so that the kernel module and ipset-binary can match their versions ++ */ ++#define IP_SET_PROTOCOL_VERSION 2 ++ ++#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ ++ ++/* Lets work with our own typedef for representing an IP address. ++ * We hope to make the code more portable, possibly to IPv6... ++ * ++ * The representation works in HOST byte order, because most set types ++ * will perform arithmetic operations and compare operations. ++ * ++ * For now the type is an uint32_t. ++ * ++ * Make sure to ONLY use the functions when translating and parsing ++ * in order to keep the host byte order and make it more portable: ++ * parse_ip() ++ * parse_mask() ++ * parse_ipandmask() ++ * ip_tostring() ++ * (Joakim: where are they???) ++ */ ++ ++typedef uint32_t ip_set_ip_t; ++ ++/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t ++ * and IP_SET_INVALID_ID if you want to increase the max number of sets. ++ */ ++typedef uint16_t ip_set_id_t; ++ ++#define IP_SET_INVALID_ID 65535 ++ ++/* How deep we follow bindings */ ++#define IP_SET_MAX_BINDINGS 6 ++ ++/* ++ * Option flags for kernel operations (ipt_set_info) ++ */ ++#define IPSET_SRC 0x01 /* Source match/add */ ++#define IPSET_DST 0x02 /* Destination match/add */ ++#define IPSET_MATCH_INV 0x04 /* Inverse matching */ ++ ++/* ++ * Set features ++ */ ++#define IPSET_TYPE_IP 0x01 /* IP address type of set */ ++#define IPSET_TYPE_PORT 0x02 /* Port type of set */ ++#define IPSET_DATA_SINGLE 0x04 /* Single data storage */ ++#define IPSET_DATA_DOUBLE 0x08 /* Double data storage */ ++ ++/* Reserved keywords */ ++#define IPSET_TOKEN_DEFAULT ":default:" ++#define IPSET_TOKEN_ALL ":all:" ++ ++/* SO_IP_SET operation constants, and their request struct types. ++ * ++ * Operation ids: ++ * 0-99: commands with version checking ++ * 100-199: add/del/test/bind/unbind ++ * 200-299: list, save, restore ++ */ ++ ++/* Single shot operations: ++ * version, create, destroy, flush, rename and swap ++ * ++ * Sets are identified by name. ++ */ ++ ++#define IP_SET_REQ_STD \ ++ unsigned op; \ ++ unsigned version; \ ++ char name[IP_SET_MAXNAMELEN] ++ ++#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ ++struct ip_set_req_create { ++ IP_SET_REQ_STD; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ ++struct ip_set_req_std { ++ IP_SET_REQ_STD; ++}; ++ ++#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */ ++/* Uses ip_set_req_std */ ++ ++#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */ ++/* Uses ip_set_req_create */ ++ ++#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */ ++/* Uses ip_set_req_create */ ++ ++union ip_set_name_index { ++ char name[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++}; ++ ++#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ ++struct ip_set_req_get_set { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++}; ++ ++#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ ++/* Uses ip_set_req_get_set */ ++ ++#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ ++struct ip_set_req_version { ++ unsigned op; ++ unsigned version; ++}; ++ ++/* Double shots operations: ++ * add, del, test, bind and unbind. ++ * ++ * First we query the kernel to get the index and type of the target set, ++ * then issue the command. Validity of IP is checked in kernel in order ++ * to minimalize sockopt operations. ++ */ ++ ++/* Get minimal set data for add/del/test/bind/unbind IP */ ++#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */ ++struct ip_set_req_adt_get { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_REQ_BYINDEX \ ++ unsigned op; \ ++ ip_set_id_t index; ++ ++struct ip_set_req_adt { ++ IP_SET_REQ_BYINDEX; ++}; ++ ++#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++struct ip_set_req_bind { ++ IP_SET_REQ_BYINDEX; ++ char binding[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */ ++/* Uses ip_set_req_bind, with type speficic addage ++ * index = 0 means unbinding for all sets */ ++ ++#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++ ++/* Multiple shots operations: list, save, restore. ++ * ++ * - check kernel version and query the max number of sets ++ * - get the basic information on all sets ++ * and size required for the next step ++ * - get actual set data: header, data, bindings ++ */ ++ ++/* Get max_sets and the index of a queried set ++ */ ++#define IP_SET_OP_MAX_SETS 0x00000020 ++struct ip_set_req_max_sets { ++ unsigned op; ++ unsigned version; ++ ip_set_id_t max_sets; /* max_sets */ ++ ip_set_id_t sets; /* real number of sets */ ++ union ip_set_name_index set; /* index of set if name used */ ++}; ++ ++/* Get the id and name of the sets plus size for next step */ ++#define IP_SET_OP_LIST_SIZE 0x00000201 ++#define IP_SET_OP_SAVE_SIZE 0x00000202 ++struct ip_set_req_setnames { ++ unsigned op; ++ ip_set_id_t index; /* set to list/save */ ++ size_t size; /* size to get setdata/bindings */ ++ /* followed by sets number of struct ip_set_name_list */ ++}; ++ ++struct ip_set_name_list { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ ip_set_id_t id; ++}; ++ ++/* The actual list operation */ ++#define IP_SET_OP_LIST 0x00000203 ++struct ip_set_req_list { ++ IP_SET_REQ_BYINDEX; ++ /* sets number of struct ip_set_list in reply */ ++}; ++ ++struct ip_set_list { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ u_int32_t ref; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++ size_t bindings_size; /* Set bindings data of bindings_size */ ++}; ++ ++struct ip_set_hash_list { ++ ip_set_ip_t ip; ++ ip_set_id_t binding; ++}; ++ ++/* The save operation */ ++#define IP_SET_OP_SAVE 0x00000204 ++/* Uses ip_set_req_list, in the reply replaced by ++ * sets number of struct ip_set_save plus a marker ++ * ip_set_save followed by ip_set_hash_save structures. ++ */ ++struct ip_set_save { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++/* At restoring, ip == 0 means default binding for the given set: */ ++struct ip_set_hash_save { ++ ip_set_ip_t ip; ++ ip_set_id_t id; ++ ip_set_id_t binding; ++}; ++ ++/* The restore operation */ ++#define IP_SET_OP_RESTORE 0x00000205 ++/* Uses ip_set_req_setnames followed by ip_set_restore structures ++ * plus a marker ip_set_restore, followed by ip_set_hash_save ++ * structures. ++ */ ++struct ip_set_restore { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ size_t header_size; /* Create data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) ++{ ++ return 4 * ((((b - a + 8) / 8) + 3) / 4); ++} ++ ++#ifdef __KERNEL__ ++ ++#define ip_set_printk(format, args...) \ ++ do { \ ++ printk("%s: %s: ", __FILE__, __FUNCTION__); \ ++ printk(format "\n" , ## args); \ ++ } while (0) ++ ++#if defined(IP_SET_DEBUG) ++#define DP(format, args...) \ ++ do { \ ++ printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ ++ printk(format "\n" , ## args); \ ++ } while (0) ++#define IP_SET_ASSERT(x) \ ++ do { \ ++ if (!(x)) \ ++ printk("IP_SET_ASSERT: %s:%i(%s)\n", \ ++ __FILE__, __LINE__, __FUNCTION__); \ ++ } while (0) ++#else ++#define DP(format, args...) ++#define IP_SET_ASSERT(x) ++#endif ++ ++struct ip_set; ++ ++/* ++ * The ip_set_type definition - one per set type, e.g. "ipmap". ++ * ++ * Each individual set has a pointer, set->type, going to one ++ * of these structures. Function pointers inside the structure implement ++ * the real behaviour of the sets. ++ * ++ * If not mentioned differently, the implementation behind the function ++ * pointers of a set_type, is expected to return 0 if ok, and a negative ++ * errno (e.g. -EINVAL) on error. ++ */ ++struct ip_set_type { ++ struct list_head list; /* next in list of set types */ ++ ++ /* test for IP in set (kernel: iptables -m set src|dst) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ ip_set_ip_t *ip, ++ const u_int32_t *flags, ++ unsigned char index); ++ ++ /* test for IP in set (userspace: ipset -T set IP) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* ++ * Size of the data structure passed by when ++ * adding/deletin/testing an entry. ++ */ ++ size_t reqsize; ++ ++ /* Add IP into set (userspace: ipset -A set IP) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* Add IP into set (kernel: iptables ... -j SET set src|dst) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ ip_set_ip_t *ip, ++ const u_int32_t *flags, ++ unsigned char index); ++ ++ /* remove IP from set (userspace: ipset -D set --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* remove IP from set (kernel: iptables ... -j SET --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ ip_set_ip_t *ip, ++ const u_int32_t *flags, ++ unsigned char index); ++ ++ /* new set creation - allocated type specific items ++ */ ++ int (*create) (struct ip_set *set, ++ const void *data, size_t size); ++ ++ /* retry the operation after successfully tweaking the set ++ */ ++ int (*retry) (struct ip_set *set); ++ ++ /* set destruction - free type specific items ++ * There is no return value. ++ * Can be called only when child sets are destroyed. ++ */ ++ void (*destroy) (struct ip_set *set); ++ ++ /* set flushing - reset all bits in the set, or something similar. ++ * There is no return value. ++ */ ++ void (*flush) (struct ip_set *set); ++ ++ /* Listing: size needed for header ++ */ ++ size_t header_size; ++ ++ /* Listing: Get the header ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_header_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_header) (const struct ip_set *set, ++ void *data); ++ ++ /* Listing: Get the size for the set members ++ */ ++ int (*list_members_size) (const struct ip_set *set); ++ ++ /* Listing: Get the set members ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_member_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_members) (const struct ip_set *set, ++ void *data); ++ ++ char typename[IP_SET_MAXNAMELEN]; ++ unsigned char features; ++ int protocol_version; ++ ++ /* Set this to THIS_MODULE if you are a module, otherwise NULL */ ++ struct module *me; ++}; ++ ++extern int ip_set_register_set_type(struct ip_set_type *set_type); ++extern void ip_set_unregister_set_type(struct ip_set_type *set_type); ++ ++/* A generic ipset */ ++struct ip_set { ++ char name[IP_SET_MAXNAMELEN]; /* the name of the set */ ++ rwlock_t lock; /* lock for concurrency control */ ++ ip_set_id_t id; /* set id for swapping */ ++ ip_set_id_t binding; /* default binding for the set */ ++ atomic_t ref; /* in kernel and in hash references */ ++ struct ip_set_type *type; /* the set types */ ++ void *data; /* pooltype specific data */ ++}; ++ ++/* Structure to bind set elements to sets */ ++struct ip_set_hash { ++ struct list_head list; /* list of clashing entries in hash */ ++ ip_set_ip_t ip; /* ip from set */ ++ ip_set_id_t id; /* set id */ ++ ip_set_id_t binding; /* set we bind the element to */ ++}; ++ ++/* register and unregister set references */ ++extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); ++extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); ++extern void ip_set_put(ip_set_id_t id); ++ ++/* API for iptables set match, and SET target */ ++extern void ip_set_addip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern void ip_set_delip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern int ip_set_testip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_iphash.h +@@ -0,0 +1,30 @@ ++#ifndef __IP_SET_IPHASH_H ++#define __IP_SET_IPHASH_H ++ ++#include ++ ++#define SETTYPE_NAME "iphash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iphash { ++ ip_set_ip_t *members; /* the iphash proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t netmask; /* netmask */ ++ void *initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_iphash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_iphash { ++ ip_set_ip_t ip; ++}; ++ ++#endif /* __IP_SET_IPHASH_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_ipmap.h +@@ -0,0 +1,56 @@ ++#ifndef __IP_SET_IPMAP_H ++#define __IP_SET_IPMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "ipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_ipmap { ++ void *members; /* the ipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ ip_set_ip_t netmask; /* subnet netmask */ ++ ip_set_ip_t sizeid; /* size of set in IPs */ ++ ip_set_ip_t hosts; /* number of hosts in a subnet */ ++}; ++ ++struct ip_set_req_ipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_ipmap { ++ ip_set_ip_t ip; ++}; ++ ++unsigned int ++mask_to_bits(ip_set_ip_t mask) ++{ ++ unsigned int bits = 32; ++ ip_set_ip_t maskaddr; ++ ++ if (mask == 0xFFFFFFFF) ++ return bits; ++ ++ maskaddr = 0xFFFFFFFE; ++ while (--bits >= 0 && maskaddr != mask) ++ maskaddr <<= 1; ++ ++ return bits; ++} ++ ++ip_set_ip_t ++range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) ++{ ++ ip_set_ip_t mask = 0xFFFFFFFE; ++ ++ *bits = 32; ++ while (--(*bits) >= 0 && mask && (to & mask) != from) ++ mask <<= 1; ++ ++ return mask; ++} ++ ++#endif /* __IP_SET_IPMAP_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_ipporthash.h +@@ -0,0 +1,34 @@ ++#ifndef __IP_SET_IPPORTHASH_H ++#define __IP_SET_IPPORTHASH_H ++ ++#include ++ ++#define SETTYPE_NAME "ipporthash" ++#define MAX_RANGE 0x0000FFFF ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++struct ip_set_ipporthash { ++ ip_set_ip_t *members; /* the ipporthash proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ void *initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_ipporthash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_ipporthash { ++ ip_set_ip_t ip; ++ ip_set_ip_t port; ++}; ++ ++#endif /* __IP_SET_IPPORTHASH_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_iptree.h +@@ -0,0 +1,40 @@ ++#ifndef __IP_SET_IPTREE_H ++#define __IP_SET_IPTREE_H ++ ++#include ++ ++#define SETTYPE_NAME "iptree" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iptreed { ++ unsigned long expires[256]; /* x.x.x.ADDR */ ++}; ++ ++struct ip_set_iptreec { ++ struct ip_set_iptreed *tree[256]; /* x.x.ADDR.* */ ++}; ++ ++struct ip_set_iptreeb { ++ struct ip_set_iptreec *tree[256]; /* x.ADDR.*.* */ ++}; ++ ++struct ip_set_iptree { ++ unsigned int timeout; ++ unsigned int gc_interval; ++#ifdef __KERNEL__ ++ uint32_t elements; /* number of elements */ ++ struct timer_list gc; ++ struct ip_set_iptreeb *tree[256]; /* ADDR.*.*.* */ ++#endif ++}; ++ ++struct ip_set_req_iptree_create { ++ unsigned int timeout; ++}; ++ ++struct ip_set_req_iptree { ++ ip_set_ip_t ip; ++ unsigned int timeout; ++}; ++ ++#endif /* __IP_SET_IPTREE_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_iptreemap.h +@@ -0,0 +1,40 @@ ++#ifndef __IP_SET_IPTREEMAP_H ++#define __IP_SET_IPTREEMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "iptreemap" ++ ++#ifdef __KERNEL__ ++struct ip_set_iptreemap_d { ++ unsigned char bitmap[32]; /* x.x.x.y */ ++}; ++ ++struct ip_set_iptreemap_c { ++ struct ip_set_iptreemap_d *tree[256]; /* x.x.y.x */ ++}; ++ ++struct ip_set_iptreemap_b { ++ struct ip_set_iptreemap_c *tree[256]; /* x.y.x.x */ ++ unsigned char dirty[32]; ++}; ++#endif ++ ++struct ip_set_iptreemap { ++ unsigned int gc_interval; ++#ifdef __KERNEL__ ++ struct timer_list gc; ++ struct ip_set_iptreemap_b *tree[256]; /* y.x.x.x */ ++#endif ++}; ++ ++struct ip_set_req_iptreemap_create { ++ unsigned int gc_interval; ++}; ++ ++struct ip_set_req_iptreemap { ++ ip_set_ip_t start; ++ ip_set_ip_t end; ++}; ++ ++#endif /* __IP_SET_IPTREEMAP_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_jhash.h +@@ -0,0 +1,148 @@ ++#ifndef _LINUX_IPSET_JHASH_H ++#define _LINUX_IPSET_JHASH_H ++ ++/* This is a copy of linux/jhash.h but the types u32/u8 are changed ++ * to __u32/__u8 so that the header file can be included into ++ * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) ++ */ ++ ++/* jhash.h: Jenkins hash support. ++ * ++ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) ++ * ++ * http://burtleburtle.net/bob/hash/ ++ * ++ * These are the credits from Bob's sources: ++ * ++ * lookup2.c, by Bob Jenkins, December 1996, Public Domain. ++ * hash(), hash2(), hash3, and mix() are externally useful functions. ++ * Routines to test the hash are included if SELF_TEST is defined. ++ * You can use this free for any purpose. It has no warranty. ++ * ++ * Copyright (C) 2003 David S. Miller (davem@redhat.com) ++ * ++ * I've modified Bob's hash to be useful in the Linux kernel, and ++ * any bugs present are surely my fault. -DaveM ++ */ ++ ++/* NOTE: Arguments are modified. */ ++#define __jhash_mix(a, b, c) \ ++{ \ ++ a -= b; a -= c; a ^= (c>>13); \ ++ b -= c; b -= a; b ^= (a<<8); \ ++ c -= a; c -= b; c ^= (b>>13); \ ++ a -= b; a -= c; a ^= (c>>12); \ ++ b -= c; b -= a; b ^= (a<<16); \ ++ c -= a; c -= b; c ^= (b>>5); \ ++ a -= b; a -= c; a ^= (c>>3); \ ++ b -= c; b -= a; b ^= (a<<10); \ ++ c -= a; c -= b; c ^= (b>>15); \ ++} ++ ++/* The golden ration: an arbitrary value */ ++#define JHASH_GOLDEN_RATIO 0x9e3779b9 ++ ++/* The most generic version, hashes an arbitrary sequence ++ * of bytes. No alignment or length assumptions are made about ++ * the input key. ++ */ ++static inline __u32 jhash(void *key, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ __u8 *k = key; ++ ++ len = length; ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ ++ while (len >= 12) { ++ a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); ++ b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); ++ c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); ++ ++ __jhash_mix(a,b,c); ++ ++ k += 12; ++ len -= 12; ++ } ++ ++ c += length; ++ switch (len) { ++ case 11: c += ((__u32)k[10]<<24); ++ case 10: c += ((__u32)k[9]<<16); ++ case 9 : c += ((__u32)k[8]<<8); ++ case 8 : b += ((__u32)k[7]<<24); ++ case 7 : b += ((__u32)k[6]<<16); ++ case 6 : b += ((__u32)k[5]<<8); ++ case 5 : b += k[4]; ++ case 4 : a += ((__u32)k[3]<<24); ++ case 3 : a += ((__u32)k[2]<<16); ++ case 2 : a += ((__u32)k[1]<<8); ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++/* A special optimized version that handles 1 or more of __u32s. ++ * The length parameter here is the number of __u32s in the key. ++ */ ++static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ len = length; ++ ++ while (len >= 3) { ++ a += k[0]; ++ b += k[1]; ++ c += k[2]; ++ __jhash_mix(a, b, c); ++ k += 3; len -= 3; ++ } ++ ++ c += length * 4; ++ ++ switch (len) { ++ case 2 : b += k[1]; ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++ ++/* A special ultra-optimized versions that knows they are hashing exactly ++ * 3, 2 or 1 word(s). ++ * ++ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally ++ * done at the end is not done here. ++ */ ++static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) ++{ ++ a += JHASH_GOLDEN_RATIO; ++ b += JHASH_GOLDEN_RATIO; ++ c += initval; ++ ++ __jhash_mix(a, b, c); ++ ++ return c; ++} ++ ++static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) ++{ ++ return jhash_3words(a, b, 0, initval); ++} ++ ++static inline __u32 jhash_1word(__u32 a, __u32 initval) ++{ ++ return jhash_3words(a, 0, 0, initval); ++} ++ ++#endif /* _LINUX_IPSET_JHASH_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_macipmap.h +@@ -0,0 +1,38 @@ ++#ifndef __IP_SET_MACIPMAP_H ++#define __IP_SET_MACIPMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "macipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++/* general flags */ ++#define IPSET_MACIP_MATCHUNSET 1 ++ ++/* per ip flags */ ++#define IPSET_MACIP_ISSET 1 ++ ++struct ip_set_macipmap { ++ void *members; /* the macipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap { ++ ip_set_ip_t ip; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++struct ip_set_macip { ++ unsigned short flags; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++#endif /* __IP_SET_MACIPMAP_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_malloc.h +@@ -0,0 +1,116 @@ ++#ifndef _IP_SET_MALLOC_H ++#define _IP_SET_MALLOC_H ++ ++#ifdef __KERNEL__ ++ ++/* Memory allocation and deallocation */ ++static size_t max_malloc_size = 0; ++ ++static inline void init_max_malloc_size(void) ++{ ++#define CACHE(x) max_malloc_size = x; ++#include ++#undef CACHE ++} ++ ++static inline void * ip_set_malloc(size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ return vmalloc(bytes); ++ else ++ return kmalloc(bytes, GFP_KERNEL); ++} ++ ++static inline void ip_set_free(void * data, size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ vfree(data); ++ else ++ kfree(data); ++} ++ ++struct harray { ++ size_t max_elements; ++ void *arrays[0]; ++}; ++ ++static inline void * ++harray_malloc(size_t hashsize, size_t typesize, int flags) ++{ ++ struct harray *harray; ++ size_t max_elements, size, i, j; ++ ++ if (!max_malloc_size) ++ init_max_malloc_size(); ++ ++ if (typesize > max_malloc_size) ++ return NULL; ++ ++ max_elements = max_malloc_size/typesize; ++ size = hashsize/max_elements; ++ if (hashsize % max_elements) ++ size++; ++ ++ /* Last pointer signals end of arrays */ ++ harray = kmalloc(sizeof(struct harray) + (size + 1) * sizeof(void *), ++ flags); ++ ++ if (!harray) ++ return NULL; ++ ++ for (i = 0; i < size - 1; i++) { ++ harray->arrays[i] = kmalloc(max_elements * typesize, flags); ++ if (!harray->arrays[i]) ++ goto undo; ++ memset(harray->arrays[i], 0, max_elements * typesize); ++ } ++ harray->arrays[i] = kmalloc((hashsize - i * max_elements) * typesize, ++ flags); ++ if (!harray->arrays[i]) ++ goto undo; ++ memset(harray->arrays[i], 0, (hashsize - i * max_elements) * typesize); ++ ++ harray->max_elements = max_elements; ++ harray->arrays[size] = NULL; ++ ++ return (void *)harray; ++ ++ undo: ++ for (j = 0; j < i; j++) { ++ kfree(harray->arrays[j]); ++ } ++ kfree(harray); ++ return NULL; ++} ++ ++static inline void harray_free(void *h) ++{ ++ struct harray *harray = (struct harray *) h; ++ size_t i; ++ ++ for (i = 0; harray->arrays[i] != NULL; i++) ++ kfree(harray->arrays[i]); ++ kfree(harray); ++} ++ ++static inline void harray_flush(void *h, size_t hashsize, size_t typesize) ++{ ++ struct harray *harray = (struct harray *) h; ++ size_t i; ++ ++ for (i = 0; harray->arrays[i+1] != NULL; i++) ++ memset(harray->arrays[i], 0, harray->max_elements * typesize); ++ memset(harray->arrays[i], 0, ++ (hashsize - i * harray->max_elements) * typesize); ++} ++ ++#define HARRAY_ELEM(h, type, which) \ ++({ \ ++ struct harray *__h = (struct harray *)(h); \ ++ ((type)((__h)->arrays[(which)/(__h)->max_elements]) \ ++ + (which)%(__h)->max_elements); \ ++}) ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_MALLOC_H*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_nethash.h +@@ -0,0 +1,55 @@ ++#ifndef __IP_SET_NETHASH_H ++#define __IP_SET_NETHASH_H ++ ++#include ++ ++#define SETTYPE_NAME "nethash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_nethash { ++ ip_set_ip_t *members; /* the nethash proper */ ++ uint32_t elements; /* number of elements */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ unsigned char cidr[30]; /* CIDR sizes */ ++ void *initval[0]; /* initvals for jhash_1word */ ++}; ++ ++struct ip_set_req_nethash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++}; ++ ++struct ip_set_req_nethash { ++ ip_set_ip_t ip; ++ unsigned char cidr; ++}; ++ ++static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; ++ ++static inline ip_set_ip_t ++pack(ip_set_ip_t ip, unsigned char cidr) ++{ ++ ip_set_ip_t addr, *paddr = &addr; ++ unsigned char n, t, *a; ++ ++ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); ++#ifdef __KERNEL__ ++ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); ++#endif ++ n = cidr / 8; ++ t = cidr % 8; ++ a = &((unsigned char *)paddr)[n]; ++ *a = *a /(1 << (8 - t)) + shifts[t]; ++#ifdef __KERNEL__ ++ DP("n: %u, t: %u, a: %u", n, t, *a); ++ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", ++ HIPQUAD(ip), cidr, NIPQUAD(addr)); ++#endif ++ ++ return ntohl(addr); ++} ++ ++#endif /* __IP_SET_NETHASH_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ip_set_portmap.h +@@ -0,0 +1,25 @@ ++#ifndef __IP_SET_PORTMAP_H ++#define __IP_SET_PORTMAP_H ++ ++#include ++ ++#define SETTYPE_NAME "portmap" ++#define MAX_RANGE 0x0000FFFF ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++struct ip_set_portmap { ++ void *members; /* the portmap proper */ ++ ip_set_ip_t first_port; /* host byte order, included in range */ ++ ip_set_ip_t last_port; /* host byte order, included in range */ ++}; ++ ++struct ip_set_req_portmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_portmap { ++ ip_set_ip_t port; ++}; ++ ++#endif /* __IP_SET_PORTMAP_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_set.h +@@ -0,0 +1,21 @@ ++#ifndef _IPT_SET_H ++#define _IPT_SET_H ++ ++#include ++ ++struct ipt_set_info { ++ ip_set_id_t index; ++ u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; ++}; ++ ++/* match info */ ++struct ipt_set_info_match { ++ struct ipt_set_info match_set; ++}; ++ ++struct ipt_set_info_target { ++ struct ipt_set_info add_set; ++ struct ipt_set_info del_set; ++}; ++ ++#endif /*_IPT_SET_H*/ +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set.c +@@ -0,0 +1,2003 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module for IP set management */ ++ ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ASSERT_READ_LOCK(x) ++#define ASSERT_WRITE_LOCK(x) ++#include ++ ++static struct list_head set_type_list; /* all registered sets */ ++static struct ip_set **ip_set_list; /* all individual sets */ ++static DEFINE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ ++static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ ++static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; ++static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; ++static struct list_head *ip_set_hash; /* hash of bindings */ ++static unsigned int ip_set_hash_random; /* random seed */ ++ ++/* ++ * Sets are identified either by the index in ip_set_list or by id. ++ * The id never changes and is used to find a key in the hash. ++ * The index may change by swapping and used at all other places ++ * (set/SET netfilter modules, binding value, etc.) ++ * ++ * Userspace requests are serialized by ip_set_mutex and sets can ++ * be deleted only from userspace. Therefore ip_set_list locking ++ * must obey the following rules: ++ * ++ * - kernel requests: read and write locking mandatory ++ * - user requests: read locking optional, write locking mandatory ++ */ ++ ++static inline void ++__ip_set_get(ip_set_id_t index) ++{ ++ atomic_inc(&ip_set_list[index]->ref); ++} ++ ++static inline void ++__ip_set_put(ip_set_id_t index) ++{ ++ atomic_dec(&ip_set_list[index]->ref); ++} ++ ++/* ++ * Binding routines ++ */ ++ ++static inline struct ip_set_hash * ++__ip_set_find(u_int32_t key, ip_set_id_t id, ip_set_ip_t ip) ++{ ++ struct ip_set_hash *set_hash; ++ ++ list_for_each_entry(set_hash, &ip_set_hash[key], list) ++ if (set_hash->id == id && set_hash->ip == ip) ++ return set_hash; ++ ++ return NULL; ++} ++ ++static ip_set_id_t ++ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ ASSERT_READ_LOCK(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ ++ set_hash = __ip_set_find(key, id, ip); ++ ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); ++} ++ ++static inline void ++__set_hash_del(struct ip_set_hash *set_hash) ++{ ++ ASSERT_WRITE_LOCK(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ ++ __ip_set_put(set_hash->binding); ++ list_del(&set_hash->list); ++ kfree(set_hash); ++} ++ ++static int ++ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ write_lock_bh(&ip_set_lock); ++ set_hash = __ip_set_find(key, id, ip); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ if (set_hash != NULL) ++ __set_hash_del(set_hash); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++} ++ ++static int ++ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ int ret = 0; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ IP_SET_ASSERT(ip_set_list[binding]); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ip_set_list[binding]->name); ++ write_lock_bh(&ip_set_lock); ++ set_hash = __ip_set_find(key, id, ip); ++ if (!set_hash) { ++ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_ATOMIC); ++ if (!set_hash) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ INIT_LIST_HEAD(&set_hash->list); ++ set_hash->id = id; ++ set_hash->ip = ip; ++ list_add(&set_hash->list, &ip_set_hash[key]); ++ } else { ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ DP("overwrite binding: %s", ++ ip_set_list[set_hash->binding]->name); ++ __ip_set_put(set_hash->binding); ++ } ++ set_hash->binding = binding; ++ __ip_set_get(set_hash->binding); ++ DP("stored: key %u, id %u (%s), ip %u.%u.%u.%u, binding %u (%s)", ++ key, id, ip_set_list[id]->name, ++ HIPQUAD(ip), binding, ip_set_list[binding]->name); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return ret; ++} ++ ++#define FOREACH_HASH_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash; \ ++ \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++#define FOREACH_HASH_RW_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash, *__n; \ ++ \ ++ ASSERT_WRITE_LOCK(&ip_set_lock); \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++/* Add, del and test set entries from kernel */ ++ ++#define follow_bindings(index, set, ip) \ ++((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ ++ || (index = (set)->binding) != IP_SET_INVALID_ID) ++ ++int ++ip_set_testip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res; ++ unsigned char i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ read_lock_bh(&set->lock); ++ res = set->type->testip_kernel(set, skb, &ip, flags, i++); ++ read_unlock_bh(&set->lock); ++ i += !!(set->type->features & IPSET_DATA_DOUBLE); ++ } while (res > 0 ++ && flags[i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++ ++ return res; ++} ++ ++void ++ip_set_addip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res; ++ unsigned char i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ retry: ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->addip_kernel(set, skb, &ip, flags, i++); ++ write_unlock_bh(&set->lock); ++ i += !!(set->type->features & IPSET_DATA_DOUBLE); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++ ++ if (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0) ++ goto retry; ++} ++ ++void ++ip_set_delip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res; ++ unsigned char i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->delip_kernel(set, skb, &ip, flags, i++); ++ write_unlock_bh(&set->lock); ++ i += !!(set->type->features & IPSET_DATA_DOUBLE); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++} ++ ++/* Register and deregister settype */ ++ ++static inline struct ip_set_type * ++find_set_type(const char *name) ++{ ++ struct ip_set_type *set_type; ++ ++ list_for_each_entry(set_type, &set_type_list, list) ++ if (!strncmp(set_type->typename, name, IP_SET_MAXNAMELEN - 1)) ++ return set_type; ++ return NULL; ++} ++ ++int ++ip_set_register_set_type(struct ip_set_type *set_type) ++{ ++ int ret = 0; ++ ++ if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { ++ ip_set_printk("'%s' uses wrong protocol version %u (want %u)", ++ set_type->typename, ++ set_type->protocol_version, ++ IP_SET_PROTOCOL_VERSION); ++ return -EINVAL; ++ } ++ ++ write_lock_bh(&ip_set_lock); ++ if (find_set_type(set_type->typename)) { ++ /* Duplicate! */ ++ ip_set_printk("'%s' already registered!", ++ set_type->typename); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ if (!try_module_get(THIS_MODULE)) { ++ ret = -EFAULT; ++ goto unlock; ++ } ++ list_add(&set_type->list, &set_type_list); ++ DP("'%s' registered.", set_type->typename); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return ret; ++} ++ ++void ++ip_set_unregister_set_type(struct ip_set_type *set_type) ++{ ++ write_lock_bh(&ip_set_lock); ++ if (!find_set_type(set_type->typename)) { ++ ip_set_printk("'%s' not registered?", ++ set_type->typename); ++ goto unlock; ++ } ++ list_del(&set_type->list); ++ module_put(THIS_MODULE); ++ DP("'%s' unregistered.", set_type->typename); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ ++} ++ ++/* ++ * Userspace routines ++ */ ++ ++/* ++ * Find set by name, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ down(&ip_set_app_mutex); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ __ip_set_get(i); ++ index = i; ++ break; ++ } ++ } ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * Find set by index, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byindex(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ ++ if (index >= ip_set_max) ++ return IP_SET_INVALID_ID; ++ ++ if (ip_set_list[index]) ++ __ip_set_get(index); ++ else ++ index = IP_SET_INVALID_ID; ++ ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * If the given set pointer points to a valid set, decrement ++ * reference count by 1. The caller shall not assume the index ++ * to be valid, after calling this function. ++ */ ++void ip_set_put(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ if (ip_set_list[index]) ++ __ip_set_put(index); ++ up(&ip_set_app_mutex); ++} ++ ++/* Find a set by name or index */ ++static ip_set_id_t ++ip_set_find_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ index = i; ++ break; ++ } ++ } ++ return index; ++} ++ ++static ip_set_id_t ++ip_set_find_byindex(ip_set_id_t index) ++{ ++ if (index >= ip_set_max || ip_set_list[index] == NULL) ++ index = IP_SET_INVALID_ID; ++ ++ return index; ++} ++ ++/* ++ * Add, del, test, bind and unbind ++ */ ++ ++static inline int ++__ip_set_testip(struct ip_set *set, ++ const void *data, ++ size_t size, ++ ip_set_ip_t *ip) ++{ ++ int res; ++ ++ read_lock_bh(&set->lock); ++ res = set->type->testip(set, data, size, ip); ++ read_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++__ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ do { ++ write_lock_bh(&set->lock); ++ res = set->type->addip(set, data, size, &ip); ++ write_unlock_bh(&set->lock); ++ } while (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0); ++ ++ return res; ++} ++ ++static int ++ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ ++ return __ip_set_addip(index, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt)); ++} ++ ++static int ++ip_set_delip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ write_lock_bh(&set->lock); ++ res = set->type->delip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ write_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++ip_set_testip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ ++ return (res > 0 ? -EEXIST : res); ++} ++ ++static int ++ip_set_bindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of a set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ write_lock_bh(&ip_set_lock); ++ /* Sets as binding values are referenced */ ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ set->binding = binding; ++ __ip_set_get(set->binding); ++ write_unlock_bh(&ip_set_lock); ++ ++ return 0; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = ip_set_hash_add(set->id, ip, binding); ++ ++ return res; ++} ++ ++#define FOREACH_SET_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __i; \ ++ struct ip_set *__set; \ ++ \ ++ for (__i = 0; __i < ip_set_max; __i++) { \ ++ __set = ip_set_list[__i]; \ ++ if (__set != NULL) \ ++ fn(__set , ##args); \ ++ } \ ++}) ++ ++static inline void ++__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) ++{ ++ if (set_hash->id == id) ++ __set_hash_del(set_hash); ++} ++ ++static inline void ++__unbind_default(struct ip_set *set) ++{ ++ if (set->binding != IP_SET_INVALID_ID) { ++ /* Sets as binding values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ } ++} ++ ++static int ++ip_set_unbindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ struct ip_set_req_bind *req_bind; ++ ip_set_ip_t ip; ++ int res; ++ ++ DP(""); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ DP("%u %s", index, req_bind->binding); ++ if (index == IP_SET_INVALID_ID) { ++ /* unbind :all: */ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of sets */ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_SET_DO(__unbind_default); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings of all sets*/ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } ++ DP("unreachable reached!"); ++ return -EINVAL; ++ } ++ ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ ip_set_id_t binding = ip_set_find_byindex(set->binding); ++ ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ write_lock_bh(&ip_set_lock); ++ /* Sets in hash values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ write_unlock_bh(&ip_set_lock); ++ ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings */ ++ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ ++ DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (res >= 0) ++ res = ip_set_hash_del(set->id, ip); ++ ++ return res; ++} ++ ++static int ++ip_set_testbind(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = (set->binding == binding) ? -EEXIST : 0; ++ ++ return res; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = (ip_set_find_in_hash(set->id, ip) == binding) ++ ? -EEXIST : 0; ++ ++ return res; ++} ++ ++static struct ip_set_type * ++find_set_type_rlock(const char *typename) ++{ ++ struct ip_set_type *type; ++ ++ read_lock_bh(&ip_set_lock); ++ type = find_set_type(typename); ++ if (type == NULL) ++ read_unlock_bh(&ip_set_lock); ++ ++ return type; ++} ++ ++static int ++find_free_id(const char *name, ++ ip_set_id_t *index, ++ ip_set_id_t *id) ++{ ++ ip_set_id_t i; ++ ++ *id = IP_SET_INVALID_ID; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) { ++ if (*id == IP_SET_INVALID_ID) ++ *id = *index = i; ++ } else if (strcmp(name, ip_set_list[i]->name) == 0) ++ /* Name clash */ ++ return -EEXIST; ++ } ++ if (*id == IP_SET_INVALID_ID) ++ /* No free slot remained */ ++ return -ERANGE; ++ /* Check that index is usable as id (swapping) */ ++ check: ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && ip_set_list[i]->id == *id) { ++ *id = i; ++ goto check; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Create a set ++ */ ++static int ++ip_set_create(const char *name, ++ const char *typename, ++ ip_set_id_t restore, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ ip_set_id_t index = 0, id; ++ int res = 0; ++ ++ DP("setname: %s, typename: %s, id: %u", name, typename, restore); ++ /* ++ * First, and without any locks, allocate and initialize ++ * a normal base set structure. ++ */ ++ set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); ++ if (!set) ++ return -ENOMEM; ++ set->lock = RW_LOCK_UNLOCKED; ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ set->binding = IP_SET_INVALID_ID; ++ atomic_set(&set->ref, 0); ++ ++ /* ++ * Next, take the &ip_set_lock, check that we know the type, ++ * and take a reference on the type, to make sure it ++ * stays available while constructing our new set. ++ * ++ * After referencing the type, we drop the &ip_set_lock, ++ * and let the new set construction run without locks. ++ */ ++ set->type = find_set_type_rlock(typename); ++ if (set->type == NULL) { ++ /* Try loading the module */ ++ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1]; ++ strcpy(modulename, "ip_set_"); ++ strcat(modulename, typename); ++ DP("try to load %s", modulename); ++ request_module(modulename); ++ set->type = find_set_type_rlock(typename); ++ } ++ if (set->type == NULL) { ++ ip_set_printk("no set type '%s', set '%s' not created", ++ typename, name); ++ res = -ENOENT; ++ goto out; ++ } ++ if (!try_module_get(set->type->me)) { ++ read_unlock_bh(&ip_set_lock); ++ res = -EFAULT; ++ goto out; ++ } ++ read_unlock_bh(&ip_set_lock); ++ ++ /* ++ * Without holding any locks, create private part. ++ */ ++ res = set->type->create(set, data, size); ++ if (res != 0) ++ goto put_out; ++ ++ /* BTW, res==0 here. */ ++ ++ /* ++ * Here, we have a valid, constructed set. &ip_set_lock again, ++ * find free id/index and check that it is not already in ++ * ip_set_list. ++ */ ++ write_lock_bh(&ip_set_lock); ++ if ((res = find_free_id(set->name, &index, &id)) != 0) { ++ DP("no free id!"); ++ goto cleanup; ++ } ++ ++ /* Make sure restore gets the same index */ ++ if (restore != IP_SET_INVALID_ID && index != restore) { ++ DP("Can't restore, sets are screwed up"); ++ res = -ERANGE; ++ goto cleanup; ++ } ++ ++ /* ++ * Finally! Add our shiny new set to the list, and be done. ++ */ ++ DP("create: '%s' created with index %u, id %u!", set->name, index, id); ++ set->id = id; ++ ip_set_list[index] = set; ++ write_unlock_bh(&ip_set_lock); ++ return res; ++ ++ cleanup: ++ write_unlock_bh(&ip_set_lock); ++ set->type->destroy(set); ++ put_out: ++ module_put(set->type->me); ++ out: ++ kfree(set); ++ return res; ++} ++ ++/* ++ * Destroy a given existing set ++ */ ++static void ++ip_set_destroy_set(ip_set_id_t index) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ++ IP_SET_ASSERT(set); ++ DP("set: %s", set->name); ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ ip_set_list[index] = NULL; ++ write_unlock_bh(&ip_set_lock); ++ ++ /* Must call it without holding any lock */ ++ set->type->destroy(set); ++ module_put(set->type->me); ++ kfree(set); ++} ++ ++/* ++ * Destroy a set - or all sets ++ * Sets must not be referenced/used. ++ */ ++static int ++ip_set_destroy(ip_set_id_t index) ++{ ++ ip_set_id_t i; ++ ++ /* ref modification always protected by the mutex */ ++ if (index != IP_SET_INVALID_ID) { ++ if (atomic_read(&ip_set_list[index]->ref)) ++ return -EBUSY; ++ ip_set_destroy_set(index); ++ } else { ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && (atomic_read(&ip_set_list[i]->ref))) ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ ip_set_destroy_set(i); ++ } ++ } ++ return 0; ++} ++ ++static void ++ip_set_flush_set(struct ip_set *set) ++{ ++ DP("set: %s %u", set->name, set->id); ++ ++ write_lock_bh(&set->lock); ++ set->type->flush(set); ++ write_unlock_bh(&set->lock); ++} ++ ++/* ++ * Flush data in a set - or in all sets ++ */ ++static int ++ip_set_flush(ip_set_id_t index) ++{ ++ if (index != IP_SET_INVALID_ID) { ++ IP_SET_ASSERT(ip_set_list[index]); ++ ip_set_flush_set(ip_set_list[index]); ++ } else ++ FOREACH_SET_DO(ip_set_flush_set); ++ ++ return 0; ++} ++ ++/* Rename a set */ ++static int ++ip_set_rename(ip_set_id_t index, const char *name) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_id_t i; ++ int res = 0; ++ ++ DP("set: %s to %s", set->name, name); ++ write_lock_bh(&ip_set_lock); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strncmp(ip_set_list[i]->name, ++ name, ++ IP_SET_MAXNAMELEN - 1) == 0) { ++ res = -EEXIST; ++ goto unlock; ++ } ++ } ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return res; ++} ++ ++/* ++ * Swap two sets so that name/index points to the other. ++ * References are also swapped. ++ */ ++static int ++ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index) ++{ ++ struct ip_set *from = ip_set_list[from_index]; ++ struct ip_set *to = ip_set_list[to_index]; ++ char from_name[IP_SET_MAXNAMELEN]; ++ u_int32_t from_ref; ++ ++ DP("set: %s to %s", from->name, to->name); ++ /* Features must not change. Artifical restriction. */ ++ if (from->type->features != to->type->features) ++ return -ENOEXEC; ++ ++ /* No magic here: ref munging protected by the mutex */ ++ write_lock_bh(&ip_set_lock); ++ strncpy(from_name, from->name, IP_SET_MAXNAMELEN); ++ from_ref = atomic_read(&from->ref); ++ ++ strncpy(from->name, to->name, IP_SET_MAXNAMELEN); ++ atomic_set(&from->ref, atomic_read(&to->ref)); ++ strncpy(to->name, from_name, IP_SET_MAXNAMELEN); ++ atomic_set(&to->ref, from_ref); ++ ++ ip_set_list[from_index] = to; ++ ip_set_list[to_index] = from; ++ ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++} ++ ++/* ++ * List set data ++ */ ++ ++static inline void ++__set_hash_bindings_size_list(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_list); ++} ++ ++static inline void ++__set_hash_bindings_size_save(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_save); ++} ++ ++static inline void ++__set_hash_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, void *data, int *used) ++{ ++ if (set_hash->id == id) { ++ struct ip_set_hash_list *hash_list = ++ (struct ip_set_hash_list *)(data + *used); ++ ++ hash_list->ip = set_hash->ip; ++ hash_list->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_list); ++ } ++} ++ ++static int ip_set_list_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_list *set_list; ++ ++ /* Pointer to our header */ ++ set_list = (struct ip_set_list *) (data + *used); ++ ++ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_list) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_list); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_list->header_size = set->type->header_size; ++ if (*used + set_list->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_list->index = index; ++ set_list->binding = set->binding; ++ set_list->ref = atomic_read(&set->ref); ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_list->header_size; ++ ++ /* Get and ensure set specific members size */ ++ set_list->members_size = set->type->list_members_size(set); ++ if (*used + set_list->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_list->members_size; ++ read_unlock_bh(&set->lock); ++ ++ /* Bindings */ ++ ++ /* Get and ensure set specific bindings size */ ++ set_list->bindings_size = 0; ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ set->id, &set_list->bindings_size); ++ if (*used + set_list->bindings_size > len) ++ goto not_enough_mem; ++ ++ /* Fill in set spefific bindings data */ ++ FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); ++ ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++/* ++ * Save sets ++ */ ++static int ip_set_save_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set; ++ struct ip_set_save *set_save; ++ ++ /* Pointer to our header */ ++ set_save = (struct ip_set_save *) (data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_save); ++ ++ set = ip_set_list[index]; ++ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, ++ data, data + *used); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_save->header_size = set->type->header_size; ++ if (*used + set_save->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_save->index = index; ++ set_save->binding = set->binding; ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_save->header_size; ++ ++ DP("set header filled: %s, used: %u(%u) %p %p", set->name, *used, ++ set_save->header_size, data, data + *used); ++ /* Get and ensure set specific members size */ ++ set_save->members_size = set->type->list_members_size(set); ++ if (*used + set_save->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_save->members_size; ++ read_unlock_bh(&set->lock); ++ DP("set members filled: %s, used: %u(%u) %p %p", set->name, *used, ++ set_save->members_size, data, data + *used); ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++static inline void ++__set_hash_save_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, ++ void *data, ++ int *used, ++ int len, ++ int *res) ++{ ++ if (*res == 0 ++ && (id == IP_SET_INVALID_ID || set_hash->id == id)) { ++ struct ip_set_hash_save *hash_save = ++ (struct ip_set_hash_save *)(data + *used); ++ /* Ensure bindings size */ ++ if (*used + sizeof(struct ip_set_hash_save) > len) { ++ *res = -ENOMEM; ++ return; ++ } ++ hash_save->id = set_hash->id; ++ hash_save->ip = set_hash->ip; ++ hash_save->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_save); ++ } ++} ++ ++static int ip_set_save_bindings(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ int res = 0; ++ struct ip_set_save *set_save; ++ ++ DP("used %u, len %u", *used, len); ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ return -ENOMEM; ++ ++ /* Marker */ ++ set_save = (struct ip_set_save *) (data + *used); ++ set_save->index = IP_SET_INVALID_ID; ++ set_save->header_size = 0; ++ set_save->members_size = 0; ++ *used += sizeof(struct ip_set_save); ++ ++ DP("marker added used %u, len %u", *used, len); ++ /* Fill in bindings data */ ++ if (index != IP_SET_INVALID_ID) ++ /* Sets are identified by id in hash */ ++ index = ip_set_list[index]->id; ++ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); ++ ++ return res; ++} ++ ++/* ++ * Restore sets ++ */ ++static int ip_set_restore(void *data, ++ int len) ++{ ++ int res = 0; ++ int line = 0, used = 0, members_size; ++ struct ip_set *set; ++ struct ip_set_hash_save *hash_save; ++ struct ip_set_restore *set_restore; ++ ip_set_id_t index; ++ ++ /* Loop to restore sets */ ++ while (1) { ++ line++; ++ ++ DP("%u %u %u", used, sizeof(struct ip_set_restore), len); ++ /* Get and ensure header size */ ++ if (used + sizeof(struct ip_set_restore) > len) ++ return line; ++ set_restore = (struct ip_set_restore *) (data + used); ++ used += sizeof(struct ip_set_restore); ++ ++ /* Ensure data size */ ++ if (used ++ + set_restore->header_size ++ + set_restore->members_size > len) ++ return line; ++ ++ /* Check marker */ ++ if (set_restore->index == IP_SET_INVALID_ID) { ++ line--; ++ goto bindings; ++ } ++ ++ /* Try to create the set */ ++ DP("restore %s %s", set_restore->name, set_restore->typename); ++ res = ip_set_create(set_restore->name, ++ set_restore->typename, ++ set_restore->index, ++ data + used, ++ set_restore->header_size); ++ ++ if (res != 0) ++ return line; ++ used += set_restore->header_size; ++ ++ index = ip_set_find_byindex(set_restore->index); ++ DP("index %u, restore_index %u", index, set_restore->index); ++ if (index != set_restore->index) ++ return line; ++ /* Try to restore members data */ ++ set = ip_set_list[index]; ++ members_size = 0; ++ DP("members_size %u reqsize %u", ++ set_restore->members_size, set->type->reqsize); ++ while (members_size + set->type->reqsize <= ++ set_restore->members_size) { ++ line++; ++ DP("members: %u, line %u", members_size, line); ++ res = __ip_set_addip(index, ++ data + used + members_size, ++ set->type->reqsize); ++ if (!(res == 0 || res == -EEXIST)) ++ return line; ++ members_size += set->type->reqsize; ++ } ++ ++ DP("members_size %u %u", ++ set_restore->members_size, members_size); ++ if (members_size != set_restore->members_size) ++ return line++; ++ used += set_restore->members_size; ++ } ++ ++ bindings: ++ /* Loop to restore bindings */ ++ while (used < len) { ++ line++; ++ ++ DP("restore binding, line %u", line); ++ /* Get and ensure size */ ++ if (used + sizeof(struct ip_set_hash_save) > len) ++ return line; ++ hash_save = (struct ip_set_hash_save *) (data + used); ++ used += sizeof(struct ip_set_hash_save); ++ ++ /* hash_save->id is used to store the index */ ++ index = ip_set_find_byindex(hash_save->id); ++ DP("restore binding index %u, id %u, %u -> %u", ++ index, hash_save->id, hash_save->ip, hash_save->binding); ++ if (index != hash_save->id) ++ return line; ++ if (ip_set_find_byindex(hash_save->binding) == IP_SET_INVALID_ID) { ++ DP("corrupt binding set index %u", hash_save->binding); ++ return line; ++ } ++ set = ip_set_list[hash_save->id]; ++ /* Null valued IP means default binding */ ++ if (hash_save->ip) ++ res = ip_set_hash_add(set->id, ++ hash_save->ip, ++ hash_save->binding); ++ else { ++ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); ++ write_lock_bh(&ip_set_lock); ++ set->binding = hash_save->binding; ++ __ip_set_get(set->binding); ++ write_unlock_bh(&ip_set_lock); ++ DP("default binding: %u", set->binding); ++ } ++ if (res != 0) ++ return line; ++ } ++ if (used != len) ++ return line; ++ ++ return 0; ++} ++ ++static int ++ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) ++{ ++ void *data; ++ int res = 0; /* Assume OK */ ++ unsigned *op; ++ struct ip_set_req_adt *req_adt; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ int (*adtfn)(ip_set_id_t index, ++ const void *data, size_t size); ++ struct fn_table { ++ int (*fn)(ip_set_id_t index, ++ const void *data, size_t size); ++ } adtfn_table[] = ++ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, ++ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, ++ }; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (len <= sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >%zu, got %u)", ++ sizeof(unsigned), len); ++ return -EINVAL; ++ } ++ data = vmalloc(len); ++ if (!data) { ++ DP("out of mem for %u bytes", len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *)data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_CREATE:{ ++ struct ip_set_req_create *req_create ++ = (struct ip_set_req_create *) data; ++ ++ if (len < sizeof(struct ip_set_req_create)) { ++ ip_set_printk("short CREATE data (want >=%zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ res = ip_set_create(req_create->name, ++ req_create->typename, ++ IP_SET_INVALID_ID, ++ data + sizeof(struct ip_set_req_create), ++ len - sizeof(struct ip_set_req_create)); ++ goto done; ++ } ++ case IP_SET_OP_DESTROY:{ ++ struct ip_set_req_std *req_destroy ++ = (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid DESTROY data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { ++ /* Destroy all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_destroy->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ ++ res = ip_set_destroy(index); ++ goto done; ++ } ++ case IP_SET_OP_FLUSH:{ ++ struct ip_set_req_std *req_flush = ++ (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid FLUSH data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_flush->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = ip_set_flush(index); ++ goto done; ++ } ++ case IP_SET_OP_RENAME:{ ++ struct ip_set_req_create *req_rename ++ = (struct ip_set_req_create *) data; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid RENAME data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_rename->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_rename(index, req_rename->typename); ++ goto done; ++ } ++ case IP_SET_OP_SWAP:{ ++ struct ip_set_req_create *req_swap ++ = (struct ip_set_req_create *) data; ++ ip_set_id_t to_index; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid SWAP data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_swap->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ to_index = ip_set_find_byname(req_swap->typename); ++ if (to_index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_swap(index, to_index); ++ goto done; ++ } ++ default: ++ break; /* Set identified by id */ ++ } ++ ++ /* There we may have add/del/test/bind/unbind/test_bind operations */ ++ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { ++ res = -EBADMSG; ++ goto done; ++ } ++ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; ++ ++ if (len < sizeof(struct ip_set_req_adt)) { ++ ip_set_printk("short data in adt request (want >=%zu, got %u)", ++ sizeof(struct ip_set_req_adt), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_adt = (struct ip_set_req_adt *) data; ++ ++ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ ++ if (!(*op == IP_SET_OP_UNBIND_SET ++ && req_adt->index == IP_SET_INVALID_ID)) { ++ index = ip_set_find_byindex(req_adt->index); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = adtfn(index, data, len); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static int ++ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) ++{ ++ int res = 0; ++ unsigned *op; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ void *data; ++ int copylen = *len; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, *len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (*len < sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >=%zu, got %d)", ++ sizeof(unsigned), *len); ++ return -EINVAL; ++ } ++ data = vmalloc(*len); ++ if (!data) { ++ DP("out of mem for %d bytes", *len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, *len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *) data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_VERSION: { ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ ++ if (*len != sizeof(struct ip_set_req_version)) { ++ ip_set_printk("invalid VERSION (want %zu, got %d)", ++ sizeof(struct ip_set_req_version), ++ *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_version->version = IP_SET_PROTOCOL_VERSION; ++ res = copy_to_user(user, req_version, ++ sizeof(struct ip_set_req_version)); ++ goto done; ++ } ++ case IP_SET_OP_GET_BYNAME: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ req_get->set.index = index; ++ goto copy; ++ } ++ case IP_SET_OP_GET_BYINDEX: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byindex(req_get->set.index); ++ strncpy(req_get->set.name, ++ index == IP_SET_INVALID_ID ? "" ++ : ip_set_list[index]->name, IP_SET_MAXNAMELEN); ++ goto copy; ++ } ++ case IP_SET_OP_ADT_GET: { ++ struct ip_set_req_adt_get *req_get ++ = (struct ip_set_req_adt_get *) data; ++ ++ if (*len != sizeof(struct ip_set_req_adt_get)) { ++ ip_set_printk("invalid ADT_GET (want %zu, got %d)", ++ sizeof(struct ip_set_req_adt_get), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ if (index != IP_SET_INVALID_ID) { ++ req_get->set.index = index; ++ strncpy(req_get->typename, ++ ip_set_list[index]->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ } else { ++ res = -ENOENT; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_MAX_SETS: { ++ struct ip_set_req_max_sets *req_max_sets ++ = (struct ip_set_req_max_sets *) data; ++ ip_set_id_t i; ++ ++ if (*len != sizeof(struct ip_set_req_max_sets)) { ++ ip_set_printk("invalid MAX_SETS (want %zu, got %d)", ++ sizeof(struct ip_set_req_max_sets), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { ++ req_max_sets->set.index = IP_SET_INVALID_ID; ++ } else { ++ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_max_sets->set.index = ++ ip_set_find_byname(req_max_sets->set.name); ++ if (req_max_sets->set.index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ req_max_sets->max_sets = ip_set_max; ++ req_max_sets->sets = 0; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ req_max_sets->sets++; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST_SIZE: ++ case IP_SET_OP_SAVE_SIZE: { ++ struct ip_set_req_setnames *req_setnames ++ = (struct ip_set_req_setnames *) data; ++ struct ip_set_name_list *name_list; ++ struct ip_set *set; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_setnames)) { ++ ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_setnames), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_setnames->size = 0; ++ used = sizeof(struct ip_set_req_setnames); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) ++ continue; ++ name_list = (struct ip_set_name_list *) ++ (data + used); ++ used += sizeof(struct ip_set_name_list); ++ if (used > copylen) { ++ res = -EAGAIN; ++ goto done; ++ } ++ set = ip_set_list[i]; ++ /* Fill in index, name, etc. */ ++ name_list->index = i; ++ name_list->id = set->id; ++ strncpy(name_list->name, ++ set->name, ++ IP_SET_MAXNAMELEN - 1); ++ strncpy(name_list->typename, ++ set->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ DP("filled %s of type %s, index %u\n", ++ name_list->name, name_list->typename, ++ name_list->index); ++ if (!(req_setnames->index == IP_SET_INVALID_ID ++ || req_setnames->index == i)) ++ continue; ++ /* Update size */ ++ switch (*op) { ++ case IP_SET_OP_LIST_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_list) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ /* Sets are identified by id in the hash */ ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ set->id, &req_setnames->size); ++ break; ++ } ++ case IP_SET_OP_SAVE_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_save) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ FOREACH_HASH_DO(__set_hash_bindings_size_save, ++ set->id, &req_setnames->size); ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST: { ++ struct ip_set_req_list *req_list ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short LIST (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_list->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* List all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_list_set(i, data, &used, *len); ++ } ++ } else { ++ /* List an individual set */ ++ res = ip_set_list_set(index, data, &used, *len); ++ } ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_SAVE: { ++ struct ip_set_req_list *req_save ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short SAVE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_save->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* Save all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_save_set(i, data, &used, *len); ++ } ++ } else { ++ /* Save an individual set */ ++ res = ip_set_save_set(index, data, &used, *len); ++ } ++ if (res == 0) ++ res = ip_set_save_bindings(index, data, &used, *len); ++ ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_RESTORE: { ++ struct ip_set_req_setnames *req_restore ++ = (struct ip_set_req_setnames *) data; ++ int line; ++ ++ if (*len < sizeof(struct ip_set_req_setnames) ++ || *len != req_restore->size) { ++ ip_set_printk("invalid RESTORE (want =%zu, got %d)", ++ req_restore->size, *len); ++ res = -EINVAL; ++ goto done; ++ } ++ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), ++ req_restore->size - sizeof(struct ip_set_req_setnames)); ++ DP("ip_set_restore: %u", line); ++ if (line != 0) { ++ res = -EAGAIN; ++ req_restore->size = line; ++ copylen = sizeof(struct ip_set_req_setnames); ++ goto copy; ++ } ++ goto done; ++ } ++ default: ++ res = -EBADMSG; ++ goto done; ++ } /* end of switch(op) */ ++ ++ copy: ++ DP("set %s, copylen %u", index != IP_SET_INVALID_ID ++ && ip_set_list[index] ++ ? ip_set_list[index]->name ++ : ":all:", copylen); ++ res = copy_to_user(user, data, copylen); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static struct nf_sockopt_ops so_set = { ++ .pf = PF_INET, ++ .set_optmin = SO_IP_SET, ++ .set_optmax = SO_IP_SET + 1, ++ .set = &ip_set_sockfn_set, ++ .get_optmin = SO_IP_SET, ++ .get_optmax = SO_IP_SET + 1, ++ .get = &ip_set_sockfn_get, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ .owner = THIS_MODULE, ++#endif ++}; ++ ++static int max_sets, hash_size; ++module_param(max_sets, int, 0600); ++MODULE_PARM_DESC(max_sets, "maximal number of sets"); ++module_param(hash_size, int, 0600); ++MODULE_PARM_DESC(hash_size, "hash size for bindings"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("module implementing core IP set support"); ++ ++static int __init ip_set_init(void) ++{ ++ int res; ++ ip_set_id_t i; ++ ++ get_random_bytes(&ip_set_hash_random, 4); ++ if (max_sets) ++ ip_set_max = max_sets; ++ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); ++ if (!ip_set_list) { ++ printk(KERN_ERR "Unable to create ip_set_list\n"); ++ return -ENOMEM; ++ } ++ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); ++ if (hash_size) ++ ip_set_bindings_hash_size = hash_size; ++ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); ++ if (!ip_set_hash) { ++ printk(KERN_ERR "Unable to create ip_set_hash\n"); ++ vfree(ip_set_list); ++ return -ENOMEM; ++ } ++ for (i = 0; i < ip_set_bindings_hash_size; i++) ++ INIT_LIST_HEAD(&ip_set_hash[i]); ++ ++ INIT_LIST_HEAD(&set_type_list); ++ ++ res = nf_register_sockopt(&so_set); ++ if (res != 0) { ++ ip_set_printk("SO_SET registry failed: %d", res); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ return res; ++ } ++ return 0; ++} ++ ++static void __exit ip_set_fini(void) ++{ ++ /* There can't be any existing set or binding */ ++ nf_unregister_sockopt(&so_set); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ DP("these are the famous last words"); ++} ++ ++EXPORT_SYMBOL(ip_set_register_set_type); ++EXPORT_SYMBOL(ip_set_unregister_set_type); ++ ++EXPORT_SYMBOL(ip_set_get_byname); ++EXPORT_SYMBOL(ip_set_get_byindex); ++EXPORT_SYMBOL(ip_set_put); ++ ++EXPORT_SYMBOL(ip_set_addip_kernel); ++EXPORT_SYMBOL(ip_set_delip_kernel); ++EXPORT_SYMBOL(ip_set_testip_kernel); ++ ++module_init(ip_set_init); ++module_exit(ip_set_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_iphash.c +@@ -0,0 +1,429 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an ip hash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++static inline __u32 ++jhash_ip(const struct ip_set_iphash *map, uint16_t i, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ __u32 id; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ *hash_ip = ip & map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ if (*elem == *hash_ip) ++ return id; ++ /* No shortcut at testing - there can be deleted ++ * entries. */ ++ } ++ return UINT_MAX; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ if (!ip || map->elements >= limit) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == *hash_ip) ++ return -EEXIST; ++ if (!*elem) { ++ *elem = *hash_ip; ++ map->elements++; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __addip((struct ip_set_iphash *) set->data, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t hash_ip, *elem; ++ void *members; ++ u_int32_t i, hashsize = map->hashsize; ++ int res; ++ struct ip_set_iphash *tmp; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new hash size */ ++ hashsize += (hashsize * map->resize)/100; ++ if (hashsize == map->hashsize) ++ hashsize++; ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, map->hashsize, hashsize); ++ ++ tmp = kmalloc(sizeof(struct ip_set_iphash) ++ + map->probes * sizeof(uint32_t), GFP_ATOMIC); ++ if (!tmp) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iphash) ++ + map->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); ++ if (!tmp->members) { ++ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); ++ kfree(tmp); ++ return -ENOMEM; ++ } ++ tmp->hashsize = hashsize; ++ tmp->elements = 0; ++ tmp->probes = map->probes; ++ tmp->resize = map->resize; ++ tmp->netmask = map->netmask; ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_iphash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ if (*elem) ++ res = __addip(tmp, *elem, &hash_ip); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ harray_free(tmp->members); ++ kfree(tmp); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ ++ map->hashsize = tmp->hashsize; ++ map->members = tmp->members; ++ write_unlock_bh(&set->lock); ++ ++ harray_free(members); ++ kfree(tmp); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t id, *elem; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ id = hash_id(set, ip, hash_ip); ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iphash_create *req = ++ (struct ip_set_req_iphash_create *) data; ++ struct ip_set_iphash *map; ++ uint16_t i; ++ ++ if (size != sizeof(struct ip_set_req_iphash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ if (req->probes < 1) { ++ ip_set_printk("probes too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iphash) ++ + req->probes * sizeof(uint32_t), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iphash) ++ + req->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ for (i = 0; i < req->probes; i++) ++ get_random_bytes(((uint32_t *) map->initval)+i, 4); ++ map->elements = 0; ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ map->netmask = req->netmask; ++ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ return -ENOMEM; ++ } ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ harray_free(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); ++ map->elements = 0; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ struct ip_set_req_iphash_create *header = ++ (struct ip_set_req_iphash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t i, *elem; ++ ++ for (i = 0; i < map->hashsize; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ ((ip_set_ip_t *)data)[i] = *elem; ++ } ++} ++ ++static struct ip_set_type ip_set_iphash = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iphash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iphash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iphash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_iphash_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_iphash); ++} ++ ++static void __exit ip_set_iphash_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iphash); ++} ++ ++module_init(ip_set_iphash_init); ++module_exit(ip_set_iphash_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_ipmap.c +@@ -0,0 +1,336 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the single bitmap type */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static inline ip_set_ip_t ++ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) ++{ ++ return (ip - map->first_ip)/map->hosts; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return !!test_bit(ip_to_id(map, *hash_ip), map->members); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ int res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u", HIPQUAD(req->ip)); ++ return __addip(set, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __addip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_ipmap_create *req = ++ (struct ip_set_req_ipmap_create *) data; ++ struct ip_set_ipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_ipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipmap)); ++ return -ENOMEM; ++ } ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ map->netmask = req->netmask; ++ ++ if (req->netmask == 0xFFFFFFFF) { ++ map->hosts = 1; ++ map->sizeid = map->last_ip - map->first_ip + 1; ++ } else { ++ unsigned int mask_bits, netmask_bits; ++ ip_set_ip_t mask; ++ ++ map->first_ip &= map->netmask; /* Should we better bark? */ ++ ++ mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); ++ netmask_bits = mask_to_bits(map->netmask); ++ ++ if ((!mask && (map->first_ip || map->last_ip != 0xFFFFFFFF)) ++ || netmask_bits <= mask_bits) ++ return -ENOEXEC; ++ ++ DP("mask_bits %u, netmask_bits %u", ++ mask_bits, netmask_bits); ++ map->hosts = 2 << (32 - netmask_bits - 1); ++ map->sizeid = 2 << (netmask_bits - mask_bits - 1); ++ } ++ if (map->sizeid > MAX_RANGE + 1) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE+1); ++ kfree(map); ++ return -ENOEXEC; ++ } ++ DP("hosts %u, sizeid %u", map->hosts, map->sizeid); ++ newbytes = bitmap_bytes(0, map->sizeid - 1); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ struct ip_set_req_ipmap_create *header = ++ (struct ip_set_req_ipmap_create *) data; ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ return bitmap_bytes(0, map->sizeid - 1); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ int bytes = bitmap_bytes(0, map->sizeid - 1); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_ipmap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_ipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_ipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipmap type of IP sets"); ++ ++static int __init ip_set_ipmap_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_ipmap); ++} ++ ++static void __exit ip_set_ipmap_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_ipmap); ++} ++ ++module_init(ip_set_ipmap_init); ++module_exit(ip_set_ipmap_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_ipporthash.c +@@ -0,0 +1,581 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an ip+port hash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++/* We must handle non-linear skbs */ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, u_int32_t flags) ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ struct iphdr *iph = ip_hdr(skb); ++#else ++ struct iphdr *iph = skb->nh.iph; ++#endif ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr tcph; ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ tcph.source : tcph.dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr udph; ++ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ udph.source : udph.dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++ ++static inline __u32 ++jhash_ip(const struct ip_set_ipporthash *map, uint16_t i, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); ++} ++ ++#define HASH_IP(map, ip, port) (port + ((ip - ((map)->first_ip)) << 16)) ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipporthash *map = ++ (struct ip_set_ipporthash *) set->data; ++ __u32 id; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ *hash_ip = HASH_IP(map, ip, port); ++ DP("set: %s, ipport:%u.%u.%u.%u:%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), port, HIPQUAD(*hash_ip)); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ if (*elem == *hash_ip) ++ return id; ++ /* No shortcut at testing - there can be deleted ++ * entries. */ ++ } ++ return UINT_MAX; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ return (hash_id(set, ip, port, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipporthash *req = ++ (struct ip_set_req_ipporthash *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, req->port, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port; ++ int res; ++ ++ if (flags[index+1] == 0) ++ return 0; ++ ++ port = get_port(skb, flags[index+1]); ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ DP("flag %s port %u", ++ flags[index+1] & IPSET_SRC ? "SRC" : "DST", ++ port); ++ if (port == INVALID_PORT) ++ return 0; ++ ++ res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ port, ++ hash_ip); ++ return (res < 0 ? 0 : res); ++ ++} ++ ++static inline int ++__add_haship(struct ip_set_ipporthash *map, ip_set_ip_t hash_ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, hash_ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == hash_ip) ++ return -EEXIST; ++ if (!*elem) { ++ *elem = hash_ip; ++ map->elements++; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__addip(struct ip_set_ipporthash *map, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ if (map->elements > limit) ++ return -ERANGE; ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = HASH_IP(map, ip, port); ++ ++ return __add_haship(map, *hash_ip); ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipporthash *req = ++ (struct ip_set_req_ipporthash *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash), ++ size); ++ return -EINVAL; ++ } ++ return __addip((struct ip_set_ipporthash *) set->data, ++ req->ip, req->port, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port; ++ ++ if (flags[index+1] == 0) ++ return -EINVAL; ++ ++ port = get_port(skb, flags[index+1]); ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ DP("flag %s port %u", ++ flags[index+1] & IPSET_SRC ? "SRC" : "DST", ++ port); ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __addip((struct ip_set_ipporthash *) set->data, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ port, ++ hash_ip); ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ip_set_ip_t *elem; ++ void *members; ++ u_int32_t i, hashsize = map->hashsize; ++ int res; ++ struct ip_set_ipporthash *tmp; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new hash size */ ++ hashsize += (hashsize * map->resize)/100; ++ if (hashsize == map->hashsize) ++ hashsize++; ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, map->hashsize, hashsize); ++ ++ tmp = kmalloc(sizeof(struct ip_set_ipporthash) ++ + map->probes * sizeof(uint32_t), GFP_ATOMIC); ++ if (!tmp) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipporthash) ++ + map->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); ++ if (!tmp->members) { ++ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); ++ kfree(tmp); ++ return -ENOMEM; ++ } ++ tmp->hashsize = hashsize; ++ tmp->elements = 0; ++ tmp->probes = map->probes; ++ tmp->resize = map->resize; ++ tmp->first_ip = map->first_ip; ++ tmp->last_ip = map->last_ip; ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_ipporthash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ if (*elem) ++ res = __add_haship(tmp, *elem); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ harray_free(tmp->members); ++ kfree(tmp); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ ++ map->hashsize = tmp->hashsize; ++ map->members = tmp->members; ++ write_unlock_bh(&set->lock); ++ ++ harray_free(members); ++ kfree(tmp); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t port, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ip_set_ip_t id; ++ ip_set_ip_t *elem; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ id = hash_id(set, ip, port, hash_ip); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipporthash *req = ++ (struct ip_set_req_ipporthash *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, req->port, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port; ++ ++ if (flags[index+1] == 0) ++ return -EINVAL; ++ ++ port = get_port(skb, flags[index+1]); ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ DP("flag %s port %u", ++ flags[index+1] & IPSET_SRC ? "SRC" : "DST", ++ port); ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ port, ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_ipporthash_create *req = ++ (struct ip_set_req_ipporthash_create *) data; ++ struct ip_set_ipporthash *map; ++ uint16_t i; ++ ++ if (size != sizeof(struct ip_set_req_ipporthash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipporthash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ if (req->probes < 1) { ++ ip_set_printk("probes too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_ipporthash) ++ + req->probes * sizeof(uint32_t), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipporthash) ++ + req->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ for (i = 0; i < req->probes; i++) ++ get_random_bytes(((uint32_t *) map->initval)+i, 4); ++ map->elements = 0; ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ return -ENOMEM; ++ } ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ++ harray_free(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); ++ map->elements = 0; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ struct ip_set_req_ipporthash_create *header = ++ (struct ip_set_req_ipporthash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipporthash *map = (struct ip_set_ipporthash *) set->data; ++ ip_set_ip_t i, *elem; ++ ++ for (i = 0; i < map->hashsize; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ ((ip_set_ip_t *)data)[i] = *elem; ++ } ++} ++ ++static struct ip_set_type ip_set_ipporthash = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_DATA_DOUBLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_ipporthash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_ipporthash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("ipporthash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_ipporthash_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_ipporthash); ++} ++ ++static void __exit ip_set_ipporthash_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_ipporthash); ++} ++ ++module_init(ip_set_ipporthash_init); ++module_exit(ip_set_ipporthash_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_iptree.c +@@ -0,0 +1,612 @@ ++/* Copyright (C) 2005 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the iptree type */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Backward compatibility */ ++#ifndef __nocast ++#define __nocast ++#endif ++ ++#include ++ ++static int limit = MAX_RANGE; ++ ++/* Garbage collection interval in seconds: */ ++#define IPTREE_GC_TIME 5*60 ++/* Sleep so many milliseconds before trying again ++ * to delete the gc timer at destroying/flushing a set */ ++#define IPTREE_DESTROY_SLEEP 100 ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++static struct kmem_cache *branch_cachep; ++static struct kmem_cache *leaf_cachep; ++#else ++static kmem_cache_t *branch_cachep; ++static kmem_cache_t *leaf_cachep; ++#endif ++ ++#if defined(__LITTLE_ENDIAN) ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[3]; \ ++ b = ((unsigned char *)addrp)[2]; \ ++ c = ((unsigned char *)addrp)[1]; \ ++ d = ((unsigned char *)addrp)[0]; \ ++} while (0) ++#elif defined(__BIG_ENDIAN) ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[0]; \ ++ b = ((unsigned char *)addrp)[1]; \ ++ c = ((unsigned char *)addrp)[2]; \ ++ d = ((unsigned char *)addrp)[3]; \ ++} while (0) ++#else ++#error "Please fix asm/byteorder.h" ++#endif /* __LITTLE_ENDIAN */ ++ ++#define TESTIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return 0; \ ++} while (0) ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); ++ TESTIP_WALK(map, a, btree); ++ TESTIP_WALK(btree, b, ctree); ++ TESTIP_WALK(ctree, c, dtree); ++ DP("%lu %lu", dtree->expires[d], jiffies); ++ return dtree->expires[d] ++ && (!map->timeout ++ || time_after(dtree->expires[d], jiffies)); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ int res; ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags[index] & IPSET_SRC ? "SRC" : "DST", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ NIPQUAD(ip_hdr(skb)->saddr), ++ NIPQUAD(ip_hdr(skb)->daddr)); ++#else ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++#endif ++ ++ res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ ++ if ((map)->tree[elem]) { \ ++ DP("found %u", elem); \ ++ branch = (map)->tree[elem]; \ ++ } else { \ ++ branch = (type *) \ ++ kmem_cache_alloc(cachep, GFP_ATOMIC); \ ++ if (branch == NULL) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[elem] = branch; \ ++ DP("alloc %u", elem); \ ++ } \ ++} while (0) ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ int ret = 0; ++ ++ if (!ip || map->elements >= limit) ++ /* We could call the garbage collector ++ * but it's probably overkill */ ++ return -ERANGE; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, timeout); ++ ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); ++ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); ++ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep); ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ ret = -EEXIST; ++ dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; ++ /* Lottery: I won! */ ++ if (dtree->expires[d] == 0) ++ dtree->expires[d] = 1; ++ DP("%u %lu", d, dtree->expires[d]); ++ if (ret == 0) ++ map->elements++; ++ return ret; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); ++ return __addip(set, req->ip, ++ req->timeout ? req->timeout : map->timeout, ++ hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ return __addip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ map->timeout, ++ hash_ip); ++} ++ ++#define DELIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return -EEXIST; \ ++} while (0) ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DELIP_WALK(map, a, btree); ++ DELIP_WALK(btree, b, ctree); ++ DELIP_WALK(ctree, c, dtree); ++ ++ if (dtree->expires[d]) { ++ dtree->expires[d] = 0; ++ map->elements--; ++ return 0; ++ } ++ return -EEXIST; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++#define LOOP_WALK_BEGIN(map, i, branch) \ ++ for (i = 0; i < 256; i++) { \ ++ if (!(map)->tree[i]) \ ++ continue; \ ++ branch = (map)->tree[i] ++ ++#define LOOP_WALK_END } ++ ++static void ip_tree_gc(unsigned long ul_set) ++{ ++ struct ip_set *set = (void *) ul_set; ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c,d; ++ unsigned char i,j,k; ++ ++ i = j = k = 0; ++ DP("gc: %s", set->name); ++ write_lock_bh(&set->lock); ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 256; d++) { ++ if (dtree->expires[d]) { ++ DP("gc: %u %u %u %u: expires %lu jiffies %lu", ++ a, b, c, d, ++ dtree->expires[d], jiffies); ++ if (map->timeout ++ && time_before(dtree->expires[d], jiffies)) { ++ dtree->expires[d] = 0; ++ map->elements--; ++ } else ++ k = 1; ++ } ++ } ++ if (k == 0) { ++ DP("gc: %s: leaf %u %u %u empty", ++ set->name, a, b, c); ++ kmem_cache_free(leaf_cachep, dtree); ++ ctree->tree[c] = NULL; ++ } else { ++ DP("gc: %s: leaf %u %u %u not empty", ++ set->name, a, b, c); ++ j = 1; ++ k = 0; ++ } ++ LOOP_WALK_END; ++ if (j == 0) { ++ DP("gc: %s: branch %u %u empty", ++ set->name, a, b); ++ kmem_cache_free(branch_cachep, ctree); ++ btree->tree[b] = NULL; ++ } else { ++ DP("gc: %s: branch %u %u not empty", ++ set->name, a, b); ++ i = 1; ++ j = k = 0; ++ } ++ LOOP_WALK_END; ++ if (i == 0) { ++ DP("gc: %s: branch %u empty", ++ set->name, a); ++ kmem_cache_free(branch_cachep, btree); ++ map->tree[a] = NULL; ++ } else { ++ DP("gc: %s: branch %u not empty", ++ set->name, a); ++ i = j = k = 0; ++ } ++ LOOP_WALK_END; ++ write_unlock_bh(&set->lock); ++ ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static inline void init_gc_timer(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ /* Even if there is no timeout for the entries, ++ * we still have to call gc because delete ++ * do not clean up empty branches */ ++ map->gc_interval = IPTREE_GC_TIME; ++ init_timer(&map->gc); ++ map->gc.data = (unsigned long) set; ++ map->gc.function = ip_tree_gc; ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iptree_create *req = ++ (struct ip_set_req_iptree_create *) data; ++ struct ip_set_iptree *map; ++ ++ if (size != sizeof(struct ip_set_req_iptree_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree_create), ++ size); ++ return -EINVAL; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iptree)); ++ return -ENOMEM; ++ } ++ memset(map, 0, sizeof(*map)); ++ map->timeout = req->timeout; ++ map->elements = 0; ++ set->data = map; ++ ++ init_gc_timer(set); ++ ++ return 0; ++} ++ ++static void __flush(struct ip_set_iptree *map) ++{ ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ kmem_cache_free(leaf_cachep, dtree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, ctree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, btree); ++ LOOP_WALK_END; ++ map->elements = 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ /* gc might be running */ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREE_DESTROY_SLEEP); ++ __flush(map); ++ kfree(map); ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ unsigned int timeout = map->timeout; ++ ++ /* gc might be running */ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREE_DESTROY_SLEEP); ++ __flush(map); ++ memset(map, 0, sizeof(*map)); ++ map->timeout = timeout; ++ ++ init_gc_timer(set); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree_create *header = ++ (struct ip_set_req_iptree_create *) data; ++ ++ header->timeout = map->timeout; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c,d; ++ unsigned int count = 0; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 256; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ count++; ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ ++ DP("members %u", count); ++ return (count * sizeof(struct ip_set_req_iptree)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c,d; ++ size_t offset = 0; ++ struct ip_set_req_iptree *entry; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 256; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) { ++ entry = (struct ip_set_req_iptree *)(data + offset); ++ entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); ++ entry->timeout = !map->timeout ? 0 ++ : (dtree->expires[d] - jiffies)/HZ; ++ offset += sizeof(struct ip_set_req_iptree); ++ } ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++} ++ ++static struct ip_set_type ip_set_iptree = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iptree), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iptree_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iptree type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_iptree_init(void) ++{ ++ int ret; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ branch_cachep = kmem_cache_create("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb), ++ 0, 0, NULL); ++#else ++ branch_cachep = kmem_cache_create("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb), ++ 0, 0, NULL, NULL); ++#endif ++ if (!branch_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ leaf_cachep = kmem_cache_create("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed), ++ 0, 0, NULL); ++#else ++ leaf_cachep = kmem_cache_create("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed), ++ 0, 0, NULL, NULL); ++#endif ++ if (!leaf_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); ++ ret = -ENOMEM; ++ goto free_branch; ++ } ++ ret = ip_set_register_set_type(&ip_set_iptree); ++ if (ret == 0) ++ goto out; ++ ++ kmem_cache_destroy(leaf_cachep); ++ free_branch: ++ kmem_cache_destroy(branch_cachep); ++ out: ++ return ret; ++} ++ ++static void __exit ip_set_iptree_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iptree); ++ kmem_cache_destroy(leaf_cachep); ++ kmem_cache_destroy(branch_cachep); ++} ++ ++module_init(ip_set_iptree_init); ++module_exit(ip_set_iptree_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_iptreemap.c +@@ -0,0 +1,829 @@ ++/* Copyright (C) 2007 Sven Wegener ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++/* This modules implements the iptreemap ipset type. It uses bitmaps to ++ * represent every single IPv4 address as a single bit. The bitmaps are managed ++ * in a tree structure, where the first three octets of an addresses are used ++ * as an index to find the bitmap and the last octet is used as the bit number. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define IPTREEMAP_DEFAULT_GC_TIME (5 * 60) ++#define IPTREEMAP_DESTROY_SLEEP (100) ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++static struct kmem_cache *cachep_b; ++static struct kmem_cache *cachep_c; ++static struct kmem_cache *cachep_d; ++#else ++static kmem_cache_t *cachep_b; ++static kmem_cache_t *cachep_c; ++static kmem_cache_t *cachep_d; ++#endif ++ ++static struct ip_set_iptreemap_d *fullbitmap_d; ++static struct ip_set_iptreemap_c *fullbitmap_c; ++static struct ip_set_iptreemap_b *fullbitmap_b; ++ ++#if defined(__LITTLE_ENDIAN) ++#define ABCD(a, b, c, d, addr) \ ++ do { \ ++ a = ((unsigned char *)addr)[3]; \ ++ b = ((unsigned char *)addr)[2]; \ ++ c = ((unsigned char *)addr)[1]; \ ++ d = ((unsigned char *)addr)[0]; \ ++ } while (0) ++#elif defined(__BIG_ENDIAN) ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[0]; \ ++ b = ((unsigned char *)addrp)[1]; \ ++ c = ((unsigned char *)addrp)[2]; \ ++ d = ((unsigned char *)addrp)[3]; \ ++} while (0) ++#else ++#error "Please fix asm/byteorder.h" ++#endif /* __LITTLE_ENDIAN */ ++ ++#define TESTIP_WALK(map, elem, branch, full) \ ++ do { \ ++ branch = (map)->tree[elem]; \ ++ if (!branch) \ ++ return 0; \ ++ else if (branch == full) \ ++ return 1; \ ++ } while (0) ++ ++#define ADDIP_WALK(map, elem, branch, type, cachep, full) \ ++ do { \ ++ branch = (map)->tree[elem]; \ ++ if (!branch) { \ ++ branch = (type *) kmem_cache_alloc(cachep, GFP_ATOMIC); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[elem] = branch; \ ++ } else if (branch == full) { \ ++ return -EEXIST; \ ++ } \ ++ } while (0) ++ ++#define ADDIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free) \ ++ for (a = a1; a <= a2; a++) { \ ++ branch = (map)->tree[a]; \ ++ if (branch != full) { \ ++ if ((a > a1 && a < a2) || (hint)) { \ ++ if (branch) \ ++ free(branch); \ ++ (map)->tree[a] = full; \ ++ continue; \ ++ } else if (!branch) { \ ++ branch = kmem_cache_alloc(cachep, GFP_ATOMIC); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[a] = branch; \ ++ } ++ ++#define ADDIP_RANGE_LOOP_END() \ ++ } \ ++ } ++ ++#define DELIP_WALK(map, elem, branch, cachep, full, flags) \ ++ do { \ ++ branch = (map)->tree[elem]; \ ++ if (!branch) { \ ++ return -EEXIST; \ ++ } else if (branch == full) { \ ++ branch = kmem_cache_alloc(cachep, flags); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memcpy(branch, full, sizeof(*full)); \ ++ (map)->tree[elem] = branch; \ ++ } \ ++ } while (0) ++ ++#define DELIP_RANGE_LOOP(map, a, a1, a2, hint, branch, full, cachep, free, flags) \ ++ for (a = a1; a <= a2; a++) { \ ++ branch = (map)->tree[a]; \ ++ if (branch) { \ ++ if ((a > a1 && a < a2) || (hint)) { \ ++ if (branch != full) \ ++ free(branch); \ ++ (map)->tree[a] = NULL; \ ++ continue; \ ++ } else if (branch == full) { \ ++ branch = kmem_cache_alloc(cachep, flags); \ ++ if (!branch) \ ++ return -ENOMEM; \ ++ memcpy(branch, full, sizeof(*branch)); \ ++ (map)->tree[a] = branch; \ ++ } ++ ++#define DELIP_RANGE_LOOP_END() \ ++ } \ ++ } ++ ++#define LOOP_WALK_BEGIN(map, i, branch) \ ++ for (i = 0; i < 256; i++) { \ ++ branch = (map)->tree[i]; \ ++ if (likely(!branch)) \ ++ continue; ++ ++#define LOOP_WALK_END() \ ++ } ++ ++#define LOOP_WALK_BEGIN_GC(map, i, branch, full, cachep, count) \ ++ count = -256; \ ++ for (i = 0; i < 256; i++) { \ ++ branch = (map)->tree[i]; \ ++ if (likely(!branch)) \ ++ continue; \ ++ count++; \ ++ if (branch == full) { \ ++ count++; \ ++ continue; \ ++ } ++ ++#define LOOP_WALK_END_GC(map, i, branch, full, cachep, count) \ ++ if (-256 == count) { \ ++ kmem_cache_free(cachep, branch); \ ++ (map)->tree[i] = NULL; \ ++ } else if (256 == count) { \ ++ kmem_cache_free(cachep, branch); \ ++ (map)->tree[i] = full; \ ++ } \ ++ } ++ ++#define LOOP_WALK_BEGIN_COUNT(map, i, branch, inrange, count) \ ++ for (i = 0; i < 256; i++) { \ ++ if (!(map)->tree[i]) { \ ++ if (inrange) { \ ++ count++; \ ++ inrange = 0; \ ++ } \ ++ continue; \ ++ } \ ++ branch = (map)->tree[i]; ++ ++#define LOOP_WALK_END_COUNT() \ ++ } ++ ++#define MIN(a, b) (a < b ? a : b) ++#define MAX(a, b) (a > b ? a : b) ++ ++#define GETVALUE1(a, a1, b1, r) \ ++ (a == a1 ? b1 : r) ++ ++#define GETVALUE2(a, b, a1, b1, c1, r) \ ++ (a == a1 && b == b1 ? c1 : r) ++ ++#define GETVALUE3(a, b, c, a1, b1, c1, d1, r) \ ++ (a == a1 && b == b1 && c == c1 ? d1 : r) ++ ++#define CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2) \ ++ ( \ ++ GETVALUE1(a, a1, b1, 0) == 0 \ ++ && GETVALUE1(a, a2, b2, 255) == 255 \ ++ && c1 == 0 \ ++ && c2 == 255 \ ++ && d1 == 0 \ ++ && d2 == 255 \ ++ ) ++ ++#define CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2) \ ++ ( \ ++ GETVALUE2(a, b, a1, b1, c1, 0) == 0 \ ++ && GETVALUE2(a, b, a2, b2, c2, 255) == 255 \ ++ && d1 == 0 \ ++ && d2 == 255 \ ++ ) ++ ++#define CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2) \ ++ ( \ ++ GETVALUE3(a, b, c, a1, b1, c1, d1, 0) == 0 \ ++ && GETVALUE3(a, b, c, a2, b2, c2, d2, 255) == 255 \ ++ ) ++ ++ ++static inline void ++free_d(struct ip_set_iptreemap_d *map) ++{ ++ kmem_cache_free(cachep_d, map); ++} ++ ++static inline void ++free_c(struct ip_set_iptreemap_c *map) ++{ ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int i; ++ ++ LOOP_WALK_BEGIN(map, i, dtree) { ++ if (dtree != fullbitmap_d) ++ free_d(dtree); ++ } LOOP_WALK_END(); ++ ++ kmem_cache_free(cachep_c, map); ++} ++ ++static inline void ++free_b(struct ip_set_iptreemap_b *map) ++{ ++ struct ip_set_iptreemap_c *ctree; ++ unsigned int i; ++ ++ LOOP_WALK_BEGIN(map, i, ctree) { ++ if (ctree != fullbitmap_c) ++ free_c(ctree); ++ } LOOP_WALK_END(); ++ ++ kmem_cache_free(cachep_b, map); ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned char a, b, c, d; ++ ++ *hash_ip = ip; ++ ++ ABCD(a, b, c, d, hash_ip); ++ ++ TESTIP_WALK(map, a, btree, fullbitmap_b); ++ TESTIP_WALK(btree, b, ctree, fullbitmap_c); ++ TESTIP_WALK(ctree, c, dtree, fullbitmap_d); ++ ++ return !!test_bit(d, (void *) dtree->bitmap); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); ++ return -EINVAL; ++ } ++ ++ return __testip(set, req->start, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) ++{ ++ int res; ++ ++ res = __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++ ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned char a, b, c, d; ++ ++ *hash_ip = ip; ++ ++ ABCD(a, b, c, d, hash_ip); ++ ++ ADDIP_WALK(map, a, btree, struct ip_set_iptreemap_b, cachep_b, fullbitmap_b); ++ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreemap_c, cachep_c, fullbitmap_c); ++ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreemap_d, cachep_d, fullbitmap_d); ++ ++ if (test_and_set_bit(d, (void *) dtree->bitmap)) ++ return -EEXIST; ++ ++ set_bit(b, (void *) btree->dirty); ++ ++ return 0; ++} ++ ++static inline int ++__addip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d; ++ unsigned char a1, b1, c1, d1; ++ unsigned char a2, b2, c2, d2; ++ ++ if (start == end) ++ return __addip_single(set, start, hash_ip); ++ ++ *hash_ip = start; ++ ++ ABCD(a1, b1, c1, d1, &start); ++ ABCD(a2, b2, c2, d2, &end); ++ ++ /* This is sooo ugly... */ ++ ADDIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b) { ++ ADDIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c) { ++ ADDIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d) { ++ for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) ++ set_bit(d, (void *) dtree->bitmap); ++ set_bit(b, (void *) btree->dirty); ++ } ADDIP_RANGE_LOOP_END(); ++ } ADDIP_RANGE_LOOP_END(); ++ } ADDIP_RANGE_LOOP_END(); ++ ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); ++ return -EINVAL; ++ } ++ ++ return __addip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) ++{ ++ ++ return __addip_single(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__delip_single(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip, unsigned int __nocast flags) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned char a,b,c,d; ++ ++ *hash_ip = ip; ++ ++ ABCD(a, b, c, d, hash_ip); ++ ++ DELIP_WALK(map, a, btree, cachep_b, fullbitmap_b, flags); ++ DELIP_WALK(btree, b, ctree, cachep_c, fullbitmap_c, flags); ++ DELIP_WALK(ctree, c, dtree, cachep_d, fullbitmap_d, flags); ++ ++ if (!test_and_clear_bit(d, (void *) dtree->bitmap)) ++ return -EEXIST; ++ ++ set_bit(b, (void *) btree->dirty); ++ ++ return 0; ++} ++ ++static inline int ++__delip_range(struct ip_set *set, ip_set_ip_t start, ip_set_ip_t end, ip_set_ip_t *hash_ip, unsigned int __nocast flags) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d; ++ unsigned char a1, b1, c1, d1; ++ unsigned char a2, b2, c2, d2; ++ ++ if (start == end) ++ return __delip_single(set, start, hash_ip, flags); ++ ++ *hash_ip = start; ++ ++ ABCD(a1, b1, c1, d1, &start); ++ ABCD(a2, b2, c2, d2, &end); ++ ++ /* This is sooo ugly... */ ++ DELIP_RANGE_LOOP(map, a, a1, a2, CHECK1(a, a1, a2, b1, b2, c1, c2, d1, d2), btree, fullbitmap_b, cachep_b, free_b, flags) { ++ DELIP_RANGE_LOOP(btree, b, GETVALUE1(a, a1, b1, 0), GETVALUE1(a, a2, b2, 255), CHECK2(a, b, a1, a2, b1, b2, c1, c2, d1, d2), ctree, fullbitmap_c, cachep_c, free_c, flags) { ++ DELIP_RANGE_LOOP(ctree, c, GETVALUE2(a, b, a1, b1, c1, 0), GETVALUE2(a, b, a2, b2, c2, 255), CHECK3(a, b, c, a1, a2, b1, b2, c1, c2, d1, d2), dtree, fullbitmap_d, cachep_d, free_d, flags) { ++ for (d = GETVALUE3(a, b, c, a1, b1, c1, d1, 0); d <= GETVALUE3(a, b, c, a2, b2, c2, d2, 255); d++) ++ clear_bit(d, (void *) dtree->bitmap); ++ set_bit(b, (void *) btree->dirty); ++ } DELIP_RANGE_LOOP_END(); ++ } DELIP_RANGE_LOOP_END(); ++ } DELIP_RANGE_LOOP_END(); ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptreemap *req = (struct ip_set_req_iptreemap *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap), size); ++ return -EINVAL; ++ } ++ ++ return __delip_range(set, MIN(req->start, req->end), MAX(req->start, req->end), hash_ip, GFP_KERNEL); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ip_set_ip_t *hash_ip, const u_int32_t *flags, unsigned char index) ++{ ++ return __delip_single(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip, ++ GFP_ATOMIC); ++} ++ ++/* Check the status of the bitmap ++ * -1 == all bits cleared ++ * 1 == all bits set ++ * 0 == anything else ++ */ ++static inline int ++bitmap_status(struct ip_set_iptreemap_d *dtree) ++{ ++ unsigned char first = dtree->bitmap[0]; ++ int a; ++ ++ for (a = 1; a < 32; a++) ++ if (dtree->bitmap[a] != first) ++ return 0; ++ ++ return (first == 0 ? -1 : (first == 255 ? 1 : 0)); ++} ++ ++static void ++gc(unsigned long addr) ++{ ++ struct ip_set *set = (struct ip_set *) addr; ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c; ++ int i, j, k; ++ ++ write_lock_bh(&set->lock); ++ ++ LOOP_WALK_BEGIN_GC(map, a, btree, fullbitmap_b, cachep_b, i) { ++ LOOP_WALK_BEGIN_GC(btree, b, ctree, fullbitmap_c, cachep_c, j) { ++ if (!test_and_clear_bit(b, (void *) btree->dirty)) ++ continue; ++ LOOP_WALK_BEGIN_GC(ctree, c, dtree, fullbitmap_d, cachep_d, k) { ++ switch (bitmap_status(dtree)) { ++ case -1: ++ kmem_cache_free(cachep_d, dtree); ++ ctree->tree[c] = NULL; ++ k--; ++ break; ++ case 1: ++ kmem_cache_free(cachep_d, dtree); ++ ctree->tree[c] = fullbitmap_d; ++ k++; ++ break; ++ } ++ } LOOP_WALK_END(); ++ } LOOP_WALK_END_GC(btree, b, ctree, fullbitmap_c, cachep_c, k); ++ } LOOP_WALK_END_GC(map, a, btree, fullbitmap_b, cachep_b, j); ++ ++ write_unlock_bh(&set->lock); ++ ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static inline void ++init_gc_timer(struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ ++ init_timer(&map->gc); ++ map->gc.data = (unsigned long) set; ++ map->gc.function = gc; ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iptreemap_create *req = (struct ip_set_req_iptreemap_create *) data; ++ struct ip_set_iptreemap *map; ++ ++ if (size != sizeof(struct ip_set_req_iptreemap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", sizeof(struct ip_set_req_iptreemap_create), size); ++ return -EINVAL; ++ } ++ ++ map = kzalloc(sizeof(*map), GFP_KERNEL); ++ if (!map) ++ return -ENOMEM; ++ ++ map->gc_interval = req->gc_interval ? req->gc_interval : IPTREEMAP_DEFAULT_GC_TIME; ++ set->data = map; ++ ++ init_gc_timer(set); ++ ++ return 0; ++} ++ ++static inline void __flush(struct ip_set_iptreemap *map) ++{ ++ struct ip_set_iptreemap_b *btree; ++ unsigned int a; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ if (btree != fullbitmap_b) ++ free_b(btree); ++ LOOP_WALK_END(); ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREEMAP_DESTROY_SLEEP); ++ ++ __flush(map); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREEMAP_DESTROY_SLEEP); ++ ++ __flush(map); ++ ++ memset(map, 0, sizeof(*map)); ++ ++ init_gc_timer(set); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_req_iptreemap_create *header = (struct ip_set_req_iptreemap_create *) data; ++ ++ header->gc_interval = map->gc_interval; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d, inrange = 0, count = 0; ++ ++ LOOP_WALK_BEGIN_COUNT(map, a, btree, inrange, count) { ++ LOOP_WALK_BEGIN_COUNT(btree, b, ctree, inrange, count) { ++ LOOP_WALK_BEGIN_COUNT(ctree, c, dtree, inrange, count) { ++ for (d = 0; d < 256; d++) { ++ if (test_bit(d, (void *) dtree->bitmap)) { ++ inrange = 1; ++ } else if (inrange) { ++ count++; ++ inrange = 0; ++ } ++ } ++ } LOOP_WALK_END_COUNT(); ++ } LOOP_WALK_END_COUNT(); ++ } LOOP_WALK_END_COUNT(); ++ ++ if (inrange) ++ count++; ++ ++ return (count * sizeof(struct ip_set_req_iptreemap)); ++} ++ ++static inline size_t add_member(void *data, size_t offset, ip_set_ip_t start, ip_set_ip_t end) ++{ ++ struct ip_set_req_iptreemap *entry = (struct ip_set_req_iptreemap *) (data + offset); ++ ++ entry->start = start; ++ entry->end = end; ++ ++ return sizeof(*entry); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptreemap *map = (struct ip_set_iptreemap *) set->data; ++ struct ip_set_iptreemap_b *btree; ++ struct ip_set_iptreemap_c *ctree; ++ struct ip_set_iptreemap_d *dtree; ++ unsigned int a, b, c, d, inrange = 0; ++ size_t offset = 0; ++ ip_set_ip_t start = 0, end = 0, ip; ++ ++ LOOP_WALK_BEGIN(map, a, btree) { ++ LOOP_WALK_BEGIN(btree, b, ctree) { ++ LOOP_WALK_BEGIN(ctree, c, dtree) { ++ for (d = 0; d < 256; d++) { ++ if (test_bit(d, (void *) dtree->bitmap)) { ++ ip = ((a << 24) | (b << 16) | (c << 8) | d); ++ if (!inrange) { ++ inrange = 1; ++ start = ip; ++ } else if (end < ip - 1) { ++ offset += add_member(data, offset, start, end); ++ start = ip; ++ } ++ end = ip; ++ } else if (inrange) { ++ offset += add_member(data, offset, start, end); ++ inrange = 0; ++ } ++ } ++ } LOOP_WALK_END(); ++ } LOOP_WALK_END(); ++ } LOOP_WALK_END(); ++ ++ if (inrange) ++ add_member(data, offset, start, end); ++} ++ ++static struct ip_set_type ip_set_iptreemap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = create, ++ .destroy = destroy, ++ .flush = flush, ++ .reqsize = sizeof(struct ip_set_req_iptreemap), ++ .addip = addip, ++ .addip_kernel = addip_kernel, ++ .delip = delip, ++ .delip_kernel = delip_kernel, ++ .testip = testip, ++ .testip_kernel = testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iptreemap_create), ++ .list_header = list_header, ++ .list_members_size = list_members_size, ++ .list_members = list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Sven Wegener "); ++MODULE_DESCRIPTION("iptreemap type of IP sets"); ++ ++static int __init ip_set_iptreemap_init(void) ++{ ++ int ret = -ENOMEM; ++ int a; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ cachep_b = kmem_cache_create("ip_set_iptreemap_b", ++ sizeof(struct ip_set_iptreemap_b), ++ 0, 0, NULL); ++#else ++ cachep_b = kmem_cache_create("ip_set_iptreemap_b", ++ sizeof(struct ip_set_iptreemap_b), ++ 0, 0, NULL, NULL); ++#endif ++ if (!cachep_b) { ++ ip_set_printk("Unable to create ip_set_iptreemap_b slab cache"); ++ goto out; ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ cachep_c = kmem_cache_create("ip_set_iptreemap_c", ++ sizeof(struct ip_set_iptreemap_c), ++ 0, 0, NULL); ++#else ++ cachep_c = kmem_cache_create("ip_set_iptreemap_c", ++ sizeof(struct ip_set_iptreemap_c), ++ 0, 0, NULL, NULL); ++#endif ++ if (!cachep_c) { ++ ip_set_printk("Unable to create ip_set_iptreemap_c slab cache"); ++ goto outb; ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ cachep_d = kmem_cache_create("ip_set_iptreemap_d", ++ sizeof(struct ip_set_iptreemap_d), ++ 0, 0, NULL); ++#else ++ cachep_d = kmem_cache_create("ip_set_iptreemap_d", ++ sizeof(struct ip_set_iptreemap_d), ++ 0, 0, NULL, NULL); ++#endif ++ if (!cachep_d) { ++ ip_set_printk("Unable to create ip_set_iptreemap_d slab cache"); ++ goto outc; ++ } ++ ++ fullbitmap_d = kmem_cache_alloc(cachep_d, GFP_KERNEL); ++ if (!fullbitmap_d) ++ goto outd; ++ ++ fullbitmap_c = kmem_cache_alloc(cachep_c, GFP_KERNEL); ++ if (!fullbitmap_c) ++ goto outbitmapd; ++ ++ fullbitmap_b = kmem_cache_alloc(cachep_b, GFP_KERNEL); ++ if (!fullbitmap_b) ++ goto outbitmapc; ++ ++ ret = ip_set_register_set_type(&ip_set_iptreemap); ++ if (0 > ret) ++ goto outbitmapb; ++ ++ /* Now init our global bitmaps */ ++ memset(fullbitmap_d->bitmap, 0xff, sizeof(fullbitmap_d->bitmap)); ++ ++ for (a = 0; a < 256; a++) ++ fullbitmap_c->tree[a] = fullbitmap_d; ++ ++ for (a = 0; a < 256; a++) ++ fullbitmap_b->tree[a] = fullbitmap_c; ++ memset(fullbitmap_b->dirty, 0, sizeof(fullbitmap_b->dirty)); ++ ++ return 0; ++ ++outbitmapb: ++ kmem_cache_free(cachep_b, fullbitmap_b); ++outbitmapc: ++ kmem_cache_free(cachep_c, fullbitmap_c); ++outbitmapd: ++ kmem_cache_free(cachep_d, fullbitmap_d); ++outd: ++ kmem_cache_destroy(cachep_d); ++outc: ++ kmem_cache_destroy(cachep_c); ++outb: ++ kmem_cache_destroy(cachep_b); ++out: ++ ++ return ret; ++} ++ ++static void __exit ip_set_iptreemap_fini(void) ++{ ++ ip_set_unregister_set_type(&ip_set_iptreemap); ++ kmem_cache_free(cachep_d, fullbitmap_d); ++ kmem_cache_free(cachep_c, fullbitmap_c); ++ kmem_cache_free(cachep_b, fullbitmap_b); ++ kmem_cache_destroy(cachep_d); ++ kmem_cache_destroy(cachep_c); ++ kmem_cache_destroy(cachep_b); ++} ++ ++module_init(ip_set_iptreemap_init); ++module_exit(ip_set_iptreemap_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_macipmap.c +@@ -0,0 +1,375 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the macipmap type */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = (struct ip_set_macip *) map->members; ++ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->ip < map->first_ip || req->ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = req->ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ (void *) &table[req->ip - map->first_ip].flags)) { ++ return (memcmp(req->ethernet, ++ &table[req->ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return 0; ++ ++ *hash_ip = ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ (void *) &table[ip - map->first_ip].flags)) { ++ /* Is mac pointer valid? ++ * If so, compare... */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ return (skb_mac_header(skb) >= skb->head ++ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data ++#else ++ return (skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data ++#endif ++ && (memcmp(eth_hdr(skb)->h_source, ++ &table[ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0)); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++/* returns 0 on success */ ++static inline int ++__addip(struct ip_set *set, ++ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (test_and_set_bit(IPSET_MACIP_ISSET, ++ (void *) &table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __addip(set, req->ip, req->ethernet, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (!(skb_mac_header(skb) >= skb->head ++ && (skb_mac_header(skb) + ETH_HLEN) <= skb->data)) ++#else ++ if (!(skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data)) ++#endif ++ return -EINVAL; ++ ++ return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (!test_and_clear_bit(IPSET_MACIP_ISSET, ++ (void *)&table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __delip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) ++{ ++ return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_macipmap_create *req = ++ (struct ip_set_req_macipmap_create *) data; ++ struct ip_set_macipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_macipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_macipmap)); ++ return -ENOMEM; ++ } ++ map->flags = req->flags; ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ newbytes = members_size(map->first_ip, map->last_ip); ++ map->members = ip_set_malloc(newbytes); ++ DP("members: %u %p", newbytes, map->members); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ memset(map->members, 0, members_size(map->first_ip, map->last_ip)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_req_macipmap_create *header = ++ (struct ip_set_req_macipmap_create *) data; ++ ++ DP("list_header %x %x %u", map->first_ip, map->last_ip, ++ map->flags); ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->flags = map->flags; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ DP("%u", members_size(map->first_ip, map->last_ip)); ++ return members_size(map->first_ip, map->last_ip); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ int bytes = members_size(map->first_ip, map->last_ip); ++ ++ DP("members: %u %p", bytes, map->members); ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_macipmap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_macipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_macipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("macipmap type of IP sets"); ++ ++static int __init ip_set_macipmap_init(void) ++{ ++ init_max_malloc_size(); ++ return ip_set_register_set_type(&ip_set_macipmap); ++} ++ ++static void __exit ip_set_macipmap_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_macipmap); ++} ++ ++module_init(ip_set_macipmap_init); ++module_exit(ip_set_macipmap_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_nethash.c +@@ -0,0 +1,497 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing a cidr nethash set */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static int limit = MAX_RANGE; ++ ++static inline __u32 ++jhash_ip(const struct ip_set_nethash *map, uint16_t i, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, *(((uint32_t *) map->initval) + i)); ++} ++ ++static inline __u32 ++hash_id_cidr(struct ip_set_nethash *map, ++ ip_set_ip_t ip, ++ unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ __u32 id; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ *hash_ip = pack(ip, cidr); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = jhash_ip(map, i, *hash_ip) % map->hashsize; ++ DP("hash key: %u", id); ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ if (*elem == *hash_ip) ++ return id; ++ } ++ return UINT_MAX; ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ __u32 id = UINT_MAX; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); ++ if (id != UINT_MAX) ++ break; ++ } ++ return id; ++} ++ ++static inline int ++__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (ip && hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (ip && hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) ++ : __testip_cidr(set, req->ip, req->cidr, hash_ip)); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ return __testip(set, ++ ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr), ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++#endif ++ hash_ip); ++} ++ ++static inline int ++__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ __u32 probe; ++ u_int16_t i; ++ ip_set_ip_t *elem; ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = jhash_ip(map, i, ip) % map->hashsize; ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, probe); ++ if (*elem == ip) ++ return -EEXIST; ++ if (!*elem) { ++ *elem = ip; ++ map->elements++; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ if (!ip || map->elements >= limit) ++ return -ERANGE; ++ ++ *hash_ip = pack(ip, cidr); ++ DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); ++ ++ return __addip_base(map, *hash_ip); ++} ++ ++static void ++update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) ++{ ++ unsigned char next; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ if (map->cidr[i] == cidr) { ++ return; ++ } else if (map->cidr[i] < cidr) { ++ next = map->cidr[i]; ++ map->cidr[i] = cidr; ++ cidr = next; ++ } ++ } ++ if (i < 30) ++ map->cidr[i] = cidr; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ int ret; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ ret = __addip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++ ++ if (ret == 0) ++ update_cidr_sizes((struct ip_set_nethash *) set->data, ++ req->cidr); ++ ++ return ret; ++} ++ ++static int ++addip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++ if (map->cidr[0]) ++ ret = __addip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ip_set_ip_t *elem; ++ void *members; ++ u_int32_t i, hashsize = map->hashsize; ++ int res; ++ struct ip_set_nethash *tmp; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new parameters */ ++ hashsize += (hashsize * map->resize)/100; ++ if (hashsize == map->hashsize) ++ hashsize++; ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, map->hashsize, hashsize); ++ ++ tmp = kmalloc(sizeof(struct ip_set_nethash) ++ + map->probes * sizeof(uint32_t), GFP_ATOMIC); ++ if (!tmp) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_nethash) ++ + map->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ tmp->members = harray_malloc(hashsize, sizeof(ip_set_ip_t), GFP_ATOMIC); ++ if (!tmp->members) { ++ DP("out of memory for %d bytes", hashsize * sizeof(ip_set_ip_t)); ++ kfree(tmp); ++ return -ENOMEM; ++ } ++ tmp->hashsize = hashsize; ++ tmp->elements = 0; ++ tmp->probes = map->probes; ++ tmp->resize = map->resize; ++ memcpy(tmp->initval, map->initval, map->probes * sizeof(uint32_t)); ++ memcpy(tmp->cidr, map->cidr, 30 * sizeof(unsigned char)); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_nethash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ if (*elem) ++ res = __addip_base(tmp, *elem); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ harray_free(tmp->members); ++ kfree(tmp); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ ++ map->hashsize = tmp->hashsize; ++ map->members = tmp->members; ++ write_unlock_bh(&set->lock); ++ ++ harray_free(members); ++ kfree(tmp); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ ip_set_ip_t id, *elem; ++ ++ if (!ip) ++ return -ERANGE; ++ ++ id = hash_id_cidr(map, ip, cidr, hash_ip); ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, id); ++ *elem = 0; ++ map->elements--; ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ /* TODO: no garbage collection in map->cidr */ ++ return __delip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_ip, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags[index] & IPSET_SRC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ ? ip_hdr(skb)->saddr ++ : ip_hdr(skb)->daddr); ++#else ++ ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++#endif ++ ++ if (map->cidr[0]) ++ ret = __delip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_nethash_create *req = ++ (struct ip_set_req_nethash_create *) data; ++ struct ip_set_nethash *map; ++ uint16_t i; ++ ++ if (size != sizeof(struct ip_set_req_nethash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ if (req->probes < 1) { ++ ip_set_printk("probes too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_nethash) ++ + req->probes * sizeof(uint32_t), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_nethash) ++ + req->probes * sizeof(uint32_t)); ++ return -ENOMEM; ++ } ++ for (i = 0; i < req->probes; i++) ++ get_random_bytes(((uint32_t *) map->initval)+i, 4); ++ map->elements = 0; ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++ map->members = harray_malloc(map->hashsize, sizeof(ip_set_ip_t), GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ return -ENOMEM; ++ } ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ harray_free(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ harray_flush(map->members, map->hashsize, sizeof(ip_set_ip_t)); ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++ map->elements = 0; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ struct ip_set_req_nethash_create *header = ++ (struct ip_set_req_nethash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ip_set_ip_t i, *elem; ++ ++ for (i = 0; i < map->hashsize; i++) { ++ elem = HARRAY_ELEM(map->members, ip_set_ip_t *, i); ++ ((ip_set_ip_t *)data)[i] = *elem; ++ } ++} ++ ++static struct ip_set_type ip_set_nethash = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_nethash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_nethash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("nethash type of IP sets"); ++module_param(limit, int, 0600); ++MODULE_PARM_DESC(limit, "maximal number of elements stored in the sets"); ++ ++static int __init ip_set_nethash_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_nethash); ++} ++ ++static void __exit ip_set_nethash_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_nethash); ++} ++ ++module_init(ip_set_nethash_init); ++module_exit(ip_set_nethash_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ip_set_portmap.c +@@ -0,0 +1,346 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing a port set type as a bitmap */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++/* We must handle non-linear skbs */ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, u_int32_t flags) ++{ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ struct iphdr *iph = ip_hdr(skb); ++#else ++ struct iphdr *iph = skb->nh.iph; ++#endif ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr tcph; ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &tcph, sizeof(tcph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ tcph.source : tcph.dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr udph; ++ ++ if (offset) ++ return INVALID_PORT; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) ++ if (skb_copy_bits(skb, ip_hdr(skb)->ihl*4, &udph, sizeof(udph)) < 0) ++#else ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) ++#endif ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ udph.source : udph.dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++ ++static inline int ++__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ ++ *hash_port = port; ++ DP("set: %s, port:%u, %u", set->name, port, *hash_port); ++ return !!test_bit(port - map->first_port, map->members); ++} ++ ++static int ++testport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __testport(set, req->port, hash_port); ++} ++ ++static int ++testport_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_port, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ int res; ++ ip_set_ip_t port = get_port(skb, flags[index]); ++ ++ DP("flag %s port %u", flags[index] & IPSET_SRC ? "SRC" : "DST", port); ++ if (port == INVALID_PORT) ++ return 0; ++ ++ res = __testport(set, port, hash_port); ++ ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (test_and_set_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++addport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __addport(set, req->port, hash_port); ++} ++ ++static int ++addport_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_port, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port = get_port(skb, flags[index]); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __addport(set, port, hash_port); ++} ++ ++static inline int ++__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (!test_and_clear_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++delport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __delport(set, req->port, hash_port); ++} ++ ++static int ++delport_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ ip_set_ip_t *hash_port, ++ const u_int32_t *flags, ++ unsigned char index) ++{ ++ ip_set_ip_t port = get_port(skb, flags[index]); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __delport(set, port, hash_port); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_portmap_create *req = ++ (struct ip_set_req_portmap_create *) data; ++ struct ip_set_portmap *map; ++ ++ if (size != sizeof(struct ip_set_req_portmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u to %u", req->from, req->to); ++ ++ if (req->from > req->to) { ++ DP("bad port range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d ports)", ++ MAX_RANGE+1); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_portmap)); ++ return -ENOMEM; ++ } ++ map->first_port = req->from; ++ map->last_port = req->to; ++ newbytes = bitmap_bytes(req->from, req->to); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ struct ip_set_req_portmap_create *header = ++ (struct ip_set_req_portmap_create *) data; ++ ++ DP("list_header %u %u", map->first_port, map->last_port); ++ ++ header->from = map->first_port; ++ header->to = map->last_port; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ return bitmap_bytes(map->first_port, map->last_port); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ int bytes = bitmap_bytes(map->first_port, map->last_port); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_portmap = { ++ .typename = SETTYPE_NAME, ++ .features = IPSET_TYPE_PORT | IPSET_DATA_SINGLE, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_portmap), ++ .addip = &addport, ++ .addip_kernel = &addport_kernel, ++ .delip = &delport, ++ .delip_kernel = &delport_kernel, ++ .testip = &testport, ++ .testip_kernel = &testport_kernel, ++ .header_size = sizeof(struct ip_set_req_portmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("portmap type of IP sets"); ++ ++static int __init ip_set_portmap_init(void) ++{ ++ return ip_set_register_set_type(&ip_set_portmap); ++} ++ ++static void __exit ip_set_portmap_fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_portmap); ++} ++ ++module_init(ip_set_portmap_init); ++module_exit(ip_set_portmap_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_set.c +@@ -0,0 +1,160 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module to match an IP set. */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static inline int ++match_set(const struct ipt_set_info *info, ++ const struct sk_buff *skb, ++ int inv) ++{ ++ if (ip_set_testip_kernel(info->index, skb, info->flags)) ++ inv = !inv; ++ return inv; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++static bool ++#else ++static int ++#endif ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_match *match, ++#endif ++ const void *matchinfo, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++ int offset, unsigned int protoff, bool *hotdrop) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ int offset, unsigned int protoff, int *hotdrop) ++#else ++ int offset, int *hotdrop) ++#endif ++{ ++ const struct ipt_set_info_match *info = matchinfo; ++ ++ return match_set(&info->match_set, ++ skb, ++ info->match_set.flags[0] & IPSET_MATCH_INV); ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++bool ++#else ++static int ++#endif ++checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *inf, ++#else ++ const struct ipt_ip *ip, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_match *match, ++#endif ++ void *matchinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int matchsize, ++#endif ++ unsigned int hook_mask) ++{ ++ struct ipt_set_info_match *info = ++ (struct ipt_set_info_match *) matchinfo; ++ ip_set_id_t index; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return 0; ++ } ++#endif ++ ++ index = ip_set_get_byindex(info->match_set.index); ++ ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("Cannot find set indentified by id %u to match", ++ info->match_set.index); ++ return 0; /* error */ ++ } ++ if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy( ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_match *match, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ void *matchinfo, unsigned int matchsize) ++#else ++ void *matchinfo) ++#endif ++{ ++ struct ipt_set_info_match *info = matchinfo; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return; ++ } ++#endif ++ ip_set_put(info->match_set.index); ++} ++ ++static struct ipt_match set_match = { ++ .name = "set", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++ .family = AF_INET, ++#endif ++ .match = &match, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .matchsize = sizeof(struct ipt_set_info_match), ++#endif ++ .checkentry = &checkentry, ++ .destroy = &destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iptables IP set match module"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++#define ipt_register_match xt_register_match ++#define ipt_unregister_match xt_unregister_match ++#endif ++ ++static int __init ipt_ipset_init(void) ++{ ++ return ipt_register_match(&set_match); ++} ++ ++static void __exit ipt_ipset_fini(void) ++{ ++ ipt_unregister_match(&set_match); ++} ++ ++module_init(ipt_ipset_init); ++module_exit(ipt_ipset_fini); +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_SET.c +@@ -0,0 +1,172 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson ++ * Patrick Schaaf ++ * Martin Josefsson ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ipt_SET.c - netfilter target to manipulate IP sets */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int ++target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ const void *targinfo, ++ void *userinfo) ++#else ++ const void *targinfo) ++#endif ++{ ++ const struct ipt_set_info_target *info = targinfo; ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_addip_kernel(info->add_set.index, ++ *pskb, ++ info->add_set.flags); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_delip_kernel(info->del_set.index, ++ *pskb, ++ info->del_set.flags); ++ ++ return IPT_CONTINUE; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) ++static bool ++#else ++static int ++#endif ++checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *e, ++#else ++ const struct ipt_entry *e, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++ void *targinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int targinfosize, ++#endif ++ unsigned int hook_mask) ++{ ++ struct ipt_set_info_target *info = ++ (struct ipt_set_info_target *) targinfo; ++ ip_set_id_t index; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targinfosize != IPT_ALIGN(sizeof(*info))) { ++ DP("bad target info size %u", targinfosize); ++ return 0; ++ } ++#endif ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->add_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find add_set index %u as target", ++ info->add_set.index); ++ return 0; /* error */ ++ } ++ } ++ ++ if (info->del_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->del_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find del_set index %u as target", ++ info->del_set.index); ++ return 0; /* error */ ++ } ++ } ++ if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 ++ || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy( ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ void *targetinfo, unsigned int targetsize) ++#else ++ void *targetinfo) ++#endif ++{ ++ struct ipt_set_info_target *info = targetinfo; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { ++ ip_set_printk("invalid targetsize %d", targetsize); ++ return; ++ } ++#endif ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->add_set.index); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->del_set.index); ++} ++ ++static struct ipt_target SET_target = { ++ .name = "SET", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++ .family = AF_INET, ++#endif ++ .target = target, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .targetsize = sizeof(struct ipt_set_info_target), ++#endif ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik "); ++MODULE_DESCRIPTION("iptables IP set target module"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++#define ipt_register_target xt_register_target ++#define ipt_unregister_target xt_unregister_target ++#endif ++ ++static int __init ipt_SET_init(void) ++{ ++ return ipt_register_target(&SET_target); ++} ++ ++static void __exit ipt_SET_fini(void) ++{ ++ ipt_unregister_target(&SET_target); ++} ++ ++module_init(ipt_SET_init); ++module_exit(ipt_SET_fini); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -408,5 +408,122 @@ + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + ++config IP_NF_SET ++ tristate "IP set support" ++ depends on INET && NETFILTER ++ help ++ This option adds IP set support to the kernel. ++ In order to define and use sets, you need the userspace utility ++ ipset(8). ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_MAX ++ int "Maximum number of IP sets" ++ default 256 ++ range 2 65534 ++ depends on IP_NF_SET ++ help ++ You can define here default value of the maximum number ++ of IP sets for the kernel. ++ ++ The value can be overriden by the 'max_sets' module ++ parameter of the 'ip_set' module. ++ ++config IP_NF_SET_HASHSIZE ++ int "Hash size for bindings of IP sets" ++ default 1024 ++ depends on IP_NF_SET ++ help ++ You can define here default value of the hash size for ++ bindings of IP sets. ++ ++ The value can be overriden by the 'hash_size' module ++ parameter of the 'ip_set' module. ++ ++config IP_NF_SET_IPMAP ++ tristate "ipmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the ipmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_MACIPMAP ++ tristate "macipmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the macipmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_PORTMAP ++ tristate "portmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the portmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPHASH ++ tristate "iphash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iphash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_NETHASH ++ tristate "nethash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the nethash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPPORTHASH ++ tristate "ipporthash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the ipporthash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPTREE ++ tristate "iptree set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iptree set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPTREEMAP ++ tristate "iptreemap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iptreemap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_MATCH_SET ++ tristate "set match support" ++ depends on IP_NF_SET ++ help ++ Set matching matches against given IP sets. ++ You need the ipset utility to create and set up the sets. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_TARGET_SET ++ tristate "SET target support" ++ depends on IP_NF_SET ++ help ++ The SET target makes possible to add/delete entries ++ in IP sets. ++ You need the ipset utility to create and set up the sets. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++ + endmenu + +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -48,6 +48,7 @@ + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o ++obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o + obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o + + obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o +@@ -64,6 +65,18 @@ + obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o + obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o + obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o ++obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o ++ ++# sets ++obj-$(CONFIG_IP_NF_SET) += ip_set.o ++obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o ++obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o ++obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o ++obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o ++obj-$(CONFIG_IP_NF_SET_NETHASH) += ip_set_nethash.o ++obj-$(CONFIG_IP_NF_SET_IPPORTHASH) += ip_set_ipporthash.o ++obj-$(CONFIG_IP_NF_SET_IPTREE) += ip_set_iptree.o ++obj-$(CONFIG_IP_NF_SET_IPTREEMAP) += ip_set_iptreemap.o + + # generic ARP tables + obj-$(CONFIG_IP_NF_ARPTABLES) += arp_tables.o diff --git a/target/linux/generic-2.6/patches-2.6.23/140-netfilter_time.patch b/target/linux/generic-2.6/patches-2.6.23/140-netfilter_time.patch new file mode 100644 index 0000000000..033b2414dc --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/140-netfilter_time.patch @@ -0,0 +1,239 @@ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_time.h +@@ -0,0 +1,18 @@ ++#ifndef __ipt_time_h_included__ ++#define __ipt_time_h_included__ ++ ++ ++struct ipt_time_info { ++ u_int8_t days_match; /* 1 bit per day. -SMTWTFS */ ++ u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */ ++ u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */ ++ ++ /* FIXME: Keep this one for userspace iptables binary compability: */ ++ u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */ ++ ++ time_t date_start; ++ time_t date_stop; ++}; ++ ++ ++#endif /* __ipt_time_h_included__ */ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_time.c +@@ -0,0 +1,180 @@ ++/* ++ This is a module which is used for time matching ++ It is using some modified code from dietlibc (localtime() function) ++ that you can find at http://www.fefe.de/dietlibc/ ++ This file is distributed under the terms of the GNU General Public ++ License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL ++ 2001-05-04 Fabrice MARIE : initial development. ++ 2001-21-05 Fabrice MARIE : bug fix in the match code, ++ thanks to "Zeng Yu" for bug report. ++ 2001-26-09 Fabrice MARIE : force the match to be in LOCAL_IN or PRE_ROUTING only. ++ 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, ++ added Nguyen Dang Phuoc Dong patch to support timezones. ++ 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Fabrice MARIE "); ++MODULE_DESCRIPTION("Match arrival timestamp/date"); ++MODULE_LICENSE("GPL"); ++ ++struct tm ++{ ++ int tm_sec; /* Seconds. [0-60] (1 leap second) */ ++ int tm_min; /* Minutes. [0-59] */ ++ int tm_hour; /* Hours. [0-23] */ ++ int tm_mday; /* Day. [1-31] */ ++ int tm_mon; /* Month. [0-11] */ ++ int tm_year; /* Year - 1900. */ ++ int tm_wday; /* Day of week. [0-6] */ ++ int tm_yday; /* Days in year.[0-365] */ ++ int tm_isdst; /* DST. [-1/0/1]*/ ++ ++ long int tm_gmtoff; /* we don't care, we count from GMT */ ++ const char *tm_zone; /* we don't care, we count from GMT */ ++}; ++ ++void ++localtime(const u32 time, struct tm *r); ++ ++static bool ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const struct xt_match *match, ++ const void *matchinfo, ++ int offset, ++ unsigned int protoff, ++ bool *hotdrop) ++{ ++ const struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ struct timeval tv; ++ struct tm currenttime; /* time human readable */ ++ u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; ++ u_int16_t packet_time; ++ ++ /* We might not have a timestamp, get one */ ++ if (skb->tstamp.tv64 == 0) ++ __net_timestamp((struct sk_buff *)skb); ++ ++ skb_get_timestamp(skb, &tv); ++ /* First we make sure we are in the date start-stop boundaries */ ++ if ((tv.tv_sec < info->date_start) || (tv.tv_sec > info->date_stop)) ++ return 0; /* We are outside the date boundaries */ ++ ++ /* Transform the timestamp of the packet, in a human readable form */ ++ localtime(tv.tv_sec, ¤ttime); ++ ++ /* check if we match this timestamp, we start by the days... */ ++ if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) ++ return 0; /* the day doesn't match */ ++ ++ /* ... check the time now */ ++ packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; ++ if ((packet_time < info->time_start) || (packet_time > info->time_stop)) ++ return 0; ++ ++ /* here we match ! */ ++ return 1; ++} ++ ++static bool ++checkentry(const char *tablename, ++ const void *ip, ++ const struct xt_match *match, ++ void *matchinfo, ++ unsigned int hook_mask) ++{ ++ struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ ++ /* First, check that we are in the correct hooks */ ++ if (hook_mask ++ & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) ++ { ++ printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); ++ return 0; ++ } ++ ++ /* Now check the coherence of the data ... */ ++ if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ ++ (info->time_stop > 1439)) ++ { ++ printk(KERN_WARNING "ipt_time: invalid argument\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_match time_match = { ++ .name = "time", ++ .match = &match, ++ .matchsize = sizeof(struct ipt_time_info), ++ .checkentry = &checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ printk("ipt_time loading\n"); ++ return xt_register_match(&time_match); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_match(&time_match); ++ printk("ipt_time unloaded\n"); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ ++/* The part below is borowed and modified from dietlibc */ ++ ++/* seconds per day */ ++#define SPD 24*60*60 ++ ++void ++localtime(const u32 time, struct tm *r) { ++ u32 i, timep; ++ extern struct timezone sys_tz; ++ const unsigned int __spm[12] = ++ { 0, ++ (31), ++ (31+28), ++ (31+28+31), ++ (31+28+31+30), ++ (31+28+31+30+31), ++ (31+28+31+30+31+30), ++ (31+28+31+30+31+30+31), ++ (31+28+31+30+31+30+31+31), ++ (31+28+31+30+31+30+31+31+30), ++ (31+28+31+30+31+30+31+31+30+31), ++ (31+28+31+30+31+30+31+31+30+31+30), ++ }; ++ register u32 work; ++ ++ timep = time - (sys_tz.tz_minuteswest * 60); ++ work=timep%(SPD); ++ r->tm_sec=work%60; work/=60; ++ r->tm_min=work%60; r->tm_hour=work/60; ++ work=timep/(SPD); ++ r->tm_wday=(4+work)%7; ++ for (i=1970; ; ++i) { ++ register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; ++ if (work>k) ++ work-=k; ++ else ++ break; ++ } ++ r->tm_year=i-1900; ++ for (i=11; i && __spm[i]>work; --i) ; ++ r->tm_mon=i; ++ r->tm_mday=work-__spm[i]+1; ++} +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -78,6 +78,22 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++ ++config IP_NF_MATCH_TIME ++ tristate 'TIME match support' ++ depends on IP_NF_IPTABLES ++ help ++ This option adds a `time' match, which allows you ++ to match based on the packet arrival time/date ++ (arrival time/date at the machine which netfilter is running on) or ++ departure time/date (for locally generated packets). ++ ++ If you say Y here, try iptables -m time --help for more information. ++ If you want to compile it as a module, say M here and read ++ ++ Documentation/modules.txt. If unsure, say `N'. ++ ++ + config IP_NF_MATCH_RECENT + tristate "recent match support" + depends on IP_NF_IPTABLES +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -44,6 +44,7 @@ + obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o + obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o + obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o ++obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o + obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o diff --git a/target/linux/generic-2.6/patches-2.6.23/150-netfilter_imq.patch b/target/linux/generic-2.6/patches-2.6.23/150-netfilter_imq.patch new file mode 100644 index 0000000000..ad149cfcf0 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/150-netfilter_imq.patch @@ -0,0 +1,849 @@ +--- /dev/null ++++ b/drivers/net/imq.c +@@ -0,0 +1,400 @@ ++/* ++ * Pseudo-driver for the intermediate queue device. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Patrick McHardy, ++ * ++ * The first version was written by Martin Devera, ++ * ++ * Credits: Jan Rafaj ++ * - Update patch to 2.4.21 ++ * Sebastian Strollo ++ * - Fix "Dead-loop on netdevice imq"-issue ++ * Marcel Sebek ++ * - Update to 2.6.2-rc1 ++ * ++ * After some time of inactivity there is a group taking care ++ * of IMQ again: http://www.linuximq.net ++ * ++ * ++ * 2004/06/30 - New version of IMQ patch to kernels <=2.6.7 including ++ * the following changes: ++ * ++ * - Correction of ipv6 support "+"s issue (Hasso Tepper) ++ * - Correction of imq_init_devs() issue that resulted in ++ * kernel OOPS unloading IMQ as module (Norbert Buchmuller) ++ * - Addition of functionality to choose number of IMQ devices ++ * during kernel config (Andre Correa) ++ * - Addition of functionality to choose how IMQ hooks on ++ * PRE and POSTROUTING (after or before NAT) (Andre Correa) ++ * - Cosmetic corrections (Norbert Buchmuller) (Andre Correa) ++ * ++ * ++ * 2005/12/16 - IMQ versions between 2.6.7 and 2.6.13 were ++ * released with almost no problems. 2.6.14-x was released ++ * with some important changes: nfcache was removed; After ++ * some weeks of trouble we figured out that some IMQ fields ++ * in skb were missing in skbuff.c - skb_clone and copy_skb_header. ++ * These functions are correctly patched by this new patch version. ++ * ++ * Thanks for all who helped to figure out all the problems with ++ * 2.6.14.x: Patrick McHardy, Rune Kock, VeNoMouS, Max CtRiX, ++ * Kevin Shanahan, Richard Lucassen, Valery Dachev (hopefully ++ * I didn't forget anybody). I apologize again for my lack of time. ++ * ++ * More info at: http://www.linuximq.net/ (Andre Correa) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ #include ++#endif ++#include ++#include ++ ++extern int qdisc_restart1(struct net_device *dev); ++ ++static nf_hookfn imq_nf_hook; ++ ++static struct nf_hook_ops imq_ingress_ipv4 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_PRE_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ .priority = NF_IP_PRI_MANGLE + 1 ++#else ++ .priority = NF_IP_PRI_NAT_DST + 1 ++#endif ++}; ++ ++static struct nf_hook_ops imq_egress_ipv4 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_POST_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) ++ .priority = NF_IP_PRI_LAST ++#else ++ .priority = NF_IP_PRI_NAT_SRC - 1 ++#endif ++}; ++ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++static struct nf_hook_ops imq_ingress_ipv6 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET6, ++ .hooknum = NF_IP6_PRE_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ .priority = NF_IP6_PRI_MANGLE + 1 ++#else ++ .priority = NF_IP6_PRI_NAT_DST + 1 ++#endif ++}; ++ ++static struct nf_hook_ops imq_egress_ipv6 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET6, ++ .hooknum = NF_IP6_POST_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) ++ .priority = NF_IP6_PRI_LAST ++#else ++ .priority = NF_IP6_PRI_NAT_SRC - 1 ++#endif ++}; ++#endif ++ ++#if defined(CONFIG_IMQ_NUM_DEVS) ++static unsigned int numdevs = CONFIG_IMQ_NUM_DEVS; ++#else ++static unsigned int numdevs = 16; ++#endif ++ ++static struct net_device *imq_devs; ++ ++static struct net_device_stats *imq_get_stats(struct net_device *dev) ++{ ++ return (struct net_device_stats *)dev->priv; ++} ++ ++/* called for packets kfree'd in qdiscs at places other than enqueue */ ++static void imq_skb_destructor(struct sk_buff *skb) ++{ ++ struct nf_info *info = skb->nf_info; ++ ++ if (info) { ++ if (info->indev) ++ dev_put(info->indev); ++ if (info->outdev) ++ dev_put(info->outdev); ++ kfree(info); ++ } ++} ++ ++static int imq_dev_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct net_device_stats *stats = (struct net_device_stats*) dev->priv; ++ ++ stats->tx_bytes += skb->len; ++ stats->tx_packets++; ++ ++ skb->imq_flags = 0; ++ skb->destructor = NULL; ++ ++ dev->trans_start = jiffies; ++ nf_reinject(skb, skb->nf_info, NF_ACCEPT); ++ return 0; ++} ++ ++static int imq_nf_queue(struct sk_buff *skb, struct nf_info *info, unsigned queue_num, void *data) ++{ ++ struct net_device *dev; ++ struct net_device_stats *stats; ++ struct sk_buff *skb2 = NULL; ++ struct Qdisc *q; ++ unsigned int index = skb->imq_flags&IMQ_F_IFMASK; ++ int ret = -1; ++ ++ if (index > numdevs) ++ return -1; ++ ++ dev = imq_devs + index; ++ if (!(dev->flags & IFF_UP)) { ++ skb->imq_flags = 0; ++ nf_reinject(skb, info, NF_ACCEPT); ++ return 0; ++ } ++ dev->last_rx = jiffies; ++ ++ if (skb->destructor) { ++ skb2 = skb; ++ skb = skb_clone(skb, GFP_ATOMIC); ++ if (!skb) ++ return -1; ++ } ++ skb->nf_info = info; ++ ++ stats = (struct net_device_stats *)dev->priv; ++ stats->rx_bytes+= skb->len; ++ stats->rx_packets++; ++ ++ spin_lock_bh(&dev->queue_lock); ++ q = dev->qdisc; ++ if (q->enqueue) { ++ q->enqueue(skb_get(skb), q); ++ if (skb_shared(skb)) { ++ skb->destructor = imq_skb_destructor; ++ kfree_skb(skb); ++ ret = 0; ++ } ++ } ++ if (spin_is_locked(&dev->_xmit_lock)) ++ netif_schedule(dev); ++ else ++ while (!netif_queue_stopped(dev) && qdisc_restart1(dev) < 0) ++ /* NOTHING */; ++ ++ spin_unlock_bh(&dev->queue_lock); ++ ++ if (skb2) ++ kfree_skb(ret ? skb : skb2); ++ ++ return ret; ++} ++ ++static struct nf_queue_handler nfqh = { ++ .name = "imq", ++ .outfn = imq_nf_queue, ++}; ++ ++static unsigned int imq_nf_hook(unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *indev, ++ const struct net_device *outdev, ++ int (*okfn)(struct sk_buff *)) ++{ ++ if ((*pskb)->imq_flags & IMQ_F_ENQUEUE) ++ return NF_QUEUE; ++ ++ return NF_ACCEPT; ++} ++ ++ ++static int __init imq_init_hooks(void) ++{ ++ int err; ++ ++ err = nf_register_queue_handler(PF_INET, &nfqh); ++ if (err > 0) ++ goto err1; ++ if ((err = nf_register_hook(&imq_ingress_ipv4))) ++ goto err2; ++ if ((err = nf_register_hook(&imq_egress_ipv4))) ++ goto err3; ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ if ((err = nf_register_queue_handler(PF_INET6, &nfqh))) ++ goto err4; ++ if ((err = nf_register_hook(&imq_ingress_ipv6))) ++ goto err5; ++ if ((err = nf_register_hook(&imq_egress_ipv6))) ++ goto err6; ++#endif ++ ++ return 0; ++ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++err6: ++ nf_unregister_hook(&imq_ingress_ipv6); ++err5: ++ nf_unregister_queue_handler(PF_INET6, &nfqh); ++err4: ++ nf_unregister_hook(&imq_egress_ipv4); ++#endif ++err3: ++ nf_unregister_hook(&imq_ingress_ipv4); ++err2: ++ nf_unregister_queue_handler(PF_INET, &nfqh); ++err1: ++ return err; ++} ++ ++static void __exit imq_unhook(void) ++{ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ nf_unregister_hook(&imq_ingress_ipv6); ++ nf_unregister_hook(&imq_egress_ipv6); ++ nf_unregister_queue_handler(PF_INET6, &nfqh); ++#endif ++ nf_unregister_hook(&imq_ingress_ipv4); ++ nf_unregister_hook(&imq_egress_ipv4); ++ nf_unregister_queue_handler(PF_INET, &nfqh); ++} ++ ++static int __init imq_dev_init(struct net_device *dev) ++{ ++ dev->hard_start_xmit = imq_dev_xmit; ++ dev->type = ARPHRD_VOID; ++ dev->mtu = 16000; ++ dev->tx_queue_len = 11000; ++ dev->flags = IFF_NOARP; ++ dev->priv = kzalloc(sizeof(struct net_device_stats), GFP_KERNEL); ++ if (dev->priv == NULL) ++ return -ENOMEM; ++ dev->get_stats = imq_get_stats; ++ ++ return 0; ++} ++ ++static void imq_dev_uninit(struct net_device *dev) ++{ ++ kfree(dev->priv); ++} ++ ++static int __init imq_init_devs(void) ++{ ++ struct net_device *dev; ++ int i,j; ++ j = numdevs; ++ ++ if (!numdevs || numdevs > IMQ_MAX_DEVS) { ++ printk(KERN_ERR "IMQ: numdevs has to be betweed 1 and %u\n", ++ IMQ_MAX_DEVS); ++ return -EINVAL; ++ } ++ ++ imq_devs = kzalloc(sizeof(struct net_device) * numdevs, GFP_KERNEL); ++ if (!imq_devs) ++ return -ENOMEM; ++ ++ /* we start counting at zero */ ++ numdevs--; ++ ++ for (i = 0, dev = imq_devs; i <= numdevs; i++, dev++) { ++ SET_MODULE_OWNER(dev); ++ strcpy(dev->name, "imq%d"); ++ dev->init = imq_dev_init; ++ dev->uninit = imq_dev_uninit; ++ ++ if (register_netdev(dev) < 0) ++ goto err_register; ++ } ++ printk(KERN_INFO "IMQ starting with %u devices...\n", j); ++ return 0; ++ ++err_register: ++ for (; i; i--) ++ unregister_netdev(--dev); ++ kfree(imq_devs); ++ return -EIO; ++} ++ ++static void imq_cleanup_devs(void) ++{ ++ int i; ++ struct net_device *dev = imq_devs; ++ ++ for (i = 0; i <= numdevs; i++) ++ unregister_netdev(dev++); ++ ++ kfree(imq_devs); ++} ++ ++static int __init imq_init_module(void) ++{ ++ int err; ++ ++ if ((err = imq_init_devs())) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_devs()\n"); ++ return err; ++ } ++ if ((err = imq_init_hooks())) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_hooks()\n"); ++ imq_cleanup_devs(); ++ return err; ++ } ++ ++ printk(KERN_INFO "IMQ driver loaded successfully.\n"); ++ ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ printk(KERN_INFO "\tHooking IMQ before NAT on PREROUTING.\n"); ++#else ++ printk(KERN_INFO "\tHooking IMQ after NAT on PREROUTING.\n"); ++#endif ++#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ printk(KERN_INFO "\tHooking IMQ before NAT on POSTROUTING.\n"); ++#else ++ printk(KERN_INFO "\tHooking IMQ after NAT on POSTROUTING.\n"); ++#endif ++ ++ return 0; ++} ++ ++static void __exit imq_cleanup_module(void) ++{ ++ imq_unhook(); ++ imq_cleanup_devs(); ++ printk(KERN_INFO "IMQ driver unloaded successfully.\n"); ++} ++ ++ ++module_init(imq_init_module); ++module_exit(imq_cleanup_module); ++ ++module_param(numdevs, int, 16); ++MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)"); ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +--- a/drivers/net/Kconfig ++++ b/drivers/net/Kconfig +@@ -112,6 +112,129 @@ + To compile this driver as a module, choose M here: the module + will be called eql. If unsure, say N. + ++config IMQ ++ tristate "IMQ (intermediate queueing device) support" ++ depends on NETDEVICES && NETFILTER ++ ---help--- ++ The IMQ device(s) is used as placeholder for QoS queueing ++ disciplines. Every packet entering/leaving the IP stack can be ++ directed through the IMQ device where it's enqueued/dequeued to the ++ attached qdisc. This allows you to treat network devices as classes ++ and distribute bandwidth among them. Iptables is used to specify ++ through which IMQ device, if any, packets travel. ++ ++ More information at: http://www.linuximq.net/ ++ ++ To compile this driver as a module, choose M here: the module ++ will be called imq. If unsure, say N. ++ ++choice ++ prompt "IMQ behavior (PRE/POSTROUTING)" ++ depends on IMQ ++ default IMQ_BEHAVIOR_AB ++ help ++ ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ IMQ can work in any of the following ways: ++ ++ PREROUTING | POSTROUTING ++ -----------------|------------------- ++ #1 After NAT | After NAT ++ #2 After NAT | Before NAT ++ #3 Before NAT | After NAT ++ #4 Before NAT | Before NAT ++ ++ The default behavior is to hook before NAT on PREROUTING ++ and after NAT on POSTROUTING (#3). ++ ++ This settings are specially usefull when trying to use IMQ ++ to shape NATed clients. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AA ++ bool "IMQ AA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AB ++ bool "IMQ AB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BA ++ bool "IMQ BA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BB ++ bool "IMQ BB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++endchoice ++ ++config IMQ_NUM_DEVS ++ ++ int "Number of IMQ devices" ++ range 2 16 ++ depends on IMQ ++ default "16" ++ help ++ ++ This settings defines how many IMQ devices will be ++ created. ++ ++ The default value is 16. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ + config TUN + tristate "Universal TUN/TAP device driver support" + select CRC32 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -131,6 +131,7 @@ + obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o + + obj-$(CONFIG_DUMMY) += dummy.o ++obj-$(CONFIG_IMQ) += imq.o + obj-$(CONFIG_IFB) += ifb.o + obj-$(CONFIG_MACVLAN) += macvlan.o + obj-$(CONFIG_DE600) += de600.o +--- /dev/null ++++ b/include/linux/imq.h +@@ -0,0 +1,9 @@ ++#ifndef _IMQ_H ++#define _IMQ_H ++ ++#define IMQ_MAX_DEVS 16 ++ ++#define IMQ_F_IFMASK 0x7f ++#define IMQ_F_ENQUEUE 0x80 ++ ++#endif /* _IMQ_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_IMQ.h +@@ -0,0 +1,8 @@ ++#ifndef _IPT_IMQ_H ++#define _IPT_IMQ_H ++ ++struct ipt_imq_info { ++ unsigned int todev; /* target imq device */ ++}; ++ ++#endif /* _IPT_IMQ_H */ +--- /dev/null ++++ b/include/linux/netfilter_ipv6/ip6t_IMQ.h +@@ -0,0 +1,8 @@ ++#ifndef _IP6T_IMQ_H ++#define _IP6T_IMQ_H ++ ++struct ip6t_imq_info { ++ unsigned int todev; /* target imq device */ ++}; ++ ++#endif /* _IP6T_IMQ_H */ +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -295,6 +295,10 @@ + struct nf_conntrack *nfct; + struct sk_buff *nfct_reasm; + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ unsigned char imq_flags; ++ struct nf_info *nf_info; ++#endif + #ifdef CONFIG_BRIDGE_NETFILTER + struct nf_bridge_info *nf_bridge; + #endif +@@ -1725,6 +1729,10 @@ + dst->nfct_reasm = src->nfct_reasm; + nf_conntrack_get_reasm(src->nfct_reasm); + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ dst->imq_flags = src->imq_flags; ++ dst->nf_info = src->nf_info; ++#endif + #ifdef CONFIG_BRIDGE_NETFILTER + dst->nf_bridge = src->nf_bridge; + nf_bridge_get(src->nf_bridge); +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -94,6 +94,9 @@ + #include + #include + #include ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++#include ++#endif + #include + #include + #include +@@ -1462,7 +1465,11 @@ + int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) + { + if (likely(!skb->next)) { +- if (!list_empty(&ptype_all)) ++ if (!list_empty(&ptype_all) ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ && !(skb->imq_flags & IMQ_F_ENQUEUE) ++#endif ++ ) + dev_queue_xmit_nit(skb, dev); + + if (netif_needs_gso(dev, skb)) { +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_IMQ.c +@@ -0,0 +1,69 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int imq_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ struct ipt_imq_info *mr = (struct ipt_imq_info*)targinfo; ++ ++ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; ++ ++ return XT_CONTINUE; ++} ++ ++static bool imq_checkentry(const char *tablename, ++ const void *e, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int hook_mask) ++{ ++ struct ipt_imq_info *mr; ++ ++ mr = (struct ipt_imq_info*)targinfo; ++ ++ if (mr->todev > IMQ_MAX_DEVS) { ++ printk(KERN_WARNING ++ "IMQ: invalid device specified, highest is %u\n", ++ IMQ_MAX_DEVS); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct xt_target ipt_imq_reg = { ++ .name = "IMQ", ++ .family = AF_INET, ++ .target = imq_target, ++ .targetsize = sizeof(struct ipt_imq_info), ++ .checkentry = imq_checkentry, ++ .me = THIS_MODULE, ++ .table = "mangle" ++}; ++ ++static int __init init(void) ++{ ++ return xt_register_target(&ipt_imq_reg); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ipt_imq_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -333,6 +333,17 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_TARGET_IMQ ++ tristate "IMQ target support" ++ depends on IP_NF_MANGLE ++ help ++ This option adds a `IMQ' target which is used to specify if and ++ to which IMQ device packets should get enqueued/dequeued. ++ ++ For more information visit: http://www.linuximq.net/ ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP_NF_TARGET_TOS + tristate "TOS target support" + depends on IP_NF_MANGLE +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -58,6 +58,7 @@ + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o + obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o + obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o ++obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o +--- /dev/null ++++ b/net/ipv6/netfilter/ip6t_IMQ.c +@@ -0,0 +1,69 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++static unsigned int imq_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ struct ip6t_imq_info *mr = (struct ip6t_imq_info*)targinfo; ++ ++ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; ++ ++ return XT_CONTINUE; ++} ++ ++static bool imq_checkentry(const char *tablename, ++ const void *entry, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int hook_mask) ++{ ++ struct ip6t_imq_info *mr; ++ ++ mr = (struct ip6t_imq_info*)targinfo; ++ ++ if (mr->todev > IMQ_MAX_DEVS) { ++ printk(KERN_WARNING ++ "IMQ: invalid device specified, highest is %u\n", ++ IMQ_MAX_DEVS); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct xt_target ip6t_imq_reg = { ++ .name = "IMQ", ++ .family = AF_INET6, ++ .target = imq_target, ++ .targetsize = sizeof(struct ip6t_imq_info), ++ .table = "mangle", ++ .checkentry = imq_checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ return xt_register_target(&ip6t_imq_reg); ++} ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ip6t_imq_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -173,6 +173,15 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP6_NF_TARGET_IMQ ++ tristate "IMQ target support" ++ depends on IP6_NF_MANGLE ++ help ++ This option adds a `IMQ' target which is used to specify if and ++ to which imq device packets should get enqueued/dequeued. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP6_NF_TARGET_HL + tristate 'HL (hoplimit) target support' + depends on IP6_NF_MANGLE +--- a/net/ipv6/netfilter/Makefile ++++ b/net/ipv6/netfilter/Makefile +@@ -13,6 +13,7 @@ + obj-$(CONFIG_IP6_NF_MATCH_OWNER) += ip6t_owner.o + obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o + obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o ++obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o + obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o + obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o + obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o +--- a/net/sched/sch_generic.c ++++ b/net/sched/sch_generic.c +@@ -191,6 +191,11 @@ + return ret; + } + ++int qdisc_restart1(struct net_device *dev) ++{ ++ return qdisc_restart(dev); ++} ++ + void __qdisc_run(struct net_device *dev) + { + do { +@@ -620,3 +625,4 @@ + EXPORT_SYMBOL(qdisc_reset); + EXPORT_SYMBOL(qdisc_lock_tree); + EXPORT_SYMBOL(qdisc_unlock_tree); ++EXPORT_SYMBOL(qdisc_restart1); diff --git a/target/linux/generic-2.6/patches-2.6.23/160-netfilter_route.patch b/target/linux/generic-2.6/patches-2.6.23/160-netfilter_route.patch new file mode 100644 index 0000000000..7441b27d6a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/160-netfilter_route.patch @@ -0,0 +1,928 @@ +--- /dev/null ++++ b/include/linux/netfilter_ipv4/ipt_ROUTE.h +@@ -0,0 +1,23 @@ ++/* Header file for iptables ipt_ROUTE target ++ * ++ * (C) 2002 by Cédric de Launois ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++#ifndef _IPT_ROUTE_H_target ++#define _IPT_ROUTE_H_target ++ ++#define IPT_ROUTE_IFNAMSIZ 16 ++ ++struct ipt_route_target_info { ++ char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */ ++ char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */ ++ u_int32_t gw; /* IP address of gateway */ ++ u_int8_t flags; ++}; ++ ++/* Values for "flags" field */ ++#define IPT_ROUTE_CONTINUE 0x01 ++#define IPT_ROUTE_TEE 0x02 ++ ++#endif /*_IPT_ROUTE_H_target*/ +--- /dev/null ++++ b/include/linux/netfilter_ipv6/ip6t_ROUTE.h +@@ -0,0 +1,23 @@ ++/* Header file for iptables ip6t_ROUTE target ++ * ++ * (C) 2003 by Cédric de Launois ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++#ifndef _IPT_ROUTE_H_target ++#define _IPT_ROUTE_H_target ++ ++#define IP6T_ROUTE_IFNAMSIZ 16 ++ ++struct ip6t_route_target_info { ++ char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */ ++ char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */ ++ u_int32_t gw[4]; /* IPv6 address of gateway */ ++ u_int8_t flags; ++}; ++ ++/* Values for "flags" field */ ++#define IP6T_ROUTE_CONTINUE 0x01 ++#define IP6T_ROUTE_TEE 0x02 ++ ++#endif /*_IP6T_ROUTE_H_target*/ +--- /dev/null ++++ b/net/ipv4/netfilter/ipt_ROUTE.c +@@ -0,0 +1,463 @@ ++/* ++ * This implements the ROUTE target, which enables you to setup unusual ++ * routes not supported by the standard kernel routing table. ++ * ++ * Copyright (C) 2002 Cedric de Launois ++ * ++ * v 1.11 2004/11/23 ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cedric de Launois "); ++MODULE_DESCRIPTION("iptables ROUTE target module"); ++ ++/* Try to route the packet according to the routing keys specified in ++ * route_info. Keys are : ++ * - ifindex : ++ * 0 if no oif preferred, ++ * otherwise set to the index of the desired oif ++ * - route_info->gw : ++ * 0 if no gateway specified, ++ * otherwise set to the next host to which the pkt must be routed ++ * If success, skb->dev is the output device to which the packet must ++ * be sent and skb->dst is not NULL ++ * ++ * RETURN: -1 if an error occured ++ * 1 if the packet was succesfully routed to the ++ * destination desired ++ * 0 if the kernel routing table could not route the packet ++ * according to the keys specified ++ */ ++static int route(struct sk_buff *skb, ++ unsigned int ifindex, ++ const struct ipt_route_target_info *route_info) ++{ ++ int err; ++ struct rtable *rt; ++ struct iphdr *iph = ip_hdr(skb); ++ struct flowi fl = { ++ .oif = ifindex, ++ .nl_u = { ++ .ip4_u = { ++ .daddr = iph->daddr, ++ .saddr = 0, ++ .tos = RT_TOS(iph->tos), ++ .scope = RT_SCOPE_UNIVERSE, ++ } ++ } ++ }; ++ ++ /* The destination address may be overloaded by the target */ ++ if (route_info->gw) ++ fl.fl4_dst = route_info->gw; ++ ++ /* Trying to route the packet using the standard routing table. */ ++ if ((err = ip_route_output_key(&rt, &fl))) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err); ++ return -1; ++ } ++ ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++ /* Success if no oif specified or if the oif correspond to the ++ * one desired */ ++ if (!ifindex || rt->u.dst.dev->ifindex == ifindex) { ++ skb->dst = &rt->u.dst; ++ skb->dev = skb->dst->dev; ++ skb->protocol = htons(ETH_P_IP); ++ return 1; ++ } ++ ++ /* The interface selected by the routing table is not the one ++ * specified by the user. This may happen because the dst address ++ * is one of our own addresses. ++ */ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", ++ NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex); ++ ++ return 0; ++} ++ ++ ++/* Stolen from ip_finish_output2 ++ * PRE : skb->dev is set to the device we are leaving by ++ * skb->dst is not NULL ++ * POST: the packet is sent with the link layer header pushed ++ * the packet is destroyed ++ */ ++static void ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ struct hh_cache *hh = dst->hh; ++ struct net_device *dev = dst->dev; ++ int hh_len = LL_RESERVED_SPACE(dev); ++ ++ /* Be paranoid, rather than too clever. */ ++ if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); ++ if (skb2 == NULL) { ++ kfree_skb(skb); ++ return; ++ } ++ if (skb->sk) ++ skb_set_owner_w(skb2, skb->sk); ++ kfree_skb(skb); ++ skb = skb2; ++ } ++ ++ if (hh) { ++ int hh_alen; ++ ++ read_lock_bh(&hh->hh_lock); ++ hh_alen = HH_DATA_ALIGN(hh->hh_len); ++ memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); ++ read_unlock_bh(&hh->hh_lock); ++ skb_push(skb, hh->hh_len); ++ hh->hh_output(skb); ++ } else if (dst->neighbour) ++ dst->neighbour->output(skb); ++ else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n"); ++ kfree_skb(skb); ++ } ++} ++ ++ ++/* PRE : skb->dev is set to the device we are leaving by ++ * POST: - the packet is directly sent to the skb->dev device, without ++ * pushing the link layer header. ++ * - the packet is destroyed ++ */ ++static inline int dev_direct_send(struct sk_buff *skb) ++{ ++ return dev_queue_xmit(skb); ++} ++ ++ ++static unsigned int route_oif(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ unsigned int ifindex = 0; ++ struct net_device *dev_out = NULL; ++ ++ /* The user set the interface name to use. ++ * Getting the current interface index. ++ */ ++ if ((dev_out = dev_get_by_name(route_info->oif))) { ++ ifindex = dev_out->ifindex; ++ } else { ++ /* Unknown interface name : packet dropped */ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif); ++ return NF_DROP; ++ } ++ ++ /* Trying the standard way of routing packets */ ++ switch (route(skb, ifindex, route_info)) { ++ case 1: ++ dev_put(dev_out); ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return IPT_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ ++ case 0: ++ /* Failed to send to oif. Trying the hard way */ ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return NF_DROP; ++ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: forcing the use of %i\n", ++ ifindex); ++ ++ /* We have to force the use of an interface. ++ * This interface must be a tunnel interface since ++ * otherwise we can't guess the hw address for ++ * the packet. For a tunnel interface, no hw address ++ * is needed. ++ */ ++ if ((dev_out->type != ARPHRD_TUNNEL) ++ && (dev_out->type != ARPHRD_IPGRE)) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: can't guess the hw addr !\n"); ++ dev_put(dev_out); ++ return NF_DROP; ++ } ++ ++ /* Send the packet. This will also free skb ++ * Do not go through the POST_ROUTING hook because ++ * skb->dst is not set and because it will probably ++ * get confused by the destination IP address. ++ */ ++ skb->dev = dev_out; ++ dev_direct_send(skb); ++ dev_put(dev_out); ++ return NF_STOLEN; ++ ++ default: ++ /* Unexpected error */ ++ dev_put(dev_out); ++ return NF_DROP; ++ } ++} ++ ++ ++static unsigned int route_iif(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ struct net_device *dev_in = NULL; ++ ++ /* Getting the current interface index. */ ++ if (!(dev_in = dev_get_by_name(route_info->iif))) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->iif); ++ return NF_DROP; ++ } ++ ++ skb->dev = dev_in; ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++ netif_rx(skb); ++ dev_put(dev_in); ++ return NF_STOLEN; ++} ++ ++ ++static unsigned int route_gw(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ if (route(skb, 0, route_info)!=1) ++ return NF_DROP; ++ ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return IPT_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++} ++ ++ ++/* To detect and deter routed packet loopback when using the --tee option, ++ * we take a page out of the raw.patch book: on the copied skb, we set up ++ * a fake ->nfct entry, pointing to the local &route_tee_track. We skip ++ * routing packets when we see they already have that ->nfct. ++ */ ++ ++static struct nf_conn route_tee_track; ++ ++static unsigned int ipt_route_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ const void *targinfo, ++ void *userinfo) ++#else ++ const void *targinfo) ++#endif ++{ ++ const struct ipt_route_target_info *route_info = targinfo; ++ struct sk_buff *skb = *pskb; ++ unsigned int res; ++ ++ if (skb->nfct == &route_tee_track.ct_general) { ++ /* Loopback - a packet we already routed, is to be ++ * routed another time. Avoid that, now. ++ */ ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: loopback - DROP!\n"); ++ return NF_DROP; ++ } ++ ++ /* If we are at PREROUTING or INPUT hook ++ * the TTL isn't decreased by the IP stack ++ */ ++ if (hooknum == NF_IP_PRE_ROUTING || ++ hooknum == NF_IP_LOCAL_IN) { ++ ++ struct iphdr *iph = ip_hdr(skb); ++ ++ if (iph->ttl <= 1) { ++ struct rtable *rt; ++ struct flowi fl = { ++ .oif = 0, ++ .nl_u = { ++ .ip4_u = { ++ .daddr = iph->daddr, ++ .saddr = iph->saddr, ++ .tos = RT_TOS(iph->tos), ++ .scope = ((iph->tos & RTO_ONLINK) ? ++ RT_SCOPE_LINK : ++ RT_SCOPE_UNIVERSE) ++ } ++ } ++ }; ++ ++ if (ip_route_output_key(&rt, &fl)) { ++ return NF_DROP; ++ } ++ ++ if (skb->dev == rt->u.dst.dev) { ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = &rt->u.dst; ++ ++ /* this will traverse normal stack, and ++ * thus call conntrack on the icmp packet */ ++ icmp_send(skb, ICMP_TIME_EXCEEDED, ++ ICMP_EXC_TTL, 0); ++ } ++ ++ return NF_DROP; ++ } ++ ++ /* ++ * If we are at INPUT the checksum must be recalculated since ++ * the length could change as the result of a defragmentation. ++ */ ++ if(hooknum == NF_IP_LOCAL_IN) { ++ iph->ttl = iph->ttl - 1; ++ iph->check = 0; ++ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); ++ } else { ++ ip_decrease_ttl(iph); ++ } ++ } ++ ++ if ((route_info->flags & IPT_ROUTE_TEE)) { ++ /* ++ * Copy the *pskb, and route the copy. Will later return ++ * IPT_CONTINUE for the original skb, which should continue ++ * on its way as if nothing happened. The copy should be ++ * independantly delivered to the ROUTE --gw. ++ */ ++ skb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: copy failed!\n"); ++ return IPT_CONTINUE; ++ } ++ } ++ ++ /* Tell conntrack to forget this packet since it may get confused ++ * when a packet is leaving with dst address == our address. ++ * Good idea ? Dunno. Need advice. ++ * ++ * NEW: mark the skb with our &route_tee_track, so we avoid looping ++ * on any already routed packet. ++ */ ++ if (!(route_info->flags & IPT_ROUTE_CONTINUE)) { ++ nf_conntrack_put(skb->nfct); ++ skb->nfct = &route_tee_track.ct_general; ++ skb->nfctinfo = IP_CT_NEW; ++ nf_conntrack_get(skb->nfct); ++ } ++ ++ if (route_info->oif[0] != '\0') { ++ res = route_oif(route_info, skb); ++ } else if (route_info->iif[0] != '\0') { ++ res = route_iif(route_info, skb); ++ } else if (route_info->gw) { ++ res = route_gw(route_info, skb); ++ } else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n"); ++ res = IPT_CONTINUE; ++ } ++ ++ if ((route_info->flags & IPT_ROUTE_TEE)) ++ res = IPT_CONTINUE; ++ ++ return res; ++} ++ ++ ++static bool ipt_route_checkentry(const char *tablename, ++ const void *e, ++ const struct xt_target *target, ++ void *targinfo, ++ unsigned int hook_mask) ++{ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n", ++ tablename); ++ return 0; ++ } ++ ++ if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING) ++ | (1 << NF_IP_LOCAL_IN) ++ | (1 << NF_IP_FORWARD) ++ | (1 << NF_IP_LOCAL_OUT) ++ | (1 << NF_IP_POST_ROUTING))) { ++ printk("ipt_ROUTE: bad hook\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static struct ipt_target ipt_route_reg = { ++ .name = "ROUTE", ++ .target = ipt_route_target, ++ .targetsize = sizeof(struct ipt_route_target_info), ++ .checkentry = ipt_route_checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ /* Set up fake conntrack (stolen from raw.patch): ++ - to never be deleted, not in any hashes */ ++ atomic_set(&route_tee_track.ct_general.use, 1); ++ /* - and look it like as a confirmed connection */ ++ set_bit(IPS_CONFIRMED_BIT, &route_tee_track.status); ++ /* Initialize fake conntrack so that NAT will skip it */ ++ route_tee_track.status |= IPS_NAT_DONE_MASK; ++ ++ return xt_register_target(&ipt_route_reg); ++} ++ ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ipt_route_reg); ++} ++ ++module_init(init); ++module_exit(fini); +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -552,5 +552,22 @@ + To compile it as a module, choose M here. If unsure, say N. + + ++config IP_NF_TARGET_ROUTE ++ tristate 'ROUTE target support' ++ depends on IP_NF_MANGLE ++ help ++ This option adds a `ROUTE' target, which enables you to setup unusual ++ routes. For example, the ROUTE lets you route a received packet through ++ an interface or towards a host, even if the regular destination of the ++ packet is the router itself. The ROUTE target is also able to change the ++ incoming interface of a packet. ++ ++ The target can be or not a final target. It has to be used inside the ++ mangle table. ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. The module will be called ipt_ROUTE.o. ++ If unsure, say `N'. ++ + endmenu + +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -61,6 +61,7 @@ + obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o ++obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o + obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o + obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o +--- a/net/ipv6/ndisc.c ++++ b/net/ipv6/ndisc.c +@@ -154,6 +154,8 @@ + .gc_thresh3 = 1024, + }; + ++EXPORT_SYMBOL(nd_tbl); ++ + /* ND options */ + struct ndisc_options { + struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX]; +--- /dev/null ++++ b/net/ipv6/netfilter/ip6t_ROUTE.c +@@ -0,0 +1,330 @@ ++/* ++ * This implements the ROUTE v6 target, which enables you to setup unusual ++ * routes not supported by the standard kernel routing table. ++ * ++ * Copyright (C) 2003 Cedric de Launois ++ * ++ * v 1.1 2004/11/23 ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if 1 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define NIP6(addr) \ ++ ntohs((addr).s6_addr16[0]), \ ++ ntohs((addr).s6_addr16[1]), \ ++ ntohs((addr).s6_addr16[2]), \ ++ ntohs((addr).s6_addr16[3]), \ ++ ntohs((addr).s6_addr16[4]), \ ++ ntohs((addr).s6_addr16[5]), \ ++ ntohs((addr).s6_addr16[6]), \ ++ ntohs((addr).s6_addr16[7]) ++ ++/* Route the packet according to the routing keys specified in ++ * route_info. Keys are : ++ * - ifindex : ++ * 0 if no oif preferred, ++ * otherwise set to the index of the desired oif ++ * - route_info->gw : ++ * 0 if no gateway specified, ++ * otherwise set to the next host to which the pkt must be routed ++ * If success, skb->dev is the output device to which the packet must ++ * be sent and skb->dst is not NULL ++ * ++ * RETURN: 1 if the packet was succesfully routed to the ++ * destination desired ++ * 0 if the kernel routing table could not route the packet ++ * according to the keys specified ++ */ ++static int ++route6(struct sk_buff *skb, ++ unsigned int ifindex, ++ const struct ip6t_route_target_info *route_info) ++{ ++ struct rt6_info *rt = NULL; ++ struct ipv6hdr *ipv6h = ipv6_hdr(skb); ++ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; ++ ++ DEBUGP("ip6t_ROUTE: called with: "); ++ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); ++ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); ++ DEBUGP("OUT=%s\n", route_info->oif); ++ ++ if (ipv6_addr_any(gw)) ++ rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); ++ else ++ rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); ++ ++ if (!rt) ++ goto no_route; ++ ++ DEBUGP("ip6t_ROUTE: routing gives: "); ++ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); ++ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); ++ DEBUGP("OUT=%s\n", rt->rt6i_dev->name); ++ ++ if (ifindex && rt->rt6i_dev->ifindex!=ifindex) ++ goto wrong_route; ++ ++ if (!rt->rt6i_nexthop) { ++ DEBUGP("ip6t_ROUTE: discovering neighbour\n"); ++ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); ++ } ++ ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = &rt->u.dst; ++ skb->dev = rt->rt6i_dev; ++ return 1; ++ ++ wrong_route: ++ dst_release(&rt->u.dst); ++ no_route: ++ if (!net_ratelimit()) ++ return 0; ++ ++ printk("ip6t_ROUTE: no explicit route found "); ++ if (ifindex) ++ printk("via interface %s ", route_info->oif); ++ if (!ipv6_addr_any(gw)) ++ printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); ++ printk("\n"); ++ return 0; ++} ++ ++ ++/* Stolen from ip6_output_finish ++ * PRE : skb->dev is set to the device we are leaving by ++ * skb->dst is not NULL ++ * POST: the packet is sent with the link layer header pushed ++ * the packet is destroyed ++ */ ++static void ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ struct hh_cache *hh = dst->hh; ++ ++ if (hh) { ++ read_lock_bh(&hh->hh_lock); ++ memcpy(skb->data - 16, hh->hh_data, 16); ++ read_unlock_bh(&hh->hh_lock); ++ skb_push(skb, hh->hh_len); ++ hh->hh_output(skb); ++ } else if (dst->neighbour) ++ dst->neighbour->output(skb); ++ else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); ++ kfree_skb(skb); ++ } ++} ++ ++ ++static unsigned int ++route6_oif(const struct ip6t_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ unsigned int ifindex = 0; ++ struct net_device *dev_out = NULL; ++ ++ /* The user set the interface name to use. ++ * Getting the current interface index. ++ */ ++ if ((dev_out = dev_get_by_name(route_info->oif))) { ++ ifindex = dev_out->ifindex; ++ } else { ++ /* Unknown interface name : packet dropped */ ++ if (net_ratelimit()) ++ DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); ++ ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ else ++ return NF_DROP; ++ } ++ ++ /* Trying the standard way of routing packets */ ++ if (route6(skb, ifindex, route_info)) { ++ dev_put(dev_out); ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ } else ++ return NF_DROP; ++} ++ ++ ++static unsigned int ++route6_gw(const struct ip6t_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ if (route6(skb, 0, route_info)) { ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ } else ++ return NF_DROP; ++} ++ ++ ++static unsigned int ++ip6t_route_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ const void *targinfo, ++ void *userinfo) ++#else ++ const void *targinfo) ++#endif ++{ ++ const struct ip6t_route_target_info *route_info = targinfo; ++ struct sk_buff *skb = *pskb; ++ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; ++ unsigned int res; ++ ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ goto do_it; ++ ++ /* If we are at PREROUTING or INPUT hook ++ * the TTL isn't decreased by the IP stack ++ */ ++ if (hooknum == NF_IP6_PRE_ROUTING || ++ hooknum == NF_IP6_LOCAL_IN) { ++ ++ struct ipv6hdr *ipv6h = ipv6_hdr(skb); ++ ++ if (ipv6h->hop_limit <= 1) { ++ /* Force OUTPUT device used as source address */ ++ skb->dev = skb->dst->dev; ++ ++ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ++ ICMPV6_EXC_HOPLIMIT, 0, skb->dev); ++ ++ return NF_DROP; ++ } ++ ++ ipv6h->hop_limit--; ++ } ++ ++ if ((route_info->flags & IP6T_ROUTE_TEE)) { ++ /* ++ * Copy the *pskb, and route the copy. Will later return ++ * IP6T_CONTINUE for the original skb, which should continue ++ * on its way as if nothing happened. The copy should be ++ * independantly delivered to the ROUTE --gw. ++ */ ++ skb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); ++ return IP6T_CONTINUE; ++ } ++ } ++ ++do_it: ++ if (route_info->oif[0]) { ++ res = route6_oif(route_info, skb); ++ } else if (!ipv6_addr_any(gw)) { ++ res = route6_gw(route_info, skb); ++ } else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); ++ res = IP6T_CONTINUE; ++ } ++ ++ if ((route_info->flags & IP6T_ROUTE_TEE)) ++ res = IP6T_CONTINUE; ++ ++ return res; ++} ++ ++ ++static int ++ip6t_route_checkentry(const char *tablename, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ const void *entry, ++#else ++ const struct ip6t_entry *entry ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ const struct xt_target *target, ++#endif ++ void *targinfo, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ unsigned int targinfosize, ++#endif ++ unsigned int hook_mask) ++{ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); ++ return 0; ++ } ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { ++ printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", ++ targinfosize, ++ IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); ++ return 0; ++ } ++#endif ++ ++ return 1; ++} ++ ++ ++static struct ip6t_target ip6t_route_reg = { ++ .name = "ROUTE", ++ .target = ip6t_route_target, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) ++ .targetsize = sizeof(struct ip6t_route_target_info), ++#endif ++ .checkentry = ip6t_route_checkentry, ++ .me = THIS_MODULE ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); ++ if (xt_register_target(&ip6t_route_reg)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++ ++static void __exit fini(void) ++{ ++ xt_unregister_target(&ip6t_route_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++MODULE_LICENSE("GPL"); +--- a/net/ipv6/netfilter/Kconfig ++++ b/net/ipv6/netfilter/Kconfig +@@ -209,5 +209,18 @@ + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + ++config IP6_NF_TARGET_ROUTE ++ tristate 'ROUTE target support' ++ depends on IP6_NF_MANGLE ++ help ++ This option adds a `ROUTE' target, which enables you to setup unusual ++ routes. The ROUTE target is also able to change the incoming interface ++ of a packet. ++ ++ The target can be or not a final target. It has to be used inside the ++ mangle table. ++ ++ Not working as a module. ++ + endmenu + +--- a/net/ipv6/netfilter/Makefile ++++ b/net/ipv6/netfilter/Makefile +@@ -20,6 +20,7 @@ + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o + obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o + obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o ++obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o + obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o + + # objects for l3 independent conntrack diff --git a/target/linux/generic-2.6/patches-2.6.23/170-netfilter_chaostables_0.8.patch b/target/linux/generic-2.6/patches-2.6.23/170-netfilter_chaostables_0.8.patch new file mode 100644 index 0000000000..32cb145f42 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/170-netfilter_chaostables_0.8.patch @@ -0,0 +1,844 @@ +--- /dev/null ++++ b/include/linux/netfilter/oot_conntrack.h +@@ -0,0 +1,5 @@ ++#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) ++# include ++#else /* linux-2.6.20+ */ ++# include ++#endif +--- /dev/null ++++ b/include/linux/netfilter/oot_trans.h +@@ -0,0 +1,14 @@ ++/* Out of tree workarounds */ ++#include ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++# define HAVE_MATCHINFOSIZE 1 ++# define HAVE_TARGUSERINFO 1 ++# define HAVE_TARGINFOSIZE 1 ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) ++# define nfmark mark ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) ++# define tcp_v4_check(tcph, tcph_sz, s, d, csp) \ ++ tcp_v4_check((tcph_sz), (s), (d), (csp)) ++#endif +--- /dev/null ++++ b/include/linux/netfilter/xt_CHAOS.h +@@ -0,0 +1,14 @@ ++#ifndef _LINUX_NETFILTER_XT_CHAOS_H ++#define _LINUX_NETFILTER_XT_CHAOS_H 1 ++ ++enum xt_chaos_target_variant { ++ XTCHAOS_NORMAL, ++ XTCHAOS_TARPIT, ++ XTCHAOS_DELUDE, ++}; ++ ++struct xt_chaos_target_info { ++ uint8_t variant; ++}; ++ ++#endif /* _LINUX_NETFILTER_XT_CHAOS_H */ +--- /dev/null ++++ b/include/linux/netfilter/xt_portscan.h +@@ -0,0 +1,8 @@ ++#ifndef _LINUX_NETFILTER_XT_PORTSCAN_H ++#define _LINUX_NETFILTER_XT_PORTSCAN_H 1 ++ ++struct xt_portscan_match_info { ++ uint8_t match_stealth, match_syn, match_cn, match_gr; ++}; ++ ++#endif /* _LINUX_NETFILTER_XT_PORTSCAN_H */ +--- /dev/null ++++ b/net/netfilter/find_match.c +@@ -0,0 +1,39 @@ ++/* ++ xt_request_find_match ++ by Jan Engelhardt , 2006 - 2007 ++ ++ Based upon linux-2.6.18.5/net/netfilter/x_tables.c: ++ Copyright (C) 2006-2006 Harald Welte ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation. ++*/ ++#include ++#include ++#include ++#include ++ ++/* ++ * Yeah this code is sub-optimal, but the function is missing in ++ * mainline so far. -jengelh ++ */ ++static struct xt_match *xt_request_find_match_lo(int af, const char *name, ++ u8 revision) ++{ ++ static const char *const xt_prefix[] = { ++ [AF_INET] = "ip", ++ [AF_INET6] = "ip6", ++ [NF_ARP] = "arp", ++ }; ++ struct xt_match *match; ++ ++ match = try_then_request_module(xt_find_match(af, name, revision), ++ "%st_%s", xt_prefix[af], name); ++ if (IS_ERR(match) || match == NULL) ++ return NULL; ++ ++ return match; ++} ++ ++/* In case it goes into mainline, let this out-of-tree package compile */ ++#define xt_request_find_match xt_request_find_match_lo +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -265,6 +265,14 @@ + + # alphabetically ordered list of targets + ++config NETFILTER_XT_TARGET_CHAOS ++ tristate '"CHAOS" target support' ++ depends on NETFILTER_XTABLES ++ help ++ This option adds a `CHAOS' target. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NETFILTER_XT_TARGET_CLASSIFY + tristate '"CLASSIFY" target support' + depends on NETFILTER_XTABLES +@@ -292,6 +300,14 @@ + . The module will be called + ipt_CONNMARK.ko. If unsure, say `N'. + ++config NETFILTER_XT_TARGET_DELUDE ++ tristate '"DELUDE" target support' ++ depends on NETFILTER_XTABLES ++ help ++ This option adds a `DELUDE' target. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NETFILTER_XT_TARGET_DSCP + tristate '"DSCP" target support' + depends on NETFILTER_XTABLES +@@ -556,6 +572,14 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_MATCH_PORTSCAN ++ tristate '"portscan" match support' ++ depends on NETFILTER_XTABLES ++ help ++ This option adds a 'portscan' match support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config NETFILTER_XT_MATCH_MULTIPORT + tristate "Multiple port match support" + depends on NETFILTER_XTABLES +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -49,6 +49,8 @@ + obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_DELUDE) += xt_DELUDE.o + + # matches + obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o +@@ -78,3 +80,4 @@ + obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o + obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o + obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o ++obj-$(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) += xt_portscan.o +--- /dev/null ++++ b/net/netfilter/xt_CHAOS.c +@@ -0,0 +1,200 @@ ++/* ++ * CHAOS target for netfilter ++ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 ++ * Contact: Jan Engelhardt ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License; either version ++ * 2 or 3 as published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(_LOCAL) ++# include "xt_CHAOS.h" ++# include "find_match.c" ++#elif defined(CONFIG_NETFILTER_XT_TARGET_CHAOS) || \ ++ defined(CONFIG_NETFILTER_XT_TARGET_CHAOS_MODULE) ++# include ++# include "find_match.c" ++#else ++# include "xt_CHAOS.h" ++# include "find_match.c" ++#endif ++#define PFX KBUILD_MODNAME ": " ++ ++/* Module parameters */ ++static unsigned int reject_percentage = ~0U * .01; ++static unsigned int delude_percentage = ~0U * .0101; ++module_param(reject_percentage, uint, S_IRUGO | S_IWUSR); ++module_param(delude_percentage, uint, S_IRUGO | S_IWUSR); ++ ++/* References to other matches/targets */ ++static struct xt_match *xm_tcp; ++static struct xt_target *xt_delude, *xt_reject, *xt_tarpit; ++ ++static int have_delude, have_tarpit; ++ ++/* Static data for other matches/targets */ ++static const struct ipt_reject_info reject_params = { ++ .with = ICMP_HOST_UNREACH, ++}; ++ ++static const struct xt_tcp tcp_params = { ++ .spts = {0, ~0}, ++ .dpts = {0, ~0}, ++}; ++ ++/* CHAOS functions */ ++static void xt_chaos_total(const struct xt_chaos_target_info *info, ++ struct sk_buff **pskb, const struct net_device *in, ++ const struct net_device *out, unsigned int hooknum) ++{ ++ const struct iphdr *iph = ip_hdr(*pskb); ++ const int protoff = 4 * iph->ihl; ++ const int offset = ntohs(iph->frag_off) & IP_OFFSET; ++ const struct xt_target *destiny; ++ bool hotdrop = false, ret; ++ ++ ret = xm_tcp->match(*pskb, in, out, xm_tcp, &tcp_params, ++ offset, protoff, &hotdrop); ++ if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage) ++ return; ++ ++ destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude; ++ destiny->target(pskb, in, out, hooknum, destiny, NULL); ++ return; ++} ++ ++static unsigned int chaos_tg(struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ unsigned int hooknum, const struct xt_target *target, const void *targinfo) ++{ ++ /* ++ * Equivalent to: ++ * -A chaos -m statistic --mode random --probability \ ++ * $reject_percentage -j REJECT --reject-with host-unreach; ++ * -A chaos -p tcp -m statistic --mode random --probability \ ++ * $delude_percentage -j DELUDE; ++ * -A chaos -j DROP; ++ */ ++ const struct xt_chaos_target_info *info = targinfo; ++ const struct iphdr *iph = ip_hdr(*pskb); ++ ++ if ((unsigned int)net_random() <= reject_percentage) ++ return xt_reject->target(pskb, in, out, hooknum, target, ++ &reject_params); ++ ++ /* TARPIT/DELUDE may not be called from the OUTPUT chain */ ++ if (iph->protocol == IPPROTO_TCP && ++ info->variant != XTCHAOS_NORMAL && hooknum != NF_IP_LOCAL_OUT) ++ xt_chaos_total(info, pskb, in, out, hooknum); ++ ++ return NF_DROP; ++} ++ ++static bool chaos_tg_check(const char *tablename, const void *entry, ++ const struct xt_target *target, void *targinfo, unsigned int hook_mask) ++{ ++ const struct xt_chaos_target_info *info = targinfo; ++ ++ if (info->variant == XTCHAOS_DELUDE && !have_delude) { ++ printk(KERN_WARNING PFX "Error: Cannot use --delude when " ++ "DELUDE module not available\n"); ++ return false; ++ } ++ if (info->variant == XTCHAOS_TARPIT && !have_tarpit) { ++ printk(KERN_WARNING PFX "Error: Cannot use --tarpit when " ++ "TARPIT module not available\n"); ++ return false; ++ } ++ ++ return true; ++} ++ ++static struct xt_target chaos_tg_reg = { ++ .name = "CHAOS", ++ .family = AF_INET, ++ .table = "filter", ++ .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | ++ (1 << NF_IP_LOCAL_OUT), ++ .checkentry = chaos_tg_check, ++ .target = chaos_tg, ++ .targetsize = sizeof(struct xt_chaos_target_info), ++ .me = THIS_MODULE, ++}; ++ ++static int __init chaos_tg_init(void) ++{ ++ int ret = -EINVAL; ++ ++ xm_tcp = xt_request_find_match(AF_INET, "tcp", 0); ++ if (xm_tcp == NULL) { ++ printk(KERN_WARNING PFX "Error: Could not find or load " ++ "\"tcp\" match\n"); ++ return -EINVAL; ++ } ++ ++ xt_reject = xt_request_find_target(AF_INET, "REJECT", 0); ++ if (xt_reject == NULL) { ++ printk(KERN_WARNING PFX "Error: Could not find or load " ++ "\"REJECT\" target\n"); ++ goto out2; ++ } ++ ++ xt_tarpit = xt_request_find_target(AF_INET, "TARPIT", 0); ++ have_tarpit = xt_tarpit != NULL; ++ if (!have_tarpit) ++ printk(KERN_WARNING PFX "Warning: Could not find or load " ++ "\"TARPIT\" target\n"); ++ ++ xt_delude = xt_request_find_target(AF_INET, "DELUDE", 0); ++ have_delude = xt_delude != NULL; ++ if (!have_delude) ++ printk(KERN_WARNING PFX "Warning: Could not find or load " ++ "\"DELUDE\" target\n"); ++ ++ if ((ret = xt_register_target(&chaos_tg_reg)) != 0) { ++ printk(KERN_WARNING PFX "xt_register_target returned " ++ "error %d\n", ret); ++ goto out3; ++ } ++ ++ return 0; ++ ++ out3: ++ if (have_delude) ++ module_put(xt_delude->me); ++ if (have_tarpit) ++ module_put(xt_tarpit->me); ++ module_put(xt_reject->me); ++ out2: ++ module_put(xm_tcp->me); ++ return ret; ++} ++ ++static void __exit chaos_tg_exit(void) ++{ ++ xt_unregister_target(&chaos_tg_reg); ++ module_put(xm_tcp->me); ++ module_put(xt_reject->me); ++ if (have_delude) ++ module_put(xt_delude->me); ++ if (have_tarpit) ++ module_put(xt_tarpit->me); ++ return; ++} ++ ++module_init(chaos_tg_init); ++module_exit(chaos_tg_exit); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_DESCRIPTION("netfilter \"CHAOS\" target"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_CHAOS"); +--- /dev/null ++++ b/net/netfilter/xt_DELUDE.c +@@ -0,0 +1,197 @@ ++/* ++ * DELUDE target ++ * Copyright © CC Computer Consultants GmbH, 2007 ++ * Contact: Jan Engelhardt ++ * ++ * Based upon linux-2.6.18.5/net/ipv4/netfilter/ipt_REJECT.c: ++ * (C) 1999-2001 Paul `Rusty' Russell ++ * (C) 2002-2004 Netfilter Core Team ++ * ++ * xt_DELUDE acts like REJECT, but does reply with SYN-ACK on SYN. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_BRIDGE_NETFILTER ++# include ++#endif ++#include ++#define PFX KBUILD_MODNAME ": " ++ ++static void delude_send_reset(struct sk_buff *oldskb, unsigned int hook) ++{ ++ struct tcphdr _otcph, *oth, *tcph; ++ unsigned int addr_type; ++ struct sk_buff *nskb; ++ u_int16_t tmp_port; ++ u_int32_t tmp_addr; ++ struct iphdr *niph; ++ bool needs_ack; ++ ++ /* IP header checks: fragment. */ ++ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) ++ return; ++ ++ oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), ++ sizeof(_otcph), &_otcph); ++ if (oth == NULL) ++ return; ++ ++ /* No RST for RST. */ ++ if (oth->rst) ++ return; ++ ++ /* Check checksum */ ++ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) ++ return; ++ ++ /* We need a linear, writeable skb. We also need to expand ++ headroom in case hh_len of incoming interface < hh_len of ++ outgoing interface */ ++ nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb), ++ GFP_ATOMIC); ++ if (!nskb) ++ return; ++ ++ /* This packet will not be the same as the other: clear nf fields */ ++ nf_reset(nskb); ++ nskb->mark = 0; ++ skb_init_secmark(nskb); ++ ++ skb_shinfo(nskb)->gso_size = 0; ++ skb_shinfo(nskb)->gso_segs = 0; ++ skb_shinfo(nskb)->gso_type = 0; ++ ++ tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb)); ++ ++ /* Swap source and dest */ ++ niph = ip_hdr(nskb); ++ tmp_addr = niph->saddr; ++ niph->saddr = niph->daddr; ++ niph->daddr = tmp_addr; ++ tmp_port = tcph->source; ++ tcph->source = tcph->dest; ++ tcph->dest = tmp_port; ++ ++ /* Truncate to length (no data) */ ++ tcph->doff = sizeof(struct tcphdr) / 4; ++ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); ++ niph->tot_len = htons(nskb->len); ++ ++ if (oth->syn && !oth->ack && !oth->rst && !oth->fin) { ++ /* DELUDE essential part */ ++ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + ++ oldskb->len - ip_hdrlen(oldskb) - ++ (oth->doff << 2)); ++ tcph->seq = false; ++ tcph->ack = true; ++ } else { ++ if (!tcph->ack) { ++ needs_ack = true; ++ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + ++ oth->fin + oldskb->len - ++ ip_hdrlen(oldskb) - (oth->doff<<2)); ++ tcph->seq = false; ++ } else { ++ needs_ack = false; ++ tcph->seq = oth->ack_seq; ++ tcph->ack_seq = false; ++ } ++ ++ /* Reset flags */ ++ ((u_int8_t *)tcph)[13] = 0; ++ tcph->rst = true; ++ tcph->ack = needs_ack; ++ } ++ ++ tcph->window = 0; ++ tcph->urg_ptr = 0; ++ ++ /* Adjust TCP checksum */ ++ tcph->check = 0; ++ tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr, ++ niph->daddr, csum_partial((char *)tcph, ++ sizeof(struct tcphdr), 0)); ++ ++ /* Set DF, id = 0 */ ++ niph->frag_off = htons(IP_DF); ++ niph->id = 0; ++ ++ addr_type = RTN_UNSPEC; ++#ifdef CONFIG_BRIDGE_NETFILTER ++ if (hook != NF_IP_FORWARD || (nskb->nf_bridge != NULL && ++ nskb->nf_bridge->mask & BRNF_BRIDGED)) ++#else ++ if (hook != NF_IP_FORWARD) ++#endif ++ addr_type = RTN_LOCAL; ++ ++ if (ip_route_me_harder(&nskb, addr_type)) ++ goto free_nskb; ++ ++ nskb->ip_summed = CHECKSUM_NONE; ++ ++ /* Adjust IP TTL */ ++ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); ++ ++ /* Adjust IP checksum */ ++ niph->check = 0; ++ niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl); ++ ++ /* "Never happens" */ ++ if (nskb->len > dst_mtu(nskb->dst)) ++ goto free_nskb; ++ ++ nf_ct_attach(nskb, oldskb); ++ ++ NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev, ++ dst_output); ++ return; ++ ++ free_nskb: ++ kfree_skb(nskb); ++} ++ ++static unsigned int delude_tg(struct sk_buff **pskb, ++ const struct net_device *in, const struct net_device *out, ++ unsigned int hooknum, const struct xt_target *target, const void *targinfo) ++{ ++ /* WARNING: This code causes reentry within iptables. ++ This means that the iptables jump stack is now crap. We ++ must return an absolute verdict. --RR */ ++ delude_send_reset(*pskb, hooknum); ++ return NF_DROP; ++} ++ ++static struct xt_target delude_tg_reg = { ++ .name = "DELUDE", ++ .family = AF_INET, ++ .table = "filter", ++ .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD), ++ .target = delude_tg, ++ .proto = IPPROTO_TCP, ++ .me = THIS_MODULE, ++}; ++ ++static int __init delude_tg_init(void) ++{ ++ return xt_register_target(&delude_tg_reg); ++} ++ ++static void __exit delude_tg_exit(void) ++{ ++ xt_unregister_target(&delude_tg_reg); ++} ++ ++module_init(delude_tg_init); ++module_exit(delude_tg_exit); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_DESCRIPTION("netfilter \"DELUDE\" target"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_DELUDE"); +--- /dev/null ++++ b/net/netfilter/xt_portscan.c +@@ -0,0 +1,269 @@ ++/* ++ * portscan match for netfilter ++ * Copyright © CC Computer Consultants GmbH, 2006 - 2007 ++ * Contact: Jan Engelhardt ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License; either version ++ * 2 or 3 as published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(_LOCAL) ++# include "xt_portscan.h" ++#elif defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) || \ ++ defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN_MODULE) ++# include ++#else ++# include "xt_portscan.h" ++#endif ++#define PFX KBUILD_MODNAME ": " ++ ++enum { ++ TCP_FLAGS_ALL3 = TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_SYN, ++ TCP_FLAGS_ALL4 = TCP_FLAGS_ALL3 | TCP_FLAG_ACK, ++ TCP_FLAGS_ALL6 = TCP_FLAGS_ALL4 | TCP_FLAG_PSH | TCP_FLAG_URG, ++}; ++ ++/* Module parameters */ ++static unsigned int ++ connmark_mask = ~0, ++ packet_mask = ~0, ++ mark_seen = 0x9, ++ mark_synrcv = 0x1, ++ mark_closed = 0x2, ++ mark_synscan = 0x3, ++ mark_estab1 = 0x4, ++ mark_estab2 = 0x5, ++ mark_cnscan = 0x6, ++ mark_grscan = 0x7, ++ mark_valid = 0x8; ++ ++module_param(connmark_mask, uint, S_IRUGO | S_IWUSR); ++module_param(packet_mask, uint, S_IRUGO | S_IWUSR); ++module_param(mark_seen, uint, S_IRUGO | S_IWUSR); ++module_param(mark_synrcv, uint, S_IRUGO | S_IWUSR); ++module_param(mark_closed, uint, S_IRUGO | S_IWUSR); ++module_param(mark_synscan, uint, S_IRUGO | S_IWUSR); ++module_param(mark_estab1, uint, S_IRUGO | S_IWUSR); ++module_param(mark_estab2, uint, S_IRUGO | S_IWUSR); ++module_param(mark_cnscan, uint, S_IRUGO | S_IWUSR); ++module_param(mark_grscan, uint, S_IRUGO | S_IWUSR); ++module_param(mark_valid, uint, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(connmark_mask, "only set specified bits in connection mark"); ++MODULE_PARM_DESC(packet_mask, "only set specified bits in packet mark"); ++MODULE_PARM_DESC(mark_seen, "nfmark value for packet-seen state"); ++MODULE_PARM_DESC(mark_synrcv, "connmark value for SYN Received state"); ++MODULE_PARM_DESC(mark_closed, "connmark value for closed state"); ++MODULE_PARM_DESC(mark_synscan, "connmark value for SYN Scan state"); ++MODULE_PARM_DESC(mark_estab1, "connmark value for Established-1 state"); ++MODULE_PARM_DESC(mark_estab2, "connmark value for Established-2 state"); ++MODULE_PARM_DESC(mark_cnscan, "connmark value for Connect Scan state"); ++MODULE_PARM_DESC(mark_grscan, "connmark value for Grab Scan state"); ++MODULE_PARM_DESC(mark_valid, "connmark value for Valid state"); ++ ++/* TCP flag functions */ ++static inline bool tflg_ack4(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_ACK; ++} ++ ++static inline bool tflg_ack6(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL6) == TCP_FLAG_ACK; ++} ++ ++static inline bool tflg_fin(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_FIN; ++} ++ ++static inline bool tflg_rst(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_RST; ++} ++ ++static inline bool tflg_rstack(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == ++ (TCP_FLAG_ACK | TCP_FLAG_RST); ++} ++ ++static inline bool tflg_syn(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_SYN; ++} ++ ++static inline bool tflg_synack(const struct tcphdr *th) ++{ ++ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == ++ (TCP_FLAG_SYN | TCP_FLAG_ACK); ++} ++ ++/* portscan functions */ ++static inline bool portscan_mt_stealth(const struct tcphdr *th) ++{ ++ /* ++ * "Connection refused" replies to our own probes must not be matched. ++ */ ++ if (tflg_rstack(th)) ++ return false; ++ ++ if (tflg_rst(th) && printk_ratelimit()) { ++ printk(KERN_WARNING PFX "Warning: Pure RST received\n"); ++ return false; ++ } ++ ++ /* ++ * -p tcp ! --syn -m conntrack --ctstate INVALID: Looking for non-start ++ * packets that are not associated with any connection -- this will ++ * match most scan types (NULL, XMAS, FIN) and ridiculous flag ++ * combinations (SYN-RST, SYN-FIN, SYN-FIN-RST, FIN-RST, etc.). ++ */ ++ return !tflg_syn(th); ++} ++ ++static inline unsigned int portscan_mt_full(int mark, ++ enum ip_conntrack_info ctstate, bool loopback, const struct tcphdr *tcph, ++ unsigned int payload_len) ++{ ++ if (mark == mark_estab2) { ++ /* ++ * -m connmark --mark $ESTAB2 ++ */ ++ if (tflg_ack4(tcph) && payload_len == 0) ++ return mark; /* keep mark */ ++ else if (tflg_rst(tcph) || tflg_fin(tcph)) ++ return mark_grscan; ++ else ++ return mark_valid; ++ } else if (mark == mark_estab1) { ++ /* ++ * -m connmark --mark $ESTAB1 ++ */ ++ if (tflg_rst(tcph) || tflg_fin(tcph)) ++ return mark_cnscan; ++ else if (!loopback && tflg_ack4(tcph) && payload_len == 0) ++ return mark_estab2; ++ else ++ return mark_valid; ++ } else if (mark == mark_synrcv) { ++ /* ++ * -m connmark --mark $SYN ++ */ ++ if (loopback && tflg_synack(tcph)) ++ return mark; /* keep mark */ ++ else if (loopback && tflg_rstack(tcph)) ++ return mark_closed; ++ else if (tflg_ack6(tcph)) ++ return mark_estab1; ++ else ++ return mark_synscan; ++ } else if (ctstate == IP_CT_NEW && tflg_syn(tcph)) { ++ /* ++ * -p tcp --syn --ctstate NEW ++ */ ++ return mark_synrcv; ++ } ++ return mark; ++} ++ ++static bool portscan_mt(const struct sk_buff *skb, ++ const struct net_device *in, const struct net_device *out, ++ const struct xt_match *match, const void *matchinfo, int offset, ++ unsigned int protoff, bool *hotdrop) ++{ ++ const struct xt_portscan_match_info *info = matchinfo; ++ enum ip_conntrack_info ctstate; ++ const struct tcphdr *tcph; ++ struct nf_conn *ctdata; ++ struct tcphdr tcph_buf; ++ ++ tcph = skb_header_pointer(skb, protoff, sizeof(tcph_buf), &tcph_buf); ++ if (tcph == NULL) ++ return false; ++ ++ /* Check for invalid packets: -m conntrack --ctstate INVALID */ ++ if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) { ++ if (info->match_stealth) ++ return portscan_mt_stealth(tcph); ++ /* ++ * If @ctdata is NULL, we cannot match the other scan ++ * types, return. ++ */ ++ return false; ++ } ++ ++ /* ++ * If -m portscan was previously applied to this packet, the rules we ++ * simulate must not be run through again. And for speedup, do not call ++ * it either when the connection is already VALID. ++ */ ++ if ((ctdata->mark & connmark_mask) == mark_valid || ++ (skb->mark & packet_mask) != mark_seen) { ++ unsigned int n; ++ ++ n = portscan_mt_full(ctdata->mark & connmark_mask, ctstate, ++ in == &loopback_dev, tcph, ++ skb->len - protoff - 4 * tcph->doff); ++ ++ ctdata->mark = (ctdata->mark & ~connmark_mask) | n; ++ ((struct sk_buff *)skb)->mark = ++ (skb->mark & ~packet_mask) ^ mark_seen; ++ } ++ ++ return (info->match_syn && ctdata->mark == mark_synscan) || ++ (info->match_cn && ctdata->mark == mark_cnscan) || ++ (info->match_gr && ctdata->mark == mark_grscan); ++} ++ ++static bool portscan_mt_check(const char *tablename, const void *entry, ++ const struct xt_match *match, void *matchinfo, unsigned int hook_mask) ++{ ++ const struct xt_portscan_match_info *info = matchinfo; ++ ++ if ((info->match_stealth & ~1) || (info->match_syn & ~1) || ++ (info->match_cn & ~1) || (info->match_gr & ~1)) { ++ printk(KERN_WARNING PFX "Invalid flags\n"); ++ return false; ++ } ++ return true; ++} ++ ++static struct xt_match portscan_mt_reg __read_mostly = { ++ .name = "portscan", ++ .family = AF_INET, ++ .match = portscan_mt, ++ .checkentry = portscan_mt_check, ++ .matchsize = sizeof(struct xt_portscan_match_info), ++ .proto = IPPROTO_TCP, ++ .me = THIS_MODULE, ++}; ++ ++static int __init portscan_mt_init(void) ++{ ++ return xt_register_match(&portscan_mt_reg); ++} ++ ++static void __exit portscan_mt_exit(void) ++{ ++ xt_unregister_match(&portscan_mt_reg); ++ return; ++} ++ ++module_init(portscan_mt_init); ++module_exit(portscan_mt_exit); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_DESCRIPTION("netfilter \"portscan\" match"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_portscan"); +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -1564,6 +1564,8 @@ + return seq; + } + ++EXPORT_SYMBOL(secure_tcp_sequence_number); ++ + /* Generate secure starting point for ephemeral IPV4 transport port search */ + u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) + { diff --git a/target/linux/generic-2.6/patches-2.6.23/171-netfilter_tarpit.patch b/target/linux/generic-2.6/patches-2.6.23/171-netfilter_tarpit.patch new file mode 100644 index 0000000000..e74c9d9f21 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/171-netfilter_tarpit.patch @@ -0,0 +1,319 @@ +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -401,6 +401,23 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config NETFILTER_XT_TARGET_TARPIT ++ tristate '"TARPIT" target support' ++ depends on NETFILTER_XTABLES ++ ---help--- ++ Adds a TARPIT target to iptables, which captures and holds ++ incoming TCP connections using no local per-connection resources. ++ Connections are accepted, but immediately switched to the persist ++ state (0 byte window), in which the remote side stops sending data ++ and asks to continue every 60-240 seconds. Attempts to close the ++ connection are ignored, forcing the remote side to time out the ++ connection in 12-24 minutes. ++ ++ This offers similar functionality to LaBrea ++ , but does not require dedicated ++ hardware or IPs. Any TCP port that you would normally DROP or REJECT ++ can instead become a tarpit. ++ + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' + depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -47,6 +47,7 @@ + obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o + obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o ++obj-$(CONFIG_NETFILTER_XT_TARGET_TARPIT) += xt_TARPIT.o + obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o + obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o +--- /dev/null ++++ b/net/netfilter/xt_TARPIT.c +@@ -0,0 +1,280 @@ ++/* ++ * Kernel module to capture and hold incoming TCP connections using ++ * no local per-connection resources. ++ * ++ * Based on ipt_REJECT.c and offering functionality similar to ++ * LaBrea . ++ * ++ * Copyright (c) 2002 Aaron Hopkins ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Goal: ++ * - Allow incoming TCP connections to be established. ++ * - Passing data should result in the connection being switched to the ++ * persist state (0 byte window), in which the remote side stops sending ++ * data and asks to continue every 60 seconds. ++ * - Attempts to shut down the connection should be ignored completely, so ++ * the remote side ends up having to time it out. ++ * ++ * This means: ++ * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes ++ * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing ++ * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++struct in_device; ++#include ++#include ++#include ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++/* Stolen from ip_finish_output2 */ ++static int ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ ++ if (dst->hh != NULL) ++ return neigh_hh_output(dst->hh, skb); ++ else if (dst->neighbour != NULL) ++ return dst->neighbour->output(skb); ++ ++ if (net_ratelimit()) ++ printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n"); ++ ++ kfree_skb(skb); ++ return -EINVAL; ++} ++ ++ ++/* Send reply */ ++static void tarpit_tcp(const struct sk_buff *oskb, struct rtable *ort, ++ unsigned int local) ++{ ++ struct sk_buff *nskb; ++ struct rtable *nrt; ++ struct tcphdr *otcph, *ntcph; ++ struct flowi fl = {}; ++ unsigned int otcplen; ++ u_int16_t tmp; ++ ++ const struct iphdr *oiph = ip_hdr(oskb); ++ struct iphdr *niph; ++ ++ /* A truncated TCP header is not going to be useful */ ++ if (oskb->len < ip_hdrlen(oskb) + sizeof(struct tcphdr)) ++ return; ++ ++ otcph = (void *)oiph + ip_hdrlen(oskb); ++ otcplen = oskb->len - ip_hdrlen(oskb); ++ ++ /* No replies for RST or FIN */ ++ if (otcph->rst || otcph->fin) ++ return; ++ ++ /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */ ++ if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ))) ++ return; ++ ++ /* Check checksum. */ ++ if (tcp_v4_check(otcplen, oiph->saddr, oiph->daddr, ++ csum_partial((char *)otcph, otcplen, 0)) != 0) ++ return; ++ ++ /* ++ * Copy skb (even if skb is about to be dropped, we cannot just ++ * clone it because there may be other things, such as tcpdump, ++ * interested in it) ++ */ ++ nskb = skb_copy(oskb, GFP_ATOMIC); ++ if (nskb == NULL) ++ return; ++ ++ niph = ip_hdr(nskb); ++ ++ /* This packet will not be the same as the other: clear nf fields */ ++ nf_conntrack_put(nskb->nfct); ++ nskb->nfct = NULL; ++#ifdef CONFIG_NETFILTER_DEBUG ++ nskb->nf_debug = 0; ++#endif ++ ++ ntcph = (void *)niph + ip_hdrlen(nskb); ++ ++ /* Truncate to length (no data) */ ++ ntcph->doff = sizeof(struct tcphdr)/4; ++ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr)); ++ niph->tot_len = htons(nskb->len); ++ ++ /* Swap source and dest */ ++ niph->daddr = xchg(&niph->saddr, niph->daddr); ++ tmp = ntcph->source; ++ ntcph->source = ntcph->dest; ++ ntcph->dest = tmp; ++ ++ /* Use supplied sequence number or make a new one */ ++ ntcph->seq = otcph->ack ? otcph->ack_seq ++ : htonl(secure_tcp_sequence_number(niph->saddr, ++ niph->daddr, ++ ntcph->source, ++ ntcph->dest)); ++ ++ /* Our SYN-ACKs must have a >0 window */ ++ ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0; ++ ++ ntcph->urg_ptr = 0; ++ ++ /* Reset flags */ ++ ((u_int8_t *)ntcph)[13] = 0; ++ ++ if (otcph->syn && otcph->ack) { ++ ntcph->rst = 1; ++ ntcph->ack_seq = 0; ++ } else { ++ ntcph->syn = otcph->syn; ++ ntcph->ack = 1; ++ ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn); ++ } ++ ++ /* Adjust TCP checksum */ ++ ntcph->check = 0; ++ ntcph->check = tcp_v4_check(sizeof(struct tcphdr), ++ niph->saddr, ++ niph->daddr, ++ csum_partial((char *)ntcph, ++ sizeof(struct tcphdr), 0)); ++ ++ fl.nl_u.ip4_u.daddr = niph->daddr; ++ fl.nl_u.ip4_u.saddr = local ? niph->saddr : 0; ++ fl.nl_u.ip4_u.tos = RT_TOS(niph->tos) | RTO_CONN; ++ fl.oif = 0; ++ ++ if (ip_route_output_key(&nrt, &fl)) ++ goto free_nskb; ++ ++ dst_release(nskb->dst); ++ nskb->dst = &nrt->u.dst; ++ ++ /* Adjust IP TTL */ ++ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT); ++ ++ /* Set DF, id = 0 */ ++ niph->frag_off = htons(IP_DF); ++ niph->id = 0; ++ ++ /* Adjust IP checksum */ ++ niph->check = 0; ++ niph->check = ip_fast_csum((unsigned char *)niph, niph->ihl); ++ ++ /* "Never happens" */ ++ if (nskb->len > dst_mtu(nskb->dst)) ++ goto free_nskb; ++ ++ ip_direct_send(nskb); ++ return; ++ ++ free_nskb: ++ kfree_skb(nskb); ++} ++ ++static unsigned int xt_tarpit_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const struct xt_target *target, ++ const void *targinfo) ++{ ++ const struct sk_buff *skb = *pskb; ++ const struct iphdr *iph = ip_hdr(skb); ++ struct rtable *rt = (void *)skb->dst; ++ ++ /* Do we have an input route cache entry? */ ++ if (rt == NULL) ++ return NF_DROP; ++ ++ /* No replies to physical multicast/broadcast */ ++ if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST) ++ return NF_DROP; ++ ++ /* Now check at the protocol level */ ++ if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) ++ return NF_DROP; ++ ++ /* ++ * Our naive response construction does not deal with IP ++ * options, and probably should not try. ++ */ ++ if (iph->ihl * 4 != sizeof(struct iphdr)) ++ return NF_DROP; ++ ++ /* We are not interested in fragments */ ++ if (iph->frag_off & htons(IP_OFFSET)) ++ return NF_DROP; ++ ++ tarpit_tcp(skb, rt, hooknum == NF_IP_LOCAL_IN); ++ return NF_DROP; ++} ++ ++static bool xt_tarpit_check(const char *tablename, const void *entry, ++ const struct xt_target *target, void *targinfo, ++ unsigned int hook_mask) ++{ ++ bool invalid; ++ ++ if (strcmp(tablename, "raw") == 0 && hook_mask == NF_IP_PRE_ROUTING) ++ return true; ++ if (strcmp(tablename, "filter") != 0) ++ return false; ++ invalid = hook_mask & ~((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD)); ++ return !invalid; ++} ++ ++static struct xt_target xt_tarpit_reg = { ++ .name = "TARPIT", ++ .family = AF_INET, ++ .proto = IPPROTO_TCP, ++ .target = xt_tarpit_target, ++ .checkentry = xt_tarpit_check, ++ .me = THIS_MODULE, ++}; ++ ++static int __init xt_tarpit_init(void) ++{ ++ return xt_register_target(&xt_tarpit_reg); ++} ++ ++static void __exit xt_tarpit_exit(void) ++{ ++ xt_unregister_target(&xt_tarpit_reg); ++} ++ ++module_init(xt_tarpit_init); ++module_exit(xt_tarpit_exit); ++MODULE_DESCRIPTION("netfilter xt_TARPIT target module"); ++MODULE_AUTHOR("Jan Engelhardt "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("ipt_TARPIT"); diff --git a/target/linux/generic-2.6/patches-2.6.23/180-netfilter_depends.patch b/target/linux/generic-2.6/patches-2.6.23/180-netfilter_depends.patch new file mode 100644 index 0000000000..bb5de23b42 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/180-netfilter_depends.patch @@ -0,0 +1,20 @@ +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -143,7 +143,7 @@ + + config NF_CONNTRACK_H323 + tristate "H.323 protocol support (EXPERIMENTAL)" +- depends on EXPERIMENTAL && NF_CONNTRACK && (IPV6 || IPV6=n) ++ depends on EXPERIMENTAL && NF_CONNTRACK + help + H.323 is a VoIP signalling protocol from ITU-T. As one of the most + important VoIP protocols, it is widely used by voice hardware and +@@ -420,7 +420,7 @@ + + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' +- depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) ++ depends on NETFILTER_XTABLES + ---help--- + This option adds a `TCPMSS' target, which allows you to alter the + MSS value of TCP SYN packets, to control the maximum size for that diff --git a/target/linux/generic-2.6/patches-2.6.23/190-netfilter_rtsp.patch b/target/linux/generic-2.6/patches-2.6.23/190-netfilter_rtsp.patch new file mode 100644 index 0000000000..ad9bc2ac17 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/190-netfilter_rtsp.patch @@ -0,0 +1,1362 @@ +--- /dev/null ++++ b/include/linux/netfilter/nf_conntrack_rtsp.h +@@ -0,0 +1,63 @@ ++/* ++ * RTSP extension for IP connection tracking. ++ * (C) 2003 by Tom Marshall ++ * based on ip_conntrack_irc.h ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#ifndef _IP_CONNTRACK_RTSP_H ++#define _IP_CONNTRACK_RTSP_H ++ ++//#define IP_NF_RTSP_DEBUG 1 ++#define IP_NF_RTSP_VERSION "0.6.21" ++ ++#ifdef __KERNEL__ ++/* port block types */ ++typedef enum { ++ pb_single, /* client_port=x */ ++ pb_range, /* client_port=x-y */ ++ pb_discon /* client_port=x/y (rtspbis) */ ++} portblock_t; ++ ++/* We record seq number and length of rtsp headers here, all in host order. */ ++ ++/* ++ * This structure is per expected connection. It is a member of struct ++ * ip_conntrack_expect. The TCP SEQ for the conntrack expect is stored ++ * there and we are expected to only store the length of the data which ++ * needs replaced. If a packet contains multiple RTSP messages, we create ++ * one expected connection per message. ++ * ++ * We use these variables to mark the entire header block. This may seem ++ * like overkill, but the nature of RTSP requires it. A header may appear ++ * multiple times in a message. We must treat two Transport headers the ++ * same as one Transport header with two entries. ++ */ ++struct ip_ct_rtsp_expect ++{ ++ u_int32_t len; /* length of header block */ ++ portblock_t pbtype; /* Type of port block that was requested */ ++ u_int16_t loport; /* Port that was requested, low or first */ ++ u_int16_t hiport; /* Port that was requested, high or second */ ++#if 0 ++ uint method; /* RTSP method */ ++ uint cseq; /* CSeq from request */ ++#endif ++}; ++ ++extern unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen, ++ struct ip_ct_rtsp_expect *prtspexp, ++ struct nf_conntrack_expect *exp); ++ ++extern void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); ++ ++#define RTSP_PORT 554 ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _IP_CONNTRACK_RTSP_H */ +--- /dev/null ++++ b/include/linux/netfilter_helpers.h +@@ -0,0 +1,133 @@ ++/* ++ * Helpers for netfiler modules. This file provides implementations for basic ++ * functions such as strncasecmp(), etc. ++ * ++ * gcc will warn for defined but unused functions, so we only include the ++ * functions requested. The following macros are used: ++ * NF_NEED_STRNCASECMP nf_strncasecmp() ++ * NF_NEED_STRTOU16 nf_strtou16() ++ * NF_NEED_STRTOU32 nf_strtou32() ++ */ ++#ifndef _NETFILTER_HELPERS_H ++#define _NETFILTER_HELPERS_H ++ ++/* Only include these functions for kernel code. */ ++#ifdef __KERNEL__ ++ ++#include ++#define iseol(c) ( (c) == '\r' || (c) == '\n' ) ++ ++/* ++ * The standard strncasecmp() ++ */ ++#ifdef NF_NEED_STRNCASECMP ++static int ++nf_strncasecmp(const char* s1, const char* s2, u_int32_t len) ++{ ++ if (s1 == NULL || s2 == NULL) ++ { ++ if (s1 == NULL && s2 == NULL) ++ { ++ return 0; ++ } ++ return (s1 == NULL) ? -1 : 1; ++ } ++ while (len > 0 && tolower(*s1) == tolower(*s2)) ++ { ++ len--; ++ s1++; ++ s2++; ++ } ++ return ( (len == 0) ? 0 : (tolower(*s1) - tolower(*s2)) ); ++} ++#endif /* NF_NEED_STRNCASECMP */ ++ ++/* ++ * Parse a string containing a 16-bit unsigned integer. ++ * Returns the number of chars used, or zero if no number is found. ++ */ ++#ifdef NF_NEED_STRTOU16 ++static int ++nf_strtou16(const char* pbuf, u_int16_t* pval) ++{ ++ int n = 0; ++ ++ *pval = 0; ++ while (isdigit(pbuf[n])) ++ { ++ *pval = (*pval * 10) + (pbuf[n] - '0'); ++ n++; ++ } ++ ++ return n; ++} ++#endif /* NF_NEED_STRTOU16 */ ++ ++/* ++ * Parse a string containing a 32-bit unsigned integer. ++ * Returns the number of chars used, or zero if no number is found. ++ */ ++#ifdef NF_NEED_STRTOU32 ++static int ++nf_strtou32(const char* pbuf, u_int32_t* pval) ++{ ++ int n = 0; ++ ++ *pval = 0; ++ while (pbuf[n] >= '0' && pbuf[n] <= '9') ++ { ++ *pval = (*pval * 10) + (pbuf[n] - '0'); ++ n++; ++ } ++ ++ return n; ++} ++#endif /* NF_NEED_STRTOU32 */ ++ ++/* ++ * Given a buffer and length, advance to the next line and mark the current ++ * line. ++ */ ++#ifdef NF_NEED_NEXTLINE ++static int ++nf_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) ++{ ++ uint off = *poff; ++ uint physlen = 0; ++ ++ if (off >= len) ++ { ++ return 0; ++ } ++ ++ while (p[off] != '\n') ++ { ++ if (len-off <= 1) ++ { ++ return 0; ++ } ++ ++ physlen++; ++ off++; ++ } ++ ++ /* if we saw a crlf, physlen needs adjusted */ ++ if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') ++ { ++ physlen--; ++ } ++ ++ /* advance past the newline */ ++ off++; ++ ++ *plineoff = *poff; ++ *plinelen = physlen; ++ *poff = off; ++ ++ return 1; ++} ++#endif /* NF_NEED_NEXTLINE */ ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _NETFILTER_HELPERS_H */ +--- /dev/null ++++ b/include/linux/netfilter_mime.h +@@ -0,0 +1,89 @@ ++/* ++ * MIME functions for netfilter modules. This file provides implementations ++ * for basic MIME parsing. MIME headers are used in many protocols, such as ++ * HTTP, RTSP, SIP, etc. ++ * ++ * gcc will warn for defined but unused functions, so we only include the ++ * functions requested. The following macros are used: ++ * NF_NEED_MIME_NEXTLINE nf_mime_nextline() ++ */ ++#ifndef _NETFILTER_MIME_H ++#define _NETFILTER_MIME_H ++ ++/* Only include these functions for kernel code. */ ++#ifdef __KERNEL__ ++ ++#include ++ ++/* ++ * Given a buffer and length, advance to the next line and mark the current ++ * line. If the current line is empty, *plinelen will be set to zero. If ++ * not, it will be set to the actual line length (including CRLF). ++ * ++ * 'line' in this context means logical line (includes LWS continuations). ++ * Returns 1 on success, 0 on failure. ++ */ ++#ifdef NF_NEED_MIME_NEXTLINE ++static int ++nf_mime_nextline(char* p, uint len, uint* poff, uint* plineoff, uint* plinelen) ++{ ++ uint off = *poff; ++ uint physlen = 0; ++ int is_first_line = 1; ++ ++ if (off >= len) ++ { ++ return 0; ++ } ++ ++ do ++ { ++ while (p[off] != '\n') ++ { ++ if (len-off <= 1) ++ { ++ return 0; ++ } ++ ++ physlen++; ++ off++; ++ } ++ ++ /* if we saw a crlf, physlen needs adjusted */ ++ if (physlen > 0 && p[off] == '\n' && p[off-1] == '\r') ++ { ++ physlen--; ++ } ++ ++ /* advance past the newline */ ++ off++; ++ ++ /* check for an empty line */ ++ if (physlen == 0) ++ { ++ break; ++ } ++ ++ /* check for colon on the first physical line */ ++ if (is_first_line) ++ { ++ is_first_line = 0; ++ if (memchr(p+(*poff), ':', physlen) == NULL) ++ { ++ return 0; ++ } ++ } ++ } ++ while (p[off] == ' ' || p[off] == '\t'); ++ ++ *plineoff = *poff; ++ *plinelen = (physlen == 0) ? 0 : (off - *poff); ++ *poff = off; ++ ++ return 1; ++} ++#endif /* NF_NEED_MIME_NEXTLINE */ ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _NETFILTER_MIME_H */ +--- a/net/ipv4/netfilter/Makefile ++++ b/net/ipv4/netfilter/Makefile +@@ -23,6 +23,7 @@ + obj-$(CONFIG_NF_NAT_FTP) += nf_nat_ftp.o + obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o + obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o ++obj-$(CONFIG_NF_NAT_RTSP) += nf_nat_rtsp.o + obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o + obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o + obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o +--- a/net/netfilter/Kconfig ++++ b/net/netfilter/Kconfig +@@ -249,6 +249,16 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config NF_CONNTRACK_RTSP ++ tristate "RTSP protocol support" ++ depends on NF_CONNTRACK ++ help ++ Support the RTSP protocol. This allows UDP transports to be setup ++ properly, including RTP and RDT. ++ ++ If you want to compile it as a module, say 'M' here and read ++ Documentation/modules.txt. If unsure, say 'Y'. ++ + config NF_CT_NETLINK + tristate 'Connection tracking netlink interface (EXPERIMENTAL)' + depends on EXPERIMENTAL && NF_CONNTRACK && NETFILTER_NETLINK +--- a/net/netfilter/Makefile ++++ b/net/netfilter/Makefile +@@ -33,6 +33,7 @@ + obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o + obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o + obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o ++obj-$(CONFIG_NF_CONNTRACK_RTSP) += nf_conntrack_rtsp.o + + # generic X tables + obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o +--- a/net/ipv4/netfilter/Kconfig ++++ b/net/ipv4/netfilter/Kconfig +@@ -296,6 +296,11 @@ + depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT + default NF_NAT && NF_CONNTRACK_IRC + ++config NF_NAT_RTSP ++ tristate ++ depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT ++ default NF_NAT && NF_CONNTRACK_RTSP ++ + config NF_NAT_TFTP + tristate + depends on IP_NF_IPTABLES && NF_CONNTRACK && NF_NAT +--- /dev/null ++++ b/net/netfilter/nf_conntrack_rtsp.c +@@ -0,0 +1,513 @@ ++/* ++ * RTSP extension for IP connection tracking ++ * (C) 2003 by Tom Marshall ++ * based on ip_conntrack_irc.c ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Module load syntax: ++ * insmod nf_conntrack_rtsp.o ports=port1,port2,...port ++ * max_outstanding=n setup_timeout=secs ++ * ++ * If no ports are specified, the default will be port 554. ++ * ++ * With max_outstanding you can define the maximum number of not yet ++ * answered SETUP requests per RTSP session (default 8). ++ * With setup_timeout you can specify how long the system waits for ++ * an expected data channel (default 300 seconds). ++ * ++ * 2005-02-13: Harald Welte ++ * - port to 2.6 ++ * - update to recent post-2.6.11 api changes ++ * 2006-09-14: Steven Van Acker ++ * - removed calls to NAT code from conntrack helper: NAT no longer needed to use rtsp-conntrack ++ * 2007-04-18: Michael Guntsche ++ * - Port to new NF API ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define NF_NEED_STRNCASECMP ++#define NF_NEED_STRTOU16 ++#define NF_NEED_STRTOU32 ++#define NF_NEED_NEXTLINE ++#include ++#define NF_NEED_MIME_NEXTLINE ++#include ++ ++#include ++#define MAX_SIMUL_SETUP 8 /* XXX: use max_outstanding */ ++#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#if 0 ++#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#else ++#define DEBUGP(fmt, args...) ++#endif ++ ++#define MAX_PORTS 8 ++static int ports[MAX_PORTS]; ++static int num_ports = 0; ++static int max_outstanding = 8; ++static unsigned int setup_timeout = 300; ++ ++MODULE_AUTHOR("Tom Marshall "); ++MODULE_DESCRIPTION("RTSP connection tracking module"); ++MODULE_LICENSE("GPL"); ++module_param_array(ports, int, &num_ports, 0400); ++MODULE_PARM_DESC(ports, "port numbers of RTSP servers"); ++module_param(max_outstanding, int, 0400); ++MODULE_PARM_DESC(max_outstanding, "max number of outstanding SETUP requests per RTSP session"); ++module_param(setup_timeout, int, 0400); ++MODULE_PARM_DESC(setup_timeout, "timeout on for unestablished data channels"); ++ ++static char *rtsp_buffer; ++static DEFINE_SPINLOCK(rtsp_buffer_lock); ++ ++unsigned int (*nf_nat_rtsp_hook)(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen,struct ip_ct_rtsp_expect* prtspexp, ++ struct nf_conntrack_expect *exp); ++void (*nf_nat_rtsp_hook_expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp); ++ ++EXPORT_SYMBOL_GPL(nf_nat_rtsp_hook); ++ ++/* ++ * Max mappings we will allow for one RTSP connection (for RTP, the number ++ * of allocated ports is twice this value). Note that SMIL burns a lot of ++ * ports so keep this reasonably high. If this is too low, you will see a ++ * lot of "no free client map entries" messages. ++ */ ++#define MAX_PORT_MAPS 16 ++ ++/*** default port list was here in the masq code: 554, 3030, 4040 ***/ ++ ++#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } ++ ++/* ++ * Parse an RTSP packet. ++ * ++ * Returns zero if parsing failed. ++ * ++ * Parameters: ++ * IN ptcp tcp data pointer ++ * IN tcplen tcp data len ++ * IN/OUT ptcpoff points to current tcp offset ++ * OUT phdrsoff set to offset of rtsp headers ++ * OUT phdrslen set to length of rtsp headers ++ * OUT pcseqoff set to offset of CSeq header ++ * OUT pcseqlen set to length of CSeq header ++ */ ++static int ++rtsp_parse_message(char* ptcp, uint tcplen, uint* ptcpoff, ++ uint* phdrsoff, uint* phdrslen, ++ uint* pcseqoff, uint* pcseqlen, ++ uint* transoff, uint* translen) ++{ ++ uint entitylen = 0; ++ uint lineoff; ++ uint linelen; ++ ++ if (!nf_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) ++ return 0; ++ ++ *phdrsoff = *ptcpoff; ++ while (nf_mime_nextline(ptcp, tcplen, ptcpoff, &lineoff, &linelen)) { ++ if (linelen == 0) { ++ if (entitylen > 0) ++ *ptcpoff += min(entitylen, tcplen - *ptcpoff); ++ break; ++ } ++ if (lineoff+linelen > tcplen) { ++ INFOP("!! overrun !!\n"); ++ break; ++ } ++ ++ if (nf_strncasecmp(ptcp+lineoff, "CSeq:", 5) == 0) { ++ *pcseqoff = lineoff; ++ *pcseqlen = linelen; ++ } ++ ++ if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) { ++ *transoff = lineoff; ++ *translen = linelen; ++ } ++ ++ if (nf_strncasecmp(ptcp+lineoff, "Content-Length:", 15) == 0) { ++ uint off = lineoff+15; ++ SKIP_WSPACE(ptcp+lineoff, linelen, off); ++ nf_strtou32(ptcp+off, &entitylen); ++ } ++ } ++ *phdrslen = (*ptcpoff) - (*phdrsoff); ++ ++ return 1; ++} ++ ++/* ++ * Find lo/hi client ports (if any) in transport header ++ * In: ++ * ptcp, tcplen = packet ++ * tranoff, tranlen = buffer to search ++ * ++ * Out: ++ * pport_lo, pport_hi = lo/hi ports (host endian) ++ * ++ * Returns nonzero if any client ports found ++ * ++ * Note: it is valid (and expected) for the client to request multiple ++ * transports, so we need to parse the entire line. ++ */ ++static int ++rtsp_parse_transport(char* ptran, uint tranlen, ++ struct ip_ct_rtsp_expect* prtspexp) ++{ ++ int rc = 0; ++ uint off = 0; ++ ++ if (tranlen < 10 || !iseol(ptran[tranlen-1]) || ++ nf_strncasecmp(ptran, "Transport:", 10) != 0) { ++ INFOP("sanity check failed\n"); ++ return 0; ++ } ++ ++ DEBUGP("tran='%.*s'\n", (int)tranlen, ptran); ++ off += 10; ++ SKIP_WSPACE(ptran, tranlen, off); ++ ++ /* Transport: tran;field;field=val,tran;field;field=val,... */ ++ while (off < tranlen) { ++ const char* pparamend; ++ uint nextparamoff; ++ ++ pparamend = memchr(ptran+off, ',', tranlen-off); ++ pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; ++ nextparamoff = pparamend-ptran; ++ ++ while (off < nextparamoff) { ++ const char* pfieldend; ++ uint nextfieldoff; ++ ++ pfieldend = memchr(ptran+off, ';', nextparamoff-off); ++ nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; ++ ++ if (strncmp(ptran+off, "client_port=", 12) == 0) { ++ u_int16_t port; ++ uint numlen; ++ ++ off += 12; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ if (prtspexp->loport != 0 && prtspexp->loport != port) ++ DEBUGP("multiple ports found, port %hu ignored\n", port); ++ else { ++ DEBUGP("lo port found : %hu\n", port); ++ prtspexp->loport = prtspexp->hiport = port; ++ if (ptran[off] == '-') { ++ off++; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ prtspexp->pbtype = pb_range; ++ prtspexp->hiport = port; ++ ++ // If we have a range, assume rtp: ++ // loport must be even, hiport must be loport+1 ++ if ((prtspexp->loport & 0x0001) != 0 || ++ prtspexp->hiport != prtspexp->loport+1) { ++ DEBUGP("incorrect range: %hu-%hu, correcting\n", ++ prtspexp->loport, prtspexp->hiport); ++ prtspexp->loport &= 0xfffe; ++ prtspexp->hiport = prtspexp->loport+1; ++ } ++ } else if (ptran[off] == '/') { ++ off++; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ prtspexp->pbtype = pb_discon; ++ prtspexp->hiport = port; ++ } ++ rc = 1; ++ } ++ } ++ ++ /* ++ * Note we don't look for the destination parameter here. ++ * If we are using NAT, the NAT module will handle it. If not, ++ * and the client is sending packets elsewhere, the expectation ++ * will quietly time out. ++ */ ++ ++ off = nextfieldoff; ++ } ++ ++ off = nextparamoff; ++ } ++ ++ return rc; ++} ++ ++void expected(struct nf_conn *ct, struct nf_conntrack_expect *exp) ++{ ++ if(nf_nat_rtsp_hook_expectfn) { ++ nf_nat_rtsp_hook_expectfn(ct,exp); ++ } ++} ++ ++/*** conntrack functions ***/ ++ ++/* outbound packet: client->server */ ++ ++static inline int ++help_out(struct sk_buff **pskb, unsigned char *rb_ptr, unsigned int datalen, ++ struct nf_conn *ct, enum ip_conntrack_info ctinfo) ++{ ++ struct ip_ct_rtsp_expect expinfo; ++ ++ int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */ ++ //struct tcphdr* tcph = (void*)iph + iph->ihl * 4; ++ //uint tcplen = pktlen - iph->ihl * 4; ++ char* pdata = rb_ptr; ++ //uint datalen = tcplen - tcph->doff * 4; ++ uint dataoff = 0; ++ int ret = NF_ACCEPT; ++ ++ struct nf_conntrack_expect *exp; ++ ++ __be16 be_loport; ++ ++ memset(&expinfo, 0, sizeof(expinfo)); ++ ++ while (dataoff < datalen) { ++ uint cmdoff = dataoff; ++ uint hdrsoff = 0; ++ uint hdrslen = 0; ++ uint cseqoff = 0; ++ uint cseqlen = 0; ++ uint transoff = 0; ++ uint translen = 0; ++ uint off; ++ ++ if (!rtsp_parse_message(pdata, datalen, &dataoff, ++ &hdrsoff, &hdrslen, ++ &cseqoff, &cseqlen, ++ &transoff, &translen)) ++ break; /* not a valid message */ ++ ++ if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0) ++ continue; /* not a SETUP message */ ++ DEBUGP("found a setup message\n"); ++ ++ off = 0; ++ if(translen) { ++ rtsp_parse_transport(pdata+transoff, translen, &expinfo); ++ } ++ ++ if (expinfo.loport == 0) { ++ DEBUGP("no udp transports found\n"); ++ continue; /* no udp transports found */ ++ } ++ ++ DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n", ++ (int)expinfo.pbtype, expinfo.loport, expinfo.hiport); ++ ++ exp = nf_ct_expect_alloc(ct); ++ if (!exp) { ++ ret = NF_DROP; ++ goto out; ++ } ++ ++ be_loport = htons(expinfo.loport); ++ ++ nf_ct_expect_init(exp, ct->tuplehash[!dir].tuple.src.l3num, ++ &ct->tuplehash[!dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3, ++ IPPROTO_UDP, NULL, &be_loport); ++ ++ exp->master = ct; ++ ++ exp->expectfn = expected; ++ exp->flags = 0; ++ ++ if (expinfo.pbtype == pb_range) { ++ DEBUGP("Changing expectation mask to handle multiple ports\n"); ++ exp->mask.src.u.udp.port = 0xfffe; ++ } ++ ++ DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", ++ NIPQUAD(exp->tuple.src.u3.ip), ++ ntohs(exp->tuple.src.u.udp.port), ++ NIPQUAD(exp->tuple.dst.u3.ip), ++ ntohs(exp->tuple.dst.u.udp.port)); ++ ++ if (nf_nat_rtsp_hook) ++ /* pass the request off to the nat helper */ ++ ret = nf_nat_rtsp_hook(pskb, ctinfo, hdrsoff, hdrslen, &expinfo, exp); ++ else if (nf_ct_expect_related(exp) != 0) { ++ INFOP("nf_ct_expect_related failed\n"); ++ ret = NF_DROP; ++ } ++ nf_ct_expect_put(exp); ++ goto out; ++ } ++out: ++ ++ return ret; ++} ++ ++ ++static inline int ++help_in(struct sk_buff **pskb, size_t pktlen, ++ struct nf_conn* ct, enum ip_conntrack_info ctinfo) ++{ ++ return NF_ACCEPT; ++} ++ ++static int help(struct sk_buff **pskb, unsigned int protoff, ++ struct nf_conn *ct, enum ip_conntrack_info ctinfo) ++{ ++ struct tcphdr _tcph, *th; ++ unsigned int dataoff, datalen; ++ char *rb_ptr; ++ int ret = NF_DROP; ++ ++ /* Until there's been traffic both ways, don't look in packets. */ ++ if (ctinfo != IP_CT_ESTABLISHED && ++ ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { ++ DEBUGP("conntrackinfo = %u\n", ctinfo); ++ return NF_ACCEPT; ++ } ++ ++ /* Not whole TCP header? */ ++ th = skb_header_pointer(*pskb,protoff, sizeof(_tcph), &_tcph); ++ ++ if (!th) ++ return NF_ACCEPT; ++ ++ /* No data ? */ ++ dataoff = protoff + th->doff*4; ++ datalen = (*pskb)->len - dataoff; ++ if (dataoff >= (*pskb)->len) ++ return NF_ACCEPT; ++ ++ spin_lock_bh(&rtsp_buffer_lock); ++ rb_ptr = skb_header_pointer(*pskb, dataoff, ++ (*pskb)->len - dataoff, rtsp_buffer); ++ BUG_ON(rb_ptr == NULL); ++ ++#if 0 ++ /* Checksum invalid? Ignore. */ ++ /* FIXME: Source route IP option packets --RR */ ++ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, ++ csum_partial((char*)tcph, tcplen, 0))) ++ { ++ DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", ++ tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); ++ return NF_ACCEPT; ++ } ++#endif ++ ++ switch (CTINFO2DIR(ctinfo)) { ++ case IP_CT_DIR_ORIGINAL: ++ ret = help_out(pskb, rb_ptr, datalen, ct, ctinfo); ++ break; ++ case IP_CT_DIR_REPLY: ++ DEBUGP("IP_CT_DIR_REPLY\n"); ++ /* inbound packet: server->client */ ++ ret = NF_ACCEPT; ++ break; ++ } ++ ++ spin_unlock_bh(&rtsp_buffer_lock); ++ ++ return ret; ++} ++ ++static struct nf_conntrack_helper rtsp_helpers[MAX_PORTS]; ++static char rtsp_names[MAX_PORTS][10]; ++ ++/* This function is intentionally _NOT_ defined as __exit */ ++static void ++fini(void) ++{ ++ int i; ++ for (i = 0; i < num_ports; i++) { ++ DEBUGP("unregistering port %d\n", ports[i]); ++ nf_conntrack_helper_unregister(&rtsp_helpers[i]); ++ } ++ kfree(rtsp_buffer); ++} ++ ++static int __init ++init(void) ++{ ++ int i, ret; ++ struct nf_conntrack_helper *hlpr; ++ char *tmpname; ++ ++ printk("nf_conntrack_rtsp v" IP_NF_RTSP_VERSION " loading\n"); ++ ++ if (max_outstanding < 1) { ++ printk("nf_conntrack_rtsp: max_outstanding must be a positive integer\n"); ++ return -EBUSY; ++ } ++ if (setup_timeout < 0) { ++ printk("nf_conntrack_rtsp: setup_timeout must be a positive integer\n"); ++ return -EBUSY; ++ } ++ ++ rtsp_buffer = kmalloc(65536, GFP_KERNEL); ++ if (!rtsp_buffer) ++ return -ENOMEM; ++ ++ /* If no port given, default to standard rtsp port */ ++ if (ports[0] == 0) { ++ ports[0] = RTSP_PORT; ++ } ++ ++ for (i = 0; (i < MAX_PORTS) && ports[i]; i++) { ++ hlpr = &rtsp_helpers[i]; ++ memset(hlpr, 0, sizeof(struct nf_conntrack_helper)); ++ hlpr->tuple.src.u.tcp.port = htons(ports[i]); ++ hlpr->tuple.dst.protonum = IPPROTO_TCP; ++ hlpr->max_expected = max_outstanding; ++ hlpr->timeout = setup_timeout; ++ hlpr->me = THIS_MODULE; ++ hlpr->help = help; ++ ++ tmpname = &rtsp_names[i][0]; ++ if (ports[i] == RTSP_PORT) { ++ sprintf(tmpname, "rtsp"); ++ } else { ++ sprintf(tmpname, "rtsp-%d", i); ++ } ++ hlpr->name = tmpname; ++ ++ DEBUGP("port #%d: %d\n", i, ports[i]); ++ ++ ret = nf_conntrack_helper_register(hlpr); ++ ++ if (ret) { ++ printk("nf_conntrack_rtsp: ERROR registering port %d\n", ports[i]); ++ fini(); ++ return -EBUSY; ++ } ++ num_ports++; ++ } ++ return 0; ++} ++ ++module_init(init); ++module_exit(fini); ++ ++EXPORT_SYMBOL(nf_nat_rtsp_hook_expectfn); ++ +--- /dev/null ++++ b/net/ipv4/netfilter/nf_nat_rtsp.c +@@ -0,0 +1,496 @@ ++/* ++ * RTSP extension for TCP NAT alteration ++ * (C) 2003 by Tom Marshall ++ * based on ip_nat_irc.c ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Module load syntax: ++ * insmod nf_nat_rtsp.o ports=port1,port2,...port ++ * stunaddr=
++ * destaction=[auto|strip|none] ++ * ++ * If no ports are specified, the default will be port 554 only. ++ * ++ * stunaddr specifies the address used to detect that a client is using STUN. ++ * If this address is seen in the destination parameter, it is assumed that ++ * the client has already punched a UDP hole in the firewall, so we don't ++ * mangle the client_port. If none is specified, it is autodetected. It ++ * only needs to be set if you have multiple levels of NAT. It should be ++ * set to the external address that the STUN clients detect. Note that in ++ * this case, it will not be possible for clients to use UDP with servers ++ * between the NATs. ++ * ++ * If no destaction is specified, auto is used. ++ * destaction=auto: strip destination parameter if it is not stunaddr. ++ * destaction=strip: always strip destination parameter (not recommended). ++ * destaction=none: do not touch destination parameter (not recommended). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#define NF_NEED_STRNCASECMP ++#define NF_NEED_STRTOU16 ++#include ++#define NF_NEED_MIME_NEXTLINE ++#include ++ ++#define INFOP(fmt, args...) printk(KERN_INFO "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#if 0 ++#define DEBUGP(fmt, args...) printk(KERN_DEBUG "%s: %s: " fmt, __FILE__, __FUNCTION__ , ## args) ++#else ++#define DEBUGP(fmt, args...) ++#endif ++ ++#define MAX_PORTS 8 ++#define DSTACT_AUTO 0 ++#define DSTACT_STRIP 1 ++#define DSTACT_NONE 2 ++ ++static char* stunaddr = NULL; ++static char* destaction = NULL; ++ ++static u_int32_t extip = 0; ++static int dstact = 0; ++ ++MODULE_AUTHOR("Tom Marshall "); ++MODULE_DESCRIPTION("RTSP network address translation module"); ++MODULE_LICENSE("GPL"); ++module_param(stunaddr, charp, 0644); ++MODULE_PARM_DESC(stunaddr, "Address for detecting STUN"); ++module_param(destaction, charp, 0644); ++MODULE_PARM_DESC(destaction, "Action for destination parameter (auto/strip/none)"); ++ ++#define SKIP_WSPACE(ptr,len,off) while(off < len && isspace(*(ptr+off))) { off++; } ++ ++/*** helper functions ***/ ++ ++static void ++get_skb_tcpdata(struct sk_buff* skb, char** pptcpdata, uint* ptcpdatalen) ++{ ++ struct iphdr* iph = ip_hdr(skb); ++ struct tcphdr* tcph = (void *)iph + ip_hdrlen(skb); ++ ++ *pptcpdata = (char*)tcph + tcph->doff*4; ++ *ptcpdatalen = ((char*)skb_transport_header(skb) + skb->len) - *pptcpdata; ++} ++ ++/*** nat functions ***/ ++ ++/* ++ * Mangle the "Transport:" header: ++ * - Replace all occurences of "client_port=" ++ * - Handle destination parameter ++ * ++ * In: ++ * ct, ctinfo = conntrack context ++ * pskb = packet ++ * tranoff = Transport header offset from TCP data ++ * tranlen = Transport header length (incl. CRLF) ++ * rport_lo = replacement low port (host endian) ++ * rport_hi = replacement high port (host endian) ++ * ++ * Returns packet size difference. ++ * ++ * Assumes that a complete transport header is present, ending with CR or LF ++ */ ++static int ++rtsp_mangle_tran(enum ip_conntrack_info ctinfo, ++ struct nf_conntrack_expect* exp, ++ struct ip_ct_rtsp_expect* prtspexp, ++ struct sk_buff** pskb, uint tranoff, uint tranlen) ++{ ++ char* ptcp; ++ uint tcplen; ++ char* ptran; ++ char rbuf1[16]; /* Replacement buffer (one port) */ ++ uint rbuf1len; /* Replacement len (one port) */ ++ char rbufa[16]; /* Replacement buffer (all ports) */ ++ uint rbufalen; /* Replacement len (all ports) */ ++ u_int32_t newip; ++ u_int16_t loport, hiport; ++ uint off = 0; ++ uint diff; /* Number of bytes we removed */ ++ ++ struct nf_conn *ct = exp->master; ++ struct nf_conntrack_tuple *t; ++ ++ char szextaddr[15+1]; ++ uint extaddrlen; ++ int is_stun; ++ ++ get_skb_tcpdata(*pskb, &ptcp, &tcplen); ++ ptran = ptcp+tranoff; ++ ++ if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || ++ tranlen < 10 || !iseol(ptran[tranlen-1]) || ++ nf_strncasecmp(ptran, "Transport:", 10) != 0) ++ { ++ INFOP("sanity check failed\n"); ++ return 0; ++ } ++ off += 10; ++ SKIP_WSPACE(ptcp+tranoff, tranlen, off); ++ ++ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; ++ t = &exp->tuple; ++ t->dst.u3.ip = newip; ++ ++ extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip)) ++ : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip)); ++ DEBUGP("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); ++ ++ rbuf1len = rbufalen = 0; ++ switch (prtspexp->pbtype) ++ { ++ case pb_single: ++ for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(loport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ DEBUGP("using port %hu\n", loport); ++ break; ++ } ++ } ++ if (loport != 0) ++ { ++ rbuf1len = sprintf(rbuf1, "%hu", loport); ++ rbufalen = sprintf(rbufa, "%hu", loport); ++ } ++ break; ++ case pb_range: ++ for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(loport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ hiport = loport + ~exp->mask.src.u.udp.port; ++ DEBUGP("using ports %hu-%hu\n", loport, hiport); ++ break; ++ } ++ } ++ if (loport != 0) ++ { ++ rbuf1len = sprintf(rbuf1, "%hu", loport); ++ rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1); ++ } ++ break; ++ case pb_discon: ++ for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(loport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ DEBUGP("using port %hu (1 of 2)\n", loport); ++ break; ++ } ++ } ++ for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ ++ { ++ t->dst.u.udp.port = htons(hiport); ++ if (nf_ct_expect_related(exp) == 0) ++ { ++ DEBUGP("using port %hu (2 of 2)\n", hiport); ++ break; ++ } ++ } ++ if (loport != 0 && hiport != 0) ++ { ++ rbuf1len = sprintf(rbuf1, "%hu", loport); ++ if (hiport == loport+1) ++ { ++ rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); ++ } ++ else ++ { ++ rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport); ++ } ++ } ++ break; ++ } ++ ++ if (rbuf1len == 0) ++ { ++ return 0; /* cannot get replacement port(s) */ ++ } ++ ++ /* Transport: tran;field;field=val,tran;field;field=val,... */ ++ while (off < tranlen) ++ { ++ uint saveoff; ++ const char* pparamend; ++ uint nextparamoff; ++ ++ pparamend = memchr(ptran+off, ',', tranlen-off); ++ pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; ++ nextparamoff = pparamend-ptcp; ++ ++ /* ++ * We pass over each param twice. On the first pass, we look for a ++ * destination= field. It is handled by the security policy. If it ++ * is present, allowed, and equal to our external address, we assume ++ * that STUN is being used and we leave the client_port= field alone. ++ */ ++ is_stun = 0; ++ saveoff = off; ++ while (off < nextparamoff) ++ { ++ const char* pfieldend; ++ uint nextfieldoff; ++ ++ pfieldend = memchr(ptran+off, ';', nextparamoff-off); ++ nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; ++ ++ if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) ++ { ++ if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) ++ { ++ is_stun = 1; ++ } ++ if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) ++ { ++ diff = nextfieldoff-off; ++ if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, ++ off, diff, NULL, 0)) ++ { ++ /* mangle failed, all we can do is bail */ ++ nf_ct_unexpect_related(exp); ++ return 0; ++ } ++ get_skb_tcpdata(*pskb, &ptcp, &tcplen); ++ ptran = ptcp+tranoff; ++ tranlen -= diff; ++ nextparamoff -= diff; ++ nextfieldoff -= diff; ++ } ++ } ++ ++ off = nextfieldoff; ++ } ++ if (is_stun) ++ { ++ continue; ++ } ++ off = saveoff; ++ while (off < nextparamoff) ++ { ++ const char* pfieldend; ++ uint nextfieldoff; ++ ++ pfieldend = memchr(ptran+off, ';', nextparamoff-off); ++ nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; ++ ++ if (strncmp(ptran+off, "client_port=", 12) == 0) ++ { ++ u_int16_t port; ++ uint numlen; ++ uint origoff; ++ uint origlen; ++ char* rbuf = rbuf1; ++ uint rbuflen = rbuf1len; ++ ++ off += 12; ++ origoff = (ptran-ptcp)+off; ++ origlen = 0; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ origlen += numlen; ++ if (port != prtspexp->loport) ++ { ++ DEBUGP("multiple ports found, port %hu ignored\n", port); ++ } ++ else ++ { ++ if (ptran[off] == '-' || ptran[off] == '/') ++ { ++ off++; ++ origlen++; ++ numlen = nf_strtou16(ptran+off, &port); ++ off += numlen; ++ origlen += numlen; ++ rbuf = rbufa; ++ rbuflen = rbufalen; ++ } ++ ++ /* ++ * note we cannot just memcpy() if the sizes are the same. ++ * the mangle function does skb resizing, checks for a ++ * cloned skb, and updates the checksums. ++ * ++ * parameter 4 below is offset from start of tcp data. ++ */ ++ diff = origlen-rbuflen; ++ if (!nf_nat_mangle_tcp_packet(pskb, ct, ctinfo, ++ origoff, origlen, rbuf, rbuflen)) ++ { ++ /* mangle failed, all we can do is bail */ ++ nf_ct_unexpect_related(exp); ++ return 0; ++ } ++ get_skb_tcpdata(*pskb, &ptcp, &tcplen); ++ ptran = ptcp+tranoff; ++ tranlen -= diff; ++ nextparamoff -= diff; ++ nextfieldoff -= diff; ++ } ++ } ++ ++ off = nextfieldoff; ++ } ++ ++ off = nextparamoff; ++ } ++ ++ return 1; ++} ++ ++static uint ++help_out(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, ++ struct nf_conntrack_expect* exp) ++{ ++ char* ptcp; ++ uint tcplen; ++ uint hdrsoff; ++ uint hdrslen; ++ uint lineoff; ++ uint linelen; ++ uint off; ++ ++ //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph; ++ //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4); ++ ++ get_skb_tcpdata(*pskb, &ptcp, &tcplen); ++ hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq); ++ hdrslen = matchlen; ++ off = hdrsoff; ++ DEBUGP("NAT rtsp help_out\n"); ++ ++ while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) ++ { ++ if (linelen == 0) ++ { ++ break; ++ } ++ if (off > hdrsoff+hdrslen) ++ { ++ INFOP("!! overrun !!"); ++ break; ++ } ++ DEBUGP("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); ++ ++ if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) ++ { ++ uint oldtcplen = tcplen; ++ DEBUGP("hdr: Transport\n"); ++ if (!rtsp_mangle_tran(ctinfo, exp, prtspexp, pskb, lineoff, linelen)) ++ { ++ DEBUGP("hdr: Transport mangle failed"); ++ break; ++ } ++ get_skb_tcpdata(*pskb, &ptcp, &tcplen); ++ hdrslen -= (oldtcplen-tcplen); ++ off -= (oldtcplen-tcplen); ++ lineoff -= (oldtcplen-tcplen); ++ linelen -= (oldtcplen-tcplen); ++ DEBUGP("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); ++ } ++ } ++ ++ return NF_ACCEPT; ++} ++ ++static unsigned int ++help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, ++ unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, ++ struct nf_conntrack_expect* exp) ++{ ++ int dir = CTINFO2DIR(ctinfo); ++ int rc = NF_ACCEPT; ++ ++ switch (dir) ++ { ++ case IP_CT_DIR_ORIGINAL: ++ rc = help_out(pskb, ctinfo, matchoff, matchlen, prtspexp, exp); ++ break; ++ case IP_CT_DIR_REPLY: ++ DEBUGP("unmangle ! %u\n", ctinfo); ++ /* XXX: unmangle */ ++ rc = NF_ACCEPT; ++ break; ++ } ++ //UNLOCK_BH(&ip_rtsp_lock); ++ ++ return rc; ++} ++ ++static void expected(struct nf_conn* ct, struct nf_conntrack_expect *exp) ++{ ++ struct nf_nat_multi_range_compat mr; ++ u_int32_t newdstip, newsrcip, newip; ++ ++ struct nf_conn *master = ct->master; ++ ++ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; ++ newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; ++ //FIXME (how to port that ?) ++ //code from 2.4 : newip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ? newsrcip : newdstip; ++ newip = newdstip; ++ ++ DEBUGP("newsrcip=%u.%u.%u.%u, newdstip=%u.%u.%u.%u, newip=%u.%u.%u.%u\n", ++ NIPQUAD(newsrcip), NIPQUAD(newdstip), NIPQUAD(newip)); ++ ++ mr.rangesize = 1; ++ // We don't want to manip the per-protocol, just the IPs. ++ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; ++ mr.range[0].min_ip = mr.range[0].max_ip = newip; ++ ++ nf_nat_setup_info(ct, &mr.range[0], NF_IP_PRE_ROUTING); ++} ++ ++ ++static void __exit fini(void) ++{ ++ nf_nat_rtsp_hook = NULL; ++ nf_nat_rtsp_hook_expectfn = NULL; ++ synchronize_net(); ++} ++ ++static int __init init(void) ++{ ++ printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n"); ++ ++ BUG_ON(nf_nat_rtsp_hook); ++ nf_nat_rtsp_hook = help; ++ nf_nat_rtsp_hook_expectfn = &expected; ++ ++ if (stunaddr != NULL) ++ extip = in_aton(stunaddr); ++ ++ if (destaction != NULL) { ++ if (strcmp(destaction, "auto") == 0) ++ dstact = DSTACT_AUTO; ++ ++ if (strcmp(destaction, "strip") == 0) ++ dstact = DSTACT_STRIP; ++ ++ if (strcmp(destaction, "none") == 0) ++ dstact = DSTACT_NONE; ++ } ++ ++ return 0; ++} ++ ++module_init(init); ++module_exit(fini); diff --git a/target/linux/generic-2.6/patches-2.6.23/200-sched_esfq.patch b/target/linux/generic-2.6/patches-2.6.23/200-sched_esfq.patch new file mode 100644 index 0000000000..6a737926af --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/200-sched_esfq.patch @@ -0,0 +1,795 @@ +--- a/include/linux/pkt_sched.h ++++ b/include/linux/pkt_sched.h +@@ -155,8 +155,37 @@ + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. ++ * ++ * If you need to play with these values, use esfq instead. + */ + ++/* ESFQ section */ ++ ++enum ++{ ++ /* traditional */ ++ TCA_SFQ_HASH_CLASSIC, ++ TCA_SFQ_HASH_DST, ++ TCA_SFQ_HASH_SRC, ++ TCA_SFQ_HASH_FWMARK, ++ /* conntrack */ ++ TCA_SFQ_HASH_CTORIGDST, ++ TCA_SFQ_HASH_CTORIGSRC, ++ TCA_SFQ_HASH_CTREPLDST, ++ TCA_SFQ_HASH_CTREPLSRC, ++ TCA_SFQ_HASH_CTNATCHG, ++}; ++ ++struct tc_esfq_qopt ++{ ++ unsigned quantum; /* Bytes per round allocated to flow */ ++ int perturb_period; /* Period of hash perturbation */ ++ __u32 limit; /* Maximal packets in queue */ ++ unsigned divisor; /* Hash divisor */ ++ unsigned flows; /* Maximal number of flows */ ++ unsigned hash_kind; /* Hash function to use for flow identification */ ++}; ++ + /* RED section */ + + enum +--- a/net/sched/Kconfig ++++ b/net/sched/Kconfig +@@ -144,6 +144,37 @@ + To compile this code as a module, choose M here: the + module will be called sch_sfq. + ++config NET_SCH_ESFQ ++ tristate "Enhanced Stochastic Fairness Queueing (ESFQ)" ++ ---help--- ++ Say Y here if you want to use the Enhanced Stochastic Fairness ++ Queueing (ESFQ) packet scheduling algorithm for some of your network ++ devices or as a leaf discipline for a classful qdisc such as HTB or ++ CBQ (see the top of for details and ++ references to the SFQ algorithm). ++ ++ This is an enchanced SFQ version which allows you to control some ++ hardcoded values in the SFQ scheduler. ++ ++ ESFQ also adds control of the hash function used to identify packet ++ flows. The original SFQ discipline hashes by connection; ESFQ add ++ several other hashing methods, such as by src IP or by dst IP, which ++ can be more fair to users in some networking situations. ++ ++ To compile this code as a module, choose M here: the ++ module will be called sch_esfq. ++ ++config NET_SCH_ESFQ_NFCT ++ bool "Connection Tracking Hash Types" ++ depends on NET_SCH_ESFQ && NF_CONNTRACK ++ ---help--- ++ Say Y here to enable support for hashing based on netfilter connection ++ tracking information. This is useful for a router that is also using ++ NAT to connect privately-addressed hosts to the Internet. If you want ++ to provide fair distribution of upstream bandwidth, ESFQ must use ++ connection tracking information, since all outgoing packets will share ++ the same source address. ++ + config NET_SCH_TEQL + tristate "True Link Equalizer (TEQL)" + ---help--- +--- a/net/sched/Makefile ++++ b/net/sched/Makefile +@@ -22,6 +22,7 @@ + obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o + obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o + obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o ++obj-$(CONFIG_NET_SCH_ESFQ) += sch_esfq.o + obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o + obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o + obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o +--- /dev/null ++++ b/net/sched/sch_esfq.c +@@ -0,0 +1,702 @@ ++/* ++ * net/sched/sch_esfq.c Extended Stochastic Fairness Queueing discipline. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Alexey Kuznetsov, ++ * ++ * Changes: Alexander Atanasov, ++ * Added dynamic depth,limit,divisor,hash_kind options. ++ * Added dst and src hashes. ++ * ++ * Alexander Clouter, ++ * Ported ESFQ to Linux 2.6. ++ * ++ * Corey Hickey, ++ * Maintenance of the Linux 2.6 port. ++ * Added fwmark hash (thanks to Robert Kurjata). ++ * Added usage of jhash. ++ * Added conntrack support. ++ * Added ctnatchg hash (thanks to Ben Pfountz). ++ */ ++ ++#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 ++ ++/* Stochastic Fairness Queuing algorithm. ++ For more comments look at sch_sfq.c. ++ The difference is that you can change limit, depth, ++ hash table size and choose alternate hash types. ++ ++ classic: same as in sch_sfq.c ++ dst: destination IP address ++ src: source IP address ++ fwmark: netfilter mark value ++ ctorigdst: original destination IP address ++ ctorigsrc: original source IP address ++ ctrepldst: reply destination IP address ++ ctreplsrc: reply source IP ++ ++*/ ++ ++#define ESFQ_HEAD 0 ++#define ESFQ_TAIL 1 ++ ++/* This type should contain at least SFQ_DEPTH*2 values */ ++typedef unsigned int esfq_index; ++ ++struct esfq_head ++{ ++ esfq_index next; ++ esfq_index prev; ++}; ++ ++struct esfq_sched_data ++{ ++/* Parameters */ ++ int perturb_period; ++ unsigned quantum; /* Allotment per round: MUST BE >= MTU */ ++ int limit; ++ unsigned depth; ++ unsigned hash_divisor; ++ unsigned hash_kind; ++/* Variables */ ++ struct timer_list perturb_timer; ++ int perturbation; ++ esfq_index tail; /* Index of current slot in round */ ++ esfq_index max_depth; /* Maximal depth */ ++ ++ esfq_index *ht; /* Hash table */ ++ esfq_index *next; /* Active slots link */ ++ short *allot; /* Current allotment per slot */ ++ unsigned short *hash; /* Hash value indexed by slots */ ++ struct sk_buff_head *qs; /* Slot queue */ ++ struct esfq_head *dep; /* Linked list of slots, indexed by depth */ ++}; ++ ++/* This contains the info we will hash. */ ++struct esfq_packet_info ++{ ++ u32 proto; /* protocol or port */ ++ u32 src; /* source from packet header */ ++ u32 dst; /* destination from packet header */ ++ u32 ctorigsrc; /* original source from conntrack */ ++ u32 ctorigdst; /* original destination from conntrack */ ++ u32 ctreplsrc; /* reply source from conntrack */ ++ u32 ctrepldst; /* reply destination from conntrack */ ++ u32 mark; /* netfilter mark (fwmark) */ ++}; ++ ++static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a) ++{ ++ return jhash_1word(a, q->perturbation) & (q->hash_divisor-1); ++} ++ ++static __inline__ unsigned esfq_jhash_2words(struct esfq_sched_data *q, u32 a, u32 b) ++{ ++ return jhash_2words(a, b, q->perturbation) & (q->hash_divisor-1); ++} ++ ++static __inline__ unsigned esfq_jhash_3words(struct esfq_sched_data *q, u32 a, u32 b, u32 c) ++{ ++ return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1); ++} ++ ++static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb) ++{ ++ struct esfq_packet_info info; ++#ifdef CONFIG_NET_SCH_ESFQ_NFCT ++ enum ip_conntrack_info ctinfo; ++ struct nf_conn *ct = nf_ct_get(skb, &ctinfo); ++#endif ++ ++ switch (skb->protocol) { ++ case __constant_htons(ETH_P_IP): ++ { ++ struct iphdr *iph = ip_hdr(skb); ++ info.dst = iph->daddr; ++ info.src = iph->saddr; ++ if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && ++ (iph->protocol == IPPROTO_TCP || ++ iph->protocol == IPPROTO_UDP || ++ iph->protocol == IPPROTO_SCTP || ++ iph->protocol == IPPROTO_DCCP || ++ iph->protocol == IPPROTO_ESP)) ++ info.proto = *(((u32*)iph) + iph->ihl); ++ else ++ info.proto = iph->protocol; ++ break; ++ } ++ case __constant_htons(ETH_P_IPV6): ++ { ++ struct ipv6hdr *iph = ipv6_hdr(skb); ++ /* Hash ipv6 addresses into a u32. This isn't ideal, ++ * but the code is simple. */ ++ info.dst = jhash2(iph->daddr.s6_addr32, 4, q->perturbation); ++ info.src = jhash2(iph->saddr.s6_addr32, 4, q->perturbation); ++ if (iph->nexthdr == IPPROTO_TCP || ++ iph->nexthdr == IPPROTO_UDP || ++ iph->nexthdr == IPPROTO_SCTP || ++ iph->nexthdr == IPPROTO_DCCP || ++ iph->nexthdr == IPPROTO_ESP) ++ info.proto = *(u32*)&iph[1]; ++ else ++ info.proto = iph->nexthdr; ++ break; ++ } ++ default: ++ info.dst = (u32)(unsigned long)skb->dst; ++ info.src = (u32)(unsigned long)skb->sk; ++ info.proto = skb->protocol; ++ } ++ ++ info.mark = skb->mark; ++ ++#ifdef CONFIG_NET_SCH_ESFQ_NFCT ++ /* defaults if there is no conntrack info */ ++ info.ctorigsrc = info.src; ++ info.ctorigdst = info.dst; ++ info.ctreplsrc = info.dst; ++ info.ctrepldst = info.src; ++ /* collect conntrack info */ ++ if (ct && ct != &nf_conntrack_untracked) { ++ if (skb->protocol == __constant_htons(ETH_P_IP)) { ++ info.ctorigsrc = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; ++ info.ctorigdst = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip; ++ info.ctreplsrc = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip; ++ info.ctrepldst = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; ++ } ++ else if (skb->protocol == __constant_htons(ETH_P_IPV6)) { ++ /* Again, hash ipv6 addresses into a single u32. */ ++ info.ctorigsrc = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6, 4, q->perturbation); ++ info.ctorigdst = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip6, 4, q->perturbation); ++ info.ctreplsrc = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6, 4, q->perturbation); ++ info.ctrepldst = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6, 4, q->perturbation); ++ } ++ ++ } ++#endif ++ ++ switch(q->hash_kind) { ++ case TCA_SFQ_HASH_CLASSIC: ++ return esfq_jhash_3words(q, info.dst, info.src, info.proto); ++ case TCA_SFQ_HASH_DST: ++ return esfq_jhash_1word(q, info.dst); ++ case TCA_SFQ_HASH_SRC: ++ return esfq_jhash_1word(q, info.src); ++ case TCA_SFQ_HASH_FWMARK: ++ return esfq_jhash_1word(q, info.mark); ++#ifdef CONFIG_NET_SCH_ESFQ_NFCT ++ case TCA_SFQ_HASH_CTORIGDST: ++ return esfq_jhash_1word(q, info.ctorigdst); ++ case TCA_SFQ_HASH_CTORIGSRC: ++ return esfq_jhash_1word(q, info.ctorigsrc); ++ case TCA_SFQ_HASH_CTREPLDST: ++ return esfq_jhash_1word(q, info.ctrepldst); ++ case TCA_SFQ_HASH_CTREPLSRC: ++ return esfq_jhash_1word(q, info.ctreplsrc); ++ case TCA_SFQ_HASH_CTNATCHG: ++ { ++ if (info.ctorigdst == info.ctreplsrc) ++ return esfq_jhash_1word(q, info.ctorigsrc); ++ return esfq_jhash_1word(q, info.ctreplsrc); ++ } ++#endif ++ default: ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Unknown hash method. Falling back to classic.\n"); ++ } ++ return esfq_jhash_3words(q, info.dst, info.src, info.proto); ++} ++ ++static inline void esfq_link(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ int d = q->qs[x].qlen + q->depth; ++ ++ p = d; ++ n = q->dep[d].next; ++ q->dep[x].next = n; ++ q->dep[x].prev = p; ++ q->dep[p].next = q->dep[n].prev = x; ++} ++ ++static inline void esfq_dec(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ ++ n = q->dep[x].next; ++ p = q->dep[x].prev; ++ q->dep[p].next = n; ++ q->dep[n].prev = p; ++ ++ if (n == p && q->max_depth == q->qs[x].qlen + 1) ++ q->max_depth--; ++ ++ esfq_link(q, x); ++} ++ ++static inline void esfq_inc(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ int d; ++ ++ n = q->dep[x].next; ++ p = q->dep[x].prev; ++ q->dep[p].next = n; ++ q->dep[n].prev = p; ++ d = q->qs[x].qlen; ++ if (q->max_depth < d) ++ q->max_depth = d; ++ ++ esfq_link(q, x); ++} ++ ++static unsigned int esfq_drop(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_index d = q->max_depth; ++ struct sk_buff *skb; ++ unsigned int len; ++ ++ /* Queue is full! Find the longest slot and ++ drop a packet from it */ ++ ++ if (d > 1) { ++ esfq_index x = q->dep[d+q->depth].next; ++ skb = q->qs[x].prev; ++ len = skb->len; ++ __skb_unlink(skb, &q->qs[x]); ++ kfree_skb(skb); ++ esfq_dec(q, x); ++ sch->q.qlen--; ++ sch->qstats.drops++; ++ sch->qstats.backlog -= len; ++ return len; ++ } ++ ++ if (d == 1) { ++ /* It is difficult to believe, but ALL THE SLOTS HAVE LENGTH 1. */ ++ d = q->next[q->tail]; ++ q->next[q->tail] = q->next[d]; ++ q->allot[q->next[d]] += q->quantum; ++ skb = q->qs[d].prev; ++ len = skb->len; ++ __skb_unlink(skb, &q->qs[d]); ++ kfree_skb(skb); ++ esfq_dec(q, d); ++ sch->q.qlen--; ++ q->ht[q->hash[d]] = q->depth; ++ sch->qstats.drops++; ++ sch->qstats.backlog -= len; ++ return len; ++ } ++ ++ return 0; ++} ++ ++static void esfq_q_enqueue(struct sk_buff *skb, struct esfq_sched_data *q, unsigned int end) ++{ ++ unsigned hash = esfq_hash(q, skb); ++ unsigned depth = q->depth; ++ esfq_index x; ++ ++ x = q->ht[hash]; ++ if (x == depth) { ++ q->ht[hash] = x = q->dep[depth].next; ++ q->hash[x] = hash; ++ } ++ ++ if (end == ESFQ_TAIL) ++ __skb_queue_tail(&q->qs[x], skb); ++ else ++ __skb_queue_head(&q->qs[x], skb); ++ ++ esfq_inc(q, x); ++ if (q->qs[x].qlen == 1) { /* The flow is new */ ++ if (q->tail == depth) { /* It is the first flow */ ++ q->tail = x; ++ q->next[x] = x; ++ q->allot[x] = q->quantum; ++ } else { ++ q->next[x] = q->next[q->tail]; ++ q->next[q->tail] = x; ++ q->tail = x; ++ } ++ } ++} ++ ++static int esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_q_enqueue(skb, q, ESFQ_TAIL); ++ sch->qstats.backlog += skb->len; ++ if (++sch->q.qlen < q->limit-1) { ++ sch->bstats.bytes += skb->len; ++ sch->bstats.packets++; ++ return 0; ++ } ++ ++ sch->qstats.drops++; ++ esfq_drop(sch); ++ return NET_XMIT_CN; ++} ++ ++ ++static int esfq_requeue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_q_enqueue(skb, q, ESFQ_HEAD); ++ sch->qstats.backlog += skb->len; ++ if (++sch->q.qlen < q->limit - 1) { ++ sch->qstats.requeues++; ++ return 0; ++ } ++ ++ sch->qstats.drops++; ++ esfq_drop(sch); ++ return NET_XMIT_CN; ++} ++ ++static struct sk_buff *esfq_q_dequeue(struct esfq_sched_data *q) ++{ ++ struct sk_buff *skb; ++ unsigned depth = q->depth; ++ esfq_index a, old_a; ++ ++ /* No active slots */ ++ if (q->tail == depth) ++ return NULL; ++ ++ a = old_a = q->next[q->tail]; ++ ++ /* Grab packet */ ++ skb = __skb_dequeue(&q->qs[a]); ++ esfq_dec(q, a); ++ ++ /* Is the slot empty? */ ++ if (q->qs[a].qlen == 0) { ++ q->ht[q->hash[a]] = depth; ++ a = q->next[a]; ++ if (a == old_a) { ++ q->tail = depth; ++ return skb; ++ } ++ q->next[q->tail] = a; ++ q->allot[a] += q->quantum; ++ } else if ((q->allot[a] -= skb->len) <= 0) { ++ q->tail = a; ++ a = q->next[a]; ++ q->allot[a] += q->quantum; ++ } ++ ++ return skb; ++} ++ ++static struct sk_buff *esfq_dequeue(struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct sk_buff *skb; ++ ++ skb = esfq_q_dequeue(q); ++ if (skb == NULL) ++ return NULL; ++ sch->q.qlen--; ++ sch->qstats.backlog -= skb->len; ++ return skb; ++} ++ ++static void esfq_q_destroy(struct esfq_sched_data *q) ++{ ++ del_timer(&q->perturb_timer); ++ if(q->ht) ++ kfree(q->ht); ++ if(q->dep) ++ kfree(q->dep); ++ if(q->next) ++ kfree(q->next); ++ if(q->allot) ++ kfree(q->allot); ++ if(q->hash) ++ kfree(q->hash); ++ if(q->qs) ++ kfree(q->qs); ++} ++ ++static void esfq_destroy(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_q_destroy(q); ++} ++ ++ ++static void esfq_reset(struct Qdisc* sch) ++{ ++ struct sk_buff *skb; ++ ++ while ((skb = esfq_dequeue(sch)) != NULL) ++ kfree_skb(skb); ++} ++ ++static void esfq_perturbation(unsigned long arg) ++{ ++ struct Qdisc *sch = (struct Qdisc*)arg; ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ ++ q->perturbation = net_random()&0x1F; ++ ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++} ++ ++static unsigned int esfq_check_hash(unsigned int kind) ++{ ++ switch (kind) { ++ case TCA_SFQ_HASH_CTORIGDST: ++ case TCA_SFQ_HASH_CTORIGSRC: ++ case TCA_SFQ_HASH_CTREPLDST: ++ case TCA_SFQ_HASH_CTREPLSRC: ++ case TCA_SFQ_HASH_CTNATCHG: ++#ifndef CONFIG_NET_SCH_ESFQ_NFCT ++ { ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Conntrack hash types disabled in kernel config. Falling back to classic.\n"); ++ return TCA_SFQ_HASH_CLASSIC; ++ } ++#endif ++ case TCA_SFQ_HASH_CLASSIC: ++ case TCA_SFQ_HASH_DST: ++ case TCA_SFQ_HASH_SRC: ++ case TCA_SFQ_HASH_FWMARK: ++ return kind; ++ default: ++ { ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Unknown hash type. Falling back to classic.\n"); ++ return TCA_SFQ_HASH_CLASSIC; ++ } ++ } ++} ++ ++static int esfq_q_init(struct esfq_sched_data *q, struct rtattr *opt) ++{ ++ struct tc_esfq_qopt *ctl = RTA_DATA(opt); ++ esfq_index p = ~0U/2; ++ int i; ++ ++ if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl))) ++ return -EINVAL; ++ ++ q->perturbation = 0; ++ q->hash_kind = TCA_SFQ_HASH_CLASSIC; ++ q->max_depth = 0; ++ if (opt == NULL) { ++ q->perturb_period = 0; ++ q->hash_divisor = 1024; ++ q->tail = q->limit = q->depth = 128; ++ ++ } else { ++ struct tc_esfq_qopt *ctl = RTA_DATA(opt); ++ if (ctl->quantum) ++ q->quantum = ctl->quantum; ++ q->perturb_period = ctl->perturb_period*HZ; ++ q->hash_divisor = ctl->divisor ? : 1024; ++ q->tail = q->limit = q->depth = ctl->flows ? : 128; ++ ++ if ( q->depth > p - 1 ) ++ return -EINVAL; ++ ++ if (ctl->limit) ++ q->limit = min_t(u32, ctl->limit, q->depth); ++ ++ if (ctl->hash_kind) { ++ q->hash_kind = esfq_check_hash(ctl->hash_kind); ++ } ++ } ++ ++ q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL); ++ if (!q->ht) ++ goto err_case; ++ q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL); ++ if (!q->dep) ++ goto err_case; ++ q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL); ++ if (!q->next) ++ goto err_case; ++ q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL); ++ if (!q->allot) ++ goto err_case; ++ q->hash = kmalloc(q->depth*sizeof(unsigned short), GFP_KERNEL); ++ if (!q->hash) ++ goto err_case; ++ q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL); ++ if (!q->qs) ++ goto err_case; ++ ++ for (i=0; i< q->hash_divisor; i++) ++ q->ht[i] = q->depth; ++ for (i=0; idepth; i++) { ++ skb_queue_head_init(&q->qs[i]); ++ q->dep[i+q->depth].next = i+q->depth; ++ q->dep[i+q->depth].prev = i+q->depth; ++ } ++ ++ for (i=0; idepth; i++) ++ esfq_link(q, i); ++ return 0; ++err_case: ++ esfq_q_destroy(q); ++ return -ENOBUFS; ++} ++ ++static int esfq_init(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ int err; ++ ++ q->quantum = psched_mtu(sch->dev); /* default */ ++ if ((err = esfq_q_init(q, opt))) ++ return err; ++ ++ init_timer(&q->perturb_timer); ++ q->perturb_timer.data = (unsigned long)sch; ++ q->perturb_timer.function = esfq_perturbation; ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++ ++ return 0; ++} ++ ++static int esfq_change(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct esfq_sched_data new; ++ struct sk_buff *skb; ++ int err; ++ ++ /* set up new queue */ ++ memset(&new, 0, sizeof(struct esfq_sched_data)); ++ new.quantum = psched_mtu(sch->dev); /* default */ ++ if ((err = esfq_q_init(&new, opt))) ++ return err; ++ ++ /* copy all packets from the old queue to the new queue */ ++ sch_tree_lock(sch); ++ while ((skb = esfq_q_dequeue(q)) != NULL) ++ esfq_q_enqueue(skb, &new, ESFQ_TAIL); ++ ++ /* clean up the old queue */ ++ esfq_q_destroy(q); ++ ++ /* copy elements of the new queue into the old queue */ ++ q->perturb_period = new.perturb_period; ++ q->quantum = new.quantum; ++ q->limit = new.limit; ++ q->depth = new.depth; ++ q->hash_divisor = new.hash_divisor; ++ q->hash_kind = new.hash_kind; ++ q->tail = new.tail; ++ q->max_depth = new.max_depth; ++ q->ht = new.ht; ++ q->dep = new.dep; ++ q->next = new.next; ++ q->allot = new.allot; ++ q->hash = new.hash; ++ q->qs = new.qs; ++ ++ /* finish up */ ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } else { ++ q->perturbation = 0; ++ } ++ sch_tree_unlock(sch); ++ return 0; ++} ++ ++static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned char *b = skb->tail; ++ struct tc_esfq_qopt opt; ++ ++ opt.quantum = q->quantum; ++ opt.perturb_period = q->perturb_period/HZ; ++ ++ opt.limit = q->limit; ++ opt.divisor = q->hash_divisor; ++ opt.flows = q->depth; ++ opt.hash_kind = q->hash_kind; ++ ++ RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); ++ ++ return skb->len; ++ ++rtattr_failure: ++ skb_trim(skb, b - skb->data); ++ return -1; ++} ++ ++static struct Qdisc_ops esfq_qdisc_ops = ++{ ++ .next = NULL, ++ .cl_ops = NULL, ++ .id = "esfq", ++ .priv_size = sizeof(struct esfq_sched_data), ++ .enqueue = esfq_enqueue, ++ .dequeue = esfq_dequeue, ++ .requeue = esfq_requeue, ++ .drop = esfq_drop, ++ .init = esfq_init, ++ .reset = esfq_reset, ++ .destroy = esfq_destroy, ++ .change = esfq_change, ++ .dump = esfq_dump, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init esfq_module_init(void) ++{ ++ return register_qdisc(&esfq_qdisc_ops); ++} ++static void __exit esfq_module_exit(void) ++{ ++ unregister_qdisc(&esfq_qdisc_ops); ++} ++module_init(esfq_module_init) ++module_exit(esfq_module_exit) ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/patches-2.6.23/201-multiple_default_gateways.patch b/target/linux/generic-2.6/patches-2.6.23/201-multiple_default_gateways.patch new file mode 100644 index 0000000000..a13bb198f9 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/201-multiple_default_gateways.patch @@ -0,0 +1,299 @@ +--- a/include/linux/rtnetlink.h ++++ b/include/linux/rtnetlink.h +@@ -293,6 +293,8 @@ + #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ + #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ + #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ ++#define RTNH_F_SUSPECT 8 /* We don't know the real state */ ++#define RTNH_F_BADSTATE (RTNH_F_DEAD | RTNH_F_SUSPECT) + + /* Macros to handle hexthops */ + +--- a/include/net/flow.h ++++ b/include/net/flow.h +@@ -19,6 +19,8 @@ + struct { + __be32 daddr; + __be32 saddr; ++ __u32 lsrc; ++ __u32 gw; + __u8 tos; + __u8 scope; + } ip4_u; +@@ -43,6 +45,8 @@ + #define fl6_flowlabel nl_u.ip6_u.flowlabel + #define fl4_dst nl_u.ip4_u.daddr + #define fl4_src nl_u.ip4_u.saddr ++#define fl4_lsrc nl_u.ip4_u.lsrc ++#define fl4_gw nl_u.ip4_u.gw + #define fl4_tos nl_u.ip4_u.tos + #define fl4_scope nl_u.ip4_u.scope + +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -1116,6 +1116,7 @@ + + /* Gateway is different ... */ + rt->rt_gateway = new_gw; ++ if (rt->fl.fl4_gw) rt->fl.fl4_gw = new_gw; + + /* Redirect received -> path was valid */ + dst_confirm(&rth->u.dst); +@@ -1551,6 +1552,7 @@ + rth->fl.fl4_tos = tos; + rth->fl.mark = skb->mark; + rth->fl.fl4_src = saddr; ++ rth->fl.fl4_lsrc = 0; + rth->rt_src = saddr; + #ifdef CONFIG_NET_CLS_ROUTE + rth->u.dst.tclassid = itag; +@@ -1561,6 +1563,7 @@ + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); + rth->fl.oif = 0; ++ rth->fl.fl4_gw = 0; + rth->rt_gateway = daddr; + rth->rt_spec_dst= spec_dst; + rth->rt_type = RTN_MULTICAST; +@@ -1624,7 +1627,7 @@ + static inline int __mkroute_input(struct sk_buff *skb, + struct fib_result* res, + struct in_device *in_dev, +- __be32 daddr, __be32 saddr, u32 tos, ++ __be32 daddr, __be32 saddr, u32 tos, u32 lsrc, + struct rtable **result) + { + +@@ -1659,6 +1662,7 @@ + flags |= RTCF_DIRECTSRC; + + if (out_dev == in_dev && err && !(flags & (RTCF_NAT | RTCF_MASQ)) && ++ !lsrc && + (IN_DEV_SHARED_MEDIA(out_dev) || + inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) + flags |= RTCF_DOREDIRECT; +@@ -1692,6 +1696,7 @@ + rth->fl.mark = skb->mark; + rth->fl.fl4_src = saddr; + rth->rt_src = saddr; ++ rth->fl.fl4_lsrc = lsrc; + rth->rt_gateway = daddr; + rth->rt_iif = + rth->fl.iif = in_dev->dev->ifindex; +@@ -1699,6 +1704,7 @@ + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); + rth->fl.oif = 0; ++ rth->fl.fl4_gw = 0; + rth->rt_spec_dst= spec_dst; + + rth->u.dst.input = ip_forward; +@@ -1720,19 +1726,21 @@ + struct fib_result* res, + const struct flowi *fl, + struct in_device *in_dev, +- __be32 daddr, __be32 saddr, u32 tos) ++ __be32 daddr, __be32 saddr, u32 tos, ++ u32 lsrc) + { + struct rtable* rth = NULL; + int err; + unsigned hash; + ++ fib_select_default(fl, res); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0) ++ if (res->fi && res->fi->fib_nhs > 1) + fib_select_multipath(fl, res); + #endif + + /* create a routing cache entry */ +- err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); ++ err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, lsrc, &rth); + if (err) + return err; + +@@ -1752,18 +1760,18 @@ + */ + + static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, +- u8 tos, struct net_device *dev) ++ u8 tos, struct net_device *dev, u32 lsrc) + { + struct fib_result res; + struct in_device *in_dev = in_dev_get(dev); + struct flowi fl = { .nl_u = { .ip4_u = + { .daddr = daddr, +- .saddr = saddr, ++ .saddr = lsrc ? : saddr, + .tos = tos, + .scope = RT_SCOPE_UNIVERSE, + } }, + .mark = skb->mark, +- .iif = dev->ifindex }; ++ .iif = lsrc? loopback_dev.ifindex : dev->ifindex }; + unsigned flags = 0; + u32 itag = 0; + struct rtable * rth; +@@ -1796,6 +1804,12 @@ + if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr)) + goto martian_destination; + ++ if (lsrc) { ++ if (MULTICAST(lsrc) || BADCLASS(lsrc) || ++ ZERONET(lsrc) || LOOPBACK(lsrc)) ++ goto e_inval; ++ } ++ + /* + * Now we are ready to route packet. + */ +@@ -1805,6 +1819,10 @@ + goto no_route; + } + free_res = 1; ++ if (lsrc && res.type != RTN_UNICAST && res.type != RTN_NAT) ++ goto e_inval; ++ fl.iif = dev->ifindex; ++ fl.fl4_src = saddr; + + RT_CACHE_STAT_INC(in_slow_tot); + +@@ -1829,7 +1847,7 @@ + if (res.type != RTN_UNICAST) + goto martian_destination; + +- err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); ++ err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos, lsrc); + if (err == -ENOBUFS) + goto e_nobufs; + if (err == -EINVAL) +@@ -1844,6 +1862,8 @@ + brd_input: + if (skb->protocol != htons(ETH_P_IP)) + goto e_inval; ++ if (lsrc) ++ goto e_inval; + + if (ZERONET(saddr)) + spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); +@@ -1884,6 +1904,7 @@ + rth->u.dst.dev = &loopback_dev; + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); ++ rth->fl.fl4_gw = 0; + rth->rt_gateway = daddr; + rth->rt_spec_dst= spec_dst; + rth->u.dst.input= ip_local_deliver; +@@ -1933,8 +1954,9 @@ + goto e_inval; + } + +-int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr, +- u8 tos, struct net_device *dev) ++static inline int ++ip_route_input_cached(struct sk_buff *skb, __be32 daddr, __be32 saddr, ++ u8 tos, struct net_device *dev, u32 lsrc) + { + struct rtable * rth; + unsigned hash; +@@ -1949,6 +1971,7 @@ + if (rth->fl.fl4_dst == daddr && + rth->fl.fl4_src == saddr && + rth->fl.iif == iif && ++ rth->fl.fl4_lsrc == lsrc && + rth->fl.oif == 0 && + rth->fl.mark == skb->mark && + rth->fl.fl4_tos == tos) { +@@ -1995,7 +2018,19 @@ + rcu_read_unlock(); + return -EINVAL; + } +- return ip_route_input_slow(skb, daddr, saddr, tos, dev); ++ return ip_route_input_slow(skb, daddr, saddr, tos, dev, lsrc); ++} ++ ++int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev) ++{ ++ return ip_route_input_cached(skb, daddr, saddr, tos, dev, 0); ++} ++ ++int ip_route_input_lookup(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev, u32 lsrc) ++{ ++ return ip_route_input_cached(skb, daddr, saddr, tos, dev, lsrc); + } + + static inline int __mkroute_output(struct rtable **result, +@@ -2067,6 +2102,7 @@ + rth->fl.fl4_tos = tos; + rth->fl.fl4_src = oldflp->fl4_src; + rth->fl.oif = oldflp->oif; ++ rth->fl.fl4_gw = oldflp->fl4_gw; + rth->fl.mark = oldflp->mark; + rth->rt_dst = fl->fl4_dst; + rth->rt_src = fl->fl4_src; +@@ -2145,6 +2181,7 @@ + struct flowi fl = { .nl_u = { .ip4_u = + { .daddr = oldflp->fl4_dst, + .saddr = oldflp->fl4_src, ++ .gw = oldflp->fl4_gw, + .tos = tos & IPTOS_RT_MASK, + .scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : +@@ -2248,6 +2285,7 @@ + dev_out = &loopback_dev; + dev_hold(dev_out); + fl.oif = loopback_dev.ifindex; ++ fl.fl4_gw = 0; + res.type = RTN_LOCAL; + flags |= RTCF_LOCAL; + goto make_route; +@@ -2255,7 +2293,7 @@ + + if (fib_lookup(&fl, &res)) { + res.fi = NULL; +- if (oldflp->oif) { ++ if (oldflp->oif && dev_out->flags & IFF_UP) { + /* Apparently, routing tables are wrong. Assume, + that the destination is on link. + +@@ -2295,6 +2333,7 @@ + dev_out = &loopback_dev; + dev_hold(dev_out); + fl.oif = dev_out->ifindex; ++ fl.fl4_gw = 0; + if (res.fi) + fib_info_put(res.fi); + res.fi = NULL; +@@ -2302,13 +2341,12 @@ + goto make_route; + } + ++ if (res.type == RTN_UNICAST) ++ fib_select_default(&fl, &res); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (res.fi->fib_nhs > 1 && fl.oif == 0) ++ if (res.fi->fib_nhs > 1) + fib_select_multipath(&fl, &res); +- else + #endif +- if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif) +- fib_select_default(&fl, &res); + + if (!fl.fl4_src) + fl.fl4_src = FIB_RES_PREFSRC(res); +@@ -2345,6 +2383,7 @@ + rth->fl.fl4_src == flp->fl4_src && + rth->fl.iif == 0 && + rth->fl.oif == flp->oif && ++ rth->fl.fl4_gw == flp->fl4_gw && + rth->fl.mark == flp->mark && + !((rth->fl.fl4_tos ^ flp->fl4_tos) & + (IPTOS_RT_MASK | RTO_ONLINK))) { +@@ -3030,3 +3069,4 @@ + EXPORT_SYMBOL(__ip_select_ident); + EXPORT_SYMBOL(ip_route_input); + EXPORT_SYMBOL(ip_route_output_key); ++EXPORT_SYMBOL(ip_route_input_lookup); diff --git a/target/linux/generic-2.6/patches-2.6.23/202-mips-freestanding.patch b/target/linux/generic-2.6/patches-2.6.23/202-mips-freestanding.patch new file mode 100644 index 0000000000..ff3849fb60 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/202-mips-freestanding.patch @@ -0,0 +1,12 @@ +--- a/arch/mips/Makefile ++++ b/arch/mips/Makefile +@@ -565,6 +565,9 @@ + cflags-$(CONFIG_TOSHIBA_RBTX4938) += -Iinclude/asm-mips/mach-tx49xx + load-$(CONFIG_TOSHIBA_RBTX4938) += 0xffffffff80100000 + ++# temporary until string.h is fixed ++cflags-y += -ffreestanding ++ + cflags-y += -Iinclude/asm-mips/mach-generic + drivers-$(CONFIG_PCI) += arch/mips/pci/ + diff --git a/target/linux/generic-2.6/patches-2.6.23/204-jffs2_eofdetect.patch b/target/linux/generic-2.6/patches-2.6.23/204-jffs2_eofdetect.patch new file mode 100644 index 0000000000..5053401f11 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/204-jffs2_eofdetect.patch @@ -0,0 +1,56 @@ +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -105,6 +105,17 @@ + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ if (c->mtd->unlock) ++ c->mtd->unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +--- a/fs/jffs2/scan.c ++++ b/fs/jffs2/scan.c +@@ -142,9 +142,12 @@ + + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); +- +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ ++ if (c->flags & (1 << 7)) ++ ret = BLK_STATE_ALLFF; ++ else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -545,6 +548,17 @@ + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + diff --git a/target/linux/generic-2.6/patches-2.6.23/207-powerpc_asm_segment_h.patch b/target/linux/generic-2.6/patches-2.6.23/207-powerpc_asm_segment_h.patch new file mode 100644 index 0000000000..1272e82c75 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/207-powerpc_asm_segment_h.patch @@ -0,0 +1,9 @@ +--- /dev/null ++++ b/include/asm-powerpc/segment.h +@@ -0,0 +1,6 @@ ++#ifndef _ASM_SEGMENT_H ++#define _ASM_SEGMENT_H ++ ++/* Only here because we have some old header files that expect it.. */ ++ ++#endif /* _ASM_SEGMENT_H */ diff --git a/target/linux/generic-2.6/patches-2.6.23/208-rtl8110sb_fix.patch b/target/linux/generic-2.6/patches-2.6.23/208-rtl8110sb_fix.patch new file mode 100644 index 0000000000..cfeff64b07 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/208-rtl8110sb_fix.patch @@ -0,0 +1,42 @@ +--- a/drivers/net/r8169.c ++++ b/drivers/net/r8169.c +@@ -1459,7 +1459,7 @@ + .hw_start = rtl_hw_start_8169, + .region = 1, + .align = 0, +- .intr_event = SYSErr | LinkChg | RxOverflow | ++ .intr_event = LinkChg | RxOverflow | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow + }, +@@ -1467,7 +1467,7 @@ + .hw_start = rtl_hw_start_8168, + .region = 2, + .align = 8, +- .intr_event = SYSErr | LinkChg | RxOverflow | ++ .intr_event = LinkChg | RxOverflow | + TxErr | TxOK | RxOK | RxErr, + .napi_event = TxErr | TxOK | RxOK | RxOverflow + }, +@@ -1475,7 +1475,7 @@ + .hw_start = rtl_hw_start_8101, + .region = 2, + .align = 8, +- .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout | ++ .intr_event = LinkChg | RxOverflow | PCSTimeout | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow + } +@@ -2779,10 +2779,12 @@ + break; + } + ++#if 0 + if (unlikely(status & SYSErr)) { + rtl8169_pcierr_interrupt(dev); + break; + } ++#endif + + if (status & LinkChg) + rtl8169_check_link_status(dev, tp, ioaddr); diff --git a/target/linux/generic-2.6/patches-2.6.23/209-mini_fo.patch b/target/linux/generic-2.6/patches-2.6.23/209-mini_fo.patch new file mode 100644 index 0000000000..01ba9b8a7e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/209-mini_fo.patch @@ -0,0 +1,7776 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -458,6 +458,9 @@ + This option will enlarge your kernel, but it allows debugging of + ocfs2 filesystem issues. + ++config MINI_FO ++ tristate "Mini fanout overlay filesystem" ++ + config MINIX_FS + tristate "Minix fs support" + help +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -76,6 +76,7 @@ + obj-$(CONFIG_RAMFS) += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ ++obj-$(CONFIG_MINI_FO) += mini_fo/ + obj-$(CONFIG_MINIX_FS) += minix/ + obj-$(CONFIG_FAT_FS) += fat/ + obj-$(CONFIG_MSDOS_FS) += msdos/ +--- /dev/null ++++ b/fs/mini_fo/aux.c +@@ -0,0 +1,577 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++/* check if file exists in storage */ ++int exists_in_storage(dentry_t *dentry) ++{ ++ check_mini_fo_dentry(dentry); ++ if(dtost(dentry) == MODIFIED || dtost(dentry) == CREATED || dtost(dentry) == DEL_REWRITTEN) ++ return 1; ++ return 0; ++} ++ ++/* check if dentry is in an existing state */ ++int is_mini_fo_existant(dentry_t *dentry) ++{ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) == DELETED || dtost(dentry) == NON_EXISTANT) ++ return 0; ++ else ++ return 1; ++} ++ ++/* ++ * This function will create a negative storage dentry for ++ * dentry, what is required for many create like options. ++ * It will create the storage structure if necessary. ++ */ ++int get_neg_sto_dentry(dentry_t *dentry) ++{ ++ int err = 0; ++ unsigned int len; ++ const unsigned char *name; ++ ++ if(!dentry || ++ !dtopd(dentry) || ++ !(dtost(dentry) == UNMODIFIED || ++ dtost(dentry) == NON_EXISTANT || ++ dtost(dentry) == DELETED)) { ++ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: invalid dentry passed.\n"); ++ err = -1; ++ goto out; ++ } ++ /* Have we got a neg. dentry already? */ ++ if(dtohd2(dentry)) { ++ err = 0; ++ goto out; ++ } ++ if(dtost(dentry->d_parent) == UNMODIFIED) { ++ /* build sto struct */ ++ err = build_sto_structure(dentry->d_parent->d_parent, dentry->d_parent); ++ if(err || ++ dtost(dentry->d_parent) != MODIFIED) { ++ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: ERROR building sto structure.\n"); ++ err = -1; ++ goto out; ++ } ++ } ++ ++ len = dentry->d_name.len; ++ name = dentry->d_name.name; ++ ++ dtohd2(dentry) = ++ lookup_one_len(name, dtohd2(dentry->d_parent), len); ++ ++ out: ++ return err; ++} ++ ++int check_mini_fo_dentry(dentry_t *dentry) ++{ ++ ASSERT(dentry != NULL); ++ ASSERT(dtopd(dentry) != NULL); ++ ASSERT((dtohd(dentry) != NULL) || (dtohd2(dentry) != NULL)); ++ ++/* if(dtost(dentry) == MODIFIED) { */ ++/* ASSERT(dentry->d_inode != NULL); */ ++/* ASSERT(dtohd(dentry) != NULL); */ ++/* ASSERT(dtohd(dentry)->d_inode != NULL); */ ++/* ASSERT(dtohd2(dentry) != NULL); */ ++/* ASSERT(dtohd2(dentry)->d_inode != NULL); */ ++/* } */ ++/* else if(dtost(dentry) == UNMODIFIED) { */ ++/* ASSERT(dentry->d_inode != NULL); */ ++/* ASSERT( */ ++/* } */ ++ return 0; ++} ++ ++int check_mini_fo_file(file_t *file) ++{ ++ ASSERT(file != NULL); ++ ASSERT(ftopd(file) != NULL); ++ ASSERT(file->f_dentry != NULL); ++ ++ /* violent checking, check depending of state and type ++ * if(S_ISDIR(file->f_dentry->d_inode->i_mode)) {} ++ */ ++ ASSERT((ftohf(file) != NULL) || (ftohf2(file) != NULL)); ++ return 0; ++} ++ ++int check_mini_fo_inode(inode_t *inode) ++{ ++ ASSERT(inode != NULL); ++ ASSERT(itopd(inode) != NULL); ++ ASSERT((itohi(inode) != NULL) || (itohi2(inode) != NULL)); ++ return 0; ++} ++ ++/* ++ * will walk a base path as provided by get_mini_fo_bpath and return ++ * the (hopefully ;-) ) positive dentry of the renamed base dir. ++ * ++ * This does some work of path_init. ++ */ ++dentry_t *bpath_walk(super_block_t *sb, char *bpath) ++{ ++ int err; ++ struct vfsmount *mnt; ++ struct nameidata nd; ++ ++ /* be paranoid */ ++ if(!bpath || bpath[0] != '/') { ++ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid string.\n"); ++ return NULL; ++ } ++ if(!sb || !stopd(sb)) { ++ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid sb.\n"); ++ return NULL; ++ } ++ ++ /* fix this: how do I reach this lock? ++ * read_lock(¤t->fs->lock); */ ++ mnt = mntget(stopd(sb)->hidden_mnt); ++ /* read_unlock(¤t->fs->lock); */ ++ ++ err = vfs_path_lookup(mnt->mnt_root, mnt, bpath+1, 0, &nd); ++ ++ /* validate */ ++ if (err || !nd.dentry || !nd.dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n"); ++ return NULL; ++ } ++ return nd.dentry; ++} ++ ++ ++/* returns the full path of the basefile incl. its name */ ++int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len) ++{ ++ char *buf_walker; ++ int len = 0; ++ dentry_t *sky_walker; ++ ++ if(!dentry || !dtohd(dentry)) { ++ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: invalid dentry passed.\n"); ++ return -1; ++ } ++ sky_walker = dtohd(dentry); ++ ++ do { ++ len += sky_walker->d_name.len + 1 ; /* 1 for '/' */ ++ sky_walker = sky_walker->d_parent; ++ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); ++ ++ /* 1 to oil the loop */ ++ *bpath = (char*) kmalloc(len + 1, GFP_KERNEL); ++ if(!*bpath) { ++ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: out of mem.\n"); ++ return -1; ++ } ++ buf_walker = *bpath+len; /* put it on last char */ ++ *buf_walker = '\n'; ++ sky_walker = dtohd(dentry); ++ ++ do { ++ buf_walker -= sky_walker->d_name.len; ++ strncpy(buf_walker, ++ sky_walker->d_name.name, ++ sky_walker->d_name.len); ++ *(--buf_walker) = '/'; ++ sky_walker = sky_walker->d_parent; ++ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); ++ ++ /* bpath_len doesn't count newline! */ ++ *bpath_len = len; ++ return 0; ++} ++ ++int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, ++ dentry_t *src_dentry, struct vfsmount *src_mnt) ++{ ++ void *buf; ++ mm_segment_t old_fs; ++ file_t *tgt_file; ++ file_t *src_file; ++ int bytes, len, tmp, err; ++ err = 0; ++ ++ if(!(tgt_dentry->d_inode && src_dentry->d_inode)) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, neg. dentry passed.\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ dget(tgt_dentry); ++ dget(src_dentry); ++ mntget(tgt_mnt); ++ mntget(src_mnt); ++ ++ /* open file write only */ ++ tgt_file = dentry_open(tgt_dentry, tgt_mnt, 0x1); ++ if(!tgt_file || IS_ERR(tgt_file)) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening target file.\n"); ++ err = PTR_ERR(tgt_file); ++ goto out_err; ++ } ++ ++ /* open file read only */ ++ src_file = dentry_open(src_dentry, src_mnt, 0x0); ++ if(!src_file || IS_ERR(src_file)) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening source file.\n"); ++ err = PTR_ERR(src_file); ++ ++ /* close target file */ ++ fput(tgt_file); ++ goto out_err; ++ } ++ ++ /* check if the filesystem(s) support read respective write */ ++ if(!src_file->f_op->read || !tgt_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, no fs read or write support.\n"); ++ err = -EPERM; ++ goto out_close; ++ } ++ ++ /* allocate a page for transfering the data */ ++ buf = (void *) __get_free_page(GFP_KERNEL); ++ if(!buf) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, out of kernel mem.\n"); ++ goto out_err; ++ } ++ ++ tgt_file->f_pos = 0; ++ src_file->f_pos = 0; ++ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* Doing this I assume that a read operation will return a full ++ * buffer while there is still data to read, and a less than ++ * full buffer when all data has been read. ++ */ ++ bytes = len = PAGE_SIZE; ++ while(bytes == len) { ++ bytes = src_file->f_op->read(src_file, buf, len, ++ &src_file->f_pos); ++ tmp = tgt_file->f_op->write(tgt_file, buf, bytes, ++ &tgt_file->f_pos); ++ if(tmp != bytes) { ++ printk(KERN_CRIT "mini_fo_cp_cont: ERROR writing.\n"); ++ goto out_close_unset; ++ } ++ } ++ ++ free_page((unsigned long) buf); ++ set_fs(old_fs); ++ fput(tgt_file); ++ fput(src_file); ++ goto out; ++ ++ out_close_unset: ++ free_page((unsigned long) buf); ++ set_fs(old_fs); ++ ++ out_close: ++ fput(tgt_file); ++ fput(src_file); ++ ++ out_err: ++ dput(tgt_dentry); ++ dput(src_dentry); ++ ++ /* mk: not sure if this need to be done */ ++ mntput(tgt_mnt); ++ mntput(src_mnt); ++ ++ out: ++ return err; ++} ++ ++/* mk: ++ * ndl (no-duplicate list) stuff ++ * This is used in mini_fo_readdir, to save the storage directory contents ++ * and later when reading base, match them against the list in order ++ * to avoid duplicates. ++ */ ++ ++/* add a file specified by name and len to the ndl ++ * Return values: 0 on success, <0 on failure. ++ */ ++int ndl_add_entry(struct readdir_data *rd, const char *name, int len) ++{ ++ struct ndl_entry *tmp_entry; ++ ++ tmp_entry = (struct ndl_entry *) ++ kmalloc(sizeof(struct ndl_entry), GFP_KERNEL); ++ if(!tmp_entry) { ++ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); ++ return -ENOMEM; ++ } ++ tmp_entry->name = (char*) kmalloc(len, GFP_KERNEL); ++ if(!tmp_entry->name) { ++ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); ++ return -ENOMEM; ++ } ++ strncpy(tmp_entry->name, name, len); ++ tmp_entry->len = len; ++ ++ list_add(&tmp_entry->list, &rd->ndl_list); ++ rd->ndl_size++; ++ return 0; ++} ++ ++/* delete all list entries and free memory */ ++void ndl_put_list(struct readdir_data *rd) ++{ ++ struct list_head *tmp; ++ struct ndl_entry *tmp_entry; ++ ++ if(rd->ndl_size <= 0) ++ return; ++ while(!list_empty(&rd->ndl_list)) { ++ tmp = rd->ndl_list.next; ++ list_del(tmp); ++ tmp_entry = list_entry(tmp, struct ndl_entry, list); ++ kfree(tmp_entry->name); ++ kfree(tmp_entry); ++ } ++ rd->ndl_size = 0; ++} ++ ++/* Check if a file specified by name and len is in the ndl ++ * Return value: 0 if not in list, 1 if file is found in ndl. ++ */ ++int ndl_check_entry(struct readdir_data *rd, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct ndl_entry *tmp_entry; ++ ++ if(rd->ndl_size <= 0) ++ return 0; ++ ++ list_for_each(tmp, &rd->ndl_list) { ++ tmp_entry = list_entry(tmp, struct ndl_entry, list); ++ if(tmp_entry->len != len) ++ continue; ++ if(!strncmp(tmp_entry->name, name, len)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* mk: ++ * Recursive function to create corresponding directorys in the storage fs. ++ * The function will build the storage directorys up to dentry. ++ */ ++int build_sto_structure(dentry_t *dir, dentry_t *dentry) ++{ ++ int err; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ if(dentry->d_parent != dir) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [1].\n"); ++ return 1; ++ } ++ ++ if(dtost(dir) != MODIFIED) { ++ err = build_sto_structure(dir->d_parent, dentry->d_parent); ++ if(err) ++ return err; ++ } ++ ++ /* ok, coming back again. */ ++ check_mini_fo_dentry(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(!hidden_sto_dentry) { ++ /* ++ * This is the case after creating the first ++ * hidden_sto_dentry. ++ * After one negative storage_dentry, all pointers to ++ * hidden_storage dentries are set to NULL. We need to ++ * create the negative dentry before we create the storage ++ * file. ++ */ ++ unsigned int len; ++ const unsigned char *name; ++ len = dtohd(dentry)->d_name.len; ++ name = dtohd(dentry)->d_name.name; ++ hidden_sto_dentry = lookup_one_len(name, dtohd2(dir), len); ++ dtohd2(dentry) = hidden_sto_dentry; ++ } ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ /* lets be safe */ ++ if(dtohd2(dir) != hidden_sto_dir_dentry) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [2].\n"); ++ return 1; ++ } ++ ++ /* check for errors in lock_parent */ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if(IS_ERR(hidden_sto_dir_dentry)) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: lock_parent failed.\n"); ++ return err; ++ } ++ ++ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dir->d_inode->i_mode); ++ ++ if(err) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [1].\n"); ++ /* was: unlock_dir(dir); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&dir->d_inode->i_mutex); ++#else ++ up(&dir->d_inode->i_sem); ++#endif ++ dput(dir); ++ return err; ++ } ++ ++ /* everything ok! */ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [2].\n"); ++ /* was: unlock_dir(dir); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&dir->d_inode->i_mutex); ++#else ++ up(&dir->d_inode->i_sem); ++#endif ++ dput(dir); ++ return 1; ++ } ++ ++ /* interpose the new inode and set new state */ ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ dtopd(dentry)->state = MODIFIED; ++ ++ /* initalize the wol list */ ++ itopd(dentry->d_inode)->deleted_list_size = -1; ++ itopd(dentry->d_inode)->renamed_list_size = -1; ++ meta_build_lists(dentry); ++ ++ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); ++ fist_copy_attr_timesizes(dir->d_inode, ++ hidden_sto_dir_dentry->d_inode); ++ dir->d_inode->i_nlink++; ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ return 0; ++} ++ ++ ++#if 0 /* unused */ ++ ++/* ++ * Read "len" bytes from "filename" into "buf". ++ * "buf" is in kernel space. ++ */ ++int ++mini_fo_read_file(const char *filename, void *buf, int len) ++{ ++ file_t *filp; ++ mm_segment_t oldfs; ++ int bytes; ++ /* Chroot? Maybe NULL isn't right here */ ++ filp = filp_open(filename, O_RDONLY, 0); ++ if (!filp || IS_ERR(filp)) { ++ printk("mini_fo_read_file err %d\n", (int) PTR_ERR(filp)); ++ return -1; /* or do something else */ ++ } ++ ++ if (!filp->f_op->read) ++ return -2; /* file(system) doesn't allow reads */ ++ ++ /* now read len bytes from offset 0 */ ++ filp->f_pos = 0; /* start offset */ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ bytes = filp->f_op->read(filp, buf, len, &filp->f_pos); ++ set_fs(oldfs); ++ ++ /* close the file */ ++ fput(filp); ++ ++ return bytes; ++} ++ ++ ++ ++/* ++ * Write "len" bytes from "buf" to "filename" ++ * "buf" is in kernel space. ++ */ ++int ++mini_fo_write_file(const char *filename, void *buf, int len) ++{ ++ file_t *filp; ++ mm_segment_t oldfs; ++ int bytes; ++ /* Chroot? Maybe NULL isn't right here */ ++ filp = filp_open(filename, O_RDWR|O_CREAT, 0640); ++ if (!filp || IS_ERR(filp)) { ++ printk("mini_fo_write_file err %d\n", (int) PTR_ERR(filp)); ++ return -1; /* or do something else */ ++ } ++ ++ if (!filp->f_op->write) ++ return -2; /* file(system) doesn't allow writes */ ++ ++ /* now write len bytes from offset 0 */ ++ filp->f_pos = 0; /* start offset */ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ bytes = filp->f_op->write(filp, buf, len, &filp->f_pos); ++ set_fs(oldfs); ++ ++ /* close the file */ ++ fput(filp); ++ ++ return bytes; ++} ++ ++#endif /* unused */ ++ +--- /dev/null ++++ b/fs/mini_fo/ChangeLog +@@ -0,0 +1,281 @@ ++2006-01-24 Markus Klotzbuecher ++ ++ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to ++ retain backwards compatibility. ++ ++2006-01-24 Ed L. Cashin ++ ++ * Support for the new mutex infrastructure ++ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) ++ ++2005-10-15 Markus Klotzbuecher ++ ++ * Bugfix for a serious memory leak in mini_fo_follow_link. ++ ++2005-09-21 Markus Klotzbuecher ++ ++ * new release 0.6.1 ++ ++ * fix of a compiler warning due to changes in 2.6.13 ++ ++2005-09-21 Klaus Wenninger ++ ++ * file.c: readdir: fix for a bug that caused directory entries ++ to show up twice when using storage filesystems such as ++ minixfs or pramfs. ++ ++2005-06-30 Eric Lammerts ++ ++ * fix for an oops when overwriting a binary thats beeing ++ executed. ++ ++2005-06-09 ++ ++ * Renamed overlay to mini_fo-overlay. ++ ++ * Added mini_fo-merge script to allow merging of storage and base ++ after making modifications. ++ ++2005-05-22 root ++ ++ * Added overlay script that allows to easily mount mini_fo ontop ++ of a given base directory ++ ++2005-05-10 ++ ++ * inode.c: xattr functions return -EOPNOSUPP instead of ++ -ENOSUPP, what confuses "ls -l" ++ ++ * Changed license from LGPL to GPL. ++ ++2005-05-08 root ++ ++ * Makefile: clean it up and added make install and make ++ uninstall. ++ ++2005-05-06 ++ ++ * merged devel branch back to main. [v0-6-0-pre3] ++ ++ * removed unused files print.c and fist_ioctl. [devel-0-0-18] ++ ++ * ioctl: removed fist_ioctl stuff, that is not needed for ++ now. ++ ++2005-05-03 ++ ++ * file.c: simplified mini_fo_open and mini_fo_setattr using ++ new state changing functions. [devel-0-0-17] ++ ++ * inode.c: Fixed getattr state bug (see below) in 2.4 function ++ mini_fo_inode revalidate. ++ ++ * inode.c: found an other bug in mini_fo_getattr. States are not ++ reliable in this function, as a file can be opened, unlinked and ++ the getattr function called. This results in a deleted dentry ++ with an inode. Fix is to ignore states and simply use the inode ++ available. ++ ++2005-04-29 ++ ++ * file.c: Bugfix and cleanup in fasync and fsync. [devel-0-0-16] ++ ++ * file.c: do not use mini_fo_lock so the generic version is ++ used (I guess). ++ ++ * inode.c: getattr, never call getattr on lower files, as this ++ will cause the inum to change. ++ ++ * inode.c: rename_reg_file renamed to rename_nondir, as it ++ doesn't matter as long it't not a dir. Removed all ++ rename_dev_file etc. ++ ++ * tagged as devel-0-0-15 ++ ++ * inode.c: added support for chosing support for extended ++ attrs at compile time by XATTR define in mini_fo.h . ++ ++ * inode.c: fixed mini_fo_getattr to use mini_fo inode and not ++ lower again, what avoids inode number changes that confused ++ rm again. This is the proper solution. ++ ++2005-04-24 ++ ++ * all files: updated Copyright notive to 2005. [devel-0-0-14] ++ ++ * inode.c: fixed mini_fo_getattr to not change the inode ++ number, even if lower files change. ++ ++ * super.c: fixed a bug that caused deleted base file to show ++ up suddenly after some time, or after creating a special ++ file. The problem was that after some time or after special ++ file creating sync_sb_inodes is called by the vfs, that ++ called our mini_fo_put_inode. There was (wrongly) called ++ __meta_put_lists, that nuked the lists, although the inode ++ was going to continue its life. Moving __meta_put_lists to ++ mini_fo_clear_inode, where an inode is really destroyed, ++ solved the problem. ++ ++ ++2005-04-23 ++ ++ * state.c, aux.c: more cleaning up and ++ simplifications. [devel-0-0-13] ++ ++ * inode.c: implemented mini_fo_getattr, that was required for ++ 2.6 because inode_revalidate has been remove there, and the ++ old "du" bug returned. ++ ++ ++2005-04-20 ++ ++ * aux.c: get_neg_sto_dentry(): allow to be called for dentries ++ in state UNMODIFIED, NON_EXISTANT _and_ DELETED. ++ ++2005-04-19 ++ ++ * Fixed a bug under 2.6 that caused files deleted via mini_fo ++ not to be deleted properly and therefore the fs filled up ++ untill no memory was left. [devel-0-0-12] ++ ++ * Added basic hard link support. This means that creating ++ hardlinks will work, but existing ones will be treated as ++ individual files. [devel-0-0-11] ++ ++2005-04-17 ++ ++ * Bugfixes ++ ++2005-04-13 root ++ ++ * Added file state.c for the state transition ++ functions. Doesn't work very well yet, though... ++ ++2005-04-12 ++ ++ * Porting to 2.6 started, which is easier than expected, also ++ due to Olivier previous work. ++ ++2005-04-08 ++ ++ * Fixed the bug that caused du to return invalid sizes of ++ directory trees. The problem was that ++ mini_fo_inode_revalidate didn't always copy the attributes ++ from the base inode properly. ++ ++2005-04-01 Markus Klotzbuecher ++ ++ * Merged devel branch back to main trunk and updated the ++ RELEASE notes. This will be 0-6-0-pre1. ++ ++2005-03-31 Markus Klotzbuecher ++ ++ * Fixed some bugs in rename_reg_file, that only showed up in ++ the kernel compile test. Kernel compiles cleanly ontop of ++ mini_fo, now also make mrproper etc. work. Seems pretty stable. ++ ++2005-03-28 Markus Klotzbuecher ++ ++ * Many, many directory renaming bugfixes and a lot of other ++ cleanup. Dir renaming seems to work relatively stable. ++ ++2005-03-22 Markus Klotzbuecher ++ ++ * Finished implementing lightweight directory renaming. Some ++ basic testing indicates it works fine. ++ Next is to implement testcases for the testsuite and confirm ++ everything is really working ok. ++ ++2005-03-18 Markus Klotzbuecher ++ ++ * Finished implementing meta.c stuff required for directory ++ renaming. ++ ++2005-03-17 Markus Klotzbuecher ++ ++ * Fixed all compile warnings + an extremly old bug that ++ somehow crept in while reworking the wol stuff to the META ++ system. Turning on -Werror again... :-) ++ ++ * Fixed some bugs in the new rename_reg_file function. ++ ++ * Rewrote mini_fo rename and split it into several ++ subfunctions, that handle the different types ++ seperately. Rewrote the regular file function aswell, as it ++ was implemented somewhat inefficient. ++ ++2005-03-16 Markus Klotzbuecher ++ ++ * Implemented new META subsystem, removed old WOL stuff in favor ++ if it. ++ ++ * After some basic testing everything seems ok... ++ ++2005-03-11 Markus Klotzbuecher ++ ++ * Renaming a non regular file caused trouble because I always ++ tried to copy the contents. Now I only do this for regular ++ files. mini_fo_rename still isn't implemented properly, renaming ++ of device files, symlinks etc. results in a empty regular file ++ instead of the proper type. ++ ++ * Directory renaming suddenly works! What a surprise! I guess ++ this is because renaming is implemented as making a copy and ++ removing the original. Still this might not work ++ everywhere... ++ ++2005-03-09 Markus Klotzbuecher ++ ++ * Bugfix, when a mini_fo directory that exists in storage ++ (state: MODIFIED, CREATED and DEL_REWRITTEN) is deleted, a ++ possibly existing WOL file contained in it needs to be ++ deleted too. ++ ++ * Starting cleanup: defined state names in order to get rid of ++ the state numbers. ++ ++2005-03-08 Markus Klotzbuecher ++ ++ * Makefile fix, fist_ioctl was built against wrong sources if ARCH=um ++ ++ * Fixed a bug in dentry.c, mini_fo_d_hash. In state 4 = ++ DEL_REWRITTEN the hash was calculated from the base dentry, ++ which was wrong and and caused assertions in ++ __mini_fo_hidden_dentry to fail. ++ ++2005-02-21 ++ ++ * Implemented directory deleting (inode.c) ++ ++ * main.c: made mini_fo_parse_options a little more robust. ++ ++2004-12-22 ++ ++ * Makefile cleanup and uml stuff, removed unneccessary files ++ ++ * Created a new and hopefully more informative README ++ ++ * CHANGELOG: created a new CHANGELOG and added old entries reversely ++ ++ ++2004-10-24 Gleb Natapov ++ ++ * Fix: owner and group where not correctly copied from base to ++ storage. ++ ++ ++2004-10-05 Gleb Natapov ++ ++ * Implementation of fsync, fasync and lock mini_fo functions. ++ ++ ++2004-09-29 Bob Lee ++ ++ * Fix of a serious pointer bug ++ ++ ++2004-09-28 Gleb Natapov ++ ++ * Implementation of mini_fo_mknod and mini_fo_rename, support ++ for device files. ++ +--- /dev/null ++++ b/fs/mini_fo/dentry.c +@@ -0,0 +1,244 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++/* ++ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. ++ */ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_d_revalidate(dentry_t *dentry, struct nameidata *nd) ++#else ++mini_fo_d_revalidate(dentry_t *dentry, int flags) ++#endif ++{ ++ int err1 = 1; /* valid = 1, invalid = 0 */ ++ int err2 = 1; ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ ++ check_mini_fo_dentry(dentry); ++ ++ hidden_dentry = dtohd(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(hidden_dentry && ++ hidden_dentry->d_op && ++ hidden_dentry->d_op->d_revalidate) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd); ++#else ++ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags); ++#endif ++ } ++ if(hidden_sto_dentry && ++ hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_revalidate) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, ++ nd); ++#else ++ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, ++ flags); ++#endif ++ } ++ ++ /* mk: if one of the lower level dentries are valid, ++ * the mini_fo dentry is too. ++ */ ++ return (err1 || err2); ++} ++ ++ ++STATIC int ++mini_fo_d_hash(dentry_t *dentry, qstr_t *name) ++{ ++ int err = 0; ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ /* hidden_dentry = mini_fo_hidden_dentry(dentry); ++ * hidden_sto_dentry = mini_fo_hidden_sto_dentry(dentry); */ ++ ++ /* state 1, 3, 4, 5: build the hash for the storage dentry */ ++ if((dtopd(dentry)->state == MODIFIED) || ++ (dtopd(dentry)->state == CREATED) || ++ (dtopd(dentry)->state == DEL_REWRITTEN) || ++ (dtopd(dentry)->state == DELETED)) { ++ hidden_sto_dentry = dtohd2(dentry); ++ if(hidden_sto_dentry && ++ hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_hash) { ++ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); ++ } ++ goto out; ++ } ++ /* state 2: build the hash for the base dentry */ ++ if(dtopd(dentry)->state == UNMODIFIED) { ++ hidden_dentry = dtohd(dentry); ++ if(hidden_dentry && ++ hidden_dentry->d_op && ++ hidden_dentry->d_op->d_hash) { ++ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); ++ } ++ goto out; ++ } ++ /* state 6: build hash for the dentry that exists */ ++ if(dtopd(dentry)->state == NON_EXISTANT) { ++ hidden_sto_dentry = dtohd2(dentry); ++ if(hidden_sto_dentry && ++ hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_hash) { ++ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); ++ goto out; ++ } ++ hidden_dentry = dtohd(dentry); ++ if(hidden_dentry && ++ hidden_dentry->d_op && ++ hidden_dentry->d_op->d_hash) { ++ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); ++ goto out; ++ } ++ } ++ ++ printk(KERN_CRIT "mini_fo: d_hash: invalid state detected.\n"); ++ ++ out: ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b) ++{ ++ int err; ++ dentry_t *hidden_dentry=NULL; ++ ++ /* hidden_dentry = mini_fo_hidden_dentry(dentry); */ ++ if(dtohd2(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else if(dtohd(dentry)) ++ hidden_dentry = dtohd(dentry); ++ ++ if (hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_compare) { ++ err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b); ++ } else { ++ err = ((a->len != b->len) || memcmp(a->name, b->name, b->len)); ++ } ++ ++ return err; ++} ++ ++ ++int ++mini_fo_d_delete(dentry_t *dentry) ++{ ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ int err = 0; ++ ++ /* this could be a negative dentry, so check first */ ++ if (!dtopd(dentry)) { ++ printk(KERN_CRIT "mini_fo_d_delete: negative dentry passed.\n"); ++ goto out; ++ } ++ hidden_dentry = dtohd(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(hidden_dentry) { ++ if(hidden_dentry->d_op && ++ hidden_dentry->d_op->d_delete) { ++ err = hidden_dentry->d_op->d_delete(hidden_dentry); ++ } ++ } ++ if(hidden_sto_dentry) { ++ if(hidden_sto_dentry->d_op && ++ hidden_sto_dentry->d_op->d_delete) { ++ err = hidden_sto_dentry->d_op->d_delete(hidden_sto_dentry); ++ } ++ } ++ ++ out: ++ return err; ++} ++ ++ ++void ++mini_fo_d_release(dentry_t *dentry) ++{ ++ dentry_t *hidden_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ ++ /* this could be a negative dentry, so check first */ ++ if (!dtopd(dentry)) { ++ printk(KERN_CRIT "mini_fo_d_release: no private data.\n"); ++ goto out; ++ } ++ hidden_dentry = dtohd(dentry); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ if(hidden_dentry) { ++ /* decrement hidden dentry's counter and free its inode */ ++ dput(hidden_dentry); ++ } ++ if(hidden_sto_dentry) { ++ /* decrement hidden dentry's counter and free its inode */ ++ dput(hidden_sto_dentry); ++ } ++ ++ /* free private data (mini_fo_dentry_info) here */ ++ kfree(dtopd(dentry)); ++ __dtopd(dentry) = NULL; /* just to be safe */ ++ out: ++ return; ++} ++ ++ ++/* ++ * we don't really need mini_fo_d_iput, because dentry_iput will call iput() if ++ * mini_fo_d_iput is not defined. We left this implemented for ease of ++ * tracing/debugging. ++ */ ++void ++mini_fo_d_iput(dentry_t *dentry, inode_t *inode) ++{ ++ iput(inode); ++} ++ ++ ++struct dentry_operations mini_fo_dops = { ++ d_revalidate: mini_fo_d_revalidate, ++ d_hash: mini_fo_d_hash, ++ d_compare: mini_fo_d_compare, ++ d_release: mini_fo_d_release, ++ d_delete: mini_fo_d_delete, ++ d_iput: mini_fo_d_iput, ++}; +--- /dev/null ++++ b/fs/mini_fo/file.c +@@ -0,0 +1,713 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) ++ ++/******************* ++ * File Operations * ++ *******************/ ++ ++STATIC loff_t ++mini_fo_llseek(file_t *file, loff_t offset, int origin) ++{ ++ loff_t err; ++ file_t *hidden_file = NULL; ++ ++ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { ++ /* Check if trying to llseek from a directory */ ++ err = -EISDIR; ++ goto out; ++ } ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ /* always set hidden position to this one */ ++ hidden_file->f_pos = file->f_pos; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ memcpy(&(hidden_file->f_ra), ++ &(file->f_ra), ++ sizeof(struct file_ra_state)); ++#else ++ if (file->f_reada) { /* update readahead information if needed */ ++ hidden_file->f_reada = file->f_reada; ++ hidden_file->f_ramax = file->f_ramax; ++ hidden_file->f_raend = file->f_raend; ++ hidden_file->f_ralen = file->f_ralen; ++ hidden_file->f_rawin = file->f_rawin; ++ } ++#endif ++ if (hidden_file->f_op && hidden_file->f_op->llseek) ++ err = hidden_file->f_op->llseek(hidden_file, offset, origin); ++ else ++ err = generic_file_llseek(hidden_file, offset, origin); ++ ++ if (err < 0) ++ goto out; ++ ++ if (err != file->f_pos) { ++ file->f_pos = err; ++ // ION maybe this? ++ // file->f_pos = hidden_file->f_pos; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ file->f_reada = 0; ++#endif ++ file->f_version++; ++ } ++ ++ out: ++ return err; ++} ++ ++ ++/* mk: fanout capable */ ++STATIC ssize_t ++mini_fo_read(file_t *file, char *buf, size_t count, loff_t *ppos) ++{ ++ int err = -EINVAL; ++ file_t *hidden_file = NULL; ++ loff_t pos = *ppos; ++ ++ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { ++ /* Check if trying to read from a directory */ ++ /* printk(KERN_CRIT "mini_fo_read: ERROR: trying to read data from a directory.\n"); */ ++ err = -EISDIR; ++ goto out; ++ } ++ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ if (!hidden_file->f_op || !hidden_file->f_op->read) ++ goto out; ++ ++ err = hidden_file->f_op->read(hidden_file, buf, count, &pos); ++ *ppos = pos; ++ ++ if (err >= 0) { ++ /* atime should also be updated for reads of size zero or more */ ++ fist_copy_attr_atime(file->f_dentry->d_inode, ++ hidden_file->f_dentry->d_inode); ++ } ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ /* ++ * MAJOR HACK ++ * because pread() does not have any way to tell us that it is ++ * our caller, then we don't know for sure if we have to update ++ * the file positions. This hack relies on read() having passed us ++ * the "real" pointer of its struct file's f_pos field. ++ */ ++ if (ppos == &file->f_pos) ++ hidden_file->f_pos = *ppos = pos; ++ if (hidden_file->f_reada) { /* update readahead information if needed */ ++ file->f_reada = hidden_file->f_reada; ++ file->f_ramax = hidden_file->f_ramax; ++ file->f_raend = hidden_file->f_raend; ++ file->f_ralen = hidden_file->f_ralen; ++ file->f_rawin = hidden_file->f_rawin; ++ } ++#else ++ memcpy(&(file->f_ra),&(hidden_file->f_ra),sizeof(struct file_ra_state)); ++#endif ++ ++ out: ++ return err; ++} ++ ++ ++/* this mini_fo_write() does not modify data pages! */ ++STATIC ssize_t ++mini_fo_write(file_t *file, const char *buf, size_t count, loff_t *ppos) ++{ ++ int err = -EINVAL; ++ file_t *hidden_file = NULL; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ loff_t pos = *ppos; ++ ++ /* mk: fan out: */ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ /* This is bad! We have no storage file to write to. This ++ * should never happen because if a file is opened for ++ * writing, a copy should have been made earlier. ++ */ ++ printk(KERN_CRIT "mini_fo: write : ERROR, no storage file to write.\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ inode = file->f_dentry->d_inode; ++ hidden_inode = itohi2(inode); ++ if(!hidden_inode) { ++ printk(KERN_CRIT "mini_fo: write: no sto inode found, not good.\n"); ++ goto out; ++ } ++ ++ if (!hidden_file->f_op || !hidden_file->f_op->write) ++ goto out; ++ ++ /* adjust for append -- seek to the end of the file */ ++ if (file->f_flags & O_APPEND) ++ pos = inode->i_size; ++ ++ err = hidden_file->f_op->write(hidden_file, buf, count, &pos); ++ ++ /* ++ * copy ctime and mtime from lower layer attributes ++ * atime is unchanged for both layers ++ */ ++ if (err >= 0) ++ fist_copy_attr_times(inode, hidden_inode); ++ ++ *ppos = pos; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ /* ++ * XXX: MAJOR HACK ++ * ++ * because pwrite() does not have any way to tell us that it is ++ * our caller, then we don't know for sure if we have to update ++ * the file positions. This hack relies on write() having passed us ++ * the "real" pointer of its struct file's f_pos field. ++ */ ++ if (ppos == &file->f_pos) ++ hidden_file->f_pos = *ppos = pos; ++#endif ++ /* update this inode's size */ ++ if (pos > inode->i_size) ++ inode->i_size = pos; ++ ++ out: ++ return err; ++} ++ ++/* Global variable to hold a file_t pointer. ++ * This serves to allow mini_fo_filldir function to know which file is ++ * beeing read, which is required for two reasons: ++ * ++ * - be able to call wol functions in order to avoid listing deleted ++ * base files. ++ * - if we're reading a directory which is in state 1, we need to ++ * maintain a list (in mini_fo_filldir) of which files allready ++ * have been copied to userspace,to detect files existing in base ++ * and storage and not list them twice. ++ */ ++filldir_t mini_fo_filldir_orig; ++file_t *mini_fo_filldir_file; ++ ++/* mainly copied from fs/readdir.c */ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, ++ u64 ino, unsigned int d_type) ++#else ++mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, ++ ino_t ino, unsigned int d_type) ++#endif ++{ ++ struct getdents_callback * buf = (struct getdents_callback *) __buf; ++ file_t* file = mini_fo_filldir_file; ++ ++ /* In theses states we filter meta files in storage (WOL) */ ++ if(file && (dtopd(file->f_dentry)->state == MODIFIED || ++ dtopd(file->f_dentry)->state == CREATED || ++ dtopd(file->f_dentry)->state == DEL_REWRITTEN)) { ++ ++ int tmp = strlen(META_FILENAME); ++ if(tmp == namlen) { ++ if(!strncmp(name, META_FILENAME, namlen)) ++ return 0; ++ } ++ } ++ ++ /* check if we are merging the contents of storage and base */ ++ if(file && dtopd(file->f_dentry)->state == MODIFIED) { ++ /* check if we are still reading storage contents, if ++ * yes, we just save the name of the file for duplicate ++ * checking later. */ ++ ++ if(!ftopd(file)->rd.sto_done) { ++ /* put file into ndl list */ ++ if(ndl_add_entry(&ftopd(file)->rd, name, namlen)) ++ printk(KERN_CRIT "mini_fo_filldir: Error adding to ndl.\n"); ++ } else { ++ /* check if file has been deleted */ ++ if(meta_check_d_entry(file->f_dentry, name, namlen)) ++ return 0; ++ ++ /* do duplicate checking */ ++ if(ndl_check_entry(&ftopd(file)->rd, name, namlen)) ++ return 0; ++ } ++ } ++ ++ return mini_fo_filldir_orig(buf, name, namlen, offset, ino, d_type); ++} ++ ++ ++STATIC int ++mini_fo_readdir(file_t *file, void *dirent, filldir_t filldir) ++{ ++ int err = 0;/* mk: ??? -ENOTDIR; */ ++ file_t *hidden_file = NULL; ++ file_t *hidden_sto_file = NULL; ++ inode_t *inode; ++ struct getdents_callback *buf; ++ int oldcount; ++ ++#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA) ++ struct mini_fo_getdents_callback buf; ++#endif /* FIST_FILTER_NAME || FIST_FILTER_SCA */ ++ ++ buf = (struct getdents_callback *) dirent; ++ oldcount = buf->count; ++ inode = file->f_dentry->d_inode; ++ mini_fo_filldir_file = file; ++ mini_fo_filldir_orig = filldir; ++ ++ ftopd(file)->rd.sto_done = 0; ++ do { ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_sto_file = ftohf2(file); ++ err = vfs_readdir(hidden_sto_file, mini_fo_filldir, dirent); ++ file->f_pos = hidden_sto_file->f_pos; ++ if (err > 0) ++ fist_copy_attr_atime(inode, hidden_sto_file->f_dentry->d_inode); ++ /* not finshed yet, we'll be called again */ ++ if (buf->count != oldcount) ++ break; ++ } ++ ++ ftopd(file)->rd.sto_done = 1; ++ ++ if(ftohf(file)) { ++ hidden_file = ftohf(file); ++ err = vfs_readdir(hidden_file, mini_fo_filldir, dirent); ++ file->f_pos = hidden_file->f_pos; ++ if (err > 0) ++ fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode); ++ } ++ ++ } ++ } while (0); ++ ++ /* mk: ++ * we need to check if all the directory data has been copied to userspace, ++ * or if we will be called again by userspace to complete the operation. ++ */ ++ if(buf->count == oldcount) { ++ ndl_put_list(&ftopd(file)->rd); ++ } ++ ++ /* unset this, safe */ ++ mini_fo_filldir_file = NULL; ++ return err; ++} ++ ++ ++STATIC unsigned int ++mini_fo_poll(file_t *file, poll_table *wait) ++{ ++ unsigned int mask = DEFAULT_POLLMASK; ++ file_t *hidden_file = NULL; ++ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ if (!hidden_file->f_op || !hidden_file->f_op->poll) ++ goto out; ++ ++ mask = hidden_file->f_op->poll(hidden_file, wait); ++ ++ out: ++ return mask; ++} ++ ++/* FIST-LITE special version of mmap */ ++STATIC int ++mini_fo_mmap(file_t *file, vm_area_t *vma) ++{ ++ int err = 0; ++ file_t *hidden_file = NULL; ++ ++ /* fanout capability */ ++ if (ftopd(file) != NULL) { ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ } else { ++ hidden_file = ftohf(file); ++ } ++ } ++ ++ ASSERT(hidden_file != NULL); ++ ASSERT(hidden_file->f_op != NULL); ++ ASSERT(hidden_file->f_op->mmap != NULL); ++ ++ vma->vm_file = hidden_file; ++ err = hidden_file->f_op->mmap(hidden_file, vma); ++ get_file(hidden_file); /* make sure it doesn't get freed on us */ ++ fput(file); /* no need to keep extra ref on ours */ ++ ++ return err; ++} ++ ++ ++ ++STATIC int ++mini_fo_open(inode_t *inode, file_t *file) ++{ ++ int err = 0; ++ int hidden_flags; ++ file_t *hidden_file = NULL; ++ dentry_t *hidden_dentry = NULL; ++ ++ /* fanout stuff */ ++ file_t *hidden_sto_file = NULL; ++ dentry_t *hidden_sto_dentry = NULL; ++ ++ __ftopd(file) = ++ kmalloc(sizeof(struct mini_fo_file_info), GFP_KERNEL); ++ if (!ftopd(file)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* init the readdir_helper structure */ ++ INIT_LIST_HEAD(&ftopd(file)->rd.ndl_list); ++ ftopd(file)->rd.ndl_size = 0; ++ ++ /* In certain paths this could stay uninitalized and cause trouble */ ++ ftohf(file) = NULL; ++ ftohf2(file) = NULL; ++ hidden_flags = file->f_flags; ++ ++ /* create storage files? */ ++ if(dtost(file->f_dentry) == UNMODIFIED) { ++ if(!IS_WRITE_FLAG(file->f_flags)) { ++ hidden_dentry = dtohd(file->f_dentry); ++ dget(hidden_dentry); ++ /* dentry_open will decrement mnt refcnt if err. ++ * otherwise fput() will do an mntput() for us upon file close. */ ++ mntget(stopd(inode->i_sb)->hidden_mnt); ++ hidden_file = dentry_open(hidden_dentry, ++ stopd(inode->i_sb)->hidden_mnt, ++ hidden_flags); ++ if (IS_ERR(hidden_file)) { ++ err = PTR_ERR(hidden_file); ++ dput(hidden_dentry); ++ goto out; ++ } ++ ftohf(file) = hidden_file; /* link two files */ ++ goto out; ++ } ++ else { ++ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { ++ err = dir_unmod_to_mod(file->f_dentry); ++ } else ++ err = nondir_unmod_to_mod(file->f_dentry, 1); ++ ++ if (err) { ++ printk("mini_fo_open: ERROR creating storage file.\n"); ++ goto out; ++ } ++ } ++ } ++ hidden_sto_dentry = dtohd2(file->f_dentry); ++ dget(hidden_sto_dentry); ++ ++ if(dtopd(file->f_dentry)->state == MODIFIED) { ++ /* Directorys are special, interpose on both lower level files */ ++ if(S_ISDIR(itohi(inode)->i_mode)) { ++ /* check for invalid file types of lower level files */ ++ if(!(S_ISDIR(itohi(inode)->i_mode) && S_ISDIR(itohi2(inode)->i_mode))) { ++ printk(KERN_CRIT "mini_fo_open: meta data corruption detected.\n"); ++ dput(hidden_sto_dentry); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* lower level directorys are ok, open the base file */ ++ hidden_dentry = dtohd(file->f_dentry); ++ dget(hidden_dentry); ++ ++ mntget(stopd(inode->i_sb)->hidden_mnt); ++ hidden_file = dentry_open(hidden_dentry, ++ stopd(inode->i_sb)->hidden_mnt, ++ hidden_flags); ++ if (IS_ERR(hidden_file)) { ++ err = PTR_ERR(hidden_file); ++ dput(hidden_dentry); ++ dput(hidden_sto_dentry); ++ goto out; ++ } ++ ftohf(file) = hidden_file; /* link the two files */ ++ } ++ } ++ ++ if(!exists_in_storage(file->f_dentry)) { ++ printk(KERN_CRIT "mini_fo_open: invalid file state detected.\n"); ++ err = -EINVAL; ++ dput(hidden_sto_dentry); ++ ++ /* If the base file has been opened, we need to close it here */ ++ if(ftohf(file)) { ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ hidden_file->f_op->flush(hidden_file, NULL); ++#else ++ hidden_file->f_op->flush(hidden_file); ++#endif ++ dput(hidden_dentry); ++ } ++ goto out; ++ } ++ ++ /* ok, now we can safely open the storage file */ ++ mntget(stopd(inode->i_sb)->hidden_mnt2); ++ hidden_sto_file = dentry_open(hidden_sto_dentry, ++ stopd(inode->i_sb)->hidden_mnt2, ++ hidden_flags); ++ ++ /* dentry_open dputs the dentry if it fails */ ++ if (IS_ERR(hidden_sto_file)) { ++ err = PTR_ERR(hidden_sto_file); ++ /* close base file if open */ ++ if(ftohf(file)) { ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ hidden_file->f_op->flush(hidden_file, NULL); ++#else ++ hidden_file->f_op->flush(hidden_file); ++#endif ++ dput(hidden_dentry); ++ } ++ goto out; ++ } ++ ftohf2(file) = hidden_sto_file; /* link storage file */ ++ ++ out: ++ if (err < 0 && ftopd(file)) { ++ kfree(ftopd(file)); ++ } ++ return err; ++} ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_flush(file_t *file, fl_owner_t id) ++#else ++mini_fo_flush(file_t *file) ++#endif ++{ ++ int err1 = 0; /* assume ok (see open.c:close_fp) */ ++ int err2 = 0; ++ file_t *hidden_file = NULL; ++ ++ check_mini_fo_file(file); ++ ++ /* mk: we don't do any state checking here, as its not worth the time. ++ * Just flush the lower level files if they exist. ++ */ ++ if(ftopd(file) != NULL) { ++ if(ftohf(file) != NULL) { ++ hidden_file = ftohf(file); ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ err1 = hidden_file->f_op->flush(hidden_file, id); ++#else ++ err1 = hidden_file->f_op->flush(hidden_file); ++#endif ++ } ++ if(ftohf2(file) != NULL) { ++ hidden_file = ftohf2(file); ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ err2 = hidden_file->f_op->flush(hidden_file, id); ++#else ++ err2 = hidden_file->f_op->flush(hidden_file); ++#endif ++ } ++ } ++ return (err1 | err2); ++} ++ ++ ++STATIC int ++mini_fo_release(inode_t *inode, file_t *file) ++{ ++ int err = 0; ++ file_t *hidden_file = NULL; ++ ++ if (ftopd(file) != NULL) { ++ if(ftohf(file)) { ++ hidden_file = ftohf(file); ++ fput(hidden_file); ++ } ++ if(ftohf2(file)) { ++ hidden_file = ftohf2(file); ++ fput(hidden_file); ++ } ++ kfree(ftopd(file)); ++ } ++ return err; ++} ++ ++STATIC int ++mini_fo_fsync(file_t *file, dentry_t *dentry, int datasync) ++{ ++ int err1 = 0; ++ int err2 = 0; ++ file_t *hidden_file = NULL; ++ dentry_t *hidden_dentry; ++ ++ check_mini_fo_file(file); ++ ++ if ((hidden_file = ftohf(file)) != NULL) { ++ hidden_dentry = dtohd(dentry); ++ if (hidden_file->f_op && hidden_file->f_op->fsync) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ err1 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ } ++ ++ if ((hidden_file = ftohf2(file)) != NULL) { ++ hidden_dentry = dtohd2(dentry); ++ if (hidden_file->f_op && hidden_file->f_op->fsync) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ err2 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ } ++ else ++ goto err; ++ ++err: ++ return (err1 || err2); ++} ++ ++ ++STATIC int ++mini_fo_fasync(int fd, file_t *file, int flag) ++{ ++ int err1 = 0; ++ int err2 = 0; ++ ++ file_t *hidden_file = NULL; ++ ++ check_mini_fo_file(file); ++ ++ if((hidden_file = ftohf(file)) != NULL) { ++ err1 = hidden_file->f_op->fasync(fd, hidden_file, flag); ++ } ++ if((hidden_file = ftohf2(file)) != NULL) { ++ err2 = hidden_file->f_op->fasync(fd, hidden_file, flag); ++ } ++ ++ return (err1 || err2); ++} ++ ++ ++ ++struct file_operations mini_fo_dir_fops = ++ { ++ read: generic_read_dir, ++ write: mini_fo_write, ++ readdir: mini_fo_readdir, ++ poll: mini_fo_poll, ++ /* ioctl: mini_fo_ioctl, */ ++ mmap: mini_fo_mmap, ++ open: mini_fo_open, ++ flush: mini_fo_flush, ++ release: mini_fo_release, ++ fsync: mini_fo_fsync, ++ fasync: mini_fo_fasync, ++ /* not needed lock: mini_fo_lock, */ ++ /* not needed: readv */ ++ /* not needed: writev */ ++ /* not implemented: sendpage */ ++ /* not implemented: get_unmapped_area */ ++ }; ++ ++struct file_operations mini_fo_main_fops = ++ { ++ llseek: mini_fo_llseek, ++ read: mini_fo_read, ++ write: mini_fo_write, ++ readdir: mini_fo_readdir, ++ poll: mini_fo_poll, ++ /* ioctl: mini_fo_ioctl, */ ++ mmap: mini_fo_mmap, ++ open: mini_fo_open, ++ flush: mini_fo_flush, ++ release: mini_fo_release, ++ fsync: mini_fo_fsync, ++ fasync: mini_fo_fasync, ++ /* not needed: lock: mini_fo_lock, */ ++ /* not needed: readv */ ++ /* not needed: writev */ ++ /* not implemented: sendpage */ ++ /* not implemented: get_unmapped_area */ ++ }; +--- /dev/null ++++ b/fs/mini_fo/fist.h +@@ -0,0 +1,252 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifndef __FIST_H_ ++#define __FIST_H_ ++ ++/* ++ * KERNEL ONLY CODE: ++ */ ++#ifdef __KERNEL__ ++#include ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) ++#include ++#else ++#include ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#ifdef CONFIG_MODVERSIONS ++# define MODVERSIONS ++# include ++#endif /* CONFIG_MODVERSIONS */ ++#endif /* KERNEL_VERSION < 2.6.0 */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#include ++#else ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) ++#include ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include ++#endif ++ ++#include ++ ++#include ++/* #include */ ++#include ++#include ++ ++/* ++ * MACROS: ++ */ ++ ++/* those mapped to ATTR_* were copied from linux/fs.h */ ++#define FA_MODE ATTR_MODE ++#define FA_UID ATTR_UID ++#define FA_GID ATTR_GID ++#define FA_SIZE ATTR_SIZE ++#define FA_ATIME ATTR_ATIME ++#define FA_MTIME ATTR_MTIME ++#define FA_CTIME ATTR_CTIME ++#define FA_ATIME_SET ATTR_ATIME_SET ++#define FA_MTIME_SET ATTR_MTIME_SET ++#define FA_FORCE ATTR_FORCE ++#define FA_ATTR_FLAGS ATTR_ATTR_FLAG ++ ++/* must be greater than all other ATTR_* flags! */ ++#define FA_NLINK 2048 ++#define FA_BLKSIZE 4096 ++#define FA_BLOCKS 8192 ++#define FA_TIMES (FA_ATIME|FA_MTIME|FA_CTIME) ++#define FA_ALL 0 ++ ++/* macros to manage changes between kernels */ ++#define INODE_DATA(i) (&(i)->i_data) ++ ++#define MIN(x,y) ((x < y) ? (x) : (y)) ++#define MAX(x,y) ((x > y) ? (x) : (y)) ++#define MAXPATHLEN PATH_MAX ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) ++# define lookup_one_len(a,b,c) lookup_one(a,b) ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) */ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) ++# define generic_file_llseek default_llseek ++#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) */ ++ ++#ifndef SEEK_SET ++# define SEEK_SET 0 ++#endif /* not SEEK_SET */ ++ ++#ifndef SEEK_CUR ++# define SEEK_CUR 1 ++#endif /* not SEEK_CUR */ ++ ++#ifndef SEEK_END ++# define SEEK_END 2 ++#endif /* not SEEK_END */ ++ ++#ifndef DEFAULT_POLLMASK ++# define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) ++#endif /* not DEFAULT_POLLMASK */ ++ ++/* XXX: fix this so fistgen generates kfree() code directly */ ++#define kfree_s(a,b) kfree(a) ++ ++/* ++ * TYPEDEFS: ++ */ ++typedef struct dentry dentry_t; ++typedef struct file file_t; ++typedef struct inode inode_t; ++typedef inode_t vnode_t; ++typedef struct page page_t; ++typedef struct qstr qstr_t; ++typedef struct super_block super_block_t; ++typedef super_block_t vfs_t; ++typedef struct vm_area_struct vm_area_t; ++ ++ ++/* ++ * EXTERNALS: ++ */ ++ ++#define FPPF(str,page) printk("PPF %s 0x%x/%d: Lck:%d Err:%d Ref:%d Upd:%d Other::%d:%d:%d:%d:\n", \ ++ str, \ ++ (int) page, \ ++ (int) page->index, \ ++ (PageLocked(page) ? 1 : 0), \ ++ (PageError(page) ? 1 : 0), \ ++ (PageReferenced(page) ? 1 : 0), \ ++ (Page_Uptodate(page) ? 1 : 0), \ ++ (PageDecrAfter(page) ? 1 : 0), \ ++ (PageSlab(page) ? 1 : 0), \ ++ (PageSwapCache(page) ? 1 : 0), \ ++ (PageReserved(page) ? 1 : 0) \ ++ ) ++#define EZKDBG printk("EZK %s:%d:%s\n",__FILE__,__LINE__,__FUNCTION__) ++#if 0 ++# define EZKDBG1 printk("EZK %s:%d\n",__FILE__,__LINE__) ++#else ++# define EZKDBG1 ++#endif ++ ++extern int fist_get_debug_value(void); ++extern int fist_set_debug_value(int val); ++#if 0 /* mini_fo doesn't need these */ ++extern void fist_dprint_internal(int level, char *str,...); ++extern void fist_print_dentry(char *str, const dentry_t *dentry); ++extern void fist_print_inode(char *str, const inode_t *inode); ++extern void fist_print_file(char *str, const file_t *file); ++extern void fist_print_buffer_flags(char *str, struct buffer_head *buffer); ++extern void fist_print_page_flags(char *str, page_t *page); ++extern void fist_print_page_bytes(char *str, page_t *page); ++extern void fist_print_pte_flags(char *str, const page_t *page); ++extern void fist_checkinode(inode_t *inode, char *msg); ++extern void fist_print_sb(char *str, const super_block_t *sb); ++ ++/* §$% by mk: special debug functions */ ++extern void fist_mk_print_dentry(char *str, const dentry_t *dentry); ++extern void fist_mk_print_inode(char *str, const inode_t *inode); ++ ++extern char *add_indent(void); ++extern char *del_indent(void); ++#endif/* mini_fo doesn't need these */ ++ ++ ++#define STATIC ++#define ASSERT(EX) \ ++do { \ ++ if (!(EX)) { \ ++ printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \ ++ __FILE__, __LINE__, __FUNCTION__); \ ++ (*((char *)0))=0; \ ++ } \ ++} while (0) ++/* same ASSERT, but tell me who was the caller of the function */ ++#define ASSERT2(EX) \ ++do { \ ++ if (!(EX)) { \ ++ printk(KERN_CRIT "ASSERTION FAILED (caller): %s at %s:%d (%s)\n", #EX, \ ++ file, line, func); \ ++ (*((char *)0))=0; \ ++ } \ ++} while (0) ++ ++#if 0 /* mini_fo doesn't need these */ ++#define dprintk(format, args...) printk(KERN_DEBUG format, ##args) ++#define fist_dprint(level, str, args...) fist_dprint_internal(level, KERN_DEBUG str, ## args) ++#define print_entry_location() fist_dprint(4, "%sIN: %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__) ++#define print_exit_location() fist_dprint(4, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__) ++#define print_exit_status(status) fist_dprint(4, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status) ++#define print_exit_pointer(status) \ ++do { \ ++ if (IS_ERR(status)) \ ++ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ ++ else \ ++ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: 0x%x\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ ++} while (0) ++#endif/* mini_fo doesn't need these */ ++ ++#endif /* __KERNEL__ */ ++ ++ ++/* ++ * DEFINITIONS FOR USER AND KERNEL CODE: ++ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest ++ * are auto-generated automatically based on the user's .fist file.) ++ */ ++# define FIST_IOCTL_GET_DEBUG_VALUE _IOR(0x15, 1, int) ++# define FIST_IOCTL_SET_DEBUG_VALUE _IOW(0x15, 2, int) ++ ++#endif /* not __FIST_H_ */ +--- /dev/null ++++ b/fs/mini_fo/inode.c +@@ -0,0 +1,1564 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd) ++#else ++mini_fo_create(inode_t *dir, dentry_t *dentry, int mode) ++#endif ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = create_sto_reg_file(dentry, mode, nd); ++#else ++ err = create_sto_reg_file(dentry, mode); ++#endif ++ check_mini_fo_dentry(dentry); ++ return err; ++} ++ ++ ++STATIC dentry_t * ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_lookup(inode_t *dir, dentry_t *dentry, struct nameidata* nd) ++#else ++mini_fo_lookup(inode_t *dir, dentry_t *dentry) ++#endif ++{ ++ int err = 0; ++ dentry_t *hidden_dir_dentry; ++ dentry_t *hidden_dentry = NULL; ++ ++ dentry_t *hidden_sto_dir_dentry; ++ dentry_t *hidden_sto_dentry = NULL; ++ ++ /* whiteout flag */ ++ int del_flag = 0; ++ char *bpath = NULL; ++ ++ const char *name; ++ unsigned int namelen; ++ ++ /* Don't allow lookups of META-files */ ++ namelen = strlen(META_FILENAME); ++ if(namelen == dentry->d_name.len) { ++ if(!strncmp(dentry->d_name.name, META_FILENAME, namelen)) { ++ err = -ENOENT; ++ goto out; ++ } ++ } ++ ++ hidden_dir_dentry = dtohd(dentry->d_parent); ++ hidden_sto_dir_dentry = dtohd2(dentry->d_parent); ++ ++ name = dentry->d_name.name; ++ namelen = dentry->d_name.len; ++ ++ /* must initialize dentry operations */ ++ dentry->d_op = &mini_fo_dops; ++ ++ /* setup the del_flag */ ++ del_flag = __meta_check_d_entry(dir, name, namelen); ++ bpath = __meta_check_r_entry(dir, name, namelen); ++ ++ /* perform the lookups of base and storage files: ++ * ++ * This caused some serious trouble, as a lookup_one_len passing ++ * a negative dentry oopses. Solution is to only do the lookup ++ * if the dentry is positive, else we set it to NULL ++ * More trouble, who said a *_dir_dentry can't be NULL? ++ */ ++ if(bpath) { ++ /* Cross-Interposing (C), yeah! */ ++ hidden_dentry = bpath_walk(dir->i_sb, bpath); ++ if(!hidden_dentry || !hidden_dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo_lookup: bpath_walk failed.\n"); ++ err= -EINVAL; ++ goto out; ++ } ++ ++ /* this can be set up safely without fear of spaghetti ++ * interposing as it is only used for copying times */ ++ hidden_dir_dentry = hidden_dentry->d_parent; ++ kfree(bpath); ++ } ++ else if(hidden_dir_dentry && hidden_dir_dentry->d_inode) ++ hidden_dentry = ++ lookup_one_len(name, hidden_dir_dentry, namelen); ++ else ++ hidden_dentry = NULL; ++ ++ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) ++ hidden_sto_dentry = ++ lookup_one_len(name, hidden_sto_dir_dentry, namelen); ++ else ++ hidden_sto_dentry = NULL; ++ ++ /* catch error in lookup */ ++ if (IS_ERR(hidden_dentry) || IS_ERR(hidden_sto_dentry)) { ++ /* mk: we need to call dput on the dentry, whose ++ * lookup_one_len operation failed, in order to avoid ++ * unmount trouble. ++ */ ++ if(IS_ERR(hidden_dentry)) { ++ printk(KERN_CRIT "mini_fo_lookup: ERR from base dentry, lookup failed.\n"); ++ err = PTR_ERR(hidden_dentry); ++ } else { ++ dput(hidden_dentry); ++ } ++ if(IS_ERR(hidden_sto_dentry)) { ++ printk(KERN_CRIT "mini_fo_lookup: ERR from storage dentry, lookup failed.\n"); ++ err = PTR_ERR(hidden_sto_dentry); ++ } else { ++ dput(hidden_sto_dentry); ++ } ++ goto out; ++ } ++ ++ /* allocate dentry private data */ ++ __dtopd(dentry) = (struct mini_fo_dentry_info *) ++ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); ++ ++ if (!dtopd(dentry)) { ++ err = -ENOMEM; ++ goto out_dput; ++ } ++ ++ /* check for different states of the mini_fo file to be looked up. */ ++ ++ /* state 1, file has been modified */ ++ if(hidden_dentry && hidden_sto_dentry && ++ hidden_dentry->d_inode && hidden_sto_dentry->d_inode && !del_flag) { ++ ++ /* update parent directory's atime */ ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = MODIFIED; ++ dtohd(dentry) = hidden_dentry; ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ err = mini_fo_tri_interpose(hidden_dentry, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state1).\n"); ++ goto out_free; ++ } ++ goto out; ++ } ++ /* state 2, file is unmodified */ ++ if(hidden_dentry && hidden_dentry->d_inode && !del_flag) { ++ ++ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = UNMODIFIED; ++ dtohd(dentry) = hidden_dentry; ++ dtohd2(dentry) = hidden_sto_dentry; /* could be negative */ ++ ++ err = mini_fo_tri_interpose(hidden_dentry, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state2).\n"); ++ goto out_free; ++ } ++ goto out; ++ } ++ /* state 3, file has been newly created */ ++ if(hidden_sto_dentry && hidden_sto_dentry->d_inode && !del_flag) { ++ ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ dtopd(dentry)->state = CREATED; ++ dtohd(dentry) = hidden_dentry; /* could be negative */ ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ err = mini_fo_tri_interpose(hidden_dentry, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state3).\n"); ++ goto out_free; ++ } ++ goto out; ++ } ++ ++ /* state 4, file has deleted and created again. */ ++ if(hidden_dentry && hidden_sto_dentry && ++ hidden_dentry->d_inode && ++ hidden_sto_dentry->d_inode && del_flag) { ++ ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ dtopd(dentry)->state = DEL_REWRITTEN; ++ dtohd(dentry) = NULL; ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ err = mini_fo_tri_interpose(NULL, ++ hidden_sto_dentry, ++ dentry, dir->i_sb, 1); ++ if (err) { ++ printk(KERN_CRIT "mini_fo_lookup: error interposing (state4).\n"); ++ goto out_free; ++ } ++ /* We will never need this dentry again, as the file has been ++ * deleted from base */ ++ dput(hidden_dentry); ++ goto out; ++ } ++ /* state 5, file has been deleted in base */ ++ if(hidden_dentry && hidden_sto_dentry && ++ hidden_dentry->d_inode && ++ !hidden_sto_dentry->d_inode && del_flag) { ++ ++ /* check which parents atime we need for updating */ ++ if(hidden_sto_dir_dentry->d_inode) ++ fist_copy_attr_atime(dir, ++ hidden_sto_dir_dentry->d_inode); ++ else ++ fist_copy_attr_atime(dir, ++ hidden_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = DELETED; ++ dtohd(dentry) = NULL; ++ dtohd2(dentry) = hidden_sto_dentry; ++ ++ /* add negative dentry to dcache to speed up lookups */ ++ d_add(dentry, NULL); ++ dput(hidden_dentry); ++ goto out; ++ } ++ /* state 6, file does not exist */ ++ if(((hidden_dentry && !hidden_dentry->d_inode) || ++ (hidden_sto_dentry && !hidden_sto_dentry->d_inode)) && !del_flag) ++ { ++ /* check which parents atime we need for updating */ ++ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) ++ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); ++ else ++ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); ++ ++ dtopd(dentry)->state = NON_EXISTANT; ++ dtohd(dentry) = hidden_dentry; ++ dtohd2(dentry) = hidden_sto_dentry; ++ d_add(dentry, NULL); ++ goto out; ++ } ++ ++ /* if we get to here, were in an invalid state. bad. */ ++ printk(KERN_CRIT "mini_fo_lookup: ERROR, meta data corruption detected.\n"); ++ ++ /* end state checking */ ++ out_free: ++ d_drop(dentry); /* so that our bad dentry will get destroyed */ ++ kfree(dtopd(dentry)); ++ __dtopd(dentry) = NULL; /* be safe */ ++ ++ out_dput: ++ if(hidden_dentry) ++ dput(hidden_dentry); ++ if(hidden_sto_dentry) ++ dput(hidden_sto_dentry); /* drops usage count and marks for release */ ++ ++ out: ++ /* initalize wol if file exists and is directory */ ++ if(dentry->d_inode) { ++ if(S_ISDIR(dentry->d_inode->i_mode)) { ++ itopd(dentry->d_inode)->deleted_list_size = -1; ++ itopd(dentry->d_inode)->renamed_list_size = -1; ++ meta_build_lists(dentry); ++ } ++ } ++ return ERR_PTR(err); ++} ++ ++ ++STATIC int ++mini_fo_link(dentry_t *old_dentry, inode_t *dir, dentry_t *new_dentry) ++{ ++ int err; ++ dentry_t *hidden_old_dentry; ++ dentry_t *hidden_new_dentry; ++ dentry_t *hidden_dir_dentry; ++ ++ ++ check_mini_fo_dentry(old_dentry); ++ check_mini_fo_dentry(new_dentry); ++ check_mini_fo_inode(dir); ++ ++ /* no links to directorys and existing targets target allowed */ ++ if(S_ISDIR(old_dentry->d_inode->i_mode) || ++ is_mini_fo_existant(new_dentry)) { ++ err = -EPERM; ++ goto out; ++ } ++ ++ /* bring it directly from unmod to del_rew */ ++ if(dtost(old_dentry) == UNMODIFIED) { ++ err = nondir_unmod_to_mod(old_dentry, 1); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ err = meta_add_d_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ dput(dtohd(old_dentry)); ++ dtohd(old_dentry) = NULL; ++ dtost(old_dentry) = DEL_REWRITTEN; ++ } ++ ++ err = get_neg_sto_dentry(new_dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ hidden_old_dentry = dtohd2(old_dentry); ++ hidden_new_dentry = dtohd2(new_dentry); ++ ++ dget(hidden_old_dentry); ++ dget(hidden_new_dentry); ++ ++ /* was: hidden_dir_dentry = lock_parent(hidden_new_dentry); */ ++ hidden_dir_dentry = dget(hidden_new_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = vfs_link(hidden_old_dentry, ++ hidden_dir_dentry->d_inode, ++ hidden_new_dentry); ++ if (err || !hidden_new_dentry->d_inode) ++ goto out_lock; ++ ++ dtost(new_dentry) = CREATED; ++ err = mini_fo_tri_interpose(NULL, hidden_new_dentry, new_dentry, dir->i_sb, 0); ++ if (err) ++ goto out_lock; ++ ++ fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode); ++ /* propagate number of hard-links */ ++ old_dentry->d_inode->i_nlink = itohi2(old_dentry->d_inode)->i_nlink; ++ ++ out_lock: ++ /* was: unlock_dir(hidden_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_dir_dentry); ++ ++ dput(hidden_new_dentry); ++ dput(hidden_old_dentry); ++ if (!new_dentry->d_inode) ++ d_drop(new_dentry); ++ ++ out: ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_unlink(inode_t *dir, dentry_t *dentry) ++{ ++ int err = 0; ++ ++ dget(dentry); ++ if(dtopd(dentry)->state == MODIFIED) { ++ err = nondir_mod_to_del(dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == UNMODIFIED) { ++ err = nondir_unmod_to_del(dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == CREATED) { ++ err = nondir_creat_to_del(dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == DEL_REWRITTEN) { ++ err = nondir_del_rew_to_del(dentry); ++ goto out; ++ } ++ ++ printk(KERN_CRIT "mini_fo_unlink: ERROR, invalid state detected.\n"); ++ ++ out: ++ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); ++ ++ if(!err) { ++ /* is this causing my pain? d_delete(dentry); */ ++ d_drop(dentry); ++ } ++ ++ dput(dentry); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_symlink(inode_t *dir, dentry_t *dentry, const char *symname) ++{ ++ int err=0; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ umode_t mode; ++#endif ++ ++ /* Fail if the symlink file exists */ ++ if(!(dtost(dentry) == DELETED || ++ dtost(dentry) == NON_EXISTANT)) { ++ err = -EEXIST; ++ goto out; ++ } ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ dget(hidden_sto_dentry); ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ mode = S_IALLUGO; ++ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, symname, mode); ++#else ++ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ symname); ++#endif ++ if (err || !hidden_sto_dentry->d_inode) ++ goto out_lock; ++ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } else if(dtost(dentry) == NON_EXISTANT) { ++ dtost(dentry) = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); ++ ++ out_lock: ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ dput(hidden_sto_dentry); ++ if (!dentry->d_inode) ++ d_drop(dentry); ++ out: ++ return err; ++} ++ ++STATIC int ++mini_fo_mkdir(inode_t *dir, dentry_t *dentry, int mode) ++{ ++ int err; ++ ++ err = create_sto_dir(dentry, mode); ++ ++ check_mini_fo_dentry(dentry); ++ ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_rmdir(inode_t *dir, dentry_t *dentry) ++{ ++ int err = 0; ++ ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ dentry_t *meta_dentry; ++ inode_t *hidden_sto_dir = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ check_mini_fo_inode(dir); ++ ++ dget(dentry); ++ if(dtopd(dentry)->state == MODIFIED) { ++ /* XXX: disabled, because it does not bother to check files on ++ * the original filesystem - just a hack, but better than simply ++ * removing it without testing */ ++ err = -EINVAL; ++ goto out; ++ ++ hidden_sto_dir = itohi2(dir); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was:hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* Delete an old WOL file contained in the storage dir */ ++ meta_dentry = lookup_one_len(META_FILENAME, ++ hidden_sto_dentry, ++ strlen(META_FILENAME)); ++ if(meta_dentry->d_inode) { ++ err = vfs_unlink(hidden_sto_dentry->d_inode, meta_dentry); ++ dput(meta_dentry); ++ if(!err) ++ d_delete(meta_dentry); ++ } ++ ++ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ ++ dput(dtohd(dentry)); ++ ++ dtohd(dentry) = NULL; ++ dtopd(dentry)->state = DELETED; ++ ++ /* carefull with R files */ ++ if( __meta_is_r_entry(dir, ++ dentry->d_name.name, ++ dentry->d_name.len) == 1) { ++ err = meta_remove_r_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: rmdir: meta_remove_r_entry failed.\n"); ++ goto out; ++ } ++ } ++ else { ++ /* ok, add deleted file to META */ ++ meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ } ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == UNMODIFIED) { ++ /* XXX: simply adding it to the delete list here is fscking dangerous! ++ * as a temporary hack, i will disable rmdir on unmodified directories ++ * for now. ++ */ ++ err = -EINVAL; ++ goto out; ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* dput base dentry, this will relase the inode and free the ++ * dentry, as we will never need it again. */ ++ dput(dtohd(dentry)); ++ dtohd(dentry) = NULL; ++ dtopd(dentry)->state = DELETED; ++ ++ /* add deleted file to META-file */ ++ meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ goto out; ++ } ++ else if(dtopd(dentry)->state == CREATED) { ++ hidden_sto_dir = itohi2(dir); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* Delete an old WOL file contained in the storage dir */ ++ meta_dentry = lookup_one_len(META_FILENAME, ++ hidden_sto_dentry, ++ strlen(META_FILENAME)); ++ if(meta_dentry->d_inode) { ++ /* is this necessary? dget(meta_dentry); */ ++ err = vfs_unlink(hidden_sto_dentry->d_inode, ++ meta_dentry); ++ dput(meta_dentry); ++ if(!err) ++ d_delete(meta_dentry); ++ } ++ ++ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ dtopd(dentry)->state = NON_EXISTANT; ++ ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ goto out; ++ } ++ else if(dtopd(dentry)->state == DEL_REWRITTEN) { ++ hidden_sto_dir = itohi2(dir); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* Delete an old WOL file contained in the storage dir */ ++ meta_dentry = lookup_one_len(META_FILENAME, ++ hidden_sto_dentry, ++ strlen(META_FILENAME)); ++ if(meta_dentry->d_inode) { ++ /* is this necessary? dget(meta_dentry); */ ++ err = vfs_unlink(hidden_sto_dentry->d_inode, ++ meta_dentry); ++ dput(meta_dentry); ++ if(!err) ++ d_delete(meta_dentry); ++ } ++ ++ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ dtopd(dentry)->state = DELETED; ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ goto out; ++ } ++ ++ printk(KERN_CRIT "mini_fo_rmdir: ERROR, invalid state detected.\n"); ++ ++ out: ++ if(!err) { ++ d_drop(dentry); ++ } ++ ++ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); ++ dput(dentry); ++ ++ return err; ++} ++ ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, dev_t dev) ++#else ++mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev) ++#endif ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++ err = create_sto_nod(dentry, mode, dev); ++ if(err) { ++ printk(KERN_CRIT "mini_fo_mknod: creating sto nod failed.\n"); ++ err = -EINVAL; ++ } ++ ++ check_mini_fo_dentry(dentry); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_rename(inode_t *old_dir, dentry_t *old_dentry, ++ inode_t *new_dir, dentry_t *new_dentry) ++{ ++ /* dispatch */ ++ if(S_ISDIR(old_dentry->d_inode->i_mode)) ++ return rename_directory(old_dir, old_dentry, new_dir, new_dentry); ++ return rename_nondir(old_dir, old_dentry, new_dir, new_dentry); ++ ++} ++ ++int rename_directory(inode_t *old_dir, dentry_t *old_dentry, ++ inode_t *new_dir, dentry_t *new_dentry) ++{ ++ int err, bpath_len; ++ char *bpath; ++ ++ dentry_t *hidden_old_dentry; ++ dentry_t *hidden_new_dentry; ++ dentry_t *hidden_old_dir_dentry; ++ dentry_t *hidden_new_dir_dentry; ++ ++ err = 0; ++ bpath = NULL; ++ bpath_len = 0; ++ ++ /* this is a test, chuck out if it works */ ++ if(!(dtopd(new_dentry)->state == DELETED || ++ dtopd(new_dentry)->state == NON_EXISTANT)) { ++ printk(KERN_CRIT "mini_fo: rename_directory: \ ++ uh, ah, new_dentry not negative.\n"); ++ /* return -1; */ ++ } ++ ++ /* state = UNMODIFIED */ ++ if(dtopd(old_dentry)->state == UNMODIFIED) { ++ err = dir_unmod_to_mod(old_dentry); ++ if (err) ++ goto out; ++ } ++ ++ /* state = MODIFIED */ ++ if(dtopd(old_dentry)->state == MODIFIED) { ++ bpath = meta_check_r_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if(bpath) { ++ err = meta_remove_r_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: rename_directory:\ ++ meta_remove_r_entry \ ++ failed.\n"); ++ goto out; ++ } ++ err = meta_add_r_entry(new_dentry->d_parent, ++ bpath, ++ strlen(bpath), ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ kfree(bpath); ++ } ++ else {/* wol it */ ++ err = meta_add_d_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ if (err) ++ goto out; ++ /* put it on rename list */ ++ err = get_mini_fo_bpath(old_dentry, ++ &bpath, ++ &bpath_len); ++ if (err) ++ goto out; ++ err = meta_add_r_entry(new_dentry->d_parent, ++ bpath, bpath_len, ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ if (err) ++ goto out; ++ } ++ /* no state change, MODIFIED stays MODIFIED */ ++ } ++ /* state = CREATED */ ++ if(dtopd(old_dentry)->state == CREATED || ++ dtopd(old_dentry)->state == DEL_REWRITTEN) { ++ if(dtohd(old_dentry)) ++ dput(dtohd(old_dentry)); ++ ++ if(dtopd(new_dentry)->state == DELETED) { ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ dtohd(old_dentry) = NULL; ++ } ++ else if(dtopd(new_dentry)->state == NON_EXISTANT) { ++ dtopd(old_dentry)->state = CREATED; ++ /* steal new dentry's neg. base dentry */ ++ dtohd(old_dentry) = dtohd(new_dentry); ++ dtohd(new_dentry) = NULL; ++ } ++ } ++ if(dtopd(new_dentry)->state == UNMODIFIED || ++ dtopd(new_dentry)->state == NON_EXISTANT) { ++ err = get_neg_sto_dentry(new_dentry); ++ if(err) ++ goto out; ++ } ++ ++ /* now move sto file */ ++ hidden_old_dentry = dtohd2(old_dentry); ++ hidden_new_dentry = dtohd2(new_dentry); ++ ++ dget(hidden_old_dentry); ++ dget(hidden_new_dentry); ++ ++ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); ++ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); ++ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ ++ err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, ++ hidden_new_dir_dentry->d_inode, hidden_new_dentry); ++ if(err) ++ goto out_lock; ++ ++ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); ++ if (new_dir != old_dir) ++ fist_copy_attr_all(old_dir, ++ hidden_old_dir_dentry->d_inode); ++ ++ out_lock: ++ /* double_unlock will dput the new/old parent dentries ++ * whose refcnts were incremented via get_parent above. */ ++ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ dput(hidden_new_dentry); ++ dput(hidden_old_dentry); ++ ++ out: ++ return err; ++} ++ ++int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, ++ inode_t *new_dir, dentry_t *new_dentry) ++{ ++ int err=0; ++ ++ check_mini_fo_dentry(old_dentry); ++ check_mini_fo_dentry(new_dentry); ++ check_mini_fo_inode(old_dir); ++ check_mini_fo_inode(new_dir); ++ ++ /* state: UNMODIFIED */ ++ if(dtost(old_dentry) == UNMODIFIED) { ++ err = nondir_unmod_to_mod(old_dentry, 1); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ ++ /* the easy states */ ++ if(exists_in_storage(old_dentry)) { ++ ++ dentry_t *hidden_old_dentry; ++ dentry_t *hidden_new_dentry; ++ dentry_t *hidden_old_dir_dentry; ++ dentry_t *hidden_new_dir_dentry; ++ ++ /* if old file is MODIFIED, add it to the deleted_list */ ++ if(dtopd(old_dentry)->state == MODIFIED) { ++ meta_add_d_entry(old_dentry->d_parent, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len); ++ ++ dput(dtohd(old_dentry)); ++ } ++ /* if old file is CREATED, we only release the base dentry */ ++ if(dtopd(old_dentry)->state == CREATED) { ++ if(dtohd(old_dentry)) ++ dput(dtohd(old_dentry)); ++ } ++ ++ /* now setup the new states (depends on new_dentry state) */ ++ /* new dentry state = MODIFIED */ ++ if(dtopd(new_dentry)->state == MODIFIED) { ++ meta_add_d_entry(new_dentry->d_parent, ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ ++ /* new dentry will be d_put'ed later by the vfs ++ * so don't do it here ++ * dput(dtohd(new_dentry)); ++ */ ++ dtohd(old_dentry) = NULL; ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ } ++ /* new dentry state = UNMODIFIED */ ++ else if(dtopd(new_dentry)->state == UNMODIFIED) { ++ if(get_neg_sto_dentry(new_dentry)) ++ return -EINVAL; ++ ++ meta_add_d_entry(new_dentry->d_parent, ++ new_dentry->d_name.name, ++ new_dentry->d_name.len); ++ ++ /* is this right??? */ ++ /*dput(dtohd(new_dentry));*/ ++ dtohd(old_dentry) = NULL; ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ } ++ /* new dentry state = CREATED */ ++ else if(dtopd(new_dentry)->state == CREATED) { ++ /* we keep the neg. base dentry (if exists) */ ++ dtohd(old_dentry) = dtohd(new_dentry); ++ /* ...and set it to Null, or we'll get ++ * dcache.c:345 if it gets dput twice... */ ++ dtohd(new_dentry) = NULL; ++ dtopd(old_dentry)->state = CREATED; ++ } ++ /* new dentry state = NON_EXISTANT */ ++ else if(dtopd(new_dentry)->state == NON_EXISTANT) { ++ if(get_neg_sto_dentry(new_dentry)) ++ return -EINVAL; ++ ++ /* we keep the neg. base dentry (if exists) */ ++ dtohd(old_dentry) = dtohd(new_dentry); ++ /* ...and set it to Null, or we'll get ++ * Dr. dcache.c:345 if it gets dput twice... */ ++ dtohd(new_dentry) = NULL; ++ dtopd(old_dentry)->state = CREATED; ++ } ++ /* new dentry state = DEL_REWRITTEN or DELETED */ ++ else if(dtopd(new_dentry)->state == DEL_REWRITTEN || ++ dtopd(new_dentry)->state == DELETED) { ++ dtohd(old_dentry) = NULL; ++ dtopd(old_dentry)->state = DEL_REWRITTEN; ++ } ++ else { /* not possible, uhh, ahh */ ++ printk(KERN_CRIT ++ "mini_fo: rename_reg_file: invalid state detected [1].\n"); ++ return -1; ++ } ++ ++ /* now we definitely have a sto file */ ++ hidden_old_dentry = dtohd2(old_dentry); ++ hidden_new_dentry = dtohd2(new_dentry); ++ ++ dget(hidden_old_dentry); ++ dget(hidden_new_dentry); ++ ++ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); ++ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); ++ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ ++ err = vfs_rename(hidden_old_dir_dentry->d_inode, ++ hidden_old_dentry, ++ hidden_new_dir_dentry->d_inode, ++ hidden_new_dentry); ++ if(err) ++ goto out_lock; ++ ++ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); ++ if (new_dir != old_dir) ++ fist_copy_attr_all(old_dir, hidden_old_dir_dentry->d_inode); ++ ++ out_lock: ++ /* double_unlock will dput the new/old parent dentries ++ * whose refcnts were incremented via get_parent above. ++ */ ++ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); ++ dput(hidden_new_dentry); ++ dput(hidden_old_dentry); ++ out: ++ return err; ++ } ++ else { /* invalid state */ ++ printk(KERN_CRIT "mini_fo: rename_reg_file: ERROR: invalid state detected [2].\n"); ++ return -1; ++ } ++} ++ ++ ++STATIC int ++mini_fo_readlink(dentry_t *dentry, char *buf, int bufsiz) ++{ ++ int err=0; ++ dentry_t *hidden_dentry = NULL; ++ ++ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { ++ hidden_dentry = dtohd2(dentry); ++ } else if(dtohd(dentry) && dtohd(dentry)->d_inode) { ++ hidden_dentry = dtohd(dentry); ++ } else { ++ goto out; ++ } ++ ++ if (!hidden_dentry->d_inode->i_op || ++ !hidden_dentry->d_inode->i_op->readlink) { ++ err = -EINVAL; goto out; ++ } ++ ++ err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, ++ buf, ++ bufsiz); ++ if (err > 0) ++ fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); ++ ++ out: ++ return err; ++} ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ++static int mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) ++#else ++static void* mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) ++#endif ++{ ++ char *buf; ++ int len = PAGE_SIZE, err; ++ mm_segment_t old_fs; ++ ++ /* in 2.6 this is freed by mini_fo_put_link called by __do_follow_link */ ++ buf = kmalloc(len, GFP_KERNEL); ++ if (!buf) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* read the symlink, and then we will follow it */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = dentry->d_inode->i_op->readlink(dentry, buf, len); ++ set_fs(old_fs); ++ if (err < 0) { ++ kfree(buf); ++ buf = NULL; ++ goto out; ++ } ++ buf[err] = 0; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ nd_set_link(nd, buf); ++ err = 0; ++#else ++ err = vfs_follow_link(nd, buf); ++#endif ++ ++ out: ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ kfree(buf); ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ++ return err; ++#else ++ return ERR_PTR(err); ++#endif ++} ++ ++STATIC ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) ++void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd) ++#else ++void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) ++#endif ++{ ++ char *link; ++ link = nd_get_link(nd); ++ kfree(link); ++} ++#endif ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_permission(inode_t *inode, int mask, struct nameidata *nd) ++#else ++mini_fo_permission(inode_t *inode, int mask) ++#endif ++{ ++ inode_t *hidden_inode; ++ int mode; ++ int err; ++ ++ if(itohi2(inode)) { ++ hidden_inode = itohi2(inode); ++ } else { ++ hidden_inode = itohi(inode); ++ } ++ mode = inode->i_mode; ++ ++ /* not really needed, as permission handles everything: ++ * err = vfs_permission(inode, mask); ++ * if (err) ++ * goto out; ++ */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = permission(hidden_inode, mask, nd); ++#else ++ err = permission(hidden_inode, mask); ++#endif ++ ++ /* out: */ ++ return err; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++STATIC int ++mini_fo_inode_revalidate(dentry_t *dentry) ++{ ++ int err = 0; ++ dentry_t *hidden_dentry; ++ inode_t *hidden_inode; ++ ++ ASSERT(dentry->d_inode); ++ ASSERT(itopd(dentry->d_inode)); ++ ++ if(itohi2(dentry->d_inode)) { ++ hidden_dentry = dtohd2(dentry); ++ hidden_inode = hidden_dentry->d_inode; ++ } else if(itohi(dentry->d_inode)) { ++ hidden_dentry = dtohd(dentry); ++ hidden_inode = hidden_dentry->d_inode; ++ } else { ++ printk(KERN_CRIT "mini_fo_inode_revalidate: ERROR, invalid state detected.\n"); ++ err = -ENOENT; ++ goto out; ++ } ++ if (hidden_inode && hidden_inode->i_op && hidden_inode->i_op->revalidate){ ++ err = hidden_inode->i_op->revalidate(hidden_dentry); ++ if (err) ++ goto out; ++ } ++ fist_copy_attr_all(dentry->d_inode, hidden_inode); ++ out: ++ return err; ++} ++#endif ++ ++STATIC int ++mini_fo_setattr(dentry_t *dentry, struct iattr *ia) ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(!is_mini_fo_existant(dentry)) { ++ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [1].\n"); ++ goto out; ++ } ++ ++ if(dtost(dentry) == UNMODIFIED) { ++ if(!IS_COPY_FLAG(ia->ia_valid)) ++ goto out; /* we ignore these changes to base */ ++ ++ if(S_ISDIR(dentry->d_inode->i_mode)) { ++ err = dir_unmod_to_mod(dentry); ++ } else { ++ /* we copy contents if file is not beeing truncated */ ++ if(S_ISREG(dentry->d_inode->i_mode) && ++ !(ia->ia_size == 0 && (ia->ia_valid & ATTR_SIZE))) { ++ err = nondir_unmod_to_mod(dentry, 1); ++ } else ++ err = nondir_unmod_to_mod(dentry, 0); ++ } ++ if(err) { ++ err = -EINVAL; ++ printk(KERN_CRIT "mini_fo_setattr: ERROR changing states.\n"); ++ goto out; ++ } ++ } ++ if(!exists_in_storage(dentry)) { ++ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [2].\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ASSERT(dentry->d_inode); ++ ASSERT(dtohd2(dentry)); ++ ASSERT(itopd(dentry->d_inode)); ++ ASSERT(itohi2(dentry->d_inode)); ++ ++ err = notify_change(dtohd2(dentry), ia); ++ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); ++ out: ++ return err; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++STATIC int ++mini_fo_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) ++{ ++ int err = 0; ++ dentry_t *hidden_dentry; ++ ++ ASSERT(dentry->d_inode); ++ ASSERT(itopd(dentry->d_inode)); ++ ++ if(itohi2(dentry->d_inode)) { ++ hidden_dentry = dtohd2(dentry); ++ } else if(itohi(dentry->d_inode)) { ++ hidden_dentry = dtohd(dentry); ++ } else { ++ printk(KERN_CRIT "mini_fo_getattr: ERROR, invalid state detected.\n"); ++ err = -ENOENT; ++ goto out; ++ } ++ fist_copy_attr_all(dentry->d_inode, hidden_dentry->d_inode); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ generic_fillattr(dentry->d_inode, stat); ++ if (!stat->blksize) { ++ struct super_block *s = hidden_dentry->d_inode->i_sb; ++ unsigned blocks; ++ blocks = (stat->size+s->s_blocksize-1) >> s->s_blocksize_bits; ++ stat->blocks = (s->s_blocksize / 512) * blocks; ++ stat->blksize = s->s_blocksize; ++ } ++ out: ++ return err; ++} ++#endif ++ ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++#if 0 /* no xattr_alloc() and xattr_free() */ ++/* This is lifted from fs/xattr.c */ ++static void * ++xattr_alloc(size_t size, size_t limit) ++{ ++ void *ptr; ++ ++ if (size > limit) ++ return ERR_PTR(-E2BIG); ++ ++ if (!size) /* size request, no buffer is needed */ ++ return NULL; ++ else if (size <= PAGE_SIZE) ++ ptr = kmalloc((unsigned long) size, GFP_KERNEL); ++ else ++ ptr = vmalloc((unsigned long) size); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ return ptr; ++} ++ ++static void ++xattr_free(void *ptr, size_t size) ++{ ++ if (!size) /* size request, no buffer was needed */ ++ return; ++ else if (size <= PAGE_SIZE) ++ kfree(ptr); ++ else ++ vfree(ptr); ++} ++#endif /* no xattr_alloc() and xattr_free() */ ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++mini_fo_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ /* Define these anyway so we don't need as much ifdef'ed code. */ ++ char *encoded_name = NULL; ++ char *encoded_value = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->getxattr) { ++ encoded_name = (char *)name; ++ encoded_value = (char *)value; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) \ ++ && LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,23)) \ ++ || LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) ++mini_fo_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++#else ++mini_fo_setxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size, int flags) ++#endif ++ ++{ ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ ++ /* Define these anyway, so we don't have as much ifdef'ed code. */ ++ char *encoded_value = NULL; ++ char *encoded_name = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->setxattr) { ++ encoded_name = (char *)name; ++ encoded_value = (char *)value; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->setxattr(hidden_dentry, encoded_name, encoded_value, size, flags); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++mini_fo_removexattr(struct dentry *dentry, const char *name) { ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ char *encoded_name; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->removexattr) { ++ encoded_name = (char *)name; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++ ++/* BKL held by caller. ++ * dentry->d_inode->i_sem down ++ */ ++STATIC int ++mini_fo_listxattr(struct dentry *dentry, char *list, size_t size) { ++ struct dentry *hidden_dentry = NULL; ++ int err = -EOPNOTSUPP; ++ char *encoded_list = NULL; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(exists_in_storage(dentry)) ++ hidden_dentry = dtohd2(dentry); ++ else ++ hidden_dentry = dtohd(dentry); ++ ++ ASSERT(hidden_dentry); ++ ASSERT(hidden_dentry->d_inode); ++ ASSERT(hidden_dentry->d_inode->i_op); ++ ++ if (hidden_dentry->d_inode->i_op->listxattr) { ++ encoded_list = list; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_dentry->d_inode->i_sem); ++#endif ++ /* lock_kernel() already done by caller. */ ++ err = hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, encoded_list, size); ++ /* unlock_kernel() will be done by caller. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_dentry->d_inode->i_sem); ++#endif ++ } ++ return err; ++} ++# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ ++ ++struct inode_operations mini_fo_symlink_iops = ++ { ++ readlink: mini_fo_readlink, ++ follow_link: mini_fo_follow_link, ++ /* mk: permission: mini_fo_permission, */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ revalidate: mini_fo_inode_revalidate, ++#endif ++ setattr: mini_fo_setattr, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ getattr: mini_fo_getattr, ++ put_link: mini_fo_put_link, ++#endif ++ ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++ setxattr: mini_fo_setxattr, ++ getxattr: mini_fo_getxattr, ++ listxattr: mini_fo_listxattr, ++ removexattr: mini_fo_removexattr ++# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ ++ }; ++ ++struct inode_operations mini_fo_dir_iops = ++ { ++ create: mini_fo_create, ++ lookup: mini_fo_lookup, ++ link: mini_fo_link, ++ unlink: mini_fo_unlink, ++ symlink: mini_fo_symlink, ++ mkdir: mini_fo_mkdir, ++ rmdir: mini_fo_rmdir, ++ mknod: mini_fo_mknod, ++ rename: mini_fo_rename, ++ /* no readlink/follow_link for non-symlinks */ ++ // off because we have setattr ++ // truncate: mini_fo_truncate, ++ /* mk:permission: mini_fo_permission, */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ revalidate: mini_fo_inode_revalidate, ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ getattr: mini_fo_getattr, ++#endif ++ setattr: mini_fo_setattr, ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++ setxattr: mini_fo_setxattr, ++ getxattr: mini_fo_getxattr, ++ listxattr: mini_fo_listxattr, ++ removexattr: mini_fo_removexattr ++# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ ++ }; ++ ++struct inode_operations mini_fo_main_iops = ++ { ++ /* permission: mini_fo_permission, */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ revalidate: mini_fo_inode_revalidate, ++#endif ++ setattr: mini_fo_setattr, ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ getattr: mini_fo_getattr, ++#endif ++#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) ++ setxattr: mini_fo_setxattr, ++ getxattr: mini_fo_getxattr, ++ listxattr: mini_fo_listxattr, ++ removexattr: mini_fo_removexattr ++# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ ++ }; +--- /dev/null ++++ b/fs/mini_fo/main.c +@@ -0,0 +1,423 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++#include ++ ++/* This definition must only appear after we include */ ++#ifndef MODULE_LICENSE ++# define MODULE_LICENSE(bison) ++#endif /* not MODULE_LICENSE */ ++ ++/* ++ * This is the mini_fo tri interpose function, which extends the ++ * functionality of the regular interpose by interposing a higher ++ * level inode on top of two lower level ones: the base filesystem ++ * inode and the storage filesystem inode. ++ * ++ * sb we pass is mini_fo's super_block ++ */ ++int ++mini_fo_tri_interpose(dentry_t *hidden_dentry, ++ dentry_t *hidden_sto_dentry, ++ dentry_t *dentry, super_block_t *sb, int flag) ++{ ++ inode_t *hidden_inode = NULL; ++ inode_t *hidden_sto_inode = NULL; /* store corresponding storage inode */ ++ int err = 0; ++ inode_t *inode; ++ ++ /* Pointer to hidden_sto_inode if exists, else to hidden_inode. ++ * This is used to copy the attributes of the correct inode. */ ++ inode_t *master_inode; ++ ++ if(hidden_dentry) ++ hidden_inode = hidden_dentry->d_inode; ++ if(hidden_sto_dentry) ++ hidden_sto_inode = hidden_sto_dentry->d_inode; ++ ++ ASSERT(dentry->d_inode == NULL); ++ ++ /* mk: One of the inodes associated with the dentrys is likely to ++ * be NULL, so carefull: ++ */ ++ ASSERT((hidden_inode != NULL) || (hidden_sto_inode != NULL)); ++ ++ if(hidden_sto_inode) ++ master_inode = hidden_sto_inode; ++ else ++ master_inode = hidden_inode; ++ ++ /* ++ * We allocate our new inode below, by calling iget. ++ * iget will call our read_inode which will initialize some ++ * of the new inode's fields ++ */ ++ ++ /* ++ * original: inode = iget(sb, hidden_inode->i_ino); ++ */ ++ inode = iget(sb, iunique(sb, 25)); ++ if (!inode) { ++ err = -EACCES; /* should be impossible??? */ ++ goto out; ++ } ++ ++ /* ++ * interpose the inode if not already interposed ++ * this is possible if the inode is being reused ++ * XXX: what happens if we get_empty_inode() but there's another already? ++ * for now, ASSERT() that this can't happen; fix later. ++ */ ++ if (itohi(inode) != NULL) { ++ printk(KERN_CRIT "mini_fo_tri_interpose: itohi(inode) != NULL.\n"); ++ } ++ if (itohi2(inode) != NULL) { ++ printk(KERN_CRIT "mini_fo_tri_interpose: itohi2(inode) != NULL.\n"); ++ } ++ ++ /* mk: Carefull, igrab can't handle NULL inodes (ok, why should it?), so ++ * we need to check here: ++ */ ++ if(hidden_inode) ++ itohi(inode) = igrab(hidden_inode); ++ else ++ itohi(inode) = NULL; ++ ++ if(hidden_sto_inode) ++ itohi2(inode) = igrab(hidden_sto_inode); ++ else ++ itohi2(inode) = NULL; ++ ++ ++ /* Use different set of inode ops for symlinks & directories*/ ++ if (S_ISLNK(master_inode->i_mode)) ++ inode->i_op = &mini_fo_symlink_iops; ++ else if (S_ISDIR(master_inode->i_mode)) ++ inode->i_op = &mini_fo_dir_iops; ++ ++ /* Use different set of file ops for directories */ ++ if (S_ISDIR(master_inode->i_mode)) ++ inode->i_fop = &mini_fo_dir_fops; ++ ++ /* properly initialize special inodes */ ++ if (S_ISBLK(master_inode->i_mode) || S_ISCHR(master_inode->i_mode) || ++ S_ISFIFO(master_inode->i_mode) || S_ISSOCK(master_inode->i_mode)) { ++ init_special_inode(inode, master_inode->i_mode, master_inode->i_rdev); ++ } ++ ++ /* Fix our inode's address operations to that of the lower inode */ ++ if (inode->i_mapping->a_ops != master_inode->i_mapping->a_ops) { ++ inode->i_mapping->a_ops = master_inode->i_mapping->a_ops; ++ } ++ ++ /* only (our) lookup wants to do a d_add */ ++ if (flag) ++ d_add(dentry, inode); ++ else ++ d_instantiate(dentry, inode); ++ ++ ASSERT(dtopd(dentry) != NULL); ++ ++ /* all well, copy inode attributes */ ++ fist_copy_attr_all(inode, master_inode); ++ ++ out: ++ return err; ++} ++ ++/* parse mount options "base=" and "sto=" */ ++dentry_t * ++mini_fo_parse_options(super_block_t *sb, char *options) ++{ ++ dentry_t *hidden_root = ERR_PTR(-EINVAL); ++ dentry_t *hidden_root2 = ERR_PTR(-EINVAL); ++ struct nameidata nd, nd2; ++ char *name, *tmp, *end; ++ int err = 0; ++ ++ /* We don't want to go off the end of our arguments later on. */ ++ for (end = options; *end; end++); ++ ++ while (options < end) { ++ tmp = options; ++ while (*tmp && *tmp != ',') ++ tmp++; ++ *tmp = '\0'; ++ if (!strncmp("base=", options, 5)) { ++ name = options + 5; ++ printk(KERN_INFO "mini_fo: using base directory: %s\n", name); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ if (path_init(name, LOOKUP_FOLLOW, &nd)) ++ err = path_walk(name, &nd); ++#else ++ err = path_lookup(name, LOOKUP_FOLLOW, &nd); ++#endif ++ if (err) { ++ printk(KERN_CRIT "mini_fo: error accessing hidden directory '%s'\n", name); ++ hidden_root = ERR_PTR(err); ++ goto out; ++ } ++ hidden_root = nd.dentry; ++ stopd(sb)->base_dir_dentry = nd.dentry; ++ stopd(sb)->hidden_mnt = nd.mnt; ++ ++ } else if(!strncmp("sto=", options, 4)) { ++ /* parse the storage dir */ ++ name = options + 4; ++ printk(KERN_INFO "mini_fo: using storage directory: %s\n", name); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ if(path_init(name, LOOKUP_FOLLOW, &nd2)) ++ err = path_walk(name, &nd2); ++#else ++ err = path_lookup(name, LOOKUP_FOLLOW, &nd2); ++#endif ++ if(err) { ++ printk(KERN_CRIT "mini_fo: error accessing hidden storage directory '%s'\n", name); ++ ++ hidden_root2 = ERR_PTR(err); ++ goto out; ++ } ++ hidden_root2 = nd2.dentry; ++ stopd(sb)->storage_dir_dentry = nd2.dentry; ++ stopd(sb)->hidden_mnt2 = nd2.mnt; ++ stohs2(sb) = hidden_root2->d_sb; ++ ++ /* validate storage dir, this is done in ++ * mini_fo_read_super for the base directory. ++ */ ++ if (IS_ERR(hidden_root2)) { ++ printk(KERN_WARNING "mini_fo_parse_options: storage dentry lookup failed (err = %ld)\n", PTR_ERR(hidden_root2)); ++ goto out; ++ } ++ if (!hidden_root2->d_inode) { ++ printk(KERN_WARNING "mini_fo_parse_options: no storage dir to interpose on.\n"); ++ goto out; ++ } ++ stohs2(sb) = hidden_root2->d_sb; ++ } else { ++ printk(KERN_WARNING "mini_fo: unrecognized option '%s'\n", options); ++ hidden_root = ERR_PTR(-EINVAL); ++ goto out; ++ } ++ options = tmp + 1; ++ } ++ ++ out: ++ if(IS_ERR(hidden_root2)) ++ return hidden_root2; ++ return hidden_root; ++} ++ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++static int ++#else ++super_block_t * ++#endif ++mini_fo_read_super(super_block_t *sb, void *raw_data, int silent) ++{ ++ dentry_t *hidden_root; ++ int err = 0; ++ ++ if (!raw_data) { ++ printk(KERN_WARNING "mini_fo_read_super: missing argument\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ /* ++ * Allocate superblock private data ++ */ ++ __stopd(sb) = kmalloc(sizeof(struct mini_fo_sb_info), GFP_KERNEL); ++ if (!stopd(sb)) { ++ printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__); ++ err = -ENOMEM; ++ goto out; ++ } ++ stohs(sb) = NULL; ++ ++ hidden_root = mini_fo_parse_options(sb, raw_data); ++ if (IS_ERR(hidden_root)) { ++ printk(KERN_WARNING "mini_fo_read_super: lookup_dentry failed (err = %ld)\n", PTR_ERR(hidden_root)); ++ err = PTR_ERR(hidden_root); ++ goto out_free; ++ } ++ if (!hidden_root->d_inode) { ++ printk(KERN_WARNING "mini_fo_read_super: no directory to interpose on\n"); ++ goto out_free; ++ } ++ stohs(sb) = hidden_root->d_sb; ++ ++ /* ++ * Linux 2.4.2-ac3 and beyond has code in ++ * mm/filemap.c:generic_file_write() that requires sb->s_maxbytes ++ * to be populated. If not set, all write()s under that sb will ++ * return 0. ++ * ++ * Linux 2.4.4+ automatically sets s_maxbytes to MAX_NON_LFS; ++ * the filesystem should override it only if it supports LFS. ++ */ ++ /* non-SCA code is good to go with LFS */ ++ sb->s_maxbytes = hidden_root->d_sb->s_maxbytes; ++ ++ sb->s_op = &mini_fo_sops; ++ /* ++ * we can't use d_alloc_root if we want to use ++ * our own interpose function unchanged, ++ * so we simply replicate *most* of the code in d_alloc_root here ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); ++#else ++ sb->s_root = d_alloc(NULL, &(const struct qstr){hash: 0, name: "/", len : 1}); ++#endif ++ if (IS_ERR(sb->s_root)) { ++ printk(KERN_WARNING "mini_fo_read_super: d_alloc failed\n"); ++ err = -ENOMEM; ++ goto out_dput; ++ } ++ ++ sb->s_root->d_op = &mini_fo_dops; ++ sb->s_root->d_sb = sb; ++ sb->s_root->d_parent = sb->s_root; ++ ++ /* link the upper and lower dentries */ ++ __dtopd(sb->s_root) = (struct mini_fo_dentry_info *) ++ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); ++ if (!dtopd(sb->s_root)) { ++ err = -ENOMEM; ++ goto out_dput2; ++ } ++ dtopd(sb->s_root)->state = MODIFIED; ++ dtohd(sb->s_root) = hidden_root; ++ ++ /* fanout relevant, interpose on storage root dentry too */ ++ dtohd2(sb->s_root) = stopd(sb)->storage_dir_dentry; ++ ++ /* ...and call tri-interpose to interpose root dir inodes ++ * if (mini_fo_interpose(hidden_root, sb->s_root, sb, 0)) ++ */ ++ if(mini_fo_tri_interpose(hidden_root, dtohd2(sb->s_root), sb->s_root, sb, 0)) ++ goto out_dput2; ++ ++ /* initalize the wol list */ ++ itopd(sb->s_root->d_inode)->deleted_list_size = -1; ++ itopd(sb->s_root->d_inode)->renamed_list_size = -1; ++ meta_build_lists(sb->s_root); ++ ++ goto out; ++ ++ out_dput2: ++ dput(sb->s_root); ++ out_dput: ++ dput(hidden_root); ++ dput(dtohd2(sb->s_root)); /* release the hidden_sto_dentry too */ ++ out_free: ++ kfree(stopd(sb)); ++ __stopd(sb) = NULL; ++ out: ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ return err; ++#else ++ if (err) { ++ return ERR_PTR(err); ++ } else { ++ return sb; ++ } ++#endif ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++static int mini_fo_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data, struct vfsmount *mnt) ++{ ++ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super, mnt); ++} ++#else ++static struct super_block *mini_fo_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data) ++{ ++ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super); ++} ++#endif ++ ++void mini_fo_kill_block_super(struct super_block *sb) ++{ ++ generic_shutdown_super(sb); ++ /* ++ * XXX: BUG: Halcrow: Things get unstable sometime after this point: ++ * lib/rwsem-spinlock.c:127: spin_is_locked on uninitialized ++ * fs/fs-writeback.c:402: spin_lock(fs/super.c:a0381828) already ++ * locked by fs/fs-writeback.c/402 ++ * ++ * Apparently, someone's not releasing a lock on sb_lock... ++ */ ++} ++ ++static struct file_system_type mini_fo_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "mini_fo", ++ .get_sb = mini_fo_get_sb, ++ .kill_sb = mini_fo_kill_block_super, ++ .fs_flags = 0, ++}; ++ ++ ++#else ++static DECLARE_FSTYPE(mini_fo_fs_type, "mini_fo", mini_fo_read_super, 0); ++#endif ++ ++static int __init init_mini_fo_fs(void) ++{ ++ printk("Registering mini_fo version $Id$\n"); ++ return register_filesystem(&mini_fo_fs_type); ++} ++static void __exit exit_mini_fo_fs(void) ++{ ++ printk("Unregistering mini_fo version $Id$\n"); ++ unregister_filesystem(&mini_fo_fs_type); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++EXPORT_NO_SYMBOLS; ++#endif ++ ++MODULE_AUTHOR("Erez Zadok "); ++MODULE_DESCRIPTION("FiST-generated mini_fo filesystem"); ++MODULE_LICENSE("GPL"); ++ ++/* MODULE_PARM(fist_debug_var, "i"); */ ++/* MODULE_PARM_DESC(fist_debug_var, "Debug level"); */ ++ ++module_init(init_mini_fo_fs) ++module_exit(exit_mini_fo_fs) +--- /dev/null ++++ b/fs/mini_fo/Makefile +@@ -0,0 +1,17 @@ ++# ++# Makefile for mini_fo 2.4 and 2.6 Linux kernels ++# ++# Copyright (C) 2004, 2005 Markus Klotzbuecher ++# ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version ++# 2 of the License, or (at your option) any later version. ++# ++ ++obj-$(CONFIG_MINI_FO) := mini_fo.o ++mini_fo-objs := meta.o dentry.o file.o inode.o main.o super.o state.o aux.o ++ ++# dependencies ++${mini_fo-objs}: mini_fo.h fist.h ++ +--- /dev/null ++++ b/fs/mini_fo/meta.c +@@ -0,0 +1,1000 @@ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++#include "fist.h" ++#include "mini_fo.h" ++ ++int meta_build_lists(dentry_t *dentry) ++{ ++ struct mini_fo_inode_info *inode_info; ++ ++ dentry_t *meta_dentry = 0; ++ file_t *meta_file = 0; ++ mm_segment_t old_fs; ++ void *buf; ++ ++ int bytes, len; ++ struct vfsmount *meta_mnt; ++ char *entry; ++ ++ inode_info = itopd(dentry->d_inode); ++ if(!(inode_info->deleted_list_size == -1 && ++ inode_info->renamed_list_size == -1)) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ Error, list(s) not virgin.\n"); ++ return -1; ++ } ++ ++ /* init our meta lists */ ++ INIT_LIST_HEAD(&inode_info->deleted_list); ++ inode_info->deleted_list_size = 0; ++ ++ INIT_LIST_HEAD(&inode_info->renamed_list); ++ inode_info->renamed_list_size = 0; ++ ++ /* might there be a META-file? */ ++ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen(META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ dput(meta_dentry); ++ goto out_ok; ++ } ++ /* $%& err, is this correct? */ ++ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ mntget(meta_mnt); ++ ++ ++ /* open META-file for reading */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x0); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ ERROR opening META file.\n"); ++ goto out_err; ++ } ++ ++ /* check if fs supports reading */ ++ if(!meta_file->f_op->read) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ ERROR, fs does not support reading.\n"); ++ goto out_err_close; ++ } ++ ++ /* allocate a page for transfering the data */ ++ buf = (void *) __get_free_page(GFP_KERNEL); ++ if(!buf) { ++ printk(KERN_CRIT "mini_fo: meta_build_lists: \ ++ ERROR, out of mem.\n"); ++ goto out_err_close; ++ } ++ meta_file->f_pos = 0; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ char *c; ++ bytes = meta_file->f_op->read(meta_file, buf, PAGE_SIZE, &meta_file->f_pos); ++ if(bytes == PAGE_SIZE) { ++ /* trim a cut off filename and adjust f_pos to get it next time */ ++ for(c = (char*) buf+PAGE_SIZE; ++ *c != '\n'; ++ c--, bytes--, meta_file->f_pos--); ++ } ++ entry = (char *) buf; ++ while(entry < (char *) buf+bytes) { ++ ++ char *old_path; ++ char *dir_name; ++ int old_len, new_len; ++ ++ /* len without '\n'*/ ++ len = (int) (strchr(entry, '\n') - entry); ++ switch (*entry) { ++ case 'D': ++ /* format: "D filename" */ ++ meta_list_add_d_entry(dentry, ++ entry+2, ++ len-2); ++ break; ++ case 'R': ++ /* format: "R path/xy/dir newDir" */ ++ old_path = entry+2; ++ dir_name = strchr(old_path, ' ') + 1; ++ old_len = dir_name - old_path - 1; ++ new_len = ((int) entry) + len - ((int ) dir_name); ++ meta_list_add_r_entry(dentry, ++ old_path, ++ old_len, ++ dir_name, ++ new_len); ++ break; ++ default: ++ /* unknown entry type detected */ ++ break; ++ } ++ entry += len+1; ++ } ++ ++ } while(meta_file->f_pos < meta_dentry->d_inode->i_size); ++ ++ free_page((unsigned long) buf); ++ set_fs(old_fs); ++ fput(meta_file); ++ } ++ goto out_ok; ++ ++ out_err_close: ++ fput(meta_file); ++ out_err: ++ mntput(meta_mnt); ++ dput(meta_dentry); ++ return -1; ++ out_ok: ++ return 1; /* check this!!! inode_info->wol_size; */ ++} ++ ++/* cleanups up all lists and free's the mem by dentry */ ++int meta_put_lists(dentry_t *dentry) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk("mini_fo: meta_put_lists: invalid dentry passed.\n"); ++ return -1; ++ } ++ return __meta_put_lists(dentry->d_inode); ++} ++ ++/* cleanups up all lists and free's the mem by inode */ ++int __meta_put_lists(inode_t *inode) ++{ ++ int err = 0; ++ if(!inode || !itopd(inode)) { ++ printk("mini_fo: __meta_put_lists: invalid inode passed.\n"); ++ return -1; ++ } ++ err = __meta_put_d_list(inode); ++ err |= __meta_put_r_list(inode); ++ return err; ++} ++ ++int meta_sync_lists(dentry_t *dentry) ++{ ++ int err = 0; ++ if(!dentry || !dentry->d_inode) { ++ printk("mini_fo: meta_sync_lists: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ err = meta_sync_d_list(dentry, 0); ++ err |= meta_sync_r_list(dentry, 1); ++ return err; ++} ++ ++ ++/* remove all D entries from the renamed list and free the mem */ ++int __meta_put_d_list(inode_t *inode) ++{ ++ struct list_head *tmp; ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: __meta_put_d_list: \ ++ invalid inode passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(inode); ++ ++ /* nuke the DELETED-list */ ++ if(inode_info->deleted_list_size <= 0) ++ return 0; ++ ++ while(!list_empty(&inode_info->deleted_list)) { ++ tmp = inode_info->deleted_list.next; ++ list_del(tmp); ++ del_entry = list_entry(tmp, struct deleted_entry, list); ++ kfree(del_entry->name); ++ kfree(del_entry); ++ } ++ inode_info->deleted_list_size = 0; ++ ++ return 0; ++} ++ ++/* remove all R entries from the renamed list and free the mem */ ++int __meta_put_r_list(inode_t *inode) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: meta_put_r_list: invalid inode.\n"); ++ return -1; ++ } ++ inode_info = itopd(inode); ++ ++ /* nuke the RENAMED-list */ ++ if(inode_info->renamed_list_size <= 0) ++ return 0; ++ ++ while(!list_empty(&inode_info->renamed_list)) { ++ tmp = inode_info->renamed_list.next; ++ list_del(tmp); ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ kfree(ren_entry->new_name); ++ kfree(ren_entry->old_name); ++ kfree(ren_entry); ++ } ++ inode_info->renamed_list_size = 0; ++ ++ return 0; ++} ++ ++int meta_add_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ int err = 0; ++ err = meta_list_add_d_entry(dentry, name, len); ++ err |= meta_write_d_entry(dentry,name,len); ++ return err; ++} ++ ++/* add a D entry to the deleted list */ ++int meta_list_add_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->deleted_list_size < 0) ++ return -1; ++ ++ del_entry = (struct deleted_entry *) ++ kmalloc(sizeof(struct deleted_entry), GFP_KERNEL); ++ del_entry->name = (char*) kmalloc(len, GFP_KERNEL); ++ if(!del_entry || !del_entry->name) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ ++ out of mem.\n"); ++ kfree(del_entry->name); ++ kfree(del_entry); ++ return -ENOMEM; ++ } ++ ++ strncpy(del_entry->name, name, len); ++ del_entry->len = len; ++ ++ list_add(&del_entry->list, &inode_info->deleted_list); ++ inode_info->deleted_list_size++; ++ return 0; ++} ++ ++int meta_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len) ++{ ++ int err = 0; ++ err = meta_list_add_r_entry(dentry, ++ old_name, old_len, ++ new_name, new_len); ++ err |= meta_write_r_entry(dentry, ++ old_name, old_len, ++ new_name, new_len); ++ return err; ++} ++ ++/* add a R entry to the renamed list */ ++int meta_list_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len) ++{ ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->renamed_list_size < 0) ++ return -1; ++ ++ ren_entry = (struct renamed_entry *) ++ kmalloc(sizeof(struct renamed_entry), GFP_KERNEL); ++ ren_entry->old_name = (char*) kmalloc(old_len, GFP_KERNEL); ++ ren_entry->new_name = (char*) kmalloc(new_len, GFP_KERNEL); ++ ++ if(!ren_entry || !ren_entry->old_name || !ren_entry->new_name) { ++ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ ++ out of mem.\n"); ++ kfree(ren_entry->new_name); ++ kfree(ren_entry->old_name); ++ kfree(ren_entry); ++ return -ENOMEM; ++ } ++ ++ strncpy(ren_entry->old_name, old_name, old_len); ++ ren_entry->old_len = old_len; ++ strncpy(ren_entry->new_name, new_name, new_len); ++ ren_entry->new_len = new_len; ++ ++ list_add(&ren_entry->list, &inode_info->renamed_list); ++ inode_info->renamed_list_size++; ++ return 0; ++} ++ ++ ++int meta_remove_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ int err = 0; ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT ++ "mini_fo: meta_remove_r_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ ++ err = meta_list_remove_r_entry(dentry, name, len); ++ err |= meta_sync_lists(dentry); ++ return err; ++} ++ ++int meta_list_remove_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT ++ "mini_fo: meta_list_remove_r_entry: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ return __meta_list_remove_r_entry(dentry->d_inode, name, len); ++} ++ ++int __meta_list_remove_r_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) ++ printk(KERN_CRIT ++ "mini_fo: __meta_list_remove_r_entry: \ ++ invalid inode passed.\n"); ++ inode_info = itopd(inode); ++ ++ if(inode_info->renamed_list_size < 0) ++ return -1; ++ if(inode_info->renamed_list_size == 0) ++ return 1; ++ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ if(ren_entry->new_len != len) ++ continue; ++ ++ if(!strncmp(ren_entry->new_name, name, len)) { ++ list_del(tmp); ++ kfree(ren_entry->new_name); ++ kfree(ren_entry->old_name); ++ kfree(ren_entry); ++ inode_info->renamed_list_size--; ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++ ++/* append a single D entry to the meta file */ ++int meta_write_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ dentry_t *meta_dentry = 0; ++ file_t *meta_file = 0; ++ mm_segment_t old_fs; ++ ++ int bytes, err; ++ struct vfsmount *meta_mnt = 0; ++ char *buf; ++ ++ err = 0; ++ ++ if(itopd(dentry->d_inode)->deleted_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ if(dtopd(dentry)->state == UNMODIFIED) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), strlen (META_FILENAME)); ++ ++ /* We need to create a META-file */ ++ if(!meta_dentry->d_inode) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, ++ S_IRUSR | S_IWUSR, ++ NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, ++ S_IRUSR | S_IWUSR); ++#endif ++ } ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ ERROR opening meta file.\n"); ++ mntput(meta_mnt); /* $%& is this necessary? */ ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* size: len for name, 1 for \n and 2 for "D " */ ++ buf = (char *) kmalloc(len+3, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ out of mem.\n"); ++ return -ENOMEM; ++ } ++ ++ buf[0] = 'D'; ++ buf[1] = ' '; ++ strncpy(buf+2, name, len); ++ buf[len+2] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, len+3, ++ &meta_file->f_pos); ++ if(bytes != len+3) { ++ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ ++ ERROR writing.\n"); ++ err = -1; ++ } ++ kfree(buf); ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++} ++ ++/* append a single R entry to the meta file */ ++int meta_write_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len) ++{ ++ dentry_t *meta_dentry = 0; ++ file_t *meta_file = 0; ++ mm_segment_t old_fs; ++ ++ int bytes, err, buf_len; ++ struct vfsmount *meta_mnt = 0; ++ char *buf; ++ ++ ++ err = 0; ++ ++ if(itopd(dentry->d_inode)->renamed_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ /* build the storage structure? */ ++ if(dtopd(dentry)->state == UNMODIFIED) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen (META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ /* We need to create a META-file */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR, NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR); ++#endif ++ } ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ ++ ERROR opening meta file.\n"); ++ mntput(meta_mnt); ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* size: 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ ++ buf_len = old_len + new_len + 4; ++ buf = (char *) kmalloc(buf_len, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: out of mem.\n"); ++ return -ENOMEM; ++ } ++ ++ buf[0] = 'R'; ++ buf[1] = ' '; ++ strncpy(buf + 2, old_name, old_len); ++ buf[old_len + 2] = ' '; ++ strncpy(buf + old_len + 3, new_name, new_len); ++ buf[buf_len -1] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, buf_len, &meta_file->f_pos); ++ if(bytes != buf_len) { ++ printk(KERN_CRIT "mini_fo: meta_write_r_entry: ERROR writing.\n"); ++ err = -1; ++ } ++ ++ kfree(buf); ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++} ++ ++/* sync D list to disk, append data if app_flag is 1 */ ++/* check the meta_mnt, which seems not to be used (properly) */ ++ ++int meta_sync_d_list(dentry_t *dentry, int app_flag) ++{ ++ dentry_t *meta_dentry; ++ file_t *meta_file; ++ mm_segment_t old_fs; ++ ++ int bytes, err; ++ struct vfsmount *meta_mnt; ++ char *buf; ++ ++ struct list_head *tmp; ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ err = 0; ++ meta_file=0; ++ meta_mnt=0; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ invalid inode passed.\n"); ++ err = -1; ++ goto out; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->deleted_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ /* ok, there is something to sync */ ++ ++ /* build the storage structure? */ ++ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen(META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ /* We need to create a META-file */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR, NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR); ++#endif ++ app_flag = 0; ++ } ++ /* need we truncate the meta file? */ ++ if(!app_flag) { ++ struct iattr newattrs; ++ newattrs.ia_size = 0; ++ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&meta_dentry->d_inode->i_mutex); ++#else ++ down(&meta_dentry->d_inode->i_sem); ++#endif ++ err = notify_change(meta_dentry, &newattrs); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&meta_dentry->d_inode->i_mutex); ++#else ++ up(&meta_dentry->d_inode->i_sem); ++#endif ++ ++ if(err || meta_dentry->d_inode->i_size != 0) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR truncating meta file.\n"); ++ goto out_err_close; ++ } ++ } ++ ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR opening meta file.\n"); ++ /* we don't mntget so we dont't mntput (for now) ++ * mntput(meta_mnt); ++ */ ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* here we go... */ ++ list_for_each(tmp, &inode_info->deleted_list) { ++ del_entry = list_entry(tmp, struct deleted_entry, list); ++ ++ /* size: len for name, 1 for \n and 2 for "D " */ ++ buf = (char *) kmalloc(del_entry->len+3, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ out of mem.\n"); ++ return -ENOMEM; ++ } ++ ++ buf[0] = 'D'; ++ buf[1] = ' '; ++ strncpy(buf+2, del_entry->name, del_entry->len); ++ buf[del_entry->len+2] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, ++ del_entry->len+3, ++ &meta_file->f_pos); ++ if(bytes != del_entry->len+3) { ++ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ ++ ERROR writing.\n"); ++ err |= -1; ++ } ++ kfree(buf); ++ } ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++ ++} ++ ++int meta_sync_r_list(dentry_t *dentry, int app_flag) ++{ ++ dentry_t *meta_dentry; ++ file_t *meta_file; ++ mm_segment_t old_fs; ++ ++ int bytes, err, buf_len; ++ struct vfsmount *meta_mnt; ++ char *buf; ++ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ err = 0; ++ meta_file=0; ++ meta_mnt=0; ++ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ invalid dentry passed.\n"); ++ err = -1; ++ goto out; ++ } ++ inode_info = itopd(dentry->d_inode); ++ ++ if(inode_info->deleted_list_size < 0) { ++ err = -1; ++ goto out; ++ } ++ ++ /* ok, there is something to sync */ ++ ++ /* build the storage structure? */ ++ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ } ++ meta_dentry = lookup_one_len(META_FILENAME, ++ dtohd2(dentry), ++ strlen(META_FILENAME)); ++ if(!meta_dentry->d_inode) { ++ /* We need to create a META-file */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR, NULL); ++#else ++ vfs_create(dtohd2(dentry)->d_inode, ++ meta_dentry, S_IRUSR | S_IWUSR); ++#endif ++ app_flag = 0; ++ } ++ /* need we truncate the meta file? */ ++ if(!app_flag) { ++ struct iattr newattrs; ++ newattrs.ia_size = 0; ++ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&meta_dentry->d_inode->i_mutex); ++#else ++ down(&meta_dentry->d_inode->i_sem); ++#endif ++ err = notify_change(meta_dentry, &newattrs); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&meta_dentry->d_inode->i_mutex); ++#else ++ up(&meta_dentry->d_inode->i_sem); ++#endif ++ if(err || meta_dentry->d_inode->i_size != 0) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR truncating meta file.\n"); ++ goto out_err_close; ++ } ++ } ++ ++ /* open META-file for writing */ ++ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); ++ if(!meta_file || IS_ERR(meta_file)) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR opening meta file.\n"); ++ /* we don't mntget so we dont't mntput (for now) ++ * mntput(meta_mnt); ++ */ ++ dput(meta_dentry); ++ err = -1; ++ goto out; ++ } ++ ++ /* check if fs supports writing */ ++ if(!meta_file->f_op->write) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR, fs does not support writing.\n"); ++ goto out_err_close; ++ } ++ ++ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ /* here we go... */ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ /* size: ++ * 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ ++ buf_len = ren_entry->old_len + ren_entry->new_len + 4; ++ buf = (char *) kmalloc(buf_len, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ out of mem.\n"); ++ return -ENOMEM; ++ } ++ buf[0] = 'R'; ++ buf[1] = ' '; ++ strncpy(buf + 2, ren_entry->old_name, ren_entry->old_len); ++ buf[ren_entry->old_len + 2] = ' '; ++ strncpy(buf + ren_entry->old_len + 3, ++ ren_entry->new_name, ren_entry->new_len); ++ buf[buf_len - 1] = '\n'; ++ bytes = meta_file->f_op->write(meta_file, buf, ++ buf_len, &meta_file->f_pos); ++ if(bytes != buf_len) { ++ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ ++ ERROR writing.\n"); ++ err |= -1; ++ } ++ kfree(buf); ++ } ++ set_fs(old_fs); ++ ++ out_err_close: ++ fput(meta_file); ++ out: ++ return err; ++} ++ ++int meta_check_d_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) ++ printk(KERN_CRIT "mini_fo: meta_check_d_dentry: \ ++ invalid dentry passed.\n"); ++ return __meta_check_d_entry(dentry->d_inode, name, len); ++} ++ ++int __meta_check_d_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct deleted_entry *del_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) ++ printk(KERN_CRIT "mini_fo: __meta_check_d_dentry: \ ++ invalid inode passed.\n"); ++ ++ inode_info = itopd(inode); ++ ++ if(inode_info->deleted_list_size <= 0) ++ return 0; ++ ++ list_for_each(tmp, &inode_info->deleted_list) { ++ del_entry = list_entry(tmp, struct deleted_entry, list); ++ if(del_entry->len != len) ++ continue; ++ ++ if(!strncmp(del_entry->name, name, len)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * check if file has been renamed and return path to orig. base dir. ++ * Implements no error return values so far, what of course sucks. ++ * String is null terminated.' ++ */ ++char* meta_check_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ ++ invalid dentry passed.\n"); ++ return NULL; ++ } ++ return __meta_check_r_entry(dentry->d_inode, name, len); ++} ++ ++char* __meta_check_r_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ char *old_path; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ ++ invalid inode passed.\n"); ++ return NULL; ++ } ++ inode_info = itopd(inode); ++ ++ if(inode_info->renamed_list_size <= 0) ++ return NULL; ++ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ if(ren_entry->new_len != len) ++ continue; ++ ++ if(!strncmp(ren_entry->new_name, name, len)) { ++ old_path = (char *) ++ kmalloc(ren_entry->old_len+1, GFP_KERNEL); ++ strncpy(old_path, ++ ren_entry->old_name, ++ ren_entry->old_len); ++ old_path[ren_entry->old_len]='\0'; ++ return old_path; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * This version only checks if entry exists and return: ++ * 1 if exists, ++ * 0 if not, ++ * -1 if error. ++ */ ++int meta_is_r_entry(dentry_t *dentry, const char *name, int len) ++{ ++ if(!dentry || !dentry->d_inode) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ ++ invalid dentry passed.\n"); ++ return -1; ++ } ++ return __meta_is_r_entry(dentry->d_inode, name, len); ++} ++ ++int __meta_is_r_entry(inode_t *inode, const char *name, int len) ++{ ++ struct list_head *tmp; ++ struct renamed_entry *ren_entry; ++ struct mini_fo_inode_info *inode_info; ++ ++ if(!inode || !itopd(inode)) { ++ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ ++ invalid inode passed.\n"); ++ return -1; ++ } ++ inode_info = itopd(inode); ++ ++ if(inode_info->renamed_list_size <= 0) ++ return -1; ++ ++ list_for_each(tmp, &inode_info->renamed_list) { ++ ren_entry = list_entry(tmp, struct renamed_entry, list); ++ if(ren_entry->new_len != len) ++ continue; ++ ++ if(!strncmp(ren_entry->new_name, name, len)) ++ return 1; ++ } ++ return 0; ++} ++ +--- /dev/null ++++ b/fs/mini_fo/mini_fo.h +@@ -0,0 +1,510 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifndef __MINI_FO_H_ ++#define __MINI_FO_H_ ++ ++#ifdef __KERNEL__ ++ ++/* META stuff */ ++#define META_FILENAME "META_dAfFgHE39ktF3HD2sr" ++ ++/* use xattrs? */ ++#define XATTR ++ ++/* File attributes that when changed, result in a file beeing copied to storage */ ++#define COPY_FLAGS ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_SIZE ++ ++/* ++ * mini_fo filestates ++ */ ++#define MODIFIED 1 ++#define UNMODIFIED 2 ++#define CREATED 3 ++#define DEL_REWRITTEN 4 ++#define DELETED 5 ++#define NON_EXISTANT 6 ++ ++/* fist file systems superblock magic */ ++# define MINI_FO_SUPER_MAGIC 0xf15f ++ ++/* ++ * STRUCTURES: ++ */ ++ ++/* mini_fo inode data in memory */ ++struct mini_fo_inode_info { ++ inode_t *wii_inode; ++ inode_t *wii_inode2; /* pointer to storage inode */ ++ ++ /* META-data lists */ ++ /* deleted list, ex wol */ ++ struct list_head deleted_list; ++ int deleted_list_size; ++ ++ /* renamed list */ ++ struct list_head renamed_list; ++ int renamed_list_size; ++ ++ /* add other lists here ... */ ++}; ++ ++/* mini_fo dentry data in memory */ ++struct mini_fo_dentry_info { ++ dentry_t *wdi_dentry; ++ dentry_t *wdi_dentry2; /* pointer to storage dentry */ ++ unsigned int state; /* state of the mini_fo dentry */ ++}; ++ ++ ++/* mini_fo super-block data in memory */ ++struct mini_fo_sb_info { ++ super_block_t *wsi_sb, *wsi_sb2; /* mk: might point to the same sb */ ++ struct vfsmount *hidden_mnt, *hidden_mnt2; ++ dentry_t *base_dir_dentry; ++ dentry_t *storage_dir_dentry; ++ ; ++}; ++ ++/* readdir_data, readdir helper struct */ ++struct readdir_data { ++ struct list_head ndl_list; /* linked list head ptr */ ++ int ndl_size; /* list size */ ++ int sto_done; /* flag to show that the storage dir entries have ++ * all been read an now follow base entries */ ++}; ++ ++/* file private data. */ ++struct mini_fo_file_info { ++ struct file *wfi_file; ++ struct file *wfi_file2; /* pointer to storage file */ ++ struct readdir_data rd; ++}; ++ ++/* struct ndl_entry */ ++struct ndl_entry { ++ struct list_head list; ++ char *name; ++ int len; ++}; ++ ++/******************************** ++ * META-data structures ++ ********************************/ ++ ++/* deleted entry */ ++struct deleted_entry { ++ struct list_head list; ++ char *name; ++ int len; ++}; ++ ++/* renamed entry */ ++struct renamed_entry { ++ struct list_head list; ++ char *old_name; /* old directory with full path */ ++ int old_len; /* length of above string */ ++ char *new_name; /* new directory name */ ++ int new_len; /* length of above string */ ++}; ++ ++/* attr_change entry */ ++struct attr_change_entry { ++ struct list_head list; ++ char *name; ++ int len; ++}; ++ ++/* link entry */ ++struct link_entry { ++ struct list_head list; ++ int links_moved; ++ int inum_base; ++ int inum_sto; ++ char *weird_name; ++ int weird_name_len; ++}; ++ ++ ++/* Some other stuff required for mini_fo_filldir64, copied from ++ * fs/readdir.c ++ */ ++ ++#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) ++#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) ++ ++ ++struct linux_dirent64 { ++ u64 d_ino; ++ s64 d_off; ++ unsigned short d_reclen; ++ unsigned char d_type; ++ char d_name[0]; ++}; ++ ++ ++struct getdents_callback64 { ++ struct linux_dirent64 * current_dir; ++ struct linux_dirent64 * previous; ++ int count; ++ int error; ++}; ++ ++struct linux_dirent { ++ unsigned long d_ino; ++ unsigned long d_off; ++ unsigned short d_reclen; ++ char d_name[1]; ++}; ++ ++struct getdents_callback { ++ struct linux_dirent * current_dir; ++ struct linux_dirent * previous; ++ int count; ++ int error; ++}; ++ ++ ++/* ++ * MACROS: ++ */ ++ ++/* file TO private_data */ ++# define ftopd(file) ((struct mini_fo_file_info *)((file)->private_data)) ++# define __ftopd(file) ((file)->private_data) ++/* file TO hidden_file */ ++# define ftohf(file) ((ftopd(file))->wfi_file) ++# define ftohf2(file) ((ftopd(file))->wfi_file2) ++ ++/* inode TO private_data */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->i_private) ++# define __itopd(ino) ((ino)->i_private) ++#else ++# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->u.generic_ip) ++# define __itopd(ino) ((ino)->u.generic_ip) ++#endif ++/* inode TO hidden_inode */ ++# define itohi(ino) (itopd(ino)->wii_inode) ++# define itohi2(ino) (itopd(ino)->wii_inode2) ++ ++/* superblock TO private_data */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++# define stopd(super) ((struct mini_fo_sb_info *)(super)->s_fs_info) ++# define __stopd(super) ((super)->s_fs_info) ++#else ++# define stopd(super) ((struct mini_fo_sb_info *)(super)->u.generic_sbp) ++# define __stopd(super) ((super)->u.generic_sbp) ++#endif ++ ++/* unused? # define vfs2priv stopd */ ++/* superblock TO hidden_superblock */ ++ ++# define stohs(super) (stopd(super)->wsi_sb) ++# define stohs2(super) (stopd(super)->wsi_sb2) ++ ++/* dentry TO private_data */ ++# define dtopd(dentry) ((struct mini_fo_dentry_info *)(dentry)->d_fsdata) ++# define __dtopd(dentry) ((dentry)->d_fsdata) ++/* dentry TO hidden_dentry */ ++# define dtohd(dent) (dtopd(dent)->wdi_dentry) ++# define dtohd2(dent) (dtopd(dent)->wdi_dentry2) ++ ++/* dentry to state */ ++# define dtost(dent) (dtopd(dent)->state) ++# define sbt(sb) ((sb)->s_type->name) ++ ++#define IS_WRITE_FLAG(flag) (flag & (O_RDWR | O_WRONLY | O_APPEND)) ++#define IS_COPY_FLAG(flag) (flag & (COPY_FLAGS)) ++ ++/* macros to simplify non-SCA code */ ++# define MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages) ++# define MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages) ++# define FREE_PAGE_POINTERS(hidden_pages, num) ++# define FREE_PAGEDATA_POINTERS(hidden_pages_data, num) ++# define FOR_EACH_PAGE ++# define CURRENT_HIDDEN_PAGE hidden_page ++# define CURRENT_HIDDEN_PAGEDATA hidden_page_data ++# define CURRENT_HIDDEN_PAGEINDEX page->index ++ ++/* ++ * EXTERNALS: ++ */ ++extern struct file_operations mini_fo_main_fops; ++extern struct file_operations mini_fo_dir_fops; ++extern struct inode_operations mini_fo_main_iops; ++extern struct inode_operations mini_fo_dir_iops; ++extern struct inode_operations mini_fo_symlink_iops; ++extern struct super_operations mini_fo_sops; ++extern struct dentry_operations mini_fo_dops; ++extern struct vm_operations_struct mini_fo_shared_vmops; ++extern struct vm_operations_struct mini_fo_private_vmops; ++extern struct address_space_operations mini_fo_aops; ++ ++#if 0 /* unused by mini_fo */ ++extern int mini_fo_interpose(dentry_t *hidden_dentry, dentry_t *this_dentry, super_block_t *sb, int flag); ++#if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA) ++extern page_t *mini_fo_get1page(file_t *file, int index); ++extern int mini_fo_fill_zeros(file_t *file, page_t *page, unsigned from); ++# endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */ ++ ++ ++# define mini_fo_hidden_dentry(d) __mini_fo_hidden_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) ++# define mini_fo_hidden_sto_dentry(d) __mini_fo_hidden_sto_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) ++ ++extern dentry_t *__mini_fo_hidden_dentry(char *file, char *func, int line, dentry_t *this_dentry); ++extern dentry_t *__mini_fo_hidden_sto_dentry(char *file, char *func, int line, dentry_t *this_dentry); ++ ++extern int mini_fo_read_file(const char *filename, void *buf, int len); ++extern int mini_fo_write_file(const char *filename, void *buf, int len); ++extern dentry_t *fist_lookup(dentry_t *dir, const char *name, vnode_t **out, uid_t uid, gid_t gid); ++#endif /* unused by mini_fo */ ++ ++/* state transition functions */ ++extern int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag); ++extern int nondir_del_rew_to_del(dentry_t *dentry); ++extern int nondir_creat_to_del(dentry_t *dentry); ++extern int nondir_mod_to_del(dentry_t *dentry); ++extern int nondir_unmod_to_del(dentry_t *dentry); ++ ++extern int dir_unmod_to_mod(dentry_t *dentry); ++ ++/* rename specials */ ++extern int rename_directory(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); ++extern int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); ++ ++/* misc stuff */ ++extern int mini_fo_tri_interpose(dentry_t *hidden_dentry, ++ dentry_t *hidden_sto_dentry, ++ dentry_t *dentry, ++ super_block_t *sb, int flag); ++ ++extern int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, ++ dentry_t *src_dentry, struct vfsmount *src_mnt); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd); ++ ++extern int create_sto_nod(dentry_t *dentry, int mode, dev_t dev); ++extern int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd); ++#else ++extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode); ++ ++extern int create_sto_nod(dentry_t *dentry, int mode, int dev); ++extern int create_sto_reg_file(dentry_t *dentry, int mode); ++#endif ++ ++extern int create_sto_dir(dentry_t *dentry, int mode); ++ ++extern int exists_in_storage(dentry_t *dentry); ++extern int is_mini_fo_existant(dentry_t *dentry); ++extern int get_neg_sto_dentry(dentry_t *dentry); ++extern int build_sto_structure(dentry_t *dir, dentry_t *dentry); ++extern int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len); ++extern dentry_t *bpath_walk(super_block_t *sb, char *bpath); ++extern int bpath_put(dentry_t *dentry); ++ ++/* check_mini_fo types functions */ ++extern int check_mini_fo_dentry(dentry_t *dentry); ++extern int check_mini_fo_file(file_t *file); ++extern int check_mini_fo_inode(inode_t *inode); ++ ++/* General meta functions, can be called from outside of meta.c */ ++extern int meta_build_lists(dentry_t *dentry); ++extern int meta_put_lists(dentry_t *dentry); ++extern int __meta_put_lists(inode_t *inode); ++ ++extern int meta_add_d_entry(dentry_t *dentry, const char *name, int len); ++extern int meta_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len); ++ ++extern int meta_remove_r_entry(dentry_t *dentry, const char *name, int len); ++ ++extern int meta_check_d_entry(dentry_t *dentry, const char *name, int len); ++extern int __meta_check_d_entry(inode_t *inode, const char *name, int len); ++ ++extern char* meta_check_r_entry(dentry_t *dentry, const char *name, int len); ++extern char* __meta_check_r_entry(inode_t *inode, const char *name, int len); ++extern int meta_is_r_entry(dentry_t *dentry, const char *name, int len); ++extern int __meta_is_r_entry(inode_t *inode, const char *name, int len); ++ ++/* Specific meta functions, should be called only inside meta.c */ ++extern int __meta_put_d_list(inode_t *inode); ++extern int __meta_put_r_list(inode_t *inode); ++ ++extern int meta_list_add_d_entry(dentry_t *dentry, ++ const char *name, int len); ++extern int meta_list_add_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len); ++ ++extern int meta_list_remove_r_entry(dentry_t *dentry, ++ const char *name, int len); ++ ++extern int __meta_list_remove_r_entry(inode_t *inode, ++ const char *name, int len); ++ ++extern int meta_write_d_entry(dentry_t *dentry, const char *name, int len); ++extern int meta_write_r_entry(dentry_t *dentry, ++ const char *old_name, int old_len, ++ const char *new_name, int new_len); ++ ++extern int meta_sync_lists(dentry_t *dentry); ++extern int meta_sync_d_list(dentry_t *dentry, int app_flag); ++extern int meta_sync_r_list(dentry_t *dentry, int app_flag); ++ ++/* ndl stuff */ ++extern int ndl_add_entry(struct readdir_data *rd, const char *name, int len); ++extern void ndl_put_list(struct readdir_data *rd); ++extern int ndl_check_entry(struct readdir_data *rd, ++ const char *name, int len); ++ ++ ++# define copy_inode_size(dst, src) \ ++ dst->i_size = src->i_size; \ ++ dst->i_blocks = src->i_blocks; ++ ++static inline void ++fist_copy_attr_atime(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_atime = src->i_atime; ++} ++static inline void ++fist_copy_attr_times(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_atime = src->i_atime; ++ dest->i_mtime = src->i_mtime; ++ dest->i_ctime = src->i_ctime; ++} ++static inline void ++fist_copy_attr_timesizes(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_atime = src->i_atime; ++ dest->i_mtime = src->i_mtime; ++ dest->i_ctime = src->i_ctime; ++ copy_inode_size(dest, src); ++} ++static inline void ++fist_copy_attr_all(inode_t *dest, const inode_t *src) ++{ ++ ASSERT(dest != NULL); ++ ASSERT(src != NULL); ++ dest->i_mode = src->i_mode; ++ dest->i_nlink = src->i_nlink; ++ dest->i_uid = src->i_uid; ++ dest->i_gid = src->i_gid; ++ dest->i_rdev = src->i_rdev; ++ dest->i_atime = src->i_atime; ++ dest->i_mtime = src->i_mtime; ++ dest->i_ctime = src->i_ctime; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ dest->i_blksize = src->i_blksize; ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12) ++ dest->i_blkbits = src->i_blkbits; ++# endif /* linux 2.4.12 and newer */ ++ copy_inode_size(dest, src); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ dest->i_attr_flags = src->i_attr_flags; ++#else ++ dest->i_flags = src->i_flags; ++#endif ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++/* copied from linux/fs.h */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++static inline void double_lock(struct dentry *d1, struct dentry *d2) ++{ ++ struct mutex *m1 = &d1->d_inode->i_mutex; ++ struct mutex *m2 = &d2->d_inode->i_mutex; ++ if (m1 != m2) { ++ if ((unsigned long) m1 < (unsigned long) m2) { ++ struct mutex *tmp = m2; ++ m2 = m1; m1 = tmp; ++ } ++ mutex_lock(m1); ++ } ++ mutex_lock(m2); ++} ++ ++static inline void double_unlock(struct dentry *d1, struct dentry *d2) ++{ ++ struct mutex *m1 = &d1->d_inode->i_mutex; ++ struct mutex *m2 = &d2->d_inode->i_mutex; ++ mutex_unlock(m1); ++ if (m1 != m2) ++ mutex_unlock(m2); ++ dput(d1); ++ dput(d2); ++} ++ ++#else ++static inline void double_down(struct semaphore *s1, struct semaphore *s2) ++{ ++ if (s1 != s2) { ++ if ((unsigned long) s1 < (unsigned long) s2) { ++ struct semaphore *tmp = s2; ++ s2 = s1; s1 = tmp; ++ } ++ down(s1); ++ } ++ down(s2); ++} ++ ++static inline void double_up(struct semaphore *s1, struct semaphore *s2) ++{ ++ up(s1); ++ if (s1 != s2) ++ up(s2); ++} ++ ++static inline void double_lock(struct dentry *d1, struct dentry *d2) ++{ ++ double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem); ++} ++ ++static inline void double_unlock(struct dentry *d1, struct dentry *d2) ++{ ++ double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem); ++ dput(d1); ++ dput(d2); ++} ++#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) */ ++#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ ++#endif /* __KERNEL__ */ ++ ++/* ++ * Definitions for user and kernel code ++ */ ++ ++/* ioctls */ ++ ++#endif /* not __MINI_FO_H_ */ +--- /dev/null ++++ b/fs/mini_fo/mini_fo-merge +@@ -0,0 +1,180 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version ++# 2 of the License, or (at your option) any later version. ++# ++ ++BASE= ++STO= ++HELP= ++DRYRUN= ++VERBOSE= ++TMP="/tmp/" ++META_NAME="META_dAfFgHE39ktF3HD2sr" ++SKIP_DEL_LIST="skip-delete-list.mini_fo-merge" ++ ++COMMAND= ++exec_command() ++{ ++ if [ x$DRYRUN == "xset" ]; then ++ echo " would run: $COMMAND" ++ elif ! [ x$DRYRUN == "xset" ]; then ++ if [ x$VERBOSE == "xset" ]; then ++ echo " running: $COMMAND" ++ fi ++ eval $COMMAND ++ fi ++} ++ ++usage() ++{ ++cat < -s ++Version 0.1 ++ ++This script merges the contents of a mini_fo storage file system back ++to the base file system. ++ ++!!! Warning: This will modify the base filesystem and can destroy data ++ if used wrongly. ++ ++Options: ++ -b ++ the directory of the base file system. ++ ++ -s ++ the directory of the storage file system. ++ ++ -d dry run, will not change anything and print the commands that ++ would be executed. ++ ++ -t tmp dir for storing temporary file. default: $TMP ++ ++ -v show what operations are performed. ++ ++ -h displays this message. ++ ++EOF ++} ++ ++# parse parameters ++while getopts hdvt:b:s: OPTS ++ do ++ case $OPTS in ++ h) HELP="set";; ++ d) DRYRUN="set";; ++ v) VERBOSE="set";; ++ b) BASE="$OPTARG";; ++ s) STO="$OPTARG";; ++ t) TMP="$OPTARG";; ++ ?) usage ++ exit 1;; ++ esac ++done ++ ++if [ "x$HELP" == "xset" ]; then ++ usage ++ exit -1 ++fi ++ ++if ! [ -d "$BASE" ] || ! [ -d "$STO" ]; then ++ echo -e "$0:\n Error, -s and/or -b argument missing. type $0 -h for help." ++ exit -1; ++fi ++ ++# get full paths ++pushd $STO; STO=`pwd`; popd ++pushd $BASE; BASE=`pwd`; popd ++TMP=${TMP%/} ++ ++ ++cat< /dev/null ++find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^R ' | tr -s ':R' ' ' | while read ENTRY; do ++ echo "entry: $ENTRY" ++ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` ++ OLD_B_DIR=`echo $ENTRY | cut -d ' ' -f 2 | sed -e 's/\///'` ++ NEW_NAME=`echo $ENTRY | cut -d ' ' -f 3` ++ NEW_B_DIR=`echo $META_FILE | sed -e "s/$META_NAME/$NEW_NAME/" | sed -e 's/^\.\///'` ++ echo "META_FILE: $META_FILE" ++ echo "OLD_B_DIR: $OLD_B_DIR" ++ echo "NEW_NAME: $NEW_NAME" ++ echo "NEW_B_DIR: $NEW_B_DIR" ++ ++ pushd $BASE &> /dev/null ++ # remove an existing dir in storage ++ COMMAND="rm -rf $NEW_B_DIR"; exec_command ++ COMMAND="cp -R $OLD_B_DIR $NEW_B_DIR"; exec_command ++ echo "" ++ popd &> /dev/null ++ ++ # remember this dir to exclude it from deleting later ++ echo $NEW_B_DIR >> $TMP/$SKIP_DEL_LIST ++done ++ ++# delete all whiteouted files from base ++echo -e "\nDeleting whiteout'ed files from base file system..." ++find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^D ' | sed -e 's/:D//' | while read ENTRY; do ++ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` ++ DEL_NAME=`echo $ENTRY | cut -d ' ' -f 2` ++ DEL_FILE=`echo $META_FILE | sed -e "s/$META_NAME/$DEL_NAME/" | sed -e 's/^\.\///'` ++ grep -x $DEL_FILE $TMP/$SKIP_DEL_LIST &> /dev/null ++ if [ $? -ne 0 ]; then ++ pushd $BASE &> /dev/null ++ COMMAND="rm -rf $DEL_FILE"; exec_command ++ popd &> /dev/null ++ else ++ echo " excluding: $DEL_FILE as in skip-del-list." ++ fi ++done ++ ++# create all dirs and update permissions ++echo -e "\nSetting up directory structures in base file system..." ++find . -type d | sed -e 's/^\.\///' | while read DIR; do ++ PERMS=`stat -c %a $DIR` ++ DIR_UID=`stat -c %u $DIR` ++ DIR_GID=`stat -c %g $DIR` ++ pushd $BASE &> /dev/null ++ if ! [ -d $DIR ]; then ++ COMMAND="mkdir -p $DIR"; exec_command ++ fi ++ COMMAND="chmod $PERMS $DIR"; exec_command ++ COMMAND="chown $DIR_UID:$DIR_GID $DIR"; exec_command ++ popd &> /dev/null ++done ++ ++# merge all non-directory files ++echo -e "\nMerging all non-directory files...." ++for i in b c p f l s; do ++ find . -type $i | sed -e 's/^\.\///' | grep -v "$META_NAME" | while read FILE; do ++ pushd $BASE #&> /dev/null ++ COMMAND="cp -df $STO/$FILE $BASE/$FILE"; exec_command ++ popd &> /dev/null ++ done ++done ++popd &> /dev/null ++ ++#rm $TMP/$SKIP_DEL_LIST ++ ++echo "Done!" +--- /dev/null ++++ b/fs/mini_fo/mini_fo-overlay +@@ -0,0 +1,130 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher ++# This program is free software; you can redistribute it and/or ++# modify it under the terms of the GNU General Public License ++# as published by the Free Software Foundation; either version ++# 2 of the License, or (at your option) any later version. ++# ++ ++HELP= ++SUFF= ++MNTP= ++MNT_DIR="/mnt" ++STO= ++STO_DIR="/tmp" ++BASE= ++ ++usage() ++{ ++cat <" in $STO_DIR, ++and mount point "mini_fo-" in $MNT_DIR. ++ ++Options: ++ -s ++ add given suffix to storage directory and the mount ++ point. This is usefull for overlaying one base directory ++ several times and avoiding conflicts with storage directory ++ names and mount points. ++ ++ -d ++ change the directory in which the storage directory will be ++ created (default is currently "$STO_DIR". ++ ++ -m ++ use an alternative directory to create the mini_fo ++ mountpoint (default is currently "$MNT_DIR". ++ ++ -h displays this message. ++ ++EOF ++exit 1; ++} ++ ++while getopts hm:s:d: OPTS ++ do ++ case $OPTS in ++ s) SUFF="$OPTARG";; ++ d) STO_DIR="$OPTARG";; ++ m) MNT_DIR="$OPTARG";; ++ h) HELP="set";; ++ ?) usage ++ exit 1;; ++ esac ++done ++shift $(($OPTIND - 1)) ++ ++BASE="$1" ++ ++if [ "x$HELP" == "xset" ]; then ++ usage ++ exit -1 ++fi ++ ++# fix suffix ++if [ "x$SUFF" != "x" ]; then ++ SUFF="-$SUFF" ++fi ++ ++# kill trailing slashes ++MNT_DIR=${MNT_DIR%/} ++STO_DIR=${STO_DIR%/} ++BASE=${BASE%/} ++ ++ ++if ! [ -d "$BASE" ]; then ++ echo "invalid base dir $BASE, run $0 -h for help." ++ exit -1 ++fi ++ ++# check opts ++if ! [ -d "$MNT_DIR" ]; then ++ echo "invalid mount dir $MNT_DIR, run $0 -h for help." ++ exit -1 ++fi ++ ++if ! [ -d "$STO_DIR" ]; then ++ echo "invalid sto_dir_dir $STO_DIR, run $0 -h for help." ++ exit -1 ++fi ++ ++MNTP="$MNT_DIR/mini_fo-`basename $BASE`$SUFF" ++STO="$STO_DIR/sto-`basename $BASE`$SUFF" ++ ++# create the mount point if it doesn't exist ++mkdir -p $MNTP ++if [ $? -ne 0 ]; then ++ echo "Error, failed to create mount point $MNTP" ++fi ++ ++mkdir -p $STO ++if [ $? -ne 0 ]; then ++ echo "Error, failed to create storage dir $STO" ++fi ++ ++# check if fs is already mounted ++mount | grep mini_fo | grep $MNTP &> /dev/null ++if [ $? -eq 0 ]; then ++ echo "Error, existing mini_fo mount at $MNTP." ++ exit -1 ++fi ++ ++mount | grep mini_fo | grep $STO &> /dev/null ++if [ $? -eq 0 ]; then ++ echo "Error, $STO seems to be used already." ++ exit -1 ++fi ++ ++# mount ++mount -t mini_fo -o base=$BASE,sto=$STO $BASE $MNTP ++ ++if [ $? -ne 0 ]; then ++ echo "Error, mounting failed, maybe no permisson to mount?" ++fi +--- /dev/null ++++ b/fs/mini_fo/mmap.c +@@ -0,0 +1,637 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++ ++#ifdef FIST_COUNT_WRITES ++/* for counting writes in the middle vs. regular writes */ ++unsigned long count_writes = 0, count_writes_middle = 0; ++#endif /* FIST_COUNT_WRITES */ ++ ++/* forward declaration of commit write and prepare write */ ++STATIC int mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to); ++STATIC int mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to); ++ ++ ++/* ++ * Function for handling creation of holes when lseek-ing past the ++ * end of the file and then writing some data. ++ */ ++int ++mini_fo_fill_zeros(file_t* file, page_t *page, unsigned from) ++{ ++ int err = 0; ++ dentry_t *dentry = file->f_dentry; ++ inode_t *inode = dentry->d_inode; ++ page_t *tmp_page; ++ int index; ++ ++ print_entry_location(); ++ ++ for (index = inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { ++ tmp_page = mini_fo_get1page(file, index); ++ if (IS_ERR(tmp_page)) { ++ err = PTR_ERR(tmp_page); ++ goto out; ++ } ++ ++ /* ++ * zero out rest of the contents of the page between the appropriate ++ * offsets. ++ */ ++ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); ++ ++ if (! (err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) ++ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); ++ ++ page_cache_release(tmp_page); ++ if (err < 0) ++ goto out; ++ if (current->need_resched) ++ schedule(); ++ } ++ ++ /* zero out appropriate parts of last page */ ++ ++ /* ++ * if the encoding type is block, then adjust the 'from' (where the ++ * zeroing will start) offset appropriately ++ */ ++ from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); ++ ++ if ((from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) { ++ ++ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, from - (inode->i_size & ~PAGE_CACHE_MASK)); ++ if (! (err = mini_fo_prepare_write(file, page, 0, PAGE_CACHE_SIZE))) ++ err = mini_fo_commit_write(file, page, 0, PAGE_CACHE_SIZE); ++ ++ if (err < 0) ++ goto out; ++ if (current->need_resched) ++ schedule(); ++ } ++ ++ out: ++ print_exit_status(err); ++ return err; ++} ++ ++ ++ ++STATIC int ++mini_fo_writepage(page_t *page) ++{ ++ int err = -EIO; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ page_t *hidden_page; ++ char *kaddr, *hidden_kaddr; ++ ++ print_entry_location(); ++ ++ inode = page->mapping->host; ++ hidden_inode = itohi(inode); ++ ++ /* ++ * writepage is called when shared mmap'ed files need to write ++ * their pages, while prepare/commit_write are called from the ++ * non-paged write() interface. (However, in 2.3 the two interfaces ++ * share the same cache, while in 2.2 they didn't.) ++ * ++ * So we pretty much have to duplicate much of what commit_write does. ++ */ ++ ++ /* find lower page (returns a locked page) */ ++ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); ++ if (!hidden_page) ++ goto out; ++ ++ /* get page address, and encode it */ ++ kaddr = (char *) kmap(page); ++ hidden_kaddr = (char*) kmap(hidden_page); ++ mini_fo_encode_block(kaddr, hidden_kaddr, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); ++ /* if encode_block could fail, then return error */ ++ kunmap(page); ++ kunmap(hidden_page); ++ ++ /* call lower writepage (expects locked page) */ ++ err = hidden_inode->i_mapping->a_ops->writepage(hidden_page); ++ ++ /* ++ * update mtime and ctime of lower level file system ++ * mini_fo' mtime and ctime are updated by generic_file_write ++ */ ++ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,1) ++ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ ++# endif /* kernel older than 2.4.1 */ ++ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ ++ ++ if (err) ++ ClearPageUptodate(page); ++ else ++ SetPageUptodate(page); ++ out: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) ++ UnlockPage(page); ++# endif /* kernel 2.4.1 and newer */ ++ print_exit_status(err); ++ return err; ++} ++ ++ ++/* ++ * get one page from cache or lower f/s, return error otherwise. ++ * returns unlocked, up-to-date page (if ok), with increased refcnt. ++ */ ++page_t * ++mini_fo_get1page(file_t *file, int index) ++{ ++ page_t *page; ++ dentry_t *dentry; ++ inode_t *inode; ++ struct address_space *mapping; ++ int err; ++ ++ print_entry_location(); ++ ++ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ ++ inode = dentry->d_inode; ++ mapping = inode->i_mapping; ++ ++ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); ++ if (index < 0) { ++ printk("%s BUG: index=%d\n", __FUNCTION__, index); ++ page = ERR_PTR(-EIO); ++ goto out; ++ } ++ page = read_cache_page(mapping, ++ index, ++ (filler_t *) mapping->a_ops->readpage, ++ (void *) file); ++ if (IS_ERR(page)) ++ goto out; ++ wait_on_page(page); ++ if (!Page_Uptodate(page)) { ++ lock_page(page); ++ err = mapping->a_ops->readpage(file, page); ++ if (err) { ++ page = ERR_PTR(err); ++ goto out; ++ } ++ wait_on_page(page); ++ if (!Page_Uptodate(page)) { ++ page = ERR_PTR(-EIO); ++ goto out; ++ } ++ } ++ ++ out: ++ print_exit_pointer(page); ++ return page; ++} ++ ++ ++/* ++ * get one page from cache or lower f/s, return error otherwise. ++ * similar to get1page, but doesn't guarantee that it will return ++ * an unlocked page. ++ */ ++page_t * ++mini_fo_get1page_cached(file_t *file, int index) ++{ ++ page_t *page; ++ dentry_t *dentry; ++ inode_t *inode; ++ struct address_space *mapping; ++ int err; ++ ++ print_entry_location(); ++ ++ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ ++ inode = dentry->d_inode; ++ mapping = inode->i_mapping; ++ ++ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); ++ if (index < 0) { ++ printk("%s BUG: index=%d\n", __FUNCTION__, index); ++ page = ERR_PTR(-EIO); ++ goto out; ++ } ++ page = read_cache_page(mapping, ++ index, ++ (filler_t *) mapping->a_ops->readpage, ++ (void *) file); ++ if (IS_ERR(page)) ++ goto out; ++ ++ out: ++ print_exit_pointer(page); ++ return page; ++} ++ ++ ++/* ++ * readpage is called from generic_page_read and the fault handler. ++ * If your file system uses generic_page_read for the read op, it ++ * must implement readpage. ++ * ++ * Readpage expects a locked page, and must unlock it. ++ */ ++STATIC int ++mini_fo_do_readpage(file_t *file, page_t *page) ++{ ++ int err = -EIO; ++ dentry_t *dentry; ++ file_t *hidden_file = NULL; ++ dentry_t *hidden_dentry; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ char *page_data; ++ page_t *hidden_page; ++ char *hidden_page_data; ++ int real_size; ++ ++ print_entry_location(); ++ ++ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ ++ if (ftopd(file) != NULL) ++ hidden_file = ftohf(file); ++ hidden_dentry = dtohd(dentry); ++ inode = dentry->d_inode; ++ hidden_inode = itohi(inode); ++ ++ fist_dprint(7, "%s: requesting page %d from file %s\n", __FUNCTION__, page->index, dentry->d_name.name); ++ ++ MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages); ++ MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); ++ FOR_EACH_PAGE ++ CURRENT_HIDDEN_PAGE = NULL; ++ ++ /* find lower page (returns a locked page) */ ++ FOR_EACH_PAGE { ++ fist_dprint(8, "%s: Current page index = %d\n", __FUNCTION__, CURRENT_HIDDEN_PAGEINDEX); ++ CURRENT_HIDDEN_PAGE = read_cache_page(hidden_inode->i_mapping, ++ CURRENT_HIDDEN_PAGEINDEX, ++ (filler_t *) hidden_inode->i_mapping->a_ops->readpage, ++ (void *) hidden_file); ++ if (IS_ERR(CURRENT_HIDDEN_PAGE)) { ++ err = PTR_ERR(CURRENT_HIDDEN_PAGE); ++ CURRENT_HIDDEN_PAGE = NULL; ++ goto out_release; ++ } ++ } ++ ++ /* ++ * wait for the page data to show up ++ * (signaled by readpage as unlocking the page) ++ */ ++ FOR_EACH_PAGE { ++ wait_on_page(CURRENT_HIDDEN_PAGE); ++ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { ++ /* ++ * call readpage() again if we returned from wait_on_page with a ++ * page that's not up-to-date; that can happen when a partial ++ * page has a few buffers which are ok, but not the whole ++ * page. ++ */ ++ lock_page(CURRENT_HIDDEN_PAGE); ++ err = hidden_inode->i_mapping->a_ops->readpage(hidden_file, ++ CURRENT_HIDDEN_PAGE); ++ if (err) { ++ CURRENT_HIDDEN_PAGE = NULL; ++ goto out_release; ++ } ++ wait_on_page(CURRENT_HIDDEN_PAGE); ++ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { ++ err = -EIO; ++ goto out_release; ++ } ++ } ++ } ++ ++ /* map pages, get their addresses */ ++ page_data = (char *) kmap(page); ++ FOR_EACH_PAGE ++ CURRENT_HIDDEN_PAGEDATA = (char *) kmap(CURRENT_HIDDEN_PAGE); ++ ++ /* if decode_block could fail, then return error */ ++ err = 0; ++ real_size = hidden_inode->i_size - (page->index << PAGE_CACHE_SHIFT); ++ if (real_size <= 0) ++ memset(page_data, 0, PAGE_CACHE_SIZE); ++ else if (real_size < PAGE_CACHE_SIZE) { ++ mini_fo_decode_block(hidden_page_data, page_data, real_size, inode, inode->i_sb, page->index); ++ memset(page_data + real_size, 0, PAGE_CACHE_SIZE - real_size); ++ } else ++ mini_fo_decode_block(hidden_page_data, page_data, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); ++ ++ FOR_EACH_PAGE ++ kunmap(CURRENT_HIDDEN_PAGE); ++ kunmap(page); ++ ++ out_release: ++ FOR_EACH_PAGE ++ if (CURRENT_HIDDEN_PAGE) ++ page_cache_release(CURRENT_HIDDEN_PAGE); /* undo read_cache_page */ ++ ++ FREE_PAGE_POINTERS(hidden_pages, num_hidden_pages); ++ FREE_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); ++ ++ out: ++ if (err == 0) ++ SetPageUptodate(page); ++ else ++ ClearPageUptodate(page); ++ ++ print_exit_status(err); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_readpage(file_t *file, page_t *page) ++{ ++ int err; ++ print_entry_location(); ++ ++ err = mini_fo_do_readpage(file, page); ++ ++ /* ++ * we have to unlock our page, b/c we _might_ have gotten a locked page. ++ * but we no longer have to wakeup on our page here, b/c UnlockPage does ++ * it ++ */ ++ UnlockPage(page); ++ ++ print_exit_status(err); ++ return err; ++} ++ ++ ++STATIC int ++mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to) ++{ ++ int err = 0; ++ ++ print_entry_location(); ++ ++ /* ++ * we call kmap(page) only here, and do the kunmap ++ * and the actual downcalls, including unlockpage and uncache ++ * in commit_write. ++ */ ++ kmap(page); ++ ++ /* fast path for whole page writes */ ++ if (from == 0 && to == PAGE_CACHE_SIZE) ++ goto out; ++ /* read the page to "revalidate" our data */ ++ /* call the helper function which doesn't unlock the page */ ++ if (!Page_Uptodate(page)) ++ err = mini_fo_do_readpage(file, page); ++ ++ out: ++ print_exit_status(err); ++ return err; ++} ++ ++ ++ ++STATIC int ++mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to) ++{ ++ int err = -ENOMEM; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ page_t *hidden_page; ++ file_t *hidden_file = NULL; ++ loff_t pos; ++ unsigned bytes = to - from; ++ unsigned hidden_from, hidden_to, hidden_bytes; ++ ++ print_entry_location(); ++ ++ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ ++ hidden_inode = itohi(inode); ++ ++ ASSERT(file != NULL); ++ /* ++ * here we have a kmapped page, with data from the user copied ++ * into it. we need to encode_block it, and then call the lower ++ * commit_write. We also need to simulate same behavior of ++ * generic_file_write, and call prepare_write on the lower f/s first. ++ */ ++#ifdef FIST_COUNT_WRITES ++ count_writes++; ++# endif /* FIST_COUNT_WRITES */ ++ ++ /* this is append and/or extend -- we can't have holes so fill them in */ ++ if (page->index > (hidden_inode->i_size >> PAGE_CACHE_SHIFT)) { ++ page_t *tmp_page; ++ int index; ++ for (index = hidden_inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { ++ tmp_page = mini_fo_get1page(file, index); ++ if (IS_ERR(tmp_page)) { ++ err = PTR_ERR(tmp_page); ++ goto out; ++ } ++ /* zero out the contents of the page at the appropriate offsets */ ++ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); ++ if (!(err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) ++ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); ++ page_cache_release(tmp_page); ++ if (err < 0) ++ goto out; ++ if (current->need_resched) ++ schedule(); ++ } ++ } ++ ++ if (ftopd(file) != NULL) ++ hidden_file = ftohf(file); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_inode->i_mutex); ++#else ++ down(&hidden_inode->i_sem); ++#endif ++ /* find lower page (returns a locked page) */ ++ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); ++ if (!hidden_page) ++ goto out; ++ ++#if FIST_ENCODING_BLOCKSIZE > 1 ++# error encoding_blocksize greater than 1 is not yet supported ++# endif /* FIST_ENCODING_BLOCKSIZE > 1 */ ++ ++ hidden_from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); ++ hidden_to = ((to + FIST_ENCODING_BLOCKSIZE - 1) & (~(FIST_ENCODING_BLOCKSIZE - 1))); ++ if ((page->index << PAGE_CACHE_SHIFT) + to > hidden_inode->i_size) { ++ ++ /* ++ * if this call to commit_write had introduced holes and the code ++ * for handling holes was invoked, then the beginning of this page ++ * must be zeroed out ++ * zero out bytes from 'size_of_file%pagesize' to 'from'. ++ */ ++ if ((hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) ++ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)); ++ ++ } ++ hidden_bytes = hidden_to - hidden_from; ++ ++ /* call lower prepare_write */ ++ err = -EINVAL; ++ if (hidden_inode->i_mapping && ++ hidden_inode->i_mapping->a_ops && ++ hidden_inode->i_mapping->a_ops->prepare_write) ++ err = hidden_inode->i_mapping->a_ops->prepare_write(hidden_file, ++ hidden_page, ++ hidden_from, ++ hidden_to); ++ if (err) ++ /* don't leave locked pages behind, esp. on an ENOSPC */ ++ goto out_unlock; ++ ++ fist_dprint(8, "%s: encoding %d bytes\n", __FUNCTION__, hidden_bytes); ++ mini_fo_encode_block((char *) page_address(page) + hidden_from, (char*) page_address(hidden_page) + hidden_from, hidden_bytes, inode, inode->i_sb, page->index); ++ /* if encode_block could fail, then goto unlock and return error */ ++ ++ /* call lower commit_write */ ++ err = hidden_inode->i_mapping->a_ops->commit_write(hidden_file, ++ hidden_page, ++ hidden_from, ++ hidden_to); ++ ++ if (err < 0) ++ goto out_unlock; ++ ++ err = bytes; /* convert error to no. of bytes */ ++ ++ inode->i_blocks = hidden_inode->i_blocks; ++ /* we may have to update i_size */ ++ pos = (page->index << PAGE_CACHE_SHIFT) + to; ++ if (pos > inode->i_size) ++ inode->i_size = pos; ++ ++ /* ++ * update mtime and ctime of lower level file system ++ * mini_fo' mtime and ctime are updated by generic_file_write ++ */ ++ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; ++ ++ mark_inode_dirty_sync(inode); ++ ++ out_unlock: ++ UnlockPage(hidden_page); ++ page_cache_release(hidden_page); ++ kunmap(page); /* kmap was done in prepare_write */ ++ out: ++ /* we must set our page as up-to-date */ ++ if (err < 0) ++ ClearPageUptodate(page); ++ else ++ SetPageUptodate(page); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_inode->i_mutex); ++#else ++ up(&hidden_inode->i_sem); ++#endif ++ print_exit_status(err); ++ return err; /* assume all is ok */ ++} ++ ++ ++STATIC int ++mini_fo_bmap(struct address_space *mapping, long block) ++{ ++ int err = 0; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ ++ print_entry_location(); ++ ++ inode = (inode_t *) mapping->host; ++ hidden_inode = itohi(inode); ++ ++ if (hidden_inode->i_mapping->a_ops->bmap) ++ err = hidden_inode->i_mapping->a_ops->bmap(hidden_inode->i_mapping, block); ++ print_exit_location(); ++ return err; ++} ++ ++ ++/* ++ * This function is copied verbatim from mm/filemap.c. ++ * XXX: It should be simply moved to some header file instead -- bug Al about it! ++ */ ++static inline int sync_page(struct page *page) ++{ ++ struct address_space *mapping = page->mapping; ++ ++ if (mapping && mapping->a_ops && mapping->a_ops->sync_page) ++ return mapping->a_ops->sync_page(page); ++ return 0; ++} ++ ++ ++/* ++ * XXX: we may not need this function if not FIST_FILTER_DATA. ++ * FIXME: for FIST_FILTER_SCA, get all lower pages and sync them each. ++ */ ++STATIC int ++mini_fo_sync_page(page_t *page) ++{ ++ int err = 0; ++ inode_t *inode; ++ inode_t *hidden_inode; ++ page_t *hidden_page; ++ ++ print_entry_location(); ++ ++ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ ++ hidden_inode = itohi(inode); ++ ++ /* find lower page (returns a locked page) */ ++ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); ++ if (!hidden_page) ++ goto out; ++ ++ err = sync_page(hidden_page); ++ ++ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ ++ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ ++ ++ out: ++ print_exit_status(err); ++ return err; ++} +--- /dev/null ++++ b/fs/mini_fo/README +@@ -0,0 +1,163 @@ ++README for the mini_fo overlay file system ++========================================= ++ ++ ++WHAT IS MINI_FO? ++---------------- ++ ++mini_fo is a virtual kernel file system that can make read-only ++file systems writable. This is done by redirecting modifying operations ++to a writeable location called "storage directory", and leaving the ++original data in the "base directory" untouched. When reading, the ++file system merges the modifed and original data so that only the ++newest versions will appear. This occurs transparently to the user, ++who can access the data like on any other read-write file system. ++ ++Base and storage directories may be located on the same or on ++different partitions and may be of different file system types. While ++the storage directory obviously needs to be writable, the base may or ++may not be writable, what doesn't matter as it will no be modified ++anyway. ++ ++ ++WHAT IS GOOD FOR? ++----------------- ++ ++The primary purpose of the mini_fo file system is to allow easy ++software updates to embedded systems, that often store their root ++file system in a read-only flash file system, but there are many ++more as for example sandboxing, or for allowing live-cds to ++permanently store information. ++ ++ ++BUILDING ++-------- ++This should be simple. Adjust the Makefile to point to the correct ++kernel headers you want to build the module for. Then: ++ ++ # make ++ ++should build "mini_fo.o" for a 2.4 kernel or "mini_fo.ko" for a 2.6 ++kernel. ++ ++If you are building the module for you current kernel, you can install ++the module (as root): ++ ++ # make install ++ ++or uninstall with ++ ++ # make uninstall ++ ++ ++USING THE FILE SYSTEM ++-------------------- ++ ++the general mount syntax is: ++ ++ mount -t mini_fo -o base=,sto=\ ++ ++ ++Example: ++ ++You have mounted a cdrom to /mnt/cdrom and want to modifiy some files ++on it: ++ ++load the module (as root) ++ ++ # insmod mini_fo.o for a 2.4 kernel or ++ ++ # insmod mini_fo.ko for a 2.6 kernel ++ ++ ++create a storage dir in tmp and a mountpoint for mini_fo: ++ ++ # mkdir /tmp/sto ++ # mkdir /mnt/mini_fo ++ ++and mount the mini_fo file system: ++ ++ # mount -t mini_fo -o base=/mnt/cdrom,sto=/tmp/sto /mnt/cdrom /mnt/mini_fo ++ ++ ++Now the data stored on the cd can be accessed via the mini_fo ++mountpoint just like any read-write file system, files can be modified ++and deleted, new ones can be created and so on. When done unmount the ++file system: ++ ++ # unmount /mnt/mini_fo ++ ++Note that if the file system is mounted again using the same storage ++file system, of course it will appear in the modified state again. If ++you remount it using an new empty storage directory, it will be ++unmodified. Therefore by executing: ++ ++ # cd /tmp/sto ++ # rm -rf * ++ ++you can nuke all the changes you made to the original file system. But ++ remember NEVER do this while the mini_fo file system is mounted! ++ ++ ++Alternatively you can use the mini_fo-overlay bash script, that ++simplifies managing mini_fo mounts. See TOOLS Section. ++ ++ ++TOOLS ++----- ++ ++mini_fo-merge (experimental): ++ ++This is a bash script that will merge changes contained in the storage ++directory back to the base directory. This allows mini_fo to function ++as a cache file system by overlaying a slow (network, ...) file system ++and using a fast (ramdisk, ...) as storage. When done, changes can be ++merged back to the (slow) base with mini_fo-merge. See "mini_fo-merge ++-h" for details. ++ ++It can be usefull for merging changes back after a successfull test ++(patches, software updates...) ++ ++ ++mini_fo-overlay: ++ ++This bash script simplifies managing one or more mini_fo mounts. For ++overlaying a directory called "basedir1", you can just call: ++ ++ # mini_fo-overlay basedir1 ++ ++This will mount mini_fo with "basedir1" as base, "/tmp/sto-basedir1/" ++as storage to "/mnt/mini_fo-basedir1/". It has more options though, ++type "mini_fo-overlay -h" for details. ++ ++ ++DOCUMENTATION, REPORTING BUGS, GETTING HELP ++------------------------------------------- ++ ++Please visit the mini_fo project page at: ++ ++http://www.denx.de/twiki/bin/view/Know/MiniFOHome ++ ++ ++WARNINGS ++-------- ++ ++Never modify the base or the storage directorys while the mini_fo ++file system is mounted, or you might crash you system. Simply accessing ++and reading should not cause any trouble. ++ ++Exporting a mini_fo mount point via NFS has not been tested, and may ++or may not work. ++ ++Check the RELEASE_NOTES for details on bugs and features. ++ ++ ++ ++Copyright (C) 2004, 2005 Markus Klotzbuecher ++ ++This program is free software; you can redistribute it and/or ++modify it under the terms of the GNU General Public License ++as published by the Free Software Foundation; either version ++2 of the License, or (at your option) any later version. ++ ++ +--- /dev/null ++++ b/fs/mini_fo/RELEASE_NOTES +@@ -0,0 +1,111 @@ ++Release: mini_fo-0.6.1 (v0-6-1) ++Date: 21.09.2005 ++ ++ ++Changes: ++-------- ++v0-6-1: ++ ++- bugfixes (see ChangeLog) ++ ++- two helper scripts "mini_fo_merge" and "mini_fo_overlay" (see ++ README for details). ++ ++v0-6-0: ++ ++- Support for 2.4 and 2.6 (see Makefile) ++ ++- Partial hard link support (creating works as expected, but already ++ existing links in the base file system will be treated as if they ++ were individual files). ++ ++- Various bugfixes and cleanups. ++ ++ ++v0-6-0-pre1: ++ ++- This is mini_fo-0-6-0-pre1! This release is a complete rewrite of ++ many vital mini_fo parts such as the old whiteout list code which ++ has been replaced by the new META subsystem. ++ ++- Light weight directory renaming implemented. This means if a ++ directory is renamed via the mini_fo filesystem this will no longer ++ result in a complete copy in storage, instead only one empty ++ directory will be created. All base filed contained in the original ++ directory stay there until modified. ++ ++- Special files (creating, renaming, deleting etc.) now working. ++ ++- Many bugfixes and cleanup, mini_fo is now a lot more stable. ++ ++ ++v0-5-10: ++ ++- Final release of the 0-5-* versions. Next will be a complete rewrite ++ of many features. This release contains several bugfixes related to ++ directory renaming. ++ ++ ++v0-5-10-pre6: ++ ++- Lots of cleanup and several bugfixes related to directory deleting ++ ++- Directory renaming suddenly works, what is most likely due to the ++ fact tha that "mv" is smart: if the classic rename doesn't work it ++ will assume that source and target file are on different fs and will ++ copy the directory and try to remove the source directory. Until ++ directory removing wasn't implemented, it would fail to do this and ++ rollback. ++ So, directory renaming works for now, but it doesn't yet do what you ++ would expect from a overlay fs, so use with care. ++ ++ ++v0-5-10-pre5: ++ ++- implemented directory deleting ++- made parsing of mount options more stable ++- New format of mount options! (See README) ++- I can't reproduce the unknown panic with 2.4.25 anymore, so I'll ++ happily assume it never existed! ++ ++ ++Implemented features: ++--------------------- ++ ++- creating hard links (see BUGS on already existing hard links) ++- lightweight directory renaming ++- renaming device files, pipes, sockets, etc. ++- creating, renaming, deleting of special files ++- deleting directorys ++- general directory reading (simple "ls" ) ++- creating files in existing directorys ++- creating directorys ++- renaming files. ++- reading and writing files (involves opening) ++- appending to files (creates copy in storage) ++- deleting files ++- llseek works too, what allows editors to work ++- persistency (a deleted file stay deleted over remounts) ++- use of symbolic links ++- creating of device files ++ ++ ++Not (yet) implemented features: ++------------------------------- ++ ++- full hard link support. ++ ++ ++ ++BUGS: ++----- ++ ++Hard links in the base file system will be treated as individual ++files, not as links to one inode. ++ ++The main problem with hard links isn't allowing to create them, but ++their pure existence. If you modify a base hard link, the changes made ++will only show up on this link, the other link will remain in the ++original state. I hope to fix this someday. Please note that this does ++not effect the special hard links '.' and '..', that are handled ++seperately by the lower fs. +--- /dev/null ++++ b/fs/mini_fo/state.c +@@ -0,0 +1,620 @@ ++/* ++ * Copyright (C) 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif /* HAVE_CONFIG_H */ ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++ ++/* create the storage file, setup new states */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd) ++#else ++int create_sto_reg_file(dentry_t *dentry, int mode) ++#endif ++{ ++ int err = 0; ++ inode_t *dir; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ if(exists_in_storage(dentry)) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: wrong type or state.\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ err = get_neg_sto_dentry(dentry); ++ ++ if (err) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR getting neg. sto dentry.\n"); ++ goto out; ++ } ++ ++ dir = dentry->d_parent->d_inode; ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* lock parent */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ mode, nd); ++#else ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ mode); ++#endif ++ if(err) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file.\n"); ++ goto out_lock; ++ } ++ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file [2].\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ ++ /* interpose the new inode */ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == NON_EXISTANT) { ++ dtost(dentry) = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == UNMODIFIED) { ++ dtost(dentry) = MODIFIED; ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: create_sto_file: invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ } ++ fist_copy_attr_timesizes(dentry->d_parent->d_inode, ++ hidden_sto_dir_dentry->d_inode); ++ ++ out_lock: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++/* create the sto dir, setup states */ ++int create_sto_dir(dentry_t *dentry, int mode) ++{ ++ int err = 0; ++ inode_t *dir; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ /* had to take the "!S_ISDIR(mode))" check out, because it failed */ ++ if(exists_in_storage(dentry)) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: wrong type or state.\\ ++n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ dir = dentry->d_parent->d_inode; ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ mode); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir.\n"); ++ goto out_lock; ++ } ++ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir [2].\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ ++ /* interpose the new inode */ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtopd(dentry)->state == NON_EXISTANT) { ++ dtopd(dentry)->state = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtopd(dentry)->state == UNMODIFIED) { ++ dtopd(dentry)->state = MODIFIED; ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR, invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ } ++ ++ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); ++ ++ /* initalize the wol list */ ++ itopd(dentry->d_inode)->deleted_list_size = -1; ++ itopd(dentry->d_inode)->renamed_list_size = -1; ++ meta_build_lists(dentry); ++ ++ ++ out_lock: ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++int create_sto_nod(dentry_t *dentry, int mode, dev_t dev) ++#else ++int create_sto_nod(dentry_t *dentry, int mode, int dev) ++#endif ++{ ++ int err = 0; ++ inode_t *dir; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ if(exists_in_storage(dentry)) { ++ err = -EEXIST; ++ goto out; ++ } ++ err = get_neg_sto_dentry(dentry); ++ ++ if (err) { ++ printk(KERN_CRIT "mini_fo: create_sto_nod: ERROR getting neg. sto dentry.\n"); ++ goto out; ++ } ++ ++ dir = dentry->d_parent->d_inode; ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* lock parent */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, dev); ++ if(err) ++ goto out_lock; ++ ++ if(!dtohd2(dentry)->d_inode) { ++ printk(KERN_CRIT "mini_fo: create_sto_nod: creating storage inode failed [1].\n"); ++ err = -EINVAL; /* return something indicating failure */ ++ goto out_lock; ++ } ++ ++ /* interpose the new inode */ ++ if(dtost(dentry) == DELETED) { ++ dtost(dentry) = DEL_REWRITTEN; ++ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == NON_EXISTANT) { ++ dtost(dentry) = CREATED; ++ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); ++ if(err) ++ goto out_lock; ++ } ++ else if(dtost(dentry) == UNMODIFIED) { ++ dtost(dentry) = MODIFIED; ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: create_sto_nod: error, invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ } ++ ++ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); ++ ++ out_lock: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++ ++/* unimplemented (and possibly not usefull): ++ ++ nondir-del_to_del_rew ++ nondir-non_exist_to_creat ++ ++ dir-unmod_to_del ++ dir-mod_to_del ++ dir-creat_to_del ++ dir-del_rew_to_del ++ dir-del_to_del_rew ++ dir-non_exist_to_creat ++*/ ++ ++ ++/* bring a file of any type from state UNMODIFIED to MODIFIED */ ++int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag) ++{ ++ int err = 0; ++ struct vfsmount *tgt_mnt; ++ struct vfsmount *src_mnt; ++ dentry_t *tgt_dentry; ++ dentry_t *src_dentry; ++ dentry_t *hidden_sto_dentry; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if((dtost(dentry) != UNMODIFIED) || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ err = get_neg_sto_dentry(dentry); ++ ++ if (err) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR getting neg. sto dentry.\n"); ++ goto out; ++ } ++ ++ /* create sto file */ ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* lock parent */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ err = PTR_ERR(hidden_sto_dir_dentry); ++ if (IS_ERR(hidden_sto_dir_dentry)) ++ goto out; ++ ++ /* handle different types of nondirs */ ++ if(S_ISCHR(dentry->d_inode->i_mode) || ++ S_ISBLK(dentry->d_inode->i_mode)) { ++ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dtohd(dentry)->d_inode->i_mode, ++ dtohd(dentry)->d_inode->i_rdev); ++ } ++ ++ else if(S_ISREG(dentry->d_inode->i_mode)) { ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dtohd(dentry)->d_inode->i_mode, NULL); ++#else ++ err = vfs_create(hidden_sto_dir_dentry->d_inode, ++ hidden_sto_dentry, ++ dtohd(dentry)->d_inode->i_mode); ++#endif ++ } ++ if(err) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR creating sto file.\n"); ++ goto out_lock; ++ } ++ ++ /* interpose on new inode */ ++ if(itohi2(dentry->d_inode) != NULL) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR, invalid inode detected.\n"); ++ err = -EINVAL; ++ goto out_lock; ++ } ++ ++ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); ++ ++ fist_copy_attr_timesizes(dentry->d_parent->d_inode, ++ hidden_sto_dir_dentry->d_inode); ++ dtost(dentry) = MODIFIED; ++ ++ /* copy contents if regular file and cp_flag = 1 */ ++ if((cp_flag == 1) && S_ISREG(dentry->d_inode->i_mode)) { ++ ++ /* unlock first */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ dput(hidden_sto_dir_dentry); ++ ++ tgt_dentry = dtohd2(dentry); ++ tgt_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; ++ src_dentry = dtohd(dentry); ++ src_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt; ++ ++ err = mini_fo_cp_cont(tgt_dentry, tgt_mnt, ++ src_dentry, src_mnt); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ ++ ERROR copying contents.\n"); ++ } ++ goto out; ++ } ++ ++ out_lock: ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ out: ++ return err; ++} ++ ++/* this function is currently identical to nondir_creat_to_del */ ++int nondir_del_rew_to_del(dentry_t *dentry) ++{ ++ return nondir_creat_to_del(dentry); ++} ++ ++int nondir_creat_to_del(dentry_t *dentry) ++{ ++ int err = 0; ++ ++ inode_t *hidden_sto_dir_inode; ++ dentry_t *hidden_sto_dir_dentry; ++ dentry_t *hidden_sto_dentry; ++ ++ check_mini_fo_dentry(dentry); ++ ++ /* for now this function serves for both state DEL_REWRITTEN and ++ * CREATED */ ++ if(!(dtost(dentry) == CREATED || (dtost(dentry) == DEL_REWRITTEN)) || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_mod_to_del/del_rew_to_del: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ ++ dtost(dentry) = NON_EXISTANT; ++ ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ out: ++ return err; ++} ++ ++int nondir_mod_to_del(dentry_t *dentry) ++{ ++ int err; ++ dentry_t *hidden_sto_dentry; ++ inode_t *hidden_sto_dir_inode; ++ dentry_t *hidden_sto_dir_dentry; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) != MODIFIED || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_mod_to_del: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); ++ hidden_sto_dentry = dtohd2(dentry); ++ ++ /* was hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ ++ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ down(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); ++ dput(hidden_sto_dentry); ++ if(!err) ++ d_delete(hidden_sto_dentry); ++ ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; ++ ++ /* dput base dentry, this will relase the inode and free the ++ * dentry, as we will never need it again. */ ++ dput(dtohd(dentry)); ++ dtohd(dentry) = NULL; ++ dtost(dentry) = DELETED; ++ ++ /* add deleted file to META-file */ ++ meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ ++ /* was: unlock_dir(hidden_sto_dir_dentry); */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) ++ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); ++#else ++ up(&hidden_sto_dir_dentry->d_inode->i_sem); ++#endif ++ dput(hidden_sto_dir_dentry); ++ ++ out: ++ return err; ++} ++ ++int nondir_unmod_to_del(dentry_t *dentry) ++{ ++ int err = 0; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) != UNMODIFIED || ++ S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: nondir_unmod_to_del: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ /* next we have to get a negative dentry for the storage file */ ++ err = get_neg_sto_dentry(dentry); ++ ++ if(err) ++ goto out; ++ ++ /* add deleted file to META lists */ ++ err = meta_add_d_entry(dentry->d_parent, ++ dentry->d_name.name, ++ dentry->d_name.len); ++ ++ if(err) ++ goto out; ++ ++ /* dput base dentry, this will relase the inode and free the ++ * dentry, as we will never need it again. */ ++ dput(dtohd(dentry)); ++ dtohd(dentry) = NULL; ++ dtost(dentry) = DELETED; ++ ++ out: ++ return err; ++} ++ ++/* bring a dir from state UNMODIFIED to MODIFIED */ ++int dir_unmod_to_mod(dentry_t *dentry) ++{ ++ int err; ++ ++ check_mini_fo_dentry(dentry); ++ ++ if(dtost(dentry) != UNMODIFIED || ++ !S_ISDIR(dentry->d_inode->i_mode)) { ++ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ ++ wrong type or state.\n"); ++ err = -1; ++ goto out; ++ } ++ ++ /* this creates our dir incl. sto. structure */ ++ err = build_sto_structure(dentry->d_parent, dentry); ++ if(err) { ++ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ ++ build_sto_structure failed.\n"); ++ goto out; ++ } ++ out: ++ return err; ++} ++ +--- /dev/null ++++ b/fs/mini_fo/super.c +@@ -0,0 +1,281 @@ ++/* ++ * Copyright (c) 1997-2003 Erez Zadok ++ * Copyright (c) 2001-2003 Stony Brook University ++ * ++ * For specific licensing information, see the COPYING file distributed with ++ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. ++ * ++ * This Copyright notice must be kept intact and distributed with all ++ * fistgen sources INCLUDING sources generated by fistgen. ++ */ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * $Id$ ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++ ++ ++STATIC void ++mini_fo_read_inode(inode_t *inode) ++{ ++ static struct address_space_operations mini_fo_empty_aops; ++ ++ __itopd(inode) = kmalloc(sizeof(struct mini_fo_inode_info), GFP_KERNEL); ++ if (!itopd(inode)) { ++ printk("<0>%s:%s:%d: No kernel memory!\n", __FILE__, __FUNCTION__, __LINE__); ++ ASSERT(NULL); ++ } ++ itohi(inode) = NULL; ++ itohi2(inode) = NULL; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ inode->i_version++; ++#else ++ inode->i_version = ++event; /* increment inode version */ ++#endif ++ inode->i_op = &mini_fo_main_iops; ++ inode->i_fop = &mini_fo_main_fops; ++#if 0 ++ /* ++ * XXX: To export a file system via NFS, it has to have the ++ * FS_REQUIRES_DEV flag, so turn it on. But should we inherit it from ++ * the lower file system, or can we allow our file system to be exported ++ * even if the lower one cannot be natively exported. ++ */ ++ inode->i_sb->s_type->fs_flags |= FS_REQUIRES_DEV; ++ /* ++ * OK, the above was a hack, which is now turned off because it may ++ * cause a panic/oops on some systems. The correct way to export a ++ * "nodev" filesystem is via using nfs-utils > 1.0 and the "fsid=" export ++ * parameter, which requires 2.4.20 or later. ++ */ ++#endif ++ /* I don't think ->a_ops is ever allowed to be NULL */ ++ inode->i_mapping->a_ops = &mini_fo_empty_aops; ++} ++ ++ ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++/* ++ * No need to call write_inode() on the lower inode, as it ++ * will have been marked 'dirty' anyway. But we might need ++ * to write some of our own stuff to disk. ++ */ ++STATIC void ++mini_fo_write_inode(inode_t *inode, int sync) ++{ ++ print_entry_location(); ++ print_exit_location(); ++} ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ ++ ++STATIC void ++mini_fo_put_inode(inode_t *inode) ++{ ++ /* ++ * This is really funky stuff: ++ * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed. ++ * It is currently holding a reference to the hidden inode. ++ * Therefore, it needs to release that reference by calling iput on the hidden inode. ++ * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0. ++ * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files. ++ * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode. ++ */ ++ if (atomic_read(&inode->i_count) == 1) ++ inode->i_nlink = 0; ++} ++ ++ ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++/* ++ * we now define delete_inode, because there are two VFS paths that may ++ * destroy an inode: one of them calls clear inode before doing everything ++ * else that's needed, and the other is fine. This way we truncate the inode ++ * size (and its pages) and then clear our own inode, which will do an iput ++ * on our and the lower inode. ++ */ ++STATIC void ++mini_fo_delete_inode(inode_t *inode) ++{ ++ print_entry_location(); ++ ++ fist_checkinode(inode, "mini_fo_delete_inode IN"); ++ inode->i_size = 0; /* every f/s seems to do that */ ++ clear_inode(inode); ++ ++ print_exit_location(); ++} ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ ++ ++/* final actions when unmounting a file system */ ++STATIC void ++mini_fo_put_super(super_block_t *sb) ++{ ++ if (stopd(sb)) { ++ mntput(stopd(sb)->hidden_mnt); ++ mntput(stopd(sb)->hidden_mnt2); ++ ++ /* mk: no! dput(stopd(sb)->base_dir_dentry); ++ dput(stopd(sb)->storage_dir_dentry); */ ++ ++ kfree(stopd(sb)); ++ __stopd(sb) = NULL; ++ } ++} ++ ++ ++#ifdef NOT_NEEDED ++/* ++ * This is called in do_umount before put_super. ++ * The superblock lock is not held yet. ++ * We probably do not need to define this or call write_super ++ * on the hidden_sb, because sync_supers() will get to hidden_sb ++ * sooner or later. But it is also called from file_fsync()... ++ */ ++STATIC void ++mini_fo_write_super(super_block_t *sb) ++{ ++ return; ++} ++#endif /* NOT_NEEDED */ ++ ++ ++STATIC int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_statfs(struct dentry *d, struct kstatfs *buf) ++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++mini_fo_statfs(super_block_t *sb, struct kstatfs *buf) ++#else ++mini_fo_statfs(super_block_t *sb, struct statfs *buf) ++#endif ++{ ++ int err = 0; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++ struct dentry *hidden_d; ++ ++ hidden_d = dtohd(d); ++ err = vfs_statfs(hidden_d, buf); ++#else ++ super_block_t *hidden_sb; ++ ++ hidden_sb = stohs(sb); ++ err = vfs_statfs(hidden_sb, buf); ++#endif ++ ++ return err; ++} ++ ++ ++/* ++ * XXX: not implemented. This is not allowed yet. ++ * Should we call this on the hidden_sb? Probably not. ++ */ ++STATIC int ++mini_fo_remount_fs(super_block_t *sb, int *flags, char *data) ++{ ++ //printk(KERN_CRIT "mini_fo_remount_fs: WARNING, this function is umimplemented.\n"); ++ return -ENOSYS; ++} ++ ++ ++/* ++ * Called by iput() when the inode reference count reached zero ++ * and the inode is not hashed anywhere. Used to clear anything ++ * that needs to be, before the inode is completely destroyed and put ++ * on the inode free list. ++ */ ++STATIC void ++mini_fo_clear_inode(inode_t *inode) ++{ ++ /* ++ * Decrement a reference to a hidden_inode, which was incremented ++ * by our read_inode when it was created initially. ++ */ ++ ++ /* release the wol_list */ ++ if(S_ISDIR(inode->i_mode)) { ++ __meta_put_lists(inode); ++ } ++ ++ /* mk: fan out fun */ ++ if(itohi(inode)) ++ iput(itohi(inode)); ++ if(itohi2(inode)) ++ iput(itohi2(inode)); ++ ++ // XXX: why this assertion fails? ++ // because it doesn't like us ++ // ASSERT((inode->i_state & I_DIRTY) == 0); ++ kfree(itopd(inode)); ++ __itopd(inode) = NULL; ++} ++ ++ ++/* ++ * Called in do_umount() if the MNT_FORCE flag was used and this ++ * function is defined. See comment in linux/fs/super.c:do_umount(). ++ * Used only in nfs, to kill any pending RPC tasks, so that subsequent ++ * code can actually succeed and won't leave tasks that need handling. ++ * ++ * PS. I wonder if this is somehow useful to undo damage that was ++ * left in the kernel after a user level file server (such as amd) ++ * dies. ++ */ ++STATIC void ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) ++mini_fo_umount_begin(struct vfsmount *mnt, int flags) ++{ ++ struct vfsmount *hidden_mnt; ++ ++ hidden_mnt = stopd(mnt->mnt_sb)->hidden_mnt; ++ ++ if (hidden_mnt->mnt_sb->s_op->umount_begin) ++ hidden_mnt->mnt_sb->s_op->umount_begin(hidden_mnt, flags); ++ ++} ++#else ++mini_fo_umount_begin(super_block_t *sb) ++{ ++ super_block_t *hidden_sb; ++ ++ hidden_sb = stohs(sb); ++ ++ if (hidden_sb->s_op->umount_begin) ++ hidden_sb->s_op->umount_begin(hidden_sb); ++ ++} ++#endif ++ ++ ++struct super_operations mini_fo_sops = ++{ ++ read_inode: mini_fo_read_inode, ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++ write_inode: mini_fo_write_inode, ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ put_inode: mini_fo_put_inode, ++#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) ++ delete_inode: mini_fo_delete_inode, ++#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ++ put_super: mini_fo_put_super, ++ statfs: mini_fo_statfs, ++ remount_fs: mini_fo_remount_fs, ++ clear_inode: mini_fo_clear_inode, ++ umount_begin: mini_fo_umount_begin, ++}; diff --git a/target/linux/generic-2.6/patches-2.6.23/210-mac80211_include_wireless_dev.patch b/target/linux/generic-2.6/patches-2.6.23/210-mac80211_include_wireless_dev.patch new file mode 100644 index 0000000000..cca0c0b840 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/210-mac80211_include_wireless_dev.patch @@ -0,0 +1,806 @@ +--- a/include/linux/ieee80211.h ++++ b/include/linux/ieee80211.h +@@ -106,6 +106,75 @@ + } __attribute__ ((packed)); + + ++struct ieee80211_ht_capability { ++ __le16 capabilities_info; ++ u8 mac_ht_params_info; ++ u8 supported_mcs_set[16]; ++ __le16 extended_ht_capability_info; ++ __le32 tx_BF_capability_info; ++ u8 antenna_selection_info; ++}__attribute__ ((packed)); ++ ++struct ieee80211_ht_additional_info { ++ u8 control_chan; ++ u8 ht_param; ++ __le16 operation_mode; ++ __le16 stbc_param; ++ u8 basic_set[16]; ++}__attribute__ ((packed)); ++ ++ ++#define IEEE80211_TSINFO_TYPE(a) ((a.byte1 & 0x01) >> 0) ++#define IEEE80211_TSINFO_TSID(a) ((a.byte1 & 0x1E) >> 1) ++#define IEEE80211_TSINFO_DIR(a) ((a.byte1 & 0x60) >> 5) ++#define IEEE80211_TSINFO_POLICY(a) ((a.byte1 & 0x80) >> 7 + \ ++ (a.byte2 & 0x01) << 1) ++#define IEEE80211_TSINFO_AGG(a) ((a.byte2 & 0x02) >> 1) ++#define IEEE80211_TSINFO_APSD(a) ((a.byte2 & 0x04) >> 2) ++#define IEEE80211_TSINFO_UP(a) ((a.byte2 & 0x38) >> 3) ++#define IEEE80211_TSINFO_ACK(a) ((a.byte2 & 0xC0) >> 6) ++#define IEEE80211_TSINFO_SCHEDULE(a) ((a.byte3 & 0x01) >> 0) ++ ++#define IEEE80211_SET_TSINFO_TYPE(i, d) (i.byte1 |= (d << 0) & 0x01) ++#define IEEE80211_SET_TSINFO_TSID(i, d) (i.byte1 |= (d << 1) & 0x1E) ++#define IEEE80211_SET_TSINFO_DIR(i, d) (i.byte1 |= (d << 5) & 0x60) ++#define IEEE80211_SET_TSINFO_POLICY(i, d) \ ++do { \ ++ i.byte1 |= (d & 0x01) << 7; \ ++ i.byte2 |= (d & 0x02) >> 1; \ ++} while(0) ++#define IEEE80211_SET_TSINFO_AGG(i, d) (i.byte2 |= (d << 1) & 0x02) ++#define IEEE80211_SET_TSINFO_APSD(i, d) (i.byte2 |= (d << 2) & 0x04) ++#define IEEE80211_SET_TSINFO_UP(i, d) (i.byte2 |= (d << 3) & 0x38) ++#define IEEE80211_SET_TSINFO_ACK(i, d) (i.byte2 |= (d << 6) & 0xC0) ++#define IEEE80211_SET_TSINFO_SCHEDULE(i, d) (i.byte3 |= (d << 0) & 0x01) ++ ++struct ieee80211_ts_info { ++ u8 byte1; ++ u8 byte2; ++ u8 byte3; ++} __attribute__ ((packed)); ++ ++struct ieee80211_elem_tspec { ++ struct ieee80211_ts_info ts_info; ++ __le16 nominal_msdu_size; ++ __le16 max_msdu_size; ++ __le32 min_service_interval; ++ __le32 max_service_interval; ++ __le32 inactivity_interval; ++ __le32 suspension_interval; ++ __le32 service_start_time; ++ __le32 min_data_rate; ++ __le32 mean_data_rate; ++ __le32 peak_data_rate; ++ __le32 burst_size; ++ __le32 delay_bound; ++ __le32 min_phy_rate; ++ __le16 surplus_band_allow; ++ __le16 medium_time; ++} __attribute__ ((packed)); ++ ++ + struct ieee80211_mgmt { + __le16 frame_control; + __le16 duration; +@@ -173,9 +242,51 @@ + struct { + u8 action_code; + u8 dialog_token; ++ u8 variable[0]; ++ } __attribute__ ((packed)) addts_req; ++ struct { ++ u8 action_code; ++ u8 dialog_token; ++ __le16 status_code; ++ u8 variable[0]; ++ } __attribute__ ((packed)) addts_resp; ++ struct { ++ u8 action_code; ++ struct ieee80211_ts_info ts_info; ++ __le16 reason_code; ++ } __attribute__ ((packed)) delts; ++ struct { ++ u8 action_code; ++ u8 dialog_token; + u8 status_code; + u8 variable[0]; + } __attribute__ ((packed)) wme_action; ++ struct { ++ u8 action_code; ++ u8 dest[6]; ++ u8 src[6]; ++ __le16 capab_info; ++ __le16 timeout; ++ /* Followed by Supported Rates and ++ * Extended Supported Rates */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) dls_req; ++ struct { ++ u8 action_code; ++ __le16 status_code; ++ u8 dest[6]; ++ u8 src[6]; ++ /* Followed by Capability Information, ++ * Supported Rates and Extended ++ * Supported Rates */ ++ u8 variable[0]; ++ } __attribute__ ((packed)) dls_resp; ++ struct { ++ u8 action_code; ++ u8 dest[6]; ++ u8 src[6]; ++ __le16 reason_code; ++ } __attribute__ ((packed)) dls_teardown; + struct{ + u8 action_code; + u8 element_id; +@@ -184,6 +295,25 @@ + u8 new_chan; + u8 switch_count; + } __attribute__((packed)) chan_switch; ++ struct{ ++ u8 action_code; ++ u8 dialog_token; ++ __le16 capab; ++ __le16 timeout; ++ __le16 start_seq_num; ++ } __attribute__((packed)) addba_req; ++ struct{ ++ u8 action_code; ++ u8 dialog_token; ++ __le16 status; ++ __le16 capab; ++ __le16 timeout; ++ } __attribute__((packed)) addba_resp; ++ struct{ ++ u8 action_code; ++ __le16 params; ++ __le16 reason_code; ++ }__attribute__((packed)) delba; + } u; + } __attribute__ ((packed)) action; + } u; +@@ -270,6 +400,18 @@ + WLAN_STATUS_UNSUPP_RSN_VERSION = 44, + WLAN_STATUS_INVALID_RSN_IE_CAP = 45, + WLAN_STATUS_CIPHER_SUITE_REJECTED = 46, ++ /* 802.11e */ ++ WLAN_STATUS_UNSPECIFIED_QOS = 32, ++ WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33, ++ WLAN_STATUS_ASSOC_DENIED_LOWACK = 34, ++ WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35, ++ WLAN_STATUS_REQUEST_DECLINED = 37, ++ WLAN_STATUS_INVALID_QOS_PARAM = 38, ++ WLAN_STATUS_CHANGE_TSPEC = 39, ++ WLAN_STATUS_WAIT_TS_DELAY = 47, ++ WLAN_STATUS_NO_DIRECT_LINK = 48, ++ WLAN_STATUS_STA_NOT_PRESENT = 49, ++ WLAN_STATUS_STA_NOT_QSTA = 50, + }; + + +@@ -300,9 +442,50 @@ + WLAN_REASON_INVALID_RSN_IE_CAP = 22, + WLAN_REASON_IEEE8021X_FAILED = 23, + WLAN_REASON_CIPHER_SUITE_REJECTED = 24, ++ /* 802.11e */ ++ WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32, ++ WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33, ++ WLAN_REASON_DISASSOC_LOW_ACK = 34, ++ WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35, ++ WLAN_REASON_QSTA_LEAVE_QBSS = 36, ++ WLAN_REASON_QSTA_NOT_USE = 37, ++ WLAN_REASON_QSTA_REQUIRE_SETUP = 38, ++ WLAN_REASON_QSTA_TIMEOUT = 39, ++ WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45, + }; + + ++/* Category Code */ ++enum ieee80211_category { ++ WLAN_CATEGORY_SPECTRUM_MGMT = 0, ++ WLAN_CATEGORY_QOS = 1, ++ WLAN_CATEGORY_DLS = 2, ++ WLAN_CATEGORY_BACK = 3, ++ WLAN_CATEGORY_WMM = 17, ++}; ++ ++/* QoS Action Code */ ++enum ieee80211_qos_actioncode { ++ WLAN_ACTION_QOS_ADDTS_REQ = 0, ++ WLAN_ACTION_QOS_ADDTS_RESP = 1, ++ WLAN_ACTION_QOS_DELTS = 2, ++ WLAN_ACTION_QOS_SCHEDULE = 3, ++}; ++ ++/* DLS Action Code */ ++enum ieee80211_dls_actioncode { ++ WLAN_ACTION_DLS_REQ = 0, ++ WLAN_ACTION_DLS_RESP = 1, ++ WLAN_ACTION_DLS_TEARDOWN = 2, ++}; ++ ++/* BACK Action Code */ ++enum ieee80211_back_actioncode { ++ WLAN_ACTION_ADDBA_REQ = 0, ++ WLAN_ACTION_ADDBA_RESP = 1, ++ WLAN_ACTION_DELBA = 2, ++}; ++ + /* Information Element IDs */ + enum ieee80211_eid { + WLAN_EID_SSID = 0, +@@ -318,6 +501,15 @@ + WLAN_EID_HP_PARAMS = 8, + WLAN_EID_HP_TABLE = 9, + WLAN_EID_REQUEST = 10, ++ /* 802.11e */ ++ WLAN_EID_QBSS_LOAD = 11, ++ WLAN_EID_EDCA_PARAM_SET = 12, ++ WLAN_EID_TSPEC = 13, ++ WLAN_EID_TCLAS = 14, ++ WLAN_EID_SCHEDULE = 15, ++ WLAN_EID_TS_DELAY = 43, ++ WLAN_EID_TCLAS_PROCESSING = 44, ++ WLAN_EID_QOS_CAPA = 46, + /* 802.11h */ + WLAN_EID_PWR_CONSTRAINT = 32, + WLAN_EID_PWR_CAPABILITY = 33, +@@ -332,6 +524,9 @@ + /* 802.11g */ + WLAN_EID_ERP_INFO = 42, + WLAN_EID_EXT_SUPP_RATES = 50, ++ /* 802.11n */ ++ WLAN_EID_HT_CAPABILITY = 45, ++ WLAN_EID_HT_EXTRA_INFO = 61, + /* 802.11i */ + WLAN_EID_RSN = 48, + WLAN_EID_WPA = 221, +@@ -340,6 +535,9 @@ + WLAN_EID_QOS_PARAMETER = 222 + }; + ++/* 80211n */ ++#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 ++ + /* cipher suite selectors */ + #define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 + #define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +@@ -350,4 +548,37 @@ + + #define WLAN_MAX_KEY_LEN 32 + ++enum ieee80211_tsinfo_direction { ++ WLAN_TSINFO_UPLINK = 0, ++ WLAN_TSINFO_DOWNLINK = 1, ++ WLAN_TSINFO_DIRECTLINK = 2, ++ WLAN_TSINFO_BIDIRECTIONAL = 3, ++}; ++ ++enum ieee80211_tsinfo_access { ++ WLAN_TSINFO_EDCA = 1, ++ WLAN_TSINFO_HCCA = 2, ++ WLAN_TSINFO_HEMM = 3, ++}; ++ ++enum ieee80211_tsinfo_psb { ++ WLAN_TSINFO_PSB_LEGACY = 0, ++ WLAN_TSINFO_PSB_APSD = 1, ++}; ++ ++ ++/* WI-FI Alliance OUI Type and Subtype */ ++enum wifi_oui_type { ++ WIFI_OUI_TYPE_WPA = 1, ++ WIFI_OUI_TYPE_WMM = 2, ++ WIFI_OUI_TYPE_WSC = 4, ++ WIFI_OUI_TYPE_PSD = 6, ++}; ++ ++enum wifi_oui_stype_wmm { ++ WIFI_OUI_STYPE_WMM_INFO = 0, ++ WIFI_OUI_STYPE_WMM_PARAM = 1, ++ WIFI_OUI_STYPE_WMM_TSPEC = 2, ++}; ++ + #endif /* IEEE80211_H */ +--- a/include/linux/nl80211.h ++++ b/include/linux/nl80211.h +@@ -7,6 +7,217 @@ + */ + + /** ++ * enum nl80211_commands - supported nl80211 commands ++ * @NL80211_CMD_UNSPEC: unspecified command to catch errors ++ * @NL80211_CMD_RENAME_WIPHY: rename a wiphy, needs ++ * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME ++ * @NL80211_CMD_WIPHY_NEWNAME: rename notification ++ * @NL80211_CMD_GET_CMDLIST: TO BE DEFINED PROPERLY. currently the code makes ++ * it depend on the wiphy only but it really should depend on the ++ * interface type too.... ++ * @NL80211_CMD_NEW_CMDLIST: command list result ++ * @NL80211_CMD_ADD_VIRTUAL_INTERFACE: create a virtual interface for the ++ * wiphy identified by an %NL80211_ATTR_WIPHY attribute with the given ++ * %NL80211_ATTR_IFTYPE and %NL80211_ATTR_IFNAME. ++ * @NL80211_CMD_DEL_VIRTUAL_INTERFACE: destroy a virtual interface identified ++ * by %NL80211_ATTR_IFINDEX. ++ * @NL80211_CMD_CHANGE_VIRTUAL_INTERFACE: change type of virtual interface to ++ * the type given by %NL80211_ATTR_IFTYPE, the interface is identified by ++ * %NL80211_ATTR_IFINDEX. ++ * @NL80211_CMD_GET_WIPHYS: request a list of all wiphys present in the system ++ * @NL80211_CMD_NEW_WIPHYS: returned list of all wiphys ++ * @NL80211_CMD_GET_INTERFACES: request a list of all interfaces belonging to ++ * the wiphy identified by %NL80211_ATTR_WIPHY ++ * @NL80211_CMD_NEW_INTERFACES: result for %NL80211_CMD_GET_INTERFACES ++ * @NL80211_CMD_INITIATE_SCAN: initiate a scan with the passed parameters. THe ++ * parameters may contain %NL80211_ATTR_FLAG_SCAN_ACTIVE, ++ * %NL80211_ATTR_PHYMODE and a list of channels in an ++ * %NL80211_ATTR_CHANNEL_LIST attribute (an array of nested attributes) ++ * containing %NL80211_ATTR_CHANNEL, %NL80211_ATTR_PHYMODE, and possibly ++ * %NL80211_ATTR_FLAG_SCAN_ACTIVE. The outer %NL80211_ATTR_FLAG_SCAN_ACTIVE ++ * is ignored when a channel list is present. ++ * @NL80211_CMD_SCAN_RESULT: scan result, contains an array in ++ * %NL80211_ATTR_BSS_LIST. ++ * @NL80211_CMD_ASSOCIATE: associate with the given parameters ++ * (%NL80211_ATTR_SSID is mandatory, %NL80211_ATTR_TIMEOUT_TU, ++ * %NL80211_ATTR_BSSID, %NL80211_ATTR_CHANNEL, %NL80211_ATTR_PHYMODE, ++ * and %NL80211_ATTR_IE may be given) ++ * @NL80211_CMD_ADD_KEY: add a key with given %NL80211_ATTR_KEY_DATA, ++ * %NL80211_ATTR_KEY_ID, %NL80211_ATTR_KEY_TYPE, %NL80211_ATTR_MAC and ++ * %NL80211_ATTR_KEY_CIPHER attributes. ++ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_ID, ++ * %NL80211_ATTR_KEY_TYPE and %NL80211_ATTR_MAC or all keys. ++ * @__NL80211_CMD_AFTER_LAST: internal use ++ */ ++enum nl80211_commands { ++/* don't change the order or add anything inbetween, this is ABI! */ ++ NL80211_CMD_UNSPEC, ++ /* %input: wiphy, wiphy_name */ ++ NL80211_CMD_RENAME_WIPHY, ++ NL80211_CMD_WIPHY_NEWNAME, ++ /* %input: wiphy|ifindex */ ++ NL80211_CMD_GET_CMDLIST, ++ NL80211_CMD_NEW_CMDLIST, ++ /* %input: wiphy, ifname, {iftype} */ ++ NL80211_CMD_ADD_VIRTUAL_INTERFACE, ++ /* %input: wiphy, ifindex */ ++ NL80211_CMD_DEL_VIRTUAL_INTERFACE, ++ /* %input: ifindex, iftype */ ++ NL80211_CMD_CHANGE_VIRTUAL_INTERFACE, ++ /* %input: */ ++ NL80211_CMD_GET_WIPHYS, ++ NL80211_CMD_NEW_WIPHYS, ++ /* %input: wiphy */ ++ NL80211_CMD_GET_INTERFACES, ++ NL80211_CMD_NEW_INTERFACES, ++ NL80211_CMD_INITIATE_SCAN, ++ NL80211_CMD_SCAN_RESULT, ++ NL80211_CMD_GET_ASSOCIATION, ++ NL80211_CMD_ASSOCIATION_CHANGED, ++ NL80211_CMD_ASSOCIATE, ++ NL80211_CMD_DISASSOCIATE, ++ NL80211_CMD_DEAUTH, ++ NL80211_CMD_GET_AUTH_LIST, ++ NL80211_CMD_NEW_AUTH_LIST, ++ NL80211_CMD_AUTHENTICATION_CHANGED, ++ NL80211_CMD_AP_SET_BEACON, ++ NL80211_CMD_AP_ADD_STA, ++ NL80211_CMD_AP_UPDATE_STA, ++ NL80211_CMD_AP_GET_STA_INFO, ++ NL80211_CMD_AP_SET_RATESETS, ++ NL80211_CMD_ADD_KEY, ++ NL80211_CMD_DEL_KEY, ++ ++ /* add commands here */ ++ ++ /* used to define NL80211_CMD_MAX below */ ++ __NL80211_CMD_AFTER_LAST ++}; ++#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1) ++ ++ ++/** ++ * enum nl80211_attrs - nl80211 netlink attributes ++ * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors ++ * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on ++ * @NL80211_ATTR_IFNAME: network interface name ++ * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. ++ * /sys/class/ieee80211//index ++ * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) ++ * @NL80211_ATTR_CMDS: list of u8's identifying commands a device supports ++ * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype ++ * @NL80211_ATTR_INTERFACE_LIST: interface array, nested netlink attribute ++ * @NL80211_ATTR_WIPHY_LIST: wiphy array, nested netlink attribute ++ * @NL80211_ATTR_BSSID: BSSID (must be 6 bytes) ++ * @NL80211_ATTR_SSID: SSID (1-32 bytes) ++ * @NL80211_ATTR_CHANNEL: channel number ++ * @NL80211_ATTR_PHYMODE: PHY mode, see &enum nl80211_phymode ++ * @NL80211_ATTR_CHANNEL_LIST: netlink nested attribute array containing scan ++ * parameters for channels ++ * @NL80211_ATTR_BSS_LIST: nested attribute containing an array ++ * @NL80211_ATTR_BSSTYPE: BSS type, see &enum nl80211_bsstype ++ * @NL80211_ATTR_BEACON_PERIOD: beacon period ++ * @NL80211_ATTR_DTIM_PERIOD: DTIM period ++ * @NL80211_ATTR_TIMESTAMP: 64-bit timestamp of received beacon/probe response ++ * @NL80211_ATTR_IE: information element(s), maximum length %NL80211_MAX_IE_LEN ++ * @NL80211_ATTR_AUTH_ALGORITHM: authentication algorithm ++ * @NL80211_ATTR_TIMEOUT_TU: timeout in TU (TO BE USED) ++ * @NL80211_ATTR_REASON_CODE: 802.11 reason code ++ * @NL80211_ATTR_ASSOCIATION_ID: association ID (u16, 1-2007) ++ * @NL80211_ATTR_DEAUTHENTICATED: TO BE USED ++ * @NL80211_ATTR_RX_SENSITIVITY: receiver sensitivity in dBm ++ * @NL80211_ATTR_TRANSMIT_POWER: transmit power in mW ++ * @NL80211_ATTR_FRAG_THRESHOLD: fragmentation threshold (bytes) ++ * @NL80211_ATTR_FLAG_SCAN_ACTIVE: netlink flag indiciating active scan ++ * @NL80211_ATTR_KEY_DATA: temporal key data ++ * @NL80211_ATTR_KEY_ID: key ID (u8, 0-3) ++ * @NL80211_ATTR_KEY_TYPE: key type (see &enum nl80211_keytype) ++ * @NL80211_ATTR_MAC: MAC address ++ * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32) ++ * @__NL80211_ATTR_AFTER_LAST: internal use ++ */ ++enum nl80211_attrs { ++/* don't change the order or add anything inbetween, this is ABI! */ ++ NL80211_ATTR_UNSPEC, ++ /* %type: u32 */ ++ NL80211_ATTR_IFINDEX, ++ /* %type: nulstring */ ++ NL80211_ATTR_IFNAME, ++ /* %type: u32 */ ++ NL80211_ATTR_WIPHY, ++ /* %type: nulstring */ ++ NL80211_ATTR_WIPHY_NAME, ++ NL80211_ATTR_CMDS, ++ /* %type: u32 */ ++ NL80211_ATTR_IFTYPE, ++ NL80211_ATTR_INTERFACE_LIST, ++ NL80211_ATTR_WIPHY_LIST, ++ NL80211_ATTR_BSSID, ++ NL80211_ATTR_SSID, ++ NL80211_ATTR_CHANNEL, ++ NL80211_ATTR_PHYMODE, ++ NL80211_ATTR_CHANNEL_LIST, ++ NL80211_ATTR_BSS_LIST, ++ NL80211_ATTR_BSSTYPE, ++ NL80211_ATTR_BEACON_PERIOD, ++ NL80211_ATTR_DTIM_PERIOD, ++ NL80211_ATTR_TIMESTAMP, ++ NL80211_ATTR_IE, ++ NL80211_ATTR_AUTH_ALGORITHM, ++ NL80211_ATTR_TIMEOUT_TU, ++ NL80211_ATTR_REASON_CODE, ++ NL80211_ATTR_ASSOCIATION_ID, ++ NL80211_ATTR_DEAUTHENTICATED, ++ NL80211_ATTR_RX_SENSITIVITY, ++ NL80211_ATTR_TRANSMIT_POWER, ++ NL80211_ATTR_FRAG_THRESHOLD, ++ NL80211_ATTR_FLAG_SCAN_ACTIVE, ++ ++ NL80211_ATTR_KEY_DATA, ++ NL80211_ATTR_KEY_ID, ++ NL80211_ATTR_KEY_TYPE, ++ NL80211_ATTR_MAC, ++ NL80211_ATTR_KEY_CIPHER, ++ ++ NL80211_ATTR_BEACON_HEAD, ++ NL80211_ATTR_BEACON_TAIL, ++ ++ /* add attributes here, update the policy in nl80211.c */ ++ ++ /* used to define NL80211_ATTR_MAX below */ ++ __NL80211_ATTR_AFTER_LAST, ++}; ++#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1) ++ ++/** ++ * enum nl80211_multicast_groups - multicast groups for nl80211 ++ * @NL80211_GROUP_CONFIG: members of this group are notified of ++ * configuration changes ++ */ ++enum nl80211_multicast_groups { ++ /* be notified of configuration changes like wiphy renames */ ++ NL80211_GROUP_CONFIG, ++ ++ /* add groups here */ ++ ++ /* keep last */ ++ __NL80211_GROUP_AFTER_LAST ++}; ++#define NL80211_GROUP_MAX (__NL80211_GROUP_AFTER_LAST - 1) ++ ++/* ++ * maximum length of IE(s) passed in an NL80211_ATTR_IE. ++ * this is an arbitrary limit, 774 means three full-length ++ * IEs would fit... increase if necessary */ ++#define NL80211_MAX_IE_LEN 774 ++ ++/* ++ * maximum number of items in an ATTR_CHANNEL_LIST, ++ * just to avoid too large allocations ++ */ ++#define NL80211_MAX_CHANNEL_LIST_ITEM 200 ++ ++/** + * enum nl80211_iftype - (virtual) interface types + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member +@@ -35,4 +246,56 @@ + }; + #define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1) + ++/** ++ * enum nl80211_phymode - PHY modes ++ * @NL80211_PHYMODE_A: 5 GHz PHY ++ * @NL80211_PHYMODE_B: 2.4 GHz PHY (B mode) ++ * @NL80211_PHYMODE_G: 2.4 GHz PHY (G, compatible with B) ++ * @__NL80211_PHYMODE_AFTER_LAST: internal use ++ * ++ * These values are used for %NL80211_ATTR_PHYMODE. ++ */ ++enum nl80211_phymode { ++ NL80211_PHYMODE_A, ++ NL80211_PHYMODE_B, ++ NL80211_PHYMODE_G, ++ ++ /* keep last */ ++ __NL80211_PHYMODE_AFTER_LAST ++}; ++#define NL80211_PHYMODE_MAX (__NL80211_PHYMODE_AFTER_LAST - 1) ++ ++/** ++ * enum nl80211_bsstype - BSS types ++ * @NL80211_BSSTYPE_INFRASTRUCTURE: infrastructure BSS ++ * @NL80211_BSSTYPE_INDEPENDENT: independent BSS (ad-hoc network) ++ * @__NL80211_BSSTYPE_AFTER_LAST: internal use ++ * ++ * These values are used for %NL80211_ATTR_BSSTYPE. ++ */ ++enum nl80211_bsstype { ++ NL80211_BSSTYPE_INFRASTRUCTURE, ++ NL80211_BSSTYPE_INDEPENDENT, ++ ++ /* keep last */ ++ __NL80211_BSSTYPE_AFTER_LAST ++}; ++#define NL80211_BSSTYPE_MAX (__NL80211_BSSTYPE_AFTER_LAST - 1) ++ ++/** ++ * enum nl80211_keytype - key types ++ * @NL80211_KEYTYPE_GROUP: group key ++ * @NL80211_KEYTYPE_PAIRWISE: pairwise key ++ * @NL80211_KEYTYPE_PEER: peer key ++ */ ++enum nl80211_keytype { ++ NL80211_KEYTYPE_GROUP, ++ NL80211_KEYTYPE_PAIRWISE, ++ NL80211_KEYTYPE_PEER, ++ ++ /* keep last */ ++ __NL80211_KEYTYPE_AFTER_LAST ++}; ++#define NL80211_KEYTYPE_MAX (__NL80211_KEYTYPE_AFTER_LAST - 1) ++ + #endif /* __LINUX_NL80211_H */ +--- a/include/net/cfg80211.h ++++ b/include/net/cfg80211.h +@@ -3,6 +3,7 @@ + + #include + #include ++#include + #include + + /* +@@ -49,6 +50,69 @@ + struct ieee80211_radiotap_iterator *iterator); + + ++/** ++ * struct scan_channel - describes a single channel to scan ++ * @phymode: PHY mode for this channel ++ * @channel: channel number (1-14, ...) ++ * @active: scan actively or passively on this channel ++ */ ++struct scan_channel { ++ enum nl80211_phymode phymode; ++ u32 channel; ++ int active; ++}; ++ ++/** ++ * struct scan_params - describes scan parameters ++ * @n_channels: number of items in @channels array or -1 to indicate all ++ * channels should be scanned (in that case @channels will be %NULL) ++ * @active: when n_channels is -1 this determines active/passive scanning. ++ * @phymode: when n_channels is -1 this determines PHY mode to scan. It is ++ * not possible to scan different PHY modes in one request w/o giving ++ * a channel list. ++ * @channels: array containing @n_channels &struct scan_channel items ++ */ ++struct scan_params { ++ int n_channels; ++ int active; ++ enum nl80211_phymode phymode; ++ struct scan_channel *channels; ++}; ++ ++/** ++ * struct association_params - describes association parameters ++ * @valid: this member contains flags which items are valid ++ * @bssid: the BSSID of the BSS to associate [%ASSOC_PARAMS_BSSID] ++ * @timeout: timeout (in TU) [%ASSOC_PARAMS_TIMEOUT] ++ * @ie: information element(s) to include in the association frames [%ASSOC_PARAMS_IE] ++ * @ie_len: length of the information element(s) ++ * @ssid: the SSID, always valid. ++ * @ssid_len: length of the SSID ++ */ ++struct association_params { ++ u8 *bssid; ++ u32 timeout; ++ u8 *ie; ++ int ie_len; ++ u8 *ssid; ++ int ssid_len; ++ ++ unsigned int valid; ++}; ++#define ASSOC_PARAMS_TIMEOUT (1<<0) ++ ++/** ++ * struct key_params - key information ++ */ ++struct key_params { ++ u8 *key; ++ int key_len; ++ int key_id; ++ u32 key_type; ++ u8 *macaddress; ++ u32 cipher; ++}; ++ + /* from net/wireless.h */ + struct wiphy; + +@@ -68,11 +132,62 @@ + * @add_virtual_intf: create a new virtual interface with the given name + * + * @del_virtual_intf: remove the virtual interface determined by ifindex. ++ * ++ * @change_virtual_intf: change type of virtual interface ++ * ++ * @associate: associate with given parameters ++ * ++ * @disassociate: disassociate from current AP ++ * ++ * @deauth: deauth from current AP ++ * ++ * @initiate_scan: scan with the given information (see &struct scan_params above) ++ * ++ * @get_association: get BSSID of the BSS that the device is currently ++ * associated to and return 1, or return 0 if not ++ * associated (or a negative error code) ++ * @get_auth_list: get list of BSSIDs of all BSSs the device has ++ * authenticated with, must call next_bssid for each, ++ * next_bssid returns non-zero on error, the given data ++ * is to be passed to that callback ++ * @add_key: add a key using &struct key_params ++ * @del_key: delete a key using info from &struct key_params + */ + struct cfg80211_ops { + int (*add_virtual_intf)(struct wiphy *wiphy, char *name, +- unsigned int type); ++ enum nl80211_iftype type); + int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); ++ int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex, ++ enum nl80211_iftype type); ++ ++ int (*associate)(struct wiphy *wiphy, struct net_device *dev, ++ struct association_params *params); ++ int (*disassociate)(struct wiphy *wiphy, struct net_device *dev); ++ int (*deauth)(struct wiphy *wiphy, struct net_device *dev); ++ ++ ++ int (*initiate_scan)(struct wiphy *wiphy, struct net_device *dev, ++ struct scan_params *params); ++ ++ ++ int (*get_association)(struct wiphy *wiphy, struct net_device *dev, ++ u8 *bssid); ++ ++ int (*get_auth_list)(struct wiphy *wiphy, struct net_device *dev, ++ void *data, ++ int (*next_bssid)(void *data, u8 *bssid)); ++ ++ int (*add_key)(struct wiphy *wiphy, struct net_device *dev, ++ struct key_params *params); ++ int (*del_key)(struct wiphy *wiphy, struct net_device *dev, ++ struct key_params *params); + }; + ++ ++/* helper functions specific to nl80211 */ ++extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid, ++ u32 seq, int flags, u8 cmd); ++extern void *nl80211msg_new(struct sk_buff **skb, u32 pid, ++ u32 seq, int flags, u8 cmd); ++ + #endif /* __NET_CFG80211_H */ +--- a/include/net/iw_handler.h ++++ b/include/net/iw_handler.h +@@ -431,7 +431,13 @@ + * Those may be called only within the kernel. + */ + +-/* functions that may be called by driver modules */ ++/* First : function strictly used inside the kernel */ ++ ++/* Handle /proc/net/wireless, called in net/code/dev.c */ ++extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, ++ int length); ++ ++/* Second : functions that may be called by driver modules */ + + /* Send a single event to user space */ + extern void wireless_send_event(struct net_device * dev, +--- a/include/net/mac80211.h ++++ b/include/net/mac80211.h +@@ -300,7 +300,6 @@ + /* Following five fields are used for IEEE 802.11H */ + unsigned int radar_detect; + unsigned int spect_mgmt; +- /* All following fields are currently unused. */ + unsigned int quiet_duration; /* duration of quiet period */ + unsigned int quiet_offset; /* how far into the beacon is the quiet + * period */ +@@ -521,6 +520,9 @@ + * per-packet RC4 key with each TX frame when doing hwcrypto */ + #define IEEE80211_HW_TKIP_REQ_PHASE2_KEY (1<<14) + ++ /* The device capable of supporting 11n */ ++#define IEEE80211_HW_SUPPORT_HT_MODE (1<<15) ++ + u32 flags; /* hardware flags defined above */ + + /* Set to the size of a needed device specific skb headroom for TX skbs. */ +@@ -649,8 +651,7 @@ + * used if the wlan hardware or low-level driver implements PAE. + * 80211.o module will anyway filter frames based on authorization + * state, so this function pointer can be NULL if low-level driver does +- * not require event notification about port state changes. +- * Currently unused. */ ++ * not require event notification about port state changes. */ + int (*set_port_auth)(struct ieee80211_hw *hw, u8 *addr, + int authorized); + +@@ -702,8 +703,9 @@ + /* Get statistics of the current TX queue status. This is used to get + * number of currently queued packets (queue length), maximum queue + * size (limit), and total number of packets sent using each TX queue +- * (count). +- * Currently unused. */ ++ * (count). This information is used for WMM to find out which TX ++ * queues have room for more packets and by hostapd to provide ++ * statistics about the current queueing state to external programs. */ + int (*get_tx_stats)(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats); + +@@ -713,12 +715,25 @@ + * Must be atomic. */ + u64 (*get_tsf)(struct ieee80211_hw *hw); + ++ /* Call low level driver with 11n Block Ack action */ ++ int (*handle_ba_action)(struct ieee80211_hw *hw, ++ struct ieee80211_mgmt *mgmt); ++ + /* Reset the TSF timer and allow firmware/hardware to synchronize with + * other STAs in the IBSS. This is only used in IBSS mode. This + * function is optional if the firmware/hardware takes full care of + * TSF synchronization. */ + void (*reset_tsf)(struct ieee80211_hw *hw); + ++ /* Configure ht parameters. */ ++ int (*conf_ht)(struct ieee80211_hw *hw, ++ struct ieee80211_ht_capability *ht_cap_param, ++ struct ieee80211_ht_additional_info *ht_extra_param); ++ ++ /* Get ht capabilities from the device */ ++ void (*get_ht_capab)(struct ieee80211_hw *hw, ++ struct ieee80211_ht_capability *ht_cap_param); ++ + /* Setup beacon data for IBSS beacons. Unlike access point (Master), + * IBSS uses a fixed beacon frame which is configured using this + * function. This handler is required only for IBSS mode. */ diff --git a/target/linux/generic-2.6/patches-2.6.23/213-kobject_uevent.patch b/target/linux/generic-2.6/patches-2.6.23/213-kobject_uevent.patch new file mode 100644 index 0000000000..bcb4465432 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/213-kobject_uevent.patch @@ -0,0 +1,37 @@ +--- a/lib/kobject_uevent.c ++++ b/lib/kobject_uevent.c +@@ -40,9 +40,22 @@ + char uevent_helper[UEVENT_HELPER_PATH_LEN] = "/sbin/hotplug"; + static DEFINE_SPINLOCK(sequence_lock); + #if defined(CONFIG_NET) +-static struct sock *uevent_sock; ++struct sock *uevent_sock = NULL; ++EXPORT_SYMBOL_GPL(uevent_sock); + #endif + ++u64 uevent_next_seqnum(void) ++{ ++ u64 seq; ++ ++ spin_lock(&sequence_lock); ++ seq = ++uevent_seqnum; ++ spin_unlock(&sequence_lock); ++ ++ return seq; ++} ++EXPORT_SYMBOL_GPL(uevent_next_seqnum); ++ + /** + * kobject_uevent_env - send an uevent with environmental data + * +@@ -159,9 +172,7 @@ + } + + /* we will send an event, request a new sequence number */ +- spin_lock(&sequence_lock); +- seq = ++uevent_seqnum; +- spin_unlock(&sequence_lock); ++ seq = uevent_next_seqnum(); + sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq); + + #if defined(CONFIG_NET) diff --git a/target/linux/generic-2.6/patches-2.6.23/220-sound_kconfig.patch b/target/linux/generic-2.6/patches-2.6.23/220-sound_kconfig.patch new file mode 100644 index 0000000000..c052f2ad29 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/220-sound_kconfig.patch @@ -0,0 +1,11 @@ +--- a/sound/core/Kconfig ++++ b/sound/core/Kconfig +@@ -9,7 +9,7 @@ + depends on SND + + config SND_HWDEP +- tristate ++ tristate "Sound hardware support" + depends on SND + + config SND_RAWMIDI diff --git a/target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch b/target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch new file mode 100644 index 0000000000..8df4724ef1 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/230-pps_support.patch @@ -0,0 +1,2152 @@ +--- /dev/null ++++ b/Documentation/pps/Makefile +@@ -0,0 +1,27 @@ ++TARGETS = ppstest ppsctl ++ ++CFLAGS += -Wall -O2 -D_GNU_SOURCE ++CFLAGS += -I . ++CFLAGS += -ggdb ++ ++# -- Actions section ---------------------------------------------------------- ++ ++.PHONY : all depend dep ++ ++all : .depend $(TARGETS) ++ ++.depend depend dep : ++ $(CC) $(CFLAGS) -M $(TARGETS:=.c) > .depend ++ ++ifeq (.depend,$(wildcard .depend)) ++include .depend ++endif ++ ++ ++# -- Clean section ------------------------------------------------------------ ++ ++.PHONY : clean ++ ++clean : ++ rm -f *.o *~ core .depend ++ rm -f ${TARGETS} +--- /dev/null ++++ b/Documentation/pps/pps.txt +@@ -0,0 +1,170 @@ ++ ++ PPS - Pulse Per Second ++ ---------------------- ++ ++(C) Copyright 2007 Rodolfo Giometti ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++ ++ ++Overview ++-------- ++ ++LinuxPPS provides a programming interface (API) to define into the ++system several PPS sources. ++ ++PPS means "pulse per second" and a PPS source is just a device which ++provides a high precision signal each second so that an application ++can use it to adjust system clock time. ++ ++A PPS source can be connected to a serial port (usually to the Data ++Carrier Detect pin) or to a parallel port (ACK-pin) or to a special ++CPU's GPIOs (this is the common case in embedded systems) but in each ++case when a new pulse comes the system must apply to it a timestamp ++and record it for the userland. ++ ++Common use is the combination of the NTPD as userland program with a ++GPS receiver as PPS source to obtain a wallclock-time with ++sub-millisecond synchronisation to UTC. ++ ++ ++RFC considerations ++------------------ ++ ++While implementing a PPS API as RFC 2783 defines and using an embedded ++CPU GPIO-Pin as physical link to the signal, I encountered a deeper ++problem: ++ ++ At startup it needs a file descriptor as argument for the function ++ time_pps_create(). ++ ++This implies that the source has a /dev/... entry. This assumption is ++ok for the serial and parallel port, where you can do something ++useful besides(!) the gathering of timestamps as it is the central ++task for a PPS-API. But this assumption does not work for a single ++purpose GPIO line. In this case even basic file-related functionality ++(like read() and write()) makes no sense at all and should not be a ++precondition for the use of a PPS-API. ++ ++The problem can be simply solved if you consider that a PPS source is ++not always connected with a GPS data source. ++ ++So your programs should check if the GPS data source (the serial port ++for instance) is a PPS source too, otherwise they should provide the ++possibility to open another device as PPS source. ++ ++In LinuxPPS the PPS sources are simply char devices usually mapped ++into files /dev/pps0, /dev/pps1, etc.. ++ ++ ++Coding example ++-------------- ++ ++To register a PPS source into the kernel you should define a struct ++pps_source_info_s as follow: ++ ++ static struct pps_source_info_s pps_ktimer_info = { ++ name : "ktimer", ++ path : "", ++ mode : PPS_CAPTUREASSERT | PPS_OFFSETASSERT | \ ++ PPS_ECHOASSERT | \ ++ PPS_CANWAIT | PPS_TSFMT_TSPEC, ++ echo : pps_ktimer_echo, ++ owner : THIS_MODULE, ++ }; ++ ++and then calling the function pps_register_source() in your ++intialization routine as follow: ++ ++ source = pps_register_source(&pps_ktimer_info, ++ PPS_CAPTUREASSERT | PPS_OFFSETASSERT); ++ ++The pps_register_source() prototype is: ++ ++ int pps_register_source(struct pps_source_info_s *info, int default_params) ++ ++where "info" is a pointer to a structure that describes a particular ++PPS source, "default_params" tells the system what the initial default ++parameters for the device should be (is obvious that these parameters ++must be a subset of ones defined into the struct ++pps_source_info_s which describe the capabilities of the driver). ++ ++Once you have registered a new PPS source into the system you can ++signal an assert event (for example in the interrupt handler routine) ++just using: ++ ++ pps_event(source, PPS_CAPTUREASSERT, ptr); ++ ++The same function may also run the defined echo function ++(pps_ktimer_echo(), passing to it the "ptr" pointer) if the user ++asked for that... etc.. ++ ++Please see the file drivers/pps/clients/ktimer.c for an example code. ++ ++ ++SYSFS support ++------------- ++ ++If the SYSFS filesystem is enabled in the kernel it provides a new class: ++ ++ $ ls /sys/class/pps/ ++ pps0/ pps1/ pps2/ ++ ++Every directory is the ID of a PPS sources defined into the system and ++inside you find several files: ++ ++ $ ls /sys/class/pps/pps0/ ++ assert clear echo mode name path subsystem@ uevent ++ ++Inside each "assert" and "clear" file you can find the timestamp and a ++sequence number: ++ ++ $ cat /sys/class/pps/pps0/assert ++ 1170026870.983207967#8 ++ ++Where before the "#" is the timestamp in seconds and after it is the ++sequence number. Other files are: ++ ++* echo: reports if the PPS source has an echo function or not; ++ ++* mode: reports available PPS functioning modes; ++ ++* name: reports the PPS source's name; ++ ++* path: reports the PPS source's device path, that is the device the ++ PPS source is connected to (if it exists). ++ ++ ++Testing the PPS support ++----------------------- ++ ++In order to test the PPS support even without specific hardware you can use ++the ktimer driver (see the client subsection in the PPS configuration menu) ++and the userland tools provided into Documentaion/pps/ directory. ++ ++Once you have enabled the compilation of ktimer just modprobe it (if ++not statically compiled): ++ ++ # modprobe ktimer ++ ++and the run ppstest as follow: ++ ++ $ ./ppstest /dev/pps0 ++ trying PPS source "/dev/pps1" ++ found PPS source "/dev/pps1" ++ ok, found 1 source(s), now start fetching data... ++ source 0 - assert 1186592699.388832443, sequence: 364 - clear 0.000000000, sequence: 0 ++ source 0 - assert 1186592700.388931295, sequence: 365 - clear 0.000000000, sequence: 0 ++ source 0 - assert 1186592701.389032765, sequence: 366 - clear 0.000000000, sequence: 0 ++ ++Please, note that to compile userland programs you need the file timepps.h ++(see Documentation/pps/). +--- /dev/null ++++ b/Documentation/pps/ppsctl.c +@@ -0,0 +1,62 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++void usage(char *name) ++{ ++ fprintf(stderr, "usage: %s [enable|disable]\n", name); ++ ++ exit(EXIT_FAILURE); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int fd; ++ int ret; ++ struct serial_struct ss; ++ ++ if (argc < 2) ++ usage(argv[0]); ++ ++ fd = open(argv[1], O_RDWR); ++ if (fd < 0) { ++ perror("open"); ++ exit(EXIT_FAILURE); ++ } ++ ++ ret = ioctl(fd, TIOCGSERIAL, &ss); ++ if (ret < 0) { ++ perror("ioctl(TIOCGSERIAL)"); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (argc < 3) { /* just read PPS status */ ++ printf("PPS is %sabled\n", ++ ss.flags & ASYNC_HARDPPS_CD ? "en" : "dis"); ++ exit(EXIT_SUCCESS); ++ } ++ ++ if (argv[2][0] == 'e' || argv[2][0] == '1') ++ ss.flags |= ASYNC_HARDPPS_CD; ++ else if (argv[2][0] == 'd' || argv[2][0] == '0') ++ ss.flags &= ~ASYNC_HARDPPS_CD; ++ else { ++ fprintf(stderr, "invalid state argument \"%s\"\n", argv[2]); ++ exit(EXIT_FAILURE); ++ } ++ ++ ret = ioctl(fd, TIOCSSERIAL, &ss); ++ if (ret < 0) { ++ perror("ioctl(TIOCSSERIAL)"); ++ exit(EXIT_FAILURE); ++ } ++ ++ return 0; ++} +--- /dev/null ++++ b/Documentation/pps/ppsfind +@@ -0,0 +1,17 @@ ++#!/bin/sh ++ ++SYS="/sys/class/pps/" ++ ++if [ $# -lt 1 ] ; then ++ echo "usage: ppsfind " >&2 ++ exit 1 ++fi ++ ++for d in $(ls $SYS) ; do ++ if grep $1 $SYS/$d/name >& /dev/null || \ ++ grep $1 $SYS/$d/path >& /dev/null ; then ++ echo "$d: name=$(cat $SYS/$d/name) path=$(cat $SYS/$d/path)" ++ fi ++done ++ ++exit 0 +--- /dev/null ++++ b/Documentation/pps/ppstest.c +@@ -0,0 +1,151 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++int find_source(char *path, pps_handle_t *handle, int *avail_mode) ++{ ++ pps_params_t params; ++ int ret; ++ ++ printf("trying PPS source \"%s\"\n", path); ++ ++ /* Try to find the source by using the supplied "path" name */ ++ ret = open(path, O_RDWR); ++ if (ret < 0) { ++ fprintf(stderr, "unable to open device \"%s\" (%m)\n", path); ++ return ret; ++ } ++ ++ /* Open the PPS source (and check the file descriptor) */ ++ ret = time_pps_create(ret, handle); ++ if (ret < 0) { ++ fprintf(stderr, "cannot create a PPS source from device " ++ "\"%s\" (%m)\n", path); ++ return -1; ++ } ++ printf("found PPS source \"%s\"\n", path); ++ ++ /* Find out what features are supported */ ++ ret = time_pps_getcap(*handle, avail_mode); ++ if (ret < 0) { ++ fprintf(stderr, "cannot get capabilities (%m)\n"); ++ return -1; ++ } ++ if ((*avail_mode & PPS_CAPTUREASSERT) == 0) { ++ fprintf(stderr, "cannot CAPTUREASSERT\n"); ++ return -1; ++ } ++ if ((*avail_mode & PPS_OFFSETASSERT) == 0) { ++ fprintf(stderr, "cannot OFFSETASSERT\n"); ++ return -1; ++ } ++ ++ /* Capture assert timestamps, and compensate for a 675 nsec ++ * propagation delay */ ++ ret = time_pps_getparams(*handle, ¶ms); ++ if (ret < 0) { ++ fprintf(stderr, "cannot get parameters (%m)\n"); ++ return -1; ++ } ++ params.assert_offset.tv_sec = 0; ++ params.assert_offset.tv_nsec = 675; ++ params.mode |= PPS_CAPTUREASSERT | PPS_OFFSETASSERT; ++ ret = time_pps_setparams(*handle, ¶ms); ++ if (ret < 0) { ++ fprintf(stderr, "cannot set parameters (%m)\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int fetch_source(int i, pps_handle_t *handle, int *avail_mode) ++{ ++ struct timespec timeout; ++ pps_info_t infobuf; ++ int ret; ++ ++ /* create a zero-valued timeout */ ++ timeout.tv_sec = 3; ++ timeout.tv_nsec = 0; ++ ++retry: ++ if (*avail_mode & PPS_CANWAIT) /* waits for the next event */ ++ ret = time_pps_fetch(*handle, PPS_TSFMT_TSPEC, &infobuf, ++ &timeout); ++ else { ++ sleep(1); ++ ret = time_pps_fetch(*handle, PPS_TSFMT_TSPEC, &infobuf, ++ &timeout); ++ } ++ if (ret < 0) { ++ if (ret == -EINTR) { ++ fprintf(stderr, "time_pps_fetch() got a signal!\n"); ++ goto retry; ++ } ++ ++ fprintf(stderr, "time_pps_fetch() error %d (%m)\n", ret); ++ return -1; ++ } ++ ++ printf("source %d - " ++ "assert %ld.%09ld, sequence: %ld - " ++ "clear %ld.%09ld, sequence: %ld\n", ++ i, ++ infobuf.assert_timestamp.tv_sec, ++ infobuf.assert_timestamp.tv_nsec, ++ infobuf.assert_sequence, ++ infobuf.clear_timestamp.tv_sec, ++ infobuf.clear_timestamp.tv_nsec, infobuf.clear_sequence); ++ ++ return 0; ++} ++ ++void usage(char *name) ++{ ++ fprintf(stderr, "usage: %s [ ...]\n", name); ++ exit(EXIT_FAILURE); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int num; ++ pps_handle_t handle[4]; ++ int avail_mode[4]; ++ int i = 0; ++ int ret; ++ ++ /* Check the command line */ ++ if (argc < 2) ++ usage(argv[0]); ++ ++ for (i = 1; i < argc && i <= 4; i++) { ++ ret = find_source(argv[i], &handle[i - 1], &avail_mode[i - 1]); ++ if (ret < 0) ++ exit(EXIT_FAILURE); ++ } ++ ++ num = i - 1; ++ printf("ok, found %d source(s), now start fetching data...\n", num); ++ ++ /* loop, printing the most recent timestamp every second or so */ ++ while (1) { ++ for (i = 0; i < num; i++) { ++ ret = fetch_source(i, &handle[i], &avail_mode[i]); ++ if (ret < 0 && errno != ETIMEDOUT) ++ exit(EXIT_FAILURE); ++ } ++ } ++ ++ for (; i >= 0; i--) ++ time_pps_destroy(handle[i]); ++ ++ return 0; ++} +--- /dev/null ++++ b/Documentation/pps/timepps.h +@@ -0,0 +1,193 @@ ++/* ++ * timepps.h -- PPS API main header ++ * ++ * Copyright (C) 2005-2007 Rodolfo Giometti ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _SYS_TIMEPPS_H_ ++#define _SYS_TIMEPPS_H_ ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * New data structures ++ */ ++ ++struct ntp_fp { ++ unsigned int integral; ++ unsigned int fractional; ++}; ++ ++union pps_timeu { ++ struct timespec tspec; ++ struct ntp_fp ntpfp; ++ unsigned long longpad[3]; ++}; ++ ++struct pps_info { ++ unsigned long assert_sequence; /* seq. num. of assert event */ ++ unsigned long clear_sequence; /* seq. num. of clear event */ ++ union pps_timeu assert_tu; /* time of assert event */ ++ union pps_timeu clear_tu; /* time of clear event */ ++ int current_mode; /* current mode bits */ ++}; ++ ++struct pps_params { ++ int api_version; /* API version # */ ++ int mode; /* mode bits */ ++ union pps_timeu assert_off_tu; /* offset compensation for assert */ ++ union pps_timeu clear_off_tu; /* offset compensation for clear */ ++}; ++ ++typedef int pps_handle_t; /* represents a PPS source */ ++typedef unsigned long pps_seq_t; /* sequence number */ ++typedef struct ntp_fp ntp_fp_t; /* NTP-compatible time stamp */ ++typedef union pps_timeu pps_timeu_t; /* generic data type for time stamps */ ++typedef struct pps_info pps_info_t; ++typedef struct pps_params pps_params_t; ++ ++#define assert_timestamp assert_tu.tspec ++#define clear_timestamp clear_tu.tspec ++ ++#define assert_timestamp_ntpfp assert_tu.ntpfp ++#define clear_timestamp_ntpfp clear_tu.ntpfp ++ ++#define assert_offset assert_off_tu.tspec ++#define clear_offset clear_off_tu.tspec ++ ++#define assert_offset_ntpfp assert_off_tu.ntpfp ++#define clear_offset_ntpfp clear_off_tu.ntpfp ++ ++/* ++ * The PPS API ++ */ ++ ++static __inline int time_pps_create(int source, pps_handle_t *handle) ++{ ++ int ret; ++ ++ if (!handle) { ++ errno = EINVAL; ++ return -1; ++ } ++ ++ /* First we check if current device is a PPS valid PPS one... ++ */ ++ ret = ioctl(source, PPS_CHECK); ++ if (ret) { ++ errno = EOPNOTSUPP; ++ return -1; ++ } ++ ++ /* ... then since in LinuxPPS there are no differences between a ++ * "PPS source" and a "PPS handle", we simply return the same value. ++ */ ++ *handle = source; ++ ++ return 0; ++} ++ ++static __inline int time_pps_destroy(pps_handle_t handle) ++{ ++ return close(handle); ++} ++ ++static __inline int time_pps_getparams(pps_handle_t handle, ++ pps_params_t *ppsparams) ++{ ++ int ret; ++ struct pps_kparams __ppsparams; ++ ++ ret = ioctl(handle, PPS_GETPARAMS, &__ppsparams); ++ ++ ppsparams->api_version = __ppsparams.api_version; ++ ppsparams->mode = __ppsparams.mode; ++ ppsparams->assert_off_tu.tspec.tv_sec = __ppsparams.assert_off_tu.sec; ++ ppsparams->assert_off_tu.tspec.tv_nsec = __ppsparams.assert_off_tu.nsec; ++ ppsparams->clear_off_tu.tspec.tv_sec = __ppsparams.clear_off_tu.sec; ++ ppsparams->clear_off_tu.tspec.tv_nsec = __ppsparams.clear_off_tu.nsec; ++ ++ return ret; ++} ++ ++static __inline int time_pps_setparams(pps_handle_t handle, ++ const pps_params_t *ppsparams) ++{ ++ struct pps_kparams __ppsparams; ++ ++ __ppsparams.api_version = ppsparams->api_version; ++ __ppsparams.mode = ppsparams->mode; ++ __ppsparams.assert_off_tu.sec = ppsparams->assert_off_tu.tspec.tv_sec; ++ __ppsparams.assert_off_tu.nsec = ppsparams->assert_off_tu.tspec.tv_nsec; ++ __ppsparams.clear_off_tu.sec = ppsparams->clear_off_tu.tspec.tv_sec; ++ __ppsparams.clear_off_tu.nsec = ppsparams->clear_off_tu.tspec.tv_nsec; ++ ++ return ioctl(handle, PPS_SETPARAMS, &__ppsparams); ++} ++ ++/* Get capabilities for handle */ ++static __inline int time_pps_getcap(pps_handle_t handle, int *mode) ++{ ++ return ioctl(handle, PPS_GETCAP, mode); ++} ++ ++static __inline int time_pps_fetch(pps_handle_t handle, const int tsformat, ++ pps_info_t *ppsinfobuf, ++ const struct timespec *timeout) ++{ ++ struct pps_fdata __fdata; ++ int ret; ++ ++ /* Sanity checks */ ++ if (tsformat != PPS_TSFMT_TSPEC) { ++ errno = EINVAL; ++ return -1; ++ } ++ ++ if (timeout) { ++ __fdata.timeout.sec = timeout->tv_sec; ++ __fdata.timeout.nsec = timeout->tv_nsec; ++ __fdata.timeout.flags = ~PPS_TIME_INVALID; ++ } else ++ __fdata.timeout.flags = PPS_TIME_INVALID; ++ ++ ret = ioctl(handle, PPS_FETCH, &__fdata); ++ ++ ppsinfobuf->assert_sequence = __fdata.info.assert_sequence; ++ ppsinfobuf->clear_sequence = __fdata.info.clear_sequence; ++ ppsinfobuf->assert_tu.tspec.tv_sec = __fdata.info.assert_tu.sec; ++ ppsinfobuf->assert_tu.tspec.tv_nsec = __fdata.info.assert_tu.nsec; ++ ppsinfobuf->clear_tu.tspec.tv_sec = __fdata.info.clear_tu.sec; ++ ppsinfobuf->clear_tu.tspec.tv_nsec = __fdata.info.clear_tu.nsec; ++ ppsinfobuf->current_mode = __fdata.info.current_mode; ++ ++ return ret; ++} ++ ++static __inline int time_pps_kcbind(pps_handle_t handle, ++ const int kernel_consumer, ++ const int edge, const int tsformat) ++{ ++ /* LinuxPPS doesn't implement kernel consumer feature */ ++ errno = EOPNOTSUPP; ++ return -1; ++} ++ ++#endif /* _SYS_TIMEPPS_H_ */ +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -3011,6 +3011,13 @@ + M: jchapman@katalix.com + S: Maintained + ++PPS SUPPORT ++P: Rodolfo Giometti ++M: giometti@enneenne.com ++W: http://wiki.enneenne.com/index.php/LinuxPPS_support ++L: linuxpps@ml.enneenne.com ++S: Maintained ++ + PREEMPTIBLE KERNEL + P: Robert Love + M: rml@tech9.net +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -52,6 +52,8 @@ + + source "drivers/spi/Kconfig" + ++source "drivers/pps/Kconfig" ++ + source "drivers/w1/Kconfig" + + source "drivers/power/Kconfig" +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -63,6 +63,7 @@ + obj-$(CONFIG_I2O) += message/ + obj-$(CONFIG_RTC_LIB) += rtc/ + obj-y += i2c/ ++obj-$(CONFIG_PPS) += pps/ + obj-$(CONFIG_W1) += w1/ + obj-$(CONFIG_POWER_SUPPLY) += power/ + obj-$(CONFIG_HWMON) += hwmon/ +--- a/drivers/char/lp.c ++++ b/drivers/char/lp.c +@@ -746,6 +746,27 @@ + + #endif /* console on line printer */ + ++/* Support for PPS signal on the line printer */ ++ ++#ifdef CONFIG_PPS_CLIENT_LP ++ ++static void lp_pps_echo(int source, int event, void *data) ++{ ++ struct parport *port = data; ++ unsigned char status = parport_read_status(port); ++ ++ /* echo event via SEL bit */ ++ parport_write_control(port, ++ parport_read_control(port) | PARPORT_CONTROL_SELECT); ++ ++ /* signal no event */ ++ if ((status & PARPORT_STATUS_ACK) != 0) ++ parport_write_control(port, ++ parport_read_control(port) & ~PARPORT_CONTROL_SELECT); ++} ++ ++#endif ++ + /* --- initialisation code ------------------------------------- */ + + static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; +@@ -817,6 +838,38 @@ + } + #endif + ++#ifdef CONFIG_PPS_CLIENT_LP ++ port->pps_info.owner = THIS_MODULE; ++ port->pps_info.dev = port->dev; ++ snprintf(port->pps_info.path, PPS_MAX_NAME_LEN, "/dev/lp%d", nr); ++ ++ /* No PPS support if lp port has no IRQ line */ ++ if (port->irq != PARPORT_IRQ_NONE) { ++ strncpy(port->pps_info.name, port->name, PPS_MAX_NAME_LEN); ++ ++ port->pps_info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | \ ++ PPS_ECHOASSERT | \ ++ PPS_CANWAIT | PPS_TSFMT_TSPEC; ++ ++ port->pps_info.echo = lp_pps_echo; ++ ++ port->pps_source = pps_register_source(&(port->pps_info), ++ PPS_CAPTUREASSERT | PPS_OFFSETASSERT); ++ if (port->pps_source < 0) ++ dev_err(port->dev, ++ "cannot register PPS source \"%s\"\n", ++ port->pps_info.path); ++ else ++ dev_info(port->dev, "PPS source #%d \"%s\" added\n", ++ port->pps_source, port->pps_info.path); ++ } else { ++ port->pps_source = -1; ++ dev_err(port->dev, "PPS support disabled due port \"%s\" is " ++ "in polling mode\n", ++ port->pps_info.path); ++ } ++#endif ++ + return 0; + } + +@@ -860,6 +913,14 @@ + console_registered = NULL; + } + #endif /* CONFIG_LP_CONSOLE */ ++ ++#ifdef CONFIG_PPS_CLIENT_LP ++ if (port->pps_source >= 0) { ++ pps_unregister_source(port->pps_source); ++ dev_dbg(port->dev, "PPS source #%d \"%s\" removed\n", ++ port->pps_source, port->pps_info.path); ++ } ++#endif + } + + static struct parport_driver lp_driver = { +--- /dev/null ++++ b/drivers/pps/Kconfig +@@ -0,0 +1,34 @@ ++# ++# PPS support configuration ++# ++ ++menu "PPS support" ++ ++config PPS ++ tristate "PPS support" ++ depends on EXPERIMENTAL ++ ---help--- ++ PPS (Pulse Per Second) is a special pulse provided by some GPS ++ antennae. Userland can use it to get an high time reference. ++ ++ Some antennae's PPS signals are connected with the CD (Carrier ++ Detect) pin of the serial line they use to communicate with the ++ host. In this case use the SERIAL_LINE client support. ++ ++ Some antennae's PPS signals are connected with some special host ++ inputs so you have to enable the corresponding client support. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pps_core.ko. ++ ++config PPS_DEBUG ++ bool "PPS debugging messages" ++ depends on PPS ++ help ++ Say Y here if you want the PPS support to produce a bunch of debug ++ messages to the system log. Select this if you are having a ++ problem with PPS support and want to see more of what is going on. ++ ++source drivers/pps/clients/Kconfig ++ ++endmenu +--- /dev/null ++++ b/drivers/pps/Makefile +@@ -0,0 +1,11 @@ ++# ++# Makefile for the PPS core. ++# ++ ++pps_core-objs += pps.o kapi.o sysfs.o ++obj-$(CONFIG_PPS) += pps_core.o ++obj-y += clients/ ++ ++ifeq ($(CONFIG_PPS_DEBUG),y) ++EXTRA_CFLAGS += -DDEBUG ++endif +--- /dev/null ++++ b/drivers/pps/clients/Kconfig +@@ -0,0 +1,38 @@ ++# ++# PPS clients configuration ++# ++ ++if PPS ++ ++comment "PPS clients support" ++ ++config PPS_CLIENT_KTIMER ++ tristate "Kernel timer client (Testing client, use for debug)" ++ help ++ If you say yes here you get support for a PPS debugging client ++ which uses a kernel timer to generate the PPS signal. ++ ++ This driver can also be built as a module. If so, the module ++ will be called ktimer.ko. ++ ++comment "UART serial support (forced off)" ++ depends on ! (SERIAL_CORE != n && !(PPS = m && SERIAL_CORE = y)) ++ ++config PPS_CLIENT_UART ++ bool "UART serial support" ++ depends on SERIAL_CORE != n && !(PPS = m && SERIAL_CORE = y) ++ help ++ If you say yes here you get support for a PPS source connected ++ with the CD (Carrier Detect) pin of your serial port. ++ ++comment "Parallel printer support (forced off)" ++ depends on !( PRINTER != n && !(PPS = m && PRINTER = y)) ++ ++config PPS_CLIENT_LP ++ bool "Parallel printer support" ++ depends on PRINTER != n && !(PPS = m && PRINTER = y) ++ help ++ If you say yes here you get support for a PPS source connected ++ with the interrupt pin of your parallel port. ++ ++endif +--- /dev/null ++++ b/drivers/pps/clients/Makefile +@@ -0,0 +1,9 @@ ++# ++# Makefile for PPS clients. ++# ++ ++obj-$(CONFIG_PPS_CLIENT_KTIMER) += ktimer.o ++ ++ifeq ($(CONFIG_PPS_DEBUG),y) ++EXTRA_CFLAGS += -DDEBUG ++endif +--- /dev/null ++++ b/drivers/pps/clients/ktimer.c +@@ -0,0 +1,114 @@ ++/* ++ * ktimer.c -- kernel timer test client ++ * ++ * ++ * Copyright (C) 2005-2006 Rodolfo Giometti ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ * Global variables ++ */ ++ ++static int source; ++static struct timer_list ktimer; ++ ++/* ++ * The kernel timer ++ */ ++ ++static void pps_ktimer_event(unsigned long ptr) ++{ ++ pr_info("PPS event at %lu\n", jiffies); ++ ++ pps_event(source, PPS_CAPTUREASSERT, NULL); ++ ++ mod_timer(&ktimer, jiffies + HZ); ++} ++ ++/* ++ * The echo function ++ */ ++ ++static void pps_ktimer_echo(int source, int event, void *data) ++{ ++ pr_info("echo %s %s for source %d\n", ++ event & PPS_CAPTUREASSERT ? "assert" : "", ++ event & PPS_CAPTURECLEAR ? "clear" : "", ++ source); ++} ++ ++/* ++ * The PPS info struct ++ */ ++ ++static struct pps_source_info pps_ktimer_info = { ++ name : "ktimer", ++ path : "", ++ mode : PPS_CAPTUREASSERT | PPS_OFFSETASSERT | \ ++ PPS_ECHOASSERT | \ ++ PPS_CANWAIT | PPS_TSFMT_TSPEC, ++ echo : pps_ktimer_echo, ++ owner : THIS_MODULE, ++}; ++ ++/* ++ * Module staff ++ */ ++ ++static void __exit pps_ktimer_exit(void) ++{ ++ del_timer_sync(&ktimer); ++ pps_unregister_source(source); ++ ++ pr_info("ktimer PPS source unregistered\n"); ++} ++ ++static int __init pps_ktimer_init(void) ++{ ++ int ret; ++ ++ ret = pps_register_source(&pps_ktimer_info, ++ PPS_CAPTUREASSERT | PPS_OFFSETASSERT); ++ if (ret < 0) { ++ printk(KERN_ERR "cannot register ktimer source\n"); ++ return ret; ++ } ++ source = ret; ++ ++ setup_timer(&ktimer, pps_ktimer_event, 0); ++ mod_timer(&ktimer, jiffies + HZ); ++ ++ pr_info("ktimer PPS source registered at %d\n", source); ++ ++ return 0; ++} ++ ++module_init(pps_ktimer_init); ++module_exit(pps_ktimer_exit); ++ ++MODULE_AUTHOR("Rodolfo Giometti "); ++MODULE_DESCRIPTION("dummy PPS source by using a kernel timer (just for debug)"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/pps/kapi.c +@@ -0,0 +1,271 @@ ++/* ++ * kapi.c -- kernel API ++ * ++ * ++ * Copyright (C) 2005-2007 Rodolfo Giometti ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Local variables ++ */ ++ ++static spinlock_t idr_lock = SPIN_LOCK_UNLOCKED; ++static DEFINE_IDR(pps_idr); ++ ++/* ++ * Local functions ++ */ ++ ++static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) ++{ ++ ts->nsec += offset->nsec; ++ if (ts->nsec >= NSEC_PER_SEC) { ++ ts->nsec -= NSEC_PER_SEC; ++ ts->sec++; ++ } else if (ts->nsec < 0) { ++ ts->nsec += NSEC_PER_SEC; ++ ts->sec--; ++ } ++ ts->sec += offset->sec; ++} ++ ++/* ++ * Exported functions ++ */ ++ ++int pps_register_source(struct pps_source_info *info, int default_params) ++{ ++ struct pps_device *pps; ++ int id; ++ int err; ++ ++ /* Sanity checks */ ++ if ((info->mode & default_params) != default_params) { ++ printk(KERN_ERR "pps: %s: unsupported default parameters\n", ++ info->name); ++ err = -EINVAL; ++ goto pps_register_source_exit; ++ } ++ if ((info->mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) != 0 && ++ info->echo == NULL) { ++ printk(KERN_ERR "pps: %s: echo function is not defined\n", ++ info->name); ++ err = -EINVAL; ++ goto pps_register_source_exit; ++ } ++ if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { ++ printk(KERN_ERR "pps: %s: unspecified time format\n", ++ info->name); ++ err = -EINVAL; ++ goto pps_register_source_exit; ++ } ++ ++ /* Allocate memory for the new PPS source struct */ ++ pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); ++ if (pps == NULL) { ++ err = -ENOMEM; ++ goto pps_register_source_exit; ++ } ++ ++ /* Get new ID for the new PPS source */ ++ if (idr_pre_get(&pps_idr, GFP_KERNEL) == 0) { ++ err = -ENOMEM; ++ goto kfree_pps; ++ } ++ ++ spin_lock_irq(&idr_lock); ++ err = idr_get_new(&pps_idr, pps, &id); ++ spin_unlock_irq(&idr_lock); ++ ++ if (err < 0) ++ goto kfree_pps; ++ ++ id = id & MAX_ID_MASK; ++ if (id >= PPS_MAX_SOURCES) { ++ printk(KERN_ERR "pps: %s: too much PPS sources in the system\n", ++ info->name); ++ err = -EBUSY; ++ goto free_idr; ++ } ++ ++ pps->id = id; ++ pps->params.api_version = PPS_API_VERS; ++ pps->params.mode = default_params; ++ pps->info = *info; ++ ++ init_waitqueue_head(&pps->queue); ++ spin_lock_init(&pps->lock); ++ atomic_set(&pps->usage, 0); ++ init_waitqueue_head(&pps->usage_queue); ++ ++ /* Create the char device */ ++ err = pps_register_cdev(pps); ++ if (err < 0) { ++ printk(KERN_ERR "pps: %s: unable to create char device\n", ++ info->name); ++ goto free_idr; ++ } ++ ++ /* Create the sysfs entry */ ++ err = pps_sysfs_create_source_entry(pps); ++ if (err < 0) { ++ printk(KERN_ERR "pps: %s: unable to create sysfs entries\n", ++ info->name); ++ goto unregister_cdev; ++ } ++ ++ pr_info("new PPS source %s at ID %d\n", info->name, id); ++ ++ return id; ++ ++unregister_cdev: ++ pps_unregister_cdev(pps); ++ ++free_idr: ++ spin_lock_irq(&idr_lock); ++ idr_remove(&pps_idr, id); ++ spin_unlock_irq(&idr_lock); ++ ++kfree_pps: ++ kfree(pps); ++ ++pps_register_source_exit: ++ printk(KERN_ERR "pps: %s: unable to register source\n", info->name); ++ ++ return err; ++} ++EXPORT_SYMBOL(pps_register_source); ++ ++void pps_unregister_source(int source) ++{ ++ struct pps_device *pps; ++ ++ spin_lock_irq(&idr_lock); ++ pps = idr_find(&pps_idr, source); ++ ++ if (!pps) { ++ spin_unlock_irq(&idr_lock); ++ return; ++ } ++ ++ /* This should be done first in order to deny IRQ handlers ++ * to access PPS structs ++ */ ++ ++ idr_remove(&pps_idr, pps->id); ++ spin_unlock_irq(&idr_lock); ++ ++ wait_event(pps->usage_queue, atomic_read(&pps->usage) == 0); ++ ++ pps_sysfs_remove_source_entry(pps); ++ pps_unregister_cdev(pps); ++ kfree(pps); ++} ++EXPORT_SYMBOL(pps_unregister_source); ++ ++void pps_event(int source, int event, void *data) ++{ ++ struct pps_device *pps; ++ struct timespec __ts; ++ struct pps_ktime ts; ++ unsigned long flags; ++ ++ /* First of all we get the time stamp... */ ++ getnstimeofday(&__ts); ++ ++ /* ... and translate it to PPS time data struct */ ++ ts.sec = __ts.tv_sec; ++ ts.nsec = __ts.tv_nsec; ++ ++ if ((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0 ) { ++ printk(KERN_ERR "pps: unknown event (%x) for source %d\n", ++ event, source); ++ return; ++ } ++ ++ spin_lock_irqsave(&idr_lock, flags); ++ pps = idr_find(&pps_idr, source); ++ ++ /* If we find a valid PPS source we lock it before leaving ++ * the lock! ++ */ ++ if (pps) ++ atomic_inc(&pps->usage); ++ spin_unlock_irqrestore(&idr_lock, flags); ++ ++ if (!pps) ++ return; ++ ++ pr_debug("PPS event on source %d at at %llu.%06u\n", ++ pps->id, ts.sec, ts.nsec); ++ ++ spin_lock_irqsave(&pps->lock, flags); ++ ++ /* Must call the echo function? */ ++ if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))) ++ pps->info.echo(source, event, data); ++ ++ /* Check the event */ ++ pps->current_mode = pps->params.mode; ++ if (event & PPS_CAPTUREASSERT) { ++ /* We have to add an offset? */ ++ if (pps->params.mode & PPS_OFFSETASSERT) ++ pps_add_offset(&ts, &pps->params.assert_off_tu); ++ ++ /* Save the time stamp */ ++ pps->assert_tu = ts; ++ pps->assert_sequence++; ++ pr_debug("capture assert seq #%u for source %d\n", ++ pps->assert_sequence, source); ++ } ++ if (event & PPS_CAPTURECLEAR) { ++ /* We have to add an offset? */ ++ if (pps->params.mode & PPS_OFFSETCLEAR) ++ pps_add_offset(&ts, &pps->params.clear_off_tu); ++ ++ /* Save the time stamp */ ++ pps->clear_tu = ts; ++ pps->clear_sequence++; ++ pr_debug("capture clear seq #%u for source %d\n", ++ pps->clear_sequence, source); ++ } ++ ++ pps->go = ~0; ++ wake_up_interruptible(&pps->queue); ++ ++ kill_fasync(&pps->async_queue, SIGIO, POLL_IN); ++ ++ spin_unlock_irqrestore(&pps->lock, flags); ++ ++ /* Now we can release the PPS source for (possible) deregistration */ ++ spin_lock_irqsave(&idr_lock, flags); ++ atomic_dec(&pps->usage); ++ wake_up_all(&pps->usage_queue); ++ spin_unlock_irqrestore(&idr_lock, flags); ++} ++EXPORT_SYMBOL(pps_event); +--- /dev/null ++++ b/drivers/pps/pps.c +@@ -0,0 +1,332 @@ ++/* ++ * pps.c -- Main PPS support file ++ * ++ * ++ * Copyright (C) 2005-2007 Rodolfo Giometti ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Local variables ++ */ ++ ++static dev_t pps_devt; ++static struct class *pps_class; ++ ++/* ++ * Char device methods ++ */ ++ ++static unsigned int pps_cdev_poll(struct file *file, poll_table *wait) ++{ ++ struct pps_device *pps = file->private_data; ++ ++ poll_wait(file, &pps->queue, wait); ++ ++ return POLLIN | POLLRDNORM; ++} ++ ++static int pps_cdev_fasync(int fd, struct file *file, int on) ++{ ++ struct pps_device *pps = file->private_data; ++ return fasync_helper(fd, file, on, &pps->async_queue); ++} ++ ++static int pps_cdev_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct pps_device *pps = file->private_data; ++ struct pps_kparams params; ++ struct pps_fdata fdata; ++ unsigned long ticks; ++ void __user *uarg = (void __user *) arg; ++ int __user *iuarg = (int __user *) arg; ++ int err; ++ ++ switch (cmd) { ++ case PPS_CHECK: ++ ++ /* This does nothing but signal we are a PPS source... */ ++ ++ return 0; ++ ++ case PPS_GETPARAMS: ++ pr_debug("PPS_GETPARAMS: source %d\n", pps->id); ++ ++ /* Sanity checks */ ++ if (!uarg) ++ return -EINVAL; ++ ++ /* Return current parameters */ ++ err = copy_to_user(uarg, &pps->params, ++ sizeof(struct pps_kparams)); ++ if (err) ++ return -EFAULT; ++ ++ break; ++ ++ case PPS_SETPARAMS: ++ pr_debug("PPS_SETPARAMS: source %d\n", pps->id); ++ ++ /* Check the capabilities */ ++ if (!capable(CAP_SYS_TIME)) ++ return -EPERM; ++ ++ /* Sanity checks */ ++ if (!uarg) ++ return -EINVAL; ++ err = copy_from_user(¶ms, uarg, sizeof(struct pps_kparams)); ++ if (err) ++ return -EFAULT; ++ if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { ++ pr_debug("capture mode unspecified (%x)\n", ++ params.mode); ++ return -EINVAL; ++ } ++ ++ /* Check for supported capabilities */ ++ if ((params.mode & ~pps->info.mode) != 0) { ++ pr_debug("unsupported capabilities (%x)\n", ++ params.mode); ++ return -EINVAL; ++ } ++ ++ spin_lock_irq(&pps->lock); ++ ++ /* Save the new parameters */ ++ pps->params = params; ++ ++ /* Restore the read only parameters */ ++ if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { ++ /* section 3.3 of RFC 2783 interpreted */ ++ pr_debug("time format unspecified (%x)\n", ++ params.mode); ++ pps->params.mode |= PPS_TSFMT_TSPEC; ++ } ++ if (pps->info.mode & PPS_CANWAIT) ++ pps->params.mode |= PPS_CANWAIT; ++ pps->params.api_version = PPS_API_VERS; ++ ++ spin_unlock_irq(&pps->lock); ++ ++ break; ++ ++ case PPS_GETCAP: ++ pr_debug("PPS_GETCAP: source %d\n", pps->id); ++ ++ /* Sanity checks */ ++ if (!uarg) ++ return -EINVAL; ++ ++ err = put_user(pps->info.mode, iuarg); ++ if (err) ++ return -EFAULT; ++ ++ break; ++ ++ case PPS_FETCH: ++ pr_debug("PPS_FETCH: source %d\n", pps->id); ++ ++ if (!uarg) ++ return -EINVAL; ++ err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); ++ if (err) ++ return -EFAULT; ++ ++ pps->go = 0; ++ ++ /* Manage the timeout */ ++ if (fdata.timeout.flags & PPS_TIME_INVALID) ++ err = wait_event_interruptible(pps->queue, pps->go); ++ else { ++ pr_debug("timeout %lld.%09d\n", ++ fdata.timeout.sec, fdata.timeout.nsec); ++ ticks = fdata.timeout.sec * HZ; ++ ticks += fdata.timeout.nsec / (NSEC_PER_SEC / HZ); ++ ++ if (ticks != 0) { ++ err = wait_event_interruptible_timeout( ++ pps->queue, pps->go, ticks); ++ if (err == 0) ++ return -ETIMEDOUT; ++ } ++ } ++ ++ /* Check for pending signals */ ++ if (err == -ERESTARTSYS) { ++ pr_debug("pending signal caught\n"); ++ return -EINTR; ++ } ++ ++ /* Return the fetched timestamp */ ++ spin_lock_irq(&pps->lock); ++ ++ fdata.info.assert_sequence = pps->assert_sequence; ++ fdata.info.clear_sequence = pps->clear_sequence; ++ fdata.info.assert_tu = pps->assert_tu; ++ fdata.info.clear_tu = pps->clear_tu; ++ fdata.info.current_mode = pps->current_mode; ++ ++ spin_unlock_irq(&pps->lock); ++ ++ err = copy_to_user(uarg, &fdata, sizeof(struct pps_fdata)); ++ if (err) ++ return -EFAULT; ++ ++ break; ++ ++ default: ++ return -ENOTTY; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int pps_cdev_open(struct inode *inode, struct file *file) ++{ ++ struct pps_device *pps = container_of(inode->i_cdev, ++ struct pps_device, cdev); ++ ++ /* Lock the PPS source against (possible) deregistration */ ++ atomic_inc(&pps->usage); ++ ++ file->private_data = pps; ++ ++ return 0; ++} ++ ++static int pps_cdev_release(struct inode *inode, struct file *file) ++{ ++ struct pps_device *pps = file->private_data; ++ ++ /* Free the PPS source and wake up (possible) deregistration */ ++ atomic_dec(&pps->usage); ++ wake_up_all(&pps->usage_queue); ++ ++ return 0; ++} ++ ++/* ++ * Char device stuff ++ */ ++ ++static const struct file_operations pps_cdev_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .poll = pps_cdev_poll, ++ .fasync = pps_cdev_fasync, ++ .ioctl = pps_cdev_ioctl, ++ .open = pps_cdev_open, ++ .release = pps_cdev_release, ++}; ++ ++int pps_register_cdev(struct pps_device *pps) ++{ ++ int err; ++ ++ pps->devno = MKDEV(MAJOR(pps_devt), pps->id); ++ cdev_init(&pps->cdev, &pps_cdev_fops); ++ pps->cdev.owner = pps->info.owner; ++ ++ err = cdev_add(&pps->cdev, pps->devno, 1); ++ if (err) { ++ printk(KERN_ERR "pps: %s: failed to add char device %d:%d\n", ++ pps->info.name, MAJOR(pps_devt), pps->id); ++ return err; ++ } ++ pps->dev = device_create(pps_class, pps->info.dev, pps->devno, ++ "pps%d", pps->id); ++ if (err) ++ goto del_cdev; ++ dev_set_drvdata(pps->dev, pps); ++ ++ pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, ++ MAJOR(pps_devt), pps->id); ++ ++ return 0; ++ ++del_cdev: ++ cdev_del(&pps->cdev); ++ ++ return err; ++} ++ ++void pps_unregister_cdev(struct pps_device *pps) ++{ ++ device_destroy(pps_class, pps->devno); ++ cdev_del(&pps->cdev); ++} ++ ++/* ++ * Module staff ++ */ ++ ++static void __exit pps_exit(void) ++{ ++ class_destroy(pps_class); ++ ++ if (pps_devt) ++ unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES); ++ ++ pr_info("LinuxPPS API ver. %d removed\n", PPS_API_VERS); ++} ++ ++static int __init pps_init(void) ++{ ++ int err; ++ ++ pps_class = class_create(THIS_MODULE, "pps"); ++ if (!pps_class) { ++ printk(KERN_ERR "pps: ailed to allocate class\n"); ++ return -ENOMEM; ++ } ++ ++ err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); ++ if (err < 0) { ++ printk(KERN_ERR "pps: failed to allocate char device region\n"); ++ goto remove_class; ++ } ++ ++ pr_info("LinuxPPS API ver. %d registered\n", PPS_API_VERS); ++ pr_info("Software ver. %s - Copyright 2005-2007 Rodolfo Giometti " ++ "\n", PPS_VERSION); ++ ++ return 0; ++ ++remove_class: ++ class_destroy(pps_class); ++ ++ return err; ++} ++ ++subsys_initcall(pps_init); ++module_exit(pps_exit); ++ ++MODULE_AUTHOR("Rodolfo Giometti "); ++MODULE_DESCRIPTION("LinuxPPS support (RFC 2783) - ver. " PPS_VERSION); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/pps/sysfs.c +@@ -0,0 +1,124 @@ ++/* ++ * sysfs.c -- sysfs support ++ * ++ * ++ * Copyright (C) 2007 Rodolfo Giometti ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * Attribute functions ++ */ ++ ++static ssize_t pps_show_assert(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pps_device *pps = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%lld.%09d#%d\n", ++ pps->assert_tu.sec, pps->assert_tu.nsec, ++ pps->assert_sequence); ++} ++DEVICE_ATTR(assert, S_IRUGO, pps_show_assert, NULL); ++ ++static ssize_t pps_show_clear(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pps_device *pps = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%lld.%09d#%d\n", ++ pps->clear_tu.sec, pps->clear_tu.nsec, ++ pps->clear_sequence); ++} ++DEVICE_ATTR(clear, S_IRUGO, pps_show_clear, NULL); ++ ++static ssize_t pps_show_mode(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pps_device *pps = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%4x\n", pps->info.mode); ++} ++DEVICE_ATTR(mode, S_IRUGO, pps_show_mode, NULL); ++ ++static ssize_t pps_show_echo(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pps_device *pps = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%d\n", !!pps->info.echo); ++} ++DEVICE_ATTR(echo, S_IRUGO, pps_show_echo, NULL); ++ ++static ssize_t pps_show_name(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pps_device *pps = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", pps->info.name); ++} ++DEVICE_ATTR(name, S_IRUGO, pps_show_name, NULL); ++ ++static ssize_t pps_show_path(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pps_device *pps = dev_get_drvdata(dev); ++ ++ return sprintf(buf, "%s\n", pps->info.path); ++} ++DEVICE_ATTR(path, S_IRUGO, pps_show_path, NULL); ++ ++/* ++ * Public functions ++ */ ++ ++void pps_sysfs_remove_source_entry(struct pps_device *pps) ++{ ++ /* Delete info files */ ++ if (pps->info.mode & PPS_CAPTUREASSERT) ++ device_remove_file(pps->dev, &dev_attr_assert); ++ ++ if (pps->info.mode & PPS_CAPTURECLEAR) ++ device_remove_file(pps->dev, &dev_attr_clear); ++ ++ device_remove_file(pps->dev, &dev_attr_mode); ++ device_remove_file(pps->dev, &dev_attr_echo); ++ device_remove_file(pps->dev, &dev_attr_name); ++ device_remove_file(pps->dev, &dev_attr_path); ++} ++ ++int pps_sysfs_create_source_entry(struct pps_device *pps) ++{ ++ /* Create file "assert" and "clear" according to source capability */ ++ if (pps->info.mode & PPS_CAPTUREASSERT) ++ device_create_file(pps->dev, &dev_attr_assert); ++ ++ if (pps->info.mode & PPS_CAPTURECLEAR) ++ device_create_file(pps->dev, &dev_attr_clear); ++ ++ device_create_file(pps->dev, &dev_attr_mode); ++ device_create_file(pps->dev, &dev_attr_echo); ++ device_create_file(pps->dev, &dev_attr_name); ++ device_create_file(pps->dev, &dev_attr_path); ++ ++ return 0; ++} +--- a/drivers/serial/8250.c ++++ b/drivers/serial/8250.c +@@ -2118,6 +2118,8 @@ + up->ier |= UART_IER_MSI; + if (up->capabilities & UART_CAP_UUE) + up->ier |= UART_IER_UUE | UART_IER_RTOIE; ++ if (up->port.flags & UPF_HARDPPS_CD) ++ up->ier |= UART_IER_MSI; /* enable interrupts */ + + serial_out(up, UART_IER, up->ier); + +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -33,6 +33,7 @@ + #include /* for serial_state and serial_icounter_struct */ + #include + #include ++#include + + #include + #include +@@ -633,6 +634,54 @@ + return 0; + } + ++#ifdef CONFIG_PPS_CLIENT_UART ++ ++static int ++uart_register_pps_port(struct uart_state *state, struct uart_port *port) ++{ ++ struct tty_driver *drv = port->info->tty->driver; ++ int ret; ++ ++ state->pps_info.owner = THIS_MODULE; ++ state->pps_info.dev = port->dev; ++ snprintf(state->pps_info.name, PPS_MAX_NAME_LEN, "%s%d", ++ drv->driver_name, port->line); ++ snprintf(state->pps_info.path, PPS_MAX_NAME_LEN, "/dev/%s%d", ++ drv->name, port->line); ++ ++ state->pps_info.mode = PPS_CAPTUREBOTH | \ ++ PPS_OFFSETASSERT | PPS_OFFSETCLEAR | \ ++ PPS_CANWAIT | PPS_TSFMT_TSPEC; ++ ++ ret = pps_register_source(&state->pps_info, PPS_CAPTUREBOTH | \ ++ PPS_OFFSETASSERT | PPS_OFFSETCLEAR); ++ if (ret < 0) { ++ dev_err(port->dev, "cannot register PPS source \"%s\"\n", ++ state->pps_info.path); ++ return ret; ++ } ++ port->pps_source = ret; ++ dev_dbg(port->dev, "PPS source #%d \"%s\" added\n", ++ port->pps_source, state->pps_info.path); ++ ++ return 0; ++} ++ ++static void ++uart_unregister_pps_port(struct uart_state *state, struct uart_port *port) ++{ ++ pps_unregister_source(port->pps_source); ++ dev_dbg(port->dev, "PPS source #%d \"%s\" removed\n", ++ port->pps_source, state->pps_info.path); ++} ++ ++#else ++ ++#define uart_register_pps_port(state, port) do { } while (0) ++#define uart_unregister_pps_port(state, port) do { } while (0) ++ ++#endif /* CONFIG_PPS_CLIENT_UART */ ++ + static int uart_set_info(struct uart_state *state, + struct serial_struct __user *newinfo) + { +@@ -807,11 +856,19 @@ + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + + check_and_exit: ++ /* PPS support enabled/disabled? */ ++ if ((old_flags & UPF_HARDPPS_CD) != (new_flags & UPF_HARDPPS_CD)) { ++ if (new_flags & UPF_HARDPPS_CD) ++ uart_register_pps_port(state, port); ++ else ++ uart_unregister_pps_port(state, port); ++ } ++ + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (state->info->flags & UIF_INITIALIZED) { +- if (((old_flags ^ port->flags) & UPF_SPD_MASK) || ++ if (((old_flags ^ port->flags) & (UPF_SPD_MASK|UPF_HARDPPS_CD)) || + old_custom_divisor != port->custom_divisor) { + /* + * If they're setting up a custom divisor or speed, +@@ -2110,6 +2167,12 @@ + port->ops->config_port(port, flags); + } + ++ /* ++ * Add the PPS support for the current port. ++ */ ++ if (port->flags & UPF_HARDPPS_CD) ++ uart_register_pps_port(state, port); ++ + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + +@@ -2359,6 +2422,12 @@ + mutex_unlock(&state->mutex); + + /* ++ * Remove PPS support from the current port. ++ */ ++ if (port->flags & UPF_HARDPPS_CD) ++ uart_unregister_pps_port(state, port); ++ ++ /* + * Remove the devices from the tty layer + */ + tty_unregister_device(drv->tty_driver, port->line); +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -295,6 +295,7 @@ + unifdef-y += poll.h + unifdef-y += ppp_defs.h + unifdef-y += ppp-comp.h ++unifdef-y += pps.h + unifdef-y += ptrace.h + unifdef-y += qnx4_fs.h + unifdef-y += quota.h +--- a/include/linux/parport.h ++++ b/include/linux/parport.h +@@ -100,6 +100,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -327,6 +328,11 @@ + + struct list_head full_list; + struct parport *slaves[3]; ++ ++#ifdef CONFIG_PPS_CLIENT_LP ++ struct pps_source_info pps_info; ++ int pps_source; /* PPS source ID */ ++#endif + }; + + #define DEFAULT_SPIN_TIME 500 /* us */ +@@ -517,6 +523,12 @@ + /* Lowlevel drivers _can_ call this support function to handle irqs. */ + static __inline__ void parport_generic_irq(int irq, struct parport *port) + { ++#ifdef CONFIG_PPS_CLIENT_LP ++ pps_event(port->pps_source, PPS_CAPTUREASSERT, port); ++ dev_dbg(port->dev, "PPS assert at %lu on source #%d\n", ++ jiffies, port->pps_source); ++#endif ++ + parport_ieee1284_interrupt (irq, port); + read_lock(&port->cad_lock); + if (port->cad && port->cad->irq_func) +--- /dev/null ++++ b/include/linux/pps.h +@@ -0,0 +1,196 @@ ++/* ++ * pps.h -- PPS API kernel header. ++ * ++ * ++ * Copyright (C) 2005-2007 Rodolfo Giometti ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#ifndef _PPS_H_ ++#define _PPS_H_ ++ ++/* Implementation note: the logical states ``assert'' and ``clear'' ++ * are implemented in terms of the chip register, i.e. ``assert'' ++ * means the bit is set. */ ++ ++/* ++ * 3.2 New data structures ++ */ ++ ++#ifndef __KERNEL__ ++#include ++#include ++#include ++#else ++#include ++#endif ++ ++#define PPS_API_VERS 1 ++#define PPS_MAX_NAME_LEN 32 ++ ++/* 32-bit vs. 64-bit compatibility. ++ * ++ * 0n i386, the alignment of a uint64_t is only 4 bytes, while on most other ++ * architectures it's 8 bytes. On i386, there will be no padding between the ++ * two consecutive 'struct pps_ktime' members of struct pps_kinfo and struct ++ * pps_kparams. But on most platforms there will be padding to ensure correct ++ * alignment. ++ * ++ * The simple fix is probably to add an explicit padding. ++ * [David Woodhouse] ++ */ ++struct pps_ktime { ++ __u64 sec; ++ __u32 nsec; ++ __u32 flags; ++}; ++#define PPS_TIME_INVALID (1<<0) /* used to specify timeout==NULL */ ++ ++struct pps_kinfo { ++ __u32 assert_sequence; /* seq. num. of assert event */ ++ __u32 clear_sequence; /* seq. num. of clear event */ ++ struct pps_ktime assert_tu; /* time of assert event */ ++ struct pps_ktime clear_tu; /* time of clear event */ ++ int current_mode; /* current mode bits */ ++}; ++ ++struct pps_kparams { ++ int api_version; /* API version # */ ++ int mode; /* mode bits */ ++ struct pps_ktime assert_off_tu; /* offset compensation for assert */ ++ struct pps_ktime clear_off_tu; /* offset compensation for clear */ ++}; ++ ++/* ++ * 3.3 Mode bit definitions ++ */ ++ ++/* Device/implementation parameters */ ++#define PPS_CAPTUREASSERT 0x01 /* capture assert events */ ++#define PPS_CAPTURECLEAR 0x02 /* capture clear events */ ++#define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ ++ ++#define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ ++#define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ ++ ++#define PPS_CANWAIT 0x100 /* can we wait for an event? */ ++#define PPS_CANPOLL 0x200 /* bit reserved for future use */ ++ ++/* Kernel actions */ ++#define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ ++#define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ ++ ++/* Timestamp formats */ ++#define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ ++#define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ ++ ++/* ++ * 3.4.4 New functions: disciplining the kernel timebase ++ */ ++ ++/* Kernel consumers */ ++#define PPS_KC_HARDPPS 0 /* hardpps() (or equivalent) */ ++#define PPS_KC_HARDPPS_PLL 1 /* hardpps() constrained to ++ use a phase-locked loop */ ++#define PPS_KC_HARDPPS_FLL 2 /* hardpps() constrained to ++ use a frequency-locked loop */ ++/* ++ * Here begins the implementation-specific part! ++ */ ++ ++struct pps_fdata { ++ struct pps_kinfo info; ++ struct pps_ktime timeout; ++}; ++ ++#include ++ ++#define PPS_CHECK _IO('P', 0) ++#define PPS_GETPARAMS _IOR('P', 1, struct pps_kparams *) ++#define PPS_SETPARAMS _IOW('P', 2, struct pps_kparams *) ++#define PPS_GETCAP _IOR('P', 3, int *) ++#define PPS_FETCH _IOWR('P', 4, struct pps_fdata *) ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++#define PPS_VERSION "5.0.0-rc2" ++#define PPS_MAX_SOURCES 16 /* should be enought... */ ++ ++/* ++ * Global defines ++ */ ++ ++/* The specific PPS source info */ ++struct pps_source_info { ++ char name[PPS_MAX_NAME_LEN]; /* simbolic name */ ++ char path[PPS_MAX_NAME_LEN]; /* path of connected device */ ++ int mode; /* PPS's allowed mode */ ++ ++ void (*echo)(int source, int event, void *data); /* PPS echo function */ ++ ++ struct module *owner; ++ struct device *dev; ++}; ++ ++/* The main struct */ ++struct pps_device { ++ struct pps_source_info info; /* PSS source info */ ++ ++ struct pps_kparams params; /* PPS's current params */ ++ ++ __u32 assert_sequence; /* PPS' assert event seq # */ ++ __u32 clear_sequence; /* PPS' clear event seq # */ ++ struct pps_ktime assert_tu; ++ struct pps_ktime clear_tu; ++ int current_mode; /* PPS mode at event time */ ++ ++ int go; /* PPS event is arrived? */ ++ wait_queue_head_t queue; /* PPS event queue */ ++ ++ unsigned int id; /* PPS source unique ID */ ++ struct cdev cdev; ++ struct device *dev; ++ int devno; ++ struct fasync_struct *async_queue; /* fasync method */ ++ spinlock_t lock; ++ ++ atomic_t usage; /* usage count */ ++ wait_queue_head_t usage_queue; ++ ++ struct class_device class_dev; ++}; ++ ++/* ++ * Exported functions ++ */ ++ ++extern int pps_register_source(struct pps_source_info *info, ++ int default_params); ++extern void pps_unregister_source(int source); ++extern int pps_register_cdev(struct pps_device *pps); ++extern void pps_unregister_cdev(struct pps_device *pps); ++extern void pps_event(int source, int event, void *data); ++ ++extern int pps_sysfs_create_source_entry(struct pps_device *pps); ++extern void pps_sysfs_remove_source_entry(struct pps_device *pps); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _PPS_H_ */ +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -157,6 +157,7 @@ + #include + #include + #include ++#include + + struct uart_port; + struct uart_info; +@@ -236,6 +237,9 @@ + unsigned char regshift; /* reg offset shift */ + unsigned char iotype; /* io access style */ + unsigned char unused1; ++#ifdef CONFIG_PPS_CLIENT_UART ++ int pps_source; /* PPS source ID */ ++#endif + + #define UPIO_PORT (0) + #define UPIO_HUB6 (1) +@@ -280,7 +284,8 @@ + #define UPF_IOREMAP ((__force upf_t) (1 << 31)) + + #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) +-#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) ++#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY\ ++ |UPF_HARDPPS_CD)) + + unsigned int mctrl; /* current modem ctrl settings */ + unsigned int timeout; /* character-based timeout */ +@@ -312,6 +317,10 @@ + struct uart_info *info; + struct uart_port *port; + ++#ifdef CONFIG_PPS_CLIENT_UART ++ struct pps_source_info pps_info; ++#endif ++ + struct mutex mutex; + }; + +@@ -476,13 +485,22 @@ + { + struct uart_info *info = port->info; + +- port->icount.dcd++; +- +-#ifdef CONFIG_HARD_PPS +- if ((port->flags & UPF_HARDPPS_CD) && status) +- hardpps(); ++#ifdef CONFIG_PPS_CLIENT_UART ++ if (port->flags & UPF_HARDPPS_CD) { ++ if (status) { ++ pps_event(port->pps_source, PPS_CAPTUREASSERT, port); ++ dev_dbg(port->dev, "PPS assert at %lu on source #%d\n", ++ jiffies, port->pps_source); ++ } else { ++ pps_event(port->pps_source, PPS_CAPTURECLEAR, port); ++ dev_dbg(port->dev, "PPS clear at %lu on source #%d\n", ++ jiffies, port->pps_source); ++ } ++ } + #endif + ++ port->icount.dcd++; ++ + if (info->flags & UIF_CHECK_CD) { + if (status) + wake_up_interruptible(&info->open_wait); diff --git a/target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch b/target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch new file mode 100644 index 0000000000..528d608d96 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch @@ -0,0 +1,7563 @@ +This is a port of the MMC-SPI driver from 2.6.24.3 +--mb + + +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -100,3 +100,16 @@ + To compile this driver as a module, choose M here: the + module will be called tifm_sd. + ++config MMC_SPI ++ tristate "MMC/SD over SPI (EXPERIMENTAL)" ++ depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL ++ select CRC7 ++ select CRC_ITU_T ++ help ++ Some systems accss MMC/SD cards using a SPI controller instead of ++ using a "native" MMC/SD controller. This has a disadvantage of ++ being relatively high overhead, but a compensating advantage of ++ working on many systems without dedicated MMC/SD controllers. ++ ++ If unsure, or if your system has no SPI master driver, say N. ++ +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -15,4 +15,5 @@ + obj-$(CONFIG_MMC_OMAP) += omap.o + obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o ++obj-$(CONFIG_MMC_SPI) += mmc_spi.o + +--- /dev/null ++++ b/drivers/mmc/host/mmc_spi.c +@@ -0,0 +1,1419 @@ ++/* ++ * mmc_spi.c - Access SD/MMC cards through SPI master controllers ++ * ++ * (C) Copyright 2005, Intec Automation, ++ * Mike Lavender (mike@steroidmicros) ++ * (C) Copyright 2006-2007, David Brownell ++ * (C) Copyright 2007, Axis Communications, ++ * Hans-Peter Nilsson (hp@axis.com) ++ * (C) Copyright 2007, ATRON electronic GmbH, ++ * Jan Nikitenko ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include /* for R1_SPI_* bit values */ ++ ++#include ++#include ++ ++#include ++ ++ ++#define sg_page(sg) (sg)->page ++ ++ ++/* NOTES: ++ * ++ * - For now, we won't try to interoperate with a real mmc/sd/sdio ++ * controller, although some of them do have hardware support for ++ * SPI protocol. The main reason for such configs would be mmc-ish ++ * cards like DataFlash, which don't support that "native" protocol. ++ * ++ * We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to ++ * switch between driver stacks, and in any case if "native" mode ++ * is available, it will be faster and hence preferable. ++ * ++ * - MMC depends on a different chipselect management policy than the ++ * SPI interface currently supports for shared bus segments: it needs ++ * to issue multiple spi_message requests with the chipselect active, ++ * using the results of one message to decide the next one to issue. ++ * ++ * Pending updates to the programming interface, this driver expects ++ * that it not share the bus with other drivers (precluding conflicts). ++ * ++ * - We tell the controller to keep the chipselect active from the ++ * beginning of an mmc_host_ops.request until the end. So beware ++ * of SPI controller drivers that mis-handle the cs_change flag! ++ * ++ * However, many cards seem OK with chipselect flapping up/down ++ * during that time ... at least on unshared bus segments. ++ */ ++ ++ ++/* ++ * Local protocol constants, internal to data block protocols. ++ */ ++ ++/* Response tokens used to ack each block written: */ ++#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) ++#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) ++#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) ++#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) ++ ++/* Read and write blocks start with these tokens and end with crc; ++ * on error, read tokens act like a subset of R2_SPI_* values. ++ */ ++#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ ++#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ ++#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ ++ ++#define MMC_SPI_BLOCKSIZE 512 ++ ++ ++/* These fixed timeouts come from the latest SD specs, which say to ignore ++ * the CSD values. The R1B value is for card erase (e.g. the "I forgot the ++ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after ++ * reads which takes nowhere near that long. Older cards may be able to use ++ * shorter timeouts ... but why bother? ++ */ ++#define readblock_timeout ktime_set(0, 100 * 1000 * 1000) ++#define writeblock_timeout ktime_set(0, 250 * 1000 * 1000) ++#define r1b_timeout ktime_set(3, 0) ++ ++ ++/****************************************************************************/ ++ ++/* ++ * Local Data Structures ++ */ ++ ++/* "scratch" is per-{command,block} data exchanged with the card */ ++struct scratch { ++ u8 status[29]; ++ u8 data_token; ++ __be16 crc_val; ++}; ++ ++struct mmc_spi_host { ++ struct mmc_host *mmc; ++ struct spi_device *spi; ++ ++ unsigned char power_mode; ++ u16 powerup_msecs; ++ ++ struct mmc_spi_platform_data *pdata; ++ ++ /* for bulk data transfers */ ++ struct spi_transfer token, t, crc, early_status; ++ struct spi_message m; ++ ++ /* for status readback */ ++ struct spi_transfer status; ++ struct spi_message readback; ++ ++ /* underlying DMA-aware controller, or null */ ++ struct device *dma_dev; ++ ++ /* buffer used for commands and for message "overhead" */ ++ struct scratch *data; ++ dma_addr_t data_dma; ++ ++ /* Specs say to write ones most of the time, even when the card ++ * has no need to read its input data; and many cards won't care. ++ * This is our source of those ones. ++ */ ++ void *ones; ++ dma_addr_t ones_dma; ++}; ++ ++ ++/****************************************************************************/ ++ ++/* ++ * MMC-over-SPI protocol glue, used by the MMC stack interface ++ */ ++ ++static inline int mmc_cs_off(struct mmc_spi_host *host) ++{ ++ /* chipselect will always be inactive after setup() */ ++ return spi_setup(host->spi); ++} ++ ++static int ++mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len) ++{ ++ int status; ++ ++ if (len > sizeof(*host->data)) { ++ WARN_ON(1); ++ return -EIO; ++ } ++ ++ host->status.len = len; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_FROM_DEVICE); ++ ++ status = spi_sync(host->spi, &host->readback); ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_FROM_DEVICE); ++ ++ return status; ++} ++ ++static int ++mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte) ++{ ++ u8 *cp = host->data->status; ++ ++ timeout = ktime_add(timeout, ktime_get()); ++ ++ while (1) { ++ int status; ++ unsigned i; ++ ++ status = mmc_spi_readbytes(host, n); ++ if (status < 0) ++ return status; ++ ++ for (i = 0; i < n; i++) { ++ if (cp[i] != byte) ++ return cp[i]; ++ } ++ ++ /* REVISIT investigate msleep() to avoid busy-wait I/O ++ * in at least some cases. ++ */ ++ if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0) ++ break; ++ } ++ return -ETIMEDOUT; ++} ++ ++static inline int ++mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout) ++{ ++ return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0); ++} ++ ++static int mmc_spi_readtoken(struct mmc_spi_host *host) ++{ ++ return mmc_spi_skip(host, readblock_timeout, 1, 0xff); ++} ++ ++ ++/* ++ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol ++ * hosts return! The low byte holds R1_SPI bits. The next byte may hold ++ * R2_SPI bits ... for SEND_STATUS, or after data read errors. ++ * ++ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on ++ * newer cards R7 (IF_COND). ++ */ ++ ++static char *maptype(struct mmc_command *cmd) ++{ ++ switch (mmc_spi_resp_type(cmd)) { ++ case MMC_RSP_SPI_R1: return "R1"; ++ case MMC_RSP_SPI_R1B: return "R1B"; ++ case MMC_RSP_SPI_R2: return "R2/R5"; ++ case MMC_RSP_SPI_R3: return "R3/R4/R7"; ++ default: return "?"; ++ } ++} ++ ++/* return zero, else negative errno after setting cmd->error */ ++static int mmc_spi_response_get(struct mmc_spi_host *host, ++ struct mmc_command *cmd, int cs_on) ++{ ++ u8 *cp = host->data->status; ++ u8 *end = cp + host->t.len; ++ int value = 0; ++ char tag[32]; ++ ++ snprintf(tag, sizeof(tag), " ... CMD%d response SPI_%s", ++ cmd->opcode, maptype(cmd)); ++ ++ /* Except for data block reads, the whole response will already ++ * be stored in the scratch buffer. It's somewhere after the ++ * command and the first byte we read after it. We ignore that ++ * first byte. After STOP_TRANSMISSION command it may include ++ * two data bits, but otherwise it's all ones. ++ */ ++ cp += 8; ++ while (cp < end && *cp == 0xff) ++ cp++; ++ ++ /* Data block reads (R1 response types) may need more data... */ ++ if (cp == end) { ++ unsigned i; ++ ++ cp = host->data->status; ++ ++ /* Card sends N(CR) (== 1..8) bytes of all-ones then one ++ * status byte ... and we already scanned 2 bytes. ++ * ++ * REVISIT block read paths use nasty byte-at-a-time I/O ++ * so it can always DMA directly into the target buffer. ++ * It'd probably be better to memcpy() the first chunk and ++ * avoid extra i/o calls... ++ */ ++ for (i = 2; i < 9; i++) { ++ value = mmc_spi_readbytes(host, 1); ++ if (value < 0) ++ goto done; ++ if (*cp != 0xff) ++ goto checkstatus; ++ } ++ value = -ETIMEDOUT; ++ goto done; ++ } ++ ++checkstatus: ++ if (*cp & 0x80) { ++ dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n", ++ tag, *cp); ++ value = -EBADR; ++ goto done; ++ } ++ ++ cmd->resp[0] = *cp++; ++ cmd->error = 0; ++ ++ /* Status byte: the entire seven-bit R1 response. */ ++ if (cmd->resp[0] != 0) { ++ if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS ++ | R1_SPI_ILLEGAL_COMMAND) ++ & cmd->resp[0]) ++ value = -EINVAL; ++ else if (R1_SPI_COM_CRC & cmd->resp[0]) ++ value = -EILSEQ; ++ else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET) ++ & cmd->resp[0]) ++ value = -EIO; ++ /* else R1_SPI_IDLE, "it's resetting" */ ++ } ++ ++ switch (mmc_spi_resp_type(cmd)) { ++ ++ /* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads) ++ * and less-common stuff like various erase operations. ++ */ ++ case MMC_RSP_SPI_R1B: ++ /* maybe we read all the busy tokens already */ ++ while (cp < end && *cp == 0) ++ cp++; ++ if (cp == end) ++ mmc_spi_wait_unbusy(host, r1b_timeout); ++ break; ++ ++ /* SPI R2 == R1 + second status byte; SEND_STATUS ++ * SPI R5 == R1 + data byte; IO_RW_DIRECT ++ */ ++ case MMC_RSP_SPI_R2: ++ cmd->resp[0] |= *cp << 8; ++ break; ++ ++ /* SPI R3, R4, or R7 == R1 + 4 bytes */ ++ case MMC_RSP_SPI_R3: ++ cmd->resp[1] = be32_to_cpu(get_unaligned((u32 *)cp)); ++ break; ++ ++ /* SPI R1 == just one status byte */ ++ case MMC_RSP_SPI_R1: ++ break; ++ ++ default: ++ dev_dbg(&host->spi->dev, "bad response type %04x\n", ++ mmc_spi_resp_type(cmd)); ++ if (value >= 0) ++ value = -EINVAL; ++ goto done; ++ } ++ ++ if (value < 0) ++ dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n", ++ tag, cmd->resp[0], cmd->resp[1]); ++ ++ /* disable chipselect on errors and some success cases */ ++ if (value >= 0 && cs_on) ++ return value; ++done: ++ if (value < 0) ++ cmd->error = value; ++ mmc_cs_off(host); ++ return value; ++} ++ ++/* Issue command and read its response. ++ * Returns zero on success, negative for error. ++ * ++ * On error, caller must cope with mmc core retry mechanism. That ++ * means immediate low-level resubmit, which affects the bus lock... ++ */ ++static int ++mmc_spi_command_send(struct mmc_spi_host *host, ++ struct mmc_request *mrq, ++ struct mmc_command *cmd, int cs_on) ++{ ++ struct scratch *data = host->data; ++ u8 *cp = data->status; ++ u32 arg = cmd->arg; ++ int status; ++ struct spi_transfer *t; ++ ++ /* We can handle most commands (except block reads) in one full ++ * duplex I/O operation before either starting the next transfer ++ * (data block or command) or else deselecting the card. ++ * ++ * First, write 7 bytes: ++ * - an all-ones byte to ensure the card is ready ++ * - opcode byte (plus start and transmission bits) ++ * - four bytes of big-endian argument ++ * - crc7 (plus end bit) ... always computed, it's cheap ++ * ++ * We init the whole buffer to all-ones, which is what we need ++ * to write while we're reading (later) response data. ++ */ ++ memset(cp++, 0xff, sizeof(data->status)); ++ ++ *cp++ = 0x40 | cmd->opcode; ++ *cp++ = (u8)(arg >> 24); ++ *cp++ = (u8)(arg >> 16); ++ *cp++ = (u8)(arg >> 8); ++ *cp++ = (u8)arg; ++ *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01; ++ ++ /* Then, read up to 13 bytes (while writing all-ones): ++ * - N(CR) (== 1..8) bytes of all-ones ++ * - status byte (for all response types) ++ * - the rest of the response, either: ++ * + nothing, for R1 or R1B responses ++ * + second status byte, for R2 responses ++ * + four data bytes, for R3 and R7 responses ++ * ++ * Finally, read some more bytes ... in the nice cases we know in ++ * advance how many, and reading 1 more is always OK: ++ * - N(EC) (== 0..N) bytes of all-ones, before deselect/finish ++ * - N(RC) (== 1..N) bytes of all-ones, before next command ++ * - N(WR) (== 1..N) bytes of all-ones, before data write ++ * ++ * So in those cases one full duplex I/O of at most 21 bytes will ++ * handle the whole command, leaving the card ready to receive a ++ * data block or new command. We do that whenever we can, shaving ++ * CPU and IRQ costs (especially when using DMA or FIFOs). ++ * ++ * There are two other cases, where it's not generally practical ++ * to rely on a single I/O: ++ * ++ * - R1B responses need at least N(EC) bytes of all-zeroes. ++ * ++ * In this case we can *try* to fit it into one I/O, then ++ * maybe read more data later. ++ * ++ * - Data block reads are more troublesome, since a variable ++ * number of padding bytes precede the token and data. ++ * + N(CX) (== 0..8) bytes of all-ones, before CSD or CID ++ * + N(AC) (== 1..many) bytes of all-ones ++ * ++ * In this case we currently only have minimal speedups here: ++ * when N(CR) == 1 we can avoid I/O in response_get(). ++ */ ++ if (cs_on && (mrq->data->flags & MMC_DATA_READ)) { ++ cp += 2; /* min(N(CR)) + status */ ++ /* R1 */ ++ } else { ++ cp += 10; /* max(N(CR)) + status + min(N(RC),N(WR)) */ ++ if (cmd->flags & MMC_RSP_SPI_S2) /* R2/R5 */ ++ cp++; ++ else if (cmd->flags & MMC_RSP_SPI_B4) /* R3/R4/R7 */ ++ cp += 4; ++ else if (cmd->flags & MMC_RSP_BUSY) /* R1B */ ++ cp = data->status + sizeof(data->status); ++ /* else: R1 (most commands) */ ++ } ++ ++ dev_dbg(&host->spi->dev, " mmc_spi: CMD%d, resp %s\n", ++ cmd->opcode, maptype(cmd)); ++ ++ /* send command, leaving chipselect active */ ++ spi_message_init(&host->m); ++ ++ t = &host->t; ++ memset(t, 0, sizeof(*t)); ++ t->tx_buf = t->rx_buf = data->status; ++ t->tx_dma = t->rx_dma = host->data_dma; ++ t->len = cp - data->status; ++ t->cs_change = 1; ++ spi_message_add_tail(t, &host->m); ++ ++ if (host->dma_dev) { ++ host->m.is_dma_mapped = 1; ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_BIDIRECTIONAL); ++ } ++ status = spi_sync(host->spi, &host->m); ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_BIDIRECTIONAL); ++ if (status < 0) { ++ dev_dbg(&host->spi->dev, " ... write returned %d\n", status); ++ cmd->error = status; ++ return status; ++ } ++ ++ /* after no-data commands and STOP_TRANSMISSION, chipselect off */ ++ return mmc_spi_response_get(host, cmd, cs_on); ++} ++ ++/* Build data message with up to four separate transfers. For TX, we ++ * start by writing the data token. And in most cases, we finish with ++ * a status transfer. ++ * ++ * We always provide TX data for data and CRC. The MMC/SD protocol ++ * requires us to write ones; but Linux defaults to writing zeroes; ++ * so we explicitly initialize it to all ones on RX paths. ++ * ++ * We also handle DMA mapping, so the underlying SPI controller does ++ * not need to (re)do it for each message. ++ */ ++static void ++mmc_spi_setup_data_message( ++ struct mmc_spi_host *host, ++ int multiple, ++ enum dma_data_direction direction) ++{ ++ struct spi_transfer *t; ++ struct scratch *scratch = host->data; ++ dma_addr_t dma = host->data_dma; ++ ++ spi_message_init(&host->m); ++ if (dma) ++ host->m.is_dma_mapped = 1; ++ ++ /* for reads, readblock() skips 0xff bytes before finding ++ * the token; for writes, this transfer issues that token. ++ */ ++ if (direction == DMA_TO_DEVICE) { ++ t = &host->token; ++ memset(t, 0, sizeof(*t)); ++ t->len = 1; ++ if (multiple) ++ scratch->data_token = SPI_TOKEN_MULTI_WRITE; ++ else ++ scratch->data_token = SPI_TOKEN_SINGLE; ++ t->tx_buf = &scratch->data_token; ++ if (dma) ++ t->tx_dma = dma + offsetof(struct scratch, data_token); ++ spi_message_add_tail(t, &host->m); ++ } ++ ++ /* Body of transfer is buffer, then CRC ... ++ * either TX-only, or RX with TX-ones. ++ */ ++ t = &host->t; ++ memset(t, 0, sizeof(*t)); ++ t->tx_buf = host->ones; ++ t->tx_dma = host->ones_dma; ++ /* length and actual buffer info are written later */ ++ spi_message_add_tail(t, &host->m); ++ ++ t = &host->crc; ++ memset(t, 0, sizeof(*t)); ++ t->len = 2; ++ if (direction == DMA_TO_DEVICE) { ++ /* the actual CRC may get written later */ ++ t->tx_buf = &scratch->crc_val; ++ if (dma) ++ t->tx_dma = dma + offsetof(struct scratch, crc_val); ++ } else { ++ t->tx_buf = host->ones; ++ t->tx_dma = host->ones_dma; ++ t->rx_buf = &scratch->crc_val; ++ if (dma) ++ t->rx_dma = dma + offsetof(struct scratch, crc_val); ++ } ++ spi_message_add_tail(t, &host->m); ++ ++ /* ++ * A single block read is followed by N(EC) [0+] all-ones bytes ++ * before deselect ... don't bother. ++ * ++ * Multiblock reads are followed by N(AC) [1+] all-ones bytes before ++ * the next block is read, or a STOP_TRANSMISSION is issued. We'll ++ * collect that single byte, so readblock() doesn't need to. ++ * ++ * For a write, the one-byte data response follows immediately, then ++ * come zero or more busy bytes, then N(WR) [1+] all-ones bytes. ++ * Then single block reads may deselect, and multiblock ones issue ++ * the next token (next data block, or STOP_TRAN). We can try to ++ * minimize I/O ops by using a single read to collect end-of-busy. ++ */ ++ if (multiple || direction == DMA_TO_DEVICE) { ++ t = &host->early_status; ++ memset(t, 0, sizeof(*t)); ++ t->len = (direction == DMA_TO_DEVICE) ++ ? sizeof(scratch->status) ++ : 1; ++ t->tx_buf = host->ones; ++ t->tx_dma = host->ones_dma; ++ t->rx_buf = scratch->status; ++ if (dma) ++ t->rx_dma = dma + offsetof(struct scratch, status); ++ t->cs_change = 1; ++ spi_message_add_tail(t, &host->m); ++ } ++} ++ ++/* ++ * Write one block: ++ * - caller handled preceding N(WR) [1+] all-ones bytes ++ * - data block ++ * + token ++ * + data bytes ++ * + crc16 ++ * - an all-ones byte ... card writes a data-response byte ++ * - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy' ++ * ++ * Return negative errno, else success. ++ */ ++static int ++mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t) ++{ ++ struct spi_device *spi = host->spi; ++ int status, i; ++ struct scratch *scratch = host->data; ++ ++ if (host->mmc->use_spi_crc) ++ scratch->crc_val = cpu_to_be16( ++ crc_itu_t(0, t->tx_buf, t->len)); ++ if (host->dma_dev) ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ status = spi_sync(spi, &host->m); ++ ++ if (status != 0) { ++ dev_dbg(&spi->dev, "write error (%d)\n", status); ++ return status; ++ } ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ /* ++ * Get the transmission data-response reply. It must follow ++ * immediately after the data block we transferred. This reply ++ * doesn't necessarily tell whether the write operation succeeded; ++ * it just says if the transmission was ok and whether *earlier* ++ * writes succeeded; see the standard. ++ */ ++ switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) { ++ case SPI_RESPONSE_ACCEPTED: ++ status = 0; ++ break; ++ case SPI_RESPONSE_CRC_ERR: ++ /* host shall then issue MMC_STOP_TRANSMISSION */ ++ status = -EILSEQ; ++ break; ++ case SPI_RESPONSE_WRITE_ERR: ++ /* host shall then issue MMC_STOP_TRANSMISSION, ++ * and should MMC_SEND_STATUS to sort it out ++ */ ++ status = -EIO; ++ break; ++ default: ++ status = -EPROTO; ++ break; ++ } ++ if (status != 0) { ++ dev_dbg(&spi->dev, "write error %02x (%d)\n", ++ scratch->status[0], status); ++ return status; ++ } ++ ++ t->tx_buf += t->len; ++ if (host->dma_dev) ++ t->tx_dma += t->len; ++ ++ /* Return when not busy. If we didn't collect that status yet, ++ * we'll need some more I/O. ++ */ ++ for (i = 1; i < sizeof(scratch->status); i++) { ++ if (scratch->status[i] != 0) ++ return 0; ++ } ++ return mmc_spi_wait_unbusy(host, writeblock_timeout); ++} ++ ++/* ++ * Read one block: ++ * - skip leading all-ones bytes ... either ++ * + N(AC) [1..f(clock,CSD)] usually, else ++ * + N(CX) [0..8] when reading CSD or CID ++ * - data block ++ * + token ... if error token, no data or crc ++ * + data bytes ++ * + crc16 ++ * ++ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow ++ * before dropping chipselect. ++ * ++ * For multiblock reads, caller either reads the next block or issues a ++ * STOP_TRANSMISSION command. ++ */ ++static int ++mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t) ++{ ++ struct spi_device *spi = host->spi; ++ int status; ++ struct scratch *scratch = host->data; ++ ++ /* At least one SD card sends an all-zeroes byte when N(CX) ++ * applies, before the all-ones bytes ... just cope with that. ++ */ ++ status = mmc_spi_readbytes(host, 1); ++ if (status < 0) ++ return status; ++ status = scratch->status[0]; ++ if (status == 0xff || status == 0) ++ status = mmc_spi_readtoken(host); ++ ++ if (status == SPI_TOKEN_SINGLE) { ++ if (host->dma_dev) { ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ dma_sync_single_for_device(host->dma_dev, ++ t->rx_dma, t->len, ++ DMA_FROM_DEVICE); ++ } ++ ++ status = spi_sync(spi, &host->m); ++ ++ if (host->dma_dev) { ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ dma_sync_single_for_cpu(host->dma_dev, ++ t->rx_dma, t->len, ++ DMA_FROM_DEVICE); ++ } ++ ++ } else { ++ dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status); ++ ++ /* we've read extra garbage, timed out, etc */ ++ if (status < 0) ++ return status; ++ ++ /* low four bits are an R2 subset, fifth seems to be ++ * vendor specific ... map them all to generic error.. ++ */ ++ return -EIO; ++ } ++ ++ if (host->mmc->use_spi_crc) { ++ u16 crc = crc_itu_t(0, t->rx_buf, t->len); ++ ++ be16_to_cpus(&scratch->crc_val); ++ if (scratch->crc_val != crc) { ++ dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, " ++ "computed=0x%04x len=%d\n", ++ scratch->crc_val, crc, t->len); ++ return -EILSEQ; ++ } ++ } ++ ++ t->rx_buf += t->len; ++ if (host->dma_dev) ++ t->rx_dma += t->len; ++ ++ return 0; ++} ++ ++/* ++ * An MMC/SD data stage includes one or more blocks, optional CRCs, ++ * and inline handshaking. That handhaking makes it unlike most ++ * other SPI protocol stacks. ++ */ ++static void ++mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, ++ struct mmc_data *data, u32 blk_size) ++{ ++ struct spi_device *spi = host->spi; ++ struct device *dma_dev = host->dma_dev; ++ struct spi_transfer *t; ++ enum dma_data_direction direction; ++ struct scatterlist *sg; ++ unsigned n_sg; ++ int multiple = (data->blocks > 1); ++ ++ if (data->flags & MMC_DATA_READ) ++ direction = DMA_FROM_DEVICE; ++ else ++ direction = DMA_TO_DEVICE; ++ mmc_spi_setup_data_message(host, multiple, direction); ++ t = &host->t; ++ ++ /* Handle scatterlist segments one at a time, with synch for ++ * each 512-byte block ++ */ ++ for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) { ++ int status = 0; ++ dma_addr_t dma_addr = 0; ++ void *kmap_addr; ++ unsigned length = sg->length; ++ enum dma_data_direction dir = direction; ++ ++ /* set up dma mapping for controller drivers that might ++ * use DMA ... though they may fall back to PIO ++ */ ++ if (dma_dev) { ++ /* never invalidate whole *shared* pages ... */ ++ if ((sg->offset != 0 || length != PAGE_SIZE) ++ && dir == DMA_FROM_DEVICE) ++ dir = DMA_BIDIRECTIONAL; ++ ++ dma_addr = dma_map_page(dma_dev, sg_page(sg), 0, ++ PAGE_SIZE, dir); ++ if (direction == DMA_TO_DEVICE) ++ t->tx_dma = dma_addr + sg->offset; ++ else ++ t->rx_dma = dma_addr + sg->offset; ++ } ++ ++ /* allow pio too; we don't allow highmem */ ++ kmap_addr = kmap(sg_page(sg)); ++ if (direction == DMA_TO_DEVICE) ++ t->tx_buf = kmap_addr + sg->offset; ++ else ++ t->rx_buf = kmap_addr + sg->offset; ++ ++ /* transfer each block, and update request status */ ++ while (length) { ++ t->len = min(length, blk_size); ++ ++ dev_dbg(&host->spi->dev, ++ " mmc_spi: %s block, %d bytes\n", ++ (direction == DMA_TO_DEVICE) ++ ? "write" ++ : "read", ++ t->len); ++ ++ if (direction == DMA_TO_DEVICE) ++ status = mmc_spi_writeblock(host, t); ++ else ++ status = mmc_spi_readblock(host, t); ++ if (status < 0) ++ break; ++ ++ data->bytes_xfered += t->len; ++ length -= t->len; ++ ++ if (!multiple) ++ break; ++ } ++ ++ /* discard mappings */ ++ if (direction == DMA_FROM_DEVICE) ++ flush_kernel_dcache_page(sg_page(sg)); ++ kunmap(sg_page(sg)); ++ if (dma_dev) ++ dma_unmap_page(dma_dev, dma_addr, PAGE_SIZE, dir); ++ ++ if (status < 0) { ++ data->error = status; ++ dev_dbg(&spi->dev, "%s status %d\n", ++ (direction == DMA_TO_DEVICE) ++ ? "write" : "read", ++ status); ++ break; ++ } ++ } ++ ++ /* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that ++ * can be issued before multiblock writes. Unlike its more widely ++ * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23), ++ * that can affect the STOP_TRAN logic. Complete (and current) ++ * MMC specs should sort that out before Linux starts using CMD23. ++ */ ++ if (direction == DMA_TO_DEVICE && multiple) { ++ struct scratch *scratch = host->data; ++ int tmp; ++ const unsigned statlen = sizeof(scratch->status); ++ ++ dev_dbg(&spi->dev, " mmc_spi: STOP_TRAN\n"); ++ ++ /* Tweak the per-block message we set up earlier by morphing ++ * it to hold single buffer with the token followed by some ++ * all-ones bytes ... skip N(BR) (0..1), scan the rest for ++ * "not busy any longer" status, and leave chip selected. ++ */ ++ INIT_LIST_HEAD(&host->m.transfers); ++ list_add(&host->early_status.transfer_list, ++ &host->m.transfers); ++ ++ memset(scratch->status, 0xff, statlen); ++ scratch->status[0] = SPI_TOKEN_STOP_TRAN; ++ ++ host->early_status.tx_buf = host->early_status.rx_buf; ++ host->early_status.tx_dma = host->early_status.rx_dma; ++ host->early_status.len = statlen; ++ ++ if (host->dma_dev) ++ dma_sync_single_for_device(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ tmp = spi_sync(spi, &host->m); ++ ++ if (host->dma_dev) ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*scratch), ++ DMA_BIDIRECTIONAL); ++ ++ if (tmp < 0) { ++ if (!data->error) ++ data->error = tmp; ++ return; ++ } ++ ++ /* Ideally we collected "not busy" status with one I/O, ++ * avoiding wasteful byte-at-a-time scanning... but more ++ * I/O is often needed. ++ */ ++ for (tmp = 2; tmp < statlen; tmp++) { ++ if (scratch->status[tmp] != 0) ++ return; ++ } ++ tmp = mmc_spi_wait_unbusy(host, writeblock_timeout); ++ if (tmp < 0 && !data->error) ++ data->error = tmp; ++ } ++} ++ ++/****************************************************************************/ ++ ++/* ++ * MMC driver implementation -- the interface to the MMC stack ++ */ ++ ++static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ int status = -EINVAL; ++ ++#ifdef DEBUG ++ /* MMC core and layered drivers *MUST* issue SPI-aware commands */ ++ { ++ struct mmc_command *cmd; ++ int invalid = 0; ++ ++ cmd = mrq->cmd; ++ if (!mmc_spi_resp_type(cmd)) { ++ dev_dbg(&host->spi->dev, "bogus command\n"); ++ cmd->error = -EINVAL; ++ invalid = 1; ++ } ++ ++ cmd = mrq->stop; ++ if (cmd && !mmc_spi_resp_type(cmd)) { ++ dev_dbg(&host->spi->dev, "bogus STOP command\n"); ++ cmd->error = -EINVAL; ++ invalid = 1; ++ } ++ ++ if (invalid) { ++ dump_stack(); ++ mmc_request_done(host->mmc, mrq); ++ return; ++ } ++ } ++#endif ++ ++ /* issue command; then optionally data and stop */ ++ status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL); ++ if (status == 0 && mrq->data) { ++ mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz); ++ if (mrq->stop) ++ status = mmc_spi_command_send(host, mrq, mrq->stop, 0); ++ else ++ mmc_cs_off(host); ++ } ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0" ++ * ++ * NOTE that here we can't know that the card has just been powered up; ++ * not all MMC/SD sockets support power switching. ++ * ++ * FIXME when the card is still in SPI mode, e.g. from a previous kernel, ++ * this doesn't seem to do the right thing at all... ++ */ ++static void mmc_spi_initsequence(struct mmc_spi_host *host) ++{ ++ /* Try to be very sure any previous command has completed; ++ * wait till not-busy, skip debris from any old commands. ++ */ ++ mmc_spi_wait_unbusy(host, r1b_timeout); ++ mmc_spi_readbytes(host, 10); ++ ++ /* ++ * Do a burst with chipselect active-high. We need to do this to ++ * meet the requirement of 74 clock cycles with both chipselect ++ * and CMD (MOSI) high before CMD0 ... after the card has been ++ * powered up to Vdd(min), and so is ready to take commands. ++ * ++ * Some cards are particularly needy of this (e.g. Viking "SD256") ++ * while most others don't seem to care. ++ * ++ * Note that this is one of the places MMC/SD plays games with the ++ * SPI protocol. Another is that when chipselect is released while ++ * the card returns BUSY status, the clock must issue several cycles ++ * with chipselect high before the card will stop driving its output. ++ */ ++ host->spi->mode |= SPI_CS_HIGH; ++ if (spi_setup(host->spi) != 0) { ++ /* Just warn; most cards work without it. */ ++ dev_warn(&host->spi->dev, ++ "can't change chip-select polarity\n"); ++ host->spi->mode &= ~SPI_CS_HIGH; ++ } else { ++ mmc_spi_readbytes(host, 18); ++ ++ host->spi->mode &= ~SPI_CS_HIGH; ++ if (spi_setup(host->spi) != 0) { ++ /* Wot, we can't get the same setup we had before? */ ++ dev_err(&host->spi->dev, ++ "can't restore chip-select polarity\n"); ++ } ++ } ++} ++ ++static char *mmc_powerstring(u8 power_mode) ++{ ++ switch (power_mode) { ++ case MMC_POWER_OFF: return "off"; ++ case MMC_POWER_UP: return "up"; ++ case MMC_POWER_ON: return "on"; ++ } ++ return "?"; ++} ++ ++static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ ++ if (host->power_mode != ios->power_mode) { ++ int canpower; ++ ++ canpower = host->pdata && host->pdata->setpower; ++ ++ dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n", ++ mmc_powerstring(ios->power_mode), ++ ios->vdd, ++ canpower ? ", can switch" : ""); ++ ++ /* switch power on/off if possible, accounting for ++ * max 250msec powerup time if needed. ++ */ ++ if (canpower) { ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ case MMC_POWER_UP: ++ host->pdata->setpower(&host->spi->dev, ++ ios->vdd); ++ if (ios->power_mode == MMC_POWER_UP) ++ msleep(host->powerup_msecs); ++ } ++ } ++ ++ /* See 6.4.1 in the simplified SD card physical spec 2.0 */ ++ if (ios->power_mode == MMC_POWER_ON) ++ mmc_spi_initsequence(host); ++ ++ /* If powering down, ground all card inputs to avoid power ++ * delivery from data lines! On a shared SPI bus, this ++ * will probably be temporary; 6.4.2 of the simplified SD ++ * spec says this must last at least 1msec. ++ * ++ * - Clock low means CPOL 0, e.g. mode 0 ++ * - MOSI low comes from writing zero ++ * - Chipselect is usually active low... ++ */ ++ if (canpower && ios->power_mode == MMC_POWER_OFF) { ++ int mres; ++ ++ host->spi->mode &= ~(SPI_CPOL|SPI_CPHA); ++ mres = spi_setup(host->spi); ++ if (mres < 0) ++ dev_dbg(&host->spi->dev, ++ "switch to SPI mode 0 failed\n"); ++ ++ if (spi_w8r8(host->spi, 0x00) < 0) ++ dev_dbg(&host->spi->dev, ++ "put spi signals to low failed\n"); ++ ++ /* ++ * Now clock should be low due to spi mode 0; ++ * MOSI should be low because of written 0x00; ++ * chipselect should be low (it is active low) ++ * power supply is off, so now MMC is off too! ++ * ++ * FIXME no, chipselect can be high since the ++ * device is inactive and SPI_CS_HIGH is clear... ++ */ ++ msleep(10); ++ if (mres == 0) { ++ host->spi->mode |= (SPI_CPOL|SPI_CPHA); ++ mres = spi_setup(host->spi); ++ if (mres < 0) ++ dev_dbg(&host->spi->dev, ++ "switch back to SPI mode 3" ++ " failed\n"); ++ } ++ } ++ ++ host->power_mode = ios->power_mode; ++ } ++ ++ if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) { ++ int status; ++ ++ host->spi->max_speed_hz = ios->clock; ++ status = spi_setup(host->spi); ++ dev_dbg(&host->spi->dev, ++ "mmc_spi: clock to %d Hz, %d\n", ++ host->spi->max_speed_hz, status); ++ } ++} ++ ++static int mmc_spi_get_ro(struct mmc_host *mmc) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ ++ if (host->pdata && host->pdata->get_ro) ++ return host->pdata->get_ro(mmc->parent); ++ /* board doesn't support read only detection; assume writeable */ ++ return 0; ++} ++ ++ ++static const struct mmc_host_ops mmc_spi_ops = { ++ .request = mmc_spi_request, ++ .set_ios = mmc_spi_set_ios, ++ .get_ro = mmc_spi_get_ro, ++}; ++ ++ ++/****************************************************************************/ ++ ++/* ++ * SPI driver implementation ++ */ ++ ++static irqreturn_t ++mmc_spi_detect_irq(int irq, void *mmc) ++{ ++ struct mmc_spi_host *host = mmc_priv(mmc); ++ u16 delay_msec = max(host->pdata->detect_delay, (u16)100); ++ ++ mmc_detect_change(mmc, msecs_to_jiffies(delay_msec)); ++ return IRQ_HANDLED; ++} ++ ++struct count_children { ++ unsigned n; ++ struct bus_type *bus; ++}; ++ ++static int maybe_count_child(struct device *dev, void *c) ++{ ++ struct count_children *ccp = c; ++ ++ if (dev->bus == ccp->bus) { ++ if (ccp->n) ++ return -EBUSY; ++ ccp->n++; ++ } ++ return 0; ++} ++ ++static int mmc_spi_probe(struct spi_device *spi) ++{ ++ void *ones; ++ struct mmc_host *mmc; ++ struct mmc_spi_host *host; ++ int status; ++ ++ /* MMC and SD specs only seem to care that sampling is on the ++ * rising edge ... meaning SPI modes 0 or 3. So either SPI mode ++ * should be legit. We'll use mode 0 since it seems to be a ++ * bit less troublesome on some hardware ... unclear why. ++ */ ++ spi->mode = SPI_MODE_0; ++ spi->bits_per_word = 8; ++ ++ status = spi_setup(spi); ++ if (status < 0) { ++ dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n", ++ spi->mode, spi->max_speed_hz / 1000, ++ status); ++ return status; ++ } ++ ++ /* We can use the bus safely iff nobody else will interfere with us. ++ * Most commands consist of one SPI message to issue a command, then ++ * several more to collect its response, then possibly more for data ++ * transfer. Clocking access to other devices during that period will ++ * corrupt the command execution. ++ * ++ * Until we have software primitives which guarantee non-interference, ++ * we'll aim for a hardware-level guarantee. ++ * ++ * REVISIT we can't guarantee another device won't be added later... ++ */ ++ if (spi->master->num_chipselect > 1) { ++ struct count_children cc; ++ ++ cc.n = 0; ++ cc.bus = spi->dev.bus; ++ status = device_for_each_child(spi->dev.parent, &cc, ++ maybe_count_child); ++ if (status < 0) { ++ dev_err(&spi->dev, "can't share SPI bus\n"); ++ return status; ++ } ++ ++ dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n"); ++ } ++ ++ /* We need a supply of ones to transmit. This is the only time ++ * the CPU touches these, so cache coherency isn't a concern. ++ * ++ * NOTE if many systems use more than one MMC-over-SPI connector ++ * it'd save some memory to share this. That's evidently rare. ++ */ ++ status = -ENOMEM; ++ ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL); ++ if (!ones) ++ goto nomem; ++ memset(ones, 0xff, MMC_SPI_BLOCKSIZE); ++ ++ mmc = mmc_alloc_host(sizeof(*host), &spi->dev); ++ if (!mmc) ++ goto nomem; ++ ++ mmc->ops = &mmc_spi_ops; ++ mmc->max_blk_size = MMC_SPI_BLOCKSIZE; ++ ++ /* As long as we keep track of the number of successfully ++ * transmitted blocks, we're good for multiwrite. ++ */ ++ mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE; ++ ++ /* SPI doesn't need the lowspeed device identification thing for ++ * MMC or SD cards, since it never comes up in open drain mode. ++ * That's good; some SPI masters can't handle very low speeds! ++ * ++ * However, low speed SDIO cards need not handle over 400 KHz; ++ * that's the only reason not to use a few MHz for f_min (until ++ * the upper layer reads the target frequency from the CSD). ++ */ ++ mmc->f_min = 400000; ++ mmc->f_max = spi->max_speed_hz; ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->spi = spi; ++ ++ host->ones = ones; ++ ++ /* Platform data is used to hook up things like card sensing ++ * and power switching gpios. ++ */ ++ host->pdata = spi->dev.platform_data; ++ if (host->pdata) ++ mmc->ocr_avail = host->pdata->ocr_mask; ++ if (!mmc->ocr_avail) { ++ dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n"); ++ mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; ++ } ++ if (host->pdata && host->pdata->setpower) { ++ host->powerup_msecs = host->pdata->powerup_msecs; ++ if (!host->powerup_msecs || host->powerup_msecs > 250) ++ host->powerup_msecs = 250; ++ } ++ ++ dev_set_drvdata(&spi->dev, mmc); ++ ++ /* preallocate dma buffers */ ++ host->data = kmalloc(sizeof(*host->data), GFP_KERNEL); ++ if (!host->data) ++ goto fail_nobuf1; ++ ++//FIXME ++#if 0 ++ if (spi->master->dev.parent->dma_mask) { ++ struct device *dev = spi->master->dev.parent; ++ ++ host->dma_dev = dev; ++ host->ones_dma = dma_map_single(dev, ones, ++ MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); ++ host->data_dma = dma_map_single(dev, host->data, ++ sizeof(*host->data), DMA_BIDIRECTIONAL); ++ ++ /* REVISIT in theory those map operations can fail... */ ++ ++ dma_sync_single_for_cpu(host->dma_dev, ++ host->data_dma, sizeof(*host->data), ++ DMA_BIDIRECTIONAL); ++ } ++#endif ++ ++ /* setup message for status/busy readback */ ++ spi_message_init(&host->readback); ++ host->readback.is_dma_mapped = (host->dma_dev != NULL); ++ ++ spi_message_add_tail(&host->status, &host->readback); ++ host->status.tx_buf = host->ones; ++ host->status.tx_dma = host->ones_dma; ++ host->status.rx_buf = &host->data->status; ++ host->status.rx_dma = host->data_dma + offsetof(struct scratch, status); ++ host->status.cs_change = 1; ++ ++ /* register card detect irq */ ++ if (host->pdata && host->pdata->init) { ++ status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc); ++ if (status != 0) ++ goto fail_glue_init; ++ } ++ ++ status = mmc_add_host(mmc); ++ if (status != 0) ++ goto fail_add_host; ++ ++ dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n", ++ mmc->class_dev.bus_id, ++ host->dma_dev ? "" : ", no DMA", ++ (host->pdata && host->pdata->get_ro) ++ ? "" : ", no WP", ++ (host->pdata && host->pdata->setpower) ++ ? "" : ", no poweroff"); ++ return 0; ++ ++fail_add_host: ++ mmc_remove_host (mmc); ++fail_glue_init: ++ if (host->dma_dev) ++ dma_unmap_single(host->dma_dev, host->data_dma, ++ sizeof(*host->data), DMA_BIDIRECTIONAL); ++ kfree(host->data); ++ ++fail_nobuf1: ++ mmc_free_host(mmc); ++ dev_set_drvdata(&spi->dev, NULL); ++ ++nomem: ++ kfree(ones); ++ return status; ++} ++ ++ ++static int __devexit mmc_spi_remove(struct spi_device *spi) ++{ ++ struct mmc_host *mmc = dev_get_drvdata(&spi->dev); ++ struct mmc_spi_host *host; ++ ++ if (mmc) { ++ host = mmc_priv(mmc); ++ ++ /* prevent new mmc_detect_change() calls */ ++ if (host->pdata && host->pdata->exit) ++ host->pdata->exit(&spi->dev, mmc); ++ ++ mmc_remove_host(mmc); ++ ++ if (host->dma_dev) { ++ dma_unmap_single(host->dma_dev, host->ones_dma, ++ MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE); ++ dma_unmap_single(host->dma_dev, host->data_dma, ++ sizeof(*host->data), DMA_BIDIRECTIONAL); ++ } ++ ++ kfree(host->data); ++ kfree(host->ones); ++ ++ spi->max_speed_hz = mmc->f_max; ++ mmc_free_host(mmc); ++ dev_set_drvdata(&spi->dev, NULL); ++ } ++ return 0; ++} ++ ++ ++static struct spi_driver mmc_spi_driver = { ++ .driver = { ++ .name = "mmc_spi", ++ .bus = &spi_bus_type, ++ .owner = THIS_MODULE, ++ }, ++ .probe = mmc_spi_probe, ++ .remove = __devexit_p(mmc_spi_remove), ++}; ++ ++ ++static int __init mmc_spi_init(void) ++{ ++ return spi_register_driver(&mmc_spi_driver); ++} ++module_init(mmc_spi_init); ++ ++ ++static void __exit mmc_spi_exit(void) ++{ ++ spi_unregister_driver(&mmc_spi_driver); ++} ++module_exit(mmc_spi_exit); ++ ++ ++MODULE_AUTHOR("Mike Lavender, David Brownell, " ++ "Hans-Peter Nilsson, Jan Nikitenko"); ++MODULE_DESCRIPTION("SPI SD/MMC host driver"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/spi/mmc_spi.h +@@ -0,0 +1,33 @@ ++#ifndef __LINUX_SPI_MMC_SPI_H ++#define __LINUX_SPI_MMC_SPI_H ++ ++struct device; ++struct mmc_host; ++ ++/* Put this in platform_data of a device being used to manage an MMC/SD ++ * card slot. (Modeled after PXA mmc glue; see that for usage examples.) ++ * ++ * REVISIT This is not a spi-specific notion. Any card slot should be ++ * able to handle it. If the MMC core doesn't adopt this kind of notion, ++ * switch the "struct device *" parameters over to "struct spi_device *". ++ */ ++struct mmc_spi_platform_data { ++ /* driver activation and (optional) card detect irq hookup */ ++ int (*init)(struct device *, ++ irqreturn_t (*)(int, void *), ++ void *); ++ void (*exit)(struct device *, void *); ++ ++ /* sense switch on sd cards */ ++ int (*get_ro)(struct device *); ++ ++ /* how long to debounce card detect, in msecs */ ++ u16 detect_delay; ++ ++ /* power management */ ++ u16 powerup_msecs; /* delay of up to 250 msec */ ++ u32 ocr_mask; /* available voltages */ ++ void (*setpower)(struct device *, unsigned int maskval); ++}; ++ ++#endif /* __LINUX_SPI_MMC_SPI_H */ +--- a/drivers/mmc/core/bus.c ++++ b/drivers/mmc/core/bus.c +@@ -19,6 +19,7 @@ + + #include "sysfs.h" + #include "core.h" ++#include "sdio_cis.h" + #include "bus.h" + + #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +@@ -34,6 +35,8 @@ + return sprintf(buf, "MMC\n"); + case MMC_TYPE_SD: + return sprintf(buf, "SD\n"); ++ case MMC_TYPE_SDIO: ++ return sprintf(buf, "SDIO\n"); + default: + return -EFAULT; + } +@@ -55,36 +58,37 @@ + } + + static int +-mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, +- int buf_size) ++mmc_bus_uevent(struct device *dev, char **envp, ++ int num_envp, char *buffer, int buffer_size) + { + struct mmc_card *card = dev_to_mmc_card(dev); +- int retval = 0, i = 0, length = 0; +- +-#define add_env(fmt,val) do { \ +- retval = add_uevent_var(envp, num_envp, &i, \ +- buf, buf_size, &length, \ +- fmt, val); \ +- if (retval) \ +- return retval; \ +-} while (0); ++ const char *type; ++ int retval = 0; ++ int i = 0, len = 0; + + switch (card->type) { + case MMC_TYPE_MMC: +- add_env("MMC_TYPE=%s", "MMC"); ++ type = "MMC"; + break; + case MMC_TYPE_SD: +- add_env("MMC_TYPE=%s", "SD"); ++ type = "SD"; + break; ++ case MMC_TYPE_SDIO: ++ type = "SDIO"; ++ break; ++ default: ++ type = NULL; + } + +- add_env("MMC_NAME=%s", mmc_card_name(card)); +- +-#undef add_env ++ if (type) { ++ retval = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MMC_TYPE=%s", type); ++ if (retval) ++ return retval; ++ } + +- envp[i] = NULL; ++ retval = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MMC_NAME=%s", mmc_card_name(card)); + +- return 0; ++ return retval; + } + + static int mmc_bus_probe(struct device *dev) +@@ -176,6 +180,11 @@ + { + struct mmc_card *card = dev_to_mmc_card(dev); + ++ sdio_free_common_cis(card); ++ ++ if (card->info) ++ kfree(card->info); ++ + kfree(card); + } + +@@ -221,15 +230,25 @@ + if (mmc_card_blockaddr(card)) + type = "SDHC"; + break; ++ case MMC_TYPE_SDIO: ++ type = "SDIO"; ++ break; + default: + type = "?"; + break; + } + +- printk(KERN_INFO "%s: new %s%s card at address %04x\n", +- mmc_hostname(card->host), +- mmc_card_highspeed(card) ? "high speed " : "", +- type, card->rca); ++ if (mmc_host_is_spi(card->host)) { ++ printk(KERN_INFO "%s: new %s%s card on SPI\n", ++ mmc_hostname(card->host), ++ mmc_card_highspeed(card) ? "high speed " : "", ++ type); ++ } else { ++ printk(KERN_INFO "%s: new %s%s card at address %04x\n", ++ mmc_hostname(card->host), ++ mmc_card_highspeed(card) ? "high speed " : "", ++ type, card->rca); ++ } + + card->dev.uevent_suppress = 1; + +@@ -261,8 +280,13 @@ + void mmc_remove_card(struct mmc_card *card) + { + if (mmc_card_present(card)) { +- printk(KERN_INFO "%s: card %04x removed\n", +- mmc_hostname(card->host), card->rca); ++ if (mmc_host_is_spi(card->host)) { ++ printk(KERN_INFO "%s: SPI card removed\n", ++ mmc_hostname(card->host)); ++ } else { ++ printk(KERN_INFO "%s: card %04x removed\n", ++ mmc_hostname(card->host), card->rca); ++ } + + if (card->host->bus_ops->sysfs_remove) + card->host->bus_ops->sysfs_remove(card->host, card); +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -18,7 +18,7 @@ + #include + #include + #include +-#include ++#include + #include + + #include +@@ -29,16 +29,27 @@ + #include "core.h" + #include "bus.h" + #include "host.h" ++#include "sdio_bus.h" + + #include "mmc_ops.h" + #include "sd_ops.h" ++#include "sdio_ops.h" + + extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); + extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); ++extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr); + + static struct workqueue_struct *workqueue; + + /* ++ * Enabling software CRCs on the data blocks can be a significant (30%) ++ * performance cost, and for other reasons may not always be desired. ++ * So we allow it it to be disabled. ++ */ ++int use_spi_crc = 1; ++module_param(use_spi_crc, bool, 0); ++ ++/* + * Internal function. Schedule delayed work in the MMC work queue. + */ + static int mmc_schedule_delayed_work(struct delayed_work *work, +@@ -68,6 +79,11 @@ + struct mmc_command *cmd = mrq->cmd; + int err = cmd->error; + ++ if (err && cmd->retries && mmc_host_is_spi(host)) { ++ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) ++ cmd->retries = 0; ++ } ++ + if (err && cmd->retries) { + pr_debug("%s: req failed (CMD%u): %d, retrying...\n", + mmc_hostname(host), cmd->opcode, err); +@@ -76,6 +92,8 @@ + cmd->error = 0; + host->ops->request(host, mrq); + } else { ++ led_trigger_event(host->led, LED_OFF); ++ + pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", + mmc_hostname(host), cmd->opcode, err, + cmd->resp[0], cmd->resp[1], +@@ -118,7 +136,7 @@ + "tsac %d ms nsac %d\n", + mmc_hostname(host), mrq->data->blksz, + mrq->data->blocks, mrq->data->flags, +- mrq->data->timeout_ns / 10000000, ++ mrq->data->timeout_ns / 1000000, + mrq->data->timeout_clks); + } + +@@ -130,6 +148,8 @@ + + WARN_ON(!host->claimed); + ++ led_trigger_event(host->led, LED_FULL); ++ + mrq->cmd->error = 0; + mrq->cmd->mrq = mrq; + if (mrq->data) { +@@ -199,7 +219,7 @@ + { + struct mmc_request mrq; + +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + memset(&mrq, 0, sizeof(struct mmc_request)); + +@@ -220,17 +240,24 @@ + * mmc_set_data_timeout - set the timeout for a data command + * @data: data phase for command + * @card: the MMC card associated with the data transfer +- * @write: flag to differentiate reads from writes + * + * Computes the data timeout parameters according to the + * correct algorithm given the card type. + */ +-void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, +- int write) ++void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) + { + unsigned int mult; + + /* ++ * SDIO cards only define an upper 1 s limit on access. ++ */ ++ if (mmc_card_sdio(card)) { ++ data->timeout_ns = 1000000000; ++ data->timeout_clks = 0; ++ return; ++ } ++ ++ /* + * SD cards use a 100 multiplier rather than 10 + */ + mult = mmc_card_sd(card) ? 100 : 10; +@@ -239,7 +266,7 @@ + * Scale up the multiplier (and therefore the timeout) by + * the r2w factor for writes. + */ +- if (write) ++ if (data->flags & MMC_DATA_WRITE) + mult <<= card->csd.r2w_factor; + + data->timeout_ns = card->csd.tacc_ns * mult; +@@ -255,7 +282,7 @@ + timeout_us += data->timeout_clks * 1000 / + (card->host->ios.clock / 1000); + +- if (write) ++ if (data->flags & MMC_DATA_WRITE) + limit_us = 250000; + else + limit_us = 100000; +@@ -272,15 +299,20 @@ + EXPORT_SYMBOL(mmc_set_data_timeout); + + /** +- * mmc_claim_host - exclusively claim a host ++ * __mmc_claim_host - exclusively claim a host + * @host: mmc host to claim ++ * @abort: whether or not the operation should be aborted + * +- * Claim a host for a set of operations. ++ * Claim a host for a set of operations. If @abort is non null and ++ * dereference a non-zero value then this will return prematurely with ++ * that non-zero value without acquiring the lock. Returns zero ++ * with the lock held otherwise. + */ +-void mmc_claim_host(struct mmc_host *host) ++int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) + { + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; ++ int stop; + + might_sleep(); + +@@ -288,19 +320,24 @@ + spin_lock_irqsave(&host->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); +- if (!host->claimed) ++ stop = abort ? atomic_read(abort) : 0; ++ if (stop || !host->claimed) + break; + spin_unlock_irqrestore(&host->lock, flags); + schedule(); + spin_lock_irqsave(&host->lock, flags); + } + set_current_state(TASK_RUNNING); +- host->claimed = 1; ++ if (!stop) ++ host->claimed = 1; ++ else ++ wake_up(&host->wq); + spin_unlock_irqrestore(&host->lock, flags); + remove_wait_queue(&host->wq, &wait); ++ return stop; + } + +-EXPORT_SYMBOL(mmc_claim_host); ++EXPORT_SYMBOL(__mmc_claim_host); + + /** + * mmc_release_host - release a host +@@ -313,7 +350,7 @@ + { + unsigned long flags; + +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + spin_lock_irqsave(&host->lock, flags); + host->claimed = 0; +@@ -433,19 +470,32 @@ + int bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; +- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; +- host->ios.chip_select = MMC_CS_DONTCARE; ++ if (mmc_host_is_spi(host)) { ++ host->ios.chip_select = MMC_CS_HIGH; ++ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; ++ } else { ++ host->ios.chip_select = MMC_CS_DONTCARE; ++ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; ++ } + host->ios.power_mode = MMC_POWER_UP; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + mmc_set_ios(host); + +- mmc_delay(1); ++ /* ++ * This delay should be sufficient to allow the power supply ++ * to reach the minimum voltage. ++ */ ++ mmc_delay(2); + + host->ios.clock = host->f_min; + host->ios.power_mode = MMC_POWER_ON; + mmc_set_ios(host); + ++ /* ++ * This delay must be at least 74 clock sizes, or 1 ms, or the ++ * time required to reach a stable voltage. ++ */ + mmc_delay(2); + } + +@@ -453,8 +503,10 @@ + { + host->ios.clock = 0; + host->ios.vdd = 0; +- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; +- host->ios.chip_select = MMC_CS_DONTCARE; ++ if (!mmc_host_is_spi(host)) { ++ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; ++ host->ios.chip_select = MMC_CS_DONTCARE; ++ } + host->ios.power_mode = MMC_POWER_OFF; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; +@@ -511,7 +563,7 @@ + BUG_ON(!host); + BUG_ON(!ops); + +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + spin_lock_irqsave(&host->lock, flags); + +@@ -535,8 +587,8 @@ + + BUG_ON(!host); + +- BUG_ON(!host->claimed); +- BUG_ON(!host->bus_ops); ++ WARN_ON(!host->claimed); ++ WARN_ON(!host->bus_ops); + + spin_lock_irqsave(&host->lock, flags); + +@@ -564,7 +616,7 @@ + #ifdef CONFIG_MMC_DEBUG + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); +- BUG_ON(host->removed); ++ WARN_ON(host->removed); + spin_unlock_irqrestore(&host->lock, flags); + #endif + +@@ -597,24 +649,38 @@ + + mmc_send_if_cond(host, host->ocr_avail); + ++ /* ++ * First we search for SDIO... ++ */ ++ err = mmc_send_io_op_cond(host, 0, &ocr); ++ if (!err) { ++ if (mmc_attach_sdio(host, ocr)) ++ mmc_power_off(host); ++ return; ++ } ++ ++ /* ++ * ...then normal SD... ++ */ + err = mmc_send_app_op_cond(host, 0, &ocr); +- if (err == MMC_ERR_NONE) { ++ if (!err) { + if (mmc_attach_sd(host, ocr)) + mmc_power_off(host); +- } else { +- /* +- * If we fail to detect any SD cards then try +- * searching for MMC cards. +- */ +- err = mmc_send_op_cond(host, 0, &ocr); +- if (err == MMC_ERR_NONE) { +- if (mmc_attach_mmc(host, ocr)) +- mmc_power_off(host); +- } else { ++ return; ++ } ++ ++ /* ++ * ...and finally MMC. ++ */ ++ err = mmc_send_op_cond(host, 0, &ocr); ++ if (!err) { ++ if (mmc_attach_mmc(host, ocr)) + mmc_power_off(host); +- mmc_release_host(host); +- } ++ return; + } ++ ++ mmc_release_host(host); ++ mmc_power_off(host); + } else { + if (host->bus_ops->detect && !host->bus_dead) + host->bus_ops->detect(host); +@@ -725,22 +791,38 @@ + return -ENOMEM; + + ret = mmc_register_bus(); +- if (ret == 0) { +- ret = mmc_register_host_class(); +- if (ret) +- mmc_unregister_bus(); +- } ++ if (ret) ++ goto destroy_workqueue; ++ ++ ret = mmc_register_host_class(); ++ if (ret) ++ goto unregister_bus; ++ ++ ret = sdio_register_bus(); ++ if (ret) ++ goto unregister_host_class; ++ ++ return 0; ++ ++unregister_host_class: ++ mmc_unregister_host_class(); ++unregister_bus: ++ mmc_unregister_bus(); ++destroy_workqueue: ++ destroy_workqueue(workqueue); ++ + return ret; + } + + static void __exit mmc_exit(void) + { ++ sdio_unregister_bus(); + mmc_unregister_host_class(); + mmc_unregister_bus(); + destroy_workqueue(workqueue); + } + +-module_init(mmc_init); ++subsys_initcall(mmc_init); + module_exit(mmc_exit); + + MODULE_LICENSE("GPL"); +--- a/drivers/mmc/core/core.h ++++ b/drivers/mmc/core/core.h +@@ -48,5 +48,7 @@ + void mmc_start_host(struct mmc_host *host); + void mmc_stop_host(struct mmc_host *host); + ++extern int use_spi_crc; ++ + #endif + +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include + +@@ -100,6 +101,9 @@ + { + int err; + ++ WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && ++ !host->ops->enable_sdio_irq); ++ + if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) + return -ENOMEM; + +@@ -112,6 +116,8 @@ + snprintf(host->class_dev.bus_id, BUS_ID_SIZE, + "mmc%d", host->index); + ++ led_trigger_register_simple(host->class_dev.bus_id, &host->led); ++ + err = device_add(&host->class_dev); + if (err) + return err; +@@ -137,6 +143,8 @@ + + device_del(&host->class_dev); + ++ led_trigger_unregister_simple(host->led); ++ + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -161,13 +161,12 @@ + { + int err; + u8 *ext_csd; ++ unsigned int ext_csd_struct; + + BUG_ON(!card); + +- err = MMC_ERR_FAILED; +- + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) +- return MMC_ERR_NONE; ++ return 0; + + /* + * As the ext_csd is so large and mostly unused, we don't store the +@@ -176,13 +175,19 @@ + ext_csd = kmalloc(512, GFP_KERNEL); + if (!ext_csd) { + printk(KERN_ERR "%s: could not allocate a buffer to " +- "receive the ext_csd. mmc v4 cards will be " +- "treated as v3.\n", mmc_hostname(card->host)); +- return MMC_ERR_FAILED; ++ "receive the ext_csd.\n", mmc_hostname(card->host)); ++ return -ENOMEM; + } + + err = mmc_send_ext_csd(card, ext_csd); +- if (err != MMC_ERR_NONE) { ++ if (err) { ++ /* ++ * We all hosts that cannot perform the command ++ * to fail more gracefully ++ */ ++ if (err != -EINVAL) ++ goto out; ++ + /* + * High capacity cards should have this "magic" size + * stored in their CSD. +@@ -197,18 +202,30 @@ + "EXT_CSD, performance might " + "suffer.\n", + mmc_hostname(card->host)); +- err = MMC_ERR_NONE; ++ err = 0; + } ++ + goto out; + } + +- card->ext_csd.sectors = +- ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | +- ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | +- ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | +- ext_csd[EXT_CSD_SEC_CNT + 3] << 24; +- if (card->ext_csd.sectors) +- mmc_card_set_blockaddr(card); ++ ext_csd_struct = ext_csd[EXT_CSD_REV]; ++ if (ext_csd_struct > 2) { ++ printk(KERN_ERR "%s: unrecognised EXT_CSD structure " ++ "version %d\n", mmc_hostname(card->host), ++ ext_csd_struct); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ if (ext_csd_struct >= 2) { ++ card->ext_csd.sectors = ++ ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | ++ ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ++ ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ++ ext_csd[EXT_CSD_SEC_CNT + 3] << 24; ++ if (card->ext_csd.sectors) ++ mmc_card_set_blockaddr(card); ++ } + + switch (ext_csd[EXT_CSD_CARD_TYPE]) { + case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: +@@ -246,7 +263,7 @@ + unsigned int max_dtr; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + /* + * Since we're changing the OCR value, we seem to +@@ -258,19 +275,33 @@ + + /* The extra bit indicates that we support high capacity */ + err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Fetch CID from card. + */ +- err = mmc_all_send_cid(host, cid); +- if (err != MMC_ERR_NONE) ++ if (mmc_host_is_spi(host)) ++ err = mmc_send_cid(host, cid); ++ else ++ err = mmc_all_send_cid(host, cid); ++ if (err) + goto err; + + if (oldcard) { +- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) ++ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { ++ err = -ENOENT; + goto err; ++ } + + card = oldcard; + } else { +@@ -278,8 +309,10 @@ + * Allocate card structure. + */ + card = mmc_alloc_card(host); +- if (IS_ERR(card)) ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); + goto err; ++ } + + card->type = MMC_TYPE_MMC; + card->rca = 1; +@@ -287,43 +320,47 @@ + } + + /* +- * Set card RCA. ++ * For native busses: set card RCA and quit open drain mode. + */ +- err = mmc_set_relative_addr(card); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_set_relative_addr(card); ++ if (err) ++ goto free_card; + +- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } + + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + err = mmc_decode_csd(card); +- if (err < 0) ++ if (err) + goto free_card; + err = mmc_decode_cid(card); +- if (err < 0) ++ if (err) + goto free_card; + } + + /* + * Select card, as all following commands rely on that. + */ +- err = mmc_select_card(card); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto free_card; ++ } + + if (!oldcard) { + /* +- * Fetch and process extened CSD. ++ * Fetch and process extended CSD. + */ + err = mmc_read_ext_csd(card); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + } + +@@ -334,7 +371,7 @@ + (host->caps & MMC_CAP_MMC_HIGHSPEED)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, 1); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + mmc_card_set_highspeed(card); +@@ -363,7 +400,7 @@ + (host->caps & MMC_CAP_4_BIT_DATA)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); +@@ -372,14 +409,14 @@ + if (!oldcard) + host->card = card; + +- return MMC_ERR_NONE; ++ return 0; + + free_card: + if (!oldcard) + mmc_remove_card(card); + err: + +- return MMC_ERR_FAILED; ++ return err; + } + + /* +@@ -413,7 +450,7 @@ + + mmc_release_host(host); + +- if (err != MMC_ERR_NONE) { ++ if (err) { + mmc_remove(host); + + mmc_claim_host(host); +@@ -480,7 +517,8 @@ + BUG_ON(!host->card); + + mmc_claim_host(host); +- mmc_deselect_cards(host); ++ if (!mmc_host_is_spi(host)) ++ mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); + } +@@ -502,7 +540,7 @@ + err = mmc_init_card(host, host->ocr, host->card); + mmc_release_host(host); + +- if (err != MMC_ERR_NONE) { ++ if (err) { + mmc_remove(host); + + mmc_claim_host(host); +@@ -536,11 +574,20 @@ + int err; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_ops); + + /* ++ * We need to get OCR a different way for SPI. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_read_ocr(host, 1, &ocr); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Sanity check the voltages that the card claims to + * support. + */ +@@ -565,7 +612,7 @@ + * Detect and init the card. + */ + err = mmc_init_card(host, host->ocr, NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + mmc_release_host(host); +@@ -587,6 +634,6 @@ + printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", + mmc_hostname(host), err); + +- return 0; ++ return err; + } + +--- a/drivers/mmc/core/mmc_ops.c ++++ b/drivers/mmc/core/mmc_ops.c +@@ -10,7 +10,6 @@ + */ + + #include +-#include + #include + + #include +@@ -40,10 +39,10 @@ + } + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_select_card(struct mmc_card *card) +@@ -63,23 +62,36 @@ + int err; + struct mmc_command cmd; + +- mmc_set_chip_select(host, MMC_CS_HIGH); +- +- mmc_delay(1); ++ /* ++ * Non-SPI hosts need to prevent chipselect going active during ++ * GO_IDLE; that would put chips into SPI mode. Remind them of ++ * that in case of hardware that won't pull up DAT3/nCS otherwise. ++ * ++ * SPI hosts ignore ios.chip_select; it's managed according to ++ * rules that must accomodate non-MMC slaves which this layer ++ * won't even know about. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ mmc_set_chip_select(host, MMC_CS_HIGH); ++ mmc_delay(1); ++ } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; +- cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; + + err = mmc_wait_for_cmd(host, &cmd, 0); + + mmc_delay(1); + +- mmc_set_chip_select(host, MMC_CS_DONTCARE); ++ if (!mmc_host_is_spi(host)) { ++ mmc_set_chip_select(host, MMC_CS_DONTCARE); ++ mmc_delay(1); ++ } + +- mmc_delay(1); ++ host->use_spi_crc = 0; + + return err; + } +@@ -94,23 +106,33 @@ + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_OP_COND; +- cmd.arg = ocr; +- cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; ++ cmd.arg = mmc_host_is_spi(host) ? 0 : ocr; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, 0); +- if (err != MMC_ERR_NONE) ++ if (err) + break; + +- if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) ++ /* if we're just probing, do a single pass */ ++ if (ocr == 0) + break; + +- err = MMC_ERR_TIMEOUT; ++ /* otherwise wait until reset completes */ ++ if (mmc_host_is_spi(host)) { ++ if (!(cmd.resp[0] & R1_SPI_IDLE)) ++ break; ++ } else { ++ if (cmd.resp[0] & MMC_CARD_BUSY) ++ break; ++ } ++ ++ err = -ETIMEDOUT; + + mmc_delay(10); + } + +- if (rocr) ++ if (rocr && !mmc_host_is_spi(host)) + *rocr = cmd.resp[0]; + + return err; +@@ -131,12 +153,12 @@ + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + memcpy(cid, cmd.resp, sizeof(u32) * 4); + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_set_relative_addr(struct mmc_card *card) +@@ -154,46 +176,52 @@ + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + +-int mmc_send_csd(struct mmc_card *card, u32 *csd) ++static int ++mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode) + { + int err; + struct mmc_command cmd; + +- BUG_ON(!card); +- BUG_ON(!card->host); +- BUG_ON(!csd); ++ BUG_ON(!host); ++ BUG_ON(!cxd); + + memset(&cmd, 0, sizeof(struct mmc_command)); + +- cmd.opcode = MMC_SEND_CSD; +- cmd.arg = card->rca << 16; ++ cmd.opcode = opcode; ++ cmd.arg = arg; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + +- err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); ++ if (err) + return err; + +- memcpy(csd, cmd.resp, sizeof(u32) * 4); ++ memcpy(cxd, cmd.resp, sizeof(u32) * 4); + +- return MMC_ERR_NONE; ++ return 0; + } + +-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ++static int ++mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, ++ u32 opcode, void *buf, unsigned len) + { + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; ++ void *data_buf; + +- BUG_ON(!card); +- BUG_ON(!card->host); +- BUG_ON(!ext_csd); ++ /* dma onto stack is unsafe/nonportable, but callers to this ++ * routine normally provide temporary on-stack buffers ... ++ */ ++ data_buf = kmalloc(len, GFP_KERNEL); ++ if (data_buf == NULL) ++ return -ENOMEM; + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); +@@ -202,28 +230,117 @@ + mrq.cmd = &cmd; + mrq.data = &data; + +- cmd.opcode = MMC_SEND_EXT_CSD; ++ cmd.opcode = opcode; + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + +- data.blksz = 512; ++ /* NOTE HACK: the MMC_RSP_SPI_R1 is always correct here, but we ++ * rely on callers to never use this with "native" calls for reading ++ * CSD or CID. Native versions of those commands use the R2 type, ++ * not R1 plus a data block. ++ */ ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; ++ ++ data.blksz = len; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + +- sg_init_one(&sg, ext_csd, 512); ++ sg_init_one(&sg, data_buf, len); ++ ++ if (card) ++ mmc_set_data_timeout(&data, card); + +- mmc_set_data_timeout(&data, card, 0); ++ mmc_wait_for_req(host, &mrq); + +- mmc_wait_for_req(card->host, &mrq); ++ memcpy(buf, data_buf, len); ++ kfree(data_buf); + +- if (cmd.error != MMC_ERR_NONE) ++ if (cmd.error) + return cmd.error; +- if (data.error != MMC_ERR_NONE) ++ if (data.error) + return data.error; + +- return MMC_ERR_NONE; ++ return 0; ++} ++ ++int mmc_send_csd(struct mmc_card *card, u32 *csd) ++{ ++ int ret, i; ++ ++ if (!mmc_host_is_spi(card->host)) ++ return mmc_send_cxd_native(card->host, card->rca << 16, ++ csd, MMC_SEND_CSD); ++ ++ ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16); ++ if (ret) ++ return ret; ++ ++ for (i = 0;i < 4;i++) ++ csd[i] = be32_to_cpu(csd[i]); ++ ++ return 0; ++} ++ ++int mmc_send_cid(struct mmc_host *host, u32 *cid) ++{ ++ int ret, i; ++ ++ if (!mmc_host_is_spi(host)) { ++ if (!host->card) ++ return -EINVAL; ++ return mmc_send_cxd_native(host, host->card->rca << 16, ++ cid, MMC_SEND_CID); ++ } ++ ++ ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16); ++ if (ret) ++ return ret; ++ ++ for (i = 0;i < 4;i++) ++ cid[i] = be32_to_cpu(cid[i]); ++ ++ return 0; ++} ++ ++int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ++{ ++ return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ++ ext_csd, 512); ++} ++ ++int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) ++{ ++ struct mmc_command cmd; ++ int err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = MMC_SPI_READ_OCR; ++ cmd.arg = highcap ? (1 << 30) : 0; ++ cmd.flags = MMC_RSP_SPI_R3; ++ ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ ++ *ocrp = cmd.resp[1]; ++ return err; ++} ++ ++int mmc_spi_set_crc(struct mmc_host *host, int use_crc) ++{ ++ struct mmc_command cmd; ++ int err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = MMC_SPI_CRC_ON_OFF; ++ cmd.flags = MMC_RSP_SPI_R1; ++ cmd.arg = use_crc; ++ ++ err = mmc_wait_for_cmd(host, &cmd, 0); ++ if (!err) ++ host->use_spi_crc = use_crc; ++ return err; + } + + int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) +@@ -241,13 +358,13 @@ + (index << 16) | + (value << 8) | + set; +- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_send_status(struct mmc_card *card, u32 *status) +@@ -261,16 +378,20 @@ + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_STATUS; +- cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ if (!mmc_host_is_spi(card->host)) ++ cmd.arg = card->rca << 16; ++ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + ++ /* NOTE: callers are required to understand the difference ++ * between "native" and SPI format status words! ++ */ + if (status) + *status = cmd.resp[0]; + +- return MMC_ERR_NONE; ++ return 0; + } + +--- a/drivers/mmc/core/mmc_ops.h ++++ b/drivers/mmc/core/mmc_ops.h +@@ -22,6 +22,9 @@ + int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); + int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value); + int mmc_send_status(struct mmc_card *card, u32 *status); ++int mmc_send_cid(struct mmc_host *host, u32 *cid); ++int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); ++int mmc_spi_set_crc(struct mmc_host *host, int use_crc); + + #endif + +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -166,8 +166,6 @@ + unsigned int scr_struct; + u32 resp[4]; + +- BUG_ON(!mmc_card_sd(card)); +- + resp[3] = card->raw_scr[1]; + resp[2] = card->raw_scr[0]; + +@@ -193,30 +191,38 @@ + u8 *status; + + if (card->scr.sda_vsn < SCR_SPEC_VER_1) +- return MMC_ERR_NONE; ++ return 0; + + if (!(card->csd.cmdclass & CCC_SWITCH)) { + printk(KERN_WARNING "%s: card lacks mandatory switch " + "function, performance might suffer.\n", + mmc_hostname(card->host)); +- return MMC_ERR_NONE; ++ return 0; + } + +- err = MMC_ERR_FAILED; ++ err = -EIO; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk(KERN_ERR "%s: could not allocate a buffer for " + "switch capabilities.\n", mmc_hostname(card->host)); +- return err; ++ return -ENOMEM; + } + + err = mmc_sd_switch(card, 0, 0, 1, status); +- if (err != MMC_ERR_NONE) { ++ if (err) { ++ /* ++ * We all hosts that cannot perform the command ++ * to fail more gracefully ++ */ ++ if (err != -EINVAL) ++ goto out; ++ + printk(KERN_WARNING "%s: problem reading switch " + "capabilities, performance might suffer.\n", + mmc_hostname(card->host)); +- err = MMC_ERR_NONE; ++ err = 0; ++ + goto out; + } + +@@ -238,28 +244,28 @@ + u8 *status; + + if (card->scr.sda_vsn < SCR_SPEC_VER_1) +- return MMC_ERR_NONE; ++ return 0; + + if (!(card->csd.cmdclass & CCC_SWITCH)) +- return MMC_ERR_NONE; ++ return 0; + + if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) +- return MMC_ERR_NONE; ++ return 0; + + if (card->sw_caps.hs_max_dtr == 0) +- return MMC_ERR_NONE; ++ return 0; + +- err = MMC_ERR_FAILED; ++ err = -EIO; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk(KERN_ERR "%s: could not allocate a buffer for " + "switch capabilities.\n", mmc_hostname(card->host)); +- return err; ++ return -ENOMEM; + } + + err = mmc_sd_switch(card, 1, 0, 1, status); +- if (err != MMC_ERR_NONE) ++ if (err) + goto out; + + if ((status[16] & 0xF) != 1) { +@@ -292,7 +298,7 @@ + unsigned int max_dtr; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + /* + * Since we're changing the OCR value, we seem to +@@ -309,23 +315,37 @@ + * block-addressed SDHC cards. + */ + err = mmc_send_if_cond(host, ocr); +- if (err == MMC_ERR_NONE) ++ if (!err) + ocr |= 1 << 30; + + err = mmc_send_app_op_cond(host, ocr, NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Fetch CID from card. + */ +- err = mmc_all_send_cid(host, cid); +- if (err != MMC_ERR_NONE) ++ if (mmc_host_is_spi(host)) ++ err = mmc_send_cid(host, cid); ++ else ++ err = mmc_all_send_cid(host, cid); ++ if (err) + goto err; + + if (oldcard) { +- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) ++ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) { ++ err = -ENOENT; + goto err; ++ } + + card = oldcard; + } else { +@@ -333,32 +353,36 @@ + * Allocate card structure. + */ + card = mmc_alloc_card(host); +- if (IS_ERR(card)) ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); + goto err; ++ } + + card->type = MMC_TYPE_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } + + /* +- * Set card RCA. ++ * For native busses: get card RCA and quit open drain mode. + */ +- err = mmc_send_relative_addr(host, &card->rca); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_send_relative_addr(host, &card->rca); ++ if (err) ++ goto free_card; + +- mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } + + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + err = mmc_decode_csd(card); +- if (err < 0) ++ if (err) + goto free_card; + + mmc_decode_cid(card); +@@ -367,16 +391,18 @@ + /* + * Select card, as all following commands rely on that. + */ +- err = mmc_select_card(card); +- if (err != MMC_ERR_NONE) +- goto free_card; ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto free_card; ++ } + + if (!oldcard) { + /* + * Fetch SCR from card. + */ + err = mmc_app_send_scr(card, card->raw_scr); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + err = mmc_decode_scr(card); +@@ -387,7 +413,7 @@ + * Fetch switch information from card. + */ + err = mmc_read_switch(card); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + } + +@@ -395,7 +421,7 @@ + * Attempt to change to high-speed (if supported) + */ + err = mmc_switch_hs(card); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + /* +@@ -418,7 +444,7 @@ + if ((host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); +- if (err != MMC_ERR_NONE) ++ if (err) + goto free_card; + + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); +@@ -442,14 +468,14 @@ + if (!oldcard) + host->card = card; + +- return MMC_ERR_NONE; ++ return 0; + + free_card: + if (!oldcard) + mmc_remove_card(card); + err: + +- return MMC_ERR_FAILED; ++ return err; + } + + /* +@@ -483,7 +509,7 @@ + + mmc_release_host(host); + +- if (err != MMC_ERR_NONE) { ++ if (err) { + mmc_sd_remove(host); + + mmc_claim_host(host); +@@ -552,7 +578,8 @@ + BUG_ON(!host->card); + + mmc_claim_host(host); +- mmc_deselect_cards(host); ++ if (!mmc_host_is_spi(host)) ++ mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); + } +@@ -574,7 +601,7 @@ + err = mmc_sd_init_card(host, host->ocr, host->card); + mmc_release_host(host); + +- if (err != MMC_ERR_NONE) { ++ if (err) { + mmc_sd_remove(host); + + mmc_claim_host(host); +@@ -608,11 +635,22 @@ + int err; + + BUG_ON(!host); +- BUG_ON(!host->claimed); ++ WARN_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_sd_ops); + + /* ++ * We need to get OCR a different way for SPI. ++ */ ++ if (mmc_host_is_spi(host)) { ++ mmc_go_idle(host); ++ ++ err = mmc_spi_read_ocr(host, 0, &ocr); ++ if (err) ++ goto err; ++ } ++ ++ /* + * Sanity check the voltages that the card claims to + * support. + */ +@@ -644,7 +682,7 @@ + * Detect and init the card. + */ + err = mmc_sd_init_card(host, host->ocr, NULL); +- if (err != MMC_ERR_NONE) ++ if (err) + goto err; + + mmc_release_host(host); +@@ -666,6 +704,6 @@ + printk(KERN_ERR "%s: error %d whilst initialising SD card\n", + mmc_hostname(host), err); + +- return 0; ++ return err; + } + +--- a/drivers/mmc/core/sd_ops.c ++++ b/drivers/mmc/core/sd_ops.c +@@ -10,7 +10,6 @@ + */ + + #include +-#include + #include + + #include +@@ -33,21 +32,21 @@ + + if (card) { + cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR; + } + + err = mmc_wait_for_cmd(host, &cmd, 0); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + /* Check that card supported application commands */ +- if (!(cmd.resp[0] & R1_APP_CMD)) +- return MMC_ERR_FAILED; ++ if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD)) ++ return -EOPNOTSUPP; + +- return MMC_ERR_NONE; ++ return 0; + } + + /** +@@ -73,7 +72,7 @@ + BUG_ON(!cmd); + BUG_ON(retries < 0); + +- err = MMC_ERR_INVALID; ++ err = -EIO; + + /* + * We have to resend MMC_APP_CMD for each attempt so +@@ -83,8 +82,14 @@ + memset(&mrq, 0, sizeof(struct mmc_request)); + + err = mmc_app_cmd(host, card); +- if (err != MMC_ERR_NONE) ++ if (err) { ++ /* no point in retrying; no APP commands allowed */ ++ if (mmc_host_is_spi(host)) { ++ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) ++ break; ++ } + continue; ++ } + + memset(&mrq, 0, sizeof(struct mmc_request)); + +@@ -97,8 +102,14 @@ + mmc_wait_for_req(host, &mrq); + + err = cmd->error; +- if (cmd->error == MMC_ERR_NONE) ++ if (!cmd->error) + break; ++ ++ /* no point in retrying illegal APP commands */ ++ if (mmc_host_is_spi(host)) { ++ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) ++ break; ++ } + } + + return err; +@@ -127,14 +138,14 @@ + cmd.arg = SD_BUS_WIDTH_4; + break; + default: +- return MMC_ERR_INVALID; ++ return -EINVAL; + } + + err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +@@ -147,23 +158,36 @@ + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_OP_COND; +- cmd.arg = ocr; +- cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; ++ if (mmc_host_is_spi(host)) ++ cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */ ++ else ++ cmd.arg = ocr; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + break; + +- if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) ++ /* if we're just probing, do a single pass */ ++ if (ocr == 0) + break; + +- err = MMC_ERR_TIMEOUT; ++ /* otherwise wait until reset completes */ ++ if (mmc_host_is_spi(host)) { ++ if (!(cmd.resp[0] & R1_SPI_IDLE)) ++ break; ++ } else { ++ if (cmd.resp[0] & MMC_CARD_BUSY) ++ break; ++ } ++ ++ err = -ETIMEDOUT; + + mmc_delay(10); + } + +- if (rocr) ++ if (rocr && !mmc_host_is_spi(host)) + *rocr = cmd.resp[0]; + + return err; +@@ -174,6 +198,7 @@ + struct mmc_command cmd; + int err; + static const u8 test_pattern = 0xAA; ++ u8 result_pattern; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND +@@ -182,16 +207,21 @@ + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; +- cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; ++ cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, 0); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + +- if ((cmd.resp[0] & 0xFF) != test_pattern) +- return MMC_ERR_FAILED; ++ if (mmc_host_is_spi(host)) ++ result_pattern = cmd.resp[1] & 0xFF; ++ else ++ result_pattern = cmd.resp[0] & 0xFF; ++ ++ if (result_pattern != test_pattern) ++ return -EIO; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) +@@ -209,12 +239,12 @@ + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + *rca = cmd.resp[0] >> 16; + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_app_send_scr(struct mmc_card *card, u32 *scr) +@@ -229,8 +259,10 @@ + BUG_ON(!card->host); + BUG_ON(!scr); + ++ /* NOTE: caller guarantees scr is heap-allocated */ ++ + err = mmc_app_cmd(card->host, card); +- if (err != MMC_ERR_NONE) ++ if (err) + return err; + + memset(&mrq, 0, sizeof(struct mmc_request)); +@@ -242,7 +274,7 @@ + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 8; + data.blocks = 1; +@@ -252,19 +284,19 @@ + + sg_init_one(&sg, scr, 8); + +- mmc_set_data_timeout(&data, card, 0); ++ mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + +- if (cmd.error != MMC_ERR_NONE) ++ if (cmd.error) + return cmd.error; +- if (data.error != MMC_ERR_NONE) ++ if (data.error) + return data.error; + +- scr[0] = ntohl(scr[0]); +- scr[1] = ntohl(scr[1]); ++ scr[0] = be32_to_cpu(scr[0]); ++ scr[1] = be32_to_cpu(scr[1]); + +- return MMC_ERR_NONE; ++ return 0; + } + + int mmc_sd_switch(struct mmc_card *card, int mode, int group, +@@ -278,6 +310,8 @@ + BUG_ON(!card); + BUG_ON(!card->host); + ++ /* NOTE: caller guarantees resp is heap-allocated */ ++ + mode = !!mode; + value &= 0xF; + +@@ -292,7 +326,7 @@ + cmd.arg = mode << 31 | 0x00FFFFFF; + cmd.arg &= ~(0xF << (group * 4)); + cmd.arg |= value << (group * 4); +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; +@@ -302,15 +336,15 @@ + + sg_init_one(&sg, resp, 64); + +- mmc_set_data_timeout(&data, card, 0); ++ mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + +- if (cmd.error != MMC_ERR_NONE) ++ if (cmd.error) + return cmd.error; +- if (data.error != MMC_ERR_NONE) ++ if (data.error) + return data.error; + +- return MMC_ERR_NONE; ++ return 0; + } + +--- /dev/null ++++ b/drivers/mmc/core/sdio.c +@@ -0,0 +1,395 @@ ++/* ++ * linux/drivers/mmc/sdio.c ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "bus.h" ++#include "sdio_bus.h" ++#include "mmc_ops.h" ++#include "sd_ops.h" ++#include "sdio_ops.h" ++#include "sdio_cis.h" ++ ++static int sdio_read_fbr(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char data; ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data); ++ if (ret) ++ goto out; ++ ++ data &= 0x0f; ++ ++ if (data == 0x0f) { ++ ret = mmc_io_rw_direct(func->card, 0, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data); ++ if (ret) ++ goto out; ++ } ++ ++ func->class = data; ++ ++out: ++ return ret; ++} ++ ++static int sdio_init_func(struct mmc_card *card, unsigned int fn) ++{ ++ int ret; ++ struct sdio_func *func; ++ ++ BUG_ON(fn > SDIO_MAX_FUNCS); ++ ++ func = sdio_alloc_func(card); ++ if (IS_ERR(func)) ++ return PTR_ERR(func); ++ ++ func->num = fn; ++ ++ ret = sdio_read_fbr(func); ++ if (ret) ++ goto fail; ++ ++ ret = sdio_read_func_cis(func); ++ if (ret) ++ goto fail; ++ ++ card->sdio_func[fn - 1] = func; ++ ++ return 0; ++ ++fail: ++ /* ++ * It is okay to remove the function here even though we hold ++ * the host lock as we haven't registered the device yet. ++ */ ++ sdio_remove_func(func); ++ return ret; ++} ++ ++static int sdio_read_cccr(struct mmc_card *card) ++{ ++ int ret; ++ int cccr_vsn; ++ unsigned char data; ++ ++ memset(&card->cccr, 0, sizeof(struct sdio_cccr)); ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data); ++ if (ret) ++ goto out; ++ ++ cccr_vsn = data & 0x0f; ++ ++ if (cccr_vsn > SDIO_CCCR_REV_1_20) { ++ printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n", ++ mmc_hostname(card->host), cccr_vsn); ++ return -EINVAL; ++ } ++ ++ card->cccr.sdio_vsn = (data & 0xf0) >> 4; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data); ++ if (ret) ++ goto out; ++ ++ if (data & SDIO_CCCR_CAP_SMB) ++ card->cccr.multi_block = 1; ++ if (data & SDIO_CCCR_CAP_LSC) ++ card->cccr.low_speed = 1; ++ if (data & SDIO_CCCR_CAP_4BLS) ++ card->cccr.wide_bus = 1; ++ ++ if (cccr_vsn >= SDIO_CCCR_REV_1_10) { ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data); ++ if (ret) ++ goto out; ++ ++ if (data & SDIO_POWER_SMPC) ++ card->cccr.high_power = 1; ++ } ++ ++ if (cccr_vsn >= SDIO_CCCR_REV_1_20) { ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data); ++ if (ret) ++ goto out; ++ ++ if (data & SDIO_SPEED_SHS) ++ card->cccr.high_speed = 1; ++ } ++ ++out: ++ return ret; ++} ++ ++static int sdio_enable_wide(struct mmc_card *card) ++{ ++ int ret; ++ u8 ctrl; ++ ++ if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) ++ return 0; ++ ++ if (card->cccr.low_speed && !card->cccr.wide_bus) ++ return 0; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl); ++ if (ret) ++ return ret; ++ ++ ctrl |= SDIO_BUS_WIDTH_4BIT; ++ ++ ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); ++ if (ret) ++ return ret; ++ ++ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); ++ ++ return 0; ++} ++ ++/* ++ * Host is being removed. Free up the current card. ++ */ ++static void mmc_sdio_remove(struct mmc_host *host) ++{ ++ int i; ++ ++ BUG_ON(!host); ++ BUG_ON(!host->card); ++ ++ for (i = 0;i < host->card->sdio_funcs;i++) { ++ if (host->card->sdio_func[i]) { ++ sdio_remove_func(host->card->sdio_func[i]); ++ host->card->sdio_func[i] = NULL; ++ } ++ } ++ ++ mmc_remove_card(host->card); ++ host->card = NULL; ++} ++ ++/* ++ * Card detection callback from host. ++ */ ++static void mmc_sdio_detect(struct mmc_host *host) ++{ ++ int err; ++ ++ BUG_ON(!host); ++ BUG_ON(!host->card); ++ ++ mmc_claim_host(host); ++ ++ /* ++ * Just check if our card has been removed. ++ */ ++ err = mmc_select_card(host->card); ++ ++ mmc_release_host(host); ++ ++ if (err) { ++ mmc_sdio_remove(host); ++ ++ mmc_claim_host(host); ++ mmc_detach_bus(host); ++ mmc_release_host(host); ++ } ++} ++ ++ ++static const struct mmc_bus_ops mmc_sdio_ops = { ++ .remove = mmc_sdio_remove, ++ .detect = mmc_sdio_detect, ++}; ++ ++ ++/* ++ * Starting point for SDIO card init. ++ */ ++int mmc_attach_sdio(struct mmc_host *host, u32 ocr) ++{ ++ int err; ++ int i, funcs; ++ struct mmc_card *card; ++ ++ BUG_ON(!host); ++ WARN_ON(!host->claimed); ++ ++ mmc_attach_bus(host, &mmc_sdio_ops); ++ ++ /* ++ * Sanity check the voltages that the card claims to ++ * support. ++ */ ++ if (ocr & 0x7F) { ++ printk(KERN_WARNING "%s: card claims to support voltages " ++ "below the defined range. These will be ignored.\n", ++ mmc_hostname(host)); ++ ocr &= ~0x7F; ++ } ++ ++ if (ocr & MMC_VDD_165_195) { ++ printk(KERN_WARNING "%s: SDIO card claims to support the " ++ "incompletely defined 'low voltage range'. This " ++ "will be ignored.\n", mmc_hostname(host)); ++ ocr &= ~MMC_VDD_165_195; ++ } ++ ++ host->ocr = mmc_select_voltage(host, ocr); ++ ++ /* ++ * Can we support the voltage(s) of the card(s)? ++ */ ++ if (!host->ocr) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ /* ++ * Inform the card of the voltage ++ */ ++ err = mmc_send_io_op_cond(host, host->ocr, &ocr); ++ if (err) ++ goto err; ++ ++ /* ++ * For SPI, enable CRC as appropriate. ++ */ ++ if (mmc_host_is_spi(host)) { ++ err = mmc_spi_set_crc(host, use_spi_crc); ++ if (err) ++ goto err; ++ } ++ ++ /* ++ * The number of functions on the card is encoded inside ++ * the ocr. ++ */ ++ funcs = (ocr & 0x70000000) >> 28; ++ ++ /* ++ * Allocate card structure. ++ */ ++ card = mmc_alloc_card(host); ++ if (IS_ERR(card)) { ++ err = PTR_ERR(card); ++ goto err; ++ } ++ ++ card->type = MMC_TYPE_SDIO; ++ card->sdio_funcs = funcs; ++ ++ host->card = card; ++ ++ /* ++ * For native busses: set card RCA and quit open drain mode. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_send_relative_addr(host, &card->rca); ++ if (err) ++ goto remove; ++ ++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); ++ } ++ ++ /* ++ * Select card, as all following commands rely on that. ++ */ ++ if (!mmc_host_is_spi(host)) { ++ err = mmc_select_card(card); ++ if (err) ++ goto remove; ++ } ++ ++ /* ++ * Read the common registers. ++ */ ++ err = sdio_read_cccr(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * Read the common CIS tuples. ++ */ ++ err = sdio_read_common_cis(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * No support for high-speed yet, so just set ++ * the card's maximum speed. ++ */ ++ mmc_set_clock(host, card->cis.max_dtr); ++ ++ /* ++ * Switch to wider bus (if supported). ++ */ ++ err = sdio_enable_wide(card); ++ if (err) ++ goto remove; ++ ++ /* ++ * Initialize (but don't add) all present functions. ++ */ ++ for (i = 0;i < funcs;i++) { ++ err = sdio_init_func(host->card, i + 1); ++ if (err) ++ goto remove; ++ } ++ ++ mmc_release_host(host); ++ ++ /* ++ * First add the card to the driver model... ++ */ ++ err = mmc_add_card(host->card); ++ if (err) ++ goto remove_added; ++ ++ /* ++ * ...then the SDIO functions. ++ */ ++ for (i = 0;i < funcs;i++) { ++ err = sdio_add_func(host->card->sdio_func[i]); ++ if (err) ++ goto remove_added; ++ } ++ ++ return 0; ++ ++ ++remove_added: ++ /* Remove without lock if the device has been added. */ ++ mmc_sdio_remove(host); ++ mmc_claim_host(host); ++remove: ++ /* And with lock if it hasn't been added. */ ++ if (host->card) ++ mmc_sdio_remove(host); ++err: ++ mmc_detach_bus(host); ++ mmc_release_host(host); ++ ++ printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n", ++ mmc_hostname(host), err); ++ ++ return err; ++} ++ +--- /dev/null ++++ b/drivers/mmc/core/sdio_bus.c +@@ -0,0 +1,265 @@ ++/* ++ * linux/drivers/mmc/core/sdio_bus.c ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ * ++ * SDIO function driver model ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "sdio_cis.h" ++#include "sdio_bus.h" ++ ++#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) ++#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) ++ ++/* show configuration fields */ ++#define sdio_config_attr(field, format_string) \ ++static ssize_t \ ++field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct sdio_func *func; \ ++ \ ++ func = dev_to_sdio_func (dev); \ ++ return sprintf (buf, format_string, func->field); \ ++} ++ ++sdio_config_attr(class, "0x%02x\n"); ++sdio_config_attr(vendor, "0x%04x\n"); ++sdio_config_attr(device, "0x%04x\n"); ++ ++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct sdio_func *func = dev_to_sdio_func (dev); ++ ++ return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n", ++ func->class, func->vendor, func->device); ++} ++ ++static struct device_attribute sdio_dev_attrs[] = { ++ __ATTR_RO(class), ++ __ATTR_RO(vendor), ++ __ATTR_RO(device), ++ __ATTR_RO(modalias), ++ __ATTR_NULL, ++}; ++ ++static const struct sdio_device_id *sdio_match_one(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class) ++ return NULL; ++ if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor) ++ return NULL; ++ if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device) ++ return NULL; ++ return id; ++} ++ ++static const struct sdio_device_id *sdio_match_device(struct sdio_func *func, ++ struct sdio_driver *sdrv) ++{ ++ const struct sdio_device_id *ids; ++ ++ ids = sdrv->id_table; ++ ++ if (ids) { ++ while (ids->class || ids->vendor || ids->device) { ++ if (sdio_match_one(func, ids)) ++ return ids; ++ ids++; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int sdio_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ struct sdio_driver *sdrv = to_sdio_driver(drv); ++ ++ if (sdio_match_device(func, sdrv)) ++ return 1; ++ ++ return 0; ++} ++ ++static int ++sdio_bus_uevent(struct device *dev, char **envp, ++ int num_envp, char *buffer, int buffer_size) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ int i = 0, len = 0; ++ ++ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, ++ "SDIO_CLASS=%02X", func->class)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, ++ "SDIO_ID=%04X:%04X", func->vendor, func->device)) ++ return -ENOMEM; ++ ++ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, ++ "MODALIAS=sdio:c%02Xv%04Xd%04X", ++ func->class, func->vendor, func->device)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static int sdio_bus_probe(struct device *dev) ++{ ++ struct sdio_driver *drv = to_sdio_driver(dev->driver); ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ const struct sdio_device_id *id; ++ int ret; ++ ++ id = sdio_match_device(func, drv); ++ if (!id) ++ return -ENODEV; ++ ++ /* Set the default block size so the driver is sure it's something ++ * sensible. */ ++ sdio_claim_host(func); ++ ret = sdio_set_block_size(func, 0); ++ sdio_release_host(func); ++ if (ret) ++ return ret; ++ ++ return drv->probe(func, id); ++} ++ ++static int sdio_bus_remove(struct device *dev) ++{ ++ struct sdio_driver *drv = to_sdio_driver(dev->driver); ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ ++ drv->remove(func); ++ ++ if (func->irq_handler) { ++ printk(KERN_WARNING "WARNING: driver %s did not remove " ++ "its interrupt handler!\n", drv->name); ++ sdio_claim_host(func); ++ sdio_release_irq(func); ++ sdio_release_host(func); ++ } ++ ++ return 0; ++} ++ ++static struct bus_type sdio_bus_type = { ++ .name = "sdio", ++ .dev_attrs = sdio_dev_attrs, ++ .match = sdio_bus_match, ++ .uevent = sdio_bus_uevent, ++ .probe = sdio_bus_probe, ++ .remove = sdio_bus_remove, ++}; ++ ++int sdio_register_bus(void) ++{ ++ return bus_register(&sdio_bus_type); ++} ++ ++void sdio_unregister_bus(void) ++{ ++ bus_unregister(&sdio_bus_type); ++} ++ ++/** ++ * sdio_register_driver - register a function driver ++ * @drv: SDIO function driver ++ */ ++int sdio_register_driver(struct sdio_driver *drv) ++{ ++ drv->drv.name = drv->name; ++ drv->drv.bus = &sdio_bus_type; ++ return driver_register(&drv->drv); ++} ++EXPORT_SYMBOL_GPL(sdio_register_driver); ++ ++/** ++ * sdio_unregister_driver - unregister a function driver ++ * @drv: SDIO function driver ++ */ ++void sdio_unregister_driver(struct sdio_driver *drv) ++{ ++ drv->drv.bus = &sdio_bus_type; ++ driver_unregister(&drv->drv); ++} ++EXPORT_SYMBOL_GPL(sdio_unregister_driver); ++ ++static void sdio_release_func(struct device *dev) ++{ ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ ++ sdio_free_func_cis(func); ++ ++ if (func->info) ++ kfree(func->info); ++ ++ kfree(func); ++} ++ ++/* ++ * Allocate and initialise a new SDIO function structure. ++ */ ++struct sdio_func *sdio_alloc_func(struct mmc_card *card) ++{ ++ struct sdio_func *func; ++ ++ func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL); ++ if (!func) ++ return ERR_PTR(-ENOMEM); ++ ++ func->card = card; ++ ++ device_initialize(&func->dev); ++ ++ func->dev.parent = &card->dev; ++ func->dev.bus = &sdio_bus_type; ++ func->dev.release = sdio_release_func; ++ ++ return func; ++} ++ ++/* ++ * Register a new SDIO function with the driver model. ++ */ ++int sdio_add_func(struct sdio_func *func) ++{ ++ int ret; ++ ++ snprintf(func->dev.bus_id, sizeof(func->dev.bus_id), ++ "%s:%d", mmc_card_id(func->card), func->num); ++ ++ ret = device_add(&func->dev); ++ if (ret == 0) ++ sdio_func_set_present(func); ++ ++ return ret; ++} ++ ++/* ++ * Unregister a SDIO function with the driver model, and ++ * (eventually) free it. ++ */ ++void sdio_remove_func(struct sdio_func *func) ++{ ++ if (sdio_func_present(func)) ++ device_del(&func->dev); ++ ++ put_device(&func->dev); ++} ++ +--- /dev/null ++++ b/drivers/mmc/core/sdio_bus.h +@@ -0,0 +1,22 @@ ++/* ++ * linux/drivers/mmc/core/sdio_bus.h ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++#ifndef _MMC_CORE_SDIO_BUS_H ++#define _MMC_CORE_SDIO_BUS_H ++ ++struct sdio_func *sdio_alloc_func(struct mmc_card *card); ++int sdio_add_func(struct sdio_func *func); ++void sdio_remove_func(struct sdio_func *func); ++ ++int sdio_register_bus(void); ++void sdio_unregister_bus(void); ++ ++#endif ++ +--- /dev/null ++++ b/drivers/mmc/core/sdio_cis.c +@@ -0,0 +1,346 @@ ++/* ++ * linux/drivers/mmc/core/sdio_cis.c ++ * ++ * Author: Nicolas Pitre ++ * Created: June 11, 2007 ++ * Copyright: MontaVista Software Inc. ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "sdio_cis.h" ++#include "sdio_ops.h" ++ ++static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ unsigned i, nr_strings; ++ char **buffer, *string; ++ ++ buf += 2; ++ size -= 2; ++ ++ nr_strings = 0; ++ for (i = 0; i < size; i++) { ++ if (buf[i] == 0xff) ++ break; ++ if (buf[i] == 0) ++ nr_strings++; ++ } ++ ++ if (buf[i-1] != '\0') { ++ printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n"); ++ return 0; ++ } ++ ++ size = i; ++ ++ buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL); ++ if (!buffer) ++ return -ENOMEM; ++ ++ string = (char*)(buffer + nr_strings); ++ ++ for (i = 0; i < nr_strings; i++) { ++ buffer[i] = string; ++ strcpy(string, buf); ++ string += strlen(string) + 1; ++ buf += strlen(buf) + 1; ++ } ++ ++ if (func) { ++ func->num_info = nr_strings; ++ func->info = (const char**)buffer; ++ } else { ++ card->num_info = nr_strings; ++ card->info = (const char**)buffer; ++ } ++ ++ return 0; ++} ++ ++static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ unsigned int vendor, device; ++ ++ /* TPLMID_MANF */ ++ vendor = buf[0] | (buf[1] << 8); ++ ++ /* TPLMID_CARD */ ++ device = buf[2] | (buf[3] << 8); ++ ++ if (func) { ++ func->vendor = vendor; ++ func->device = device; ++ } else { ++ card->cis.vendor = vendor; ++ card->cis.device = device; ++ } ++ ++ return 0; ++} ++ ++static const unsigned char speed_val[16] = ++ { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; ++static const unsigned int speed_unit[8] = ++ { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; ++ ++static int cistpl_funce_common(struct mmc_card *card, ++ const unsigned char *buf, unsigned size) ++{ ++ if (size < 0x04 || buf[0] != 0) ++ return -EINVAL; ++ ++ /* TPLFE_FN0_BLK_SIZE */ ++ card->cis.blksize = buf[1] | (buf[2] << 8); ++ ++ /* TPLFE_MAX_TRAN_SPEED */ ++ card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] * ++ speed_unit[buf[3] & 7]; ++ ++ return 0; ++} ++ ++static int cistpl_funce_func(struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ unsigned vsn; ++ unsigned min_size; ++ ++ vsn = func->card->cccr.sdio_vsn; ++ min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; ++ ++ if (size < min_size || buf[0] != 1) ++ return -EINVAL; ++ ++ /* TPLFE_MAX_BLK_SIZE */ ++ func->max_blksize = buf[12] | (buf[13] << 8); ++ ++ return 0; ++} ++ ++static int cistpl_funce(struct mmc_card *card, struct sdio_func *func, ++ const unsigned char *buf, unsigned size) ++{ ++ int ret; ++ ++ /* ++ * There should be two versions of the CISTPL_FUNCE tuple, ++ * one for the common CIS (function 0) and a version used by ++ * the individual function's CIS (1-7). Yet, the later has a ++ * different length depending on the SDIO spec version. ++ */ ++ if (func) ++ ret = cistpl_funce_func(func, buf, size); ++ else ++ ret = cistpl_funce_common(card, buf, size); ++ ++ if (ret) { ++ printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u " ++ "type %u\n", mmc_hostname(card->host), size, buf[0]); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *, ++ const unsigned char *, unsigned); ++ ++struct cis_tpl { ++ unsigned char code; ++ unsigned char min_size; ++ tpl_parse_t *parse; ++}; ++ ++static const struct cis_tpl cis_tpl_list[] = { ++ { 0x15, 3, cistpl_vers_1 }, ++ { 0x20, 4, cistpl_manfid }, ++ { 0x21, 2, /* cistpl_funcid */ }, ++ { 0x22, 0, cistpl_funce }, ++}; ++ ++static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) ++{ ++ int ret; ++ struct sdio_func_tuple *this, **prev; ++ unsigned i, ptr = 0; ++ ++ /* ++ * Note that this works for the common CIS (function number 0) as ++ * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS ++ * have the same offset. ++ */ ++ for (i = 0; i < 3; i++) { ++ unsigned char x, fn; ++ ++ if (func) ++ fn = func->num; ++ else ++ fn = 0; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, ++ SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); ++ if (ret) ++ return ret; ++ ptr |= x << (i * 8); ++ } ++ ++ if (func) ++ prev = &func->tuples; ++ else ++ prev = &card->tuples; ++ ++ BUG_ON(*prev); ++ ++ do { ++ unsigned char tpl_code, tpl_link; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code); ++ if (ret) ++ break; ++ ++ /* 0xff means we're done */ ++ if (tpl_code == 0xff) ++ break; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); ++ if (ret) ++ break; ++ ++ this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); ++ if (!this) ++ return -ENOMEM; ++ ++ for (i = 0; i < tpl_link; i++) { ++ ret = mmc_io_rw_direct(card, 0, 0, ++ ptr + i, 0, &this->data[i]); ++ if (ret) ++ break; ++ } ++ if (ret) { ++ kfree(this); ++ break; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++) ++ if (cis_tpl_list[i].code == tpl_code) ++ break; ++ if (i >= ARRAY_SIZE(cis_tpl_list)) { ++ /* this tuple is unknown to the core */ ++ this->next = NULL; ++ this->code = tpl_code; ++ this->size = tpl_link; ++ *prev = this; ++ prev = &this->next; ++ printk(KERN_DEBUG ++ "%s: queuing CIS tuple 0x%02x length %u\n", ++ mmc_hostname(card->host), tpl_code, tpl_link); ++ } else { ++ const struct cis_tpl *tpl = cis_tpl_list + i; ++ if (tpl_link < tpl->min_size) { ++ printk(KERN_ERR ++ "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n", ++ mmc_hostname(card->host), ++ tpl_code, tpl_link, tpl->min_size); ++ ret = -EINVAL; ++ } else if (tpl->parse) { ++ ret = tpl->parse(card, func, ++ this->data, tpl_link); ++ } ++ kfree(this); ++ } ++ ++ ptr += tpl_link; ++ } while (!ret); ++ ++ /* ++ * Link in all unknown tuples found in the common CIS so that ++ * drivers don't have to go digging in two places. ++ */ ++ if (func) ++ *prev = card->tuples; ++ ++ return ret; ++} ++ ++int sdio_read_common_cis(struct mmc_card *card) ++{ ++ return sdio_read_cis(card, NULL); ++} ++ ++void sdio_free_common_cis(struct mmc_card *card) ++{ ++ struct sdio_func_tuple *tuple, *victim; ++ ++ tuple = card->tuples; ++ ++ while (tuple) { ++ victim = tuple; ++ tuple = tuple->next; ++ kfree(victim); ++ } ++ ++ card->tuples = NULL; ++} ++ ++int sdio_read_func_cis(struct sdio_func *func) ++{ ++ int ret; ++ ++ ret = sdio_read_cis(func->card, func); ++ if (ret) ++ return ret; ++ ++ /* ++ * Since we've linked to tuples in the card structure, ++ * we must make sure we have a reference to it. ++ */ ++ get_device(&func->card->dev); ++ ++ /* ++ * Vendor/device id is optional for function CIS, so ++ * copy it from the card structure as needed. ++ */ ++ if (func->vendor == 0) { ++ func->vendor = func->card->cis.vendor; ++ func->device = func->card->cis.device; ++ } ++ ++ return 0; ++} ++ ++void sdio_free_func_cis(struct sdio_func *func) ++{ ++ struct sdio_func_tuple *tuple, *victim; ++ ++ tuple = func->tuples; ++ ++ while (tuple && tuple != func->card->tuples) { ++ victim = tuple; ++ tuple = tuple->next; ++ kfree(victim); ++ } ++ ++ func->tuples = NULL; ++ ++ /* ++ * We have now removed the link to the tuples in the ++ * card structure, so remove the reference. ++ */ ++ put_device(&func->card->dev); ++} ++ +--- /dev/null ++++ b/drivers/mmc/core/sdio_cis.h +@@ -0,0 +1,23 @@ ++/* ++ * linux/drivers/mmc/core/sdio_cis.h ++ * ++ * Author: Nicolas Pitre ++ * Created: June 11, 2007 ++ * Copyright: MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef _MMC_SDIO_CIS_H ++#define _MMC_SDIO_CIS_H ++ ++int sdio_read_common_cis(struct mmc_card *card); ++void sdio_free_common_cis(struct mmc_card *card); ++ ++int sdio_read_func_cis(struct sdio_func *func); ++void sdio_free_func_cis(struct sdio_func *func); ++ ++#endif +--- /dev/null ++++ b/drivers/mmc/core/sdio_io.c +@@ -0,0 +1,548 @@ ++/* ++ * linux/drivers/mmc/core/sdio_io.c ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "sdio_ops.h" ++ ++/** ++ * sdio_claim_host - exclusively claim a bus for a certain SDIO function ++ * @func: SDIO function that will be accessed ++ * ++ * Claim a bus for a set of operations. The SDIO function given ++ * is used to figure out which bus is relevant. ++ */ ++void sdio_claim_host(struct sdio_func *func) ++{ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ mmc_claim_host(func->card->host); ++} ++EXPORT_SYMBOL_GPL(sdio_claim_host); ++ ++/** ++ * sdio_release_host - release a bus for a certain SDIO function ++ * @func: SDIO function that was accessed ++ * ++ * Release a bus, allowing others to claim the bus for their ++ * operations. ++ */ ++void sdio_release_host(struct sdio_func *func) ++{ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ mmc_release_host(func->card->host); ++} ++EXPORT_SYMBOL_GPL(sdio_release_host); ++ ++/** ++ * sdio_enable_func - enables a SDIO function for usage ++ * @func: SDIO function to enable ++ * ++ * Powers up and activates a SDIO function so that register ++ * access is possible. ++ */ ++int sdio_enable_func(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ unsigned long timeout; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func)); ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); ++ if (ret) ++ goto err; ++ ++ reg |= 1 << func->num; ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); ++ if (ret) ++ goto err; ++ ++ /* ++ * FIXME: This should timeout based on information in the CIS, ++ * but we don't have card to parse that yet. ++ */ ++ timeout = jiffies + HZ; ++ ++ while (1) { ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®); ++ if (ret) ++ goto err; ++ if (reg & (1 << func->num)) ++ break; ++ ret = -ETIME; ++ if (time_after(jiffies, timeout)) ++ goto err; ++ } ++ ++ pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func)); ++ ++ return 0; ++ ++err: ++ pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func)); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdio_enable_func); ++ ++/** ++ * sdio_disable_func - disable a SDIO function ++ * @func: SDIO function to disable ++ * ++ * Powers down and deactivates a SDIO function. Register access ++ * to this function will fail until the function is reenabled. ++ */ ++int sdio_disable_func(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func)); ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); ++ if (ret) ++ goto err; ++ ++ reg &= ~(1 << func->num); ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); ++ if (ret) ++ goto err; ++ ++ pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func)); ++ ++ return 0; ++ ++err: ++ pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func)); ++ return -EIO; ++} ++EXPORT_SYMBOL_GPL(sdio_disable_func); ++ ++/** ++ * sdio_set_block_size - set the block size of an SDIO function ++ * @func: SDIO function to change ++ * @blksz: new block size or 0 to use the default. ++ * ++ * The default block size is the largest supported by both the function ++ * and the host, with a maximum of 512 to ensure that arbitrarily sized ++ * data transfer use the optimal (least) number of commands. ++ * ++ * A driver may call this to override the default block size set by the ++ * core. This can be used to set a block size greater than the maximum ++ * that reported by the card; it is the driver's responsibility to ensure ++ * it uses a value that the card supports. ++ * ++ * Returns 0 on success, -EINVAL if the host does not support the ++ * requested block size, or -EIO (etc.) if one of the resultant FBR block ++ * size register writes failed. ++ * ++ */ ++int sdio_set_block_size(struct sdio_func *func, unsigned blksz) ++{ ++ int ret; ++ ++ if (blksz > func->card->host->max_blk_size) ++ return -EINVAL; ++ ++ if (blksz == 0) { ++ blksz = min(min( ++ func->max_blksize, ++ func->card->host->max_blk_size), ++ 512u); ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE, ++ blksz & 0xff, NULL); ++ if (ret) ++ return ret; ++ ret = mmc_io_rw_direct(func->card, 1, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1, ++ (blksz >> 8) & 0xff, NULL); ++ if (ret) ++ return ret; ++ func->cur_blksize = blksz; ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(sdio_set_block_size); ++ ++/* Split an arbitrarily sized data transfer into several ++ * IO_RW_EXTENDED commands. */ ++static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, ++ unsigned addr, int incr_addr, u8 *buf, unsigned size) ++{ ++ unsigned remainder = size; ++ unsigned max_blocks; ++ int ret; ++ ++ /* Do the bulk of the transfer using block mode (if supported). */ ++ if (func->card->cccr.multi_block) { ++ /* Blocks per command is limited by host count, host transfer ++ * size (we only use a single sg entry) and the maximum for ++ * IO_RW_EXTENDED of 511 blocks. */ ++ max_blocks = min(min( ++ func->card->host->max_blk_count, ++ func->card->host->max_seg_size / func->cur_blksize), ++ 511u); ++ ++ while (remainder > func->cur_blksize) { ++ unsigned blocks; ++ ++ blocks = remainder / func->cur_blksize; ++ if (blocks > max_blocks) ++ blocks = max_blocks; ++ size = blocks * func->cur_blksize; ++ ++ ret = mmc_io_rw_extended(func->card, write, ++ func->num, addr, incr_addr, buf, ++ blocks, func->cur_blksize); ++ if (ret) ++ return ret; ++ ++ remainder -= size; ++ buf += size; ++ if (incr_addr) ++ addr += size; ++ } ++ } ++ ++ /* Write the remainder using byte mode. */ ++ while (remainder > 0) { ++ size = remainder; ++ if (size > func->cur_blksize) ++ size = func->cur_blksize; ++ if (size > 512) ++ size = 512; /* maximum size for byte mode */ ++ ++ ret = mmc_io_rw_extended(func->card, write, func->num, addr, ++ incr_addr, buf, 1, size); ++ if (ret) ++ return ret; ++ ++ remainder -= size; ++ buf += size; ++ if (incr_addr) ++ addr += size; ++ } ++ return 0; ++} ++ ++/** ++ * sdio_readb - read a single byte from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a single byte from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned char sdio_readb(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_readb); ++ ++/** ++ * sdio_writeb - write a single byte to a SDIO function ++ * @func: SDIO function to access ++ * @b: byte to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a single byte to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ BUG_ON(!func); ++ ++ ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writeb); ++ ++/** ++ * sdio_memcpy_fromio - read a chunk of memory from a SDIO function ++ * @func: SDIO function to access ++ * @dst: buffer to store the data ++ * @addr: address to begin reading from ++ * @count: number of bytes to read ++ * ++ * Reads from the address space of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_memcpy_fromio(struct sdio_func *func, void *dst, ++ unsigned int addr, int count) ++{ ++ return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count); ++} ++EXPORT_SYMBOL_GPL(sdio_memcpy_fromio); ++ ++/** ++ * sdio_memcpy_toio - write a chunk of memory to a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to start writing to ++ * @src: buffer that contains the data to write ++ * @count: number of bytes to write ++ * ++ * Writes to the address space of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, ++ void *src, int count) ++{ ++ return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count); ++} ++EXPORT_SYMBOL_GPL(sdio_memcpy_toio); ++ ++/** ++ * sdio_readsb - read from a FIFO on a SDIO function ++ * @func: SDIO function to access ++ * @dst: buffer to store the data ++ * @addr: address of (single byte) FIFO ++ * @count: number of bytes to read ++ * ++ * Reads from the specified FIFO of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, ++ int count) ++{ ++ return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count); ++} ++ ++EXPORT_SYMBOL_GPL(sdio_readsb); ++ ++/** ++ * sdio_writesb - write to a FIFO of a SDIO function ++ * @func: SDIO function to access ++ * @addr: address of (single byte) FIFO ++ * @src: buffer that contains the data to write ++ * @count: number of bytes to write ++ * ++ * Writes to the specified FIFO of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src, ++ int count) ++{ ++ return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count); ++} ++EXPORT_SYMBOL_GPL(sdio_writesb); ++ ++/** ++ * sdio_readw - read a 16 bit integer from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a 16 bit integer from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xffff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned short sdio_readw(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFFFF; ++ } ++ ++ return le16_to_cpu(*(u16*)func->tmpbuf); ++} ++EXPORT_SYMBOL_GPL(sdio_readw); ++ ++/** ++ * sdio_writew - write a 16 bit integer to a SDIO function ++ * @func: SDIO function to access ++ * @b: integer to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a 16 bit integer to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ *(u16*)func->tmpbuf = cpu_to_le16(b); ++ ++ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writew); ++ ++/** ++ * sdio_readl - read a 32 bit integer from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a 32 bit integer from the address space of a given SDIO ++ * function. If there is a problem reading the address, ++ * 0xffffffff is returned and @err_ret will contain the error ++ * code. ++ */ ++unsigned long sdio_readl(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFFFFFFFF; ++ } ++ ++ return le32_to_cpu(*(u32*)func->tmpbuf); ++} ++EXPORT_SYMBOL_GPL(sdio_readl); ++ ++/** ++ * sdio_writel - write a 32 bit integer to a SDIO function ++ * @func: SDIO function to access ++ * @b: integer to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a 32 bit integer to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ *(u32*)func->tmpbuf = cpu_to_le32(b); ++ ++ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writel); ++ ++/** ++ * sdio_f0_readb - read a single byte from SDIO function 0 ++ * @func: an SDIO function of the card ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a single byte from the address space of SDIO function 0. ++ * If there is a problem reading the address, 0xff is returned ++ * and @err_ret will contain the error code. ++ */ ++unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_f0_readb); ++ ++/** ++ * sdio_f0_writeb - write a single byte to SDIO function 0 ++ * @func: an SDIO function of the card ++ * @b: byte to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a single byte to the address space of SDIO function 0. ++ * @err_ret will contain the status of the actual transfer. ++ * ++ * Only writes to the vendor specific CCCR registers (0xF0 - ++ * 0xFF) are permiited; @err_ret will be set to -EINVAL for * ++ * writes outside this range. ++ */ ++void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ BUG_ON(!func); ++ ++ if (addr < 0xF0 || addr > 0xFF) { ++ if (err_ret) ++ *err_ret = -EINVAL; ++ return; ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_f0_writeb); +--- /dev/null ++++ b/drivers/mmc/core/sdio_irq.c +@@ -0,0 +1,267 @@ ++/* ++ * linux/drivers/mmc/core/sdio_irq.c ++ * ++ * Author: Nicolas Pitre ++ * Created: June 18, 2007 ++ * Copyright: MontaVista Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sdio_ops.h" ++ ++static int process_sdio_pending_irqs(struct mmc_card *card) ++{ ++ int i, ret, count; ++ unsigned char pending; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); ++ if (ret) { ++ printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", ++ mmc_card_id(card), ret); ++ return ret; ++ } ++ ++ count = 0; ++ for (i = 1; i <= 7; i++) { ++ if (pending & (1 << i)) { ++ struct sdio_func *func = card->sdio_func[i - 1]; ++ if (!func) { ++ printk(KERN_WARNING "%s: pending IRQ for " ++ "non-existant function\n", ++ mmc_card_id(card)); ++ ret = -EINVAL; ++ } else if (func->irq_handler) { ++ func->irq_handler(func); ++ count++; ++ } else { ++ printk(KERN_WARNING "%s: pending IRQ with no handler\n", ++ sdio_func_id(func)); ++ ret = -EINVAL; ++ } ++ } ++ } ++ ++ if (count) ++ return count; ++ ++ return ret; ++} ++ ++static int sdio_irq_thread(void *_host) ++{ ++ struct mmc_host *host = _host; ++ struct sched_param param = { .sched_priority = 1 }; ++ unsigned long period, idle_period; ++ int ret; ++ ++ sched_setscheduler(current, SCHED_FIFO, ¶m); ++ ++ /* ++ * We want to allow for SDIO cards to work even on non SDIO ++ * aware hosts. One thing that non SDIO host cannot do is ++ * asynchronous notification of pending SDIO card interrupts ++ * hence we poll for them in that case. ++ */ ++ idle_period = msecs_to_jiffies(10); ++ period = (host->caps & MMC_CAP_SDIO_IRQ) ? ++ MAX_SCHEDULE_TIMEOUT : idle_period; ++ ++ pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", ++ mmc_hostname(host), period); ++ ++ do { ++ /* ++ * We claim the host here on drivers behalf for a couple ++ * reasons: ++ * ++ * 1) it is already needed to retrieve the CCCR_INTx; ++ * 2) we want the driver(s) to clear the IRQ condition ASAP; ++ * 3) we need to control the abort condition locally. ++ * ++ * Just like traditional hard IRQ handlers, we expect SDIO ++ * IRQ handlers to be quick and to the point, so that the ++ * holding of the host lock does not cover too much work ++ * that doesn't require that lock to be held. ++ */ ++ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); ++ if (ret) ++ break; ++ ret = process_sdio_pending_irqs(host->card); ++ mmc_release_host(host); ++ ++ /* ++ * Give other threads a chance to run in the presence of ++ * errors. FIXME: determine if due to card removal and ++ * possibly exit this thread if so. ++ */ ++ if (ret < 0) ++ ssleep(1); ++ ++ /* ++ * Adaptive polling frequency based on the assumption ++ * that an interrupt will be closely followed by more. ++ * This has a substantial benefit for network devices. ++ */ ++ if (!(host->caps & MMC_CAP_SDIO_IRQ)) { ++ if (ret > 0) ++ period /= 2; ++ else { ++ period++; ++ if (period > idle_period) ++ period = idle_period; ++ } ++ } ++ ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ if (host->caps & MMC_CAP_SDIO_IRQ) ++ host->ops->enable_sdio_irq(host, 1); ++ if (!kthread_should_stop()) ++ schedule_timeout(period); ++ set_task_state(current, TASK_RUNNING); ++ } while (!kthread_should_stop()); ++ ++ if (host->caps & MMC_CAP_SDIO_IRQ) ++ host->ops->enable_sdio_irq(host, 0); ++ ++ pr_debug("%s: IRQ thread exiting with code %d\n", ++ mmc_hostname(host), ret); ++ ++ return ret; ++} ++ ++static int sdio_card_irq_get(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ ++ WARN_ON(!host->claimed); ++ ++ if (!host->sdio_irqs++) { ++ atomic_set(&host->sdio_irq_thread_abort, 0); ++ host->sdio_irq_thread = ++ kthread_run(sdio_irq_thread, host, "ksdiorqd"); ++ if (IS_ERR(host->sdio_irq_thread)) { ++ int err = PTR_ERR(host->sdio_irq_thread); ++ host->sdio_irqs--; ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static int sdio_card_irq_put(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ ++ WARN_ON(!host->claimed); ++ BUG_ON(host->sdio_irqs < 1); ++ ++ if (!--host->sdio_irqs) { ++ atomic_set(&host->sdio_irq_thread_abort, 1); ++ kthread_stop(host->sdio_irq_thread); ++ } ++ ++ return 0; ++} ++ ++/** ++ * sdio_claim_irq - claim the IRQ for a SDIO function ++ * @func: SDIO function ++ * @handler: IRQ handler callback ++ * ++ * Claim and activate the IRQ for the given SDIO function. The provided ++ * handler will be called when that IRQ is asserted. The host is always ++ * claimed already when the handler is called so the handler must not ++ * call sdio_claim_host() nor sdio_release_host(). ++ */ ++int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); ++ ++ if (func->irq_handler) { ++ pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); ++ return -EBUSY; ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); ++ if (ret) ++ return ret; ++ ++ reg |= 1 << func->num; ++ ++ reg |= 1; /* Master interrupt enable */ ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); ++ if (ret) ++ return ret; ++ ++ func->irq_handler = handler; ++ ret = sdio_card_irq_get(func->card); ++ if (ret) ++ func->irq_handler = NULL; ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdio_claim_irq); ++ ++/** ++ * sdio_release_irq - release the IRQ for a SDIO function ++ * @func: SDIO function ++ * ++ * Disable and release the IRQ for the given SDIO function. ++ */ ++int sdio_release_irq(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func)); ++ ++ if (func->irq_handler) { ++ func->irq_handler = NULL; ++ sdio_card_irq_put(func->card); ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); ++ if (ret) ++ return ret; ++ ++ reg &= ~(1 << func->num); ++ ++ /* Disable master interrupt with the last function interrupt */ ++ if (!(reg & 0xFE)) ++ reg = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(sdio_release_irq); ++ +--- /dev/null ++++ b/drivers/mmc/core/sdio_ops.c +@@ -0,0 +1,175 @@ ++/* ++ * linux/drivers/mmc/sdio_ops.c ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++ ++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ++{ ++ struct mmc_command cmd; ++ int i, err = 0; ++ ++ BUG_ON(!host); ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = SD_IO_SEND_OP_COND; ++ cmd.arg = ocr; ++ cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; ++ ++ for (i = 100; i; i--) { ++ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); ++ if (err) ++ break; ++ ++ /* if we're just probing, do a single pass */ ++ if (ocr == 0) ++ break; ++ ++ /* otherwise wait until reset completes */ ++ if (mmc_host_is_spi(host)) { ++ /* ++ * Both R1_SPI_IDLE and MMC_CARD_BUSY indicate ++ * an initialized card under SPI, but some cards ++ * (Marvell's) only behave when looking at this ++ * one. ++ */ ++ if (cmd.resp[1] & MMC_CARD_BUSY) ++ break; ++ } else { ++ if (cmd.resp[0] & MMC_CARD_BUSY) ++ break; ++ } ++ ++ err = -ETIMEDOUT; ++ ++ mmc_delay(10); ++ } ++ ++ if (rocr) ++ *rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0]; ++ ++ return err; ++} ++ ++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, u8 in, u8* out) ++{ ++ struct mmc_command cmd; ++ int err; ++ ++ BUG_ON(!card); ++ BUG_ON(fn > 7); ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ ++ cmd.opcode = SD_IO_RW_DIRECT; ++ cmd.arg = write ? 0x80000000 : 0x00000000; ++ cmd.arg |= fn << 28; ++ cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; ++ cmd.arg |= addr << 9; ++ cmd.arg |= in; ++ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; ++ ++ err = mmc_wait_for_cmd(card->host, &cmd, 0); ++ if (err) ++ return err; ++ ++ if (mmc_host_is_spi(card->host)) { ++ /* host driver already reported errors */ ++ } else { ++ if (cmd.resp[0] & R5_ERROR) ++ return -EIO; ++ if (cmd.resp[0] & R5_FUNCTION_NUMBER) ++ return -EINVAL; ++ if (cmd.resp[0] & R5_OUT_OF_RANGE) ++ return -ERANGE; ++ } ++ ++ if (out) { ++ if (mmc_host_is_spi(card->host)) ++ *out = (cmd.resp[0] >> 8) & 0xFF; ++ else ++ *out = cmd.resp[0] & 0xFF; ++ } ++ ++ return 0; ++} ++ ++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz) ++{ ++ struct mmc_request mrq; ++ struct mmc_command cmd; ++ struct mmc_data data; ++ struct scatterlist sg; ++ ++ BUG_ON(!card); ++ BUG_ON(fn > 7); ++ BUG_ON(blocks == 1 && blksz > 512); ++ WARN_ON(blocks == 0); ++ WARN_ON(blksz == 0); ++ ++ memset(&mrq, 0, sizeof(struct mmc_request)); ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ memset(&data, 0, sizeof(struct mmc_data)); ++ ++ mrq.cmd = &cmd; ++ mrq.data = &data; ++ ++ cmd.opcode = SD_IO_RW_EXTENDED; ++ cmd.arg = write ? 0x80000000 : 0x00000000; ++ cmd.arg |= fn << 28; ++ cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; ++ cmd.arg |= addr << 9; ++ if (blocks == 1 && blksz <= 512) ++ cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ ++ else ++ cmd.arg |= 0x08000000 | blocks; /* block mode */ ++ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; ++ ++ data.blksz = blksz; ++ data.blocks = blocks; ++ data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; ++ data.sg = &sg; ++ data.sg_len = 1; ++ ++ sg_init_one(&sg, buf, blksz * blocks); ++ ++ mmc_set_data_timeout(&data, card); ++ ++ mmc_wait_for_req(card->host, &mrq); ++ ++ if (cmd.error) ++ return cmd.error; ++ if (data.error) ++ return data.error; ++ ++ if (mmc_host_is_spi(card->host)) { ++ /* host driver already reported errors */ ++ } else { ++ if (cmd.resp[0] & R5_ERROR) ++ return -EIO; ++ if (cmd.resp[0] & R5_FUNCTION_NUMBER) ++ return -EINVAL; ++ if (cmd.resp[0] & R5_OUT_OF_RANGE) ++ return -ERANGE; ++ } ++ ++ return 0; ++} ++ +--- /dev/null ++++ b/drivers/mmc/core/sdio_ops.h +@@ -0,0 +1,22 @@ ++/* ++ * linux/drivers/mmc/sdio_ops.c ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef _MMC_SDIO_OPS_H ++#define _MMC_SDIO_OPS_H ++ ++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); ++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, u8 in, u8* out); ++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, ++ unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); ++ ++#endif ++ +--- a/include/linux/mmc/card.h ++++ b/include/linux/mmc/card.h +@@ -55,7 +55,28 @@ + unsigned int hs_max_dtr; + }; + ++struct sdio_cccr { ++ unsigned int sdio_vsn; ++ unsigned int sd_vsn; ++ unsigned int multi_block:1, ++ low_speed:1, ++ wide_bus:1, ++ high_power:1, ++ high_speed:1; ++}; ++ ++struct sdio_cis { ++ unsigned short vendor; ++ unsigned short device; ++ unsigned short blksize; ++ unsigned int max_dtr; ++}; ++ + struct mmc_host; ++struct sdio_func; ++struct sdio_func_tuple; ++ ++#define SDIO_MAX_FUNCS 7 + + /* + * MMC device +@@ -67,11 +88,13 @@ + unsigned int type; /* card type */ + #define MMC_TYPE_MMC 0 /* MMC card */ + #define MMC_TYPE_SD 1 /* SD card */ ++#define MMC_TYPE_SDIO 2 /* SDIO card */ + unsigned int state; /* (our) card state */ + #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ + #define MMC_STATE_READONLY (1<<1) /* card is read-only */ + #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ + #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ ++ + u32 raw_cid[4]; /* raw card CID */ + u32 raw_csd[4]; /* raw card CSD */ + u32 raw_scr[2]; /* raw card SCR */ +@@ -80,10 +103,19 @@ + struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ + struct sd_scr scr; /* extra SD information */ + struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ ++ ++ unsigned int sdio_funcs; /* number of SDIO functions */ ++ struct sdio_cccr cccr; /* common card info */ ++ struct sdio_cis cis; /* common tuple info */ ++ struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */ ++ unsigned num_info; /* number of info strings */ ++ const char **info; /* info strings */ ++ struct sdio_func_tuple *tuples; /* unknown common tuples */ + }; + + #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) + #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) ++#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO) + + #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) + #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) +--- a/include/linux/mmc/core.h ++++ b/include/linux/mmc/core.h +@@ -25,14 +25,20 @@ + #define MMC_RSP_CRC (1 << 2) /* expect valid crc */ + #define MMC_RSP_BUSY (1 << 3) /* card may send busy */ + #define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ +-#define MMC_CMD_MASK (3 << 5) /* command type */ ++ ++#define MMC_CMD_MASK (3 << 5) /* non-SPI command type */ + #define MMC_CMD_AC (0 << 5) + #define MMC_CMD_ADTC (1 << 5) + #define MMC_CMD_BC (2 << 5) + #define MMC_CMD_BCR (3 << 5) + ++#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */ ++#define MMC_RSP_SPI_S2 (1 << 8) /* second byte */ ++#define MMC_RSP_SPI_B4 (1 << 9) /* four data bytes */ ++#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ ++ + /* +- * These are the response types, and correspond to valid bit ++ * These are the native response types, and correspond to valid bit + * patterns of the above flags. One additional valid pattern + * is all zeros, which means we don't expect a response. + */ +@@ -41,12 +47,30 @@ + #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) + #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) + #define MMC_RSP_R3 (MMC_RSP_PRESENT) ++#define MMC_RSP_R4 (MMC_RSP_PRESENT) ++#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + + #define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) + + /* ++ * These are the SPI response types for MMC, SD, and SDIO cards. ++ * Commands return R1, with maybe more info. Zero is an error type; ++ * callers must always provide the appropriate MMC_RSP_SPI_Rx flags. ++ */ ++#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1) ++#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY) ++#define MMC_RSP_SPI_R2 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2) ++#define MMC_RSP_SPI_R3 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4) ++#define MMC_RSP_SPI_R4 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4) ++#define MMC_RSP_SPI_R5 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2) ++#define MMC_RSP_SPI_R7 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4) ++ ++#define mmc_spi_resp_type(cmd) ((cmd)->flags & \ ++ (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_B4)) ++ ++/* + * These are the command types. + */ + #define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK) +@@ -54,12 +78,19 @@ + unsigned int retries; /* max number of retries */ + unsigned int error; /* command error */ + +-#define MMC_ERR_NONE 0 +-#define MMC_ERR_TIMEOUT 1 +-#define MMC_ERR_BADCRC 2 +-#define MMC_ERR_FIFO 3 +-#define MMC_ERR_FAILED 4 +-#define MMC_ERR_INVALID 5 ++/* ++ * Standard errno values are used for errors, but some have specific ++ * meaning in the MMC layer: ++ * ++ * ETIMEDOUT Card took too long to respond ++ * EILSEQ Basic format problem with the received or sent data ++ * (e.g. CRC check failed, incorrect opcode in response ++ * or bad end bit) ++ * EINVAL Request cannot be performed because of restrictions ++ * in hardware and/or the driver ++ * ENOMEDIUM Host can determine that the slot is empty and is ++ * actively failing requests ++ */ + + struct mmc_data *data; /* data segment associated with cmd */ + struct mmc_request *mrq; /* associated request */ +@@ -76,7 +107,6 @@ + #define MMC_DATA_WRITE (1 << 8) + #define MMC_DATA_READ (1 << 9) + #define MMC_DATA_STREAM (1 << 10) +-#define MMC_DATA_MULTI (1 << 11) + + unsigned int bytes_xfered; + +@@ -104,9 +134,20 @@ + extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, + struct mmc_command *, int); + +-extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); ++extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); + +-extern void mmc_claim_host(struct mmc_host *host); ++extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); + extern void mmc_release_host(struct mmc_host *host); + ++/** ++ * mmc_claim_host - exclusively claim a host ++ * @host: mmc host to claim ++ * ++ * Claim a host for a set of operations. ++ */ ++static inline void mmc_claim_host(struct mmc_host *host) ++{ ++ __mmc_claim_host(host, NULL); ++} ++ + #endif +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -10,6 +10,8 @@ + #ifndef LINUX_MMC_HOST_H + #define LINUX_MMC_HOST_H + ++#include ++ + #include + + struct mmc_ios { +@@ -51,6 +53,7 @@ + void (*request)(struct mmc_host *host, struct mmc_request *req); + void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); + int (*get_ro)(struct mmc_host *host); ++ void (*enable_sdio_irq)(struct mmc_host *host, int enable); + }; + + struct mmc_card; +@@ -87,9 +90,10 @@ + + #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ + #define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */ +-#define MMC_CAP_BYTEBLOCK (1 << 2) /* Can do non-log2 block sizes */ +-#define MMC_CAP_MMC_HIGHSPEED (1 << 3) /* Can do MMC high-speed timing */ +-#define MMC_CAP_SD_HIGHSPEED (1 << 4) /* Can do SD high-speed timing */ ++#define MMC_CAP_MMC_HIGHSPEED (1 << 2) /* Can do MMC high-speed timing */ ++#define MMC_CAP_SD_HIGHSPEED (1 << 3) /* Can do SD high-speed timing */ ++#define MMC_CAP_SDIO_IRQ (1 << 4) /* Can signal pending SDIO IRQs */ ++#define MMC_CAP_SPI (1 << 5) /* Talks only SPI protocols */ + + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ +@@ -106,23 +110,30 @@ + struct mmc_ios ios; /* current io bus settings */ + u32 ocr; /* the current OCR setting */ + +- unsigned int mode; /* current card mode of host */ +-#define MMC_MODE_MMC 0 +-#define MMC_MODE_SD 1 ++ /* group bitfields together to minimize padding */ ++ unsigned int use_spi_crc:1; ++ unsigned int claimed:1; /* host exclusively claimed */ ++ unsigned int bus_dead:1; /* bus has been released */ ++#ifdef CONFIG_MMC_DEBUG ++ unsigned int removed:1; /* host is being removed */ ++#endif + + struct mmc_card *card; /* device attached to this host */ + + wait_queue_head_t wq; +- unsigned int claimed:1; /* host exclusively claimed */ + + struct delayed_work detect; +-#ifdef CONFIG_MMC_DEBUG +- unsigned int removed:1; /* host is being removed */ +-#endif + + const struct mmc_bus_ops *bus_ops; /* current bus driver */ + unsigned int bus_refs; /* reference counter */ +- unsigned int bus_dead:1; /* bus has been released */ ++ ++ unsigned int sdio_irqs; ++ struct task_struct *sdio_irq_thread; ++ atomic_t sdio_irq_thread_abort; ++ ++#ifdef CONFIG_LEDS_TRIGGERS ++ struct led_trigger *led; /* activity led */ ++#endif + + unsigned long private[0] ____cacheline_aligned; + }; +@@ -137,6 +148,8 @@ + return (void *)host->private; + } + ++#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI) ++ + #define mmc_dev(x) ((x)->parent) + #define mmc_classdev(x) (&(x)->class_dev) + #define mmc_hostname(x) ((x)->class_dev.bus_id) +@@ -147,5 +160,11 @@ + extern void mmc_detect_change(struct mmc_host *, unsigned long delay); + extern void mmc_request_done(struct mmc_host *, struct mmc_request *); + ++static inline void mmc_signal_sdio_irq(struct mmc_host *host) ++{ ++ host->ops->enable_sdio_irq(host, 0); ++ wake_up_process(host->sdio_irq_thread); ++} ++ + #endif + +--- a/include/linux/mmc/mmc.h ++++ b/include/linux/mmc/mmc.h +@@ -27,7 +27,7 @@ + + /* Standard MMC commands (4.1) type argument response */ + /* class 1 */ +-#define MMC_GO_IDLE_STATE 0 /* bc */ ++#define MMC_GO_IDLE_STATE 0 /* bc */ + #define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ + #define MMC_ALL_SEND_CID 2 /* bcr R2 */ + #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +@@ -39,8 +39,10 @@ + #define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ + #define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ + #define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +-#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ ++#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ + #define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ ++#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */ ++#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */ + + /* class 2 */ + #define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +@@ -90,15 +92,15 @@ + */ + + /* +- MMC status in R1 ++ MMC status in R1, for native mode (SPI bits are different) + Type +- e : error bit ++ e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition +- a : according to the card state ++ a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read +@@ -124,10 +126,33 @@ + #define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ + #define R1_ERASE_RESET (1 << 13) /* sr, c */ + #define R1_STATUS(x) (x & 0xFFFFE000) +-#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ ++#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ + #define R1_READY_FOR_DATA (1 << 8) /* sx, a */ + #define R1_APP_CMD (1 << 5) /* sr, c */ + ++/* ++ * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS ++ * R1 is the low order byte; R2 is the next highest byte, when present. ++ */ ++#define R1_SPI_IDLE (1 << 0) ++#define R1_SPI_ERASE_RESET (1 << 1) ++#define R1_SPI_ILLEGAL_COMMAND (1 << 2) ++#define R1_SPI_COM_CRC (1 << 3) ++#define R1_SPI_ERASE_SEQ (1 << 4) ++#define R1_SPI_ADDRESS (1 << 5) ++#define R1_SPI_PARAMETER (1 << 6) ++/* R1 bit 7 is always zero */ ++#define R2_SPI_CARD_LOCKED (1 << 8) ++#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ ++#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP ++#define R2_SPI_ERROR (1 << 10) ++#define R2_SPI_CC_ERROR (1 << 11) ++#define R2_SPI_CARD_ECC_ERROR (1 << 12) ++#define R2_SPI_WP_VIOLATION (1 << 13) ++#define R2_SPI_ERASE_PARAM (1 << 14) ++#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ ++#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE ++ + /* These are unpacked versions of the actual responses */ + + struct _mmc_csd { +@@ -182,6 +207,7 @@ + */ + #define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ + /* (CMD0,1,2,3,4,7,9,10,12,13,15) */ ++ /* (and for SPI, CMD58,59) */ + #define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ + /* (CMD11) */ + #define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ +@@ -227,6 +253,7 @@ + #define EXT_CSD_BUS_WIDTH 183 /* R/W */ + #define EXT_CSD_HS_TIMING 185 /* R/W */ + #define EXT_CSD_CARD_TYPE 196 /* RO */ ++#define EXT_CSD_REV 192 /* RO */ + #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ + + /* +--- /dev/null ++++ b/include/linux/mmc/sdio.h +@@ -0,0 +1,159 @@ ++/* ++ * include/linux/mmc/sdio.h ++ * ++ * Copyright 2006-2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef MMC_SDIO_H ++#define MMC_SDIO_H ++ ++/* SDIO commands type argument response */ ++#define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */ ++#define SD_IO_RW_DIRECT 52 /* ac [31:0] See below R5 */ ++#define SD_IO_RW_EXTENDED 53 /* adtc [31:0] See below R5 */ ++ ++/* ++ * SD_IO_RW_DIRECT argument format: ++ * ++ * [31] R/W flag ++ * [30:28] Function number ++ * [27] RAW flag ++ * [25:9] Register address ++ * [7:0] Data ++ */ ++ ++/* ++ * SD_IO_RW_EXTENDED argument format: ++ * ++ * [31] R/W flag ++ * [30:28] Function number ++ * [27] Block mode ++ * [26] Increment address ++ * [25:9] Register address ++ * [8:0] Byte/block count ++ */ ++ ++/* ++ SDIO status in R5 ++ Type ++ e : error bit ++ s : status bit ++ r : detected and set for the actual command response ++ x : detected and set during command execution. the host must poll ++ the card by sending status command in order to read these bits. ++ Clear condition ++ a : according to the card state ++ b : always related to the previous command. Reception of ++ a valid command will clear it (with a delay of one command) ++ c : clear by read ++ */ ++ ++#define R5_COM_CRC_ERROR (1 << 15) /* er, b */ ++#define R5_ILLEGAL_COMMAND (1 << 14) /* er, b */ ++#define R5_ERROR (1 << 11) /* erx, c */ ++#define R5_FUNCTION_NUMBER (1 << 9) /* er, c */ ++#define R5_OUT_OF_RANGE (1 << 8) /* er, c */ ++#define R5_STATUS(x) (x & 0xCB00) ++#define R5_IO_CURRENT_STATE(x) ((x & 0x3000) >> 12) /* s, b */ ++ ++/* ++ * Card Common Control Registers (CCCR) ++ */ ++ ++#define SDIO_CCCR_CCCR 0x00 ++ ++#define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */ ++#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */ ++#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */ ++ ++#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */ ++#define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */ ++#define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */ ++#define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */ ++ ++#define SDIO_CCCR_SD 0x01 ++ ++#define SDIO_SD_REV_1_01 0 /* SD Physical Spec Version 1.01 */ ++#define SDIO_SD_REV_1_10 1 /* SD Physical Spec Version 1.10 */ ++#define SDIO_SD_REV_2_00 2 /* SD Physical Spec Version 2.00 */ ++ ++#define SDIO_CCCR_IOEx 0x02 ++#define SDIO_CCCR_IORx 0x03 ++ ++#define SDIO_CCCR_IENx 0x04 /* Function/Master Interrupt Enable */ ++#define SDIO_CCCR_INTx 0x05 /* Function Interrupt Pending */ ++ ++#define SDIO_CCCR_ABORT 0x06 /* function abort/card reset */ ++ ++#define SDIO_CCCR_IF 0x07 /* bus interface controls */ ++ ++#define SDIO_BUS_WIDTH_1BIT 0x00 ++#define SDIO_BUS_WIDTH_4BIT 0x02 ++ ++#define SDIO_BUS_CD_DISABLE 0x80 /* disable pull-up on DAT3 (pin 1) */ ++ ++#define SDIO_CCCR_CAPS 0x08 ++ ++#define SDIO_CCCR_CAP_SDC 0x01 /* can do CMD52 while data transfer */ ++#define SDIO_CCCR_CAP_SMB 0x02 /* can do multi-block xfers (CMD53) */ ++#define SDIO_CCCR_CAP_SRW 0x04 /* supports read-wait protocol */ ++#define SDIO_CCCR_CAP_SBS 0x08 /* supports suspend/resume */ ++#define SDIO_CCCR_CAP_S4MI 0x10 /* interrupt during 4-bit CMD53 */ ++#define SDIO_CCCR_CAP_E4MI 0x20 /* enable ints during 4-bit CMD53 */ ++#define SDIO_CCCR_CAP_LSC 0x40 /* low speed card */ ++#define SDIO_CCCR_CAP_4BLS 0x80 /* 4 bit low speed card */ ++ ++#define SDIO_CCCR_CIS 0x09 /* common CIS pointer (3 bytes) */ ++ ++/* Following 4 regs are valid only if SBS is set */ ++#define SDIO_CCCR_SUSPEND 0x0c ++#define SDIO_CCCR_SELx 0x0d ++#define SDIO_CCCR_EXECx 0x0e ++#define SDIO_CCCR_READYx 0x0f ++ ++#define SDIO_CCCR_BLKSIZE 0x10 ++ ++#define SDIO_CCCR_POWER 0x12 ++ ++#define SDIO_POWER_SMPC 0x01 /* Supports Master Power Control */ ++#define SDIO_POWER_EMPC 0x02 /* Enable Master Power Control */ ++ ++#define SDIO_CCCR_SPEED 0x13 ++ ++#define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */ ++#define SDIO_SPEED_EHS 0x02 /* Enable High-Speed mode */ ++ ++/* ++ * Function Basic Registers (FBR) ++ */ ++ ++#define SDIO_FBR_BASE(f) ((f) * 0x100) /* base of function f's FBRs */ ++ ++#define SDIO_FBR_STD_IF 0x00 ++ ++#define SDIO_FBR_SUPPORTS_CSA 0x40 /* supports Code Storage Area */ ++#define SDIO_FBR_ENABLE_CSA 0x80 /* enable Code Storage Area */ ++ ++#define SDIO_FBR_STD_IF_EXT 0x01 ++ ++#define SDIO_FBR_POWER 0x02 ++ ++#define SDIO_FBR_POWER_SPS 0x01 /* Supports Power Selection */ ++#define SDIO_FBR_POWER_EPS 0x02 /* Enable (low) Power Selection */ ++ ++#define SDIO_FBR_CIS 0x09 /* CIS pointer (3 bytes) */ ++ ++ ++#define SDIO_FBR_CSA 0x0C /* CSA pointer (3 bytes) */ ++ ++#define SDIO_FBR_CSA_DATA 0x0F ++ ++#define SDIO_FBR_BLKSIZE 0x10 /* block size (2 bytes) */ ++ ++#endif ++ +--- /dev/null ++++ b/include/linux/mmc/sdio_func.h +@@ -0,0 +1,153 @@ ++/* ++ * include/linux/mmc/sdio_func.h ++ * ++ * Copyright 2007 Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++#ifndef MMC_SDIO_FUNC_H ++#define MMC_SDIO_FUNC_H ++ ++#include ++#include ++ ++struct mmc_card; ++struct sdio_func; ++ ++typedef void (sdio_irq_handler_t)(struct sdio_func *); ++ ++/* ++ * SDIO function CIS tuple (unknown to the core) ++ */ ++struct sdio_func_tuple { ++ struct sdio_func_tuple *next; ++ unsigned char code; ++ unsigned char size; ++ unsigned char data[0]; ++}; ++ ++/* ++ * SDIO function devices ++ */ ++struct sdio_func { ++ struct mmc_card *card; /* the card this device belongs to */ ++ struct device dev; /* the device */ ++ sdio_irq_handler_t *irq_handler; /* IRQ callback */ ++ unsigned int num; /* function number */ ++ ++ unsigned char class; /* standard interface class */ ++ unsigned short vendor; /* vendor id */ ++ unsigned short device; /* device id */ ++ ++ unsigned max_blksize; /* maximum block size */ ++ unsigned cur_blksize; /* current block size */ ++ ++ unsigned int state; /* function state */ ++#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ ++ ++ u8 tmpbuf[4]; /* DMA:able scratch buffer */ ++ ++ unsigned num_info; /* number of info strings */ ++ const char **info; /* info strings */ ++ ++ struct sdio_func_tuple *tuples; ++}; ++ ++#define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT) ++ ++#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT) ++ ++#define sdio_func_id(f) ((f)->dev.bus_id) ++ ++#define sdio_get_drvdata(f) dev_get_drvdata(&(f)->dev) ++#define sdio_set_drvdata(f,d) dev_set_drvdata(&(f)->dev, d) ++ ++/* ++ * SDIO function device driver ++ */ ++struct sdio_driver { ++ char *name; ++ const struct sdio_device_id *id_table; ++ ++ int (*probe)(struct sdio_func *, const struct sdio_device_id *); ++ void (*remove)(struct sdio_func *); ++ ++ struct device_driver drv; ++}; ++ ++/** ++ * SDIO_DEVICE - macro used to describe a specific SDIO device ++ * @vend: the 16 bit manufacturer code ++ * @dev: the 16 bit function id ++ * ++ * This macro is used to create a struct sdio_device_id that matches a ++ * specific device. The class field will be set to SDIO_ANY_ID. ++ */ ++#define SDIO_DEVICE(vend,dev) \ ++ .class = SDIO_ANY_ID, \ ++ .vendor = (vend), .device = (dev) ++ ++/** ++ * SDIO_DEVICE_CLASS - macro used to describe a specific SDIO device class ++ * @dev_class: the 8 bit standard interface code ++ * ++ * This macro is used to create a struct sdio_device_id that matches a ++ * specific standard SDIO function type. The vendor and device fields will ++ * be set to SDIO_ANY_ID. ++ */ ++#define SDIO_DEVICE_CLASS(dev_class) \ ++ .class = (dev_class), \ ++ .vendor = SDIO_ANY_ID, .device = SDIO_ANY_ID ++ ++extern int sdio_register_driver(struct sdio_driver *); ++extern void sdio_unregister_driver(struct sdio_driver *); ++ ++/* ++ * SDIO I/O operations ++ */ ++extern void sdio_claim_host(struct sdio_func *func); ++extern void sdio_release_host(struct sdio_func *func); ++ ++extern int sdio_enable_func(struct sdio_func *func); ++extern int sdio_disable_func(struct sdio_func *func); ++ ++extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz); ++ ++extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler); ++extern int sdio_release_irq(struct sdio_func *func); ++ ++extern unsigned char sdio_readb(struct sdio_func *func, ++ unsigned int addr, int *err_ret); ++extern unsigned short sdio_readw(struct sdio_func *func, ++ unsigned int addr, int *err_ret); ++extern unsigned long sdio_readl(struct sdio_func *func, ++ unsigned int addr, int *err_ret); ++ ++extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst, ++ unsigned int addr, int count); ++extern int sdio_readsb(struct sdio_func *func, void *dst, ++ unsigned int addr, int count); ++ ++extern void sdio_writeb(struct sdio_func *func, unsigned char b, ++ unsigned int addr, int *err_ret); ++extern void sdio_writew(struct sdio_func *func, unsigned short b, ++ unsigned int addr, int *err_ret); ++extern void sdio_writel(struct sdio_func *func, unsigned long b, ++ unsigned int addr, int *err_ret); ++ ++extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, ++ void *src, int count); ++extern int sdio_writesb(struct sdio_func *func, unsigned int addr, ++ void *src, int count); ++ ++extern unsigned char sdio_f0_readb(struct sdio_func *func, ++ unsigned int addr, int *err_ret); ++extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b, ++ unsigned int addr, int *err_ret); ++ ++#endif ++ +--- /dev/null ++++ b/include/linux/mmc/sdio_ids.h +@@ -0,0 +1,29 @@ ++/* ++ * SDIO Classes, Interface Types, Manufacturer IDs, etc. ++ */ ++ ++#ifndef MMC_SDIO_IDS_H ++#define MMC_SDIO_IDS_H ++ ++/* ++ * Standard SDIO Function Interfaces ++ */ ++ ++#define SDIO_CLASS_NONE 0x00 /* Not a SDIO standard interface */ ++#define SDIO_CLASS_UART 0x01 /* standard UART interface */ ++#define SDIO_CLASS_BT_A 0x02 /* Type-A BlueTooth std interface */ ++#define SDIO_CLASS_BT_B 0x03 /* Type-B BlueTooth std interface */ ++#define SDIO_CLASS_GPS 0x04 /* GPS standard interface */ ++#define SDIO_CLASS_CAMERA 0x05 /* Camera standard interface */ ++#define SDIO_CLASS_PHS 0x06 /* PHS standard interface */ ++#define SDIO_CLASS_WLAN 0x07 /* WLAN interface */ ++#define SDIO_CLASS_ATA 0x08 /* Embedded SDIO-ATA std interface */ ++ ++/* ++ * Vendors and devices. Sort key: vendor first, device next. ++ */ ++ ++#define SDIO_VENDOR_ID_MARVELL 0x02df ++#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103 ++ ++#endif +--- a/include/linux/mod_devicetable.h ++++ b/include/linux/mod_devicetable.h +@@ -22,6 +22,18 @@ + }; + + ++/* SDIO */ ++ ++#define SDIO_ANY_ID (~0) ++ ++struct sdio_device_id { ++ __u8 class; /* Standard interface or SDIO_ANY_ID */ ++ __u16 vendor; /* Vendor or SDIO_ANY_ID */ ++ __u16 device; /* Device ID or SDIO_ANY_ID */ ++ kernel_ulong_t driver_data; /* Data private to the driver */ ++}; ++ ++ + #define IEEE1394_MATCH_VENDOR_ID 0x0001 + #define IEEE1394_MATCH_MODEL_ID 0x0002 + #define IEEE1394_MATCH_SPECIFIER_ID 0x0004 +--- a/drivers/mmc/card/Kconfig ++++ b/drivers/mmc/card/Kconfig +@@ -32,3 +32,10 @@ + + If unsure, say Y here. + ++config SDIO_UART ++ tristate "SDIO UART/GPS class support" ++ depends on MMC ++ help ++ SDIO function driver for SDIO cards that implements the UART ++ class, as well as the GPS class which appears like a UART. ++ +--- a/drivers/mmc/card/Makefile ++++ b/drivers/mmc/card/Makefile +@@ -9,3 +9,5 @@ + obj-$(CONFIG_MMC_BLOCK) += mmc_block.o + mmc_block-objs := block.o queue.o + ++obj-$(CONFIG_SDIO_UART) += sdio_uart.o ++ +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -44,6 +44,9 @@ + * max 8 partitions per card + */ + #define MMC_SHIFT 3 ++#define MMC_NUM_MINORS (256 >> MMC_SHIFT) ++ ++static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + + /* + * There is one mmc_blk_data per slot. +@@ -80,6 +83,9 @@ + mutex_lock(&open_lock); + md->usage--; + if (md->usage == 0) { ++ int devidx = md->disk->first_minor >> MMC_SHIFT; ++ __clear_bit(devidx, dev_use); ++ + put_disk(md->disk); + kfree(md); + } +@@ -151,17 +157,19 @@ + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, 0); +- if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) ++ if (err) ++ return (u32)-1; ++ if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD)) + return (u32)-1; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; + cmd.arg = 0; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + +@@ -192,7 +200,7 @@ + + mmc_wait_for_req(card->host, &mrq); + +- if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) ++ if (cmd.error || data.error) + return (u32)-1; + + blocks = ntohl(blocks); +@@ -220,17 +228,15 @@ + brq.cmd.arg = req->sector; + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; +- brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; ++ brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + brq.data.blksz = 1 << md->block_bits; + brq.stop.opcode = MMC_STOP_TRANSMISSION; + brq.stop.arg = 0; +- brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; ++ brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); + if (brq.data.blocks > card->host->max_blk_count) + brq.data.blocks = card->host->max_blk_count; + +- mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); +- + /* + * If the host doesn't support multiple block writes, force + * block writes to single block. SD cards are excepted from +@@ -243,8 +249,12 @@ + brq.data.blocks = 1; + + if (brq.data.blocks > 1) { +- brq.data.flags |= MMC_DATA_MULTI; +- brq.mrq.stop = &brq.stop; ++ /* SPI multiblock writes terminate using a special ++ * token, not a STOP_TRANSMISSION request. ++ */ ++ if (!mmc_host_is_spi(card->host) ++ || rq_data_dir(req) == READ) ++ brq.mrq.stop = &brq.stop; + readcmd = MMC_READ_MULTIPLE_BLOCK; + writecmd = MMC_WRITE_MULTIPLE_BLOCK; + } else { +@@ -261,6 +271,8 @@ + brq.data.flags |= MMC_DATA_WRITE; + } + ++ mmc_set_data_timeout(&brq.data, card); ++ + brq.data.sg = mq->sg; + brq.data.sg_len = mmc_queue_map_sg(mq); + +@@ -302,7 +314,7 @@ + goto cmd_err; + } + +- if (rq_data_dir(req) != READ) { ++ if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) { + do { + int err; + +@@ -315,7 +327,13 @@ + req->rq_disk->disk_name, err); + goto cmd_err; + } +- } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); ++ /* ++ * Some cards mishandle the status bits, ++ * so make sure to check both the busy ++ * indication and the card state. ++ */ ++ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || ++ (R1_CURRENT_STATE(cmd.resp[0]) == 7)); + + #if 0 + if (cmd.resp[0] & ~0x00000900) +@@ -394,9 +412,6 @@ + return 0; + } + +-#define MMC_NUM_MINORS (256 >> MMC_SHIFT) +- +-static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + + static inline int mmc_blk_readonly(struct mmc_card *card) + { +@@ -510,7 +525,7 @@ + mmc_claim_host(card->host); + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = 1 << md->block_bits; +- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + mmc_release_host(card->host); + +@@ -562,17 +577,12 @@ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { +- int devidx; +- + /* Stop new requests from getting into the queue */ + del_gendisk(md->disk); + + /* Then flush out any already in there */ + mmc_cleanup_queue(&md->queue); + +- devidx = md->disk->first_minor >> MMC_SHIFT; +- __clear_bit(devidx, dev_use); +- + mmc_blk_put(md); + } + mmc_set_drvdata(card, NULL); +--- a/drivers/mmc/card/queue.c ++++ b/drivers/mmc/card/queue.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -22,6 +23,12 @@ + + #define MMC_QUEUE_SUSPENDED (1 << 0) + ++#define sg_init_table(sg, n) do { \ ++ memset(sg, 0, sizeof(*(sg)) * (n)); \ ++} while (0) ++ ++#define sg_virt(sg) (page_address((sg)->page) + (sg)->offset) ++ + /* + * Prepare a MMC request. This just filters out odd stuff. + */ +@@ -159,6 +166,7 @@ + ret = -ENOMEM; + goto cleanup_queue; + } ++ sg_init_table(mq->sg, 1); + + mq->bounce_sg = kmalloc(sizeof(struct scatterlist) * + bouncesz / 512, GFP_KERNEL); +@@ -166,6 +174,7 @@ + ret = -ENOMEM; + goto cleanup_queue; + } ++ sg_init_table(mq->bounce_sg, bouncesz / 512); + } + } + #endif +@@ -183,6 +192,7 @@ + ret = -ENOMEM; + goto cleanup_queue; + } ++ sg_init_table(mq->sg, host->max_phys_segs); + } + + init_MUTEX(&mq->thread_sem); +@@ -302,12 +312,12 @@ + BUG_ON(dst_len == 0); + + if (dst_size == 0) { +- dst_buf = page_address(dst->page) + dst->offset; ++ dst_buf = sg_virt(dst); + dst_size = dst->length; + } + + if (src_size == 0) { +- src_buf = page_address(src->page) + src->offset; ++ src_buf = sg_virt(src); + src_size = src->length; + } + +@@ -353,9 +363,7 @@ + return 1; + } + +- mq->sg[0].page = virt_to_page(mq->bounce_buf); +- mq->sg[0].offset = offset_in_page(mq->bounce_buf); +- mq->sg[0].length = 0; ++ sg_init_one(mq->sg, mq->bounce_buf, 0); + + while (sg_len) { + mq->sg[0].length += mq->bounce_sg[sg_len - 1].length; +--- /dev/null ++++ b/drivers/mmc/card/sdio_uart.c +@@ -0,0 +1,1158 @@ ++/* ++ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver ++ * ++ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c ++ * by Russell King. ++ * ++ * Author: Nicolas Pitre ++ * Created: June 15, 2007 ++ * Copyright: MontaVista Software, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ */ ++ ++/* ++ * Note: Although this driver assumes a 16550A-like UART implementation, ++ * it is not possible to leverage the common 8250/16550 driver, nor the ++ * core UART infrastructure, as they assumes direct access to the hardware ++ * registers, often under a spinlock. This is not possible in the SDIO ++ * context as SDIO access functions must be able to sleep. ++ * ++ * Because we need to lock the SDIO host to ensure an exclusive access to ++ * the card, we simply rely on that lock to also prevent and serialize ++ * concurrent access to the same port. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++ ++#define UART_NR 8 /* Number of UARTs this driver can handle */ ++ ++ ++#define UART_XMIT_SIZE PAGE_SIZE ++#define WAKEUP_CHARS 256 ++ ++#define circ_empty(circ) ((circ)->head == (circ)->tail) ++#define circ_clear(circ) ((circ)->head = (circ)->tail = 0) ++ ++#define circ_chars_pending(circ) \ ++ (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) ++ ++#define circ_chars_free(circ) \ ++ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) ++ ++ ++struct uart_icount { ++ __u32 cts; ++ __u32 dsr; ++ __u32 rng; ++ __u32 dcd; ++ __u32 rx; ++ __u32 tx; ++ __u32 frame; ++ __u32 overrun; ++ __u32 parity; ++ __u32 brk; ++}; ++ ++struct sdio_uart_port { ++ struct kref kref; ++ struct tty_struct *tty; ++ unsigned int index; ++ unsigned int opened; ++ struct mutex open_lock; ++ struct sdio_func *func; ++ struct mutex func_lock; ++ struct task_struct *in_sdio_uart_irq; ++ unsigned int regs_offset; ++ struct circ_buf xmit; ++ spinlock_t write_lock; ++ struct uart_icount icount; ++ unsigned int uartclk; ++ unsigned int mctrl; ++ unsigned int read_status_mask; ++ unsigned int ignore_status_mask; ++ unsigned char x_char; ++ unsigned char ier; ++ unsigned char lcr; ++}; ++ ++static struct sdio_uart_port *sdio_uart_table[UART_NR]; ++static DEFINE_SPINLOCK(sdio_uart_table_lock); ++ ++static int sdio_uart_add_port(struct sdio_uart_port *port) ++{ ++ int index, ret = -EBUSY; ++ ++ kref_init(&port->kref); ++ mutex_init(&port->open_lock); ++ mutex_init(&port->func_lock); ++ spin_lock_init(&port->write_lock); ++ ++ spin_lock(&sdio_uart_table_lock); ++ for (index = 0; index < UART_NR; index++) { ++ if (!sdio_uart_table[index]) { ++ port->index = index; ++ sdio_uart_table[index] = port; ++ ret = 0; ++ break; ++ } ++ } ++ spin_unlock(&sdio_uart_table_lock); ++ ++ return ret; ++} ++ ++static struct sdio_uart_port *sdio_uart_port_get(unsigned index) ++{ ++ struct sdio_uart_port *port; ++ ++ if (index >= UART_NR) ++ return NULL; ++ ++ spin_lock(&sdio_uart_table_lock); ++ port = sdio_uart_table[index]; ++ if (port) ++ kref_get(&port->kref); ++ spin_unlock(&sdio_uart_table_lock); ++ ++ return port; ++} ++ ++static void sdio_uart_port_destroy(struct kref *kref) ++{ ++ struct sdio_uart_port *port = ++ container_of(kref, struct sdio_uart_port, kref); ++ kfree(port); ++} ++ ++static void sdio_uart_port_put(struct sdio_uart_port *port) ++{ ++ kref_put(&port->kref, sdio_uart_port_destroy); ++} ++ ++static void sdio_uart_port_remove(struct sdio_uart_port *port) ++{ ++ struct sdio_func *func; ++ ++ BUG_ON(sdio_uart_table[port->index] != port); ++ ++ spin_lock(&sdio_uart_table_lock); ++ sdio_uart_table[port->index] = NULL; ++ spin_unlock(&sdio_uart_table_lock); ++ ++ /* ++ * We're killing a port that potentially still is in use by ++ * the tty layer. Be careful to prevent any further access ++ * to the SDIO function and arrange for the tty layer to ++ * give up on that port ASAP. ++ * Beware: the lock ordering is critical. ++ */ ++ mutex_lock(&port->open_lock); ++ mutex_lock(&port->func_lock); ++ func = port->func; ++ sdio_claim_host(func); ++ port->func = NULL; ++ mutex_unlock(&port->func_lock); ++ if (port->opened) ++ tty_hangup(port->tty); ++ mutex_unlock(&port->open_lock); ++ sdio_release_irq(func); ++ sdio_disable_func(func); ++ sdio_release_host(func); ++ ++ sdio_uart_port_put(port); ++} ++ ++static int sdio_uart_claim_func(struct sdio_uart_port *port) ++{ ++ mutex_lock(&port->func_lock); ++ if (unlikely(!port->func)) { ++ mutex_unlock(&port->func_lock); ++ return -ENODEV; ++ } ++ if (likely(port->in_sdio_uart_irq != current)) ++ sdio_claim_host(port->func); ++ mutex_unlock(&port->func_lock); ++ return 0; ++} ++ ++static inline void sdio_uart_release_func(struct sdio_uart_port *port) ++{ ++ if (likely(port->in_sdio_uart_irq != current)) ++ sdio_release_host(port->func); ++} ++ ++static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset) ++{ ++ unsigned char c; ++ c = sdio_readb(port->func, port->regs_offset + offset, NULL); ++ return c; ++} ++ ++static inline void sdio_out(struct sdio_uart_port *port, int offset, int value) ++{ ++ sdio_writeb(port->func, value, port->regs_offset + offset, NULL); ++} ++ ++static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port) ++{ ++ unsigned char status; ++ unsigned int ret; ++ ++ status = sdio_in(port, UART_MSR); ++ ++ ret = 0; ++ if (status & UART_MSR_DCD) ++ ret |= TIOCM_CAR; ++ if (status & UART_MSR_RI) ++ ret |= TIOCM_RNG; ++ if (status & UART_MSR_DSR) ++ ret |= TIOCM_DSR; ++ if (status & UART_MSR_CTS) ++ ret |= TIOCM_CTS; ++ return ret; ++} ++ ++static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl) ++{ ++ unsigned char mcr = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ mcr |= UART_MCR_RTS; ++ if (mctrl & TIOCM_DTR) ++ mcr |= UART_MCR_DTR; ++ if (mctrl & TIOCM_OUT1) ++ mcr |= UART_MCR_OUT1; ++ if (mctrl & TIOCM_OUT2) ++ mcr |= UART_MCR_OUT2; ++ if (mctrl & TIOCM_LOOP) ++ mcr |= UART_MCR_LOOP; ++ ++ sdio_out(port, UART_MCR, mcr); ++} ++ ++static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port, ++ unsigned int set, unsigned int clear) ++{ ++ unsigned int old; ++ ++ old = port->mctrl; ++ port->mctrl = (old & ~clear) | set; ++ if (old != port->mctrl) ++ sdio_uart_write_mctrl(port, port->mctrl); ++} ++ ++#define sdio_uart_set_mctrl(port, x) sdio_uart_update_mctrl(port, x, 0) ++#define sdio_uart_clear_mctrl(port, x) sdio_uart_update_mctrl(port, 0, x) ++ ++static void sdio_uart_change_speed(struct sdio_uart_port *port, ++ struct ktermios *termios, ++ struct ktermios *old) ++{ ++ unsigned char cval, fcr = 0; ++ unsigned int baud, quot; ++ ++ switch (termios->c_cflag & CSIZE) { ++ case CS5: ++ cval = UART_LCR_WLEN5; ++ break; ++ case CS6: ++ cval = UART_LCR_WLEN6; ++ break; ++ case CS7: ++ cval = UART_LCR_WLEN7; ++ break; ++ default: ++ case CS8: ++ cval = UART_LCR_WLEN8; ++ break; ++ } ++ ++ if (termios->c_cflag & CSTOPB) ++ cval |= UART_LCR_STOP; ++ if (termios->c_cflag & PARENB) ++ cval |= UART_LCR_PARITY; ++ if (!(termios->c_cflag & PARODD)) ++ cval |= UART_LCR_EPAR; ++ ++ for (;;) { ++ baud = tty_termios_baud_rate(termios); ++ if (baud == 0) ++ baud = 9600; /* Special case: B0 rate. */ ++ if (baud <= port->uartclk) ++ break; ++ /* ++ * Oops, the quotient was zero. Try again with the old ++ * baud rate if possible, otherwise default to 9600. ++ */ ++ termios->c_cflag &= ~CBAUD; ++ if (old) { ++ termios->c_cflag |= old->c_cflag & CBAUD; ++ old = NULL; ++ } else ++ termios->c_cflag |= B9600; ++ } ++ quot = (2 * port->uartclk + baud) / (2 * baud); ++ ++ if (baud < 2400) ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; ++ else ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10; ++ ++ port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; ++ if (termios->c_iflag & INPCK) ++ port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; ++ if (termios->c_iflag & (BRKINT | PARMRK)) ++ port->read_status_mask |= UART_LSR_BI; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; ++ if (termios->c_iflag & IGNBRK) { ++ port->ignore_status_mask |= UART_LSR_BI; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (termios->c_iflag & IGNPAR) ++ port->ignore_status_mask |= UART_LSR_OE; ++ } ++ ++ /* ++ * ignore all characters if CREAD is not set ++ */ ++ if ((termios->c_cflag & CREAD) == 0) ++ port->ignore_status_mask |= UART_LSR_DR; ++ ++ /* ++ * CTS flow control flag and modem status interrupts ++ */ ++ port->ier &= ~UART_IER_MSI; ++ if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL)) ++ port->ier |= UART_IER_MSI; ++ ++ port->lcr = cval; ++ ++ sdio_out(port, UART_IER, port->ier); ++ sdio_out(port, UART_LCR, cval | UART_LCR_DLAB); ++ sdio_out(port, UART_DLL, quot & 0xff); ++ sdio_out(port, UART_DLM, quot >> 8); ++ sdio_out(port, UART_LCR, cval); ++ sdio_out(port, UART_FCR, fcr); ++ ++ sdio_uart_write_mctrl(port, port->mctrl); ++} ++ ++static void sdio_uart_start_tx(struct sdio_uart_port *port) ++{ ++ if (!(port->ier & UART_IER_THRI)) { ++ port->ier |= UART_IER_THRI; ++ sdio_out(port, UART_IER, port->ier); ++ } ++} ++ ++static void sdio_uart_stop_tx(struct sdio_uart_port *port) ++{ ++ if (port->ier & UART_IER_THRI) { ++ port->ier &= ~UART_IER_THRI; ++ sdio_out(port, UART_IER, port->ier); ++ } ++} ++ ++static void sdio_uart_stop_rx(struct sdio_uart_port *port) ++{ ++ port->ier &= ~UART_IER_RLSI; ++ port->read_status_mask &= ~UART_LSR_DR; ++ sdio_out(port, UART_IER, port->ier); ++} ++ ++static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status) ++{ ++ struct tty_struct *tty = port->tty; ++ unsigned int ch, flag; ++ int max_count = 256; ++ ++ do { ++ ch = sdio_in(port, UART_RX); ++ flag = TTY_NORMAL; ++ port->icount.rx++; ++ ++ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | ++ UART_LSR_FE | UART_LSR_OE))) { ++ /* ++ * For statistics only ++ */ ++ if (*status & UART_LSR_BI) { ++ *status &= ~(UART_LSR_FE | UART_LSR_PE); ++ port->icount.brk++; ++ } else if (*status & UART_LSR_PE) ++ port->icount.parity++; ++ else if (*status & UART_LSR_FE) ++ port->icount.frame++; ++ if (*status & UART_LSR_OE) ++ port->icount.overrun++; ++ ++ /* ++ * Mask off conditions which should be ignored. ++ */ ++ *status &= port->read_status_mask; ++ if (*status & UART_LSR_BI) { ++ flag = TTY_BREAK; ++ } else if (*status & UART_LSR_PE) ++ flag = TTY_PARITY; ++ else if (*status & UART_LSR_FE) ++ flag = TTY_FRAME; ++ } ++ ++ if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0) ++ tty_insert_flip_char(tty, ch, flag); ++ ++ /* ++ * Overrun is special. Since it's reported immediately, ++ * it doesn't affect the current character. ++ */ ++ if (*status & ~port->ignore_status_mask & UART_LSR_OE) ++ tty_insert_flip_char(tty, 0, TTY_OVERRUN); ++ ++ *status = sdio_in(port, UART_LSR); ++ } while ((*status & UART_LSR_DR) && (max_count-- > 0)); ++ tty_flip_buffer_push(tty); ++} ++ ++static void sdio_uart_transmit_chars(struct sdio_uart_port *port) ++{ ++ struct circ_buf *xmit = &port->xmit; ++ int count; ++ ++ if (port->x_char) { ++ sdio_out(port, UART_TX, port->x_char); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) { ++ sdio_uart_stop_tx(port); ++ return; ++ } ++ ++ count = 16; ++ do { ++ sdio_out(port, UART_TX, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (circ_empty(xmit)) ++ break; ++ } while (--count > 0); ++ ++ if (circ_chars_pending(xmit) < WAKEUP_CHARS) ++ tty_wakeup(port->tty); ++ ++ if (circ_empty(xmit)) ++ sdio_uart_stop_tx(port); ++} ++ ++static void sdio_uart_check_modem_status(struct sdio_uart_port *port) ++{ ++ int status; ++ ++ status = sdio_in(port, UART_MSR); ++ ++ if ((status & UART_MSR_ANY_DELTA) == 0) ++ return; ++ ++ if (status & UART_MSR_TERI) ++ port->icount.rng++; ++ if (status & UART_MSR_DDSR) ++ port->icount.dsr++; ++ if (status & UART_MSR_DDCD) ++ port->icount.dcd++; ++ if (status & UART_MSR_DCTS) { ++ port->icount.cts++; ++ if (port->tty->termios->c_cflag & CRTSCTS) { ++ int cts = (status & UART_MSR_CTS); ++ if (port->tty->hw_stopped) { ++ if (cts) { ++ port->tty->hw_stopped = 0; ++ sdio_uart_start_tx(port); ++ tty_wakeup(port->tty); ++ } ++ } else { ++ if (!cts) { ++ port->tty->hw_stopped = 1; ++ sdio_uart_stop_tx(port); ++ } ++ } ++ } ++ } ++} ++ ++/* ++ * This handles the interrupt from one port. ++ */ ++static void sdio_uart_irq(struct sdio_func *func) ++{ ++ struct sdio_uart_port *port = sdio_get_drvdata(func); ++ unsigned int iir, lsr; ++ ++ /* ++ * In a few places sdio_uart_irq() is called directly instead of ++ * waiting for the actual interrupt to be raised and the SDIO IRQ ++ * thread scheduled in order to reduce latency. However, some ++ * interaction with the tty core may end up calling us back ++ * (serial echo, flow control, etc.) through those same places ++ * causing undesirable effects. Let's stop the recursion here. ++ */ ++ if (unlikely(port->in_sdio_uart_irq == current)) ++ return; ++ ++ iir = sdio_in(port, UART_IIR); ++ if (iir & UART_IIR_NO_INT) ++ return; ++ ++ port->in_sdio_uart_irq = current; ++ lsr = sdio_in(port, UART_LSR); ++ if (lsr & UART_LSR_DR) ++ sdio_uart_receive_chars(port, &lsr); ++ sdio_uart_check_modem_status(port); ++ if (lsr & UART_LSR_THRE) ++ sdio_uart_transmit_chars(port); ++ port->in_sdio_uart_irq = NULL; ++} ++ ++static int sdio_uart_startup(struct sdio_uart_port *port) ++{ ++ unsigned long page; ++ int ret; ++ ++ /* ++ * Set the TTY IO error marker - we will only clear this ++ * once we have successfully opened the port. ++ */ ++ set_bit(TTY_IO_ERROR, &port->tty->flags); ++ ++ /* Initialise and allocate the transmit buffer. */ ++ page = __get_free_page(GFP_KERNEL); ++ if (!page) ++ return -ENOMEM; ++ port->xmit.buf = (unsigned char *)page; ++ circ_clear(&port->xmit); ++ ++ ret = sdio_uart_claim_func(port); ++ if (ret) ++ goto err1; ++ ret = sdio_enable_func(port->func); ++ if (ret) ++ goto err2; ++ ret = sdio_claim_irq(port->func, sdio_uart_irq); ++ if (ret) ++ goto err3; ++ ++ /* ++ * Clear the FIFO buffers and disable them. ++ * (they will be reenabled in sdio_change_speed()) ++ */ ++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO); ++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); ++ sdio_out(port, UART_FCR, 0); ++ ++ /* ++ * Clear the interrupt registers. ++ */ ++ (void) sdio_in(port, UART_LSR); ++ (void) sdio_in(port, UART_RX); ++ (void) sdio_in(port, UART_IIR); ++ (void) sdio_in(port, UART_MSR); ++ ++ /* ++ * Now, initialize the UART ++ */ ++ sdio_out(port, UART_LCR, UART_LCR_WLEN8); ++ ++ port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; ++ port->mctrl = TIOCM_OUT2; ++ ++ sdio_uart_change_speed(port, port->tty->termios, NULL); ++ ++ if (port->tty->termios->c_cflag & CBAUD) ++ sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); ++ ++ if (port->tty->termios->c_cflag & CRTSCTS) ++ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) ++ port->tty->hw_stopped = 1; ++ ++ clear_bit(TTY_IO_ERROR, &port->tty->flags); ++ ++ /* Kick the IRQ handler once while we're still holding the host lock */ ++ sdio_uart_irq(port->func); ++ ++ sdio_uart_release_func(port); ++ return 0; ++ ++err3: ++ sdio_disable_func(port->func); ++err2: ++ sdio_uart_release_func(port); ++err1: ++ free_page((unsigned long)port->xmit.buf); ++ return ret; ++} ++ ++static void sdio_uart_shutdown(struct sdio_uart_port *port) ++{ ++ int ret; ++ ++ ret = sdio_uart_claim_func(port); ++ if (ret) ++ goto skip; ++ ++ sdio_uart_stop_rx(port); ++ ++ /* TODO: wait here for TX FIFO to drain */ ++ ++ /* Turn off DTR and RTS early. */ ++ if (port->tty->termios->c_cflag & HUPCL) ++ sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); ++ ++ /* Disable interrupts from this port */ ++ sdio_release_irq(port->func); ++ port->ier = 0; ++ sdio_out(port, UART_IER, 0); ++ ++ sdio_uart_clear_mctrl(port, TIOCM_OUT2); ++ ++ /* Disable break condition and FIFOs. */ ++ port->lcr &= ~UART_LCR_SBC; ++ sdio_out(port, UART_LCR, port->lcr); ++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT); ++ sdio_out(port, UART_FCR, 0); ++ ++ sdio_disable_func(port->func); ++ ++ sdio_uart_release_func(port); ++ ++skip: ++ /* Free the transmit buffer page. */ ++ free_page((unsigned long)port->xmit.buf); ++} ++ ++static int sdio_uart_open (struct tty_struct *tty, struct file * filp) ++{ ++ struct sdio_uart_port *port; ++ int ret; ++ ++ port = sdio_uart_port_get(tty->index); ++ if (!port) ++ return -ENODEV; ++ ++ mutex_lock(&port->open_lock); ++ ++ /* ++ * Make sure not to mess up with a dead port ++ * which has not been closed yet. ++ */ ++ if (tty->driver_data && tty->driver_data != port) { ++ mutex_unlock(&port->open_lock); ++ sdio_uart_port_put(port); ++ return -EBUSY; ++ } ++ ++ if (!port->opened) { ++ tty->driver_data = port; ++ port->tty = tty; ++ ret = sdio_uart_startup(port); ++ if (ret) { ++ tty->driver_data = NULL; ++ port->tty = NULL; ++ mutex_unlock(&port->open_lock); ++ sdio_uart_port_put(port); ++ return ret; ++ } ++ } ++ port->opened++; ++ mutex_unlock(&port->open_lock); ++ return 0; ++} ++ ++static void sdio_uart_close(struct tty_struct *tty, struct file * filp) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (!port) ++ return; ++ ++ mutex_lock(&port->open_lock); ++ BUG_ON(!port->opened); ++ ++ /* ++ * This is messy. The tty layer calls us even when open() ++ * returned an error. Ignore this close request if tty->count ++ * is larger than port->count. ++ */ ++ if (tty->count > port->opened) { ++ mutex_unlock(&port->open_lock); ++ return; ++ } ++ ++ if (--port->opened == 0) { ++ tty->closing = 1; ++ sdio_uart_shutdown(port); ++ tty_ldisc_flush(tty); ++ port->tty = NULL; ++ tty->driver_data = NULL; ++ tty->closing = 0; ++ } ++ mutex_unlock(&port->open_lock); ++ sdio_uart_port_put(port); ++} ++ ++static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf, ++ int count) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ struct circ_buf *circ = &port->xmit; ++ int c, ret = 0; ++ ++ if (!port->func) ++ return -ENODEV; ++ ++ spin_lock(&port->write_lock); ++ while (1) { ++ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); ++ if (count < c) ++ c = count; ++ if (c <= 0) ++ break; ++ memcpy(circ->buf + circ->head, buf, c); ++ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); ++ buf += c; ++ count -= c; ++ ret += c; ++ } ++ spin_unlock(&port->write_lock); ++ ++ if ( !(port->ier & UART_IER_THRI)) { ++ int err = sdio_uart_claim_func(port); ++ if (!err) { ++ sdio_uart_start_tx(port); ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++ } else ++ ret = err; ++ } ++ ++ return ret; ++} ++ ++static int sdio_uart_write_room(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ return port ? circ_chars_free(&port->xmit) : 0; ++} ++ ++static int sdio_uart_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ return port ? circ_chars_pending(&port->xmit) : 0; ++} ++ ++static void sdio_uart_send_xchar(struct tty_struct *tty, char ch) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ port->x_char = ch; ++ if (ch && !(port->ier & UART_IER_THRI)) { ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ sdio_uart_start_tx(port); ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++ } ++} ++ ++static void sdio_uart_throttle(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS)) ++ return; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ if (I_IXOFF(tty)) { ++ port->x_char = STOP_CHAR(tty); ++ sdio_uart_start_tx(port); ++ } ++ ++ if (tty->termios->c_cflag & CRTSCTS) ++ sdio_uart_clear_mctrl(port, TIOCM_RTS); ++ ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++} ++ ++static void sdio_uart_unthrottle(struct tty_struct *tty) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS)) ++ return; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ if (I_IXOFF(tty)) { ++ if (port->x_char) { ++ port->x_char = 0; ++ } else { ++ port->x_char = START_CHAR(tty); ++ sdio_uart_start_tx(port); ++ } ++ } ++ ++ if (tty->termios->c_cflag & CRTSCTS) ++ sdio_uart_set_mctrl(port, TIOCM_RTS); ++ ++ sdio_uart_irq(port->func); ++ sdio_uart_release_func(port); ++} ++ ++static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ unsigned int cflag = tty->termios->c_cflag; ++ ++#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) ++ ++ if ((cflag ^ old_termios->c_cflag) == 0 && ++ RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) ++ return; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ sdio_uart_change_speed(port, tty->termios, old_termios); ++ ++ /* Handle transition to B0 status */ ++ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) ++ sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR); ++ ++ /* Handle transition away from B0 status */ ++ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { ++ unsigned int mask = TIOCM_DTR; ++ if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) ++ mask |= TIOCM_RTS; ++ sdio_uart_set_mctrl(port, mask); ++ } ++ ++ /* Handle turning off CRTSCTS */ ++ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { ++ tty->hw_stopped = 0; ++ sdio_uart_start_tx(port); ++ } ++ ++ /* Handle turning on CRTSCTS */ ++ if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { ++ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) { ++ tty->hw_stopped = 1; ++ sdio_uart_stop_tx(port); ++ } ++ } ++ ++ sdio_uart_release_func(port); ++} ++ ++static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ ++ if (sdio_uart_claim_func(port) != 0) ++ return; ++ ++ if (break_state == -1) ++ port->lcr |= UART_LCR_SBC; ++ else ++ port->lcr &= ~UART_LCR_SBC; ++ sdio_out(port, UART_LCR, port->lcr); ++ ++ sdio_uart_release_func(port); ++} ++ ++static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ int result; ++ ++ result = sdio_uart_claim_func(port); ++ if (!result) { ++ result = port->mctrl | sdio_uart_get_mctrl(port); ++ sdio_uart_release_func(port); ++ } ++ ++ return result; ++} ++ ++static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear) ++{ ++ struct sdio_uart_port *port = tty->driver_data; ++ int result; ++ ++ result =sdio_uart_claim_func(port); ++ if(!result) { ++ sdio_uart_update_mctrl(port, set, clear); ++ sdio_uart_release_func(port); ++ } ++ ++ return result; ++} ++ ++static int sdio_uart_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int i, len = 0; ++ off_t begin = 0; ++ ++ len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", ++ "", "", ""); ++ for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) { ++ struct sdio_uart_port *port = sdio_uart_port_get(i); ++ if (port) { ++ len += sprintf(page+len, "%d: uart:SDIO", i); ++ if(capable(CAP_SYS_ADMIN)) { ++ len += sprintf(page + len, " tx:%d rx:%d", ++ port->icount.tx, port->icount.rx); ++ if (port->icount.frame) ++ len += sprintf(page + len, " fe:%d", ++ port->icount.frame); ++ if (port->icount.parity) ++ len += sprintf(page + len, " pe:%d", ++ port->icount.parity); ++ if (port->icount.brk) ++ len += sprintf(page + len, " brk:%d", ++ port->icount.brk); ++ if (port->icount.overrun) ++ len += sprintf(page + len, " oe:%d", ++ port->icount.overrun); ++ if (port->icount.cts) ++ len += sprintf(page + len, " cts:%d", ++ port->icount.cts); ++ if (port->icount.dsr) ++ len += sprintf(page + len, " dsr:%d", ++ port->icount.dsr); ++ if (port->icount.rng) ++ len += sprintf(page + len, " rng:%d", ++ port->icount.rng); ++ if (port->icount.dcd) ++ len += sprintf(page + len, " dcd:%d", ++ port->icount.dcd); ++ } ++ strcat(page, "\n"); ++ len++; ++ sdio_uart_port_put(port); ++ } ++ ++ if (len + begin > off + count) ++ goto done; ++ if (len + begin < off) { ++ begin += len; ++ len = 0; ++ } ++ } ++ *eof = 1; ++ ++done: ++ if (off >= len + begin) ++ return 0; ++ *start = page + (off - begin); ++ return (count < begin + len - off) ? count : (begin + len - off); ++} ++ ++static const struct tty_operations sdio_uart_ops = { ++ .open = sdio_uart_open, ++ .close = sdio_uart_close, ++ .write = sdio_uart_write, ++ .write_room = sdio_uart_write_room, ++ .chars_in_buffer = sdio_uart_chars_in_buffer, ++ .send_xchar = sdio_uart_send_xchar, ++ .throttle = sdio_uart_throttle, ++ .unthrottle = sdio_uart_unthrottle, ++ .set_termios = sdio_uart_set_termios, ++ .break_ctl = sdio_uart_break_ctl, ++ .tiocmget = sdio_uart_tiocmget, ++ .tiocmset = sdio_uart_tiocmset, ++ .read_proc = sdio_uart_read_proc, ++}; ++ ++static struct tty_driver *sdio_uart_tty_driver; ++ ++static int sdio_uart_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ struct sdio_uart_port *port; ++ int ret; ++ ++ port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL); ++ if (!port) ++ return -ENOMEM; ++ ++ if (func->class == SDIO_CLASS_UART) { ++ printk(KERN_WARNING "%s: need info on UART class basic setup\n", ++ sdio_func_id(func)); ++ kfree(port); ++ return -ENOSYS; ++ } else if (func->class == SDIO_CLASS_GPS) { ++ /* ++ * We need tuple 0x91. It contains SUBTPL_SIOREG ++ * and SUBTPL_RCVCAPS. ++ */ ++ struct sdio_func_tuple *tpl; ++ for (tpl = func->tuples; tpl; tpl = tpl->next) { ++ if (tpl->code != 0x91) ++ continue; ++ if (tpl->size < 10) ++ continue; ++ if (tpl->data[1] == 0) /* SUBTPL_SIOREG */ ++ break; ++ } ++ if (!tpl) { ++ printk(KERN_WARNING ++ "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", ++ sdio_func_id(func)); ++ kfree(port); ++ return -EINVAL; ++ } ++ printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n", ++ sdio_func_id(func), tpl->data[2], tpl->data[3]); ++ port->regs_offset = (tpl->data[4] << 0) | ++ (tpl->data[5] << 8) | ++ (tpl->data[6] << 16); ++ printk(KERN_DEBUG "%s: regs offset = 0x%x\n", ++ sdio_func_id(func), port->regs_offset); ++ port->uartclk = tpl->data[7] * 115200; ++ if (port->uartclk == 0) ++ port->uartclk = 115200; ++ printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n", ++ sdio_func_id(func), port->uartclk, ++ tpl->data[7], tpl->data[8] | (tpl->data[9] << 8)); ++ } else { ++ kfree(port); ++ return -EINVAL; ++ } ++ ++ port->func = func; ++ sdio_set_drvdata(func, port); ++ ++ ret = sdio_uart_add_port(port); ++ if (ret) { ++ kfree(port); ++ } else { ++ struct device *dev; ++ dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev); ++ if (IS_ERR(dev)) { ++ sdio_uart_port_remove(port); ++ ret = PTR_ERR(dev); ++ } ++ } ++ ++ return ret; ++} ++ ++static void sdio_uart_remove(struct sdio_func *func) ++{ ++ struct sdio_uart_port *port = sdio_get_drvdata(func); ++ ++ tty_unregister_device(sdio_uart_tty_driver, port->index); ++ sdio_uart_port_remove(port); ++} ++ ++static const struct sdio_device_id sdio_uart_ids[] = { ++ { SDIO_DEVICE_CLASS(SDIO_CLASS_UART) }, ++ { SDIO_DEVICE_CLASS(SDIO_CLASS_GPS) }, ++ { /* end: all zeroes */ }, ++}; ++ ++MODULE_DEVICE_TABLE(sdio, sdio_uart_ids); ++ ++static struct sdio_driver sdio_uart_driver = { ++ .probe = sdio_uart_probe, ++ .remove = sdio_uart_remove, ++ .name = "sdio_uart", ++ .id_table = sdio_uart_ids, ++}; ++ ++static int __init sdio_uart_init(void) ++{ ++ int ret; ++ struct tty_driver *tty_drv; ++ ++ sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR); ++ if (!tty_drv) ++ return -ENOMEM; ++ ++ tty_drv->owner = THIS_MODULE; ++ tty_drv->driver_name = "sdio_uart"; ++ tty_drv->name = "ttySDIO"; ++ tty_drv->major = 0; /* dynamically allocated */ ++ tty_drv->minor_start = 0; ++ tty_drv->type = TTY_DRIVER_TYPE_SERIAL; ++ tty_drv->subtype = SERIAL_TYPE_NORMAL; ++ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ tty_drv->init_termios = tty_std_termios; ++ tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; ++ tty_drv->init_termios.c_ispeed = 4800; ++ tty_drv->init_termios.c_ospeed = 4800; ++ tty_set_operations(tty_drv, &sdio_uart_ops); ++ ++ ret = tty_register_driver(tty_drv); ++ if (ret) ++ goto err1; ++ ++ ret = sdio_register_driver(&sdio_uart_driver); ++ if (ret) ++ goto err2; ++ ++ return 0; ++ ++err2: ++ tty_unregister_driver(tty_drv); ++err1: ++ put_tty_driver(tty_drv); ++ return ret; ++} ++ ++static void __exit sdio_uart_exit(void) ++{ ++ sdio_unregister_driver(&sdio_uart_driver); ++ tty_unregister_driver(sdio_uart_tty_driver); ++ put_tty_driver(sdio_uart_tty_driver); ++} ++ ++module_init(sdio_uart_init); ++module_exit(sdio_uart_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_LICENSE("GPL"); +--- a/drivers/mmc/core/Makefile ++++ b/drivers/mmc/core/Makefile +@@ -8,5 +8,7 @@ + + obj-$(CONFIG_MMC) += mmc_core.o + mmc_core-y := core.o sysfs.o bus.o host.o \ +- mmc.o mmc_ops.o sd.o sd_ops.o ++ mmc.o mmc_ops.o sd.o sd_ops.o \ ++ sdio.o sdio_ops.o sdio_bus.o \ ++ sdio_cis.o sdio_io.o sdio_irq.o + diff --git a/target/linux/generic-2.6/patches-2.6.23/301-mmc_sdhci_fixes.patch b/target/linux/generic-2.6/patches-2.6.23/301-mmc_sdhci_fixes.patch new file mode 100644 index 0000000000..e6152b219f --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/301-mmc_sdhci_fixes.patch @@ -0,0 +1,152 @@ +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -481,16 +481,16 @@ + * Controller doesn't count down when in single block mode. + */ + if (data->blocks == 1) +- blocks = (data->error == MMC_ERR_NONE) ? 0 : 1; ++ blocks = (data->error == 0) ? 0 : 1; + else + blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT); + data->bytes_xfered = data->blksz * (data->blocks - blocks); + +- if ((data->error == MMC_ERR_NONE) && blocks) { ++ if (!data->error && blocks) { + printk(KERN_ERR "%s: Controller signalled completion even " + "though there were blocks left.\n", + mmc_hostname(host->mmc)); +- data->error = MMC_ERR_FAILED; ++ data->error = -EIO; + } + + if (data->stop) { +@@ -498,7 +498,7 @@ + * The controller needs a reset of internal state machines + * upon error conditions. + */ +- if (data->error != MMC_ERR_NONE) { ++ if (data->error) { + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + } +@@ -533,7 +533,7 @@ + printk(KERN_ERR "%s: Controller never released " + "inhibit bit(s).\n", mmc_hostname(host->mmc)); + sdhci_dumpregs(host); +- cmd->error = MMC_ERR_FAILED; ++ cmd->error = -EIO; + tasklet_schedule(&host->finish_tasklet); + return; + } +@@ -554,7 +554,7 @@ + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { + printk(KERN_ERR "%s: Unsupported response type!\n", + mmc_hostname(host->mmc)); +- cmd->error = MMC_ERR_INVALID; ++ cmd->error = -EINVAL; + tasklet_schedule(&host->finish_tasklet); + return; + } +@@ -601,7 +601,7 @@ + } + } + +- host->cmd->error = MMC_ERR_NONE; ++ host->cmd->error = 0; + + if (host->data && host->data_early) + sdhci_finish_data(host); +@@ -722,7 +722,7 @@ + host->mrq = mrq; + + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { +- host->mrq->cmd->error = MMC_ERR_TIMEOUT; ++ host->mrq->cmd->error = -ENOMEDIUM; + tasklet_schedule(&host->finish_tasklet); + } else + sdhci_send_command(host, mrq->cmd); +@@ -831,7 +831,7 @@ + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + +- host->mrq->cmd->error = MMC_ERR_FAILED; ++ host->mrq->cmd->error = -ENOMEDIUM; + tasklet_schedule(&host->finish_tasklet); + } + } +@@ -859,9 +859,9 @@ + * The controller needs a reset of internal state machines + * upon error conditions. + */ +- if ((mrq->cmd->error != MMC_ERR_NONE) || +- (mrq->data && ((mrq->data->error != MMC_ERR_NONE) || +- (mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) { ++ if (mrq->cmd->error || ++ (mrq->data && (mrq->data->error || ++ (mrq->data->stop && mrq->data->stop->error)))) { + + /* Some controllers need this kick or reset won't work here */ + if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { +@@ -906,13 +906,13 @@ + sdhci_dumpregs(host); + + if (host->data) { +- host->data->error = MMC_ERR_TIMEOUT; ++ host->data->error = -ETIMEDOUT; + sdhci_finish_data(host); + } else { + if (host->cmd) +- host->cmd->error = MMC_ERR_TIMEOUT; ++ host->cmd->error = -ETIMEDOUT; + else +- host->mrq->cmd->error = MMC_ERR_TIMEOUT; ++ host->mrq->cmd->error = -ETIMEDOUT; + + tasklet_schedule(&host->finish_tasklet); + } +@@ -941,13 +941,12 @@ + } + + if (intmask & SDHCI_INT_TIMEOUT) +- host->cmd->error = MMC_ERR_TIMEOUT; +- else if (intmask & SDHCI_INT_CRC) +- host->cmd->error = MMC_ERR_BADCRC; +- else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) +- host->cmd->error = MMC_ERR_FAILED; ++ host->cmd->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | ++ SDHCI_INT_INDEX)) ++ host->cmd->error = -EILSEQ; + +- if (host->cmd->error != MMC_ERR_NONE) ++ if (host->cmd->error) + tasklet_schedule(&host->finish_tasklet); + else if (intmask & SDHCI_INT_RESPONSE) + sdhci_finish_command(host); +@@ -974,13 +973,11 @@ + } + + if (intmask & SDHCI_INT_DATA_TIMEOUT) +- host->data->error = MMC_ERR_TIMEOUT; +- else if (intmask & SDHCI_INT_DATA_CRC) +- host->data->error = MMC_ERR_BADCRC; +- else if (intmask & SDHCI_INT_DATA_END_BIT) +- host->data->error = MMC_ERR_FAILED; ++ host->data->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) ++ host->data->error = -EILSEQ; + +- if (host->data->error != MMC_ERR_NONE) ++ if (host->data->error) + sdhci_finish_data(host); + else { + if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) +@@ -1312,7 +1309,7 @@ + mmc->ops = &sdhci_ops; + mmc->f_min = host->max_clk / 256; + mmc->f_max = host->max_clk; +- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; ++ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; + + if (caps & SDHCI_CAN_DO_HISPD) + mmc->caps |= MMC_CAP_SD_HIGHSPEED; diff --git a/target/linux/generic-2.6/patches-2.6.23/400-ledtrig_morse.patch b/target/linux/generic-2.6/patches-2.6.23/400-ledtrig_morse.patch new file mode 100644 index 0000000000..c582480996 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/400-ledtrig_morse.patch @@ -0,0 +1,18 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -133,4 +133,8 @@ + load average. + If unsure, say Y. + ++config LEDS_TRIGGER_MORSE ++ tristate "LED Morse Trigger" ++ depends on LEDS_TRIGGERS ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -22,3 +22,4 @@ + obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o + obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o + obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o ++obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o diff --git a/target/linux/generic-2.6/patches-2.6.23/401-led_alix.patch b/target/linux/generic-2.6/patches-2.6.23/401-led_alix.patch new file mode 100644 index 0000000000..a40ea02c1e --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/401-led_alix.patch @@ -0,0 +1,25 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -81,6 +81,12 @@ + help + This option enables support for the PCEngines WRAP programmable LEDs. + ++config LEDS_ALIX ++ tristate "LED Support for the ALIX 2/3 boards" ++ depends on LEDS_CLASS ++ help ++ This option enables support for the three LEDs on the PCEngines ALIX 2/3 boards. ++ + config LEDS_H1940 + tristate "LED Support for iPAQ H1940 device" + depends LEDS_CLASS && ARCH_H1940 +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -14,6 +14,7 @@ + obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o + obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o + obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o ++obj-$(CONFIG_LEDS_ALIX) += leds-alix.o + obj-$(CONFIG_LEDS_H1940) += leds-h1940.o + obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o + obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o diff --git a/target/linux/generic-2.6/patches-2.6.23/402-ledtrig_default_on.patch b/target/linux/generic-2.6/patches-2.6.23/402-ledtrig_default_on.patch new file mode 100644 index 0000000000..2dabf931a3 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/402-ledtrig_default_on.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -143,4 +143,11 @@ + tristate "LED Morse Trigger" + depends on LEDS_TRIGGERS + ++config LEDS_TRIGGER_DEFAULT_ON ++ tristate "LED Default ON Trigger" ++ depends on LEDS_TRIGGERS ++ help ++ This allows LEDs to be initialised in the ON state. ++ If unsure, say Y. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -24,3 +24,4 @@ + obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o + obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o ++obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o diff --git a/target/linux/generic-2.6/patches-2.6.23/403-ledtrig_netdev.patch b/target/linux/generic-2.6/patches-2.6.23/403-ledtrig_netdev.patch new file mode 100644 index 0000000000..2b17d3385a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/403-ledtrig_netdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -150,4 +150,11 @@ + This allows LEDs to be initialised in the ON state. + If unsure, say Y. + ++config LEDS_TRIGGER_NETDEV ++ tristate "LED Netdev Trigger" ++ depends on LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by network device activity. ++ If unsure, say Y. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -25,3 +25,4 @@ + obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o ++obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o diff --git a/target/linux/generic-2.6/patches-2.6.23/410-gpio_buttons.patch b/target/linux/generic-2.6/patches-2.6.23/410-gpio_buttons.patch new file mode 100644 index 0000000000..c7ede5dfb9 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/410-gpio_buttons.patch @@ -0,0 +1,30 @@ +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -183,4 +183,20 @@ + Say Y here if you want to support the built-in real time clock + of the HP SDC controller. + ++config INPUT_GPIO_BUTTONS ++ tristate "Polled GPIO buttons interface" ++ depends on GENERIC_GPIO ++ select INPUT_POLLDEV ++ help ++ This driver implements support for buttons connected ++ to GPIO pins of various CPUs (and some other chips). ++ ++ Say Y here if your device has buttons connected ++ directly to such GPIO pins. Your board-specific ++ setup logic must also provide a platform device, ++ with configuration data saying which GPIOs are used. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called gpio-buttons. ++ + endif +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -18,3 +18,4 @@ + obj-$(CONFIG_INPUT_YEALINK) += yealink.o + obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o + obj-$(CONFIG_INPUT_UINPUT) += uinput.o ++obj-$(CONFIG_INPUT_GPIO_BUTTONS) += gpio_buttons.o diff --git a/target/linux/generic-2.6/patches-2.6.23/420-gpiodev.patch b/target/linux/generic-2.6/patches-2.6.23/420-gpiodev.patch new file mode 100644 index 0000000000..1508685b65 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/420-gpiodev.patch @@ -0,0 +1,26 @@ +--- a/drivers/char/Kconfig ++++ b/drivers/char/Kconfig +@@ -970,6 +970,13 @@ + + If compiled as a module, it will be called cs5535_gpio. + ++config GPIO_DEVICE ++ tristate "GPIO device support" ++ depends on GENERIC_GPIO ++ help ++ Say Y to enable Linux GPIO device support. This allows control of ++ GPIO pins using a character device ++ + config GPIO_VR41XX + tristate "NEC VR4100 series General-purpose I/O Unit support" + depends on CPU_VR41XX +--- a/drivers/char/Makefile ++++ b/drivers/char/Makefile +@@ -93,6 +93,7 @@ + obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o + obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o + obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o ++obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o + obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o + obj-$(CONFIG_GPIO_TB0219) += tb0219.o + obj-$(CONFIG_TELCLOCK) += tlclk.o diff --git a/target/linux/generic-2.6/patches-2.6.23/510-yaffs_support.patch b/target/linux/generic-2.6/patches-2.6.23/510-yaffs_support.patch new file mode 100644 index 0000000000..a48dfbbd2d --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/510-yaffs_support.patch @@ -0,0 +1,17 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -419,6 +419,7 @@ + + source "fs/xfs/Kconfig" + source "fs/gfs2/Kconfig" ++source "fs/yaffs2/Kconfig" + + config OCFS2_FS + tristate "OCFS2 file system support" +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -120,3 +120,4 @@ + obj-$(CONFIG_DEBUG_FS) += debugfs/ + obj-$(CONFIG_OCFS2_FS) += ocfs2/ + obj-$(CONFIG_GFS2_FS) += gfs2/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ diff --git a/target/linux/generic-2.6/patches-2.6.23/600-phy_extension.patch b/target/linux/generic-2.6/patches-2.6.23/600-phy_extension.patch new file mode 100644 index 0000000000..cf874169f8 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/600-phy_extension.patch @@ -0,0 +1,63 @@ +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -344,6 +344,50 @@ + } + EXPORT_SYMBOL(phy_ethtool_gset); + ++int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr) ++{ ++ u32 cmd; ++ int tmp; ++ struct ethtool_cmd ecmd = { ETHTOOL_GSET }; ++ struct ethtool_value edata = { ETHTOOL_GLINK }; ++ ++ if (get_user(cmd, (u32 *) useraddr)) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case ETHTOOL_GSET: ++ phy_ethtool_gset(phydev, &ecmd); ++ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) ++ return -EFAULT; ++ return 0; ++ ++ case ETHTOOL_SSET: ++ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) ++ return -EFAULT; ++ return phy_ethtool_sset(phydev, &ecmd); ++ ++ case ETHTOOL_NWAY_RST: ++ /* if autoneg is off, it's an error */ ++ tmp = phy_read(phydev, MII_BMCR); ++ if (tmp & BMCR_ANENABLE) { ++ tmp |= (BMCR_ANRESTART); ++ phy_write(phydev, MII_BMCR, tmp); ++ return 0; ++ } ++ return -EINVAL; ++ ++ case ETHTOOL_GLINK: ++ edata.data = (phy_read(phydev, ++ MII_BMSR) & BMSR_LSTATUS) ? 1 : 0; ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ return -EOPNOTSUPP; ++} ++EXPORT_SYMBOL(phy_ethtool_ioctl); ++ + /** + * phy_mii_ioctl - generic PHY MII ioctl interface + * @phydev: the phy_device struct +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -393,6 +393,7 @@ + void phy_stop_machine(struct phy_device *phydev); + int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd); + int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd); ++int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr); + int phy_mii_ioctl(struct phy_device *phydev, + struct mii_ioctl_data *mii_data, int cmd); + int phy_start_interrupts(struct phy_device *phydev); diff --git a/target/linux/generic-2.6/patches-2.6.23/610-phy_detect.patch b/target/linux/generic-2.6/patches-2.6.23/610-phy_detect.patch new file mode 100644 index 0000000000..87af641bc3 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/610-phy_detect.patch @@ -0,0 +1,26 @@ +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -131,6 +131,9 @@ + struct phy_device *phydev = to_phy_device(dev); + struct phy_driver *phydrv = to_phy_driver(drv); + ++ if (phydrv->detect) ++ return (phydrv->detect(phydev->bus, phydev->addr)); ++ + return ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask)); + } +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -319,6 +319,11 @@ + u32 features; + u32 flags; + ++ /* Called during discovery to test if the ++ * device can attach to the bus, even if ++ * phy id and mask do not match */ ++ bool (*detect)(struct mii_bus *bus, int addr); ++ + /* Called to initialize the PHY, + * including after a reset */ + int (*config_init)(struct phy_device *phydev); diff --git a/target/linux/generic-2.6/patches-2.6.23/620-phy_adm6996.patch b/target/linux/generic-2.6/patches-2.6.23/620-phy_adm6996.patch new file mode 100644 index 0000000000..c7aadae3ec --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/620-phy_adm6996.patch @@ -0,0 +1,22 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -60,6 +60,11 @@ + ---help--- + Currently supports the IP175C PHY. + ++config ADM6996_PHY ++ tristate "Driver for ADM6996 switches" ++ ---help--- ++ Currently supports the ADM6996F switch ++ + config FIXED_PHY + tristate "Drivers for PHY emulation on fixed speed/link" + ---help--- +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -12,4 +12,5 @@ + obj-$(CONFIG_VITESSE_PHY) += vitesse.o + obj-$(CONFIG_BROADCOM_PHY) += broadcom.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o ++obj-$(CONFIG_ADM6996_PHY) += adm6996.o + obj-$(CONFIG_FIXED_PHY) += fixed.o diff --git a/target/linux/generic-2.6/patches-2.6.23/630-phy_packets.patch b/target/linux/generic-2.6/patches-2.6.23/630-phy_packets.patch new file mode 100644 index 0000000000..32d6c2fddd --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/630-phy_packets.patch @@ -0,0 +1,60 @@ +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -44,6 +44,18 @@ + extern int mdio_bus_init(void); + extern void mdio_bus_exit(void); + ++static int generic_receive_skb(struct sk_buff *skb) ++{ ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ return netif_receive_skb(skb); ++} ++ ++static int generic_rx(struct sk_buff *skb) ++{ ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ return netif_rx(skb); ++} ++ + struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) + { + struct phy_device *dev; +@@ -67,6 +79,8 @@ + dev->bus = bus; + + dev->state = PHY_DOWN; ++ dev->netif_receive_skb = &generic_receive_skb; ++ dev->netif_rx = &generic_rx; + + spin_lock_init(&dev->lock); + +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -289,6 +289,17 @@ + void (*adjust_link)(struct net_device *dev); + + void (*adjust_state)(struct net_device *dev); ++ ++ /* ++ * By default these point to the original functions ++ * with the same name. adding them to the phy_device ++ * allows the phy driver to override them for packet ++ * mangling if the ethernet driver supports it ++ * This is required to support some really horrible ++ * switches such as the Marvell 88E6060 ++ */ ++ int (*netif_receive_skb)(struct sk_buff *skb); ++ int (*netif_rx)(struct sk_buff *skb); + }; + #define to_phy_device(d) container_of(d, struct phy_device, dev) + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -426,6 +426,7 @@ + void *ax25_ptr; /* AX.25 specific data */ + struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, + assign before registering */ ++ void *phy_ptr; /* PHY device specific data */ + + /* + * Cache line mostly used on receive path (including eth_type_trans()) diff --git a/target/linux/generic-2.6/patches-2.6.23/640-mvswitch.patch b/target/linux/generic-2.6/patches-2.6.23/640-mvswitch.patch new file mode 100644 index 0000000000..ba628326cb --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/640-mvswitch.patch @@ -0,0 +1,46 @@ +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -65,6 +65,12 @@ + ---help--- + Currently supports the ADM6996F switch + ++config MVSWITCH_PHY ++ tristate "Driver for Marvell switches" ++ select VLAN_8021Q ++ ---help--- ++ Currently supports the Marvell 88E6060 switch. ++ + config FIXED_PHY + tristate "Drivers for PHY emulation on fixed speed/link" + ---help--- +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -13,4 +13,5 @@ + obj-$(CONFIG_BROADCOM_PHY) += broadcom.o + obj-$(CONFIG_ICPLUS_PHY) += icplus.o + obj-$(CONFIG_ADM6996_PHY) += adm6996.o ++obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o + obj-$(CONFIG_FIXED_PHY) += fixed.o +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -35,6 +35,12 @@ + #include + #include + ++static void mdio_dev_release(struct device *dev) ++{ ++ /* nothing to do */ ++} ++ ++ + /** + * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * @bus: target mii_bus +@@ -85,6 +91,7 @@ + + phydev->dev.parent = bus->dev; + phydev->dev.bus = &mdio_bus_type; ++ phydev->dev.release = mdio_dev_release; + snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i); + + phydev->bus = bus; diff --git a/target/linux/generic-2.6/patches-2.6.23/700-unable_to_open_console.patch b/target/linux/generic-2.6/patches-2.6.23/700-unable_to_open_console.patch new file mode 100644 index 0000000000..9f707363b4 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/700-unable_to_open_console.patch @@ -0,0 +1,11 @@ +--- a/init/main.c ++++ b/init/main.c +@@ -780,7 +780,7 @@ + numa_default_policy(); + + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) +- printk(KERN_WARNING "Warning: unable to open an initial console.\n"); ++ printk(KERN_WARNING "Please be patient, while OpenWrt loads ...\n"); + + (void) sys_dup(0); + (void) sys_dup(0); diff --git a/target/linux/generic-2.6/patches-2.6.23/801-usb_serial_endpoint_size.patch b/target/linux/generic-2.6/patches-2.6.23/801-usb_serial_endpoint_size.patch new file mode 100644 index 0000000000..1370c8c3c7 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/801-usb_serial_endpoint_size.patch @@ -0,0 +1,25 @@ +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -58,6 +58,7 @@ + drivers depend on it. + */ + ++static ushort maxSize = 0; + static int debug; + static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ + static DEFINE_MUTEX(table_lock); +@@ -866,7 +867,7 @@ + dev_err(&interface->dev, "No free urbs available\n"); + goto probe_error; + } +- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); ++ buffer_size = (endpoint->wMaxPacketSize > maxSize) ? endpoint->wMaxPacketSize : maxSize; + port->bulk_in_size = buffer_size; + port->bulk_in_endpointAddress = endpoint->bEndpointAddress; + port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); +@@ -1276,3 +1277,5 @@ + + module_param(debug, bool, S_IRUGO | S_IWUSR); + MODULE_PARM_DESC(debug, "Debug enabled or not"); ++module_param(maxSize, ushort,0); ++MODULE_PARM_DESC(maxSize,"User specified USB endpoint size"); diff --git a/target/linux/generic-2.6/patches-2.6.23/900-headers_type_and_time.patch b/target/linux/generic-2.6/patches-2.6.23/900-headers_type_and_time.patch new file mode 100644 index 0000000000..9dd011ca5a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/900-headers_type_and_time.patch @@ -0,0 +1,46 @@ +--- a/include/linux/time.h ++++ b/include/linux/time.h +@@ -1,6 +1,10 @@ + #ifndef _LINUX_TIME_H + #define _LINUX_TIME_H + ++#ifndef __KERNEL__ ++#include ++#else ++ + #include + + #ifdef __KERNEL__ +@@ -227,4 +231,6 @@ + */ + #define TIMER_ABSTIME 0x01 + ++#endif /* __KERNEL__ DEBIAN */ ++ + #endif +--- a/include/linux/types.h ++++ b/include/linux/types.h +@@ -1,6 +1,14 @@ + #ifndef _LINUX_TYPES_H + #define _LINUX_TYPES_H + ++/* Debian: Use userland types instead. */ ++#ifndef __KERNEL__ ++# include ++/* For other kernel headers. */ ++# include ++# include ++#else ++ + #ifdef __KERNEL__ + + #define BITS_TO_LONGS(bits) \ +@@ -162,6 +170,8 @@ + + #endif /* __KERNEL_STRICT_NAMES */ + ++#endif /* __KERNEL__ DEBIAN */ ++ + /* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. diff --git a/target/linux/generic-2.6/patches-2.6.23/902-darwin_scripts_include.patch b/target/linux/generic-2.6/patches-2.6.23/902-darwin_scripts_include.patch new file mode 100644 index 0000000000..9bf058bc74 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/902-darwin_scripts_include.patch @@ -0,0 +1,102 @@ +--- a/scripts/genksyms/parse.c_shipped ++++ b/scripts/genksyms/parse.c_shipped +@@ -144,7 +144,9 @@ + + + #include ++#ifndef __APPLE__ + #include ++#endif + #include "genksyms.h" + + static int is_typedef; +--- a/scripts/genksyms/parse.y ++++ b/scripts/genksyms/parse.y +@@ -24,7 +24,9 @@ + %{ + + #include ++#ifndef __APPLE__ + #include ++#endif + #include "genksyms.h" + + static int is_typedef; +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -28,6 +28,35 @@ + #include + #include + #include ++#ifdef __APPLE__ ++/* Darwin has no memmem implementation, this one is ripped of the uClibc-0.9.28 source */ ++void *memmem (const void *haystack, size_t haystack_len, ++ const void *needle, size_t needle_len) ++{ ++ const char *begin; ++ const char *const last_possible ++ = (const char *) haystack + haystack_len - needle_len; ++ ++ if (needle_len == 0) ++ /* The first occurrence of the empty string is deemed to occur at ++ the beginning of the string. */ ++ return (void *) haystack; ++ ++ /* Sanity check, otherwise the loop might search through the whole ++ memory. */ ++ if (__builtin_expect (haystack_len < needle_len, 0)) ++ return NULL; ++ ++ for (begin = (const char *) haystack; begin <= last_possible; ++begin) ++ if (begin[0] == ((const char *) needle)[0] && ++ !memcmp ((const void *) &begin[1], ++ (const void *) ((const char *) needle + 1), ++ needle_len - 1)) ++ return (void *) begin; ++ ++ return NULL; ++} ++#endif + + #define KSYM_NAME_LEN 128 + +--- a/scripts/kconfig/Makefile ++++ b/scripts/kconfig/Makefile +@@ -88,6 +88,9 @@ + # we really need to do so. (Do not call gcc as part of make mrproper) + HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) + HOST_LOADLIBES = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) ++ifeq ($(shell uname -s),Darwin) ++HOST_LOADLIBES += -lncurses ++endif + + HOST_EXTRACFLAGS += -DLOCALE + +--- a/scripts/mod/mk_elfconfig.c ++++ b/scripts/mod/mk_elfconfig.c +@@ -1,7 +1,11 @@ + #include + #include + #include ++#ifndef __APPLE__ + #include ++#else ++#include "../../../../../tools/sstrip/include/elf.h" ++#endif + + int + main(int argc, char **argv) +--- a/scripts/mod/modpost.h ++++ b/scripts/mod/modpost.h +@@ -7,7 +7,11 @@ + #include + #include + #include ++#ifndef __APPLE__ + #include ++#else ++#include "../../../../../tools/sstrip/include/elf.h" ++#endif + + #include "elfconfig.h" + diff --git a/target/linux/generic-2.6/patches-2.6.23/903-stddef_include.patch b/target/linux/generic-2.6/patches-2.6.23/903-stddef_include.patch new file mode 100644 index 0000000000..c0c01c0101 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/903-stddef_include.patch @@ -0,0 +1,17 @@ +--- a/include/linux/stddef.h ++++ b/include/linux/stddef.h +@@ -16,6 +16,7 @@ + false = 0, + true = 1 + }; ++#endif /* __KERNEL__ */ + + #undef offsetof + #ifdef __compiler_offsetof +@@ -23,6 +24,5 @@ + #else + #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + #endif +-#endif /* __KERNEL__ */ + + #endif diff --git a/target/linux/generic-2.6/patches-2.6.23/904-ls_time_locale.patch b/target/linux/generic-2.6/patches-2.6.23/904-ls_time_locale.patch new file mode 100644 index 0000000000..401b15126d --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/904-ls_time_locale.patch @@ -0,0 +1,20 @@ +--- a/scripts/gen_initramfs_list.sh ++++ b/scripts/gen_initramfs_list.sh +@@ -125,7 +125,7 @@ + str="${ftype} ${name} ${location} ${str}" + ;; + "nod") +- local dev=`LC_ALL=C ls -l "${location}"` ++ local dev=`LC_ALL=C ls -l --time-style=locale "${location}"` + local maj=`field 5 ${dev}` + local min=`field 6 ${dev}` + maj=${maj%,} +@@ -135,7 +135,7 @@ + str="${ftype} ${name} ${str} ${dev} ${maj} ${min}" + ;; + "slink") +- local target=`field 11 $(LC_ALL=C ls -l "${location}")` ++ local target=`field 11 $(LC_ALL=C ls -l --time-style=locale "${location}")` + str="${ftype} ${name} ${target} ${str}" + ;; + *) diff --git a/target/linux/generic-2.6/patches-2.6.23/905-i386_build.patch b/target/linux/generic-2.6/patches-2.6.23/905-i386_build.patch new file mode 100644 index 0000000000..cdbe0997a5 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/905-i386_build.patch @@ -0,0 +1,10 @@ +--- a/arch/i386/boot/tools/build.c ++++ b/arch/i386/boot/tools/build.c +@@ -29,7 +29,6 @@ + #include + #include + #include +-#include + #include + #include + #include diff --git a/target/linux/generic-2.6/patches-2.6.23/910-arm_binutils_build_id_fix.patch b/target/linux/generic-2.6/patches-2.6.23/910-arm_binutils_build_id_fix.patch new file mode 100644 index 0000000000..440f06017a --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/910-arm_binutils_build_id_fix.patch @@ -0,0 +1,35 @@ +From: Lennert Buytenhek +Date: Fri, 12 Oct 2007 13:38:54 +0000 (+0100) +Subject: [ARM] 4600/1: fix kernel build failure with build-id-supporting binutils +X-Git-Tag: v2.6.24-rc1~1273^2~3 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=1e621a8e3752367d4aae78a8ab00a18fb2793f34;hp=033b8ffe3f1ea8174d51d125838ac6deea60f63f + +[ARM] 4600/1: fix kernel build failure with build-id-supporting binutils + +Newer versions of binutils support --build-id, which adds an ELF +note section called ".note.gnu.build-id" to the output. On the ARM +kernel build, because there is no explicit mention of this section +in the shipped ld script, this section is placed at vaddr 0x00000000 +(whereas the normal kernel text/data typically starts at vaddr +0xc0008000), causing the output of objcopy (Image) to produce a 3G+ +file. + +This patch makes objcopy strip the .note.gnu.build-id section from +the Image file along with all other note sections, which fixes the +build. + +Signed-off-by: Lennert Buytenhek +Signed-off-by: Russell King +--- + +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -12,7 +12,7 @@ + + LDFLAGS_vmlinux :=-p --no-undefined -X + CPPFLAGS_vmlinux.lds = -DTEXT_OFFSET=$(TEXT_OFFSET) +-OBJCOPYFLAGS :=-O binary -R .note -R .comment -S ++OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S + GZFLAGS :=-9 + #CFLAGS +=-pipe + # Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb: diff --git a/target/linux/generic-2.6/patches-2.6.23/920-ppc_big_endian_io_memory_accessors.patch b/target/linux/generic-2.6/patches-2.6.23/920-ppc_big_endian_io_memory_accessors.patch new file mode 100644 index 0000000000..a3221fa9a5 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/920-ppc_big_endian_io_memory_accessors.patch @@ -0,0 +1,78 @@ +From a.othieno@bluewin.ch Tue Oct 11 07:50:21 2005 +From: Arthur Othieno +Subject: Big-endian I/O memory accessors. +Date: Tue, 11 Oct 2005 07:50:21 +1000 +X-Patchwork-ID: 2759 + +From: Arthur Othieno + +I/O memory accessors. Big endian version. For those busses/devices +that do export big-endian I/O memory. + +Of notable relevance/reference: + + http://lwn.net/Articles/132804/ + http://ozlabs.org/pipermail/linuxppc-embedded/2005-August/019798.html + http://ozlabs.org/pipermail/linuxppc-embedded/2005-August/019752.html + +Signed-off-by: Arthur Othieno +--- + +Paulus, + +A similar patch for ppc64 made it upstream with your big ppc64 merge. +This one is still sitting in http://patchwork.ozlabs.org/linuxppc/ +and didn't make it with the ppc32 equivalent. Thanks. + + + include/asm-ppc/io.h | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + + +--- +--- a/include/asm-ppc/io.h ++++ b/include/asm-ppc/io.h +@@ -453,11 +453,21 @@ + return readw(addr); + } + ++static inline unsigned int ioread16be(void __iomem *addr) ++{ ++ return in_be16(addr); ++} ++ + static inline unsigned int ioread32(void __iomem *addr) + { + return readl(addr); + } + ++static inline unsigned int ioread32be(void __iomem *addr) ++{ ++ return in_be32(addr); ++} ++ + static inline void iowrite8(u8 val, void __iomem *addr) + { + writeb(val, addr); +@@ -468,11 +478,21 @@ + writew(val, addr); + } + ++static inline void iowrite16be(u16 val, void __iomem *addr) ++{ ++ out_be16(addr, val); ++} ++ + static inline void iowrite32(u32 val, void __iomem *addr) + { + writel(val, addr); + } + ++static inline void iowrite32be(u32 val, void __iomem *addr) ++{ ++ out_be32(addr, val); ++} ++ + static inline void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) + { + _insb(addr, dst, count); diff --git a/target/linux/generic-2.6/patches-2.6.23/921-gpio_spi_driver.patch b/target/linux/generic-2.6/patches-2.6.23/921-gpio_spi_driver.patch new file mode 100644 index 0000000000..eeb7285a04 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/921-gpio_spi_driver.patch @@ -0,0 +1,24 @@ +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -100,6 +100,11 @@ + inexpensive battery powered microcontroller evaluation board. + This same cable can be used to flash new firmware. + ++config SPI_GPIO ++ tristate "GPIO API based bitbanging SPI controller" ++ depends on SPI_MASTER && GENERIC_GPIO && EXPERIMENTAL ++ select SPI_BITBANG ++ + config SPI_IMX + tristate "Freescale iMX SPI controller" + depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -16,6 +16,7 @@ + obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o + obj-$(CONFIG_SPI_AU1550) += au1550_spi.o + obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o ++obj-$(CONFIG_SPI_GPIO) += spi_gpio.o + obj-$(CONFIG_SPI_IMX) += spi_imx.o + obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o + obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o diff --git a/target/linux/generic-2.6/patches-2.6.23/922-w1_gpio_driver_backport.patch b/target/linux/generic-2.6/patches-2.6.23/922-w1_gpio_driver_backport.patch new file mode 100644 index 0000000000..fa2f38e547 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/922-w1_gpio_driver_backport.patch @@ -0,0 +1,178 @@ +--- a/drivers/w1/masters/Kconfig ++++ b/drivers/w1/masters/Kconfig +@@ -42,5 +42,15 @@ + in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like + hx4700. + ++config W1_MASTER_GPIO ++ tristate "GPIO 1-wire busmaster" ++ depends on GENERIC_GPIO ++ help ++ Say Y here if you want to communicate with your 1-wire devices using ++ GPIO pins. This driver uses the GPIO API to control the wire. ++ ++ This support is also available as a module. If so, the module ++ will be called w1-gpio.ko. ++ + endmenu + +--- a/drivers/w1/masters/Makefile ++++ b/drivers/w1/masters/Makefile +@@ -6,3 +6,4 @@ + obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o + obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o + obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o ++obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o +--- /dev/null ++++ b/drivers/w1/masters/w1-gpio.c +@@ -0,0 +1,124 @@ ++/* ++ * w1-gpio - GPIO w1 bus master driver ++ * ++ * Copyright (C) 2007 Ville Syrjala ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "../w1.h" ++#include "../w1_int.h" ++ ++#include ++ ++static void w1_gpio_write_bit_dir(void *data, u8 bit) ++{ ++ struct w1_gpio_platform_data *pdata = data; ++ ++ if (bit) ++ gpio_direction_input(pdata->pin); ++ else ++ gpio_direction_output(pdata->pin, 0); ++} ++ ++static void w1_gpio_write_bit_val(void *data, u8 bit) ++{ ++ struct w1_gpio_platform_data *pdata = data; ++ ++ gpio_set_value(pdata->pin, bit); ++} ++ ++static u8 w1_gpio_read_bit(void *data) ++{ ++ struct w1_gpio_platform_data *pdata = data; ++ ++ return gpio_get_value(pdata->pin); ++} ++ ++static int __init w1_gpio_probe(struct platform_device *pdev) ++{ ++ struct w1_bus_master *master; ++ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; ++ int err; ++ ++ if (!pdata) ++ return -ENXIO; ++ ++ master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL); ++ if (!master) ++ return -ENOMEM; ++ ++ err = gpio_request(pdata->pin, "w1"); ++ if (err) ++ goto free_master; ++ ++ master->data = pdata; ++ master->read_bit = w1_gpio_read_bit; ++ ++ if (pdata->is_open_drain) { ++ gpio_direction_output(pdata->pin, 1); ++ master->write_bit = w1_gpio_write_bit_val; ++ } else { ++ gpio_direction_input(pdata->pin); ++ master->write_bit = w1_gpio_write_bit_dir; ++ } ++ ++ err = w1_add_master_device(master); ++ if (err) ++ goto free_gpio; ++ ++ platform_set_drvdata(pdev, master); ++ ++ return 0; ++ ++ free_gpio: ++ gpio_free(pdata->pin); ++ free_master: ++ kfree(master); ++ ++ return err; ++} ++ ++static int __exit w1_gpio_remove(struct platform_device *pdev) ++{ ++ struct w1_bus_master *master = platform_get_drvdata(pdev); ++ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; ++ ++ w1_remove_master_device(master); ++ gpio_free(pdata->pin); ++ kfree(master); ++ ++ return 0; ++} ++ ++static struct platform_driver w1_gpio_driver = { ++ .driver = { ++ .name = "w1-gpio", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __exit_p(w1_gpio_remove), ++}; ++ ++static int __init w1_gpio_init(void) ++{ ++ return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe); ++} ++ ++static void __exit w1_gpio_exit(void) ++{ ++ platform_driver_unregister(&w1_gpio_driver); ++} ++ ++module_init(w1_gpio_init); ++module_exit(w1_gpio_exit); ++ ++MODULE_DESCRIPTION("GPIO w1 bus master driver"); ++MODULE_AUTHOR("Ville Syrjala "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/w1-gpio.h +@@ -0,0 +1,23 @@ ++/* ++ * w1-gpio interface to platform code ++ * ++ * Copyright (C) 2007 Ville Syrjala ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation. ++ */ ++#ifndef _LINUX_W1_GPIO_H ++#define _LINUX_W1_GPIO_H ++ ++/** ++ * struct w1_gpio_platform_data - Platform-dependent data for w1-gpio ++ * @pin: GPIO pin to use ++ * @is_open_drain: GPIO pin is configured as open drain ++ */ ++struct w1_gpio_platform_data { ++ unsigned int pin; ++ unsigned int is_open_drain:1; ++}; ++ ++#endif /* _LINUX_W1_GPIO_H */ diff --git a/target/linux/rb532/Makefile b/target/linux/rb532/Makefile index e35b81c7f1..8bc88c1a88 100644 --- a/target/linux/rb532/Makefile +++ b/target/linux/rb532/Makefile @@ -11,7 +11,7 @@ BOARD:=rb532 BOARDNAME:=Mikrotik RouterBoard 532 FEATURES:=jffs2 pci tgz -LINUX_VERSION:=2.6.24.7 +LINUX_VERSION:=2.6.23.17 include $(INCLUDE_DIR)/target.mk DEFAULT_PACKAGES += hostapd-mini kmod-madwifi diff --git a/target/linux/rb532/config-2.6.23 b/target/linux/rb532/config-2.6.23 new file mode 100644 index 0000000000..53c1517530 --- /dev/null +++ b/target/linux/rb532/config-2.6.23 @@ -0,0 +1,201 @@ +CONFIG_32BIT=y +# CONFIG_64BIT is not set +# CONFIG_8139TOO is not set +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_ATM is not set +# CONFIG_AX25 is not set +CONFIG_BASE_SMALL=0 +CONFIG_BITREVERSE=y +CONFIG_BLK_DEV_CF_MIPS=y +# CONFIG_BT is not set +# CONFIG_CPU_BIG_ENDIAN is not set +CONFIG_CPU_HAS_LLSC=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +# CONFIG_CPU_LOONGSON2 is not set +CONFIG_CPU_MIPS32=y +CONFIG_CPU_MIPS32_R1=y +# CONFIG_CPU_MIPS32_R2 is not set +# CONFIG_CPU_MIPS64_R1 is not set +# CONFIG_CPU_MIPS64_R2 is not set +CONFIG_CPU_MIPSR1=y +# CONFIG_CPU_NEVADA is not set +# CONFIG_CPU_R10000 is not set +# CONFIG_CPU_R3000 is not set +# CONFIG_CPU_R4300 is not set +# CONFIG_CPU_R4X00 is not set +# CONFIG_CPU_R5000 is not set +# CONFIG_CPU_R5432 is not set +# CONFIG_CPU_R6000 is not set +# CONFIG_CPU_R8000 is not set +# CONFIG_CPU_RM7000 is not set +# CONFIG_CPU_RM9000 is not set +# CONFIG_CPU_SB1 is not set +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +# CONFIG_CPU_TX39XX is not set +# CONFIG_CPU_TX49XX is not set +# CONFIG_CPU_VR41XX is not set +CONFIG_DEVPORT=y +# CONFIG_DM9000 is not set +CONFIG_DMA_NEED_PCI_MAP_STATE=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_EXT2_FS=y +CONFIG_FS_POSIX_ACL=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_GPIO=y +# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HID_SUPPORT=y +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=y +# CONFIG_I2C is not set +# CONFIG_IDE is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INPUT=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_IRQ_CPU=y +CONFIG_KEXEC=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_KORINA=y +# CONFIG_LEDS_ALIX is not set +# CONFIG_LEDS_GPIO is not set +CONFIG_LEDS_RB500=y +# CONFIG_LEMOTE_FULONG is not set +# CONFIG_MACH_ALCHEMY is not set +# CONFIG_MACH_DECSTATION is not set +# CONFIG_MACH_JAZZ is not set +# CONFIG_MACH_VR41XX is not set +CONFIG_MIKROTIK_RB500=y +CONFIG_MINI_FO=m +CONFIG_MIPS=y +# CONFIG_MIPS_ATLAS is not set +# CONFIG_MIPS_COBALT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=4 +# CONFIG_MIPS_MALTA is not set +CONFIG_MIPS_MT_DISABLED=y +# CONFIG_MIPS_MT_SMP is not set +# CONFIG_MIPS_MT_SMTC is not set +# CONFIG_MIPS_SEAD is not set +# CONFIG_MIPS_SIM is not set +CONFIG_MTD=y +# CONFIG_MTD_ABSENT is not set +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_BLOCK2MTD=y +# CONFIG_MTD_CFI is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CHAR=y +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_CONCAT is not set +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MTDRAM is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_CAFE is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_ONENAND is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_PLATRAM is not set +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NET_SCH_ESFQ_NFCT is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NO_IOPORT is not set +# CONFIG_PAGE_SIZE_16KB is not set +CONFIG_PAGE_SIZE_4KB=y +# CONFIG_PAGE_SIZE_64KB is not set +# CONFIG_PAGE_SIZE_8KB is not set +# CONFIG_PCIPCWATCHDOG is not set +CONFIG_PCI=y +# CONFIG_PMC_MSP is not set +# CONFIG_PMC_YOSEMITE is not set +# CONFIG_PNX8550_JBS is not set +# CONFIG_PNX8550_STB810 is not set +CONFIG_RC32434_WDT=y +# CONFIG_RTC is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y +CONFIG_SCSI_WAIT_SCAN=m +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SGI_IP22 is not set +# CONFIG_SGI_IP27 is not set +# CONFIG_SGI_IP32 is not set +# CONFIG_SIBYTE_BIGSUR is not set +# CONFIG_SIBYTE_CARMEL is not set +# CONFIG_SIBYTE_CRHINE is not set +# CONFIG_SIBYTE_CRHONE is not set +# CONFIG_SIBYTE_LITTLESUR is not set +# CONFIG_SIBYTE_PTSWARM is not set +# CONFIG_SIBYTE_RHONE is not set +# CONFIG_SIBYTE_SENTOSA is not set +# CONFIG_SIBYTE_SWARM is not set +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_SOUND is not set +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SWAP_IO_SPACE=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +# CONFIG_TC35815 is not set +# CONFIG_TOSHIBA_JMR3927 is not set +# CONFIG_TOSHIBA_RBTX4927 is not set +# CONFIG_TOSHIBA_RBTX4938 is not set +CONFIG_TRAD_SIGNALS=y +CONFIG_USB=m +# CONFIG_USER_NS is not set +# CONFIG_VGASTATE is not set +CONFIG_VIA_RHINE=y +# CONFIG_VIA_RHINE_MMIO is not set +CONFIG_VIA_RHINE_NAPI=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=0 +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +CONFIG_YAFFS_DOES_ECC=y +CONFIG_YAFFS_ECC_WRONG_ORDER=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +CONFIG_YAFFS_YAFFS1=y +CONFIG_YAFFS_YAFFS2=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/rb532/config-default b/target/linux/rb532/config-default deleted file mode 100644 index 366f655769..0000000000 --- a/target/linux/rb532/config-default +++ /dev/null @@ -1,258 +0,0 @@ -CONFIG_32BIT=y -# CONFIG_64BIT is not set -# CONFIG_8139TOO is not set -# CONFIG_ARCH_HAS_ILOG2_U32 is not set -# CONFIG_ARCH_HAS_ILOG2_U64 is not set -CONFIG_ARCH_POPULATES_NODE_MAP=y -# CONFIG_ARCH_SUPPORTS_MSI is not set -CONFIG_ARCH_SUPPORTS_OPROFILE=y -# CONFIG_ATM is not set -# CONFIG_AX25 is not set -CONFIG_BASE_SMALL=0 -# CONFIG_BCM47XX is not set -CONFIG_BITREVERSE=y -CONFIG_BLK_DEV_CF_MIPS=y -# CONFIG_BOOT_PRINTK_DELAY is not set -# CONFIG_BT is not set -CONFIG_CEVT_R4K=y -# CONFIG_CPU_BIG_ENDIAN is not set -CONFIG_CPU_HAS_LLSC=y -CONFIG_CPU_HAS_PREFETCH=y -CONFIG_CPU_HAS_SYNC=y -CONFIG_CPU_LITTLE_ENDIAN=y -# CONFIG_CPU_LOONGSON2 is not set -CONFIG_CPU_MIPS32=y -CONFIG_CPU_MIPS32_R1=y -# CONFIG_CPU_MIPS32_R2 is not set -# CONFIG_CPU_MIPS64_R1 is not set -# CONFIG_CPU_MIPS64_R2 is not set -CONFIG_CPU_MIPSR1=y -# CONFIG_CPU_NEVADA is not set -# CONFIG_CPU_R10000 is not set -# CONFIG_CPU_R3000 is not set -# CONFIG_CPU_R4300 is not set -# CONFIG_CPU_R4X00 is not set -# CONFIG_CPU_R5000 is not set -# CONFIG_CPU_R5432 is not set -# CONFIG_CPU_R6000 is not set -# CONFIG_CPU_R8000 is not set -# CONFIG_CPU_RM7000 is not set -# CONFIG_CPU_RM9000 is not set -# CONFIG_CPU_SB1 is not set -CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y -CONFIG_CPU_SUPPORTS_HIGHMEM=y -# CONFIG_CPU_TX39XX is not set -# CONFIG_CPU_TX49XX is not set -# CONFIG_CPU_VR41XX is not set -CONFIG_CSRC_R4K=y -# CONFIG_DEBUG_DEVRES is not set -# CONFIG_DEBUG_DRIVER is not set -# CONFIG_DEBUG_INFO is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_KOBJECT is not set -# CONFIG_DEBUG_LIST is not set -# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set -# CONFIG_DEBUG_LOCK_ALLOC is not set -# CONFIG_DEBUG_MUTEXES is not set -# CONFIG_DEBUG_RT_MUTEXES is not set -# CONFIG_DEBUG_SG is not set -# CONFIG_DEBUG_SHIRQ is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set -# CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_DEBUG_VM is not set -# CONFIG_DETECT_SOFTLOCKUP is not set -CONFIG_DEVPORT=y -# CONFIG_DM9000 is not set -CONFIG_DMA_NEED_PCI_MAP_STATE=y -CONFIG_DMA_NONCOHERENT=y -CONFIG_EXT2_FS=y -# CONFIG_FAULT_INJECTION is not set -CONFIG_FORCED_INLINING=y -CONFIG_FS_POSIX_ACL=y -CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BUILD=y -CONFIG_GENERIC_CMOS_UPDATE=y -CONFIG_GENERIC_FIND_NEXT_BIT=y -CONFIG_GENERIC_GPIO=y -# CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set -CONFIG_HAS_DMA=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT=y -CONFIG_HID=m -CONFIG_HID_SUPPORT=y -# CONFIG_HIGH_RES_TIMERS is not set -CONFIG_HW_HAS_PCI=y -CONFIG_HW_RANDOM=y -# CONFIG_I2C is not set -# CONFIG_IBM_NEW_EMAC_EMAC4 is not set -# CONFIG_IBM_NEW_EMAC_RGMII is not set -# CONFIG_IBM_NEW_EMAC_TAH is not set -# CONFIG_IBM_NEW_EMAC_ZMII is not set -# CONFIG_IDE is not set -CONFIG_INITRAMFS_SOURCE="" -CONFIG_INPUT=y -CONFIG_INPUT_KEYBOARD=y -CONFIG_IRQ_CPU=y -CONFIG_KALLSYMS=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y -CONFIG_KEXEC=y -# CONFIG_KEYBOARD_ATKBD is not set -CONFIG_KEYBOARD_GPIO=y -# CONFIG_KEYBOARD_LKKBD is not set -# CONFIG_KEYBOARD_NEWTON is not set -# CONFIG_KEYBOARD_STOWAWAY is not set -# CONFIG_KEYBOARD_SUNKBD is not set -# CONFIG_KEYBOARD_XTKBD is not set -CONFIG_KORINA=y -CONFIG_LEDS_RB500=y -# CONFIG_LEMOTE_FULONG is not set -# CONFIG_LOCK_STAT is not set -# CONFIG_MACH_ALCHEMY is not set -# CONFIG_MACH_DECSTATION is not set -# CONFIG_MACH_JAZZ is not set -# CONFIG_MACH_VR41XX is not set -CONFIG_MIKROTIK_RB500=y -CONFIG_MINI_FO=m -CONFIG_MIPS=y -# CONFIG_MIPS_ATLAS is not set -# CONFIG_MIPS_COBALT is not set -CONFIG_MIPS_L1_CACHE_SHIFT=4 -# CONFIG_MIPS_MALTA is not set -CONFIG_MIPS_MT_DISABLED=y -# CONFIG_MIPS_MT_SMP is not set -# CONFIG_MIPS_MT_SMTC is not set -# CONFIG_MIPS_SEAD is not set -# CONFIG_MIPS_SIM is not set -# CONFIG_MIPS_UNCACHED is not set -CONFIG_MTD=y -# CONFIG_MTD_ABSENT is not set -# CONFIG_MTD_ALAUDA is not set -CONFIG_MTD_BLKDEVS=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_BLOCK2MTD=y -# CONFIG_MTD_CFI is not set -CONFIG_MTD_CFI_I1=y -CONFIG_MTD_CFI_I2=y -# CONFIG_MTD_CFI_I4 is not set -# CONFIG_MTD_CFI_I8 is not set -CONFIG_MTD_CHAR=y -# CONFIG_MTD_CMDLINE_PARTS is not set -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_CONCAT is not set -# CONFIG_MTD_DEBUG is not set -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001 is not set -# CONFIG_MTD_DOC2001PLUS is not set -# CONFIG_MTD_JEDECPROBE is not set -CONFIG_MTD_MAP_BANK_WIDTH_1=y -# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set -CONFIG_MTD_MAP_BANK_WIDTH_2=y -# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set -CONFIG_MTD_MAP_BANK_WIDTH_4=y -# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set -# CONFIG_MTD_MTDRAM is not set -CONFIG_MTD_NAND=y -# CONFIG_MTD_NAND_CAFE is not set -# CONFIG_MTD_NAND_DISKONCHIP is not set -# CONFIG_MTD_NAND_ECC_SMC is not set -CONFIG_MTD_NAND_IDS=y -# CONFIG_MTD_NAND_MUSEUM_IDS is not set -# CONFIG_MTD_NAND_NANDSIM is not set -CONFIG_MTD_NAND_PLATFORM=y -CONFIG_MTD_NAND_VERIFY_WRITE=y -# CONFIG_MTD_ONENAND is not set -CONFIG_MTD_PARTITIONS=y -# CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_PLATRAM is not set -# CONFIG_MTD_PMC551 is not set -# CONFIG_MTD_RAM is not set -# CONFIG_MTD_REDBOOT_PARTS is not set -# CONFIG_MTD_ROM is not set -# CONFIG_MTD_SLRAM is not set -# CONFIG_NATSEMI is not set -# CONFIG_NE2K_PCI is not set -# CONFIG_NET_SCH_ESFQ_NFCT is not set -# CONFIG_NET_VENDOR_3COM is not set -CONFIG_NF_CONNTRACK=y -# CONFIG_NO_IOPORT is not set -# CONFIG_PAGE_SIZE_16KB is not set -CONFIG_PAGE_SIZE_4KB=y -# CONFIG_PAGE_SIZE_64KB is not set -# CONFIG_PAGE_SIZE_8KB is not set -CONFIG_PCI=y -# CONFIG_PCIPCWATCHDOG is not set -# CONFIG_PCI_DEBUG is not set -CONFIG_PCI_DOMAINS=y -# CONFIG_PMC_MSP is not set -# CONFIG_PMC_YOSEMITE is not set -# CONFIG_PNX8550_JBS is not set -# CONFIG_PNX8550_STB810 is not set -# CONFIG_PROVE_LOCKING is not set -CONFIG_RC32434_WDT=y -# CONFIG_RCU_TORTURE_TEST is not set -# CONFIG_RTC is not set -CONFIG_RTC_LIB=y -# CONFIG_RT_MUTEX_TESTER is not set -# CONFIG_RUNTIME_DEBUG is not set -CONFIG_RWSEM_GENERIC_SPINLOCK=y -# CONFIG_SCHEDSTATS is not set -# CONFIG_SCHED_DEBUG is not set -CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y -CONFIG_SCSI_WAIT_SCAN=m -# CONFIG_SERIAL_8250_EXTENDED is not set -# CONFIG_SGI_IP22 is not set -# CONFIG_SGI_IP27 is not set -# CONFIG_SGI_IP32 is not set -# CONFIG_SIBYTE_BIGSUR is not set -# CONFIG_SIBYTE_CARMEL is not set -# CONFIG_SIBYTE_CRHINE is not set -# CONFIG_SIBYTE_CRHONE is not set -# CONFIG_SIBYTE_LITTLESUR is not set -# CONFIG_SIBYTE_PTSWARM is not set -# CONFIG_SIBYTE_RHONE is not set -# CONFIG_SIBYTE_SENTOSA is not set -# CONFIG_SIBYTE_SWARM is not set -CONFIG_SLABINFO=y -# CONFIG_SOFT_WATCHDOG is not set -# CONFIG_SOUND is not set -# CONFIG_SPARSEMEM_STATIC is not set -# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set -CONFIG_SSB_POSSIBLE=y -CONFIG_SWAP_IO_SPACE=y -CONFIG_SYSVIPC_SYSCTL=y -CONFIG_SYS_HAS_CPU_MIPS32_R1=y -CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y -CONFIG_SYS_SUPPORTS_ARBIT_HZ=y -CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y -# CONFIG_TC35815 is not set -# CONFIG_TICK_ONESHOT is not set -# CONFIG_TIMER_STATS is not set -# CONFIG_TOSHIBA_JMR3927 is not set -# CONFIG_TOSHIBA_RBTX4927 is not set -# CONFIG_TOSHIBA_RBTX4938 is not set -CONFIG_TRAD_SIGNALS=y -CONFIG_USB=m -# CONFIG_USBPCWATCHDOG is not set -# CONFIG_USB_EHCI_HCD is not set -# CONFIG_USB_UHCI_HCD is not set -# CONFIG_USER_NS is not set -# CONFIG_VGASTATE is not set -CONFIG_VIA_RHINE=y -# CONFIG_VIA_RHINE_MMIO is not set -CONFIG_VIA_RHINE_NAPI=y -# CONFIG_YAFFS_9BYTE_TAGS is not set -CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED=y -CONFIG_YAFFS_AUTO_YAFFS2=y -CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS=0 -# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set -# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set -CONFIG_YAFFS_DOES_ECC=y -CONFIG_YAFFS_ECC_WRONG_ORDER=y -CONFIG_YAFFS_FS=y -CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y -CONFIG_YAFFS_YAFFS1=y -CONFIG_YAFFS_YAFFS2=y -CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/rb532/files-2.6.23/arch/mips/pci/fixup-rb500.c b/target/linux/rb532/files-2.6.23/arch/mips/pci/fixup-rb500.c new file mode 100644 index 0000000000..76c465fea4 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/pci/fixup-rb500.c @@ -0,0 +1,51 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include +#include + +#include + +static int __devinitdata irq_map[2][12] = { + { 0, 0, 2, 3, 2, 3, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3 } +}; + +int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + int irq = 0; + + if (dev->bus->number < 2 && PCI_SLOT(dev->devfn) < 12) { + irq = irq_map[dev->bus->number][PCI_SLOT(dev->devfn)]; + } + return irq + GROUP4_IRQ_BASE + 4; +} + diff --git a/target/linux/rb532/files-2.6.23/arch/mips/pci/ops-rc32434.c b/target/linux/rb532/files-2.6.23/arch/mips/pci/ops-rc32434.c new file mode 100644 index 0000000000..b90ab1d129 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/pci/ops-rc32434.c @@ -0,0 +1,218 @@ +/************************************************************************** + * + * BRIEF MODULE DESCRIPTION + * pci_ops for IDT EB434 board + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * Copyright 2006 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Initial Release + * + * + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + + +#define PCI_CFG_SET(bus,slot,func,off) \ + (rc32434_pci->pcicfga = (0x80000000 | \ + ((bus) << 16) | ((slot)<<11) | \ + ((func)<<8) | (off))) + +static inline int config_access(unsigned char access_type, struct pci_bus *bus, + unsigned int devfn, unsigned char where, + u32 * data) +{ + unsigned int slot = PCI_SLOT(devfn); + u8 func = PCI_FUNC(devfn); + + /* Setup address */ + PCI_CFG_SET(bus->number, slot, func, where); + rc32434_sync(); + + if (access_type == PCI_ACCESS_WRITE) + rc32434_pci->pcicfgd = *data; + else + *data = rc32434_pci->pcicfgd; + + rc32434_sync(); + + return 0; +} + + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static int read_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u8 * val) +{ + u32 data; + int ret; + + ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); + *val = (data >> ((where & 3) << 3)) & 0xff; + return ret; +} + +static int read_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u16 * val) +{ + u32 data; + int ret; + + ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); + *val = (data >> ((where & 3) << 3)) & 0xffff; + return ret; +} + +static int read_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 * val) +{ + int ret; + int delay = 1; + + if (bus->number == 0 && PCI_SLOT(devfn) > 21) + return 0; + +retry: + ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val); + + /* PCI scan: check for invalid values, device may not have + * finished initializing */ + + if (where == PCI_VENDOR_ID) { + if (ret == 0xffffffff || ret == 0x00000000 || + ret == 0x0000ffff || ret == 0xffff0000) { + + if (delay > 4) + return 0; + + delay *= 2; + msleep(delay); + goto retry; + } + } + + return ret; +} + +static int +write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, + u8 val) +{ + u32 data = 0; + + if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return -1; + + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + + +static int +write_config_word(struct pci_bus *bus, unsigned int devfn, int where, + u16 val) +{ + u32 data = 0; + + if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return -1; + + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) + return -1; + + + return PCIBIOS_SUCCESSFUL; +} + + +static int +write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, + u32 val) +{ + if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + switch (size) { + case 1: + return read_config_byte(bus, devfn, where, (u8 *) val); + case 2: + return read_config_word(bus, devfn, where, (u16 *) val); + default: + return read_config_dword(bus, devfn, where, val); + } +} + +static int pci_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) { + case 1: + return write_config_byte(bus, devfn, where, (u8) val); + case 2: + return write_config_word(bus, devfn, where, (u16) val); + default: + return write_config_dword(bus, devfn, where, val); + } +} + +struct pci_ops rc32434_pci_ops = { + .read = pci_config_read, + .write = pci_config_write, +}; diff --git a/target/linux/rb532/files-2.6.23/arch/mips/pci/pci-rc32434.c b/target/linux/rb532/files-2.6.23/arch/mips/pci/pci-rc32434.c new file mode 100644 index 0000000000..9ae596c223 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/pci/pci-rc32434.c @@ -0,0 +1,236 @@ +/************************************************************************** + * + * BRIEF MODULE DESCRIPTION + * PCI initialization for IDT EB434 board + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Initial Release + * + * + * + ************************************************************************** + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +/* define an unsigned array for the PCI registers */ +unsigned int korinaCnfgRegs[25] = { + KORINA_CNFG1, KORINA_CNFG2, KORINA_CNFG3, KORINA_CNFG4, + KORINA_CNFG5, KORINA_CNFG6, KORINA_CNFG7, KORINA_CNFG8, + KORINA_CNFG9, KORINA_CNFG10, KORINA_CNFG11, KORINA_CNFG12, + KORINA_CNFG13, KORINA_CNFG14, KORINA_CNFG15, KORINA_CNFG16, + KORINA_CNFG17, KORINA_CNFG18, KORINA_CNFG19, KORINA_CNFG20, + KORINA_CNFG21, KORINA_CNFG22, KORINA_CNFG23, KORINA_CNFG24 +}; +static struct resource rc32434_res_pci_mem1; +static struct resource rc32434_res_pci_mem2; + +static struct resource rc32434_res_pci_mem1 = { + .name = "PCI MEM1", + .start = 0x50000000, + .end = 0x5FFFFFFF, + .flags = IORESOURCE_MEM, + .parent = &rc32434_res_pci_mem1, + .sibling = NULL, + .child = &rc32434_res_pci_mem2 +}; + +static struct resource rc32434_res_pci_mem2 = { + .name = "PCI Mem2", + .start = 0x60000000, + .end = 0x6FFFFFFF, + .flags = IORESOURCE_MEM, + .parent = &rc32434_res_pci_mem1, + .sibling = NULL, + .child = NULL +}; + +static struct resource rc32434_res_pci_io1 = { + .name = "PCI I/O1", + .start = 0x18800000, + .end = 0x188FFFFF, + .flags = IORESOURCE_IO, +}; + +extern struct pci_ops rc32434_pci_ops; + +#define PCI_MEM1_START PCI_ADDR_START +#define PCI_MEM1_END PCI_ADDR_START + CPUTOPCI_MEM_WIN - 1 +#define PCI_MEM2_START PCI_ADDR_START + CPUTOPCI_MEM_WIN +#define PCI_MEM2_END PCI_ADDR_START + ( 2* CPUTOPCI_MEM_WIN) - 1 +#define PCI_IO1_START PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) +#define PCI_IO1_END PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN -1 +#define PCI_IO2_START PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN +#define PCI_IO2_END PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + (2 * CPUTOPCI_IO_WIN) -1 + + +struct pci_controller rc32434_controller2; + +struct pci_controller rc32434_controller = { + .pci_ops = &rc32434_pci_ops, + .mem_resource = &rc32434_res_pci_mem1, + .io_resource = &rc32434_res_pci_io1, + .mem_offset = 0, + .io_offset = 0, + +}; + +#ifdef __MIPSEB__ +#define PCI_ENDIAN_FLAG PCILBAC_sb_m +#else +#define PCI_ENDIAN_FLAG 0 +#endif + +static int __init rc32434_pcibridge_init(void) +{ + unsigned int pcicValue, pcicData = 0; + unsigned int dummyRead, pciCntlVal; + int loopCount; + unsigned int pciConfigAddr; + + pcicValue = rc32434_pci->pcic; + pcicValue = (pcicValue >> PCIM_SHFT) & PCIM_BIT_LEN; + if (!((pcicValue == PCIM_H_EA) || + (pcicValue == PCIM_H_IA_FIX) || + (pcicValue == PCIM_H_IA_RR))) { + printk("PCI init error!!!\n"); + /* Not in Host Mode, return ERROR */ + return -1; + } + /* Enables the Idle Grant mode, Arbiter Parking */ + pcicData |=(PCIC_igm_m|PCIC_eap_m|PCIC_en_m); + rc32434_pci->pcic = pcicData; /* Enable the PCI bus Interface */ + /* Zero out the PCI status & PCI Status Mask */ + for(;;) + { + pcicData = rc32434_pci->pcis; + if (!(pcicData & PCIS_rip_m)) + break; + } + + rc32434_pci->pcis = 0; + rc32434_pci->pcism = 0xFFFFFFFF; + /* Zero out the PCI decoupled registers */ + rc32434_pci->pcidac=0; /* disable PCI decoupled accesses at initialization */ + rc32434_pci->pcidas=0; /* clear the status */ + rc32434_pci->pcidasm=0x0000007F; /* Mask all the interrupts */ + /* Mask PCI Messaging Interrupts */ + rc32434_pci_msg->pciiic = 0; + rc32434_pci_msg->pciiim = 0xFFFFFFFF; + rc32434_pci_msg->pciioic = 0; + rc32434_pci_msg->pciioim = 0; + + + /* Setup PCILB0 as Memory Window */ + rc32434_pci->pcilba[0].a = (unsigned int) (PCI_ADDR_START); + + /* setup the PCI map address as same as the local address */ + + rc32434_pci->pcilba[0].m = (unsigned int) (PCI_ADDR_START); + + + /* Setup PCILBA1 as MEM */ + rc32434_pci->pcilba[0].c = ( ((SIZE_256MB & 0x1f) << PCILBAC_size_b) | PCI_ENDIAN_FLAG); + dummyRead = rc32434_pci->pcilba[0].c; /* flush the CPU write Buffers */ + rc32434_pci->pcilba[1].a = 0x60000000; + rc32434_pci->pcilba[1].m = 0x60000000; + + /* setup PCILBA2 as IO Window*/ + rc32434_pci->pcilba[1].c = (((SIZE_256MB & 0x1f) << PCILBAC_size_b )| PCI_ENDIAN_FLAG); + dummyRead = rc32434_pci->pcilba[1].c; /* flush the CPU write Buffers */ + rc32434_pci->pcilba[2].a = 0x18C00000; + rc32434_pci->pcilba[2].m = 0x18FFFFFF; + + /* setup PCILBA2 as IO Window*/ + rc32434_pci->pcilba[2].c = (((SIZE_4MB & 0x1f) << PCILBAC_size_b) | PCI_ENDIAN_FLAG ); + dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */ + + /* Setup PCILBA3 as IO Window */ + rc32434_pci->pcilba[3].a = 0x18800000; + rc32434_pci->pcilba[3].m = 0x18800000; + rc32434_pci->pcilba[3].c = ( (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m) | PCI_ENDIAN_FLAG); + dummyRead = rc32434_pci->pcilba[3].c; /* flush the CPU write Buffers */ + + pciConfigAddr=(unsigned int)(0x80000004); + for(loopCount=0;loopCount<24;loopCount++){ + rc32434_pci->pcicfga=pciConfigAddr; + dummyRead=rc32434_pci->pcicfga; + rc32434_pci->pcicfgd = korinaCnfgRegs[loopCount]; + dummyRead=rc32434_pci->pcicfgd; + pciConfigAddr += 4; + } + rc32434_pci->pcitc = (unsigned int)((PCITC_RTIMER_VAL&0xff) << PCITC_rtimer_b) + | ((PCITC_DTIMER_VAL&0xff) << PCITC_dtimer_b); + + pciCntlVal=rc32434_pci->pcic; + pciCntlVal &=~(PCIC_tnr_m); + rc32434_pci->pcic = pciCntlVal; + pciCntlVal=rc32434_pci->pcic; + return 0; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + if (PCI_SLOT(dev->devfn) == 6 && dev->bus->number == 0) { + /* disable prefetched memory range */ + pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0); + pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x10); + + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4); + } + return 0; +} + +static int __init rc32434_pci_init(void) +{ + printk("PCI: Initializing PCI\n"); + + ioport_resource.start = rc32434_res_pci_io1.start; + ioport_resource.end = rc32434_res_pci_io1.end; + + rc32434_pcibridge_init(); + + register_pci_controller(&rc32434_controller); + rc32434_sync(); + + return 0; +} + +arch_initcall(rc32434_pci_init); + diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/Makefile b/target/linux/rb532/files-2.6.23/arch/mips/rb500/Makefile new file mode 100644 index 0000000000..d3ae3d9122 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the RB500 board specific parts of the kernel +# + +obj-y += irq.o time.o setup.o serial.o prom.o gpio.o devices.o diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/devices.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/devices.c new file mode 100644 index 0000000000..6372737de7 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/devices.c @@ -0,0 +1,338 @@ +/* + * RouterBoard 500 Platform devices + * + * Copyright (C) 2006 Felix Fietkau + * Copyright (C) 2007 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define ETH0_DMA_RX_IRQ GROUP1_IRQ_BASE + 0 +#define ETH0_DMA_TX_IRQ GROUP1_IRQ_BASE + 1 +#define ETH0_RX_OVR_IRQ GROUP3_IRQ_BASE + 9 +#define ETH0_TX_UND_IRQ GROUP3_IRQ_BASE + 10 + +#define ETH0_RX_DMA_ADDR (DMA0_PhysicalAddress + 0*DMA_CHAN_OFFSET) +#define ETH0_TX_DMA_ADDR (DMA0_PhysicalAddress + 1*DMA_CHAN_OFFSET) + +/* NAND definitions */ +#define MEM32(x) *((volatile unsigned *) (x)) + +#define GPIO_RDY (1 << 0x08) +#define GPIO_WPX (1 << 0x09) +#define GPIO_ALE (1 << 0x0a) +#define GPIO_CLE (1 << 0x0b) + +extern char* board_type; + +static struct resource korina_dev0_res[] = { + { + .name = "korina_regs", + .start = ETH0_PhysicalAddress, + .end = ETH0_PhysicalAddress + sizeof(ETH_t), + .flags = IORESOURCE_MEM, + }, { + .name = "korina_rx", + .start = ETH0_DMA_RX_IRQ, + .end = ETH0_DMA_RX_IRQ, + .flags = IORESOURCE_IRQ + }, { + .name = "korina_tx", + .start = ETH0_DMA_TX_IRQ, + .end = ETH0_DMA_TX_IRQ, + .flags = IORESOURCE_IRQ + }, { + .name = "korina_ovr", + .start = ETH0_RX_OVR_IRQ, + .end = ETH0_RX_OVR_IRQ, + .flags = IORESOURCE_IRQ + }, { + .name = "korina_und", + .start = ETH0_TX_UND_IRQ, + .end = ETH0_TX_UND_IRQ, + .flags = IORESOURCE_IRQ + }, { + .name = "korina_dma_rx", + .start = ETH0_RX_DMA_ADDR, + .end = ETH0_RX_DMA_ADDR + DMA_CHAN_OFFSET - 1, + .flags = IORESOURCE_MEM, + }, { + .name = "korina_dma_tx", + .start = ETH0_TX_DMA_ADDR, + .end = ETH0_TX_DMA_ADDR + DMA_CHAN_OFFSET - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct korina_device korina_dev0_data = { + .name = "korina0", + .mac = {0xde, 0xca, 0xff, 0xc0, 0xff, 0xee} +}; + +static struct platform_device korina_dev0 = { + .id = 0, + .name = "korina", + .dev.platform_data = &korina_dev0_data, + .resource = korina_dev0_res, + .num_resources = ARRAY_SIZE(korina_dev0_res), +}; + +#define CF_GPIO_NUM 13 + +static struct resource cf_slot0_res[] = { + { + .name = "cf_membase", + .flags = IORESOURCE_MEM + }, { + .name = "cf_irq", + .start = (8 + 4 * 32 + CF_GPIO_NUM), /* 149 */ + .end = (8 + 4 * 32 + CF_GPIO_NUM), + .flags = IORESOURCE_IRQ + } +}; + +static struct cf_device cf_slot0_data = { + .gpio_pin = 13 +}; + +static struct platform_device cf_slot0 = { + .id = 0, + .name = "rb500-cf", + .dev.platform_data = &cf_slot0_data, + .resource = cf_slot0_res, + .num_resources = ARRAY_SIZE(cf_slot0_res), +}; + +/* Resources and device for NAND. There is no data needed and no irqs, so just define the memory used. */ + +/* + * We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader + * will not be able to find the kernel that we load. So set the oobinfo + * when creating the partitions + */ +static struct nand_ecclayout rb500_nand_ecclayout = { + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15 }, + .oobavail = 9, + .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } +}; + +int rb500_dev_ready(struct mtd_info *mtd) +{ + return MEM32(IDT434_REG_BASE + GPIOD) & GPIO_RDY; +} + +void rb500_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + unsigned char orbits, nandbits; + + if (ctrl & NAND_CTRL_CHANGE) { + + orbits = (ctrl & NAND_CLE) << 1; + orbits |= (ctrl & NAND_ALE) >> 1; + + nandbits = (~ctrl & NAND_CLE) << 1; + nandbits |= (~ctrl & NAND_ALE) >> 1; + + changeLatchU5(orbits, nandbits); + } + if (cmd != NAND_CMD_NONE) + writeb(cmd, chip->IO_ADDR_W); +} + +static struct resource nand_slot0_res[] = { + [0] = { + .name = "nand_membase", + .flags = IORESOURCE_MEM + } +}; + +struct platform_nand_data rb500_nand_data = { + .ctrl.dev_ready = rb500_dev_ready, + .ctrl.cmd_ctrl = rb500_cmd_ctrl, +}; + +static struct platform_device nand_slot0 = { + .name = "gen_nand", + .id = -1, + .resource = nand_slot0_res, + .num_resources = ARRAY_SIZE(nand_slot0_res), + .dev.platform_data = &rb500_nand_data, +}; + +static struct mtd_partition rb500_partition_info[] = { + { + .name = "Routerboard NAND boot", + .offset = 0, + .size = 4 * 1024 * 1024, + }, { + .name = "rootfs", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct platform_device rb500_led = { + .name = "rb500-led", + .id = 0, +}; + +static struct gpio_keys_button rb500_gpio_btn[] = { + { + .gpio = 1, + .code = BTN_0, + .desc = "S1", + .active_low = 1, + } +}; + +static struct gpio_keys_platform_data rb500_gpio_btn_data = { + .buttons = rb500_gpio_btn, + .nbuttons = ARRAY_SIZE(rb500_gpio_btn), +}; + +static struct platform_device rb500_button = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &rb500_gpio_btn_data, + } +}; + +static struct platform_device *rb500_devs[] = { + &korina_dev0, + &nand_slot0, + &cf_slot0, + &rb500_led, + &rb500_button +}; + +static void __init parse_mac_addr(char *macstr) +{ + int i, j; + unsigned char result, value; + + for (i = 0; i < 6; i++) { + result = 0; + + if (i != 5 && *(macstr + 2) != ':') + return; + + for (j = 0; j < 2; j++) { + if (isxdigit(*macstr) + && (value = + isdigit(*macstr) ? *macstr - + '0' : toupper(*macstr) - 'A' + 10) < 16) { + result = result * 16 + value; + macstr++; + } else + return; + } + + macstr++; + korina_dev0_data.mac[i] = result; + } +} + + +/* DEVICE CONTROLLER 1 */ +#define CFG_DC_DEV1 (void*)0xb8010010 +#define CFG_DC_DEV2 (void*)0xb8010020 +#define CFG_DC_DEVBASE 0x0 +#define CFG_DC_DEVMASK 0x4 +#define CFG_DC_DEVC 0x8 +#define CFG_DC_DEVTC 0xC + +/* NAND definitions */ +#define NAND_CHIP_DELAY 25 + +static int rb500_nand_fixup(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (mtd->writesize == 512) + chip->ecc.layout = &rb500_nand_ecclayout; + + return 0; +} + +static void __init rb500_nand_setup(void) +{ + switch (mips_machtype) { + case MACH_MIKROTIK_RB532A: + changeLatchU5(LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE | LO_WPX); + break; + default: + changeLatchU5(LO_WPX | LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE); + break; + } + + /* Setup NAND specific settings */ + rb500_nand_data.chip.nr_chips = 1; + rb500_nand_data.chip.nr_partitions = ARRAY_SIZE(rb500_partition_info); + rb500_nand_data.chip.partitions = rb500_partition_info; + rb500_nand_data.chip.chip_delay = NAND_CHIP_DELAY; + rb500_nand_data.chip.options = NAND_NO_AUTOINCR; + + rb500_nand_data.chip.chip_fixup = &rb500_nand_fixup; +} + + +static int __init plat_setup_devices(void) +{ + /* Look for the CF card reader */ + if (!readl(CFG_DC_DEV1 + CFG_DC_DEVMASK)) + rb500_devs[1] = NULL; + else { + cf_slot0_res[0].start = + readl(CFG_DC_DEV1 + CFG_DC_DEVBASE); + cf_slot0_res[0].end = cf_slot0_res[0].start + 0x1000; + } + + /* Read the NAND resources from the device controller */ + nand_slot0_res[0].start = readl(CFG_DC_DEV2 + CFG_DC_DEVBASE); + nand_slot0_res[0].end = nand_slot0_res[0].start + 0x1000; + + /* Initialise the NAND device */ + rb500_nand_setup(); + + return platform_add_devices(rb500_devs, ARRAY_SIZE(rb500_devs)); +} + +static int __init setup_kmac(char *s) +{ + printk("korina mac = %s\n", s); + parse_mac_addr(s); + return 0; +} + +__setup("kmac=", setup_kmac); + +arch_initcall(plat_setup_devices); diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/gpio.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/gpio.c new file mode 100644 index 0000000000..6c92a8fe56 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/gpio.c @@ -0,0 +1,198 @@ +/* + * Miscellaneous functions for IDT EB434 board + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * Copyright 2006 Phil Sutter + * Copyright 2007 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define GPIO_BADDR 0x18050000 + +static volatile unsigned char *devCtl3Base; +static unsigned char latchU5State; +static spinlock_t clu5Lock = SPIN_LOCK_UNLOCKED; + +struct rb500_gpio_reg __iomem *rb500_gpio_reg0; +EXPORT_SYMBOL(rb500_gpio_reg0); + +static struct resource rb500_gpio_reg0_res[] = { + { + .name = "gpio_reg0", + .start = GPIO_BADDR, + .end = GPIO_BADDR + sizeof(struct rb500_gpio_reg), + .flags = IORESOURCE_MEM, + } +}; + +void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val) +{ + unsigned flags, data; + unsigned i = 0; + + spin_lock_irqsave(&clu5Lock, flags); + data = *(volatile unsigned *) (IDT434_REG_BASE + regOffs); + for (i = 0; i != len; ++i) { + if (val & (1 << i)) + data |= (1 << (i + bit)); + else + data &= ~(1 << (i + bit)); + } + *(volatile unsigned *) (IDT434_REG_BASE + regOffs) = data; + spin_unlock_irqrestore(&clu5Lock, flags); +} +EXPORT_SYMBOL(set434Reg); + +void changeLatchU5(unsigned char orMask, unsigned char nandMask) +{ + unsigned flags; + + spin_lock_irqsave(&clu5Lock, flags); + latchU5State = (latchU5State | orMask) & ~nandMask; + if (!devCtl3Base) + devCtl3Base = (volatile unsigned char *) + KSEG1ADDR(*(volatile unsigned *) + KSEG1ADDR(0x18010030)); + *devCtl3Base = latchU5State; + spin_unlock_irqrestore(&clu5Lock, flags); +} +EXPORT_SYMBOL(changeLatchU5); + +unsigned char getLatchU5State(void) +{ + return latchU5State; +} +EXPORT_SYMBOL(getLatchU5State); + +int rb500_gpio_get_value(unsigned gpio) +{ + return readl(&rb500_gpio_reg0->gpiod) & (1 << gpio); +} +EXPORT_SYMBOL(rb500_gpio_get_value); + +void rb500_gpio_set_value(unsigned gpio, int value) +{ + unsigned tmp; + + tmp = readl(&rb500_gpio_reg0->gpiod) & ~(1 << gpio); + if (value) + tmp |= 1 << gpio; + + writel(tmp, (void *)&rb500_gpio_reg0->gpiod); +} +EXPORT_SYMBOL(rb500_gpio_set_value); + +int rb500_gpio_direction_input(unsigned gpio) +{ + writel(readl(&rb500_gpio_reg0->gpiocfg) & ~(1 << gpio), (void *)&rb500_gpio_reg0->gpiocfg); + + return 0; +} +EXPORT_SYMBOL(rb500_gpio_direction_input); + +int rb500_gpio_direction_output(unsigned gpio, int value) +{ + gpio_set_value(gpio, value); + writel(readl(&rb500_gpio_reg0->gpiocfg) | (1 << gpio), (void *)&rb500_gpio_reg0->gpiocfg); + + return 0; +} +EXPORT_SYMBOL(rb500_gpio_direction_output); + +void rb500_gpio_set_int_level(unsigned gpio, int value) +{ + unsigned tmp; + + tmp = readl(&rb500_gpio_reg0->gpioilevel) & ~(1 << gpio); + if (value) + tmp |= 1 << gpio; + writel(tmp, (void *)&rb500_gpio_reg0->gpioilevel); +} +EXPORT_SYMBOL(rb500_gpio_set_int_level); + +int rb500_gpio_get_int_level(unsigned gpio) +{ + return readl(&rb500_gpio_reg0->gpioilevel) & (1 << gpio); +} +EXPORT_SYMBOL(rb500_gpio_get_int_level); + +void rb500_gpio_set_int_status(unsigned gpio, int value) +{ + unsigned tmp; + + tmp = readl(&rb500_gpio_reg0->gpioistat); + if (value) + tmp |= 1 << gpio; + writel(tmp, (void *)&rb500_gpio_reg0->gpioistat); +} +EXPORT_SYMBOL(rb500_gpio_set_int_status); + +int rb500_gpio_get_int_status(unsigned gpio) +{ + return readl(&rb500_gpio_reg0->gpioistat) & (1 << gpio); +} +EXPORT_SYMBOL(rb500_gpio_get_int_status); + +void rb500_gpio_set_func(unsigned gpio, int value) +{ + unsigned tmp; + + tmp = readl(&rb500_gpio_reg0->gpiofunc); + if (value) + tmp |= 1 << gpio; + writel(tmp, (void *)&rb500_gpio_reg0->gpiofunc); +} +EXPORT_SYMBOL(rb500_gpio_set_func); + +int rb500_gpio_get_func(unsigned gpio) +{ + return readl(&rb500_gpio_reg0->gpiofunc) & (1 << gpio); +} +EXPORT_SYMBOL(rb500_gpio_get_func); + +int __init rb500_gpio_init(void) +{ + rb500_gpio_reg0 = ioremap_nocache(rb500_gpio_reg0_res[0].start, + rb500_gpio_reg0_res[0].end - + rb500_gpio_reg0_res[0].start); + + if (!rb500_gpio_reg0) { + printk(KERN_ERR "rb500: cannot remap GPIO register 0\n"); + return -ENXIO; + } + + return 0; +} +arch_initcall(rb500_gpio_init); diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/irq.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/irq.c new file mode 100644 index 0000000000..5a4d349d49 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/irq.c @@ -0,0 +1,265 @@ +/* + * BRIEF MODULE DESCRIPTION + * RC32434 interrupt routines. + * + * Copyright 2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void set_debug_traps(void); +extern irq_cpustat_t irq_stat [NR_CPUS]; +unsigned int local_bh_count[NR_CPUS]; +unsigned int local_irq_count[NR_CPUS]; + +static unsigned int startup_irq(unsigned int irq); +static void rb500_end_irq(unsigned int irq_nr); +static void mask_and_ack_irq(unsigned int irq_nr); +static void rb500_enable_irq(unsigned int irq_nr); +static void rb500_disable_irq(unsigned int irq_nr); + +extern void __init init_generic_irq(void); +extern struct rb500_gpio_reg __iomem *rb500_gpio_reg0; + +typedef struct { + u32 mask; /* mask of valid bits in pending/mask registers */ + volatile u32 *base_addr; +} intr_group_t; + +#define RC32434_NR_IRQS (GROUP4_IRQ_BASE + 32) + +#if (NR_IRQS < RC32434_NR_IRQS) +#error Too little irqs defined. Did you override ? +#endif + +static const intr_group_t intr_group[NUM_INTR_GROUPS] = { + { 0x0000efff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 0 * IC_GROUP_OFFSET) }, + { 0x00001fff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 1 * IC_GROUP_OFFSET) }, + { 0x00000007, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 2 * IC_GROUP_OFFSET) }, + { 0x0003ffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 3 * IC_GROUP_OFFSET) }, + { 0xffffffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 4 * IC_GROUP_OFFSET) } +}; + +#define READ_PEND(base) (*(base)) +#define READ_MASK(base) (*(base + 2)) +#define WRITE_MASK(base, val) (*(base + 2) = (val)) + +static inline int irq_to_group(unsigned int irq_nr) +{ + return ((irq_nr - GROUP0_IRQ_BASE) >> 5); +} + +static inline int group_to_ip(unsigned int group) +{ + return group + 2; +} + +static inline void enable_local_irq(unsigned int ip) +{ + int ipnum = 0x100 << ip; + clear_c0_cause(ipnum); + set_c0_status(ipnum); +} + +static inline void disable_local_irq(unsigned int ip) +{ + int ipnum = 0x100 << ip; + clear_c0_status(ipnum); +} + +static inline void ack_local_irq(unsigned int ip) +{ + int ipnum = 0x100 << ip; + clear_c0_cause(ipnum); +} + +static void rb500_enable_irq(unsigned int irq_nr) +{ + int ip = irq_nr - GROUP0_IRQ_BASE; + unsigned int group, intr_bit; + volatile unsigned int *addr; + + + if (ip < 0) + enable_local_irq(irq_nr); + else { + group = ip >> 5; + + ip &= (1<<5)-1; + intr_bit = 1 << ip; + + enable_local_irq(group_to_ip(group)); + + addr = intr_group[group].base_addr; + WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit); + } +} + +static void rb500_disable_irq(unsigned int irq_nr) +{ + int ip = irq_nr - GROUP0_IRQ_BASE; + unsigned int group, intr_bit, mask; + volatile unsigned int *addr; + + if (ip < 0) { + disable_local_irq(irq_nr); + }else{ + group = ip >> 5; + + ip &= (1<<5) -1; + intr_bit = 1 << ip; + addr = intr_group[group].base_addr; + mask = READ_MASK(addr); + mask |= intr_bit; + WRITE_MASK(addr,mask); + + /* + * if there are no more interrupts enabled in this + * group, disable corresponding IP + */ + if (mask == intr_group[group].mask) + disable_local_irq(group_to_ip(group)); + } +} + +static unsigned int startup_irq(unsigned int irq_nr) +{ + rb500_enable_irq(irq_nr); + return 0; +} + +static void shutdown_irq(unsigned int irq_nr) +{ + rb500_disable_irq(irq_nr); + return; +} + +static void mask_and_ack_irq(unsigned int irq_nr) +{ + rb500_disable_irq(irq_nr); + ack_local_irq(group_to_ip(irq_to_group(irq_nr))); +} + +static void rb500_end_irq(unsigned int irq_nr) +{ + + int ip = irq_nr - GROUP0_IRQ_BASE; + unsigned int intr_bit, group; + volatile unsigned int *addr; + + if ((irq_desc[irq_nr].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + printk("warning: end_irq %d did not enable (%x)\n", + irq_nr, irq_desc[irq_nr].status); + return; + } + + if (ip < 0) { + enable_local_irq(irq_nr); + } else { + group = ip >> 5; + + ip &= (1 << 5) - 1; + intr_bit = 1 << ip; + + if (irq_nr >= GROUP4_IRQ_BASE && irq_nr <= (GROUP4_IRQ_BASE + 13)) { + rb500_gpio_reg0->gpioistat = rb500_gpio_reg0->gpioistat & ~intr_bit; + } + + enable_local_irq(group_to_ip(group)); + + addr = intr_group[group].base_addr; + WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit); + } +} + +static struct hw_interrupt_type rc32434_irq_type = { + .typename = "RB500", + .startup = startup_irq, + .shutdown = shutdown_irq, + .enable = rb500_enable_irq, + .disable = rb500_disable_irq, + .ack = mask_and_ack_irq, + .end = rb500_end_irq, +}; + + +void __init arch_init_irq(void) +{ + int i; + + printk("Initializing IRQ's: %d out of %d\n", RC32434_NR_IRQS, NR_IRQS); + memset(irq_desc, 0, sizeof(irq_desc)); + + for (i = 0; i < RC32434_NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].chip = &rc32434_irq_type; + spin_lock_init(&irq_desc[i].lock); + } +} + +/* Main Interrupt dispatcher */ +asmlinkage void plat_irq_dispatch(void) +{ + unsigned int ip, pend, group; + volatile unsigned int *addr; + unsigned int cp0_cause = read_c0_cause() & read_c0_status(); + + if (cp0_cause & CAUSEF_IP7) { + ll_timer_interrupt(7); + } else if ((ip = (cp0_cause & 0x7c00))) { + group = 21 - rc32434_clz(ip); + + addr = intr_group[group].base_addr; + + pend = READ_PEND(addr); + pend &= ~READ_MASK(addr); // only unmasked interrupts + pend = 39 - rc32434_clz(pend); + do_IRQ((group << 5) + pend); + } +} diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/prom.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/prom.c new file mode 100644 index 0000000000..b10172fb51 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/prom.c @@ -0,0 +1,178 @@ +/* +* prom.c +********************************************************************** +* P . Sadik Oct 10, 2003 +* +* Started change log +* idt_cpu_freq is make a kernel configuration parameter +* idt_cpu_freq is exported so that other modules can use it. +* Code cleanup +********************************************************************** +* P. Sadik Oct 20, 2003 +* +* Removed NVRAM code from here, since they are already available under +* nvram directory. +* Added serial port initialisation. +********************************************************************** +********************************************************************** +* P. Sadik Oct 30, 2003 +* +* Added reset_cons_port +********************************************************************** + + P.Christeas, 2005-2006 + Port to 2.6, add 2.6 cmdline parsing + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROM_ENTRY(x) (0xbfc00000+((x)*8)) +extern void __init setup_serial_port(void); + +unsigned int idt_cpu_freq = 132000000; +EXPORT_SYMBOL(idt_cpu_freq); +unsigned int gpio_bootup_state = 0; +EXPORT_SYMBOL(gpio_bootup_state); + +char mips_mac_address[18] = "08:00:06:05:40:01"; +EXPORT_SYMBOL(mips_mac_address); + +/* what to append to cmdline when button is [not] pressed */ +#define GPIO_INIT_NOBUTTON "" +#define GPIO_INIT_BUTTON " 2" + +#ifdef CONFIG_MIKROTIK_RB500 +unsigned soft_reboot = 0; +EXPORT_SYMBOL(soft_reboot); +#endif + +#define SR_NMI 0x00180000 /* NMI */ +#define SERIAL_SPEED_ENTRY 0x00000001 + +#ifdef CONFIG_REMOTE_DEBUG +extern int remote_debug; +#endif + +#define FREQ_TAG "HZ=" +#define GPIO_TAG "gpio=" +#define KMAC_TAG "kmac=" +#define MEM_TAG "mem=" +#define BOARD_TAG "board=" +#define IGNORE_CMDLINE_MEM 1 +#define DEBUG_DDR + +#define BOARD_RB532 "500" +#define BOARD_RB532A "500r5" + +void parse_soft_settings(unsigned *ptr, unsigned size); +void parse_hard_settings(unsigned *ptr, unsigned size); + +void __init prom_setup_cmdline(void); + +void __init prom_init(void) +{ + DDR_t ddr = (DDR_t) DDR_VirtualAddress; /* define the pointer to the DDR registers */ + phys_t memsize = 0-ddr->ddrmask; + + /* this should be the very first message, even before serial is properly initialized */ + prom_setup_cmdline(); + setup_serial_port(); + + mips_machgroup = MACH_GROUP_MIKROTIK; + soft_reboot = read_c0_status() & SR_NMI; + pm_power_off = NULL; + + /* + * give all RAM to boot allocator, + * except for the first 0x400 and the last 0x200 bytes + */ + add_memory_region(ddr->ddrbase + 0x400, memsize - 0x600, BOOT_MEM_RAM); +} + +void __init prom_free_prom_memory(void) +{ + /* No prom memory to free */ +} + +static inline int match_tag(char *arg, const char *tag) +{ + return (strncmp(arg, tag, strlen(tag)) == 0); +} + +static inline unsigned long tag2ul(char *arg, const char *tag) +{ + char *num = arg+strlen(tag); + return simple_strtoul(num, 0, 10); +} + +extern char _image_cmdline; +void __init prom_setup_cmdline(void){ + char cmd_line[CL_SIZE]; + char *cp; + int prom_argc; + char **prom_argv, **prom_envp; + int i; + + prom_argc = fw_arg0; + prom_argv = (char **) fw_arg1; + prom_envp = (char **) fw_arg2; + + cp=cmd_line; + /* Note: it is common that parameters start at argv[1] and not argv[0], + however, our elf loader starts at [0] */ + for(i=0;i0) *(cp++) = ' '; + if (match_tag(prom_argv[i], BOARD_TAG)) { + char *board = prom_argv[i] + strlen(BOARD_TAG); + if (match_tag(board, BOARD_RB532A)) + mips_machtype = MACH_MIKROTIK_RB532A; + else + mips_machtype = MACH_MIKROTIK_RB532; + } + + if (match_tag(prom_argv[i], GPIO_TAG)) { + gpio_bootup_state = tag2ul(prom_argv[i], GPIO_TAG); + } + strcpy(cp,prom_argv[i]); + cp+=strlen(prom_argv[i]); + } + *(cp++) = ' '; + strcpy(cp,(&_image_cmdline + 8)); + cp += strlen(&_image_cmdline); + + i=strlen(arcs_cmdline); + if (i>0){ + *(cp++) = ' '; + strcpy(cp,arcs_cmdline); + cp+=strlen(arcs_cmdline); + } + if (gpio_bootup_state&0x02) + strcpy(cp,GPIO_INIT_NOBUTTON); + else + strcpy(cp,GPIO_INIT_BUTTON); + cmd_line[CL_SIZE-1] = '\0'; + + strcpy(arcs_cmdline,cmd_line); +} + diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/serial.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/serial.c new file mode 100644 index 0000000000..25a03e0108 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/serial.c @@ -0,0 +1,78 @@ +/************************************************************************** + * + * BRIEF MODULE DESCRIPTION + * Serial port initialisation. + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Initial Release + * + * + * + ************************************************************************** + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +extern unsigned int idt_cpu_freq; + +static struct uart_port serial_req = { + .type = PORT_16550A, + .line = 0, + .irq = RC32434_UART0_IRQ, + //.flags = STD_COM_FLAGS, + .iotype = UPIO_MEM, + .membase = (char *) KSEG1ADDR(RC32434_UART0_BASE), +// .fifosize = 14 + .regshift = 2 +}; + +int __init setup_serial_port(void) +{ + serial_req.uartclk = idt_cpu_freq; + + if (early_serial_setup(&serial_req)) + return -ENODEV; + + return(0); +} diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/setup.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/setup.c new file mode 100644 index 0000000000..2fda0e2ab1 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/setup.c @@ -0,0 +1,81 @@ +/* + * setup.c - boot time setup code + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for KSEG1ADDR() */ +#include +#include +#include +#include + +#ifdef CONFIG_PCI +extern void rc32434_time_init(void); +extern int __init rc32434_pcibridge_init(void); +#endif + +#define epldMask ((volatile unsigned char *)0xB900000d) + +static void rb_machine_restart(char *command) +{ + /* just jump to the reset vector */ + * (volatile unsigned *) KSEG1ADDR(0x18008000) = 0x80000001; + ((void (*)(void))KSEG1ADDR(0x1FC00000u))(); +} + +static void rb_machine_halt(void) +{ + for(;;) continue; +} + +#ifdef CONFIG_CPU_HAS_WB +void (*__wbflush) (void); + +static void rb_write_buffer_flush(void) +{ + __asm__ __volatile__ + ("sync\n\t" "nop\n\t" "loop: bc0f loop\n\t" "nop\n\t"); +} +#endif + +void __init plat_mem_setup(void) +{ + unsigned int pciCntlVal; + + board_time_init = rc32434_time_init; + +#ifdef CONFIG_CPU_HAS_WB + __wbflush = rb_write_buffer_flush; +#endif + _machine_restart = rb_machine_restart; + _machine_halt = rb_machine_halt; + /*_machine_power_off = rb_machine_power_halt;*/ + pm_power_off = rb_machine_halt; + + set_io_port_base(KSEG1); + + pciCntlVal=rc32434_pci->pcic; + pciCntlVal &= 0xFFFFFF7; + rc32434_pci->pcic = pciCntlVal; + +#ifdef CONFIG_PCI + /* Enable PCI interrupts in EPLD Mask register */ + *epldMask = 0x0; + *(epldMask + 1) = 0x0; +#endif + write_c0_wired(0); +} + +const char *get_system_type(void) +{ + return "MIPS RB500"; +} diff --git a/target/linux/rb532/files-2.6.23/arch/mips/rb500/time.c b/target/linux/rb532/files-2.6.23/arch/mips/rb500/time.c new file mode 100644 index 0000000000..13320a6caf --- /dev/null +++ b/target/linux/rb532/files-2.6.23/arch/mips/rb500/time.c @@ -0,0 +1,94 @@ +/* +**************************************************************************** +* Carsten Langgaard, carstenl@mips.com +* Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. +* +*************************************************************************** +* +* This program is free software; you can distribute it and/or modify it +* under the terms of the GNU General Public License (Version 2) as +* published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +* +**************************************************************************** +* +* Setting up the clock on the MIPS boards. +* +**************************************************************************** +* P. Sadik Oct 10, 2003 +* +* Started change log. +* mips_counter_frequency is now calculated at run time, based on idt_cpu_freq. +* Code cleanup +**************************************************************************** +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static unsigned long r4k_offset; /* Amount to incr compare reg each time */ +static unsigned long r4k_cur; /* What counter should be at next timer irq */ +extern unsigned int mips_hpt_frequency; +extern unsigned int idt_cpu_freq; + +/* + * Figure out the r4k offset, the amount to increment the compare + * register for each time tick. There is no RTC available. + * + * The RC32434 counts at half the CPU *core* speed. + */ +static unsigned long __init cal_r4koff(void) +{ + mips_hpt_frequency = idt_cpu_freq * IDT_CLOCK_MULT / 2; + return (mips_hpt_frequency / HZ); +} + + +void __init rc32434_time_init(void) +{ + unsigned int est_freq, flags; + + local_irq_save(flags); + + printk("calculating r4koff... "); + r4k_offset = cal_r4koff(); + printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); + + est_freq = 2*r4k_offset*HZ; + est_freq += 5000; /* round */ + est_freq -= est_freq%10000; + printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, + (est_freq%1000000)*100/1000000); + local_irq_restore(flags); +} + +void __init plat_timer_setup(struct irqaction *irq) +{ + /* we are using the cpu counter for timer interrupts */ + setup_irq(MIPS_CPU_TIMER_IRQ, irq); + + /* to generate the first timer interrupt */ + r4k_cur = (read_c0_count() + r4k_offset); + write_c0_compare(r4k_cur); +} + diff --git a/target/linux/rb532/files-2.6.23/drivers/block/rb500/Makefile b/target/linux/rb532/files-2.6.23/drivers/block/rb500/Makefile new file mode 100644 index 0000000000..3e14dc35bd --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/block/rb500/Makefile @@ -0,0 +1,3 @@ +## Makefile for the RB532 CF port + +obj-y += bdev.o ata.o diff --git a/target/linux/rb532/files-2.6.23/drivers/block/rb500/ata.c b/target/linux/rb532/files-2.6.23/drivers/block/rb500/ata.c new file mode 100644 index 0000000000..31e6782fa4 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/block/rb500/ata.c @@ -0,0 +1,487 @@ +/* CF-mips driver + This is a block driver for the direct (mmaped) interface to the CF-slot, + found in Routerboard.com's RB532 board + See SDK provided from routerboard.com. + + Module adapted By P.Christeas , 2005-6. + Cleaned up and adapted to platform_device by Felix Fietkau + + This work is redistributed under the terms of the GNU General Public License. +*/ + +#include /* printk() */ +#include /* module to be loadable */ +#include +#include +#include +#include /* request_mem_region() */ + +#include +#include /* ioremap() */ +#include /* ioremap() */ +#include + +#include "ata.h" + +#define REQUEST_MEM_REGION 0 +#define DEBUG 1 + +#if DEBUG +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +#define SECS 1000000 /* unit for wait_not_busy() is 1us */ + +unsigned cf_head = 0; +unsigned cf_cyl = 0; +unsigned cf_spt = 0; +unsigned cf_sectors = 0; +static unsigned cf_block_size = 1; +static void *baddr = 0; + +#define DBUF32 ((volatile u32 *)((unsigned long)dev->baddr | ATA_DBUF_OFFSET)) + + +static void cf_do_tasklet(unsigned long dev_l); + + +static inline void wareg(u8 val, unsigned reg, struct cf_mips_dev* dev) +{ + writeb(val, dev->baddr + ATA_REG_OFFSET + reg); +} + +static inline u8 rareg(unsigned reg, struct cf_mips_dev* dev) +{ + return readb(dev->baddr + ATA_REG_OFFSET + reg); +} + +static inline int cfrdy(struct cf_mips_dev *dev) +{ + return gpio_get_value(dev->pin); +} + +static inline void prepare_cf_irq(struct cf_mips_dev *dev) +{ + rb500_gpio_set_int_level(1, dev->pin); /* interrupt on cf ready (not busy) */ + rb500_gpio_set_int_status(0, dev->pin); /* clear interrupt status */ +} + +static inline int cf_present(struct cf_mips_dev* dev) +{ + /* TODO: read and configure CIS into memory mapped mode + * TODO: parse CISTPL_CONFIG on CF+ cards to get base address (0x200) + * TODO: maybe adjust power saving setting for Hitachi Microdrive + */ + int i; + + /* setup CFRDY GPIO as input */ + rb500_gpio_set_func(dev->pin, 0); + gpio_direction_input(dev->pin); + + for (i = 0; i < 0x10; ++i) { + if (rareg(i,dev) != 0xff) + return 1; + } + return 0; +} + +static inline int is_busy(struct cf_mips_dev *dev) +{ + return !cfrdy(dev); +} + +static int wait_not_busy(int to_us, int wait_for_busy,struct cf_mips_dev *dev) +{ + int us_passed = 0; + if (wait_for_busy && !is_busy(dev)) { + /* busy must appear within 400ns, + * but it may dissapear before we see it + * => must not wait for busy in a loop + */ + ndelay(400); + } + + do { + if (us_passed) + udelay(1); /* never reached in async mode */ + if (!is_busy(dev)) { + if (us_passed > 1 * SECS) { + printk(KERN_WARNING "cf-mips: not busy ok (after %dus)" + ", status 0x%02x\n", us_passed, (unsigned) rareg(ATA_REG_ST,dev)); + } + return CF_TRANS_OK; + } + if (us_passed == 1 * SECS) { + printk(KERN_WARNING "cf-mips: wait not busy %dus..\n", to_us); + } + if (dev->async_mode) { + dev->to_timer.expires = jiffies + (to_us * HZ / SECS); + dev->irq_enable_time = jiffies; + prepare_cf_irq(dev); + if (is_busy(dev)) { + add_timer(&dev->to_timer); + enable_irq(dev->irq); + return CF_TRANS_IN_PROGRESS; + } + continue; + } + ++us_passed; + } while (us_passed < to_us); + + printk(KERN_ERR "cf-mips: wait not busy timeout (%dus)" + ", status 0x%02x, state %d\n", + to_us, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate); + return CF_TRANS_FAILED; +} + +static irqreturn_t cf_irq_handler(int irq, void *dev_id) +{ + /* While tasklet has not disabled irq, irq will be retried all the time + * because of ILEVEL matching GPIO pin status => deadlock. + * To avoid this, we change ILEVEL to 0. + */ + struct cf_mips_dev *dev=dev_id; + + rb500_gpio_set_int_level(0, dev->pin); + rb500_gpio_set_int_status(0, dev->pin); + + del_timer(&dev->to_timer); + tasklet_schedule(&dev->tasklet); + return IRQ_HANDLED; +} + +static int do_reset(struct cf_mips_dev *dev) +{ + printk(KERN_INFO "cf-mips: resetting..\n"); + + wareg(ATA_REG_DC_SRST, ATA_REG_DC,dev); + udelay(1); /* FIXME: how long should we wait here? */ + wareg(0, ATA_REG_DC,dev); + + return wait_not_busy(30 * SECS, 1,dev); +} + +static int set_multiple(struct cf_mips_dev *dev) +{ + if (dev->block_size <= 1) + return CF_TRANS_OK; + + wareg(dev->block_size, ATA_REG_SC,dev); + wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev); + wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev); + + return wait_not_busy(10 * SECS, 1,dev); +} + +static int set_cmd(struct cf_mips_dev *dev) +{ + //DEBUGP(KERN_INFO "cf-mips: ata cmd 0x%02x\n", dev->tcmd); + // sector_count should be <=24 bits.. + BUG_ON(dev->tsect_start>=0x10000000); + // This way, it addresses 2^24 * 512 = 128G + + if (dev->tsector_count) { + wareg(dev->tsector_count & 0xff, ATA_REG_SC,dev); + wareg(dev->tsect_start & 0xff, ATA_REG_SN,dev); + wareg((dev->tsect_start >> 8) & 0xff, ATA_REG_CL,dev); + wareg((dev->tsect_start >> 16) & 0xff, ATA_REG_CH,dev); + } + wareg(((dev->tsect_start >> 24) & 0x0f) | ATA_REG_DH_BASE | ATA_REG_DH_LBA, + ATA_REG_DH,dev); /* select drive on all commands */ + wareg(dev->tcmd, ATA_REG_CMD,dev); + return wait_not_busy(10 * SECS, 1,dev); +} + +static int do_trans(struct cf_mips_dev *dev) +{ + int res; + unsigned st; + int transfered; + + //printk("do_trans: %d sectors left\n",dev->tsectors_left); + while (dev->tsectors_left) { + transfered = 0; + + st = rareg(ATA_REG_ST,dev); + if (!(st & ATA_REG_ST_DRQ)) { + printk(KERN_ERR "cf-mips: do_trans without DRQ (status 0x%x)!\n", st); + if (st & ATA_REG_ST_ERR) { + int errId = rareg(ATA_REG_ERR,dev); + printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n", + (dev->tread ? "read" : "write"), st, errId); + } + return CF_TRANS_FAILED; + } + do { /* Fill/read the buffer one block */ + u32 *qbuf, *qend; + qbuf = (u32 *)dev->tbuf; + qend = qbuf + CF_SECT_SIZE / sizeof(u32); + if (dev->tread) { + while (qbuf!=qend) + put_unaligned(*DBUF32,qbuf++); + //*(qbuf++) = *DBUF32; + } + else { + while(qbuf!=qend) + *DBUF32 = get_unaligned(qbuf++); + } + + dev->tsectors_left--; + dev->tbuf += CF_SECT_SIZE; + dev->tbuf_size -= CF_SECT_SIZE; + transfered++; + } while (transfered != dev->block_size && dev->tsectors_left > 0); + + res = wait_not_busy(10 * SECS, 1,dev); + if (res != CF_TRANS_OK) + return res; + }; + + st = rareg(ATA_REG_ST,dev); + if (st & (ATA_REG_ST_DRQ | ATA_REG_ST_DWF | ATA_REG_ST_ERR)) { + if (st & ATA_REG_ST_DRQ) { + printk(KERN_ERR "cf-mips: DRQ after all %d sectors are %s" + ", status 0x%x\n", dev->tsector_count, (dev->tread ? "read" : "written"), st); + } else if (st & ATA_REG_ST_DWF) { + printk(KERN_ERR "cf-mips: write fault, status 0x%x\n", st); + } else { + int errId = rareg(ATA_REG_ERR,dev); + printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n", + (dev->tread ? "read" : "write"), st, errId); + } + return CF_TRANS_FAILED; + } + return CF_TRANS_OK; +} + +static int cf_do_state(struct cf_mips_dev *dev) +{ + int res; + switch (dev->tstate) { /* fall through everywhere */ + case TS_IDLE: + dev->tstate = TS_READY; + if (is_busy(dev)) { + dev->tstate = TS_AFTER_RESET; + res = do_reset(dev); + if (res != CF_TRANS_OK) + break; + } + case TS_AFTER_RESET: + if (dev->tstate == TS_AFTER_RESET) { + dev->tstate = TS_READY; + res = set_multiple(dev); + if (res != CF_TRANS_OK) + break; + } + case TS_READY: + dev->tstate = TS_CMD; + res = set_cmd(dev); + if (res != CF_TRANS_OK) + break;; + case TS_CMD: + dev->tstate = TS_TRANS; + case TS_TRANS: + res = do_trans(dev); + break; + default: + printk(KERN_ERR "cf-mips: BUG: unknown tstate %d\n", dev->tstate); + return CF_TRANS_FAILED; + } + if (res != CF_TRANS_IN_PROGRESS) + dev->tstate = TS_IDLE; + return res; +} + +static void cf_do_tasklet(unsigned long dev_l) +{ + struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l; + int res; + + disable_irq(dev->irq); + + if (dev->tstate == TS_IDLE) + return; /* can happen when irq is first registered */ + +#if 0 + DEBUGP(KERN_WARNING "cf-mips: not busy ok (tasklet) status 0x%02x\n", + (unsigned) rareg(ATA_REG_ST,dev)); +#endif + + res = cf_do_state(dev); + if (res == CF_TRANS_IN_PROGRESS) + return; + cf_async_trans_done(dev,res); +} + +static void cf_async_timeout(unsigned long dev_l) +{ + struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l; + disable_irq(dev->irq); + /* Perhaps send abort to the device? */ + printk(KERN_ERR "cf-mips: wait not busy timeout (%lus)" + ", status 0x%02x, state %d\n", + jiffies - dev->irq_enable_time, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate); + dev->tstate = TS_IDLE; + cf_async_trans_done(dev,CF_TRANS_FAILED); +} + +int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, + char* buffer, int is_write) +{ + BUG_ON(dev->tstate!=TS_IDLE); + if (nsect > ATA_MAX_SECT_PER_CMD) { + printk(KERN_WARNING "cf-mips: sector count %lu out of range\n",nsect); + return CF_TRANS_FAILED; + } + if (sector + nsect > dev->sectors) { + printk(KERN_WARNING "cf-mips: sector %lu out of range\n",sector); + return CF_TRANS_FAILED; + } + dev->tbuf = buffer; + dev->tbuf_size = nsect*512; + dev->tsect_start = sector; + dev->tsector_count = nsect; + dev->tsectors_left = dev->tsector_count; + dev->tread = (is_write)?0:1; + + dev->tcmd = (dev->block_size == 1 ? + (is_write ? ATA_CMD_WRITE_SECTORS : ATA_CMD_READ_SECTORS) : + (is_write ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_READ_MULTIPLE)); + + return cf_do_state(dev); +} + +static int do_identify(struct cf_mips_dev *dev) +{ + u16 sbuf[CF_SECT_SIZE >> 1]; + int res; + char tstr[17]; //serial + char tmp; + int i; + BUG_ON(dev->tstate!=TS_IDLE); + dev->tbuf = (char *) sbuf; + dev->tbuf_size = CF_SECT_SIZE; + dev->tsect_start = 0; + dev->tsector_count = 0; + dev->tsectors_left = 1; + dev->tread = 1; + dev->tcmd = ATA_CMD_IDENTIFY_DRIVE; + + DEBUGP(KERN_INFO "cf-mips: identify drive..\n"); + res = cf_do_state(dev); + if (res == CF_TRANS_IN_PROGRESS) { + printk(KERN_ERR "cf-mips: BUG: async identify cmd\n"); + return CF_TRANS_FAILED; + } + if (res != CF_TRANS_OK) + return 0; + + dev->head = sbuf[3]; + dev->cyl = sbuf[1]; + dev->spt = sbuf[6]; + dev->sectors = ((unsigned long) sbuf[7] << 16) | sbuf[8]; + dev->dtype=sbuf[0]; + memcpy(tstr, &sbuf[12], 16); + tstr[16] = '\0'; + + /* Byte-swap the serial number */ + for (i = 0; i<8; i++) { + tmp = tstr[i * 2]; + tstr[i * 2] = tstr[i * 2 +1]; + tstr[i * 2 + 1] = tmp; + } + + printk(KERN_INFO "cf-mips: %s detected, C/H/S=%d/%d/%d sectors=%u (%uMB) Serial=%s\n", + (sbuf[0] == 0x848A ? "CF card" : "ATA drive"), dev->cyl, dev->head, + dev->spt, dev->sectors, dev->sectors >> 11, tstr); + return 1; +} + +static void init_multiple(struct cf_mips_dev * dev) +{ + int res; + DEBUGP(KERN_INFO "cf-mips: detecting block size\n"); + + dev->block_size = 128; /* max block size = 128 sectors (64KB) */ + do { + wareg(dev->block_size, ATA_REG_SC,dev); + wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev); + wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev); + + res = wait_not_busy(10 * SECS, 1,dev); + if (res != CF_TRANS_OK) { + printk(KERN_ERR "cf-mips: failed to detect block size: busy!\n"); + dev->block_size = 1; + return; + } + if ((rareg(ATA_REG_ST,dev) & ATA_REG_ST_ERR) == 0) + break; + dev->block_size /= 2; + } while (dev->block_size > 1); + + printk(KERN_INFO "cf-mips: multiple sectors = %d\n", dev->block_size); +} + +int cf_init(struct cf_mips_dev *dev) +{ + tasklet_init(&dev->tasklet,cf_do_tasklet,(unsigned long)dev); + dev->baddr = ioremap_nocache((unsigned long)dev->base, CFDEV_BUF_SIZE); + if (!dev->baddr) { + printk(KERN_ERR "cf-mips: cf_init: ioremap for (%lx,%x) failed\n", + (unsigned long) dev->base, CFDEV_BUF_SIZE); + return -EBUSY; + } + + if (!cf_present(dev)) { + printk(KERN_WARNING "cf-mips: cf card not present\n"); + iounmap(dev->baddr); + return -ENODEV; + } + + if (do_reset(dev) != CF_TRANS_OK) { + printk(KERN_ERR "cf-mips: cf reset failed\n"); + iounmap(dev->baddr); + return -EBUSY; + } + + if (!do_identify(dev)) { + printk(KERN_ERR "cf-mips: cf identify failed\n"); + iounmap(dev->baddr); + return -EBUSY; + } + +/* set_apm_level(ATA_APM_WITH_STANDBY); */ + init_multiple(dev); + + init_timer(&dev->to_timer); + dev->to_timer.function = cf_async_timeout; + dev->to_timer.data = (unsigned long)dev; + + prepare_cf_irq(dev); + if (request_irq(dev->irq, cf_irq_handler, 0, "CF Mips", dev)) { + printk(KERN_ERR "cf-mips: failed to get irq\n"); + iounmap(dev->baddr); + return -EBUSY; + } + /* Disable below would be odd, because request will enable, and the tasklet + will disable it itself */ + //disable_irq(dev->irq); + + dev->async_mode = 1; + + return 0; +} + +void cf_cleanup(struct cf_mips_dev *dev) +{ + iounmap(dev->baddr); + free_irq(dev->irq, NULL); +#if REQUEST_MEM_REGION + release_mem_region((unsigned long)dev->base, CFDEV_BUF_SIZE); +#endif +} + + +/*eof*/ diff --git a/target/linux/rb532/files-2.6.23/drivers/block/rb500/ata.h b/target/linux/rb532/files-2.6.23/drivers/block/rb500/ata.h new file mode 100644 index 0000000000..15e8826308 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/block/rb500/ata.h @@ -0,0 +1,143 @@ +/* CF-mips driver + This is a block driver for the direct (mmaped) interface to the CF-slot, + found in Routerboard.com's RB532 board + See SDK provided from routerboard.com. + + Module adapted By P.Christeas , 2005-6. + Cleaned up and adapted to platform_device by Felix Fietkau + + This work is redistributed under the terms of the GNU General Public License. +*/ + +#ifndef __CFMIPS_ATA_H__ +#define __CFMIPS_ATA_H__ + +#include + +#define CFG_DC_DEV1 (void*)0xb8010010 +#define CFG_DC_DEVBASE 0x0 +#define CFG_DC_DEVMASK 0x4 +#define CFG_DC_DEVC 0x8 +#define CFG_DC_DEVTC 0xC + +#define CFDEV_BUF_SIZE 0x1000 +#define ATA_CIS_OFFSET 0x200 +#define ATA_REG_OFFSET 0x800 +#define ATA_DBUF_OFFSET 0xC00 + +#define ATA_REG_FEAT 0x1 +#define ATA_REG_SC 0x2 +#define ATA_REG_SN 0x3 +#define ATA_REG_CL 0x4 +#define ATA_REG_CH 0x5 +#define ATA_REG_DH 0x6 +#define ATA_REG_DH_BASE 0xa0 +#define ATA_REG_DH_LBA 0x40 +#define ATA_REG_DH_DRV 0x10 +#define ATA_REG_CMD 0x7 +#define ATA_REG_ST 0x7 +#define ATA_REG_ST_BUSY 0x80 +#define ATA_REG_ST_RDY 0x40 +#define ATA_REG_ST_DWF 0x20 +#define ATA_REG_ST_DSC 0x10 +#define ATA_REG_ST_DRQ 0x08 +#define ATA_REG_ST_CORR 0x04 +#define ATA_REG_ST_ERR 0x01 +#define ATA_REG_ERR 0xd +#define ATA_REG_DC 0xe +#define ATA_REG_DC_IEN 0x02 +#define ATA_REG_DC_SRST 0x04 + +#define ATA_CMD_READ_SECTORS 0x20 +#define ATA_CMD_WRITE_SECTORS 0x30 +#define ATA_CMD_EXEC_DRIVE_DIAG 0x90 +#define ATA_CMD_READ_MULTIPLE 0xC4 +#define ATA_CMD_WRITE_MULTIPLE 0xC5 +#define ATA_CMD_SET_MULTIPLE 0xC6 +#define ATA_CMD_IDENTIFY_DRIVE 0xEC +#define ATA_CMD_SET_FEATURES 0xEF + +#define ATA_FEATURE_ENABLE_APM 0x05 +#define ATA_FEATURE_DISABLE_APM 0x85 +#define ATA_APM_DISABLED 0x00 +#define ATA_APM_MIN_POWER 0x01 +#define ATA_APM_WITH_STANDBY 0x7f +#define ATA_APM_WITHOUT_STANDBY 0x80 +#define ATA_APM_MAX_PERFORMANCE 0xfe + +#define CF_SECT_SIZE 0x200 +/* That is the ratio CF_SECT_SIZE/512 (the kernel sector size) */ +#define CF_KERNEL_MUL 1 +#define ATA_MAX_SECT_PER_CMD 0x100 + +#define CF_TRANS_FAILED 0 +#define CF_TRANS_OK 1 +#define CF_TRANS_IN_PROGRESS 2 + + +enum trans_state { + TS_IDLE = 0, + TS_AFTER_RESET, + TS_READY, + TS_CMD, + TS_TRANS +}; + +// +// #if DEBUG +// static unsigned long busy_time; +// #endif + +/** Struct to hold the cfdev +Actually, all the data here only has one instance. However, for +reasons of programming conformity, it is passed around as a pointer +*/ +struct cf_mips_dev { + void *base; /* base address for I/O */ + void *baddr; /* remapped address */ + + int pin; /* gpio pin */ + int irq; /* gpio irq */ + + unsigned head; + unsigned cyl; + unsigned spt; + unsigned sectors; + + unsigned short block_size; + unsigned dtype ; // ATA or CF + struct request_queue *queue; + struct gendisk *gd; + + /* Transaction state */ + enum trans_state tstate; + char *tbuf; + unsigned long tbuf_size; + sector_t tsect_start; + unsigned tsector_count; + unsigned tsectors_left; + int tread; + unsigned tcmd; + int async_mode; + unsigned long irq_enable_time; + + struct request *active_req; /* A request is being carried out. Is that different from tstate? */ + int users; + struct timer_list to_timer; + struct tasklet_struct tasklet; + + /** This lock ensures that the requests to this device are all done + atomically. Transfers can run in parallel, requests are all queued + one-by-one */ + spinlock_t lock; +}; + +int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, + char* buffer, int is_write); +int cf_init(struct cf_mips_dev* dev); +void cf_cleanup(struct cf_mips_dev* dev); + +void cf_async_trans_done(struct cf_mips_dev* dev, int result); +// void *cf_get_next_buf(unsigned long *buf_size); + +#endif diff --git a/target/linux/rb532/files-2.6.23/drivers/block/rb500/bdev.c b/target/linux/rb532/files-2.6.23/drivers/block/rb500/bdev.c new file mode 100644 index 0000000000..f8a9b02d70 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/block/rb500/bdev.c @@ -0,0 +1,340 @@ +/* CF-mips driver + This is a block driver for the direct (mmaped) interface to the CF-slot, + found in Routerboard.com's RB532 board + See SDK provided from routerboard.com. + + Module adapted By P.Christeas , 2005-6. + Cleaned up and adapted to platform_device by Felix Fietkau + + This work is redistributed under the terms of the GNU General Public License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef DEBUG +#define DEBUGP printk +#define DLEVEL 1 +#else +#define DEBUGP(format, args...) +#define DLEVEL 0 +#endif + +#define CF_MIPS_MAJOR 13 +#define MAJOR_NR CF_MIPS_MAJOR +#define CF_MAX_PART 16 /* max 15 partitions */ + +#include "ata.h" + +//extern struct block_device_operations cf_bdops; + +// static struct hd_struct cf_parts[CF_MAX_PART]; +// static int cf_part_sizes[CF_MAX_PART]; +// static int cf_hsect_sizes[CF_MAX_PART]; +// static int cf_max_sectors[CF_MAX_PART]; +// static int cf_blksize_sizes[CF_MAX_PART]; + +// static spinlock_t lock = SPIN_LOCK_UNLOCKED; + +// volatile int cf_busy = 0; + +static struct request *active_req = NULL; + +static int cf_open (struct inode *, struct file *); +static int cf_release (struct inode *, struct file *); +static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long); + +static void cf_request(request_queue_t * q); +static int cf_transfer(const struct request *req); + +/*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); +long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/ +// int (*direct_access) (struct block_device *, sector_t, unsigned long *); +// int (*media_changed) (struct gendisk *); +// int (*revalidate_disk) (struct gendisk *); + +static struct block_device_operations cf_bdops = { + .owner = THIS_MODULE, + .open = cf_open, + .release = cf_release, + .ioctl = cf_ioctl, + .media_changed = NULL, + .unlocked_ioctl = NULL, + .revalidate_disk = NULL, + .compat_ioctl = NULL, + .direct_access = NULL +}; + + +int cf_mips_probe(struct platform_device *pdev) +{ + struct gendisk* cf_gendisk=NULL; + struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data; + struct cf_mips_dev *dev; + struct resource *r; + int reg_result; + + reg_result = register_blkdev(MAJOR_NR, "cf-mips"); + if (reg_result < 0) { + printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR); + return reg_result; + } + + dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL); + if (!dev) + goto out_err; + memset(dev, 0, sizeof(struct cf_mips_dev)); + cdev->dev = dev; + + dev->pin = cdev->gpio_pin; + dev->irq = platform_get_irq_byname(pdev, "cf_irq"); + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase"); + dev->base = (void *) r->start; + + if (cf_init(dev)) goto out_err; + printk("init done"); + + spin_lock_init(&dev->lock); + dev->queue = blk_init_queue(cf_request,&dev->lock); + if (!dev->queue){ + printk(KERN_ERR "cf-mips: no mem for queue\n"); + goto out_err; + } + blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD); + + /* For memory devices, it is always better to avoid crossing segments + inside the same request. */ +/* if (dev->dtype==0x848A){ + printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1); + blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1); + }*/ + + dev->gd = alloc_disk(CF_MAX_PART); + cf_gendisk = dev->gd; + cdev->gd = dev->gd; + if (!cf_gendisk) goto out_err; /* Last of these goto's */ + + cf_gendisk->major = MAJOR_NR; + cf_gendisk->first_minor = 0; + cf_gendisk->queue=dev->queue; + BUG_ON(cf_gendisk->minors != CF_MAX_PART); + strcpy(cf_gendisk->disk_name,"cfa"); + cf_gendisk->fops = &cf_bdops; + cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */ + cf_gendisk->private_data=dev; + + set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL); + + /* Let the disk go live */ + add_disk(cf_gendisk); +#if 0 + result = cf_init(); + + /* default cfg for all partitions */ + memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART); + memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART); + for (i = 0; i < CF_MAX_PART; ++i) { + cf_hsect_sizes[i] = CF_SECT_SIZE; + cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD; + cf_blksize_sizes[i] = BLOCK_SIZE; + } + + /* setup info for whole disk (partition 0) */ + cf_part_sizes[0] = cf_sectors / 2; + cf_parts[0].nr_sects = cf_sectors; + + blk_size[MAJOR_NR] = cf_part_sizes; + blksize_size[MAJOR_NR] = cf_blksize_sizes; + max_sectors[MAJOR_NR] = cf_max_sectors; + hardsect_size[MAJOR_NR] = cf_hsect_sizes; + read_ahead[MAJOR_NR] = 8; /* (4kB) */ + + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + + add_gendisk(&cf_gendisk); +#endif +// printk(KERN_INFO "cf-mips partition check: \n"); +// register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART, +// &cf_bdops, dev->sectors); + return 0; + +out_err: + if (dev->queue){ + blk_cleanup_queue(dev->queue); + } + if (reg_result) { + unregister_blkdev(MAJOR_NR, "cf-mips"); + return reg_result; + } + if (dev){ + cf_cleanup(dev); + kfree(dev); + } + return 1; +} + +static int +cf_mips_remove(struct platform_device *pdev) +{ + struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data; + struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev; + + unregister_blkdev(MAJOR_NR, "cf-mips"); + blk_cleanup_queue(dev->queue); + + del_gendisk(dev->gd); + cf_cleanup(dev); + return 0; +} + + +static struct platform_driver cf_driver = { + .driver.name = "rb500-cf", + .probe = cf_mips_probe, + .remove = cf_mips_remove, +}; + +static int __init cf_mips_init(void) +{ + printk(KERN_INFO "cf-mips module loaded\n"); + return platform_driver_register(&cf_driver); +} + +static void cf_mips_cleanup(void) +{ + platform_driver_unregister(&cf_driver); + printk(KERN_INFO "cf-mips module removed\n"); +} + +module_init(cf_mips_init); +module_exit(cf_mips_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR); + + +static int cf_open(struct inode *inode, struct file *filp) +{ + struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; + int minor = MINOR(inode->i_rdev); + + if (minor >= CF_MAX_PART) + return -ENODEV; + //DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor); + spin_lock(&dev->lock); + dev->users++; + spin_unlock(&dev->lock); + filp->private_data=dev; + + /* dirty workaround to set CFRDY GPIO as an input when some other + program sets it as an output */ + gpio_set_value(dev->pin, 0); + return 0; /* success */ +} + +static int cf_release(struct inode *inode, struct file *filp) +{ + int minor = MINOR(inode->i_rdev); + struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; + spin_lock(&dev->lock); + dev->users--; + spin_unlock(&dev->lock); + return 0; +} + +static int cf_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + unsigned minor = MINOR(inode->i_rdev); + struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; + + DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd); + switch (cmd) { + case BLKRRPART: /* re-read partition table */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + printk(KERN_INFO "cf-mips partition check: \n"); + register_disk(dev->gd); + return 0; + + case HDIO_GETGEO: + { + struct hd_geometry geo; + geo.cylinders = dev->cyl; + geo.heads = dev->head; + geo.sectors = dev->spt; + geo.start = (*dev->gd->part)[minor].start_sect; + if (copy_to_user((void *) arg, &geo, sizeof (geo))) + return -EFAULT; + } + return 0; + } + + return -EINVAL; /* unknown command */ +} + +static void cf_request(request_queue_t * q) +{ + struct cf_mips_dev* dev; + + struct request * req; + int status; + + /* We could have q->queuedata = dev , but haven't yet. */ + if (active_req) + return; + + while ((req=elv_next_request(q))!=NULL){ + dev=req->rq_disk->private_data; + status=cf_transfer(req); + if (status==CF_TRANS_IN_PROGRESS){ + active_req=req; + return; + } + end_request(req,status); + } +} + +static int cf_transfer(const struct request *req) +{ + struct cf_mips_dev* dev=req->rq_disk->private_data; + + if (!blk_fs_request(req)){ + if (printk_ratelimit()) + printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]); + return CF_TRANS_FAILED; + } + + return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req)); +} + +void cf_async_trans_done(struct cf_mips_dev * dev,int result) +{ + struct request *req; + + spin_lock(&dev->lock); + req=active_req; + active_req=NULL; + end_request(req,result); + spin_unlock(&dev->lock); + + spin_lock(&dev->lock); + cf_request(dev->queue); + spin_unlock(&dev->lock); +} + diff --git a/target/linux/rb532/files-2.6.23/drivers/char/watchdog/rc32434_wdt.c b/target/linux/rb532/files-2.6.23/drivers/char/watchdog/rc32434_wdt.c new file mode 100644 index 0000000000..2266329def --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/char/watchdog/rc32434_wdt.c @@ -0,0 +1,240 @@ +/* + * RC32434_WDT 0.01: IDT Interprise 79RC32434 watchdog driver + * Copyright (c) Ondrej Zajicek , 2006 + * + * based on + * + * SoftDog 0.05: A Software Watchdog Device + * + * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_TIMEOUT 15 /* (secs) Default is 15 seconds */ +#define MAX_TIMEOUT 20 +/* + * (secs) Max is 20 seconds + * (max frequency of counter is ~200 MHz, counter is 32-bit unsigned int) + */ + +#define NAME "rc32434_wdt" +#define VERSION "0.1" + +static INTEG_t rc_wdt = (INTEG_t) INTEG_VirtualAddress; + +static int expect_close = 0; +static int access = 0; +static int timeout = 0; + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +MODULE_LICENSE("GPL"); + + +static inline void start_wdt(void) +{ + rc_wdt -> wtcount = 0; + rc_wdt -> errcs |= ERRCS_wre_m; + rc_wdt -> wtc |= WTC_en_m; +} + +static inline void stop_wdt(void) +{ + rc_wdt -> wtc &= ~WTC_en_m; + rc_wdt -> errcs &= ~ERRCS_wre_m; +} + +static inline void set_wdt(int new_timeout) +{ + u32 cmp = new_timeout * mips_hpt_frequency; + u32 state; + + timeout = new_timeout; + /* + * store and disable WTC + */ + state = rc_wdt -> wtc & WTC_en_m; + rc_wdt -> wtc &= ~WTC_en_m; + + rc_wdt -> wtcount = 0; + rc_wdt -> wtcompare = cmp; + + /* + * restore WTC + */ + rc_wdt -> wtc |= state; +} + +static inline void update_wdt(void) +{ + rc_wdt -> wtcount = 0; +} + +/* + * Allow only one person to hold it open + */ + +static int wdt_open(struct inode *inode, struct file *file) +{ + if (access) + return -EBUSY; + if (nowayout) { + __module_get(THIS_MODULE); + } + /* + * Activate timer + */ + start_wdt(); + printk(KERN_INFO NAME ": enabling watchdog timer\n"); + access = 1; + return 0; +} + +static int wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + * Lock it in if it's a module and we set nowayout + */ + if (expect_close && nowayout == 0) { + stop_wdt (); + printk(KERN_INFO NAME ": disabling watchdog timer\n"); + module_put(THIS_MODULE); + } else { + printk (KERN_CRIT NAME ": device closed unexpectedly. WDT will not stop!\n"); + } + access = 0; + return 0; +} + +static ssize_t wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* + * Refresh the timer. + */ + if (len) { + if (!nowayout) { + size_t i; + + /* In case it was set long ago */ + expect_close = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + } + update_wdt (); + return len; + } + return 0; +} + +static int wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_timeout; + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "RC32434_WDT Watchdog", + }; + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0,(int *)arg); + case WDIOC_KEEPALIVE: + update_wdt(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, (int *)arg)) + return -EFAULT; + if (new_timeout < 1) + return -EINVAL; + if (new_timeout > MAX_TIMEOUT) + return -EINVAL; + set_wdt(new_timeout); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timeout, (int *)arg); + } +} + +static struct file_operations wdt_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + write: wdt_write, + ioctl: wdt_ioctl, + open: wdt_open, + release: wdt_release, +}; + +static struct miscdevice wdt_miscdev = { + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt_fops, +}; + +static char banner[] __initdata = KERN_INFO NAME ": Watchdog Timer version " VERSION ", timer margin: %d sec\n"; + +static int __init watchdog_init(void) +{ + int ret; + + /* + * There should be check for RC32434 SoC + */ + if (mips_machgroup != MACH_GROUP_MIKROTIK) return -1; + + ret = misc_register(&wdt_miscdev); + + if (ret) + return ret; + + stop_wdt(); + set_wdt(DEFAULT_TIMEOUT); + + printk(banner, timeout); + + return 0; +} + +static void __exit watchdog_exit(void) +{ + misc_deregister(&wdt_miscdev); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); diff --git a/target/linux/rb532/files-2.6.23/drivers/leds/leds-rb500.c b/target/linux/rb532/files-2.6.23/drivers/leds/leds-rb500.c new file mode 100644 index 0000000000..3ab530a758 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/leds/leds-rb500.c @@ -0,0 +1,81 @@ +/* + * linux/drivers/leds/leds-rb500.c + * + * Copyright (C) 2006 + * Twente Institute for Wireless and Mobile Communications BV + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details (see file GPLv2). + * + * Author: Tjalling Hattink + */ + +#include +#include +#include +#include +#include + +static void rb500led_amber_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + if (value) + changeLatchU5(LO_ULED, 0); + else + changeLatchU5(0, LO_ULED); +} + +static struct led_classdev rb500_amber_led = { + .name = "rb500led:amber", + .default_trigger = "ide-disk", + .brightness_set = rb500led_amber_set, +}; + +static int rb500led_probe(struct platform_device *pdev) +{ + int ret; + + changeLatchU5(0, LO_ULED); + + ret = led_classdev_register(&pdev->dev, &rb500_amber_led); + + return ret; +} + +static int rb500led_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&rb500_amber_led); + + return 0; +} + +static struct platform_driver rb500led_driver = { + .probe = rb500led_probe, + .remove = rb500led_remove, + .driver = { + .name = "rb500-led", + }, +}; + +static int __init rb500led_init(void) +{ + return platform_driver_register(&rb500led_driver); +} + +static void __exit rb500led_exit(void) +{ + platform_driver_unregister(&rb500led_driver); +} + +module_init(rb500led_init); +module_exit(rb500led_exit); + +MODULE_AUTHOR("tjalling.hattink@ti-wmc.nl"); +MODULE_DESCRIPTION("Mikrotik RB500 LED driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/rb532/files-2.6.23/drivers/net/korina.c b/target/linux/rb532/files-2.6.23/drivers/net/korina.c new file mode 100644 index 0000000000..e89f334cf2 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/net/korina.c @@ -0,0 +1,1167 @@ +/************************************************************************** + * + * BRIEF MODULE DESCRIPTION + * Driver for the IDT RC32434 on-chip ethernet controller. + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * Copyright 2006 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Based on the driver developed by B. Maruthanayakam, H. Kou and others. + * + * Aug 2004 Sadik + * + * Added NAPI + * + ************************************************************************** + */ + +#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 "rc32434_eth.h" + +#define DRIVER_VERSION "(mar2904)" + +#define DRIVER_NAME "rc32434 Ethernet driver. " DRIVER_VERSION + +#define STATION_ADDRESS_HIGH(dev) (((dev)->dev_addr[0] << 8) | \ + ((dev)->dev_addr[1])) +#define STATION_ADDRESS_LOW(dev) (((dev)->dev_addr[2] << 24) | \ + ((dev)->dev_addr[3] << 16) | \ + ((dev)->dev_addr[4] << 8) | \ + ((dev)->dev_addr[5])) + +#define MII_CLOCK 1250000 /* no more than 2.5MHz */ +#define CONFIG_IDT_USE_NAPI 1 + + +static inline void rc32434_abort_tx(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + rc32434_abort_dma(dev, lp->tx_dma_regs); + +} + +static inline void rc32434_abort_rx(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + rc32434_abort_dma(dev, lp->rx_dma_regs); + +} + +static inline void rc32434_start_tx(struct rc32434_local *lp, volatile DMAD_t td) +{ + rc32434_start_dma(lp->tx_dma_regs, CPHYSADDR(td)); +} + +static inline void rc32434_start_rx(struct rc32434_local *lp, volatile DMAD_t rd) +{ + rc32434_start_dma(lp->rx_dma_regs, CPHYSADDR(rd)); +} + +static inline void rc32434_chain_tx(struct rc32434_local *lp, volatile DMAD_t td) +{ + rc32434_chain_dma(lp->tx_dma_regs, CPHYSADDR(td)); +} + +static inline void rc32434_chain_rx(struct rc32434_local *lp, volatile DMAD_t rd) +{ + rc32434_chain_dma(lp->rx_dma_regs, CPHYSADDR(rd)); +} + +#ifdef RC32434_PROC_DEBUG +static int rc32434_read_proc(char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct net_device *dev = (struct net_device *)data; + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + int len = 0; + + /* print out header */ + len += sprintf(buf + len, "\n\tKorina Ethernet Debug\n\n"); + len += sprintf (buf + len, + "DMA halt count = %10d, DMA run count = %10d\n", + lp->dma_halt_cnt, lp->dma_run_cnt); + + if (fpos >= len) { + *start = buf; + *eof = 1; + return 0; + } + *start = buf + fpos; + + if ((len -= fpos) > length) + return length; + *eof = 1; + + return len; + +} +#endif + + +/* + * Restart the RC32434 ethernet controller. + */ +static int rc32434_restart(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + + /* + * Disable interrupts + */ + disable_irq(lp->rx_irq); + disable_irq(lp->tx_irq); +#ifdef RC32434_REVISION + disable_irq(lp->ovr_irq); +#endif + disable_irq(lp->und_irq); + + /* Mask F E bit in Tx DMA */ + __raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) | DMASM_f_m | DMASM_e_m, &lp->tx_dma_regs->dmasm); + /* Mask D H E bit in Rx DMA */ + __raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) | DMASM_d_m | DMASM_h_m | DMASM_e_m, &lp->rx_dma_regs->dmasm); + + rc32434_init(dev); + rc32434_multicast_list(dev); + + enable_irq(lp->und_irq); +#ifdef RC32434_REVISION + enable_irq(lp->ovr_irq); +#endif + enable_irq(lp->tx_irq); + enable_irq(lp->rx_irq); + + return 0; +} + +static int rc32434_probe(struct platform_device *pdev) +{ + struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data; + struct rc32434_local *lp = NULL; + struct net_device *dev = NULL; + struct resource *r; + int i, retval,err; + + dev = alloc_etherdev(sizeof(struct rc32434_local)); + if(!dev) { + ERR("Korina_eth: alloc_etherdev failed\n"); + return -1; + } + + platform_set_drvdata(pdev, dev); + SET_MODULE_OWNER(dev); + bif->dev = dev; + + memcpy(dev->dev_addr, bif->mac, 6); + + /* Initialize the device structure. */ + if (dev->priv == NULL) { + lp = (struct rc32434_local *)kmalloc(sizeof(*lp), GFP_KERNEL); + memset(lp, 0, sizeof(struct rc32434_local)); + } + else { + lp = (struct rc32434_local *)dev->priv; + } + + lp->rx_irq = platform_get_irq_byname(pdev, "korina_rx"); + lp->tx_irq = platform_get_irq_byname(pdev, "korina_tx"); + lp->ovr_irq = platform_get_irq_byname(pdev, "korina_ovr"); + lp->und_irq = platform_get_irq_byname(pdev, "korina_und"); + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_regs"); + dev->base_addr = r->start; + lp->eth_regs = ioremap_nocache(r->start, r->end - r->start); + if (!lp->eth_regs) { + ERR("Can't remap eth registers\n"); + retval = -ENXIO; + goto probe_err_out; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_rx"); + lp->rx_dma_regs = ioremap_nocache(r->start, r->end - r->start); + if (!lp->rx_dma_regs) { + ERR("Can't remap Rx DMA registers\n"); + retval = -ENXIO; + goto probe_err_out; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_tx"); + lp->tx_dma_regs = ioremap_nocache(r->start, r->end - r->start); + if (!lp->tx_dma_regs) { + ERR("Can't remap Tx DMA registers\n"); + retval = -ENXIO; + goto probe_err_out; + } + +#ifdef RC32434_PROC_DEBUG + lp->ps = create_proc_read_entry (bif->name, 0, proc_net, + rc32434_read_proc, dev); +#endif + + lp->td_ring = (DMAD_t)kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL); + if (!lp->td_ring) { + ERR("Can't allocate descriptors\n"); + retval = -ENOMEM; + goto probe_err_out; + } + + dma_cache_inv((unsigned long)(lp->td_ring), TD_RING_SIZE + RD_RING_SIZE); + + /* now convert TD_RING pointer to KSEG1 */ + lp->td_ring = (DMAD_t )KSEG1ADDR(lp->td_ring); + lp->rd_ring = &lp->td_ring[RC32434_NUM_TDS]; + + + spin_lock_init(&lp->lock); + + /* just use the rx dma irq */ + dev->irq = lp->rx_irq; + + dev->priv = lp; + + dev->open = rc32434_open; + dev->stop = rc32434_close; + dev->hard_start_xmit = rc32434_send_packet; + dev->get_stats = rc32434_get_stats; + dev->set_multicast_list = &rc32434_multicast_list; + dev->tx_timeout = rc32434_tx_timeout; + dev->watchdog_timeo = RC32434_TX_TIMEOUT; + +#ifdef CONFIG_IDT_USE_NAPI + dev->poll = rc32434_poll; + dev->weight = 64; + printk("Using NAPI with weight %d\n",dev->weight); +#else + lp->rx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL); + tasklet_init(lp->rx_tasklet, rc32434_rx_tasklet, (unsigned long)dev); +#endif + lp->tx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL); + tasklet_init(lp->tx_tasklet, rc32434_tx_tasklet, (unsigned long)dev); + + if ((err = register_netdev(dev))) { + printk(KERN_ERR "rc32434 ethernet. Cannot register net device %d\n", err); + free_netdev(dev); + retval = -EINVAL; + goto probe_err_out; + } + + INFO("Rx IRQ %d, Tx IRQ %d, ", lp->rx_irq, lp->tx_irq); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i<5) + printk(":"); + } + printk("\n"); + + return 0; + + probe_err_out: + rc32434_cleanup_module(); + ERR(" failed. Returns %d\n", retval); + return retval; + +} + +static int rc32434_remove(struct platform_device *pdev) +{ + struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data; + + if (bif->dev != NULL) { + struct rc32434_local *lp = (struct rc32434_local *)bif->dev->priv; + if (lp != NULL) { + if (lp->eth_regs) + iounmap((void*)lp->eth_regs); + if (lp->rx_dma_regs) + iounmap((void*)lp->rx_dma_regs); + if (lp->tx_dma_regs) + iounmap((void*)lp->tx_dma_regs); + if (lp->td_ring) + kfree((void*)KSEG0ADDR(lp->td_ring)); + +#ifdef RC32434_PROC_DEBUG + if (lp->ps) { + remove_proc_entry(bif->name, proc_net); + } +#endif + kfree(lp); + } + + platform_set_drvdata(pdev, NULL); + unregister_netdev(bif->dev); + free_netdev(bif->dev); + kfree(bif->dev); + } + return 0; +} + + +static int rc32434_open(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + + /* Initialize */ + if (rc32434_init(dev)) { + ERR("Error: cannot open the Ethernet device\n"); + return -EAGAIN; + } + + /* Install the interrupt handler that handles the Done Finished Ovr and Und Events */ + if (request_irq(lp->rx_irq, &rc32434_rx_dma_interrupt, + SA_SHIRQ | SA_INTERRUPT, + "Korina ethernet Rx", dev)) { + ERR(": unable to get Rx DMA IRQ %d\n", + lp->rx_irq); + return -EAGAIN; + } + if (request_irq(lp->tx_irq, &rc32434_tx_dma_interrupt, + SA_SHIRQ | SA_INTERRUPT, + "Korina ethernet Tx", dev)) { + ERR(": unable to get Tx DMA IRQ %d\n", + lp->tx_irq); + free_irq(lp->rx_irq, dev); + return -EAGAIN; + } + +#ifdef RC32434_REVISION + /* Install handler for overrun error. */ + if (request_irq(lp->ovr_irq, &rc32434_ovr_interrupt, + SA_SHIRQ | SA_INTERRUPT, + "Ethernet Overflow", dev)) { + ERR(": unable to get OVR IRQ %d\n", + lp->ovr_irq); + free_irq(lp->rx_irq, dev); + free_irq(lp->tx_irq, dev); + return -EAGAIN; + } +#endif + + /* Install handler for underflow error. */ + if (request_irq(lp->und_irq, &rc32434_und_interrupt, + SA_SHIRQ | SA_INTERRUPT, + "Ethernet Underflow", dev)) { + ERR(": unable to get UND IRQ %d\n", + lp->und_irq); + free_irq(lp->rx_irq, dev); + free_irq(lp->tx_irq, dev); +#ifdef RC32434_REVISION + free_irq(lp->ovr_irq, dev); +#endif + return -EAGAIN; + } + + + return 0; +} + + + + +static int rc32434_close(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + u32 tmp; + + /* Disable interrupts */ + disable_irq(lp->rx_irq); + disable_irq(lp->tx_irq); +#ifdef RC32434_REVISION + disable_irq(lp->ovr_irq); +#endif + disable_irq(lp->und_irq); + + tmp = __raw_readl(&lp->tx_dma_regs->dmasm); + tmp = tmp | DMASM_f_m | DMASM_e_m; + __raw_writel(tmp, &lp->tx_dma_regs->dmasm); + + tmp = __raw_readl(&lp->rx_dma_regs->dmasm); + tmp = tmp | DMASM_d_m | DMASM_h_m | DMASM_e_m; + __raw_writel(tmp, &lp->rx_dma_regs->dmasm); + + free_irq(lp->rx_irq, dev); + free_irq(lp->tx_irq, dev); +#ifdef RC32434_REVISION + free_irq(lp->ovr_irq, dev); +#endif + free_irq(lp->und_irq, dev); + return 0; +} + + +/* transmit packet */ +static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + unsigned long flags; + u32 length; + DMAD_t td; + + + spin_lock_irqsave(&lp->lock, flags); + + td = &lp->td_ring[lp->tx_chain_tail]; + + /* stop queue when full, drop pkts if queue already full */ + if(lp->tx_count >= (RC32434_NUM_TDS - 2)) { + lp->tx_full = 1; + + if(lp->tx_count == (RC32434_NUM_TDS - 2)) { + netif_stop_queue(dev); + } + else { + lp->stats.tx_dropped++; + dev_kfree_skb_any(skb); + spin_unlock_irqrestore(&lp->lock, flags); + return 1; + } + } + + lp->tx_count ++; + + lp->tx_skb[lp->tx_chain_tail] = skb; + + length = skb->len; + dma_cache_wback((u32)skb->data, skb->len); + + /* Setup the transmit descriptor. */ + dma_cache_inv((u32) td, sizeof(*td)); + td->ca = CPHYSADDR(skb->data); + + if(__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0) { + if( lp->tx_chain_status == empty ) { + td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; /* Update tail */ + lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ + __raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR */ + lp->tx_chain_head = lp->tx_chain_tail; /* Move head to tail */ + } + else { + td->control = DMA_COUNT(length) |DMAD_cof_m|DMAD_iof_m; /* Update tail */ + lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &= ~(DMAD_cof_m); /* Link to prev */ + lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link = CPHYSADDR(td); /* Link to prev */ + lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ + __raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR */ + lp->tx_chain_head = lp->tx_chain_tail; /* Move head to tail */ + lp->tx_chain_status = empty; + } + } + else { + if( lp->tx_chain_status == empty ) { + td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; /* Update tail */ + lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ + lp->tx_chain_status = filled; + } + else { + td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; /* Update tail */ + lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &= ~(DMAD_cof_m); /* Link to prev */ + lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link = CPHYSADDR(td); /* Link to prev */ + lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ + } + } + dma_cache_wback((u32) td, sizeof(*td)); + + dev->trans_start = jiffies; + + spin_unlock_irqrestore(&lp->lock, flags); + + return 0; +} + + +/* Ethernet MII-PHY Handler */ +static void rc32434_mii_handler(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + unsigned long flags; + unsigned long duplex_status; + int port_addr = (lp->rx_irq == 0x2c? 1:0) << 8; + + spin_lock_irqsave(&lp->lock, flags); + + /* Two ports are using the same MII, the difference is the PHY address */ + __raw_writel(0, &rc32434_eth0_regs->miimcfg); + __raw_writel(0, &rc32434_eth0_regs->miimcmd); + __raw_writel(port_addr |0x05, &rc32434_eth0_regs->miimaddr); + __raw_writel(MIIMCMD_scn_m, &rc32434_eth0_regs->miimcmd); + while(__raw_readl(&rc32434_eth0_regs->miimind) & MIIMIND_nv_m); + + ERR("irq:%x port_addr:%x RDD:%x\n", + lp->rx_irq, port_addr, __raw_readl(&rc32434_eth0_regs->miimrdd)); + duplex_status = (__raw_readl(&rc32434_eth0_regs->miimrdd) & 0x140)? ETHMAC2_fd_m: 0; + if(duplex_status != lp->duplex_mode) { + ERR("The MII-PHY is Auto-negotiated to %s-Duplex mode for Eth-%x\n", duplex_status? "Full":"Half", lp->rx_irq == 0x2c? 1:0); + lp->duplex_mode = duplex_status; + rc32434_restart(dev); + } + + lp->mii_phy_timer.expires = jiffies + 10 * HZ; + add_timer(&lp->mii_phy_timer); + + spin_unlock_irqrestore(&lp->lock, flags); + +} + +#ifdef RC32434_REVISION +/* Ethernet Rx Overflow interrupt */ +static irqreturn_t +rc32434_ovr_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct rc32434_local *lp; + unsigned int ovr; + irqreturn_t retval = IRQ_NONE; + + ASSERT(dev != NULL); + + lp = (struct rc32434_local *)dev->priv; + spin_lock(&lp->lock); + ovr = __raw_readl(&lp->eth_regs->ethintfc); + + if(ovr & ETHINTFC_ovr_m) { + netif_stop_queue(dev); + + /* clear OVR bit */ + __raw_writel((ovr & ~ETHINTFC_ovr_m), &lp->eth_regs->ethintfc); + + /* Restart interface */ + rc32434_restart(dev); + retval = IRQ_HANDLED; + } + spin_unlock(&lp->lock); + + return retval; +} + +#endif + + +/* Ethernet Tx Underflow interrupt */ +static irqreturn_t +rc32434_und_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct rc32434_local *lp; + unsigned int und; + irqreturn_t retval = IRQ_NONE; + + ASSERT(dev != NULL); + + lp = (struct rc32434_local *)dev->priv; + + spin_lock(&lp->lock); + + und = __raw_readl(&lp->eth_regs->ethintfc); + + if(und & ETHINTFC_und_m) { + netif_stop_queue(dev); + + __raw_writel((und & ~ETHINTFC_und_m), &lp->eth_regs->ethintfc); + + /* Restart interface */ + rc32434_restart(dev); + retval = IRQ_HANDLED; + } + + spin_unlock(&lp->lock); + + return retval; +} + + +/* Ethernet Rx DMA interrupt */ +static irqreturn_t +rc32434_rx_dma_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct rc32434_local* lp; + volatile u32 dmas,dmasm; + irqreturn_t retval; + + ASSERT(dev != NULL); + + lp = (struct rc32434_local *)dev->priv; + + spin_lock(&lp->lock); + dmas = __raw_readl(&lp->rx_dma_regs->dmas); + if(dmas & (DMAS_d_m|DMAS_h_m|DMAS_e_m)) { + /* Mask D H E bit in Rx DMA */ + dmasm = __raw_readl(&lp->rx_dma_regs->dmasm); + __raw_writel(dmasm | (DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); +#ifdef CONFIG_IDT_USE_NAPI + if(netif_rx_schedule_prep(dev)) + __netif_rx_schedule(dev); +#else + tasklet_hi_schedule(lp->rx_tasklet); +#endif + + if (dmas & DMAS_e_m) + ERR(": DMA error\n"); + + retval = IRQ_HANDLED; + } + else + retval = IRQ_NONE; + + spin_unlock(&lp->lock); + return retval; +} + +#ifdef CONFIG_IDT_USE_NAPI +static int rc32434_poll(struct net_device *rx_data_dev, int *budget) +#else +static void rc32434_rx_tasklet(unsigned long rx_data_dev) +#endif +{ + struct net_device *dev = (struct net_device *)rx_data_dev; + struct rc32434_local* lp = netdev_priv(dev); + volatile DMAD_t rd = &lp->rd_ring[lp->rx_next_done]; + struct sk_buff *skb, *skb_new; + u8* pkt_buf; + u32 devcs, count, pkt_len, pktuncrc_len; + volatile u32 dmas; +#ifdef CONFIG_IDT_USE_NAPI + u32 received = 0; + int rx_work_limit = min(*budget,dev->quota); +#else + unsigned long flags; + spin_lock_irqsave(&lp->lock, flags); +#endif + + dma_cache_inv((u32)rd, sizeof(*rd)); + while ( (count = RC32434_RBSIZE - (u32)DMA_COUNT(rd->control)) != 0) { +#ifdef CONFIG_IDT_USE_NAPI + if(--rx_work_limit <0) + { + break; + } +#endif + /* init the var. used for the later operations within the while loop */ + skb_new = NULL; + devcs = rd->devcs; + pkt_len = RCVPKT_LENGTH(devcs); + skb = lp->rx_skb[lp->rx_next_done]; + + if (count < 64) { + lp->stats.rx_errors++; + lp->stats.rx_dropped++; + } + else if ((devcs & ( ETHRX_ld_m)) != ETHRX_ld_m) { + /* check that this is a whole packet */ + /* WARNING: DMA_FD bit incorrectly set in Rc32434 (errata ref #077) */ + lp->stats.rx_errors++; + lp->stats.rx_dropped++; + } + else if ( (devcs & ETHRX_rok_m) ) { + + { + /* must be the (first and) last descriptor then */ + pkt_buf = (u8*)lp->rx_skb[lp->rx_next_done]->data; + + pktuncrc_len = pkt_len - 4; + /* invalidate the cache */ + dma_cache_inv((unsigned long)pkt_buf, pktuncrc_len); + + /* Malloc up new buffer. */ + skb_new = dev_alloc_skb(RC32434_RBSIZE + 2); + + if (skb_new != NULL){ + /* Make room */ + skb_put(skb, pktuncrc_len); + + skb->protocol = eth_type_trans(skb, dev); + + /* pass the packet to upper layers */ +#ifdef CONFIG_IDT_USE_NAPI + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif + + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += pktuncrc_len; + + if (IS_RCV_MP(devcs)) + lp->stats.multicast++; + + /* 16 bit align */ + skb_reserve(skb_new, 2); + + skb_new->dev = dev; + lp->rx_skb[lp->rx_next_done] = skb_new; + } + else { + ERR("no memory, dropping rx packet.\n"); + lp->stats.rx_errors++; + lp->stats.rx_dropped++; + } + } + + } + else { + /* This should only happen if we enable accepting broken packets */ + lp->stats.rx_errors++; + lp->stats.rx_dropped++; + + /* add statistics counters */ + if (IS_RCV_CRC_ERR(devcs)) { + DBG(2, "RX CRC error\n"); + lp->stats.rx_crc_errors++; + } + else if (IS_RCV_LOR_ERR(devcs)) { + DBG(2, "RX LOR error\n"); + lp->stats.rx_length_errors++; + } + else if (IS_RCV_LE_ERR(devcs)) { + DBG(2, "RX LE error\n"); + lp->stats.rx_length_errors++; + } + else if (IS_RCV_OVR_ERR(devcs)) { + lp->stats.rx_over_errors++; + } + else if (IS_RCV_CV_ERR(devcs)) { + /* code violation */ + DBG(2, "RX CV error\n"); + lp->stats.rx_frame_errors++; + } + else if (IS_RCV_CES_ERR(devcs)) { + DBG(2, "RX Preamble error\n"); + } + } + + rd->devcs = 0; + + /* restore descriptor's curr_addr */ + if(skb_new) + rd->ca = CPHYSADDR(skb_new->data); + else + rd->ca = CPHYSADDR(skb->data); + + rd->control = DMA_COUNT(RC32434_RBSIZE) |DMAD_cod_m |DMAD_iod_m; + lp->rd_ring[(lp->rx_next_done-1)& RC32434_RDS_MASK].control &= ~(DMAD_cod_m); + + lp->rx_next_done = (lp->rx_next_done + 1) & RC32434_RDS_MASK; + dma_cache_wback((u32)rd, sizeof(*rd)); + rd = &lp->rd_ring[lp->rx_next_done]; + __raw_writel( ~DMAS_d_m, &lp->rx_dma_regs->dmas); + } +#ifdef CONFIG_IDT_USE_NAPI + dev->quota -= received; + *budget =- received; + if(rx_work_limit < 0) + goto not_done; +#endif + + dmas = __raw_readl(&lp->rx_dma_regs->dmas); + + if(dmas & DMAS_h_m) { + __raw_writel( ~(DMAS_h_m | DMAS_e_m), &lp->rx_dma_regs->dmas); +#ifdef RC32434_PROC_DEBUG + lp->dma_halt_cnt++; +#endif + rd->devcs = 0; + skb = lp->rx_skb[lp->rx_next_done]; + rd->ca = CPHYSADDR(skb->data); + dma_cache_wback((u32)rd, sizeof(*rd)); + rc32434_chain_rx(lp,rd); + } + +#ifdef CONFIG_IDT_USE_NAPI + netif_rx_complete(dev); +#endif + /* Enable D H E bit in Rx DMA */ + __raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m |DMASM_e_m), &lp->rx_dma_regs->dmasm); +#ifdef CONFIG_IDT_USE_NAPI + return 0; + not_done: + return 1; +#else + spin_unlock_irqrestore(&lp->lock, flags); + return; +#endif + + +} + + + +/* Ethernet Tx DMA interrupt */ +static irqreturn_t +rc32434_tx_dma_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct rc32434_local *lp; + volatile u32 dmas,dmasm; + irqreturn_t retval; + + ASSERT(dev != NULL); + + lp = (struct rc32434_local *)dev->priv; + + spin_lock(&lp->lock); + + dmas = __raw_readl(&lp->tx_dma_regs->dmas); + + if (dmas & (DMAS_f_m | DMAS_e_m)) { + dmasm = __raw_readl(&lp->tx_dma_regs->dmasm); + /* Mask F E bit in Tx DMA */ + __raw_writel(dmasm | (DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); + + tasklet_hi_schedule(lp->tx_tasklet); + + if(lp->tx_chain_status == filled && (__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0)) { + __raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); + lp->tx_chain_status = empty; + lp->tx_chain_head = lp->tx_chain_tail; + dev->trans_start = jiffies; + } + + if (dmas & DMAS_e_m) + ERR(": DMA error\n"); + + retval = IRQ_HANDLED; + } + else + retval = IRQ_NONE; + + spin_unlock(&lp->lock); + + return retval; +} + + +static void rc32434_tx_tasklet(unsigned long tx_data_dev) +{ + struct net_device *dev = (struct net_device *)tx_data_dev; + struct rc32434_local* lp = (struct rc32434_local *)dev->priv; + volatile DMAD_t td = &lp->td_ring[lp->tx_next_done]; + u32 devcs; + unsigned long flags; + volatile u32 dmas; + + spin_lock_irqsave(&lp->lock, flags); + + /* process all desc that are done */ + while(IS_DMA_FINISHED(td->control)) { + if(lp->tx_full == 1) { + netif_wake_queue(dev); + lp->tx_full = 0; + } + + devcs = lp->td_ring[lp->tx_next_done].devcs; + if ((devcs & (ETHTX_fd_m | ETHTX_ld_m)) != (ETHTX_fd_m | ETHTX_ld_m)) { + lp->stats.tx_errors++; + lp->stats.tx_dropped++; + + /* should never happen */ + DBG(1, __FUNCTION__ ": split tx ignored\n"); + } + else if (IS_TX_TOK(devcs)) { + lp->stats.tx_packets++; + lp->stats.tx_bytes+=lp->tx_skb[lp->tx_next_done]->len; + } + else { + lp->stats.tx_errors++; + lp->stats.tx_dropped++; + + /* underflow */ + if (IS_TX_UND_ERR(devcs)) + lp->stats.tx_fifo_errors++; + + /* oversized frame */ + if (IS_TX_OF_ERR(devcs)) + lp->stats.tx_aborted_errors++; + + /* excessive deferrals */ + if (IS_TX_ED_ERR(devcs)) + lp->stats.tx_carrier_errors++; + + /* collisions: medium busy */ + if (IS_TX_EC_ERR(devcs)) + lp->stats.collisions++; + + /* late collision */ + if (IS_TX_LC_ERR(devcs)) + lp->stats.tx_window_errors++; + + } + + /* We must always free the original skb */ + if (lp->tx_skb[lp->tx_next_done] != NULL) { + dev_kfree_skb_any(lp->tx_skb[lp->tx_next_done]); + lp->tx_skb[lp->tx_next_done] = NULL; + } + + lp->td_ring[lp->tx_next_done].control = DMAD_iof_m; + lp->td_ring[lp->tx_next_done].devcs = ETHTX_fd_m | ETHTX_ld_m; + lp->td_ring[lp->tx_next_done].link = 0; + lp->td_ring[lp->tx_next_done].ca = 0; + lp->tx_count --; + + /* go on to next transmission */ + lp->tx_next_done = (lp->tx_next_done + 1) & RC32434_TDS_MASK; + td = &lp->td_ring[lp->tx_next_done]; + + } + + dmas = __raw_readl(&lp->tx_dma_regs->dmas); + __raw_writel( ~dmas, &lp->tx_dma_regs->dmas); + + /* Enable F E bit in Tx DMA */ + __raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); + spin_unlock_irqrestore(&lp->lock, flags); + +} + + +static struct net_device_stats * rc32434_get_stats(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + return &lp->stats; +} + + +/* + * Set or clear the multicast filter for this adaptor. + */ +static void rc32434_multicast_list(struct net_device *dev) +{ + /* listen to broadcasts always and to treat */ + /* IFF bits independantly */ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + unsigned long flags; + u32 recognise = ETHARC_ab_m; /* always accept broadcasts */ + + if (dev->flags & IFF_PROMISC) /* set promiscuous mode */ + recognise |= ETHARC_pro_m; + + if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 15)) + recognise |= ETHARC_am_m; /* all multicast & bcast */ + else if (dev->mc_count > 0) { + DBG(2, __FUNCTION__ ": mc_count %d\n", dev->mc_count); + recognise |= ETHARC_am_m; /* for the time being */ + } + + spin_lock_irqsave(&lp->lock, flags); + __raw_writel(recognise, &lp->eth_regs->etharc); + spin_unlock_irqrestore(&lp->lock, flags); +} + + +static void rc32434_tx_timeout(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + rc32434_restart(dev); + spin_unlock_irqrestore(&lp->lock, flags); + +} + + +/* + * Initialize the RC32434 ethernet controller. + */ +static int rc32434_init(struct net_device *dev) +{ + struct rc32434_local *lp = (struct rc32434_local *)dev->priv; + int i, j; + + /* Disable DMA */ + rc32434_abort_tx(dev); + rc32434_abort_rx(dev); + + /* reset ethernet logic */ + __raw_writel(0, &lp->eth_regs->ethintfc); + while((__raw_readl(&lp->eth_regs->ethintfc) & ETHINTFC_rip_m)) + dev->trans_start = jiffies; + + /* Enable Ethernet Interface */ + __raw_writel(ETHINTFC_en_m, &lp->eth_regs->ethintfc); + +#ifndef CONFIG_IDT_USE_NAPI + tasklet_disable(lp->rx_tasklet); +#endif + tasklet_disable(lp->tx_tasklet); + + /* Initialize the transmit Descriptors */ + for (i = 0; i < RC32434_NUM_TDS; i++) { + lp->td_ring[i].control = DMAD_iof_m; + lp->td_ring[i].devcs = ETHTX_fd_m | ETHTX_ld_m; + lp->td_ring[i].ca = 0; + lp->td_ring[i].link = 0; + if (lp->tx_skb[i] != NULL) { + dev_kfree_skb_any(lp->tx_skb[i]); + lp->tx_skb[i] = NULL; + } + } + lp->tx_next_done = lp->tx_chain_head = lp->tx_chain_tail = lp->tx_full = lp->tx_count = 0; + lp-> tx_chain_status = empty; + + /* + * Initialize the receive descriptors so that they + * become a circular linked list, ie. let the last + * descriptor point to the first again. + */ + for (i=0; irx_skb[i]; + + if (lp->rx_skb[i] == NULL) { + skb = dev_alloc_skb(RC32434_RBSIZE + 2); + if (skb == NULL) { + ERR("No memory in the system\n"); + for (j = 0; j < RC32434_NUM_RDS; j ++) + if (lp->rx_skb[j] != NULL) + dev_kfree_skb_any(lp->rx_skb[j]); + + return 1; + } + else { + skb->dev = dev; + skb_reserve(skb, 2); + lp->rx_skb[i] = skb; + lp->rd_ring[i].ca = CPHYSADDR(skb->data); + + } + } + lp->rd_ring[i].control = DMAD_iod_m | DMA_COUNT(RC32434_RBSIZE); + lp->rd_ring[i].devcs = 0; + lp->rd_ring[i].ca = CPHYSADDR(skb->data); + lp->rd_ring[i].link = CPHYSADDR(&lp->rd_ring[i+1]); + + } + /* loop back */ + lp->rd_ring[RC32434_NUM_RDS-1].link = CPHYSADDR(&lp->rd_ring[0]); + lp->rx_next_done = 0; + + lp->rd_ring[RC32434_NUM_RDS-1].control |= DMAD_cod_m; + lp->rx_chain_head = 0; + lp->rx_chain_tail = 0; + lp->rx_chain_status = empty; + + __raw_writel(0, &lp->rx_dma_regs->dmas); + /* Start Rx DMA */ + rc32434_start_rx(lp, &lp->rd_ring[0]); + + /* Enable F E bit in Tx DMA */ + __raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); + /* Enable D H E bit in Rx DMA */ + __raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); + + /* Accept only packets destined for this Ethernet device address */ + __raw_writel(ETHARC_ab_m, &lp->eth_regs->etharc); + + /* Set all Ether station address registers to their initial values */ + __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal0); + __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah0); + + __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal1); + __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah1); + + __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal2); + __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah2); + + __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal3); + __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah3); + + + /* Frame Length Checking, Pad Enable, CRC Enable, Full Duplex set */ + __raw_writel(ETHMAC2_pe_m | ETHMAC2_cen_m | ETHMAC2_fd_m, &lp->eth_regs->ethmac2); + //ETHMAC2_flc_m ETHMAC2_fd_m lp->duplex_mode + + /* Back to back inter-packet-gap */ + __raw_writel(0x15, &lp->eth_regs->ethipgt); + /* Non - Back to back inter-packet-gap */ + __raw_writel(0x12, &lp->eth_regs->ethipgr); + + /* Management Clock Prescaler Divisor */ + /* Clock independent setting */ + __raw_writel(((idt_cpu_freq)/MII_CLOCK+1) & ~1, + &lp->eth_regs->ethmcp); + + /* don't transmit until fifo contains 48b */ + __raw_writel(48, &lp->eth_regs->ethfifott); + + __raw_writel(ETHMAC1_re_m, &lp->eth_regs->ethmac1); + +#ifndef CONFIG_IDT_USE_NAPI + tasklet_enable(lp->rx_tasklet); +#endif + tasklet_enable(lp->tx_tasklet); + + netif_start_queue(dev); + + return 0; +} + +static struct platform_driver korina_driver = { + .driver.name = "korina", + .probe = rc32434_probe, + .remove = rc32434_remove, +}; + +static int __init rc32434_init_module(void) +{ + return platform_driver_register(&korina_driver); +} + +static void rc32434_cleanup_module(void) +{ + return platform_driver_unregister(&korina_driver); +} + +module_init(rc32434_init_module); +module_exit(rc32434_cleanup_module); diff --git a/target/linux/rb532/files-2.6.23/drivers/net/rc32434_eth.h b/target/linux/rb532/files-2.6.23/drivers/net/rc32434_eth.h new file mode 100644 index 0000000000..eaa0bfe51b --- /dev/null +++ b/target/linux/rb532/files-2.6.23/drivers/net/rc32434_eth.h @@ -0,0 +1,178 @@ +/************************************************************************** + * + * BRIEF MODULE DESCRIPTION + * Definitions for IDT RC32434 on-chip ethernet controller. + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb + * + * Initial Release + * + * Aug 2004 + * + * Added NAPI + * + ************************************************************************** + */ + + +#include +#include +#include + +#define CONFIG_IDT_USE_NAPI 1 +#define RC32434_DEBUG 2 +//#define RC32434_PROC_DEBUG +#undef RC32434_DEBUG + +#ifdef RC32434_DEBUG + +/* use 0 for production, 1 for verification, >2 for debug */ +static int rc32434_debug = RC32434_DEBUG; +#define ASSERT(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); } +#define DBG(lvl, format, arg...) if (rc32434_debug > lvl) printk(KERN_INFO "%s: " format, dev->name , ## arg) +#else +#define ASSERT(expr) do {} while (0) +#define DBG(lvl, format, arg...) do {} while (0) +#endif + +#define INFO(format, arg...) printk(KERN_INFO "%s: " format, dev->name , ## arg) +#define ERR(format, arg...) printk(KERN_ERR "%s: " format, dev->name , ## arg) +#define WARN(format, arg...) printk(KERN_WARNING "%s: " format, dev->name , ## arg) + +/* the following must be powers of two */ +#ifdef CONFIG_IDT_USE_NAPI +#define RC32434_NUM_RDS 64 /* number of receive descriptors */ +#define RC32434_NUM_TDS 64 /* number of transmit descriptors */ +#else +#define RC32434_NUM_RDS 128 /* number of receive descriptors */ +#define RC32434_NUM_TDS 128 /* number of transmit descriptors */ +#endif + +#define RC32434_RBSIZE 1536 /* size of one resource buffer = Ether MTU */ +#define RC32434_RDS_MASK (RC32434_NUM_RDS-1) +#define RC32434_TDS_MASK (RC32434_NUM_TDS-1) +#define RD_RING_SIZE (RC32434_NUM_RDS * sizeof(struct DMAD_s)) +#define TD_RING_SIZE (RC32434_NUM_TDS * sizeof(struct DMAD_s)) + +#define RC32434_TX_TIMEOUT HZ * 100 + +#define rc32434_eth0_regs ((ETH_t)(ETH0_VirtualAddress)) +#define rc32434_eth1_regs ((ETH_t)(ETH1_VirtualAddress)) + +enum status { filled, empty}; +#define IS_DMA_FINISHED(X) (((X) & (DMAD_f_m)) != 0) +#define IS_DMA_DONE(X) (((X) & (DMAD_d_m)) != 0) + + +/* Information that need to be kept for each board. */ +struct rc32434_local { + ETH_t eth_regs; + DMA_Chan_t rx_dma_regs; + DMA_Chan_t tx_dma_regs; + volatile DMAD_t td_ring; /* transmit descriptor ring */ + volatile DMAD_t rd_ring; /* receive descriptor ring */ + + struct sk_buff* tx_skb[RC32434_NUM_TDS]; /* skbuffs for pkt to trans */ + struct sk_buff* rx_skb[RC32434_NUM_RDS]; /* skbuffs for pkt to trans */ + +#ifndef CONFIG_IDT_USE_NAPI + struct tasklet_struct * rx_tasklet; +#endif + struct tasklet_struct * tx_tasklet; + + int rx_next_done; + int rx_chain_head; + int rx_chain_tail; + enum status rx_chain_status; + + int tx_next_done; + int tx_chain_head; + int tx_chain_tail; + enum status tx_chain_status; + int tx_count; + int tx_full; + + struct timer_list mii_phy_timer; + unsigned long duplex_mode; + + int rx_irq; + int tx_irq; + int ovr_irq; + int und_irq; + + struct net_device_stats stats; + spinlock_t lock; + + /* debug /proc entry */ + struct proc_dir_entry *ps; + int dma_halt_cnt; int dma_run_cnt; +}; + +extern unsigned int idt_cpu_freq; + +/* Index to functions, as function prototypes. */ +static int rc32434_open(struct net_device *dev); +static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev); +static void rc32434_mii_handler(unsigned long data); +static irqreturn_t rc32434_und_interrupt(int irq, void *dev_id); +static irqreturn_t rc32434_rx_dma_interrupt(int irq, void *dev_id); +static irqreturn_t rc32434_tx_dma_interrupt(int irq, void *dev_id); +#ifdef RC32434_REVISION +static irqreturn_t rc32434_ovr_interrupt(int irq, void *dev_id); +#endif +static int rc32434_close(struct net_device *dev); +static struct net_device_stats *rc32434_get_stats(struct net_device *dev); +static void rc32434_multicast_list(struct net_device *dev); +static int rc32434_init(struct net_device *dev); +static void rc32434_tx_timeout(struct net_device *dev); + +static void rc32434_tx_tasklet(unsigned long tx_data_dev); +#ifdef CONFIG_IDT_USE_NAPI +static int rc32434_poll(struct net_device *rx_data_dev, int *budget); +#else +static void rc32434_rx_tasklet(unsigned long rx_data_dev); +#endif +static void rc32434_cleanup_module(void); + + +static inline void rc32434_abort_dma(struct net_device *dev, DMA_Chan_t ch) +{ + if (__raw_readl(&ch->dmac) & DMAC_run_m) { + __raw_writel(0x10, &ch->dmac); + + while (!(__raw_readl(&ch->dmas) & DMAS_h_m)) + dev->trans_start = jiffies; + + __raw_writel(0, &ch->dmas); + } + + __raw_writel(0, &ch->dmadptr); + __raw_writel(0, &ch->dmandptr); +} diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/ddr.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/ddr.h new file mode 100644 index 0000000000..03923a6588 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/ddr.h @@ -0,0 +1,173 @@ +#ifndef __IDT_DDR_H__ +#define __IDT_DDR_H__ + +/******************************************************************************* + * + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * DDR register definition. + * + * File : $Id: ddr.h,v 1.2 2002/06/06 18:34:03 astichte Exp $ + * + * Author : ryan.holmQVist@idt.com + * Date : 20011005 + * Update : + * $Log: ddr.h,v $ + * Revision 1.2 2002/06/06 18:34:03 astichte + * Added XXX_PhysicalAddress and XXX_VirtualAddress + * + * Revision 1.1 2002/05/29 17:33:21 sysarch + * jba File moved from vcode/include/idt/acacia + * + * + ******************************************************************************/ + +enum +{ + DDR0_PhysicalAddress = 0x18018000, + DDR_PhysicalAddress = DDR0_PhysicalAddress, // Default + + DDR0_VirtualAddress = 0xb8018000, + DDR_VirtualAddress = DDR0_VirtualAddress, // Default +} ; + +typedef struct DDR_s +{ + u32 ddrbase ; + u32 ddrmask ; + u32 res1; + u32 res2; + u32 ddrc ; + u32 ddrabase ; + u32 ddramask ; + u32 ddramap ; + u32 ddrcust; + u32 ddrrdc; + u32 ddrspare; +} volatile *DDR_t ; + +enum +{ + DDR0BASE_baseaddr_b = 16, + DDR0BASE_baseaddr_m = 0xffff0000, + + DDR0MASK_mask_b = 16, + DDR0MASK_mask_m = 0xffff0000, + + DDR1BASE_baseaddr_b = 16, + DDR1BASE_baseaddr_m = 0xffff0000, + + DDR1MASK_mask_b = 16, + DDR1MASK_mask_m = 0xffff0000, + + DDRC_ata_b = 5, + DDRC_ata_m = 0x000000E0, + DDRC_dbw_b = 8, + DDRC_dbw_m = 0x00000100, + DDRC_wr_b = 9, + DDRC_wr_m = 0x00000600, + DDRC_ps_b = 11, + DDRC_ps_m = 0x00001800, + DDRC_dtype_b = 13, + DDRC_dtype_m = 0x0000e000, + DDRC_rfc_b = 16, + DDRC_rfc_m = 0x000f0000, + DDRC_rp_b = 20, + DDRC_rp_m = 0x00300000, + DDRC_ap_b = 22, + DDRC_ap_m = 0x00400000, + DDRC_rcd_b = 23, + DDRC_rcd_m = 0x01800000, + DDRC_cl_b = 25, + DDRC_cl_m = 0x06000000, + DDRC_dbm_b = 27, + DDRC_dbm_m = 0x08000000, + DDRC_sds_b = 28, + DDRC_sds_m = 0x10000000, + DDRC_atp_b = 29, + DDRC_atp_m = 0x60000000, + DDRC_re_b = 31, + DDRC_re_m = 0x80000000, + + DDRRDC_ces_b = 0, + DDRRDC_ces_m = 0x00000001, + DDRRDC_ace_b = 1, + DDRRDC_ace_m = 0x00000002, + + DDRABASE_baseaddr_b = 16, + DDRABASE_baseaddr_m = 0xffff0000, + + DDRAMASK_mask_b = 16, + DDRAMASK_mask_m = 0xffff0000, + + DDRAMAP_map_b = 16, + DDRAMAP_map_m = 0xffff0000, + + DDRCUST_cs_b = 0, + DDRCUST_cs_m = 0x00000003, + DDRCUST_we_b = 2, + DDRCUST_we_m = 0x00000004, + DDRCUST_ras_b = 3, + DDRCUST_ras_m = 0x00000008, + DDRCUST_cas_b = 4, + DDRCUST_cas_m = 0x00000010, + DDRCUST_cke_b = 5, + DDRCUST_cke_m = 0x00000020, + DDRCUST_ba_b = 6, + DDRCUST_ba_m = 0x000000c0, + + RCOUNT_rcount_b = 0, + RCOUNT_rcount_m = 0x0000ffff, + + RCOMPARE_rcompare_b = 0, + RCOMPARE_rcompare_m = 0x0000ffff, + + RTC_ce_b = 0, + RTC_ce_m = 0x00000001, + RTC_to_b = 1, + RTC_to_m = 0x00000002, + RTC_rqe_b = 2, + RTC_rqe_m = 0x00000004, + + DDRDQSC_dm_b = 0, + DDRDQSC_dm_m = 0x00000003, + DDRDQSC_dqsbs_b = 2, + DDRDQSC_dqsbs_m = 0x000000fc, + DDRDQSC_db_b = 8, + DDRDQSC_db_m = 0x00000100, + DDRDQSC_dbsp_b = 9, + DDRDQSC_dbsp_m = 0x01fffe00, + DDRDQSC_bdp_b = 25, + DDRDQSC_bdp_m = 0x7e000000, + + DDRDLLC_eao_b = 0, + DDRDLLC_eao_m = 0x00000001, + DDRDLLC_eo_b = 1, + DDRDLLC_eo_m = 0x0000003e, + DDRDLLC_fs_b = 6, + DDRDLLC_fs_m = 0x000000c0, + DDRDLLC_as_b = 8, + DDRDLLC_as_m = 0x00000700, + DDRDLLC_sp_b = 11, + DDRDLLC_sp_m = 0x001ff800, + + DDRDLLFC_men_b = 0, + DDRDLLFC_men_m = 0x00000001, + DDRDLLFC_aen_b = 1, + DDRDLLFC_aen_m = 0x00000002, + DDRDLLFC_ff_b = 2, + DDRDLLFC_ff_m = 0x00000004, + + DDRDLLTA_addr_b = 2, + DDRDLLTA_addr_m = 0xfffffffc, + + DDRDLLED_dbe_b = 0, + DDRDLLED_dbe_m = 0x00000001, + DDRDLLED_dte_b = 1, + DDRDLLED_dte_m = 0x00000002, + + +} ; + +#endif // __IDT_DDR_H__ diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/dma.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/dma.h new file mode 100644 index 0000000000..2124f7e510 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/dma.h @@ -0,0 +1,168 @@ +#ifndef __IDT_DMA_H__ +#define __IDT_DMA_H__ + +/******************************************************************************* + * + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * DMA register definition. + * + * File : $Id: dma.h,v 1.3 2002/06/06 18:34:03 astichte Exp $ + * + * Author : ryan.holmQVist@idt.com + * Date : 20011005 + * Update : + * $Log: dma.h,v $ + * Revision 1.3 2002/06/06 18:34:03 astichte + * Added XXX_PhysicalAddress and XXX_VirtualAddress + * + * Revision 1.2 2002/06/05 18:30:46 astichte + * Removed IDTField + * + * Revision 1.1 2002/05/29 17:33:21 sysarch + * jba File moved from vcode/include/idt/acacia + * + * + ******************************************************************************/ + +enum +{ + DMA0_PhysicalAddress = 0x18040000, + DMA_PhysicalAddress = DMA0_PhysicalAddress, // Default + + DMA0_VirtualAddress = 0xb8040000, + DMA_VirtualAddress = DMA0_VirtualAddress, // Default +} ; + +/* + * DMA descriptor (in physical memory). + */ + +typedef struct DMAD_s +{ + u32 control ; // Control. use DMAD_* + u32 ca ; // Current Address. + u32 devcs ; // Device control and status. + u32 link ; // Next descriptor in chain. +} volatile *DMAD_t ; + +enum +{ + DMAD_size = sizeof (struct DMAD_s), + DMAD_count_b = 0, // in DMAD_t -> control + DMAD_count_m = 0x0003ffff, // in DMAD_t -> control + DMAD_ds_b = 20, // in DMAD_t -> control + DMAD_ds_m = 0x00300000, // in DMAD_t -> control + DMAD_ds_ethRcv_v = 0, + DMAD_ds_ethXmt_v = 0, + DMAD_ds_memToFifo_v = 0, + DMAD_ds_fifoToMem_v = 0, + DMAD_ds_pciToMem_v = 0, + DMAD_ds_memToPci_v = 0, + + DMAD_devcmd_b = 22, // in DMAD_t -> control + DMAD_devcmd_m = 0x01c00000, // in DMAD_t -> control + DMAD_devcmd_byte_v = 0, //memory-to-memory + DMAD_devcmd_halfword_v = 1, //memory-to-memory + DMAD_devcmd_word_v = 2, //memory-to-memory + DMAD_devcmd_2words_v = 3, //memory-to-memory + DMAD_devcmd_4words_v = 4, //memory-to-memory + DMAD_devcmd_6words_v = 5, //memory-to-memory + DMAD_devcmd_8words_v = 6, //memory-to-memory + DMAD_devcmd_16words_v = 7, //memory-to-memory + DMAD_cof_b = 25, // chain on finished + DMAD_cof_m = 0x02000000, // + DMAD_cod_b = 26, // chain on done + DMAD_cod_m = 0x04000000, // + DMAD_iof_b = 27, // interrupt on finished + DMAD_iof_m = 0x08000000, // + DMAD_iod_b = 28, // interrupt on done + DMAD_iod_m = 0x10000000, // + DMAD_t_b = 29, // terminated + DMAD_t_m = 0x20000000, // + DMAD_d_b = 30, // done + DMAD_d_m = 0x40000000, // + DMAD_f_b = 31, // finished + DMAD_f_m = 0x80000000, // +} ; + +/* + * DMA register (within Internal Register Map). + */ + +struct DMA_Chan_s +{ + u32 dmac ; // Control. + u32 dmas ; // Status. + u32 dmasm ; // Mask. + u32 dmadptr ; // Descriptor pointer. + u32 dmandptr ; // Next descriptor pointer. +}; + +typedef struct DMA_Chan_s volatile *DMA_Chan_t ; + +//DMA_Channels use DMACH_count instead + +enum +{ + DMAC_run_b = 0, // + DMAC_run_m = 0x00000001, // + DMAC_dm_b = 1, // done mask + DMAC_dm_m = 0x00000002, // + DMAC_mode_b = 2, // + DMAC_mode_m = 0x0000000c, // + DMAC_mode_auto_v = 0, + DMAC_mode_burst_v = 1, + DMAC_mode_transfer_v = 2, //usually used + DMAC_mode_reserved_v = 3, + DMAC_a_b = 4, // + DMAC_a_m = 0x00000010, // + + DMAS_f_b = 0, // finished (sticky) + DMAS_f_m = 0x00000001, // + DMAS_d_b = 1, // done (sticky) + DMAS_d_m = 0x00000002, // + DMAS_c_b = 2, // chain (sticky) + DMAS_c_m = 0x00000004, // + DMAS_e_b = 3, // error (sticky) + DMAS_e_m = 0x00000008, // + DMAS_h_b = 4, // halt (sticky) + DMAS_h_m = 0x00000010, // + + DMASM_f_b = 0, // finished (1=mask) + DMASM_f_m = 0x00000001, // + DMASM_d_b = 1, // done (1=mask) + DMASM_d_m = 0x00000002, // + DMASM_c_b = 2, // chain (1=mask) + DMASM_c_m = 0x00000004, // + DMASM_e_b = 3, // error (1=mask) + DMASM_e_m = 0x00000008, // + DMASM_h_b = 4, // halt (1=mask) + DMASM_h_m = 0x00000010, // +} ; + +/* + * DMA channel definitions + */ + +enum +{ + DMACH_ethRcv = 0, + DMACH_ethXmt = 1, + DMACH_memToFifo = 2, + DMACH_fifoToMem = 3, + DMACH_pciToMem = 4, + DMACH_memToPci = 5, + + DMACH_count //must be last +}; + + +typedef struct DMAC_s +{ + struct DMA_Chan_s ch [DMACH_count] ; //use ch[DMACH_] +} volatile *DMA_t ; + +#endif // __IDT_DMA_H__ + diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/dma_v.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/dma_v.h new file mode 100644 index 0000000000..81cb40505d --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/dma_v.h @@ -0,0 +1,72 @@ +#ifndef __IDT_DMA_V_H__ +#define __IDT_DMA_V_H__ + +/******************************************************************************* + * + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * DMA register definition. + * + * File : $Id: dma.h,v 1.3 2002/06/06 18:34:03 astichte Exp $ + * + * Author : ryan.holmQVist@idt.com + * Date : 20011005 + * Update : + * $Log: dma.h,v $ + * Revision 1.3 2002/06/06 18:34:03 astichte + * Added XXX_PhysicalAddress and XXX_VirtualAddress + * + * Revision 1.2 2002/06/05 18:30:46 astichte + * Removed IDTField + * + * Revision 1.1 2002/05/29 17:33:21 sysarch + * jba File moved from vcode/include/idt/acacia + * + * + ******************************************************************************/ +#include +#include +#define DMA_CHAN_OFFSET 0x14 +#define IS_DMA_USED(X) (((X) & (DMAD_f_m | DMAD_d_m | DMAD_t_m)) != 0) +#define DMA_COUNT(count) \ + ((count) & DMAD_count_m) + +#define DMA_HALT_TIMEOUT 500 + + +static inline int rc32434_halt_dma(DMA_Chan_t ch) +{ + int timeout=1; + if (local_readl(&ch->dmac) & DMAC_run_m) { + local_writel(0, &ch->dmac); + for (timeout = DMA_HALT_TIMEOUT; timeout > 0; timeout--) { + if (local_readl(&ch->dmas) & DMAS_h_m) { + local_writel(0, &ch->dmas); + break; + } + } + } + + return timeout ? 0 : 1; +} + +static inline void rc32434_start_dma(DMA_Chan_t ch, u32 dma_addr) +{ + local_writel(0, &ch->dmandptr); + local_writel(dma_addr, &ch->dmadptr); +} + +static inline void rc32434_chain_dma(DMA_Chan_t ch, u32 dma_addr) +{ + local_writel(dma_addr, &ch->dmandptr); +} + +#endif // __IDT_DMA_V_H__ + + + + + + + diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/eth.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/eth.h new file mode 100644 index 0000000000..2f42976508 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/eth.h @@ -0,0 +1,320 @@ +#ifndef __IDT_ETH_H__ +#define __IDT_ETH_H__ + +/******************************************************************************* + * + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * Ethernet register definition. + * + * File : $Id: eth.h,v 1.3 2002/06/06 18:34:04 astichte Exp $ + * + * Author : Allen.Stichter@idt.com + * Date : 20020605 + * Update : + * $Log: eth.h,v $ + * Revision 1.3 2002/06/06 18:34:04 astichte + * Added XXX_PhysicalAddress and XXX_VirtualAddress + * + * Revision 1.2 2002/06/05 18:19:46 astichte + * Added + * + * Revision 1.1 2002/05/29 17:33:22 sysarch + * jba File moved from vcode/include/idt/acacia + * + ******************************************************************************/ + +enum +{ + ETH0_PhysicalAddress = 0x18060000, + ETH_PhysicalAddress = ETH0_PhysicalAddress, // Default + + ETH0_VirtualAddress = 0xb8060000, + ETH_VirtualAddress = ETH0_VirtualAddress, // Default +} ; + +typedef struct +{ + u32 ethintfc ; + u32 ethfifott ; + u32 etharc ; + u32 ethhash0 ; + u32 ethhash1 ; + u32 ethu0 [4] ; // Reserved. + u32 ethpfs ; + u32 ethmcp ; + u32 eth_u1 [10] ; // Reserved. + u32 ethspare ; + u32 eth_u2 [42] ; // Reserved. + u32 ethsal0 ; + u32 ethsah0 ; + u32 ethsal1 ; + u32 ethsah1 ; + u32 ethsal2 ; + u32 ethsah2 ; + u32 ethsal3 ; + u32 ethsah3 ; + u32 ethrbc ; + u32 ethrpc ; + u32 ethrupc ; + u32 ethrfc ; + u32 ethtbc ; + u32 ethgpf ; + u32 eth_u9 [50] ; // Reserved. + u32 ethmac1 ; + u32 ethmac2 ; + u32 ethipgt ; + u32 ethipgr ; + u32 ethclrt ; + u32 ethmaxf ; + u32 eth_u10 ; // Reserved. + u32 ethmtest ; + u32 miimcfg ; + u32 miimcmd ; + u32 miimaddr ; + u32 miimwtd ; + u32 miimrdd ; + u32 miimind ; + u32 eth_u11 ; // Reserved. + u32 eth_u12 ; // Reserved. + u32 ethcfsa0 ; + u32 ethcfsa1 ; + u32 ethcfsa2 ; +} volatile *ETH_t; + +enum +{ + ETHINTFC_en_b = 0, + ETHINTFC_en_m = 0x00000001, + ETHINTFC_its_b = 1, + ETHINTFC_its_m = 0x00000002, + ETHINTFC_rip_b = 2, + ETHINTFC_rip_m = 0x00000004, + ETHINTFC_jam_b = 3, + ETHINTFC_jam_m = 0x00000008, + ETHINTFC_ovr_b = 4, + ETHINTFC_ovr_m = 0x00000010, + ETHINTFC_und_b = 5, + ETHINTFC_und_m = 0x00000020, + ETHINTFC_iom_b = 6, + ETHINTFC_iom_m = 0x000000c0, + + ETHFIFOTT_tth_b = 0, + ETHFIFOTT_tth_m = 0x0000007f, + + ETHARC_pro_b = 0, + ETHARC_pro_m = 0x00000001, + ETHARC_am_b = 1, + ETHARC_am_m = 0x00000002, + ETHARC_afm_b = 2, + ETHARC_afm_m = 0x00000004, + ETHARC_ab_b = 3, + ETHARC_ab_m = 0x00000008, + + ETHSAL_byte5_b = 0, + ETHSAL_byte5_m = 0x000000ff, + ETHSAL_byte4_b = 8, + ETHSAL_byte4_m = 0x0000ff00, + ETHSAL_byte3_b = 16, + ETHSAL_byte3_m = 0x00ff0000, + ETHSAL_byte2_b = 24, + ETHSAL_byte2_m = 0xff000000, + + ETHSAH_byte1_b = 0, + ETHSAH_byte1_m = 0x000000ff, + ETHSAH_byte0_b = 8, + ETHSAH_byte0_m = 0x0000ff00, + + ETHGPF_ptv_b = 0, + ETHGPF_ptv_m = 0x0000ffff, + + ETHPFS_pfd_b = 0, + ETHPFS_pfd_m = 0x00000001, + + ETHCFSA0_cfsa4_b = 0, + ETHCFSA0_cfsa4_m = 0x000000ff, + ETHCFSA0_cfsa5_b = 8, + ETHCFSA0_cfsa5_m = 0x0000ff00, + + ETHCFSA1_cfsa2_b = 0, + ETHCFSA1_cfsa2_m = 0x000000ff, + ETHCFSA1_cfsa3_b = 8, + ETHCFSA1_cfsa3_m = 0x0000ff00, + + ETHCFSA2_cfsa0_b = 0, + ETHCFSA2_cfsa0_m = 0x000000ff, + ETHCFSA2_cfsa1_b = 8, + ETHCFSA2_cfsa1_m = 0x0000ff00, + + ETHMAC1_re_b = 0, + ETHMAC1_re_m = 0x00000001, + ETHMAC1_paf_b = 1, + ETHMAC1_paf_m = 0x00000002, + ETHMAC1_rfc_b = 2, + ETHMAC1_rfc_m = 0x00000004, + ETHMAC1_tfc_b = 3, + ETHMAC1_tfc_m = 0x00000008, + ETHMAC1_lb_b = 4, + ETHMAC1_lb_m = 0x00000010, + ETHMAC1_mr_b = 31, + ETHMAC1_mr_m = 0x80000000, + + ETHMAC2_fd_b = 0, + ETHMAC2_fd_m = 0x00000001, + ETHMAC2_flc_b = 1, + ETHMAC2_flc_m = 0x00000002, + ETHMAC2_hfe_b = 2, + ETHMAC2_hfe_m = 0x00000004, + ETHMAC2_dc_b = 3, + ETHMAC2_dc_m = 0x00000008, + ETHMAC2_cen_b = 4, + ETHMAC2_cen_m = 0x00000010, + ETHMAC2_pe_b = 5, + ETHMAC2_pe_m = 0x00000020, + ETHMAC2_vpe_b = 6, + ETHMAC2_vpe_m = 0x00000040, + ETHMAC2_ape_b = 7, + ETHMAC2_ape_m = 0x00000080, + ETHMAC2_ppe_b = 8, + ETHMAC2_ppe_m = 0x00000100, + ETHMAC2_lpe_b = 9, + ETHMAC2_lpe_m = 0x00000200, + ETHMAC2_nb_b = 12, + ETHMAC2_nb_m = 0x00001000, + ETHMAC2_bp_b = 13, + ETHMAC2_bp_m = 0x00002000, + ETHMAC2_ed_b = 14, + ETHMAC2_ed_m = 0x00004000, + + ETHIPGT_ipgt_b = 0, + ETHIPGT_ipgt_m = 0x0000007f, + + ETHIPGR_ipgr2_b = 0, + ETHIPGR_ipgr2_m = 0x0000007f, + ETHIPGR_ipgr1_b = 8, + ETHIPGR_ipgr1_m = 0x00007f00, + + ETHCLRT_maxret_b = 0, + ETHCLRT_maxret_m = 0x0000000f, + ETHCLRT_colwin_b = 8, + ETHCLRT_colwin_m = 0x00003f00, + + ETHMAXF_maxf_b = 0, + ETHMAXF_maxf_m = 0x0000ffff, + + ETHMTEST_tb_b = 2, + ETHMTEST_tb_m = 0x00000004, + + ETHMCP_div_b = 0, + ETHMCP_div_m = 0x000000ff, + + MIIMCFG_rsv_b = 0, + MIIMCFG_rsv_m = 0x0000000c, + + MIIMCMD_rd_b = 0, + MIIMCMD_rd_m = 0x00000001, + MIIMCMD_scn_b = 1, + MIIMCMD_scn_m = 0x00000002, + + MIIMADDR_regaddr_b = 0, + MIIMADDR_regaddr_m = 0x0000001f, + MIIMADDR_phyaddr_b = 8, + MIIMADDR_phyaddr_m = 0x00001f00, + + MIIMWTD_wdata_b = 0, + MIIMWTD_wdata_m = 0x0000ffff, + + MIIMRDD_rdata_b = 0, + MIIMRDD_rdata_m = 0x0000ffff, + + MIIMIND_bsy_b = 0, + MIIMIND_bsy_m = 0x00000001, + MIIMIND_scn_b = 1, + MIIMIND_scn_m = 0x00000002, + MIIMIND_nv_b = 2, + MIIMIND_nv_m = 0x00000004, + +} ; + +/* + * Values for the DEVCS field of the Ethernet DMA Rx and Tx descriptors. + */ +enum +{ + ETHRX_fd_b = 0, + ETHRX_fd_m = 0x00000001, + ETHRX_ld_b = 1, + ETHRX_ld_m = 0x00000002, + ETHRX_rok_b = 2, + ETHRX_rok_m = 0x00000004, + ETHRX_fm_b = 3, + ETHRX_fm_m = 0x00000008, + ETHRX_mp_b = 4, + ETHRX_mp_m = 0x00000010, + ETHRX_bp_b = 5, + ETHRX_bp_m = 0x00000020, + ETHRX_vlt_b = 6, + ETHRX_vlt_m = 0x00000040, + ETHRX_cf_b = 7, + ETHRX_cf_m = 0x00000080, + ETHRX_ovr_b = 8, + ETHRX_ovr_m = 0x00000100, + ETHRX_crc_b = 9, + ETHRX_crc_m = 0x00000200, + ETHRX_cv_b = 10, + ETHRX_cv_m = 0x00000400, + ETHRX_db_b = 11, + ETHRX_db_m = 0x00000800, + ETHRX_le_b = 12, + ETHRX_le_m = 0x00001000, + ETHRX_lor_b = 13, + ETHRX_lor_m = 0x00002000, + ETHRX_ces_b = 14, + ETHRX_ces_m = 0x00004000, + ETHRX_length_b = 16, + ETHRX_length_m = 0xffff0000, + + ETHTX_fd_b = 0, + ETHTX_fd_m = 0x00000001, + ETHTX_ld_b = 1, + ETHTX_ld_m = 0x00000002, + ETHTX_oen_b = 2, + ETHTX_oen_m = 0x00000004, + ETHTX_pen_b = 3, + ETHTX_pen_m = 0x00000008, + ETHTX_cen_b = 4, + ETHTX_cen_m = 0x00000010, + ETHTX_hen_b = 5, + ETHTX_hen_m = 0x00000020, + ETHTX_tok_b = 6, + ETHTX_tok_m = 0x00000040, + ETHTX_mp_b = 7, + ETHTX_mp_m = 0x00000080, + ETHTX_bp_b = 8, + ETHTX_bp_m = 0x00000100, + ETHTX_und_b = 9, + ETHTX_und_m = 0x00000200, + ETHTX_of_b = 10, + ETHTX_of_m = 0x00000400, + ETHTX_ed_b = 11, + ETHTX_ed_m = 0x00000800, + ETHTX_ec_b = 12, + ETHTX_ec_m = 0x00001000, + ETHTX_lc_b = 13, + ETHTX_lc_m = 0x00002000, + ETHTX_td_b = 14, + ETHTX_td_m = 0x00004000, + ETHTX_crc_b = 15, + ETHTX_crc_m = 0x00008000, + ETHTX_le_b = 16, + ETHTX_le_m = 0x00010000, + ETHTX_cc_b = 17, + ETHTX_cc_m = 0x001E0000, +} ; + +#endif // __IDT_ETH_H__ + + + + diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/eth_v.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/eth_v.h new file mode 100644 index 0000000000..3ea03c6725 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/eth_v.h @@ -0,0 +1,63 @@ +#ifndef __IDT_ETH_V_H__ +#define __IDT_ETH_V_H__ + +/******************************************************************************* + * + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * Ethernet register definition. + * + * File : $Id: eth.h,v 1.3 2002/06/06 18:34:04 astichte Exp $ + * + * Author : Allen.Stichter@idt.com + * Date : 20020605 + * Update : + * $Log: eth.h,v $ + * Revision 1.3 2002/06/06 18:34:04 astichte + * Added XXX_PhysicalAddress and XXX_VirtualAddress + * + * Revision 1.2 2002/06/05 18:19:46 astichte + * Added + * + * Revision 1.1 2002/05/29 17:33:22 sysarch + * jba File moved from vcode/include/idt/acacia + * + ******************************************************************************/ + +#include + +#define IS_TX_TOK(X) (((X) & (1<> ETHTX_tok_b ) /* Transmit Okay */ +#define IS_TX_MP(X) (((X) & (1<> ETHTX_mp_b ) /* Multicast */ +#define IS_TX_BP(X) (((X) & (1<> ETHTX_bp_b ) /* Broadcast */ +#define IS_TX_UND_ERR(X) (((X) & (1<> ETHTX_und_b ) /* Transmit FIFO Underflow */ +#define IS_TX_OF_ERR(X) (((X) & (1<> ETHTX_of_b ) /* Oversized frame */ +#define IS_TX_ED_ERR(X) (((X) & (1<> ETHTX_ed_b ) /* Excessive deferral */ +#define IS_TX_EC_ERR(X) (((X) & (1<> ETHTX_ec_b) /* Excessive collisions */ +#define IS_TX_LC_ERR(X) (((X) & (1<> ETHTX_lc_b ) /* Late Collision */ +#define IS_TX_TD_ERR(X) (((X) & (1<> ETHTX_td_b ) /* Transmit deferred*/ +#define IS_TX_CRC_ERR(X) (((X) & (1<> ETHTX_crc_b ) /* CRC Error */ +#define IS_TX_LE_ERR(X) (((X) & (1<> ETHTX_le_b ) /* Length Error */ + +#define TX_COLLISION_COUNT(X) (((X) & ETHTX_cc_m)>>ETHTX_cc_b) /* Collision Count */ + +#define IS_RCV_ROK(X) (((X) & (1<> ETHRX_rok_b) /* Receive Okay */ +#define IS_RCV_FM(X) (((X) & (1<> ETHRX_fm_b) /* Is Filter Match */ +#define IS_RCV_MP(X) (((X) & (1<> ETHRX_mp_b) /* Is it MP */ +#define IS_RCV_BP(X) (((X) & (1<> ETHRX_bp_b) /* Is it BP */ +#define IS_RCV_VLT(X) (((X) & (1<> ETHRX_vlt_b) /* VLAN Tag Detect */ +#define IS_RCV_CF(X) (((X) & (1<> ETHRX_cf_b) /* Control Frame */ +#define IS_RCV_OVR_ERR(X) (((X) & (1<> ETHRX_ovr_b) /* Receive Overflow */ +#define IS_RCV_CRC_ERR(X) (((X) & (1<> ETHRX_crc_b) /* CRC Error */ +#define IS_RCV_CV_ERR(X) (((X) & (1<> ETHRX_cv_b) /* Code Violation */ +#define IS_RCV_DB_ERR(X) (((X) & (1<> ETHRX_db_b) /* Dribble Bits */ +#define IS_RCV_LE_ERR(X) (((X) & (1<> ETHRX_le_b) /* Length error */ +#define IS_RCV_LOR_ERR(X) (((X) & (1<> ETHRX_lor_b) /* Length Out of Range */ +#define IS_RCV_CES_ERR(X) (((X) & (1<> ETHRX_ces_b) /* Preamble error */ +#define RCVPKT_LENGTH(X) (((X) & ETHRX_length_m) >> ETHRX_length_b) /* Length of the received packet */ +#endif // __IDT_ETH_V_H__ + + + + + diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/gpio.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/gpio.h new file mode 100644 index 0000000000..859b9c9219 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/gpio.h @@ -0,0 +1,122 @@ +/* + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * GPIO register definition. + * + * Author : ryan.holmQVist@idt.com + * Date : 20011005 + * Copyright (C) 2001, 2002 Ryan Holm + * Copyright (C) 2008 Florian Fainelli + */ + +#ifndef _RC32434_GPIO_H_ +#define _RC32434_GPIO_H_ + +#include + +struct rb500_gpio_reg { + u32 gpiofunc; /* GPIO Function Register + * gpiofunc[x]==0 bit = gpio + * func[x]==1 bit = altfunc + */ + u32 gpiocfg; /* GPIO Configuration Register + * gpiocfg[x]==0 bit = input + * gpiocfg[x]==1 bit = output + */ + u32 gpiod; /* GPIO Data Register + * gpiod[x] read/write gpio pinX status + */ + u32 gpioilevel; /* GPIO Interrupt Status Register + * interrupt level (see gpioistat) + */ + u32 gpioistat; /* Gpio Interrupt Status Register + * istat[x] = (gpiod[x] == level[x]) + * cleared in ISR (STICKY bits) + */ + u32 gpionmien; /* GPIO Non-maskable Interrupt Enable Register */ +}; + +/* UART GPIO signals */ +#define RC32434_UART0_SOUT (1 << 0) +#define RC32434_UART0_SIN (1 << 1) +#define RC32434_UART0_RTS (1 << 2) +#define RC32434_UART0_CTS (1 << 3) + +/* M & P bus GPIO signals */ +#define RC32434_MP_BIT_22 (1 << 4) +#define RC32434_MP_BIT_23 (1 << 5) +#define RC32434_MP_BIT_24 (1 << 6) +#define RC32434_MP_BIT_25 (1 << 7) + +/* CPU GPIO signals */ +#define RC32434_CPU_GPIO (1 << 8) + +/* Reserved GPIO signals */ +#define RC32434_AF_SPARE_6 (1 << 9) +#define RC32434_AF_SPARE_4 (1 << 10) +#define RC32434_AF_SPARE_3 (1 << 11) +#define RC32434_AF_SPARE_2 (1 << 12) + +/* PCI messaging unit */ +#define RC32434_PCI_MSU_GPIO (1 << 13) + +extern int rb500_gpio_get_value(unsigned gpio); +extern void rb500_gpio_set_value(unsigned gpio, int value); +extern int rb500_gpio_direction_input(unsigned gpio); +extern int rb500_gpio_direction_output(unsigned gpio, int value); +extern void rb500_gpio_set_int_level(unsigned gpio, int value); +extern int rb500_gpio_get_int_level(unsigned gpio); +extern void rb500_gpio_set_int_status(unsigned gpio, int value); +extern int rb500_gpio_get_int_status(unsigned gpio); +extern void rb500_gpio_set_func(unsigned gpio, int value); +extern int rb500_gpio_get_func(unsigned gpio); + + +/* Wrappers for the arch-neutral GPIO API */ + +static inline int gpio_request(unsigned gpio, const char *label) +{ + /* Not yet implemented */ + return 0; +} + +static inline void gpio_free(unsigned gpio) +{ + /* Not yet implemented */ +} + +static inline int gpio_direction_input(unsigned gpio) +{ + return rb500_gpio_direction_input(gpio); +} + +static inline int gpio_direction_output(unsigned gpio, int value) +{ + return rb500_gpio_direction_output(gpio, value); +} + +static inline int gpio_get_value(unsigned gpio) +{ + return rb500_gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned gpio, int value) +{ + rb500_gpio_set_value(gpio, value); +} + +static inline int gpio_to_irq(unsigned gpio) +{ + return gpio; +} + +static inline int irq_to_gpio(unsigned irq) +{ + return irq; +} + +/* For cansleep */ +#include + +#endif /* _RC32434_GPIO_H_ */ diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/integ.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/integ.h new file mode 100644 index 0000000000..a9e99e4be7 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/integ.h @@ -0,0 +1,76 @@ +#ifndef __IDT_INTEG_H__ +#define __IDT_INTEG_H__ + +/******************************************************************************* + * + * Copyright 2002 Integrated Device Technology, Inc. + * All rights reserved. + * + * System Integrity register definition. + * + * File : $Id: integ.h,v 1.3 2002/06/06 18:34:04 astichte Exp $ + * + * Author : ryan.holmQVist@idt.com + * Date : 20011005 + * Update : + * $Log: integ.h,v $ + * Revision 1.3 2002/06/06 18:34:04 astichte + * Added XXX_PhysicalAddress and XXX_VirtualAddress + * + * Revision 1.2 2002/06/05 18:32:33 astichte + * Removed IDTField + * + * Revision 1.1 2002/05/29 17:33:22 sysarch + * jba File moved from vcode/include/idt/acacia + * + ******************************************************************************/ + +enum +{ + INTEG0_PhysicalAddress = 0x18030000, + INTEG_PhysicalAddress = INTEG0_PhysicalAddress, // Default + + INTEG0_VirtualAddress = 0xb8030000, + INTEG_VirtualAddress = INTEG0_VirtualAddress, // Default +} ; + +// if you are looing for CEA, try rst.h +typedef struct +{ + u32 filler [0xc] ; // 0x30 bytes unused. + u32 errcs ; // sticky use ERRCS_ + u32 wtcount ; // Watchdog timer count reg. + u32 wtcompare ; // Watchdog timer timeout value. + u32 wtc ; // Watchdog timer control. use WTC_ +} volatile *INTEG_t ; + +enum +{ + ERRCS_wto_b = 0, // In INTEG_t -> errcs + ERRCS_wto_m = 0x00000001, + ERRCS_wne_b = 1, // In INTEG_t -> errcs + ERRCS_wne_m = 0x00000002, + ERRCS_ucw_b = 2, // In INTEG_t -> errcs + ERRCS_ucw_m = 0x00000004, + ERRCS_ucr_b = 3, // In INTEG_t -> errcs + ERRCS_ucr_m = 0x00000008, + ERRCS_upw_b = 4, // In INTEG_t -> errcs + ERRCS_upw_m = 0x00000010, + ERRCS_upr_b = 5, // In INTEG_t -> errcs + ERRCS_upr_m = 0x00000020, + ERRCS_udw_b = 6, // In INTEG_t -> errcs + ERRCS_udw_m = 0x00000040, + ERRCS_udr_b = 7, // In INTEG_t -> errcs + ERRCS_udr_m = 0x00000080, + ERRCS_sae_b = 8, // In INTEG_t -> errcs + ERRCS_sae_m = 0x00000100, + ERRCS_wre_b = 9, // In INTEG_t -> errcs + ERRCS_wre_m = 0x00000200, + + WTC_en_b = 0, // In INTEG_t -> wtc + WTC_en_m = 0x00000001, + WTC_to_b = 1, // In INTEG_t -> wtc + WTC_to_m = 0x00000002, +} ; + +#endif // __IDT_INTEG_H__ diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/irq.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/irq.h new file mode 100644 index 0000000000..65cc13c920 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/irq.h @@ -0,0 +1,7 @@ +#ifndef __ASM_MACH_MIPS_IRQ_H +#define __ASM_MACH_MIPS_IRQ_H + +#define NR_IRQS 256 +#include_next + +#endif /* __ASM_MACH_MIPS_IRQ_H */ diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/pci.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/pci.h new file mode 100644 index 0000000000..585a0ae21b --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/pci.h @@ -0,0 +1,692 @@ +/************************************************************************** + * + * BRIEF MODULE DESCRIPTION + * PCI register definitio + * + * Copyright 2004 IDT Inc. (rischelp@idt.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + ************************************************************************** + * May 2004 rkt, neb. + * + * Initial Release + * + * + * + ************************************************************************** + */ + +#ifndef __IDT_PCI_H__ +#define __IDT_PCI_H__ + +enum +{ + PCI0_PhysicalAddress = 0x18080000, + PCI_PhysicalAddress = PCI0_PhysicalAddress, + + PCI0_VirtualAddress = 0xB8080000, + PCI_VirtualAddress = PCI0_VirtualAddress, +} ; + +enum +{ + PCI_LbaCount = 4, // Local base addresses. +} ; + +typedef struct +{ + u32 a ; // Address. + u32 c ; // Control. + u32 m ; // mapping. +} PCI_Map_s ; + +typedef struct +{ + u32 pcic ; + u32 pcis ; + u32 pcism ; + u32 pcicfga ; + u32 pcicfgd ; + PCI_Map_s pcilba [PCI_LbaCount] ; + u32 pcidac ; + u32 pcidas ; + u32 pcidasm ; + u32 pcidad ; + u32 pcidma8c ; + u32 pcidma9c ; + u32 pcitc ; +} volatile *PCI_t ; + +// PCI messaging unit. +enum +{ + PCIM_Count = 2, +} ; +typedef struct +{ + u32 pciim [PCIM_Count] ; + u32 pciom [PCIM_Count] ; + u32 pciid ; + u32 pciiic ; + u32 pciiim ; + u32 pciiod ; + u32 pciioic ; + u32 pciioim ; +} volatile *PCIM_t ; + +/******************************************************************************* + * + * PCI Control Register + * + ******************************************************************************/ +enum +{ + PCIC_en_b = 0, + PCIC_en_m = 0x00000001, + PCIC_tnr_b = 1, + PCIC_tnr_m = 0x00000002, + PCIC_sce_b = 2, + PCIC_sce_m = 0x00000004, + PCIC_ien_b = 3, + PCIC_ien_m = 0x00000008, + PCIC_aaa_b = 4, + PCIC_aaa_m = 0x00000010, + PCIC_eap_b = 5, + PCIC_eap_m = 0x00000020, + PCIC_pcim_b = 6, + PCIC_pcim_m = 0x000001c0, + PCIC_pcim_disabled_v = 0, + PCIC_pcim_tnr_v = 1, // Satellite - target not ready + PCIC_pcim_suspend_v = 2, // Satellite - suspended CPU. + PCIC_pcim_extern_v = 3, // Host - external arbiter. + PCIC_pcim_fixed_v = 4, // Host - fixed priority arb. + PCIC_pcim_roundrobin_v = 5, // Host - round robin priority. + PCIC_pcim_reserved6_v = 6, + PCIC_pcim_reserved7_v = 7, + PCIC_igm_b = 9, + PCIC_igm_m = 0x00000200, +} ; + +/******************************************************************************* + * + * PCI Status Register + * + ******************************************************************************/ +enum { + PCIS_eed_b = 0, + PCIS_eed_m = 0x00000001, + PCIS_wr_b = 1, + PCIS_wr_m = 0x00000002, + PCIS_nmi_b = 2, + PCIS_nmi_m = 0x00000004, + PCIS_ii_b = 3, + PCIS_ii_m = 0x00000008, + PCIS_cwe_b = 4, + PCIS_cwe_m = 0x00000010, + PCIS_cre_b = 5, + PCIS_cre_m = 0x00000020, + PCIS_mdpe_b = 6, + PCIS_mdpe_m = 0x00000040, + PCIS_sta_b = 7, + PCIS_sta_m = 0x00000080, + PCIS_rta_b = 8, + PCIS_rta_m = 0x00000100, + PCIS_rma_b = 9, + PCIS_rma_m = 0x00000200, + PCIS_sse_b = 10, + PCIS_sse_m = 0x00000400, + PCIS_ose_b = 11, + PCIS_ose_m = 0x00000800, + PCIS_pe_b = 12, + PCIS_pe_m = 0x00001000, + PCIS_tae_b = 13, + PCIS_tae_m = 0x00002000, + PCIS_rle_b = 14, + PCIS_rle_m = 0x00004000, + PCIS_bme_b = 15, + PCIS_bme_m = 0x00008000, + PCIS_prd_b = 16, + PCIS_prd_m = 0x00010000, + PCIS_rip_b = 17, + PCIS_rip_m = 0x00020000, +} ; + +/******************************************************************************* + * + * PCI Status Mask Register + * + ******************************************************************************/ +enum { + PCISM_eed_b = 0, + PCISM_eed_m = 0x00000001, + PCISM_wr_b = 1, + PCISM_wr_m = 0x00000002, + PCISM_nmi_b = 2, + PCISM_nmi_m = 0x00000004, + PCISM_ii_b = 3, + PCISM_ii_m = 0x00000008, + PCISM_cwe_b = 4, + PCISM_cwe_m = 0x00000010, + PCISM_cre_b = 5, + PCISM_cre_m = 0x00000020, + PCISM_mdpe_b = 6, + PCISM_mdpe_m = 0x00000040, + PCISM_sta_b = 7, + PCISM_sta_m = 0x00000080, + PCISM_rta_b = 8, + PCISM_rta_m = 0x00000100, + PCISM_rma_b = 9, + PCISM_rma_m = 0x00000200, + PCISM_sse_b = 10, + PCISM_sse_m = 0x00000400, + PCISM_ose_b = 11, + PCISM_ose_m = 0x00000800, + PCISM_pe_b = 12, + PCISM_pe_m = 0x00001000, + PCISM_tae_b = 13, + PCISM_tae_m = 0x00002000, + PCISM_rle_b = 14, + PCISM_rle_m = 0x00004000, + PCISM_bme_b = 15, + PCISM_bme_m = 0x00008000, + PCISM_prd_b = 16, + PCISM_prd_m = 0x00010000, + PCISM_rip_b = 17, + PCISM_rip_m = 0x00020000, +} ; + +/******************************************************************************* + * + * PCI Configuration Address Register + * + ******************************************************************************/ +enum { + PCICFGA_reg_b = 2, + PCICFGA_reg_m = 0x000000fc, + PCICFGA_reg_id_v = 0x00>>2, //use PCFGID_ + PCICFGA_reg_04_v = 0x04>>2, //use PCFG04_ + PCICFGA_reg_08_v = 0x08>>2, //use PCFG08_ + PCICFGA_reg_0C_v = 0x0C>>2, //use PCFG0C_ + PCICFGA_reg_pba0_v = 0x10>>2, //use PCIPBA_ + PCICFGA_reg_pba1_v = 0x14>>2, //use PCIPBA_ + PCICFGA_reg_pba2_v = 0x18>>2, //use PCIPBA_ + PCICFGA_reg_pba3_v = 0x1c>>2, //use PCIPBA_ + PCICFGA_reg_subsystem_v = 0x2c>>2, //use PCFGSS_ + PCICFGA_reg_3C_v = 0x3C>>2, //use PCFG3C_ + PCICFGA_reg_pba0c_v = 0x44>>2, //use PCIPBAC_ + PCICFGA_reg_pba0m_v = 0x48>>2, + PCICFGA_reg_pba1c_v = 0x4c>>2, //use PCIPBAC_ + PCICFGA_reg_pba1m_v = 0x50>>2, + PCICFGA_reg_pba2c_v = 0x54>>2, //use PCIPBAC_ + PCICFGA_reg_pba2m_v = 0x58>>2, + PCICFGA_reg_pba3c_v = 0x5c>>2, //use PCIPBAC_ + PCICFGA_reg_pba3m_v = 0x60>>2, + PCICFGA_reg_pmgt_v = 0x64>>2, + PCICFGA_func_b = 8, + PCICFGA_func_m = 0x00000700, + PCICFGA_dev_b = 11, + PCICFGA_dev_m = 0x0000f800, + PCICFGA_dev_internal_v = 0, + PCICFGA_bus_b = 16, + PCICFGA_bus_m = 0x00ff0000, + PCICFGA_bus_type0_v = 0, //local bus + PCICFGA_en_b = 31, // read only + PCICFGA_en_m = 0x80000000, +} ; + +enum { + PCFGID_vendor_b = 0, + PCFGID_vendor_m = 0x0000ffff, + PCFGID_vendor_IDT_v = 0x111d, + PCFGID_device_b = 16, + PCFGID_device_m = 0xffff0000, + PCFGID_device_Korinade_v = 0x0214, + + PCFG04_command_ioena_b = 1, + PCFG04_command_ioena_m = 0x00000001, + PCFG04_command_memena_b = 2, + PCFG04_command_memena_m = 0x00000002, + PCFG04_command_bmena_b = 3, + PCFG04_command_bmena_m = 0x00000004, + PCFG04_command_mwinv_b = 5, + PCFG04_command_mwinv_m = 0x00000010, + PCFG04_command_parena_b = 7, + PCFG04_command_parena_m = 0x00000040, + PCFG04_command_serrena_b = 9, + PCFG04_command_serrena_m = 0x00000100, + PCFG04_command_fastbbena_b = 10, + PCFG04_command_fastbbena_m = 0x00000200, + PCFG04_status_b = 16, + PCFG04_status_m = 0xffff0000, + PCFG04_status_66MHz_b = 21, // 66 MHz enable + PCFG04_status_66MHz_m = 0x00200000, + PCFG04_status_fbb_b = 23, + PCFG04_status_fbb_m = 0x00800000, + PCFG04_status_mdpe_b = 24, + PCFG04_status_mdpe_m = 0x01000000, + PCFG04_status_dst_b = 25, + PCFG04_status_dst_m = 0x06000000, + PCFG04_status_sta_b = 27, + PCFG04_status_sta_m = 0x08000000, + PCFG04_status_rta_b = 28, + PCFG04_status_rta_m = 0x10000000, + PCFG04_status_rma_b = 29, + PCFG04_status_rma_m = 0x20000000, + PCFG04_status_sse_b = 30, + PCFG04_status_sse_m = 0x40000000, + PCFG04_status_pe_b = 31, + PCFG04_status_pe_m = 0x40000000, + + PCFG08_revId_b = 0, + PCFG08_revId_m = 0x000000ff, + PCFG08_classCode_b = 0, + PCFG08_classCode_m = 0xffffff00, + PCFG08_classCode_bridge_v = 06, + PCFG08_classCode_proc_v = 0x0b3000, // processor-MIPS + PCFG0C_cacheline_b = 0, + PCFG0C_cacheline_m = 0x000000ff, + PCFG0C_masterLatency_b = 8, + PCFG0C_masterLatency_m = 0x0000ff00, + PCFG0C_headerType_b = 16, + PCFG0C_headerType_m = 0x00ff0000, + PCFG0C_bist_b = 24, + PCFG0C_bist_m = 0xff000000, + + PCIPBA_msi_b = 0, + PCIPBA_msi_m = 0x00000001, + PCIPBA_p_b = 3, + PCIPBA_p_m = 0x00000004, + PCIPBA_baddr_b = 8, + PCIPBA_baddr_m = 0xffffff00, + + PCFGSS_vendorId_b = 0, + PCFGSS_vendorId_m = 0x0000ffff, + PCFGSS_id_b = 16, + PCFGSS_id_m = 0xffff0000, + + PCFG3C_interruptLine_b = 0, + PCFG3C_interruptLine_m = 0x000000ff, + PCFG3C_interruptPin_b = 8, + PCFG3C_interruptPin_m = 0x0000ff00, + PCFG3C_minGrant_b = 16, + PCFG3C_minGrant_m = 0x00ff0000, + PCFG3C_maxLat_b = 24, + PCFG3C_maxLat_m = 0xff000000, + + PCIPBAC_msi_b = 0, + PCIPBAC_msi_m = 0x00000001, + PCIPBAC_p_b = 1, + PCIPBAC_p_m = 0x00000002, + PCIPBAC_size_b = 2, + PCIPBAC_size_m = 0x0000007c, + PCIPBAC_sb_b = 7, + PCIPBAC_sb_m = 0x00000080, + PCIPBAC_pp_b = 8, + PCIPBAC_pp_m = 0x00000100, + PCIPBAC_mr_b = 9, + PCIPBAC_mr_m = 0x00000600, + PCIPBAC_mr_read_v =0, //no prefetching + PCIPBAC_mr_readLine_v =1, + PCIPBAC_mr_readMult_v =2, + PCIPBAC_mrl_b = 11, + PCIPBAC_mrl_m = 0x00000800, + PCIPBAC_mrm_b = 12, + PCIPBAC_mrm_m = 0x00001000, + PCIPBAC_trp_b = 13, + PCIPBAC_trp_m = 0x00002000, + + PCFG40_trdyTimeout_b = 0, + PCFG40_trdyTimeout_m = 0x000000ff, + PCFG40_retryLim_b = 8, + PCFG40_retryLim_m = 0x0000ff00, +}; + +/******************************************************************************* + * + * PCI Local Base Address [0|1|2|3] Register + * + ******************************************************************************/ +enum { + PCILBA_baddr_b = 0, // In PCI_t -> pcilba [] .a + PCILBA_baddr_m = 0xffffff00, +} ; +/******************************************************************************* + * + * PCI Local Base Address Control Register + * + ******************************************************************************/ +enum { + PCILBAC_msi_b = 0, // In pPci->pcilba[i].c + PCILBAC_msi_m = 0x00000001, + PCILBAC_msi_mem_v = 0, + PCILBAC_msi_io_v = 1, + PCILBAC_size_b = 2, // In pPci->pcilba[i].c + PCILBAC_size_m = 0x0000007c, + PCILBAC_sb_b = 7, // In pPci->pcilba[i].c + PCILBAC_sb_m = 0x00000080, + PCILBAC_rt_b = 8, // In pPci->pcilba[i].c + PCILBAC_rt_m = 0x00000100, + PCILBAC_rt_noprefetch_v = 0, // mem read + PCILBAC_rt_prefetch_v = 1, // mem readline +} ; + +/******************************************************************************* + * + * PCI Local Base Address [0|1|2|3] Mapping Register + * + ******************************************************************************/ +enum { + PCILBAM_maddr_b = 8, + PCILBAM_maddr_m = 0xffffff00, +} ; + +/******************************************************************************* + * + * PCI Decoupled Access Control Register + * + ******************************************************************************/ +enum { + PCIDAC_den_b = 0, + PCIDAC_den_m = 0x00000001, +} ; + +/******************************************************************************* + * + * PCI Decoupled Access Status Register + * + ******************************************************************************/ +enum { + PCIDAS_d_b = 0, + PCIDAS_d_m = 0x00000001, + PCIDAS_b_b = 1, + PCIDAS_b_m = 0x00000002, + PCIDAS_e_b = 2, + PCIDAS_e_m = 0x00000004, + PCIDAS_ofe_b = 3, + PCIDAS_ofe_m = 0x00000008, + PCIDAS_off_b = 4, + PCIDAS_off_m = 0x00000010, + PCIDAS_ife_b = 5, + PCIDAS_ife_m = 0x00000020, + PCIDAS_iff_b = 6, + PCIDAS_iff_m = 0x00000040, +} ; + +/******************************************************************************* + * + * PCI DMA Channel 8 Configuration Register + * + ******************************************************************************/ +enum +{ + PCIDMA8C_mbs_b = 0, // Maximum Burst Size. + PCIDMA8C_mbs_m = 0x00000fff, // { pcidma8c } + PCIDMA8C_our_b = 12, // Optimize Unaligned Burst Reads. + PCIDMA8C_our_m = 0x00001000, // { pcidma8c } +} ; + +/******************************************************************************* + * + * PCI DMA Channel 9 Configuration Register + * + ******************************************************************************/ +enum +{ + PCIDMA9C_mbs_b = 0, // Maximum Burst Size. + PCIDMA9C_mbs_m = 0x00000fff, // { pcidma9c } +} ; + +/******************************************************************************* + * + * PCI to Memory(DMA Channel 8) AND Memory to PCI DMA(DMA Channel 9)Descriptors + * + ******************************************************************************/ +enum { + PCIDMAD_pt_b = 22, // in DEVCMD field (descriptor) + PCIDMAD_pt_m = 0x00c00000, // preferred transaction field + // These are for reads (DMA channel 8) + PCIDMAD_devcmd_mr_v = 0, //memory read + PCIDMAD_devcmd_mrl_v = 1, //memory read line + PCIDMAD_devcmd_mrm_v = 2, //memory read multiple + PCIDMAD_devcmd_ior_v = 3, //I/O read + // These are for writes (DMA channel 9) + PCIDMAD_devcmd_mw_v = 0, //memory write + PCIDMAD_devcmd_mwi_v = 1, //memory write invalidate + PCIDMAD_devcmd_iow_v = 3, //I/O write + + // Swap byte field applies to both DMA channel 8 and 9 + PCIDMAD_sb_b = 24, // in DEVCMD field (descriptor) + PCIDMAD_sb_m = 0x01000000, // swap byte field +} ; + + +/******************************************************************************* + * + * PCI Target Control Register + * + ******************************************************************************/ +enum +{ + PCITC_rtimer_b = 0, // In PCITC_t -> pcitc + PCITC_rtimer_m = 0x000000ff, + PCITC_dtimer_b = 8, // In PCITC_t -> pcitc + PCITC_dtimer_m = 0x0000ff00, + PCITC_rdr_b = 18, // In PCITC_t -> pcitc + PCITC_rdr_m = 0x00040000, + PCITC_ddt_b = 19, // In PCITC_t -> pcitc + PCITC_ddt_m = 0x00080000, +} ; +/******************************************************************************* + * + * PCI messaging unit [applies to both inbound and outbound registers ] + * + ******************************************************************************/ +enum +{ + PCIM_m0_b = 0, // In PCIM_t -> {pci{iic,iim,ioic,ioim}} + PCIM_m0_m = 0x00000001, // inbound or outbound message 0 + PCIM_m1_b = 1, // In PCIM_t -> {pci{iic,iim,ioic,ioim}} + PCIM_m1_m = 0x00000002, // inbound or outbound message 1 + PCIM_db_b = 2, // In PCIM_t -> {pci{iic,iim,ioic,ioim}} + PCIM_db_m = 0x00000004, // inbound or outbound doorbell +}; + + + + + + +#define PCI_MSG_VirtualAddress 0xB8088010 +#define rc32434_pci ((volatile PCI_t) PCI0_VirtualAddress) +#define rc32434_pci_msg ((volatile PCIM_t) PCI_MSG_VirtualAddress) + +#define PCIM_SHFT 0x6 +#define PCIM_BIT_LEN 0x7 +#define PCIM_H_EA 0x3 +#define PCIM_H_IA_FIX 0x4 +#define PCIM_H_IA_RR 0x5 + +#define PCI_ADDR_START 0x50000000 + +#define CPUTOPCI_MEM_WIN 0x02000000 +#define CPUTOPCI_IO_WIN 0x00100000 +#define PCILBA_SIZE_SHFT 2 +#define PCILBA_SIZE_MASK 0x1F +#define SIZE_256MB 0x1C +#define SIZE_128MB 0x1B +#define SIZE_64MB 0x1A +#define SIZE_32MB 0x19 +#define SIZE_16MB 0x18 +#define SIZE_4MB 0x16 +#define SIZE_2MB 0x15 +#define SIZE_1MB 0x14 +#define KORINA_CONFIG0_ADDR 0x80000000 +#define KORINA_CONFIG1_ADDR 0x80000004 +#define KORINA_CONFIG2_ADDR 0x80000008 +#define KORINA_CONFIG3_ADDR 0x8000000C +#define KORINA_CONFIG4_ADDR 0x80000010 +#define KORINA_CONFIG5_ADDR 0x80000014 +#define KORINA_CONFIG6_ADDR 0x80000018 +#define KORINA_CONFIG7_ADDR 0x8000001C +#define KORINA_CONFIG8_ADDR 0x80000020 +#define KORINA_CONFIG9_ADDR 0x80000024 +#define KORINA_CONFIG10_ADDR 0x80000028 +#define KORINA_CONFIG11_ADDR 0x8000002C +#define KORINA_CONFIG12_ADDR 0x80000030 +#define KORINA_CONFIG13_ADDR 0x80000034 +#define KORINA_CONFIG14_ADDR 0x80000038 +#define KORINA_CONFIG15_ADDR 0x8000003C +#define KORINA_CONFIG16_ADDR 0x80000040 +#define KORINA_CONFIG17_ADDR 0x80000044 +#define KORINA_CONFIG18_ADDR 0x80000048 +#define KORINA_CONFIG19_ADDR 0x8000004C +#define KORINA_CONFIG20_ADDR 0x80000050 +#define KORINA_CONFIG21_ADDR 0x80000054 +#define KORINA_CONFIG22_ADDR 0x80000058 +#define KORINA_CONFIG23_ADDR 0x8000005C +#define KORINA_CONFIG24_ADDR 0x80000060 +#define KORINA_CONFIG25_ADDR 0x80000064 +#define KORINA_CMD (PCFG04_command_ioena_m | \ + PCFG04_command_memena_m | \ + PCFG04_command_bmena_m | \ + PCFG04_command_mwinv_m | \ + PCFG04_command_parena_m | \ + PCFG04_command_serrena_m ) + +#define KORINA_STAT (PCFG04_status_mdpe_m | \ + PCFG04_status_sta_m | \ + PCFG04_status_rta_m | \ + PCFG04_status_rma_m | \ + PCFG04_status_sse_m | \ + PCFG04_status_pe_m) + +#define KORINA_CNFG1 ((KORINA_STAT<<16)|KORINA_CMD) + +#define KORINA_REVID 0 +#define KORINA_CLASS_CODE 0 +#define KORINA_CNFG2 ((KORINA_CLASS_CODE<<8) | \ + KORINA_REVID) + +#define KORINA_CACHE_LINE_SIZE 4 +#define KORINA_MASTER_LAT 0x3c +#define KORINA_HEADER_TYPE 0 +#define KORINA_BIST 0 + +#define KORINA_CNFG3 ((KORINA_BIST << 24) | \ + (KORINA_HEADER_TYPE<<16) | \ + (KORINA_MASTER_LAT<<8) | \ + KORINA_CACHE_LINE_SIZE ) + +#define KORINA_BAR0 0x00000008 /* 128 MB Memory */ +#define KORINA_BAR1 0x18800001 /* 1 MB IO */ +#define KORINA_BAR2 0x18000001 /* 2 MB IO window for Korina + internal Registers */ +#define KORINA_BAR3 0x48000008 /* Spare 128 MB Memory */ + +#define KORINA_CNFG4 KORINA_BAR0 +#define KORINA_CNFG5 KORINA_BAR1 +#define KORINA_CNFG6 KORINA_BAR2 +#define KORINA_CNFG7 KORINA_BAR3 + +#define KORINA_SUBSYS_VENDOR_ID 0x011d +#define KORINA_SUBSYSTEM_ID 0x0214 +#define KORINA_CNFG8 0 +#define KORINA_CNFG9 0 +#define KORINA_CNFG10 0 +#define KORINA_CNFG11 ((KORINA_SUBSYS_VENDOR_ID<<16) | \ + KORINA_SUBSYSTEM_ID) +#define KORINA_INT_LINE 1 +#define KORINA_INT_PIN 1 +#define KORINA_MIN_GNT 8 +#define KORINA_MAX_LAT 0x38 +#define KORINA_CNFG12 0 +#define KORINA_CNFG13 0 +#define KORINA_CNFG14 0 +#define KORINA_CNFG15 ((KORINA_MAX_LAT<<24) | \ + (KORINA_MIN_GNT<<16) | \ + (KORINA_INT_PIN<<8) | \ + KORINA_INT_LINE) +#define KORINA_RETRY_LIMIT 0x80 +#define KORINA_TRDY_LIMIT 0x80 +#define KORINA_CNFG16 ((KORINA_RETRY_LIMIT<<8) | \ + KORINA_TRDY_LIMIT) +#define PCI_PBAxC_R 0x0 +#define PCI_PBAxC_RL 0x1 +#define PCI_PBAxC_RM 0x2 +#define SIZE_SHFT 2 + +#if defined(__MIPSEB__) +#define KORINA_PBA0C ( PCIPBAC_mrl_m | PCIPBAC_sb_m | \ + ((PCI_PBAxC_RM &0x3) << PCIPBAC_mr_b) | \ + PCIPBAC_pp_m | \ + (SIZE_128MB< + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __MIPS_RB_H__ +#define __MIPS_RB_H__ +#include + +#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000)) +#define DEV0BASE 0x010000 +#define DEV0MASK 0x010004 +#define DEV0C 0x010008 +#define DEV0TC 0x01000C +#define DEV1BASE 0x010010 +#define DEV1MASK 0x010014 +#define DEV1C 0x010018 +#define DEV1TC 0x01001C +#define DEV2BASE 0x010020 +#define DEV2MASK 0x010024 +#define DEV2C 0x010028 +#define DEV2TC 0x01002C +#define DEV3BASE 0x010030 +#define DEV3MASK 0x010034 +#define DEV3C 0x010038 +#define DEV3TC 0x01003C +#define BTCS 0x010040 +#define BTCOMPARE 0x010044 +#define GPIOFUNC 0x050000 +#define GPIOCFG 0x050004 +#define GPIOD 0x050008 +#define GPIOILEVEL 0x05000C +#define GPIOISTAT 0x050010 +#define GPIONMIEN 0x050014 +#define IMASK6 0x038038 + +#define LO_WPX (1 << 0) +#define LO_ALE (1 << 1) +#define LO_CLE (1 << 2) +#define LO_CEX (1 << 3) +#define LO_FOFF (1 << 5) +#define LO_SPICS (1 << 6) +#define LO_ULED (1 << 7) + +typedef enum { + FUNC = 0x00, + CFG = 0x04, + DATA = 0x08, + ILEVEL = 0x0c, + ISTAT = 0x10, + NMIEN = 0x14 +} gpio_func; + +extern void changeLatchU5(unsigned char orMask, unsigned char nandMask); +extern unsigned get434Reg(unsigned regOffs); +extern void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val); +extern void gpio_set(gpio_func func, u32 mask, u32 value); +extern u32 gpio_get(gpio_func func); + +#define get434Reg(x) (*(volatile unsigned *) (IDT434_REG_BASE + (x))) + +struct korina_device { + char *name; + unsigned char mac[6]; + struct net_device *dev; +}; + +struct cf_device { + int gpio_pin; + void *dev; + struct gendisk *gd; +}; + +#endif diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/rc32434.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/rc32434.h new file mode 100644 index 0000000000..f3e53e4332 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/rc32434.h @@ -0,0 +1,121 @@ +/* + *************************************************************************** + * Definitions for IDT RC323434 CPU. + * + **************************************************************************** + * Kiran Rao + * + * Original form + **************************************************************************** + * P. Sadik Oct 08, 2003 + * + * Started revision history + * Made IDT_BUS_FREQ a kernel configuration parameter + **************************************************************************** + * P. Sadik Oct 10, 2003 + * + * Removed IDT_BUS_FREQ, since this parameter is no longer required. Instead + * idt_cpu_freq is used everywhere + **************************************************************************** + * P. Sadik Oct 20, 2003 + * + * Removed RC32434_BASE_BAUD + **************************************************************************** +*/ +#ifndef _RC32434_H_ +#define _RC32434_H_ + +#include +#include +#include + +#define RC32434_REG_BASE 0x18000000 + +#define interrupt ((volatile INT_t ) INT0_VirtualAddress) + + +#define IDT_CLOCK_MULT 2 +#define MIPS_CPU_TIMER_IRQ 7 +/* Interrupt Controller */ +#define IC_GROUP0_PEND (RC32434_REG_BASE + 0x38000) +#define IC_GROUP0_MASK (RC32434_REG_BASE + 0x38008) +#define IC_GROUP_OFFSET 0x0C + +#define NUM_INTR_GROUPS 5 +/* 16550 UARTs */ + +#define GROUP0_IRQ_BASE 8 /* GRP2 IRQ numbers start here */ +#define GROUP1_IRQ_BASE (GROUP0_IRQ_BASE + 32) /* GRP3 IRQ numbers start here */ +#define GROUP2_IRQ_BASE (GROUP1_IRQ_BASE + 32) /* GRP4 IRQ numbers start here */ +#define GROUP3_IRQ_BASE (GROUP2_IRQ_BASE + 32) /* GRP5 IRQ numbers start here */ +#define GROUP4_IRQ_BASE (GROUP3_IRQ_BASE + 32) + + +#ifdef __MIPSEB__ +#define RC32434_UART0_BASE (RC32434_REG_BASE + 0x58003) +#else +#define RC32434_UART0_BASE (RC32434_REG_BASE + 0x58000) +#endif + +#define RC32434_UART0_IRQ GROUP3_IRQ_BASE + 0 +// #define EB434_UART1_IRQ GROUP4_IRQ_BASE + 11 + +#define local_readl(addr) __raw_readl(addr) +#define local_writel(l,addr) __raw_writel(l,addr) + +/* cpu pipeline flush */ +static inline void rc32434_sync(void) +{ + __asm__ volatile ("sync"); +} + +static inline void rc32434_sync_udelay(int us) +{ + __asm__ volatile ("sync"); + udelay(us); +} + +static inline void rc32434_sync_delay(int ms) +{ + __asm__ volatile ("sync"); + mdelay(ms); +} + +/* + * C access to CLZ and CLO instructions + * (count leading zeroes/ones). + */ +static inline int rc32434_clz(unsigned long val) +{ + int ret; + __asm__ volatile ( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips32\n\t" + "clz\t%0,%1\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + : "=r" (ret) + : "r" (val)); + + return ret; +} +static inline int rc32434_clo(unsigned long val) +{ + int ret; + __asm__ volatile ( + ".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips32\n\t" + "clo\t%0,%1\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + : "=r" (ret) + : "r" (val)); + + return ret; +} + +#endif /* _RC32434_H_ */ diff --git a/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/war.h b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/war.h new file mode 100644 index 0000000000..3ddf187e98 --- /dev/null +++ b/target/linux/rb532/files-2.6.23/include/asm-mips/rc32434/war.h @@ -0,0 +1,25 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle + */ +#ifndef __ASM_MIPS_MACH_MIPS_WAR_H +#define __ASM_MIPS_MACH_MIPS_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR 0 +#define R4600_V1_HIT_CACHEOP_WAR 0 +#define R4600_V2_HIT_CACHEOP_WAR 0 +#define R5432_CP0_INTERRUPT_WAR 0 +#define BCM1250_M3_WAR 0 +#define SIBYTE_1956_WAR 0 +#define MIPS4K_ICACHE_REFILL_WAR 1 +#define MIPS_CACHE_SYNC_WAR 0 +#define TX49XX_ICACHE_INDEX_INV_WAR 0 +#define RM9000_CDEX_SMP_WAR 0 +#define ICACHE_REFILLS_WORKAROUND_WAR 0 +#define R10000_LLSC_WAR 0 +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MIPS_MACH_MIPS_WAR_H */ diff --git a/target/linux/rb532/files/arch/mips/pci/fixup-rb500.c b/target/linux/rb532/files/arch/mips/pci/fixup-rb500.c deleted file mode 100644 index 76c465fea4..0000000000 --- a/target/linux/rb532/files/arch/mips/pci/fixup-rb500.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * stevel@mvista.com or source@mvista.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include - -#include -#include - -#include - -static int __devinitdata irq_map[2][12] = { - { 0, 0, 2, 3, 2, 3, 0, 0, 0, 0, 0, 1 }, - { 0, 0, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3 } -}; - -int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - int irq = 0; - - if (dev->bus->number < 2 && PCI_SLOT(dev->devfn) < 12) { - irq = irq_map[dev->bus->number][PCI_SLOT(dev->devfn)]; - } - return irq + GROUP4_IRQ_BASE + 4; -} - diff --git a/target/linux/rb532/files/arch/mips/pci/ops-rc32434.c b/target/linux/rb532/files/arch/mips/pci/ops-rc32434.c deleted file mode 100644 index b90ab1d129..0000000000 --- a/target/linux/rb532/files/arch/mips/pci/ops-rc32434.c +++ /dev/null @@ -1,218 +0,0 @@ -/************************************************************************** - * - * BRIEF MODULE DESCRIPTION - * pci_ops for IDT EB434 board - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * Copyright 2006 Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - ************************************************************************** - * May 2004 rkt, neb - * - * Initial Release - * - * - * - ************************************************************************** - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#define PCI_ACCESS_READ 0 -#define PCI_ACCESS_WRITE 1 - - -#define PCI_CFG_SET(bus,slot,func,off) \ - (rc32434_pci->pcicfga = (0x80000000 | \ - ((bus) << 16) | ((slot)<<11) | \ - ((func)<<8) | (off))) - -static inline int config_access(unsigned char access_type, struct pci_bus *bus, - unsigned int devfn, unsigned char where, - u32 * data) -{ - unsigned int slot = PCI_SLOT(devfn); - u8 func = PCI_FUNC(devfn); - - /* Setup address */ - PCI_CFG_SET(bus->number, slot, func, where); - rc32434_sync(); - - if (access_type == PCI_ACCESS_WRITE) - rc32434_pci->pcicfgd = *data; - else - *data = rc32434_pci->pcicfgd; - - rc32434_sync(); - - return 0; -} - - -/* - * We can't address 8 and 16 bit words directly. Instead we have to - * read/write a 32bit word and mask/modify the data we actually want. - */ -static int read_config_byte(struct pci_bus *bus, unsigned int devfn, - int where, u8 * val) -{ - u32 data; - int ret; - - ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); - *val = (data >> ((where & 3) << 3)) & 0xff; - return ret; -} - -static int read_config_word(struct pci_bus *bus, unsigned int devfn, - int where, u16 * val) -{ - u32 data; - int ret; - - ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); - *val = (data >> ((where & 3) << 3)) & 0xffff; - return ret; -} - -static int read_config_dword(struct pci_bus *bus, unsigned int devfn, - int where, u32 * val) -{ - int ret; - int delay = 1; - - if (bus->number == 0 && PCI_SLOT(devfn) > 21) - return 0; - -retry: - ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val); - - /* PCI scan: check for invalid values, device may not have - * finished initializing */ - - if (where == PCI_VENDOR_ID) { - if (ret == 0xffffffff || ret == 0x00000000 || - ret == 0x0000ffff || ret == 0xffff0000) { - - if (delay > 4) - return 0; - - delay *= 2; - msleep(delay); - goto retry; - } - } - - return ret; -} - -static int -write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, - u8 val) -{ - u32 data = 0; - - if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) - return -1; - - data = (data & ~(0xff << ((where & 3) << 3))) | - (val << ((where & 3) << 3)); - - if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) - return -1; - - return PCIBIOS_SUCCESSFUL; -} - - -static int -write_config_word(struct pci_bus *bus, unsigned int devfn, int where, - u16 val) -{ - u32 data = 0; - - if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) - return -1; - - data = (data & ~(0xffff << ((where & 3) << 3))) | - (val << ((where & 3) << 3)); - - if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) - return -1; - - - return PCIBIOS_SUCCESSFUL; -} - - -static int -write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, - u32 val) -{ - if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val)) - return -1; - - return PCIBIOS_SUCCESSFUL; -} - -static int pci_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 * val) -{ - switch (size) { - case 1: - return read_config_byte(bus, devfn, where, (u8 *) val); - case 2: - return read_config_word(bus, devfn, where, (u16 *) val); - default: - return read_config_dword(bus, devfn, where, val); - } -} - -static int pci_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - switch (size) { - case 1: - return write_config_byte(bus, devfn, where, (u8) val); - case 2: - return write_config_word(bus, devfn, where, (u16) val); - default: - return write_config_dword(bus, devfn, where, val); - } -} - -struct pci_ops rc32434_pci_ops = { - .read = pci_config_read, - .write = pci_config_write, -}; diff --git a/target/linux/rb532/files/arch/mips/pci/pci-rc32434.c b/target/linux/rb532/files/arch/mips/pci/pci-rc32434.c deleted file mode 100644 index 9ae596c223..0000000000 --- a/target/linux/rb532/files/arch/mips/pci/pci-rc32434.c +++ /dev/null @@ -1,236 +0,0 @@ -/************************************************************************** - * - * BRIEF MODULE DESCRIPTION - * PCI initialization for IDT EB434 board - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - ************************************************************************** - * May 2004 rkt, neb - * - * Initial Release - * - * - * - ************************************************************************** - */ - -#include -#include -#include -#include -#include - -#include -#include - -#define PCI_ACCESS_READ 0 -#define PCI_ACCESS_WRITE 1 - -/* define an unsigned array for the PCI registers */ -unsigned int korinaCnfgRegs[25] = { - KORINA_CNFG1, KORINA_CNFG2, KORINA_CNFG3, KORINA_CNFG4, - KORINA_CNFG5, KORINA_CNFG6, KORINA_CNFG7, KORINA_CNFG8, - KORINA_CNFG9, KORINA_CNFG10, KORINA_CNFG11, KORINA_CNFG12, - KORINA_CNFG13, KORINA_CNFG14, KORINA_CNFG15, KORINA_CNFG16, - KORINA_CNFG17, KORINA_CNFG18, KORINA_CNFG19, KORINA_CNFG20, - KORINA_CNFG21, KORINA_CNFG22, KORINA_CNFG23, KORINA_CNFG24 -}; -static struct resource rc32434_res_pci_mem1; -static struct resource rc32434_res_pci_mem2; - -static struct resource rc32434_res_pci_mem1 = { - .name = "PCI MEM1", - .start = 0x50000000, - .end = 0x5FFFFFFF, - .flags = IORESOURCE_MEM, - .parent = &rc32434_res_pci_mem1, - .sibling = NULL, - .child = &rc32434_res_pci_mem2 -}; - -static struct resource rc32434_res_pci_mem2 = { - .name = "PCI Mem2", - .start = 0x60000000, - .end = 0x6FFFFFFF, - .flags = IORESOURCE_MEM, - .parent = &rc32434_res_pci_mem1, - .sibling = NULL, - .child = NULL -}; - -static struct resource rc32434_res_pci_io1 = { - .name = "PCI I/O1", - .start = 0x18800000, - .end = 0x188FFFFF, - .flags = IORESOURCE_IO, -}; - -extern struct pci_ops rc32434_pci_ops; - -#define PCI_MEM1_START PCI_ADDR_START -#define PCI_MEM1_END PCI_ADDR_START + CPUTOPCI_MEM_WIN - 1 -#define PCI_MEM2_START PCI_ADDR_START + CPUTOPCI_MEM_WIN -#define PCI_MEM2_END PCI_ADDR_START + ( 2* CPUTOPCI_MEM_WIN) - 1 -#define PCI_IO1_START PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) -#define PCI_IO1_END PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN -1 -#define PCI_IO2_START PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN -#define PCI_IO2_END PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + (2 * CPUTOPCI_IO_WIN) -1 - - -struct pci_controller rc32434_controller2; - -struct pci_controller rc32434_controller = { - .pci_ops = &rc32434_pci_ops, - .mem_resource = &rc32434_res_pci_mem1, - .io_resource = &rc32434_res_pci_io1, - .mem_offset = 0, - .io_offset = 0, - -}; - -#ifdef __MIPSEB__ -#define PCI_ENDIAN_FLAG PCILBAC_sb_m -#else -#define PCI_ENDIAN_FLAG 0 -#endif - -static int __init rc32434_pcibridge_init(void) -{ - unsigned int pcicValue, pcicData = 0; - unsigned int dummyRead, pciCntlVal; - int loopCount; - unsigned int pciConfigAddr; - - pcicValue = rc32434_pci->pcic; - pcicValue = (pcicValue >> PCIM_SHFT) & PCIM_BIT_LEN; - if (!((pcicValue == PCIM_H_EA) || - (pcicValue == PCIM_H_IA_FIX) || - (pcicValue == PCIM_H_IA_RR))) { - printk("PCI init error!!!\n"); - /* Not in Host Mode, return ERROR */ - return -1; - } - /* Enables the Idle Grant mode, Arbiter Parking */ - pcicData |=(PCIC_igm_m|PCIC_eap_m|PCIC_en_m); - rc32434_pci->pcic = pcicData; /* Enable the PCI bus Interface */ - /* Zero out the PCI status & PCI Status Mask */ - for(;;) - { - pcicData = rc32434_pci->pcis; - if (!(pcicData & PCIS_rip_m)) - break; - } - - rc32434_pci->pcis = 0; - rc32434_pci->pcism = 0xFFFFFFFF; - /* Zero out the PCI decoupled registers */ - rc32434_pci->pcidac=0; /* disable PCI decoupled accesses at initialization */ - rc32434_pci->pcidas=0; /* clear the status */ - rc32434_pci->pcidasm=0x0000007F; /* Mask all the interrupts */ - /* Mask PCI Messaging Interrupts */ - rc32434_pci_msg->pciiic = 0; - rc32434_pci_msg->pciiim = 0xFFFFFFFF; - rc32434_pci_msg->pciioic = 0; - rc32434_pci_msg->pciioim = 0; - - - /* Setup PCILB0 as Memory Window */ - rc32434_pci->pcilba[0].a = (unsigned int) (PCI_ADDR_START); - - /* setup the PCI map address as same as the local address */ - - rc32434_pci->pcilba[0].m = (unsigned int) (PCI_ADDR_START); - - - /* Setup PCILBA1 as MEM */ - rc32434_pci->pcilba[0].c = ( ((SIZE_256MB & 0x1f) << PCILBAC_size_b) | PCI_ENDIAN_FLAG); - dummyRead = rc32434_pci->pcilba[0].c; /* flush the CPU write Buffers */ - rc32434_pci->pcilba[1].a = 0x60000000; - rc32434_pci->pcilba[1].m = 0x60000000; - - /* setup PCILBA2 as IO Window*/ - rc32434_pci->pcilba[1].c = (((SIZE_256MB & 0x1f) << PCILBAC_size_b )| PCI_ENDIAN_FLAG); - dummyRead = rc32434_pci->pcilba[1].c; /* flush the CPU write Buffers */ - rc32434_pci->pcilba[2].a = 0x18C00000; - rc32434_pci->pcilba[2].m = 0x18FFFFFF; - - /* setup PCILBA2 as IO Window*/ - rc32434_pci->pcilba[2].c = (((SIZE_4MB & 0x1f) << PCILBAC_size_b) | PCI_ENDIAN_FLAG ); - dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */ - - /* Setup PCILBA3 as IO Window */ - rc32434_pci->pcilba[3].a = 0x18800000; - rc32434_pci->pcilba[3].m = 0x18800000; - rc32434_pci->pcilba[3].c = ( (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m) | PCI_ENDIAN_FLAG); - dummyRead = rc32434_pci->pcilba[3].c; /* flush the CPU write Buffers */ - - pciConfigAddr=(unsigned int)(0x80000004); - for(loopCount=0;loopCount<24;loopCount++){ - rc32434_pci->pcicfga=pciConfigAddr; - dummyRead=rc32434_pci->pcicfga; - rc32434_pci->pcicfgd = korinaCnfgRegs[loopCount]; - dummyRead=rc32434_pci->pcicfgd; - pciConfigAddr += 4; - } - rc32434_pci->pcitc = (unsigned int)((PCITC_RTIMER_VAL&0xff) << PCITC_rtimer_b) - | ((PCITC_DTIMER_VAL&0xff) << PCITC_dtimer_b); - - pciCntlVal=rc32434_pci->pcic; - pciCntlVal &=~(PCIC_tnr_m); - rc32434_pci->pcic = pciCntlVal; - pciCntlVal=rc32434_pci->pcic; - return 0; -} - -/* Do platform specific device initialization at pci_enable_device() time */ -int pcibios_plat_dev_init(struct pci_dev *dev) -{ - if (PCI_SLOT(dev->devfn) == 6 && dev->bus->number == 0) { - /* disable prefetched memory range */ - pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0); - pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x10); - - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4); - } - return 0; -} - -static int __init rc32434_pci_init(void) -{ - printk("PCI: Initializing PCI\n"); - - ioport_resource.start = rc32434_res_pci_io1.start; - ioport_resource.end = rc32434_res_pci_io1.end; - - rc32434_pcibridge_init(); - - register_pci_controller(&rc32434_controller); - rc32434_sync(); - - return 0; -} - -arch_initcall(rc32434_pci_init); - diff --git a/target/linux/rb532/files/arch/mips/rb500/Makefile b/target/linux/rb532/files/arch/mips/rb500/Makefile deleted file mode 100644 index d3ae3d9122..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the RB500 board specific parts of the kernel -# - -obj-y += irq.o time.o setup.o serial.o prom.o gpio.o devices.o diff --git a/target/linux/rb532/files/arch/mips/rb500/devices.c b/target/linux/rb532/files/arch/mips/rb500/devices.c deleted file mode 100644 index 6372737de7..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/devices.c +++ /dev/null @@ -1,338 +0,0 @@ -/* - * RouterBoard 500 Platform devices - * - * Copyright (C) 2006 Felix Fietkau - * Copyright (C) 2007 Florian Fainelli - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#define ETH0_DMA_RX_IRQ GROUP1_IRQ_BASE + 0 -#define ETH0_DMA_TX_IRQ GROUP1_IRQ_BASE + 1 -#define ETH0_RX_OVR_IRQ GROUP3_IRQ_BASE + 9 -#define ETH0_TX_UND_IRQ GROUP3_IRQ_BASE + 10 - -#define ETH0_RX_DMA_ADDR (DMA0_PhysicalAddress + 0*DMA_CHAN_OFFSET) -#define ETH0_TX_DMA_ADDR (DMA0_PhysicalAddress + 1*DMA_CHAN_OFFSET) - -/* NAND definitions */ -#define MEM32(x) *((volatile unsigned *) (x)) - -#define GPIO_RDY (1 << 0x08) -#define GPIO_WPX (1 << 0x09) -#define GPIO_ALE (1 << 0x0a) -#define GPIO_CLE (1 << 0x0b) - -extern char* board_type; - -static struct resource korina_dev0_res[] = { - { - .name = "korina_regs", - .start = ETH0_PhysicalAddress, - .end = ETH0_PhysicalAddress + sizeof(ETH_t), - .flags = IORESOURCE_MEM, - }, { - .name = "korina_rx", - .start = ETH0_DMA_RX_IRQ, - .end = ETH0_DMA_RX_IRQ, - .flags = IORESOURCE_IRQ - }, { - .name = "korina_tx", - .start = ETH0_DMA_TX_IRQ, - .end = ETH0_DMA_TX_IRQ, - .flags = IORESOURCE_IRQ - }, { - .name = "korina_ovr", - .start = ETH0_RX_OVR_IRQ, - .end = ETH0_RX_OVR_IRQ, - .flags = IORESOURCE_IRQ - }, { - .name = "korina_und", - .start = ETH0_TX_UND_IRQ, - .end = ETH0_TX_UND_IRQ, - .flags = IORESOURCE_IRQ - }, { - .name = "korina_dma_rx", - .start = ETH0_RX_DMA_ADDR, - .end = ETH0_RX_DMA_ADDR + DMA_CHAN_OFFSET - 1, - .flags = IORESOURCE_MEM, - }, { - .name = "korina_dma_tx", - .start = ETH0_TX_DMA_ADDR, - .end = ETH0_TX_DMA_ADDR + DMA_CHAN_OFFSET - 1, - .flags = IORESOURCE_MEM, - } -}; - -static struct korina_device korina_dev0_data = { - .name = "korina0", - .mac = {0xde, 0xca, 0xff, 0xc0, 0xff, 0xee} -}; - -static struct platform_device korina_dev0 = { - .id = 0, - .name = "korina", - .dev.platform_data = &korina_dev0_data, - .resource = korina_dev0_res, - .num_resources = ARRAY_SIZE(korina_dev0_res), -}; - -#define CF_GPIO_NUM 13 - -static struct resource cf_slot0_res[] = { - { - .name = "cf_membase", - .flags = IORESOURCE_MEM - }, { - .name = "cf_irq", - .start = (8 + 4 * 32 + CF_GPIO_NUM), /* 149 */ - .end = (8 + 4 * 32 + CF_GPIO_NUM), - .flags = IORESOURCE_IRQ - } -}; - -static struct cf_device cf_slot0_data = { - .gpio_pin = 13 -}; - -static struct platform_device cf_slot0 = { - .id = 0, - .name = "rb500-cf", - .dev.platform_data = &cf_slot0_data, - .resource = cf_slot0_res, - .num_resources = ARRAY_SIZE(cf_slot0_res), -}; - -/* Resources and device for NAND. There is no data needed and no irqs, so just define the memory used. */ - -/* - * We need to use the OLD Yaffs-1 OOB layout, otherwise the RB bootloader - * will not be able to find the kernel that we load. So set the oobinfo - * when creating the partitions - */ -static struct nand_ecclayout rb500_nand_ecclayout = { - .eccbytes = 6, - .eccpos = { 8, 9, 10, 13, 14, 15 }, - .oobavail = 9, - .oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } } -}; - -int rb500_dev_ready(struct mtd_info *mtd) -{ - return MEM32(IDT434_REG_BASE + GPIOD) & GPIO_RDY; -} - -void rb500_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - unsigned char orbits, nandbits; - - if (ctrl & NAND_CTRL_CHANGE) { - - orbits = (ctrl & NAND_CLE) << 1; - orbits |= (ctrl & NAND_ALE) >> 1; - - nandbits = (~ctrl & NAND_CLE) << 1; - nandbits |= (~ctrl & NAND_ALE) >> 1; - - changeLatchU5(orbits, nandbits); - } - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -static struct resource nand_slot0_res[] = { - [0] = { - .name = "nand_membase", - .flags = IORESOURCE_MEM - } -}; - -struct platform_nand_data rb500_nand_data = { - .ctrl.dev_ready = rb500_dev_ready, - .ctrl.cmd_ctrl = rb500_cmd_ctrl, -}; - -static struct platform_device nand_slot0 = { - .name = "gen_nand", - .id = -1, - .resource = nand_slot0_res, - .num_resources = ARRAY_SIZE(nand_slot0_res), - .dev.platform_data = &rb500_nand_data, -}; - -static struct mtd_partition rb500_partition_info[] = { - { - .name = "Routerboard NAND boot", - .offset = 0, - .size = 4 * 1024 * 1024, - }, { - .name = "rootfs", - .offset = MTDPART_OFS_NXTBLK, - .size = MTDPART_SIZ_FULL, - } -}; - -static struct platform_device rb500_led = { - .name = "rb500-led", - .id = 0, -}; - -static struct gpio_keys_button rb500_gpio_btn[] = { - { - .gpio = 1, - .code = BTN_0, - .desc = "S1", - .active_low = 1, - } -}; - -static struct gpio_keys_platform_data rb500_gpio_btn_data = { - .buttons = rb500_gpio_btn, - .nbuttons = ARRAY_SIZE(rb500_gpio_btn), -}; - -static struct platform_device rb500_button = { - .name = "gpio-keys", - .id = -1, - .dev = { - .platform_data = &rb500_gpio_btn_data, - } -}; - -static struct platform_device *rb500_devs[] = { - &korina_dev0, - &nand_slot0, - &cf_slot0, - &rb500_led, - &rb500_button -}; - -static void __init parse_mac_addr(char *macstr) -{ - int i, j; - unsigned char result, value; - - for (i = 0; i < 6; i++) { - result = 0; - - if (i != 5 && *(macstr + 2) != ':') - return; - - for (j = 0; j < 2; j++) { - if (isxdigit(*macstr) - && (value = - isdigit(*macstr) ? *macstr - - '0' : toupper(*macstr) - 'A' + 10) < 16) { - result = result * 16 + value; - macstr++; - } else - return; - } - - macstr++; - korina_dev0_data.mac[i] = result; - } -} - - -/* DEVICE CONTROLLER 1 */ -#define CFG_DC_DEV1 (void*)0xb8010010 -#define CFG_DC_DEV2 (void*)0xb8010020 -#define CFG_DC_DEVBASE 0x0 -#define CFG_DC_DEVMASK 0x4 -#define CFG_DC_DEVC 0x8 -#define CFG_DC_DEVTC 0xC - -/* NAND definitions */ -#define NAND_CHIP_DELAY 25 - -static int rb500_nand_fixup(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - - if (mtd->writesize == 512) - chip->ecc.layout = &rb500_nand_ecclayout; - - return 0; -} - -static void __init rb500_nand_setup(void) -{ - switch (mips_machtype) { - case MACH_MIKROTIK_RB532A: - changeLatchU5(LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE | LO_WPX); - break; - default: - changeLatchU5(LO_WPX | LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE); - break; - } - - /* Setup NAND specific settings */ - rb500_nand_data.chip.nr_chips = 1; - rb500_nand_data.chip.nr_partitions = ARRAY_SIZE(rb500_partition_info); - rb500_nand_data.chip.partitions = rb500_partition_info; - rb500_nand_data.chip.chip_delay = NAND_CHIP_DELAY; - rb500_nand_data.chip.options = NAND_NO_AUTOINCR; - - rb500_nand_data.chip.chip_fixup = &rb500_nand_fixup; -} - - -static int __init plat_setup_devices(void) -{ - /* Look for the CF card reader */ - if (!readl(CFG_DC_DEV1 + CFG_DC_DEVMASK)) - rb500_devs[1] = NULL; - else { - cf_slot0_res[0].start = - readl(CFG_DC_DEV1 + CFG_DC_DEVBASE); - cf_slot0_res[0].end = cf_slot0_res[0].start + 0x1000; - } - - /* Read the NAND resources from the device controller */ - nand_slot0_res[0].start = readl(CFG_DC_DEV2 + CFG_DC_DEVBASE); - nand_slot0_res[0].end = nand_slot0_res[0].start + 0x1000; - - /* Initialise the NAND device */ - rb500_nand_setup(); - - return platform_add_devices(rb500_devs, ARRAY_SIZE(rb500_devs)); -} - -static int __init setup_kmac(char *s) -{ - printk("korina mac = %s\n", s); - parse_mac_addr(s); - return 0; -} - -__setup("kmac=", setup_kmac); - -arch_initcall(plat_setup_devices); diff --git a/target/linux/rb532/files/arch/mips/rb500/gpio.c b/target/linux/rb532/files/arch/mips/rb500/gpio.c deleted file mode 100644 index 6c92a8fe56..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/gpio.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Miscellaneous functions for IDT EB434 board - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * Copyright 2006 Phil Sutter - * Copyright 2007 Florian Fainelli - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define GPIO_BADDR 0x18050000 - -static volatile unsigned char *devCtl3Base; -static unsigned char latchU5State; -static spinlock_t clu5Lock = SPIN_LOCK_UNLOCKED; - -struct rb500_gpio_reg __iomem *rb500_gpio_reg0; -EXPORT_SYMBOL(rb500_gpio_reg0); - -static struct resource rb500_gpio_reg0_res[] = { - { - .name = "gpio_reg0", - .start = GPIO_BADDR, - .end = GPIO_BADDR + sizeof(struct rb500_gpio_reg), - .flags = IORESOURCE_MEM, - } -}; - -void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val) -{ - unsigned flags, data; - unsigned i = 0; - - spin_lock_irqsave(&clu5Lock, flags); - data = *(volatile unsigned *) (IDT434_REG_BASE + regOffs); - for (i = 0; i != len; ++i) { - if (val & (1 << i)) - data |= (1 << (i + bit)); - else - data &= ~(1 << (i + bit)); - } - *(volatile unsigned *) (IDT434_REG_BASE + regOffs) = data; - spin_unlock_irqrestore(&clu5Lock, flags); -} -EXPORT_SYMBOL(set434Reg); - -void changeLatchU5(unsigned char orMask, unsigned char nandMask) -{ - unsigned flags; - - spin_lock_irqsave(&clu5Lock, flags); - latchU5State = (latchU5State | orMask) & ~nandMask; - if (!devCtl3Base) - devCtl3Base = (volatile unsigned char *) - KSEG1ADDR(*(volatile unsigned *) - KSEG1ADDR(0x18010030)); - *devCtl3Base = latchU5State; - spin_unlock_irqrestore(&clu5Lock, flags); -} -EXPORT_SYMBOL(changeLatchU5); - -unsigned char getLatchU5State(void) -{ - return latchU5State; -} -EXPORT_SYMBOL(getLatchU5State); - -int rb500_gpio_get_value(unsigned gpio) -{ - return readl(&rb500_gpio_reg0->gpiod) & (1 << gpio); -} -EXPORT_SYMBOL(rb500_gpio_get_value); - -void rb500_gpio_set_value(unsigned gpio, int value) -{ - unsigned tmp; - - tmp = readl(&rb500_gpio_reg0->gpiod) & ~(1 << gpio); - if (value) - tmp |= 1 << gpio; - - writel(tmp, (void *)&rb500_gpio_reg0->gpiod); -} -EXPORT_SYMBOL(rb500_gpio_set_value); - -int rb500_gpio_direction_input(unsigned gpio) -{ - writel(readl(&rb500_gpio_reg0->gpiocfg) & ~(1 << gpio), (void *)&rb500_gpio_reg0->gpiocfg); - - return 0; -} -EXPORT_SYMBOL(rb500_gpio_direction_input); - -int rb500_gpio_direction_output(unsigned gpio, int value) -{ - gpio_set_value(gpio, value); - writel(readl(&rb500_gpio_reg0->gpiocfg) | (1 << gpio), (void *)&rb500_gpio_reg0->gpiocfg); - - return 0; -} -EXPORT_SYMBOL(rb500_gpio_direction_output); - -void rb500_gpio_set_int_level(unsigned gpio, int value) -{ - unsigned tmp; - - tmp = readl(&rb500_gpio_reg0->gpioilevel) & ~(1 << gpio); - if (value) - tmp |= 1 << gpio; - writel(tmp, (void *)&rb500_gpio_reg0->gpioilevel); -} -EXPORT_SYMBOL(rb500_gpio_set_int_level); - -int rb500_gpio_get_int_level(unsigned gpio) -{ - return readl(&rb500_gpio_reg0->gpioilevel) & (1 << gpio); -} -EXPORT_SYMBOL(rb500_gpio_get_int_level); - -void rb500_gpio_set_int_status(unsigned gpio, int value) -{ - unsigned tmp; - - tmp = readl(&rb500_gpio_reg0->gpioistat); - if (value) - tmp |= 1 << gpio; - writel(tmp, (void *)&rb500_gpio_reg0->gpioistat); -} -EXPORT_SYMBOL(rb500_gpio_set_int_status); - -int rb500_gpio_get_int_status(unsigned gpio) -{ - return readl(&rb500_gpio_reg0->gpioistat) & (1 << gpio); -} -EXPORT_SYMBOL(rb500_gpio_get_int_status); - -void rb500_gpio_set_func(unsigned gpio, int value) -{ - unsigned tmp; - - tmp = readl(&rb500_gpio_reg0->gpiofunc); - if (value) - tmp |= 1 << gpio; - writel(tmp, (void *)&rb500_gpio_reg0->gpiofunc); -} -EXPORT_SYMBOL(rb500_gpio_set_func); - -int rb500_gpio_get_func(unsigned gpio) -{ - return readl(&rb500_gpio_reg0->gpiofunc) & (1 << gpio); -} -EXPORT_SYMBOL(rb500_gpio_get_func); - -int __init rb500_gpio_init(void) -{ - rb500_gpio_reg0 = ioremap_nocache(rb500_gpio_reg0_res[0].start, - rb500_gpio_reg0_res[0].end - - rb500_gpio_reg0_res[0].start); - - if (!rb500_gpio_reg0) { - printk(KERN_ERR "rb500: cannot remap GPIO register 0\n"); - return -ENXIO; - } - - return 0; -} -arch_initcall(rb500_gpio_init); diff --git a/target/linux/rb532/files/arch/mips/rb500/irq.c b/target/linux/rb532/files/arch/mips/rb500/irq.c deleted file mode 100644 index 3f3e900c9c..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/irq.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * RC32434 interrupt routines. - * - * Copyright 2002 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * stevel@mvista.com or source@mvista.com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern void set_debug_traps(void); -extern irq_cpustat_t irq_stat [NR_CPUS]; -unsigned int local_bh_count[NR_CPUS]; -unsigned int local_irq_count[NR_CPUS]; - -static unsigned int startup_irq(unsigned int irq); -static void rb500_end_irq(unsigned int irq_nr); -static void mask_and_ack_irq(unsigned int irq_nr); -static void rb500_enable_irq(unsigned int irq_nr); -static void rb500_disable_irq(unsigned int irq_nr); - -extern void __init init_generic_irq(void); -extern struct rb500_gpio_reg __iomem *rb500_gpio_reg0; - -typedef struct { - u32 mask; /* mask of valid bits in pending/mask registers */ - volatile u32 *base_addr; -} intr_group_t; - -#define RC32434_NR_IRQS (GROUP4_IRQ_BASE + 32) - -#if (NR_IRQS < RC32434_NR_IRQS) -#error Too little irqs defined. Did you override ? -#endif - -static const intr_group_t intr_group[NUM_INTR_GROUPS] = { - { 0x0000efff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 0 * IC_GROUP_OFFSET) }, - { 0x00001fff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 1 * IC_GROUP_OFFSET) }, - { 0x00000007, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 2 * IC_GROUP_OFFSET) }, - { 0x0003ffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 3 * IC_GROUP_OFFSET) }, - { 0xffffffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 4 * IC_GROUP_OFFSET) } -}; - -#define READ_PEND(base) (*(base)) -#define READ_MASK(base) (*(base + 2)) -#define WRITE_MASK(base, val) (*(base + 2) = (val)) - -static inline int irq_to_group(unsigned int irq_nr) -{ - return ((irq_nr - GROUP0_IRQ_BASE) >> 5); -} - -static inline int group_to_ip(unsigned int group) -{ - return group + 2; -} - -static inline void enable_local_irq(unsigned int ip) -{ - int ipnum = 0x100 << ip; - clear_c0_cause(ipnum); - set_c0_status(ipnum); -} - -static inline void disable_local_irq(unsigned int ip) -{ - int ipnum = 0x100 << ip; - clear_c0_status(ipnum); -} - -static inline void ack_local_irq(unsigned int ip) -{ - int ipnum = 0x100 << ip; - clear_c0_cause(ipnum); -} - -static void rb500_enable_irq(unsigned int irq_nr) -{ - int ip = irq_nr - GROUP0_IRQ_BASE; - unsigned int group, intr_bit; - volatile unsigned int *addr; - - - if (ip < 0) - enable_local_irq(irq_nr); - else { - group = ip >> 5; - - ip &= (1<<5)-1; - intr_bit = 1 << ip; - - enable_local_irq(group_to_ip(group)); - - addr = intr_group[group].base_addr; - WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit); - } -} - -static void rb500_disable_irq(unsigned int irq_nr) -{ - int ip = irq_nr - GROUP0_IRQ_BASE; - unsigned int group, intr_bit, mask; - volatile unsigned int *addr; - - if (ip < 0) { - disable_local_irq(irq_nr); - }else{ - group = ip >> 5; - - ip &= (1<<5) -1; - intr_bit = 1 << ip; - addr = intr_group[group].base_addr; - mask = READ_MASK(addr); - mask |= intr_bit; - WRITE_MASK(addr,mask); - - /* - * if there are no more interrupts enabled in this - * group, disable corresponding IP - */ - if (mask == intr_group[group].mask) - disable_local_irq(group_to_ip(group)); - } -} - -static unsigned int startup_irq(unsigned int irq_nr) -{ - rb500_enable_irq(irq_nr); - return 0; -} - -static void shutdown_irq(unsigned int irq_nr) -{ - rb500_disable_irq(irq_nr); - return; -} - -static void mask_and_ack_irq(unsigned int irq_nr) -{ - rb500_disable_irq(irq_nr); - ack_local_irq(group_to_ip(irq_to_group(irq_nr))); -} - -static void rb500_end_irq(unsigned int irq_nr) -{ - - int ip = irq_nr - GROUP0_IRQ_BASE; - unsigned int intr_bit, group; - volatile unsigned int *addr; - - if ((irq_desc[irq_nr].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { - printk("warning: end_irq %d did not enable (%x)\n", - irq_nr, irq_desc[irq_nr].status); - return; - } - - if (ip < 0) { - enable_local_irq(irq_nr); - } else { - group = ip >> 5; - - ip &= (1 << 5) - 1; - intr_bit = 1 << ip; - - if (irq_nr >= GROUP4_IRQ_BASE && irq_nr <= (GROUP4_IRQ_BASE + 13)) { - rb500_gpio_reg0->gpioistat = rb500_gpio_reg0->gpioistat & ~intr_bit; - } - - enable_local_irq(group_to_ip(group)); - - addr = intr_group[group].base_addr; - WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit); - } -} - -static struct hw_interrupt_type rc32434_irq_type = { - .typename = "RB500", - .startup = startup_irq, - .shutdown = shutdown_irq, - .enable = rb500_enable_irq, - .disable = rb500_disable_irq, - .ack = mask_and_ack_irq, - .end = rb500_end_irq, -}; - - -void __init arch_init_irq(void) -{ - int i; - - printk("Initializing IRQ's: %d out of %d\n", RC32434_NR_IRQS, NR_IRQS); - memset(irq_desc, 0, sizeof(irq_desc)); - - for (i = 0; i < RC32434_NR_IRQS; i++) { - irq_desc[i].status = IRQ_DISABLED; - irq_desc[i].action = NULL; - irq_desc[i].depth = 1; - irq_desc[i].chip = &rc32434_irq_type; - spin_lock_init(&irq_desc[i].lock); - } -} - -/* Main Interrupt dispatcher */ -asmlinkage void plat_irq_dispatch(void) -{ - unsigned int ip, pend, group; - volatile unsigned int *addr; - unsigned int cp0_cause = read_c0_cause() & read_c0_status(); - - if (cp0_cause & CAUSEF_IP7) { - do_IRQ(7); - } else if ((ip = (cp0_cause & 0x7c00))) { - group = 21 - rc32434_clz(ip); - - addr = intr_group[group].base_addr; - - pend = READ_PEND(addr); - pend &= ~READ_MASK(addr); // only unmasked interrupts - pend = 39 - rc32434_clz(pend); - do_IRQ((group << 5) + pend); - } -} diff --git a/target/linux/rb532/files/arch/mips/rb500/prom.c b/target/linux/rb532/files/arch/mips/rb500/prom.c deleted file mode 100644 index 2d03b57f90..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/prom.c +++ /dev/null @@ -1,177 +0,0 @@ -/* -* prom.c -********************************************************************** -* P . Sadik Oct 10, 2003 -* -* Started change log -* idt_cpu_freq is make a kernel configuration parameter -* idt_cpu_freq is exported so that other modules can use it. -* Code cleanup -********************************************************************** -* P. Sadik Oct 20, 2003 -* -* Removed NVRAM code from here, since they are already available under -* nvram directory. -* Added serial port initialisation. -********************************************************************** -********************************************************************** -* P. Sadik Oct 30, 2003 -* -* Added reset_cons_port -********************************************************************** - - P.Christeas, 2005-2006 - Port to 2.6, add 2.6 cmdline parsing - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PROM_ENTRY(x) (0xbfc00000+((x)*8)) -extern void __init setup_serial_port(void); - -unsigned int idt_cpu_freq = 132000000; -EXPORT_SYMBOL(idt_cpu_freq); -unsigned int gpio_bootup_state = 0; -EXPORT_SYMBOL(gpio_bootup_state); - -char mips_mac_address[18] = "08:00:06:05:40:01"; -EXPORT_SYMBOL(mips_mac_address); - -/* what to append to cmdline when button is [not] pressed */ -#define GPIO_INIT_NOBUTTON "" -#define GPIO_INIT_BUTTON " 2" - -#ifdef CONFIG_MIKROTIK_RB500 -unsigned soft_reboot = 0; -EXPORT_SYMBOL(soft_reboot); -#endif - -#define SR_NMI 0x00180000 /* NMI */ -#define SERIAL_SPEED_ENTRY 0x00000001 - -#ifdef CONFIG_REMOTE_DEBUG -extern int remote_debug; -#endif - -#define FREQ_TAG "HZ=" -#define GPIO_TAG "gpio=" -#define KMAC_TAG "kmac=" -#define MEM_TAG "mem=" -#define BOARD_TAG "board=" -#define IGNORE_CMDLINE_MEM 1 -#define DEBUG_DDR - -#define BOARD_RB532 "500" -#define BOARD_RB532A "500r5" - -void parse_soft_settings(unsigned *ptr, unsigned size); -void parse_hard_settings(unsigned *ptr, unsigned size); - -void __init prom_setup_cmdline(void); - -void __init prom_init(void) -{ - DDR_t ddr = (DDR_t) DDR_VirtualAddress; /* define the pointer to the DDR registers */ - phys_t memsize = 0-ddr->ddrmask; - - /* this should be the very first message, even before serial is properly initialized */ - prom_setup_cmdline(); - setup_serial_port(); - - soft_reboot = read_c0_status() & SR_NMI; - pm_power_off = NULL; - - /* - * give all RAM to boot allocator, - * except for the first 0x400 and the last 0x200 bytes - */ - add_memory_region(ddr->ddrbase + 0x400, memsize - 0x600, BOOT_MEM_RAM); -} - -void __init prom_free_prom_memory(void) -{ - /* No prom memory to free */ -} - -static inline int match_tag(char *arg, const char *tag) -{ - return (strncmp(arg, tag, strlen(tag)) == 0); -} - -static inline unsigned long tag2ul(char *arg, const char *tag) -{ - char *num = arg+strlen(tag); - return simple_strtoul(num, 0, 10); -} - -extern char _image_cmdline; -void __init prom_setup_cmdline(void){ - char cmd_line[CL_SIZE]; - char *cp; - int prom_argc; - char **prom_argv, **prom_envp; - int i; - - prom_argc = fw_arg0; - prom_argv = (char **) fw_arg1; - prom_envp = (char **) fw_arg2; - - cp=cmd_line; - /* Note: it is common that parameters start at argv[1] and not argv[0], - however, our elf loader starts at [0] */ - for(i=0;i0) *(cp++) = ' '; - if (match_tag(prom_argv[i], BOARD_TAG)) { - char *board = prom_argv[i] + strlen(BOARD_TAG); - if (match_tag(board, BOARD_RB532A)) - mips_machtype = MACH_MIKROTIK_RB532A; - else - mips_machtype = MACH_MIKROTIK_RB532; - } - - if (match_tag(prom_argv[i], GPIO_TAG)) { - gpio_bootup_state = tag2ul(prom_argv[i], GPIO_TAG); - } - strcpy(cp,prom_argv[i]); - cp+=strlen(prom_argv[i]); - } - *(cp++) = ' '; - strcpy(cp,(&_image_cmdline + 8)); - cp += strlen(&_image_cmdline); - - i=strlen(arcs_cmdline); - if (i>0){ - *(cp++) = ' '; - strcpy(cp,arcs_cmdline); - cp+=strlen(arcs_cmdline); - } - if (gpio_bootup_state&0x02) - strcpy(cp,GPIO_INIT_NOBUTTON); - else - strcpy(cp,GPIO_INIT_BUTTON); - cmd_line[CL_SIZE-1] = '\0'; - - strcpy(arcs_cmdline,cmd_line); -} - diff --git a/target/linux/rb532/files/arch/mips/rb500/serial.c b/target/linux/rb532/files/arch/mips/rb500/serial.c deleted file mode 100644 index 25a03e0108..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/serial.c +++ /dev/null @@ -1,78 +0,0 @@ -/************************************************************************** - * - * BRIEF MODULE DESCRIPTION - * Serial port initialisation. - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - ************************************************************************** - * May 2004 rkt, neb - * - * Initial Release - * - * - * - ************************************************************************** - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -extern unsigned int idt_cpu_freq; - -static struct uart_port serial_req = { - .type = PORT_16550A, - .line = 0, - .irq = RC32434_UART0_IRQ, - //.flags = STD_COM_FLAGS, - .iotype = UPIO_MEM, - .membase = (char *) KSEG1ADDR(RC32434_UART0_BASE), -// .fifosize = 14 - .regshift = 2 -}; - -int __init setup_serial_port(void) -{ - serial_req.uartclk = idt_cpu_freq; - - if (early_serial_setup(&serial_req)) - return -ENODEV; - - return(0); -} diff --git a/target/linux/rb532/files/arch/mips/rb500/setup.c b/target/linux/rb532/files/arch/mips/rb500/setup.c deleted file mode 100644 index 0da7e63409..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/setup.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * setup.c - boot time setup code - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for KSEG1ADDR() */ -#include -#include -#include -#include - -#ifdef CONFIG_PCI -extern void rc32434_time_init(void); -extern int __init rc32434_pcibridge_init(void); -#endif - -#define epldMask ((volatile unsigned char *)0xB900000d) - -static void rb_machine_restart(char *command) -{ - /* just jump to the reset vector */ - * (volatile unsigned *) KSEG1ADDR(0x18008000) = 0x80000001; - ((void (*)(void))KSEG1ADDR(0x1FC00000u))(); -} - -static void rb_machine_halt(void) -{ - for(;;) continue; -} - -#ifdef CONFIG_CPU_HAS_WB -void (*__wbflush) (void); - -static void rb_write_buffer_flush(void) -{ - __asm__ __volatile__ - ("sync\n\t" "nop\n\t" "loop: bc0f loop\n\t" "nop\n\t"); -} -#endif - -void __init plat_mem_setup(void) -{ - unsigned int pciCntlVal; - - //board_time_init = rc32434_time_init; - -#ifdef CONFIG_CPU_HAS_WB - __wbflush = rb_write_buffer_flush; -#endif - _machine_restart = rb_machine_restart; - _machine_halt = rb_machine_halt; - /*_machine_power_off = rb_machine_power_halt;*/ - pm_power_off = rb_machine_halt; - - set_io_port_base(KSEG1); - - pciCntlVal=rc32434_pci->pcic; - pciCntlVal &= 0xFFFFFF7; - rc32434_pci->pcic = pciCntlVal; - -#ifdef CONFIG_PCI - /* Enable PCI interrupts in EPLD Mask register */ - *epldMask = 0x0; - *(epldMask + 1) = 0x0; -#endif - write_c0_wired(0); -} - -const char *get_system_type(void) -{ - return "MIPS RB500"; -} diff --git a/target/linux/rb532/files/arch/mips/rb500/time.c b/target/linux/rb532/files/arch/mips/rb500/time.c deleted file mode 100644 index db85849062..0000000000 --- a/target/linux/rb532/files/arch/mips/rb500/time.c +++ /dev/null @@ -1,82 +0,0 @@ -/* -**************************************************************************** -* Carsten Langgaard, carstenl@mips.com -* Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. -* -*************************************************************************** -* -* This program is free software; you can distribute it and/or modify it -* under the terms of the GNU General Public License (Version 2) as -* published by the Free Software Foundation. -* -* This program is distributed in the hope it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -* for more details. -* -* You should have received a copy of the GNU General Public License along -* with this program; if not, write to the Free Software Foundation, Inc., -* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. -* -**************************************************************************** -* -* Setting up the clock on the MIPS boards. -* -**************************************************************************** -* P. Sadik Oct 10, 2003 -* -* Started change log. -* mips_counter_frequency is now calculated at run time, based on idt_cpu_freq. -* Code cleanup -**************************************************************************** -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static unsigned long r4k_offset; /* Amount to incr compare reg each time */ -extern unsigned int mips_hpt_frequency; -extern unsigned int idt_cpu_freq; - -/* - * Figure out the r4k offset, the amount to increment the compare - * register for each time tick. There is no RTC available. - * - * The RC32434 counts at half the CPU *core* speed. - */ -static unsigned long __init cal_r4koff(void) -{ - mips_hpt_frequency = idt_cpu_freq * IDT_CLOCK_MULT / 2; - return (mips_hpt_frequency / HZ); -} - - -void __init plat_time_init(void) -{ - unsigned int est_freq, flags; - - local_irq_save(flags); - - printk("calculating r4koff... "); - r4k_offset = cal_r4koff(); - printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset); - - est_freq = 2*r4k_offset*HZ; - est_freq += 5000; /* round */ - est_freq -= est_freq%10000; - printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, - (est_freq%1000000)*100/1000000); - local_irq_restore(flags); -} diff --git a/target/linux/rb532/files/drivers/block/rb500/Makefile b/target/linux/rb532/files/drivers/block/rb500/Makefile deleted file mode 100644 index 3e14dc35bd..0000000000 --- a/target/linux/rb532/files/drivers/block/rb500/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -## Makefile for the RB532 CF port - -obj-y += bdev.o ata.o diff --git a/target/linux/rb532/files/drivers/block/rb500/ata.c b/target/linux/rb532/files/drivers/block/rb500/ata.c deleted file mode 100644 index 31e6782fa4..0000000000 --- a/target/linux/rb532/files/drivers/block/rb500/ata.c +++ /dev/null @@ -1,487 +0,0 @@ -/* CF-mips driver - This is a block driver for the direct (mmaped) interface to the CF-slot, - found in Routerboard.com's RB532 board - See SDK provided from routerboard.com. - - Module adapted By P.Christeas , 2005-6. - Cleaned up and adapted to platform_device by Felix Fietkau - - This work is redistributed under the terms of the GNU General Public License. -*/ - -#include /* printk() */ -#include /* module to be loadable */ -#include -#include -#include -#include /* request_mem_region() */ - -#include -#include /* ioremap() */ -#include /* ioremap() */ -#include - -#include "ata.h" - -#define REQUEST_MEM_REGION 0 -#define DEBUG 1 - -#if DEBUG -#define DEBUGP printk -#else -#define DEBUGP(format, args...) -#endif - -#define SECS 1000000 /* unit for wait_not_busy() is 1us */ - -unsigned cf_head = 0; -unsigned cf_cyl = 0; -unsigned cf_spt = 0; -unsigned cf_sectors = 0; -static unsigned cf_block_size = 1; -static void *baddr = 0; - -#define DBUF32 ((volatile u32 *)((unsigned long)dev->baddr | ATA_DBUF_OFFSET)) - - -static void cf_do_tasklet(unsigned long dev_l); - - -static inline void wareg(u8 val, unsigned reg, struct cf_mips_dev* dev) -{ - writeb(val, dev->baddr + ATA_REG_OFFSET + reg); -} - -static inline u8 rareg(unsigned reg, struct cf_mips_dev* dev) -{ - return readb(dev->baddr + ATA_REG_OFFSET + reg); -} - -static inline int cfrdy(struct cf_mips_dev *dev) -{ - return gpio_get_value(dev->pin); -} - -static inline void prepare_cf_irq(struct cf_mips_dev *dev) -{ - rb500_gpio_set_int_level(1, dev->pin); /* interrupt on cf ready (not busy) */ - rb500_gpio_set_int_status(0, dev->pin); /* clear interrupt status */ -} - -static inline int cf_present(struct cf_mips_dev* dev) -{ - /* TODO: read and configure CIS into memory mapped mode - * TODO: parse CISTPL_CONFIG on CF+ cards to get base address (0x200) - * TODO: maybe adjust power saving setting for Hitachi Microdrive - */ - int i; - - /* setup CFRDY GPIO as input */ - rb500_gpio_set_func(dev->pin, 0); - gpio_direction_input(dev->pin); - - for (i = 0; i < 0x10; ++i) { - if (rareg(i,dev) != 0xff) - return 1; - } - return 0; -} - -static inline int is_busy(struct cf_mips_dev *dev) -{ - return !cfrdy(dev); -} - -static int wait_not_busy(int to_us, int wait_for_busy,struct cf_mips_dev *dev) -{ - int us_passed = 0; - if (wait_for_busy && !is_busy(dev)) { - /* busy must appear within 400ns, - * but it may dissapear before we see it - * => must not wait for busy in a loop - */ - ndelay(400); - } - - do { - if (us_passed) - udelay(1); /* never reached in async mode */ - if (!is_busy(dev)) { - if (us_passed > 1 * SECS) { - printk(KERN_WARNING "cf-mips: not busy ok (after %dus)" - ", status 0x%02x\n", us_passed, (unsigned) rareg(ATA_REG_ST,dev)); - } - return CF_TRANS_OK; - } - if (us_passed == 1 * SECS) { - printk(KERN_WARNING "cf-mips: wait not busy %dus..\n", to_us); - } - if (dev->async_mode) { - dev->to_timer.expires = jiffies + (to_us * HZ / SECS); - dev->irq_enable_time = jiffies; - prepare_cf_irq(dev); - if (is_busy(dev)) { - add_timer(&dev->to_timer); - enable_irq(dev->irq); - return CF_TRANS_IN_PROGRESS; - } - continue; - } - ++us_passed; - } while (us_passed < to_us); - - printk(KERN_ERR "cf-mips: wait not busy timeout (%dus)" - ", status 0x%02x, state %d\n", - to_us, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate); - return CF_TRANS_FAILED; -} - -static irqreturn_t cf_irq_handler(int irq, void *dev_id) -{ - /* While tasklet has not disabled irq, irq will be retried all the time - * because of ILEVEL matching GPIO pin status => deadlock. - * To avoid this, we change ILEVEL to 0. - */ - struct cf_mips_dev *dev=dev_id; - - rb500_gpio_set_int_level(0, dev->pin); - rb500_gpio_set_int_status(0, dev->pin); - - del_timer(&dev->to_timer); - tasklet_schedule(&dev->tasklet); - return IRQ_HANDLED; -} - -static int do_reset(struct cf_mips_dev *dev) -{ - printk(KERN_INFO "cf-mips: resetting..\n"); - - wareg(ATA_REG_DC_SRST, ATA_REG_DC,dev); - udelay(1); /* FIXME: how long should we wait here? */ - wareg(0, ATA_REG_DC,dev); - - return wait_not_busy(30 * SECS, 1,dev); -} - -static int set_multiple(struct cf_mips_dev *dev) -{ - if (dev->block_size <= 1) - return CF_TRANS_OK; - - wareg(dev->block_size, ATA_REG_SC,dev); - wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev); - wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev); - - return wait_not_busy(10 * SECS, 1,dev); -} - -static int set_cmd(struct cf_mips_dev *dev) -{ - //DEBUGP(KERN_INFO "cf-mips: ata cmd 0x%02x\n", dev->tcmd); - // sector_count should be <=24 bits.. - BUG_ON(dev->tsect_start>=0x10000000); - // This way, it addresses 2^24 * 512 = 128G - - if (dev->tsector_count) { - wareg(dev->tsector_count & 0xff, ATA_REG_SC,dev); - wareg(dev->tsect_start & 0xff, ATA_REG_SN,dev); - wareg((dev->tsect_start >> 8) & 0xff, ATA_REG_CL,dev); - wareg((dev->tsect_start >> 16) & 0xff, ATA_REG_CH,dev); - } - wareg(((dev->tsect_start >> 24) & 0x0f) | ATA_REG_DH_BASE | ATA_REG_DH_LBA, - ATA_REG_DH,dev); /* select drive on all commands */ - wareg(dev->tcmd, ATA_REG_CMD,dev); - return wait_not_busy(10 * SECS, 1,dev); -} - -static int do_trans(struct cf_mips_dev *dev) -{ - int res; - unsigned st; - int transfered; - - //printk("do_trans: %d sectors left\n",dev->tsectors_left); - while (dev->tsectors_left) { - transfered = 0; - - st = rareg(ATA_REG_ST,dev); - if (!(st & ATA_REG_ST_DRQ)) { - printk(KERN_ERR "cf-mips: do_trans without DRQ (status 0x%x)!\n", st); - if (st & ATA_REG_ST_ERR) { - int errId = rareg(ATA_REG_ERR,dev); - printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n", - (dev->tread ? "read" : "write"), st, errId); - } - return CF_TRANS_FAILED; - } - do { /* Fill/read the buffer one block */ - u32 *qbuf, *qend; - qbuf = (u32 *)dev->tbuf; - qend = qbuf + CF_SECT_SIZE / sizeof(u32); - if (dev->tread) { - while (qbuf!=qend) - put_unaligned(*DBUF32,qbuf++); - //*(qbuf++) = *DBUF32; - } - else { - while(qbuf!=qend) - *DBUF32 = get_unaligned(qbuf++); - } - - dev->tsectors_left--; - dev->tbuf += CF_SECT_SIZE; - dev->tbuf_size -= CF_SECT_SIZE; - transfered++; - } while (transfered != dev->block_size && dev->tsectors_left > 0); - - res = wait_not_busy(10 * SECS, 1,dev); - if (res != CF_TRANS_OK) - return res; - }; - - st = rareg(ATA_REG_ST,dev); - if (st & (ATA_REG_ST_DRQ | ATA_REG_ST_DWF | ATA_REG_ST_ERR)) { - if (st & ATA_REG_ST_DRQ) { - printk(KERN_ERR "cf-mips: DRQ after all %d sectors are %s" - ", status 0x%x\n", dev->tsector_count, (dev->tread ? "read" : "written"), st); - } else if (st & ATA_REG_ST_DWF) { - printk(KERN_ERR "cf-mips: write fault, status 0x%x\n", st); - } else { - int errId = rareg(ATA_REG_ERR,dev); - printk(KERN_ERR "cf-mips: %s error, status 0x%x, errid 0x%x\n", - (dev->tread ? "read" : "write"), st, errId); - } - return CF_TRANS_FAILED; - } - return CF_TRANS_OK; -} - -static int cf_do_state(struct cf_mips_dev *dev) -{ - int res; - switch (dev->tstate) { /* fall through everywhere */ - case TS_IDLE: - dev->tstate = TS_READY; - if (is_busy(dev)) { - dev->tstate = TS_AFTER_RESET; - res = do_reset(dev); - if (res != CF_TRANS_OK) - break; - } - case TS_AFTER_RESET: - if (dev->tstate == TS_AFTER_RESET) { - dev->tstate = TS_READY; - res = set_multiple(dev); - if (res != CF_TRANS_OK) - break; - } - case TS_READY: - dev->tstate = TS_CMD; - res = set_cmd(dev); - if (res != CF_TRANS_OK) - break;; - case TS_CMD: - dev->tstate = TS_TRANS; - case TS_TRANS: - res = do_trans(dev); - break; - default: - printk(KERN_ERR "cf-mips: BUG: unknown tstate %d\n", dev->tstate); - return CF_TRANS_FAILED; - } - if (res != CF_TRANS_IN_PROGRESS) - dev->tstate = TS_IDLE; - return res; -} - -static void cf_do_tasklet(unsigned long dev_l) -{ - struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l; - int res; - - disable_irq(dev->irq); - - if (dev->tstate == TS_IDLE) - return; /* can happen when irq is first registered */ - -#if 0 - DEBUGP(KERN_WARNING "cf-mips: not busy ok (tasklet) status 0x%02x\n", - (unsigned) rareg(ATA_REG_ST,dev)); -#endif - - res = cf_do_state(dev); - if (res == CF_TRANS_IN_PROGRESS) - return; - cf_async_trans_done(dev,res); -} - -static void cf_async_timeout(unsigned long dev_l) -{ - struct cf_mips_dev* dev=(struct cf_mips_dev*) dev_l; - disable_irq(dev->irq); - /* Perhaps send abort to the device? */ - printk(KERN_ERR "cf-mips: wait not busy timeout (%lus)" - ", status 0x%02x, state %d\n", - jiffies - dev->irq_enable_time, (unsigned) rareg(ATA_REG_ST,dev), dev->tstate); - dev->tstate = TS_IDLE; - cf_async_trans_done(dev,CF_TRANS_FAILED); -} - -int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, - char* buffer, int is_write) -{ - BUG_ON(dev->tstate!=TS_IDLE); - if (nsect > ATA_MAX_SECT_PER_CMD) { - printk(KERN_WARNING "cf-mips: sector count %lu out of range\n",nsect); - return CF_TRANS_FAILED; - } - if (sector + nsect > dev->sectors) { - printk(KERN_WARNING "cf-mips: sector %lu out of range\n",sector); - return CF_TRANS_FAILED; - } - dev->tbuf = buffer; - dev->tbuf_size = nsect*512; - dev->tsect_start = sector; - dev->tsector_count = nsect; - dev->tsectors_left = dev->tsector_count; - dev->tread = (is_write)?0:1; - - dev->tcmd = (dev->block_size == 1 ? - (is_write ? ATA_CMD_WRITE_SECTORS : ATA_CMD_READ_SECTORS) : - (is_write ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_READ_MULTIPLE)); - - return cf_do_state(dev); -} - -static int do_identify(struct cf_mips_dev *dev) -{ - u16 sbuf[CF_SECT_SIZE >> 1]; - int res; - char tstr[17]; //serial - char tmp; - int i; - BUG_ON(dev->tstate!=TS_IDLE); - dev->tbuf = (char *) sbuf; - dev->tbuf_size = CF_SECT_SIZE; - dev->tsect_start = 0; - dev->tsector_count = 0; - dev->tsectors_left = 1; - dev->tread = 1; - dev->tcmd = ATA_CMD_IDENTIFY_DRIVE; - - DEBUGP(KERN_INFO "cf-mips: identify drive..\n"); - res = cf_do_state(dev); - if (res == CF_TRANS_IN_PROGRESS) { - printk(KERN_ERR "cf-mips: BUG: async identify cmd\n"); - return CF_TRANS_FAILED; - } - if (res != CF_TRANS_OK) - return 0; - - dev->head = sbuf[3]; - dev->cyl = sbuf[1]; - dev->spt = sbuf[6]; - dev->sectors = ((unsigned long) sbuf[7] << 16) | sbuf[8]; - dev->dtype=sbuf[0]; - memcpy(tstr, &sbuf[12], 16); - tstr[16] = '\0'; - - /* Byte-swap the serial number */ - for (i = 0; i<8; i++) { - tmp = tstr[i * 2]; - tstr[i * 2] = tstr[i * 2 +1]; - tstr[i * 2 + 1] = tmp; - } - - printk(KERN_INFO "cf-mips: %s detected, C/H/S=%d/%d/%d sectors=%u (%uMB) Serial=%s\n", - (sbuf[0] == 0x848A ? "CF card" : "ATA drive"), dev->cyl, dev->head, - dev->spt, dev->sectors, dev->sectors >> 11, tstr); - return 1; -} - -static void init_multiple(struct cf_mips_dev * dev) -{ - int res; - DEBUGP(KERN_INFO "cf-mips: detecting block size\n"); - - dev->block_size = 128; /* max block size = 128 sectors (64KB) */ - do { - wareg(dev->block_size, ATA_REG_SC,dev); - wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH,dev); - wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD,dev); - - res = wait_not_busy(10 * SECS, 1,dev); - if (res != CF_TRANS_OK) { - printk(KERN_ERR "cf-mips: failed to detect block size: busy!\n"); - dev->block_size = 1; - return; - } - if ((rareg(ATA_REG_ST,dev) & ATA_REG_ST_ERR) == 0) - break; - dev->block_size /= 2; - } while (dev->block_size > 1); - - printk(KERN_INFO "cf-mips: multiple sectors = %d\n", dev->block_size); -} - -int cf_init(struct cf_mips_dev *dev) -{ - tasklet_init(&dev->tasklet,cf_do_tasklet,(unsigned long)dev); - dev->baddr = ioremap_nocache((unsigned long)dev->base, CFDEV_BUF_SIZE); - if (!dev->baddr) { - printk(KERN_ERR "cf-mips: cf_init: ioremap for (%lx,%x) failed\n", - (unsigned long) dev->base, CFDEV_BUF_SIZE); - return -EBUSY; - } - - if (!cf_present(dev)) { - printk(KERN_WARNING "cf-mips: cf card not present\n"); - iounmap(dev->baddr); - return -ENODEV; - } - - if (do_reset(dev) != CF_TRANS_OK) { - printk(KERN_ERR "cf-mips: cf reset failed\n"); - iounmap(dev->baddr); - return -EBUSY; - } - - if (!do_identify(dev)) { - printk(KERN_ERR "cf-mips: cf identify failed\n"); - iounmap(dev->baddr); - return -EBUSY; - } - -/* set_apm_level(ATA_APM_WITH_STANDBY); */ - init_multiple(dev); - - init_timer(&dev->to_timer); - dev->to_timer.function = cf_async_timeout; - dev->to_timer.data = (unsigned long)dev; - - prepare_cf_irq(dev); - if (request_irq(dev->irq, cf_irq_handler, 0, "CF Mips", dev)) { - printk(KERN_ERR "cf-mips: failed to get irq\n"); - iounmap(dev->baddr); - return -EBUSY; - } - /* Disable below would be odd, because request will enable, and the tasklet - will disable it itself */ - //disable_irq(dev->irq); - - dev->async_mode = 1; - - return 0; -} - -void cf_cleanup(struct cf_mips_dev *dev) -{ - iounmap(dev->baddr); - free_irq(dev->irq, NULL); -#if REQUEST_MEM_REGION - release_mem_region((unsigned long)dev->base, CFDEV_BUF_SIZE); -#endif -} - - -/*eof*/ diff --git a/target/linux/rb532/files/drivers/block/rb500/ata.h b/target/linux/rb532/files/drivers/block/rb500/ata.h deleted file mode 100644 index 15e8826308..0000000000 --- a/target/linux/rb532/files/drivers/block/rb500/ata.h +++ /dev/null @@ -1,143 +0,0 @@ -/* CF-mips driver - This is a block driver for the direct (mmaped) interface to the CF-slot, - found in Routerboard.com's RB532 board - See SDK provided from routerboard.com. - - Module adapted By P.Christeas , 2005-6. - Cleaned up and adapted to platform_device by Felix Fietkau - - This work is redistributed under the terms of the GNU General Public License. -*/ - -#ifndef __CFMIPS_ATA_H__ -#define __CFMIPS_ATA_H__ - -#include - -#define CFG_DC_DEV1 (void*)0xb8010010 -#define CFG_DC_DEVBASE 0x0 -#define CFG_DC_DEVMASK 0x4 -#define CFG_DC_DEVC 0x8 -#define CFG_DC_DEVTC 0xC - -#define CFDEV_BUF_SIZE 0x1000 -#define ATA_CIS_OFFSET 0x200 -#define ATA_REG_OFFSET 0x800 -#define ATA_DBUF_OFFSET 0xC00 - -#define ATA_REG_FEAT 0x1 -#define ATA_REG_SC 0x2 -#define ATA_REG_SN 0x3 -#define ATA_REG_CL 0x4 -#define ATA_REG_CH 0x5 -#define ATA_REG_DH 0x6 -#define ATA_REG_DH_BASE 0xa0 -#define ATA_REG_DH_LBA 0x40 -#define ATA_REG_DH_DRV 0x10 -#define ATA_REG_CMD 0x7 -#define ATA_REG_ST 0x7 -#define ATA_REG_ST_BUSY 0x80 -#define ATA_REG_ST_RDY 0x40 -#define ATA_REG_ST_DWF 0x20 -#define ATA_REG_ST_DSC 0x10 -#define ATA_REG_ST_DRQ 0x08 -#define ATA_REG_ST_CORR 0x04 -#define ATA_REG_ST_ERR 0x01 -#define ATA_REG_ERR 0xd -#define ATA_REG_DC 0xe -#define ATA_REG_DC_IEN 0x02 -#define ATA_REG_DC_SRST 0x04 - -#define ATA_CMD_READ_SECTORS 0x20 -#define ATA_CMD_WRITE_SECTORS 0x30 -#define ATA_CMD_EXEC_DRIVE_DIAG 0x90 -#define ATA_CMD_READ_MULTIPLE 0xC4 -#define ATA_CMD_WRITE_MULTIPLE 0xC5 -#define ATA_CMD_SET_MULTIPLE 0xC6 -#define ATA_CMD_IDENTIFY_DRIVE 0xEC -#define ATA_CMD_SET_FEATURES 0xEF - -#define ATA_FEATURE_ENABLE_APM 0x05 -#define ATA_FEATURE_DISABLE_APM 0x85 -#define ATA_APM_DISABLED 0x00 -#define ATA_APM_MIN_POWER 0x01 -#define ATA_APM_WITH_STANDBY 0x7f -#define ATA_APM_WITHOUT_STANDBY 0x80 -#define ATA_APM_MAX_PERFORMANCE 0xfe - -#define CF_SECT_SIZE 0x200 -/* That is the ratio CF_SECT_SIZE/512 (the kernel sector size) */ -#define CF_KERNEL_MUL 1 -#define ATA_MAX_SECT_PER_CMD 0x100 - -#define CF_TRANS_FAILED 0 -#define CF_TRANS_OK 1 -#define CF_TRANS_IN_PROGRESS 2 - - -enum trans_state { - TS_IDLE = 0, - TS_AFTER_RESET, - TS_READY, - TS_CMD, - TS_TRANS -}; - -// -// #if DEBUG -// static unsigned long busy_time; -// #endif - -/** Struct to hold the cfdev -Actually, all the data here only has one instance. However, for -reasons of programming conformity, it is passed around as a pointer -*/ -struct cf_mips_dev { - void *base; /* base address for I/O */ - void *baddr; /* remapped address */ - - int pin; /* gpio pin */ - int irq; /* gpio irq */ - - unsigned head; - unsigned cyl; - unsigned spt; - unsigned sectors; - - unsigned short block_size; - unsigned dtype ; // ATA or CF - struct request_queue *queue; - struct gendisk *gd; - - /* Transaction state */ - enum trans_state tstate; - char *tbuf; - unsigned long tbuf_size; - sector_t tsect_start; - unsigned tsector_count; - unsigned tsectors_left; - int tread; - unsigned tcmd; - int async_mode; - unsigned long irq_enable_time; - - struct request *active_req; /* A request is being carried out. Is that different from tstate? */ - int users; - struct timer_list to_timer; - struct tasklet_struct tasklet; - - /** This lock ensures that the requests to this device are all done - atomically. Transfers can run in parallel, requests are all queued - one-by-one */ - spinlock_t lock; -}; - -int cf_do_transfer(struct cf_mips_dev* dev,sector_t sector, unsigned long nsect, - char* buffer, int is_write); -int cf_init(struct cf_mips_dev* dev); -void cf_cleanup(struct cf_mips_dev* dev); - -void cf_async_trans_done(struct cf_mips_dev* dev, int result); -// void *cf_get_next_buf(unsigned long *buf_size); - -#endif diff --git a/target/linux/rb532/files/drivers/block/rb500/bdev.c b/target/linux/rb532/files/drivers/block/rb500/bdev.c deleted file mode 100644 index f8a9b02d70..0000000000 --- a/target/linux/rb532/files/drivers/block/rb500/bdev.c +++ /dev/null @@ -1,340 +0,0 @@ -/* CF-mips driver - This is a block driver for the direct (mmaped) interface to the CF-slot, - found in Routerboard.com's RB532 board - See SDK provided from routerboard.com. - - Module adapted By P.Christeas , 2005-6. - Cleaned up and adapted to platform_device by Felix Fietkau - - This work is redistributed under the terms of the GNU General Public License. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#ifdef DEBUG -#define DEBUGP printk -#define DLEVEL 1 -#else -#define DEBUGP(format, args...) -#define DLEVEL 0 -#endif - -#define CF_MIPS_MAJOR 13 -#define MAJOR_NR CF_MIPS_MAJOR -#define CF_MAX_PART 16 /* max 15 partitions */ - -#include "ata.h" - -//extern struct block_device_operations cf_bdops; - -// static struct hd_struct cf_parts[CF_MAX_PART]; -// static int cf_part_sizes[CF_MAX_PART]; -// static int cf_hsect_sizes[CF_MAX_PART]; -// static int cf_max_sectors[CF_MAX_PART]; -// static int cf_blksize_sizes[CF_MAX_PART]; - -// static spinlock_t lock = SPIN_LOCK_UNLOCKED; - -// volatile int cf_busy = 0; - -static struct request *active_req = NULL; - -static int cf_open (struct inode *, struct file *); -static int cf_release (struct inode *, struct file *); -static int cf_ioctl (struct inode *, struct file *, unsigned, unsigned long); - -static void cf_request(request_queue_t * q); -static int cf_transfer(const struct request *req); - -/*long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); -long (*compat_ioctl) (struct file *, unsigned, unsigned long);*/ -// int (*direct_access) (struct block_device *, sector_t, unsigned long *); -// int (*media_changed) (struct gendisk *); -// int (*revalidate_disk) (struct gendisk *); - -static struct block_device_operations cf_bdops = { - .owner = THIS_MODULE, - .open = cf_open, - .release = cf_release, - .ioctl = cf_ioctl, - .media_changed = NULL, - .unlocked_ioctl = NULL, - .revalidate_disk = NULL, - .compat_ioctl = NULL, - .direct_access = NULL -}; - - -int cf_mips_probe(struct platform_device *pdev) -{ - struct gendisk* cf_gendisk=NULL; - struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data; - struct cf_mips_dev *dev; - struct resource *r; - int reg_result; - - reg_result = register_blkdev(MAJOR_NR, "cf-mips"); - if (reg_result < 0) { - printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR); - return reg_result; - } - - dev = (struct cf_mips_dev *)kmalloc(sizeof(struct cf_mips_dev),GFP_KERNEL); - if (!dev) - goto out_err; - memset(dev, 0, sizeof(struct cf_mips_dev)); - cdev->dev = dev; - - dev->pin = cdev->gpio_pin; - dev->irq = platform_get_irq_byname(pdev, "cf_irq"); - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cf_membase"); - dev->base = (void *) r->start; - - if (cf_init(dev)) goto out_err; - printk("init done"); - - spin_lock_init(&dev->lock); - dev->queue = blk_init_queue(cf_request,&dev->lock); - if (!dev->queue){ - printk(KERN_ERR "cf-mips: no mem for queue\n"); - goto out_err; - } - blk_queue_max_sectors(dev->queue,ATA_MAX_SECT_PER_CMD); - - /* For memory devices, it is always better to avoid crossing segments - inside the same request. */ -/* if (dev->dtype==0x848A){ - printk(KERN_INFO "Setting boundary for cf to 0x%x",(dev->block_size*512)-1); - blk_queue_segment_boundary(dev->queue, (dev->block_size*512)-1); - }*/ - - dev->gd = alloc_disk(CF_MAX_PART); - cf_gendisk = dev->gd; - cdev->gd = dev->gd; - if (!cf_gendisk) goto out_err; /* Last of these goto's */ - - cf_gendisk->major = MAJOR_NR; - cf_gendisk->first_minor = 0; - cf_gendisk->queue=dev->queue; - BUG_ON(cf_gendisk->minors != CF_MAX_PART); - strcpy(cf_gendisk->disk_name,"cfa"); - cf_gendisk->fops = &cf_bdops; - cf_gendisk->flags = 0 ; /* is not yet GENHD_FL_REMOVABLE */ - cf_gendisk->private_data=dev; - - set_capacity(cf_gendisk,dev->sectors * CF_KERNEL_MUL); - - /* Let the disk go live */ - add_disk(cf_gendisk); -#if 0 - result = cf_init(); - - /* default cfg for all partitions */ - memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART); - memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART); - for (i = 0; i < CF_MAX_PART; ++i) { - cf_hsect_sizes[i] = CF_SECT_SIZE; - cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD; - cf_blksize_sizes[i] = BLOCK_SIZE; - } - - /* setup info for whole disk (partition 0) */ - cf_part_sizes[0] = cf_sectors / 2; - cf_parts[0].nr_sects = cf_sectors; - - blk_size[MAJOR_NR] = cf_part_sizes; - blksize_size[MAJOR_NR] = cf_blksize_sizes; - max_sectors[MAJOR_NR] = cf_max_sectors; - hardsect_size[MAJOR_NR] = cf_hsect_sizes; - read_ahead[MAJOR_NR] = 8; /* (4kB) */ - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); - - add_gendisk(&cf_gendisk); -#endif -// printk(KERN_INFO "cf-mips partition check: \n"); -// register_disk(cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART, -// &cf_bdops, dev->sectors); - return 0; - -out_err: - if (dev->queue){ - blk_cleanup_queue(dev->queue); - } - if (reg_result) { - unregister_blkdev(MAJOR_NR, "cf-mips"); - return reg_result; - } - if (dev){ - cf_cleanup(dev); - kfree(dev); - } - return 1; -} - -static int -cf_mips_remove(struct platform_device *pdev) -{ - struct cf_device *cdev = (struct cf_device *) pdev->dev.platform_data; - struct cf_mips_dev *dev = (struct cf_mips_dev *) cdev->dev; - - unregister_blkdev(MAJOR_NR, "cf-mips"); - blk_cleanup_queue(dev->queue); - - del_gendisk(dev->gd); - cf_cleanup(dev); - return 0; -} - - -static struct platform_driver cf_driver = { - .driver.name = "rb500-cf", - .probe = cf_mips_probe, - .remove = cf_mips_remove, -}; - -static int __init cf_mips_init(void) -{ - printk(KERN_INFO "cf-mips module loaded\n"); - return platform_driver_register(&cf_driver); -} - -static void cf_mips_cleanup(void) -{ - platform_driver_unregister(&cf_driver); - printk(KERN_INFO "cf-mips module removed\n"); -} - -module_init(cf_mips_init); -module_exit(cf_mips_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_BLOCKDEV_MAJOR(CF_MIPS_MAJOR); - - -static int cf_open(struct inode *inode, struct file *filp) -{ - struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; - int minor = MINOR(inode->i_rdev); - - if (minor >= CF_MAX_PART) - return -ENODEV; - //DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor); - spin_lock(&dev->lock); - dev->users++; - spin_unlock(&dev->lock); - filp->private_data=dev; - - /* dirty workaround to set CFRDY GPIO as an input when some other - program sets it as an output */ - gpio_set_value(dev->pin, 0); - return 0; /* success */ -} - -static int cf_release(struct inode *inode, struct file *filp) -{ - int minor = MINOR(inode->i_rdev); - struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; - spin_lock(&dev->lock); - dev->users--; - spin_unlock(&dev->lock); - return 0; -} - -static int cf_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - unsigned minor = MINOR(inode->i_rdev); - struct cf_mips_dev *dev=inode->i_bdev->bd_disk->private_data; - - DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd); - switch (cmd) { - case BLKRRPART: /* re-read partition table */ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - printk(KERN_INFO "cf-mips partition check: \n"); - register_disk(dev->gd); - return 0; - - case HDIO_GETGEO: - { - struct hd_geometry geo; - geo.cylinders = dev->cyl; - geo.heads = dev->head; - geo.sectors = dev->spt; - geo.start = (*dev->gd->part)[minor].start_sect; - if (copy_to_user((void *) arg, &geo, sizeof (geo))) - return -EFAULT; - } - return 0; - } - - return -EINVAL; /* unknown command */ -} - -static void cf_request(request_queue_t * q) -{ - struct cf_mips_dev* dev; - - struct request * req; - int status; - - /* We could have q->queuedata = dev , but haven't yet. */ - if (active_req) - return; - - while ((req=elv_next_request(q))!=NULL){ - dev=req->rq_disk->private_data; - status=cf_transfer(req); - if (status==CF_TRANS_IN_PROGRESS){ - active_req=req; - return; - } - end_request(req,status); - } -} - -static int cf_transfer(const struct request *req) -{ - struct cf_mips_dev* dev=req->rq_disk->private_data; - - if (!blk_fs_request(req)){ - if (printk_ratelimit()) - printk(KERN_WARNING "cf-mips: skipping non-fs request 0x%x\n",req->cmd[0]); - return CF_TRANS_FAILED; - } - - return cf_do_transfer(dev,req->sector,req->current_nr_sectors,req->buffer,rq_data_dir(req)); -} - -void cf_async_trans_done(struct cf_mips_dev * dev,int result) -{ - struct request *req; - - spin_lock(&dev->lock); - req=active_req; - active_req=NULL; - end_request(req,result); - spin_unlock(&dev->lock); - - spin_lock(&dev->lock); - cf_request(dev->queue); - spin_unlock(&dev->lock); -} - diff --git a/target/linux/rb532/files/drivers/leds/leds-rb500.c b/target/linux/rb532/files/drivers/leds/leds-rb500.c deleted file mode 100644 index 3ab530a758..0000000000 --- a/target/linux/rb532/files/drivers/leds/leds-rb500.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * linux/drivers/leds/leds-rb500.c - * - * Copyright (C) 2006 - * Twente Institute for Wireless and Mobile Communications BV - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details (see file GPLv2). - * - * Author: Tjalling Hattink - */ - -#include -#include -#include -#include -#include - -static void rb500led_amber_set(struct led_classdev *led_cdev, enum led_brightness value) -{ - if (value) - changeLatchU5(LO_ULED, 0); - else - changeLatchU5(0, LO_ULED); -} - -static struct led_classdev rb500_amber_led = { - .name = "rb500led:amber", - .default_trigger = "ide-disk", - .brightness_set = rb500led_amber_set, -}; - -static int rb500led_probe(struct platform_device *pdev) -{ - int ret; - - changeLatchU5(0, LO_ULED); - - ret = led_classdev_register(&pdev->dev, &rb500_amber_led); - - return ret; -} - -static int rb500led_remove(struct platform_device *pdev) -{ - led_classdev_unregister(&rb500_amber_led); - - return 0; -} - -static struct platform_driver rb500led_driver = { - .probe = rb500led_probe, - .remove = rb500led_remove, - .driver = { - .name = "rb500-led", - }, -}; - -static int __init rb500led_init(void) -{ - return platform_driver_register(&rb500led_driver); -} - -static void __exit rb500led_exit(void) -{ - platform_driver_unregister(&rb500led_driver); -} - -module_init(rb500led_init); -module_exit(rb500led_exit); - -MODULE_AUTHOR("tjalling.hattink@ti-wmc.nl"); -MODULE_DESCRIPTION("Mikrotik RB500 LED driver"); -MODULE_LICENSE("GPL"); diff --git a/target/linux/rb532/files/drivers/net/korina.c b/target/linux/rb532/files/drivers/net/korina.c deleted file mode 100644 index 96b2ffe0eb..0000000000 --- a/target/linux/rb532/files/drivers/net/korina.c +++ /dev/null @@ -1,1118 +0,0 @@ -/************************************************************************** - * - * BRIEF MODULE DESCRIPTION - * Driver for the IDT RC32434 on-chip ethernet controller. - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * Copyright 2006 Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - ************************************************************************** - * May 2004 rkt, neb - * - * Based on the driver developed by B. Maruthanayakam, H. Kou and others. - * - * Aug 2004 Sadik - * - * Added NAPI - * - ************************************************************************** - */ - -#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 "rc32434_eth.h" - -#define DRIVER_VERSION "(mar2904)" - -#define DRIVER_NAME "rc32434 Ethernet driver. " DRIVER_VERSION - -#define STATION_ADDRESS_HIGH(dev) (((dev)->dev_addr[0] << 8) | \ - ((dev)->dev_addr[1])) -#define STATION_ADDRESS_LOW(dev) (((dev)->dev_addr[2] << 24) | \ - ((dev)->dev_addr[3] << 16) | \ - ((dev)->dev_addr[4] << 8) | \ - ((dev)->dev_addr[5])) - -#define MII_CLOCK 1250000 /* no more than 2.5MHz */ -#define CONFIG_IDT_USE_NAPI 1 - - -static inline void rc32434_abort_tx(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - rc32434_abort_dma(dev, lp->tx_dma_regs); - -} - -static inline void rc32434_abort_rx(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - rc32434_abort_dma(dev, lp->rx_dma_regs); - -} - -static inline void rc32434_start_tx(struct rc32434_local *lp, volatile DMAD_t td) -{ - rc32434_start_dma(lp->tx_dma_regs, CPHYSADDR(td)); -} - -static inline void rc32434_start_rx(struct rc32434_local *lp, volatile DMAD_t rd) -{ - rc32434_start_dma(lp->rx_dma_regs, CPHYSADDR(rd)); -} - -static inline void rc32434_chain_tx(struct rc32434_local *lp, volatile DMAD_t td) -{ - rc32434_chain_dma(lp->tx_dma_regs, CPHYSADDR(td)); -} - -static inline void rc32434_chain_rx(struct rc32434_local *lp, volatile DMAD_t rd) -{ - rc32434_chain_dma(lp->rx_dma_regs, CPHYSADDR(rd)); -} - -#ifdef RC32434_PROC_DEBUG -static int rc32434_read_proc(char *buf, char **start, off_t fpos, - int length, int *eof, void *data) -{ - struct net_device *dev = (struct net_device *)data; - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - int len = 0; - - /* print out header */ - len += sprintf(buf + len, "\n\tKorina Ethernet Debug\n\n"); - len += sprintf (buf + len, - "DMA halt count = %10d, DMA run count = %10d\n", - lp->dma_halt_cnt, lp->dma_run_cnt); - - if (fpos >= len) { - *start = buf; - *eof = 1; - return 0; - } - *start = buf + fpos; - - if ((len -= fpos) > length) - return length; - *eof = 1; - - return len; - -} -#endif - - -/* - * Restart the RC32434 ethernet controller. - */ -static int rc32434_restart(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - - /* - * Disable interrupts - */ - disable_irq(lp->rx_irq); - disable_irq(lp->tx_irq); -#ifdef RC32434_REVISION - disable_irq(lp->ovr_irq); -#endif - disable_irq(lp->und_irq); - - /* Mask F E bit in Tx DMA */ - __raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) | DMASM_f_m | DMASM_e_m, &lp->tx_dma_regs->dmasm); - /* Mask D H E bit in Rx DMA */ - __raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) | DMASM_d_m | DMASM_h_m | DMASM_e_m, &lp->rx_dma_regs->dmasm); - - rc32434_init(dev); - rc32434_multicast_list(dev); - - enable_irq(lp->und_irq); -#ifdef RC32434_REVISION - enable_irq(lp->ovr_irq); -#endif - enable_irq(lp->tx_irq); - enable_irq(lp->rx_irq); - - return 0; -} - -static int rc32434_probe(struct platform_device *pdev) -{ - struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data; - struct rc32434_local *lp = NULL; - struct net_device *dev = NULL; - struct resource *r; - int i, retval,err; - - dev = alloc_etherdev(sizeof(struct rc32434_local)); - if(!dev) { - ERR("Korina_eth: alloc_etherdev failed\n"); - return -1; - } - - platform_set_drvdata(pdev, dev); - SET_NETDEV_DEV(dev, &pdev->dev); - bif->dev = dev; - - memcpy(dev->dev_addr, bif->mac, 6); - - /* Initialize the device structure. */ - if (dev->priv == NULL) { - lp = (struct rc32434_local *)kmalloc(sizeof(*lp), GFP_KERNEL); - memset(lp, 0, sizeof(struct rc32434_local)); - } - else { - lp = (struct rc32434_local *)dev->priv; - } - - lp->rx_irq = platform_get_irq_byname(pdev, "korina_rx"); - lp->tx_irq = platform_get_irq_byname(pdev, "korina_tx"); - lp->ovr_irq = platform_get_irq_byname(pdev, "korina_ovr"); - lp->und_irq = platform_get_irq_byname(pdev, "korina_und"); - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_regs"); - dev->base_addr = r->start; - lp->eth_regs = ioremap_nocache(r->start, r->end - r->start); - if (!lp->eth_regs) { - ERR("Can't remap eth registers\n"); - retval = -ENXIO; - goto probe_err_out; - } - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_rx"); - lp->rx_dma_regs = ioremap_nocache(r->start, r->end - r->start); - if (!lp->rx_dma_regs) { - ERR("Can't remap Rx DMA registers\n"); - retval = -ENXIO; - goto probe_err_out; - } - - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_tx"); - lp->tx_dma_regs = ioremap_nocache(r->start, r->end - r->start); - if (!lp->tx_dma_regs) { - ERR("Can't remap Tx DMA registers\n"); - retval = -ENXIO; - goto probe_err_out; - } - -#ifdef RC32434_PROC_DEBUG - lp->ps = create_proc_read_entry (bif->name, 0, proc_net, - rc32434_read_proc, dev); -#endif - - lp->td_ring = (DMAD_t)kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL); - if (!lp->td_ring) { - ERR("Can't allocate descriptors\n"); - retval = -ENOMEM; - goto probe_err_out; - } - - dma_cache_inv((unsigned long)(lp->td_ring), TD_RING_SIZE + RD_RING_SIZE); - - /* now convert TD_RING pointer to KSEG1 */ - lp->td_ring = (DMAD_t )KSEG1ADDR(lp->td_ring); - lp->rd_ring = &lp->td_ring[RC32434_NUM_TDS]; - - - spin_lock_init(&lp->lock); - - /* just use the rx dma irq */ - dev->irq = lp->rx_irq; - - dev->priv = lp; - lp->dev = dev; - - dev->open = rc32434_open; - dev->stop = rc32434_close; - dev->hard_start_xmit = rc32434_send_packet; - dev->get_stats = rc32434_get_stats; - dev->set_multicast_list = &rc32434_multicast_list; - dev->tx_timeout = rc32434_tx_timeout; - dev->watchdog_timeo = RC32434_TX_TIMEOUT; - - netif_napi_add(dev, &lp->napi, rc32434_poll, 64); - lp->tx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL); - tasklet_init(lp->tx_tasklet, rc32434_tx_tasklet, (unsigned long)dev); - - if ((err = register_netdev(dev))) { - printk(KERN_ERR "rc32434 ethernet. Cannot register net device %d\n", err); - free_netdev(dev); - retval = -EINVAL; - goto probe_err_out; - } - - INFO("Rx IRQ %d, Tx IRQ %d, ", lp->rx_irq, lp->tx_irq); - for (i = 0; i < 6; i++) { - printk("%2.2x", dev->dev_addr[i]); - if (i<5) - printk(":"); - } - printk("\n"); - - return 0; - - probe_err_out: - rc32434_cleanup_module(); - ERR(" failed. Returns %d\n", retval); - return retval; - -} - -static int rc32434_remove(struct platform_device *pdev) -{ - struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data; - - if (bif->dev != NULL) { - struct rc32434_local *lp = (struct rc32434_local *)bif->dev->priv; - if (lp != NULL) { - if (lp->eth_regs) - iounmap((void*)lp->eth_regs); - if (lp->rx_dma_regs) - iounmap((void*)lp->rx_dma_regs); - if (lp->tx_dma_regs) - iounmap((void*)lp->tx_dma_regs); - if (lp->td_ring) - kfree((void*)KSEG0ADDR(lp->td_ring)); - -#ifdef RC32434_PROC_DEBUG - if (lp->ps) { - remove_proc_entry(bif->name, proc_net); - } -#endif - kfree(lp); - } - - platform_set_drvdata(pdev, NULL); - unregister_netdev(bif->dev); - free_netdev(bif->dev); - kfree(bif->dev); - } - return 0; -} - - -static int rc32434_open(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - - /* Initialize */ - if (rc32434_init(dev)) { - ERR("Error: cannot open the Ethernet device\n"); - return -EAGAIN; - } - - /* Install the interrupt handler that handles the Done Finished Ovr and Und Events */ - if (request_irq(lp->rx_irq, &rc32434_rx_dma_interrupt, - IRQF_SHARED | IRQF_DISABLED, - "Korina ethernet Rx", dev)) { - ERR(": unable to get Rx DMA IRQ %d\n", - lp->rx_irq); - return -EAGAIN; - } - if (request_irq(lp->tx_irq, &rc32434_tx_dma_interrupt, - IRQF_SHARED | IRQF_DISABLED, - "Korina ethernet Tx", dev)) { - ERR(": unable to get Tx DMA IRQ %d\n", - lp->tx_irq); - free_irq(lp->rx_irq, dev); - return -EAGAIN; - } - -#ifdef RC32434_REVISION - /* Install handler for overrun error. */ - if (request_irq(lp->ovr_irq, &rc32434_ovr_interrupt, - IRQF_SHARED | IRQF_DISABLED, - "Ethernet Overflow", dev)) { - ERR(": unable to get OVR IRQ %d\n", - lp->ovr_irq); - free_irq(lp->rx_irq, dev); - free_irq(lp->tx_irq, dev); - return -EAGAIN; - } -#endif - - /* Install handler for underflow error. */ - if (request_irq(lp->und_irq, &rc32434_und_interrupt, - IRQF_SHARED | IRQF_DISABLED, - "Ethernet Underflow", dev)) { - ERR(": unable to get UND IRQ %d\n", - lp->und_irq); - free_irq(lp->rx_irq, dev); - free_irq(lp->tx_irq, dev); -#ifdef RC32434_REVISION - free_irq(lp->ovr_irq, dev); -#endif - return -EAGAIN; - } - - - return 0; -} - - - - -static int rc32434_close(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - u32 tmp; - - /* Disable interrupts */ - disable_irq(lp->rx_irq); - disable_irq(lp->tx_irq); -#ifdef RC32434_REVISION - disable_irq(lp->ovr_irq); -#endif - disable_irq(lp->und_irq); - - tmp = __raw_readl(&lp->tx_dma_regs->dmasm); - tmp = tmp | DMASM_f_m | DMASM_e_m; - __raw_writel(tmp, &lp->tx_dma_regs->dmasm); - - tmp = __raw_readl(&lp->rx_dma_regs->dmasm); - tmp = tmp | DMASM_d_m | DMASM_h_m | DMASM_e_m; - __raw_writel(tmp, &lp->rx_dma_regs->dmasm); - - free_irq(lp->rx_irq, dev); - free_irq(lp->tx_irq, dev); -#ifdef RC32434_REVISION - free_irq(lp->ovr_irq, dev); -#endif - free_irq(lp->und_irq, dev); - return 0; -} - - -/* transmit packet */ -static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - unsigned long flags; - u32 length; - DMAD_t td; - - - spin_lock_irqsave(&lp->lock, flags); - - td = &lp->td_ring[lp->tx_chain_tail]; - - /* stop queue when full, drop pkts if queue already full */ - if(lp->tx_count >= (RC32434_NUM_TDS - 2)) { - lp->tx_full = 1; - - if(lp->tx_count == (RC32434_NUM_TDS - 2)) { - netif_stop_queue(dev); - } - else { - lp->stats.tx_dropped++; - dev_kfree_skb_any(skb); - spin_unlock_irqrestore(&lp->lock, flags); - return 1; - } - } - - lp->tx_count ++; - - lp->tx_skb[lp->tx_chain_tail] = skb; - - length = skb->len; - dma_cache_wback((u32)skb->data, skb->len); - - /* Setup the transmit descriptor. */ - dma_cache_inv((u32) td, sizeof(*td)); - td->ca = CPHYSADDR(skb->data); - - if(__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0) { - if( lp->tx_chain_status == empty ) { - td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; /* Update tail */ - lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ - __raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR */ - lp->tx_chain_head = lp->tx_chain_tail; /* Move head to tail */ - } - else { - td->control = DMA_COUNT(length) |DMAD_cof_m|DMAD_iof_m; /* Update tail */ - lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &= ~(DMAD_cof_m); /* Link to prev */ - lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link = CPHYSADDR(td); /* Link to prev */ - lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ - __raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR */ - lp->tx_chain_head = lp->tx_chain_tail; /* Move head to tail */ - lp->tx_chain_status = empty; - } - } - else { - if( lp->tx_chain_status == empty ) { - td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; /* Update tail */ - lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ - lp->tx_chain_status = filled; - } - else { - td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; /* Update tail */ - lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &= ~(DMAD_cof_m); /* Link to prev */ - lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link = CPHYSADDR(td); /* Link to prev */ - lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK; /* Move tail */ - } - } - dma_cache_wback((u32) td, sizeof(*td)); - - dev->trans_start = jiffies; - - spin_unlock_irqrestore(&lp->lock, flags); - - return 0; -} - - -/* Ethernet MII-PHY Handler */ -static void rc32434_mii_handler(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - unsigned long flags; - unsigned long duplex_status; - int port_addr = (lp->rx_irq == 0x2c? 1:0) << 8; - - spin_lock_irqsave(&lp->lock, flags); - - /* Two ports are using the same MII, the difference is the PHY address */ - __raw_writel(0, &rc32434_eth0_regs->miimcfg); - __raw_writel(0, &rc32434_eth0_regs->miimcmd); - __raw_writel(port_addr |0x05, &rc32434_eth0_regs->miimaddr); - __raw_writel(MIIMCMD_scn_m, &rc32434_eth0_regs->miimcmd); - while(__raw_readl(&rc32434_eth0_regs->miimind) & MIIMIND_nv_m); - - ERR("irq:%x port_addr:%x RDD:%x\n", - lp->rx_irq, port_addr, __raw_readl(&rc32434_eth0_regs->miimrdd)); - duplex_status = (__raw_readl(&rc32434_eth0_regs->miimrdd) & 0x140)? ETHMAC2_fd_m: 0; - if(duplex_status != lp->duplex_mode) { - ERR("The MII-PHY is Auto-negotiated to %s-Duplex mode for Eth-%x\n", duplex_status? "Full":"Half", lp->rx_irq == 0x2c? 1:0); - lp->duplex_mode = duplex_status; - rc32434_restart(dev); - } - - lp->mii_phy_timer.expires = jiffies + 10 * HZ; - add_timer(&lp->mii_phy_timer); - - spin_unlock_irqrestore(&lp->lock, flags); - -} - -#ifdef RC32434_REVISION -/* Ethernet Rx Overflow interrupt */ -static irqreturn_t -rc32434_ovr_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct rc32434_local *lp; - unsigned int ovr; - irqreturn_t retval = IRQ_NONE; - - ASSERT(dev != NULL); - - lp = (struct rc32434_local *)dev->priv; - spin_lock(&lp->lock); - ovr = __raw_readl(&lp->eth_regs->ethintfc); - - if(ovr & ETHINTFC_ovr_m) { - netif_stop_queue(dev); - - /* clear OVR bit */ - __raw_writel((ovr & ~ETHINTFC_ovr_m), &lp->eth_regs->ethintfc); - - /* Restart interface */ - rc32434_restart(dev); - retval = IRQ_HANDLED; - } - spin_unlock(&lp->lock); - - return retval; -} - -#endif - - -/* Ethernet Tx Underflow interrupt */ -static irqreturn_t -rc32434_und_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct rc32434_local *lp; - unsigned int und; - irqreturn_t retval = IRQ_NONE; - - ASSERT(dev != NULL); - - lp = (struct rc32434_local *)dev->priv; - - spin_lock(&lp->lock); - - und = __raw_readl(&lp->eth_regs->ethintfc); - - if(und & ETHINTFC_und_m) { - netif_stop_queue(dev); - - __raw_writel((und & ~ETHINTFC_und_m), &lp->eth_regs->ethintfc); - - /* Restart interface */ - rc32434_restart(dev); - retval = IRQ_HANDLED; - } - - spin_unlock(&lp->lock); - - return retval; -} - - -/* Ethernet Rx DMA interrupt */ -static irqreturn_t -rc32434_rx_dma_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct rc32434_local* lp; - volatile u32 dmas,dmasm; - irqreturn_t retval; - - ASSERT(dev != NULL); - - lp = (struct rc32434_local *)dev->priv; - - dmas = __raw_readl(&lp->rx_dma_regs->dmas); - if(dmas & (DMAS_d_m|DMAS_h_m|DMAS_e_m)) { - /* Mask D H E bit in Rx DMA */ - dmasm = __raw_readl(&lp->rx_dma_regs->dmasm); - __raw_writel(dmasm | (DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); - netif_rx_schedule(dev, &lp->napi); - - if (dmas & DMAS_e_m) - ERR(": DMA error\n"); - - retval = IRQ_HANDLED; - } - else - retval = IRQ_NONE; - - return retval; -} - - -static int rc32434_rx(struct net_device *dev, int limit) -{ - struct rc32434_local *lp = netdev_priv(dev); - volatile DMAD_t rd = &lp->rd_ring[lp->rx_next_done]; - struct sk_buff *skb, *skb_new; - u8 *pkt_buf; - u32 devcs, pkt_len, dmas, rx_free_desc; - u32 pktuncrc_len; - int count; - - dma_cache_inv((u32)rd, sizeof(*rd)); - for (count = 0; count < limit; count++) { - /* init the var. used for the later operations within the while loop */ - skb_new = NULL; - devcs = rd->devcs; - pkt_len = RCVPKT_LENGTH(devcs); - skb = lp->rx_skb[lp->rx_next_done]; - - if ((devcs & ( ETHRX_ld_m)) != ETHRX_ld_m) { - /* check that this is a whole packet */ - /* WARNING: DMA_FD bit incorrectly set in Rc32434 (errata ref #077) */ - lp->stats.rx_errors++; - lp->stats.rx_dropped++; - } - else if ( (devcs & ETHRX_rok_m) ) { - - /* must be the (first and) last descriptor then */ - pkt_buf = (u8*)lp->rx_skb[lp->rx_next_done]->data; - - pktuncrc_len = pkt_len - 4; - /* invalidate the cache */ - dma_cache_inv((unsigned long)pkt_buf, pktuncrc_len); - - /* Malloc up new buffer. */ - skb_new = netdev_alloc_skb(dev, RC32434_RBSIZE + 2); - - if (skb_new != NULL){ - /* Make room */ - skb_put(skb, pktuncrc_len); - - skb->protocol = eth_type_trans(skb, dev); - - /* pass the packet to upper layers */ - netif_receive_skb(skb); - - dev->last_rx = jiffies; - lp->stats.rx_packets++; - lp->stats.rx_bytes += pktuncrc_len; - - if (IS_RCV_MP(devcs)) - lp->stats.multicast++; - - /* 16 bit align */ - skb_reserve(skb_new, 2); - - skb_new->dev = dev; - lp->rx_skb[lp->rx_next_done] = skb_new; - } - else { - ERR("no memory, dropping rx packet.\n"); - lp->stats.rx_errors++; - lp->stats.rx_dropped++; - } - } - else { - /* This should only happen if we enable accepting broken packets */ - lp->stats.rx_errors++; - lp->stats.rx_dropped++; - - /* add statistics counters */ - if (IS_RCV_CRC_ERR(devcs)) { - DBG(2, "RX CRC error\n"); - lp->stats.rx_crc_errors++; - } - else if (IS_RCV_LOR_ERR(devcs)) { - DBG(2, "RX LOR error\n"); - lp->stats.rx_length_errors++; - } - else if (IS_RCV_LE_ERR(devcs)) { - DBG(2, "RX LE error\n"); - lp->stats.rx_length_errors++; - } - else if (IS_RCV_OVR_ERR(devcs)) { - lp->stats.rx_over_errors++; - } - else if (IS_RCV_CV_ERR(devcs)) { - /* code violation */ - DBG(2, "RX CV error\n"); - lp->stats.rx_frame_errors++; - } - else if (IS_RCV_CES_ERR(devcs)) { - DBG(2, "RX Preamble error\n"); - } - } - rd->devcs = 0; - - /* restore descriptor's curr_addr */ - if(skb_new) { - rd->ca = CPHYSADDR(skb_new->data); - } - else - rd->ca = CPHYSADDR(skb->data); - - rd->control = DMA_COUNT(RC32434_RBSIZE) |DMAD_cod_m |DMAD_iod_m; - lp->rd_ring[(lp->rx_next_done-1)& RC32434_RDS_MASK].control &= ~(DMAD_cod_m); - - lp->rx_next_done = (lp->rx_next_done + 1) & RC32434_RDS_MASK; - dma_cache_wback((u32)rd, sizeof(*rd)); - rd = &lp->rd_ring[lp->rx_next_done]; - __raw_writel( ~DMAS_d_m, &lp->rx_dma_regs->dmas); - } - - dmas = __raw_readl(&lp->rx_dma_regs->dmas); - - if(dmas & DMAS_h_m) { - /* Mask off halt and error bits */ - __raw_writel( ~(DMAS_h_m | DMAS_e_m), &lp->rx_dma_regs->dmas); -#ifdef RC32434_PROC_DEBUG - lp->dma_halt_cnt++; -#endif - rd->devcs = 0; - skb = lp->rx_skb[lp->rx_next_done]; - rd->ca = CPHYSADDR(skb->data); - dma_cache_wback((u32)rd, sizeof(*rd)); - rc32434_chain_rx(lp,rd); - } - - return count; -} - -static int rc32434_poll(struct napi_struct *napi, int budget) -{ - struct rc32434_local *lp = - container_of(napi, struct rc32434_local, napi); - struct net_device *dev = lp->dev; - int work_done; - - work_done = rc32434_rx(dev, budget); - if (work_done < budget) { - netif_rx_complete(dev, napi); - - /* Mask off interrupts */ - writel(readl(&lp->rx_dma_regs->dmasm) & - (DMASM_d_m | DMASM_h_m |DMASM_e_m), - &lp->rx_dma_regs->dmasm); - } - return work_done; -} - - - -/* Ethernet Tx DMA interrupt */ -static irqreturn_t -rc32434_tx_dma_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct rc32434_local *lp; - volatile u32 dmas,dmasm; - irqreturn_t retval; - - ASSERT(dev != NULL); - - lp = (struct rc32434_local *)dev->priv; - - dmas = __raw_readl(&lp->tx_dma_regs->dmas); - - if (dmas & (DMAS_f_m | DMAS_e_m)) { - dmasm = __raw_readl(&lp->tx_dma_regs->dmasm); - /* Mask F E bit in Tx DMA */ - __raw_writel(dmasm | (DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); - - tasklet_hi_schedule(lp->tx_tasklet); - - if(lp->tx_chain_status == filled && (__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0)) { - __raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); - lp->tx_chain_status = empty; - lp->tx_chain_head = lp->tx_chain_tail; - dev->trans_start = jiffies; - } - - if (dmas & DMAS_e_m) - ERR(": DMA error\n"); - - retval = IRQ_HANDLED; - } - else - retval = IRQ_NONE; - - return retval; -} - - -static void rc32434_tx_tasklet(unsigned long tx_data_dev) -{ - struct net_device *dev = (struct net_device *)tx_data_dev; - struct rc32434_local* lp = (struct rc32434_local *)dev->priv; - volatile DMAD_t td = &lp->td_ring[lp->tx_next_done]; - u32 devcs; - unsigned long flags; - volatile u32 dmas; - - spin_lock_irqsave(&lp->lock, flags); - - /* process all desc that are done */ - while(IS_DMA_FINISHED(td->control)) { - if(lp->tx_full == 1) { - netif_wake_queue(dev); - lp->tx_full = 0; - } - - devcs = lp->td_ring[lp->tx_next_done].devcs; - if ((devcs & (ETHTX_fd_m | ETHTX_ld_m)) != (ETHTX_fd_m | ETHTX_ld_m)) { - lp->stats.tx_errors++; - lp->stats.tx_dropped++; - - /* should never happen */ - DBG(1, __FUNCTION__ ": split tx ignored\n"); - } - else if (IS_TX_TOK(devcs)) { - lp->stats.tx_packets++; - lp->stats.tx_bytes+=lp->tx_skb[lp->tx_next_done]->len; - } - else { - lp->stats.tx_errors++; - lp->stats.tx_dropped++; - - /* underflow */ - if (IS_TX_UND_ERR(devcs)) - lp->stats.tx_fifo_errors++; - - /* oversized frame */ - if (IS_TX_OF_ERR(devcs)) - lp->stats.tx_aborted_errors++; - - /* excessive deferrals */ - if (IS_TX_ED_ERR(devcs)) - lp->stats.tx_carrier_errors++; - - /* collisions: medium busy */ - if (IS_TX_EC_ERR(devcs)) - lp->stats.collisions++; - - /* late collision */ - if (IS_TX_LC_ERR(devcs)) - lp->stats.tx_window_errors++; - - } - - /* We must always free the original skb */ - if (lp->tx_skb[lp->tx_next_done] != NULL) { - dev_kfree_skb_any(lp->tx_skb[lp->tx_next_done]); - lp->tx_skb[lp->tx_next_done] = NULL; - } - - lp->td_ring[lp->tx_next_done].control = DMAD_iof_m; - lp->td_ring[lp->tx_next_done].devcs = ETHTX_fd_m | ETHTX_ld_m; - lp->td_ring[lp->tx_next_done].link = 0; - lp->td_ring[lp->tx_next_done].ca = 0; - lp->tx_count --; - - /* go on to next transmission */ - lp->tx_next_done = (lp->tx_next_done + 1) & RC32434_TDS_MASK; - td = &lp->td_ring[lp->tx_next_done]; - - } - - dmas = __raw_readl(&lp->tx_dma_regs->dmas); - __raw_writel( ~dmas, &lp->tx_dma_regs->dmas); - - /* Enable F E bit in Tx DMA */ - __raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); - spin_unlock_irqrestore(&lp->lock, flags); - -} - - -static struct net_device_stats * rc32434_get_stats(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - return &lp->stats; -} - - -/* - * Set or clear the multicast filter for this adaptor. - */ -static void rc32434_multicast_list(struct net_device *dev) -{ - /* listen to broadcasts always and to treat */ - /* IFF bits independantly */ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - unsigned long flags; - u32 recognise = ETHARC_ab_m; /* always accept broadcasts */ - - if (dev->flags & IFF_PROMISC) /* set promiscuous mode */ - recognise |= ETHARC_pro_m; - - if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 15)) - recognise |= ETHARC_am_m; /* all multicast & bcast */ - else if (dev->mc_count > 0) { - DBG(2, __FUNCTION__ ": mc_count %d\n", dev->mc_count); - recognise |= ETHARC_am_m; /* for the time being */ - } - - spin_lock_irqsave(&lp->lock, flags); - __raw_writel(recognise, &lp->eth_regs->etharc); - spin_unlock_irqrestore(&lp->lock, flags); -} - - -static void rc32434_tx_timeout(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - unsigned long flags; - - spin_lock_irqsave(&lp->lock, flags); - rc32434_restart(dev); - spin_unlock_irqrestore(&lp->lock, flags); - -} - - -/* - * Initialize the RC32434 ethernet controller. - */ -static int rc32434_init(struct net_device *dev) -{ - struct rc32434_local *lp = (struct rc32434_local *)dev->priv; - int i, j; - - /* Disable DMA */ - rc32434_abort_tx(dev); - rc32434_abort_rx(dev); - - /* reset ethernet logic */ - __raw_writel(0, &lp->eth_regs->ethintfc); - while((__raw_readl(&lp->eth_regs->ethintfc) & ETHINTFC_rip_m)) - dev->trans_start = jiffies; - - /* Enable Ethernet Interface */ - __raw_writel(ETHINTFC_en_m, &lp->eth_regs->ethintfc); - - tasklet_disable(lp->tx_tasklet); - - /* Initialize the transmit Descriptors */ - for (i = 0; i < RC32434_NUM_TDS; i++) { - lp->td_ring[i].control = DMAD_iof_m; - lp->td_ring[i].devcs = ETHTX_fd_m | ETHTX_ld_m; - lp->td_ring[i].ca = 0; - lp->td_ring[i].link = 0; - if (lp->tx_skb[i] != NULL) { - dev_kfree_skb_any(lp->tx_skb[i]); - lp->tx_skb[i] = NULL; - } - } - lp->tx_next_done = lp->tx_chain_head = lp->tx_chain_tail = lp->tx_full = lp->tx_count = 0; - lp-> tx_chain_status = empty; - - /* - * Initialize the receive descriptors so that they - * become a circular linked list, ie. let the last - * descriptor point to the first again. - */ - for (i=0; irx_skb[i]; - - if (lp->rx_skb[i] == NULL) { - skb = dev_alloc_skb(RC32434_RBSIZE + 2); - if (skb == NULL) { - ERR("No memory in the system\n"); - for (j = 0; j < RC32434_NUM_RDS; j ++) - if (lp->rx_skb[j] != NULL) - dev_kfree_skb_any(lp->rx_skb[j]); - - return 1; - } - else { - skb->dev = dev; - skb_reserve(skb, 2); - lp->rx_skb[i] = skb; - lp->rd_ring[i].ca = CPHYSADDR(skb->data); - - } - } - lp->rd_ring[i].control = DMAD_iod_m | DMA_COUNT(RC32434_RBSIZE); - lp->rd_ring[i].devcs = 0; - lp->rd_ring[i].ca = CPHYSADDR(skb->data); - lp->rd_ring[i].link = CPHYSADDR(&lp->rd_ring[i+1]); - - } - /* loop back */ - lp->rd_ring[RC32434_NUM_RDS-1].link = CPHYSADDR(&lp->rd_ring[0]); - lp->rx_next_done = 0; - - lp->rd_ring[RC32434_NUM_RDS-1].control |= DMAD_cod_m; - lp->rx_chain_head = 0; - lp->rx_chain_tail = 0; - lp->rx_chain_status = empty; - - __raw_writel(0, &lp->rx_dma_regs->dmas); - /* Start Rx DMA */ - rc32434_start_rx(lp, &lp->rd_ring[0]); - - /* Enable F E bit in Tx DMA */ - __raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); - /* Enable D H E bit in Rx DMA */ - __raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); - - /* Accept only packets destined for this Ethernet device address */ - __raw_writel(ETHARC_ab_m, &lp->eth_regs->etharc); - - /* Set all Ether station address registers to their initial values */ - __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal0); - __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah0); - - __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal1); - __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah1); - - __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal2); - __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah2); - - __raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal3); - __raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah3); - - - /* Frame Length Checking, Pad Enable, CRC Enable, Full Duplex set */ - __raw_writel(ETHMAC2_pe_m | ETHMAC2_cen_m | ETHMAC2_fd_m, &lp->eth_regs->ethmac2); - //ETHMAC2_flc_m ETHMAC2_fd_m lp->duplex_mode - - /* Back to back inter-packet-gap */ - __raw_writel(0x15, &lp->eth_regs->ethipgt); - /* Non - Back to back inter-packet-gap */ - __raw_writel(0x12, &lp->eth_regs->ethipgr); - - /* Management Clock Prescaler Divisor */ - /* Clock independent setting */ - __raw_writel(((idt_cpu_freq)/MII_CLOCK+1) & ~1, - &lp->eth_regs->ethmcp); - - /* don't transmit until fifo contains 48b */ - __raw_writel(48, &lp->eth_regs->ethfifott); - - __raw_writel(ETHMAC1_re_m, &lp->eth_regs->ethmac1); - - napi_enable(&lp->napi); - tasklet_enable(lp->tx_tasklet); - - netif_start_queue(dev); - - return 0; -} - -static struct platform_driver korina_driver = { - .driver.name = "korina", - .probe = rc32434_probe, - .remove = rc32434_remove, -}; - -static int __init rc32434_init_module(void) -{ - return platform_driver_register(&korina_driver); -} - -static void rc32434_cleanup_module(void) -{ - return platform_driver_unregister(&korina_driver); -} - -module_init(rc32434_init_module); -module_exit(rc32434_cleanup_module); diff --git a/target/linux/rb532/files/drivers/net/rc32434_eth.h b/target/linux/rb532/files/drivers/net/rc32434_eth.h deleted file mode 100644 index 5ac6dc3546..0000000000 --- a/target/linux/rb532/files/drivers/net/rc32434_eth.h +++ /dev/null @@ -1,173 +0,0 @@ -/************************************************************************** - * - * BRIEF MODULE DESCRIPTION - * Definitions for IDT RC32434 on-chip ethernet controller. - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - ************************************************************************** - * May 2004 rkt, neb - * - * Initial Release - * - * Aug 2004 - * - * Added NAPI - * - ************************************************************************** - */ - - -#include -#include -#include - -#define CONFIG_IDT_USE_NAPI 1 -#define RC32434_DEBUG 2 -//#define RC32434_PROC_DEBUG -#undef RC32434_DEBUG - -#ifdef RC32434_DEBUG - -/* use 0 for production, 1 for verification, >2 for debug */ -static int rc32434_debug = RC32434_DEBUG; -#define ASSERT(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); } -#define DBG(lvl, format, arg...) if (rc32434_debug > lvl) printk(KERN_INFO "%s: " format, dev->name , ## arg) -#else -#define ASSERT(expr) do {} while (0) -#define DBG(lvl, format, arg...) do {} while (0) -#endif - -#define INFO(format, arg...) printk(KERN_INFO "%s: " format, dev->name , ## arg) -#define ERR(format, arg...) printk(KERN_ERR "%s: " format, dev->name , ## arg) -#define WARN(format, arg...) printk(KERN_WARNING "%s: " format, dev->name , ## arg) - -/* the following must be powers of two */ -#ifdef CONFIG_IDT_USE_NAPI -#define RC32434_NUM_RDS 64 /* number of receive descriptors */ -#define RC32434_NUM_TDS 64 /* number of transmit descriptors */ -#else -#define RC32434_NUM_RDS 128 /* number of receive descriptors */ -#define RC32434_NUM_TDS 128 /* number of transmit descriptors */ -#endif - -#define RC32434_RBSIZE 1536 /* size of one resource buffer = Ether MTU */ -#define RC32434_RDS_MASK (RC32434_NUM_RDS-1) -#define RC32434_TDS_MASK (RC32434_NUM_TDS-1) -#define RD_RING_SIZE (RC32434_NUM_RDS * sizeof(struct DMAD_s)) -#define TD_RING_SIZE (RC32434_NUM_TDS * sizeof(struct DMAD_s)) - -#define RC32434_TX_TIMEOUT HZ * 100 - -#define rc32434_eth0_regs ((ETH_t)(ETH0_VirtualAddress)) -#define rc32434_eth1_regs ((ETH_t)(ETH1_VirtualAddress)) - -enum status { filled, empty}; -#define IS_DMA_FINISHED(X) (((X) & (DMAD_f_m)) != 0) -#define IS_DMA_DONE(X) (((X) & (DMAD_d_m)) != 0) - - -/* Information that need to be kept for each board. */ -struct rc32434_local { - ETH_t eth_regs; - DMA_Chan_t rx_dma_regs; - DMA_Chan_t tx_dma_regs; - volatile DMAD_t td_ring; /* transmit descriptor ring */ - volatile DMAD_t rd_ring; /* receive descriptor ring */ - - struct sk_buff* tx_skb[RC32434_NUM_TDS]; /* skbuffs for pkt to trans */ - struct sk_buff* rx_skb[RC32434_NUM_RDS]; /* skbuffs for pkt to trans */ - - struct tasklet_struct * tx_tasklet; - - int rx_next_done; - int rx_chain_head; - int rx_chain_tail; - enum status rx_chain_status; - - int tx_next_done; - int tx_chain_head; - int tx_chain_tail; - enum status tx_chain_status; - int tx_count; - int tx_full; - - struct timer_list mii_phy_timer; - unsigned long duplex_mode; - - int rx_irq; - int tx_irq; - int ovr_irq; - int und_irq; - - struct net_device_stats stats; - spinlock_t lock; - - /* debug /proc entry */ - struct proc_dir_entry *ps; - int dma_halt_cnt; int dma_run_cnt; - struct napi_struct napi; - struct net_device *dev; -}; - -extern unsigned int idt_cpu_freq; - -/* Index to functions, as function prototypes. */ -static int rc32434_open(struct net_device *dev); -static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev); -static void rc32434_mii_handler(unsigned long data); -static irqreturn_t rc32434_und_interrupt(int irq, void *dev_id); -static irqreturn_t rc32434_rx_dma_interrupt(int irq, void *dev_id); -static irqreturn_t rc32434_tx_dma_interrupt(int irq, void *dev_id); -#ifdef RC32434_REVISION -static irqreturn_t rc32434_ovr_interrupt(int irq, void *dev_id); -#endif -static int rc32434_close(struct net_device *dev); -static struct net_device_stats *rc32434_get_stats(struct net_device *dev); -static void rc32434_multicast_list(struct net_device *dev); -static int rc32434_init(struct net_device *dev); -static void rc32434_tx_timeout(struct net_device *dev); - -static void rc32434_tx_tasklet(unsigned long tx_data_dev); -static int rc32434_poll(struct napi_struct *napi, int budget); -static void rc32434_cleanup_module(void); - - -static inline void rc32434_abort_dma(struct net_device *dev, DMA_Chan_t ch) -{ - if (__raw_readl(&ch->dmac) & DMAC_run_m) { - __raw_writel(0x10, &ch->dmac); - - while (!(__raw_readl(&ch->dmas) & DMAS_h_m)) - dev->trans_start = jiffies; - - __raw_writel(0, &ch->dmas); - } - - __raw_writel(0, &ch->dmadptr); - __raw_writel(0, &ch->dmandptr); -} diff --git a/target/linux/rb532/files/drivers/watchdog/rc32434_wdt.c b/target/linux/rb532/files/drivers/watchdog/rc32434_wdt.c deleted file mode 100644 index 2266329def..0000000000 --- a/target/linux/rb532/files/drivers/watchdog/rc32434_wdt.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * RC32434_WDT 0.01: IDT Interprise 79RC32434 watchdog driver - * Copyright (c) Ondrej Zajicek , 2006 - * - * based on - * - * SoftDog 0.05: A Software Watchdog Device - * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEFAULT_TIMEOUT 15 /* (secs) Default is 15 seconds */ -#define MAX_TIMEOUT 20 -/* - * (secs) Max is 20 seconds - * (max frequency of counter is ~200 MHz, counter is 32-bit unsigned int) - */ - -#define NAME "rc32434_wdt" -#define VERSION "0.1" - -static INTEG_t rc_wdt = (INTEG_t) INTEG_VirtualAddress; - -static int expect_close = 0; -static int access = 0; -static int timeout = 0; - -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -MODULE_LICENSE("GPL"); - - -static inline void start_wdt(void) -{ - rc_wdt -> wtcount = 0; - rc_wdt -> errcs |= ERRCS_wre_m; - rc_wdt -> wtc |= WTC_en_m; -} - -static inline void stop_wdt(void) -{ - rc_wdt -> wtc &= ~WTC_en_m; - rc_wdt -> errcs &= ~ERRCS_wre_m; -} - -static inline void set_wdt(int new_timeout) -{ - u32 cmp = new_timeout * mips_hpt_frequency; - u32 state; - - timeout = new_timeout; - /* - * store and disable WTC - */ - state = rc_wdt -> wtc & WTC_en_m; - rc_wdt -> wtc &= ~WTC_en_m; - - rc_wdt -> wtcount = 0; - rc_wdt -> wtcompare = cmp; - - /* - * restore WTC - */ - rc_wdt -> wtc |= state; -} - -static inline void update_wdt(void) -{ - rc_wdt -> wtcount = 0; -} - -/* - * Allow only one person to hold it open - */ - -static int wdt_open(struct inode *inode, struct file *file) -{ - if (access) - return -EBUSY; - if (nowayout) { - __module_get(THIS_MODULE); - } - /* - * Activate timer - */ - start_wdt(); - printk(KERN_INFO NAME ": enabling watchdog timer\n"); - access = 1; - return 0; -} - -static int wdt_release(struct inode *inode, struct file *file) -{ - /* - * Shut off the timer. - * Lock it in if it's a module and we set nowayout - */ - if (expect_close && nowayout == 0) { - stop_wdt (); - printk(KERN_INFO NAME ": disabling watchdog timer\n"); - module_put(THIS_MODULE); - } else { - printk (KERN_CRIT NAME ": device closed unexpectedly. WDT will not stop!\n"); - } - access = 0; - return 0; -} - -static ssize_t wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - /* - * Refresh the timer. - */ - if (len) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - expect_close = 1; - } - } - update_wdt (); - return len; - } - return 0; -} - -static int wdt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int new_timeout; - static struct watchdog_info ident = { - .options = WDIOF_SETTIMEOUT | - WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, - .firmware_version = 0, - .identity = "RC32434_WDT Watchdog", - }; - switch (cmd) { - default: - return -ENOTTY; - case WDIOC_GETSUPPORT: - if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) - return -EFAULT; - return 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0,(int *)arg); - case WDIOC_KEEPALIVE: - update_wdt(); - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, (int *)arg)) - return -EFAULT; - if (new_timeout < 1) - return -EINVAL; - if (new_timeout > MAX_TIMEOUT) - return -EINVAL; - set_wdt(new_timeout); - /* Fall */ - case WDIOC_GETTIMEOUT: - return put_user(timeout, (int *)arg); - } -} - -static struct file_operations wdt_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - write: wdt_write, - ioctl: wdt_ioctl, - open: wdt_open, - release: wdt_release, -}; - -static struct miscdevice wdt_miscdev = { - minor: WATCHDOG_MINOR, - name: "watchdog", - fops: &wdt_fops, -}; - -static char banner[] __initdata = KERN_INFO NAME ": Watchdog Timer version " VERSION ", timer margin: %d sec\n"; - -static int __init watchdog_init(void) -{ - int ret; - - /* - * There should be check for RC32434 SoC - */ - if (mips_machgroup != MACH_GROUP_MIKROTIK) return -1; - - ret = misc_register(&wdt_miscdev); - - if (ret) - return ret; - - stop_wdt(); - set_wdt(DEFAULT_TIMEOUT); - - printk(banner, timeout); - - return 0; -} - -static void __exit watchdog_exit(void) -{ - misc_deregister(&wdt_miscdev); -} - -module_init(watchdog_init); -module_exit(watchdog_exit); diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/ddr.h b/target/linux/rb532/files/include/asm-mips/rc32434/ddr.h deleted file mode 100644 index 03923a6588..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/ddr.h +++ /dev/null @@ -1,173 +0,0 @@ -#ifndef __IDT_DDR_H__ -#define __IDT_DDR_H__ - -/******************************************************************************* - * - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * DDR register definition. - * - * File : $Id: ddr.h,v 1.2 2002/06/06 18:34:03 astichte Exp $ - * - * Author : ryan.holmQVist@idt.com - * Date : 20011005 - * Update : - * $Log: ddr.h,v $ - * Revision 1.2 2002/06/06 18:34:03 astichte - * Added XXX_PhysicalAddress and XXX_VirtualAddress - * - * Revision 1.1 2002/05/29 17:33:21 sysarch - * jba File moved from vcode/include/idt/acacia - * - * - ******************************************************************************/ - -enum -{ - DDR0_PhysicalAddress = 0x18018000, - DDR_PhysicalAddress = DDR0_PhysicalAddress, // Default - - DDR0_VirtualAddress = 0xb8018000, - DDR_VirtualAddress = DDR0_VirtualAddress, // Default -} ; - -typedef struct DDR_s -{ - u32 ddrbase ; - u32 ddrmask ; - u32 res1; - u32 res2; - u32 ddrc ; - u32 ddrabase ; - u32 ddramask ; - u32 ddramap ; - u32 ddrcust; - u32 ddrrdc; - u32 ddrspare; -} volatile *DDR_t ; - -enum -{ - DDR0BASE_baseaddr_b = 16, - DDR0BASE_baseaddr_m = 0xffff0000, - - DDR0MASK_mask_b = 16, - DDR0MASK_mask_m = 0xffff0000, - - DDR1BASE_baseaddr_b = 16, - DDR1BASE_baseaddr_m = 0xffff0000, - - DDR1MASK_mask_b = 16, - DDR1MASK_mask_m = 0xffff0000, - - DDRC_ata_b = 5, - DDRC_ata_m = 0x000000E0, - DDRC_dbw_b = 8, - DDRC_dbw_m = 0x00000100, - DDRC_wr_b = 9, - DDRC_wr_m = 0x00000600, - DDRC_ps_b = 11, - DDRC_ps_m = 0x00001800, - DDRC_dtype_b = 13, - DDRC_dtype_m = 0x0000e000, - DDRC_rfc_b = 16, - DDRC_rfc_m = 0x000f0000, - DDRC_rp_b = 20, - DDRC_rp_m = 0x00300000, - DDRC_ap_b = 22, - DDRC_ap_m = 0x00400000, - DDRC_rcd_b = 23, - DDRC_rcd_m = 0x01800000, - DDRC_cl_b = 25, - DDRC_cl_m = 0x06000000, - DDRC_dbm_b = 27, - DDRC_dbm_m = 0x08000000, - DDRC_sds_b = 28, - DDRC_sds_m = 0x10000000, - DDRC_atp_b = 29, - DDRC_atp_m = 0x60000000, - DDRC_re_b = 31, - DDRC_re_m = 0x80000000, - - DDRRDC_ces_b = 0, - DDRRDC_ces_m = 0x00000001, - DDRRDC_ace_b = 1, - DDRRDC_ace_m = 0x00000002, - - DDRABASE_baseaddr_b = 16, - DDRABASE_baseaddr_m = 0xffff0000, - - DDRAMASK_mask_b = 16, - DDRAMASK_mask_m = 0xffff0000, - - DDRAMAP_map_b = 16, - DDRAMAP_map_m = 0xffff0000, - - DDRCUST_cs_b = 0, - DDRCUST_cs_m = 0x00000003, - DDRCUST_we_b = 2, - DDRCUST_we_m = 0x00000004, - DDRCUST_ras_b = 3, - DDRCUST_ras_m = 0x00000008, - DDRCUST_cas_b = 4, - DDRCUST_cas_m = 0x00000010, - DDRCUST_cke_b = 5, - DDRCUST_cke_m = 0x00000020, - DDRCUST_ba_b = 6, - DDRCUST_ba_m = 0x000000c0, - - RCOUNT_rcount_b = 0, - RCOUNT_rcount_m = 0x0000ffff, - - RCOMPARE_rcompare_b = 0, - RCOMPARE_rcompare_m = 0x0000ffff, - - RTC_ce_b = 0, - RTC_ce_m = 0x00000001, - RTC_to_b = 1, - RTC_to_m = 0x00000002, - RTC_rqe_b = 2, - RTC_rqe_m = 0x00000004, - - DDRDQSC_dm_b = 0, - DDRDQSC_dm_m = 0x00000003, - DDRDQSC_dqsbs_b = 2, - DDRDQSC_dqsbs_m = 0x000000fc, - DDRDQSC_db_b = 8, - DDRDQSC_db_m = 0x00000100, - DDRDQSC_dbsp_b = 9, - DDRDQSC_dbsp_m = 0x01fffe00, - DDRDQSC_bdp_b = 25, - DDRDQSC_bdp_m = 0x7e000000, - - DDRDLLC_eao_b = 0, - DDRDLLC_eao_m = 0x00000001, - DDRDLLC_eo_b = 1, - DDRDLLC_eo_m = 0x0000003e, - DDRDLLC_fs_b = 6, - DDRDLLC_fs_m = 0x000000c0, - DDRDLLC_as_b = 8, - DDRDLLC_as_m = 0x00000700, - DDRDLLC_sp_b = 11, - DDRDLLC_sp_m = 0x001ff800, - - DDRDLLFC_men_b = 0, - DDRDLLFC_men_m = 0x00000001, - DDRDLLFC_aen_b = 1, - DDRDLLFC_aen_m = 0x00000002, - DDRDLLFC_ff_b = 2, - DDRDLLFC_ff_m = 0x00000004, - - DDRDLLTA_addr_b = 2, - DDRDLLTA_addr_m = 0xfffffffc, - - DDRDLLED_dbe_b = 0, - DDRDLLED_dbe_m = 0x00000001, - DDRDLLED_dte_b = 1, - DDRDLLED_dte_m = 0x00000002, - - -} ; - -#endif // __IDT_DDR_H__ diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/dma.h b/target/linux/rb532/files/include/asm-mips/rc32434/dma.h deleted file mode 100644 index 2124f7e510..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/dma.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef __IDT_DMA_H__ -#define __IDT_DMA_H__ - -/******************************************************************************* - * - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * DMA register definition. - * - * File : $Id: dma.h,v 1.3 2002/06/06 18:34:03 astichte Exp $ - * - * Author : ryan.holmQVist@idt.com - * Date : 20011005 - * Update : - * $Log: dma.h,v $ - * Revision 1.3 2002/06/06 18:34:03 astichte - * Added XXX_PhysicalAddress and XXX_VirtualAddress - * - * Revision 1.2 2002/06/05 18:30:46 astichte - * Removed IDTField - * - * Revision 1.1 2002/05/29 17:33:21 sysarch - * jba File moved from vcode/include/idt/acacia - * - * - ******************************************************************************/ - -enum -{ - DMA0_PhysicalAddress = 0x18040000, - DMA_PhysicalAddress = DMA0_PhysicalAddress, // Default - - DMA0_VirtualAddress = 0xb8040000, - DMA_VirtualAddress = DMA0_VirtualAddress, // Default -} ; - -/* - * DMA descriptor (in physical memory). - */ - -typedef struct DMAD_s -{ - u32 control ; // Control. use DMAD_* - u32 ca ; // Current Address. - u32 devcs ; // Device control and status. - u32 link ; // Next descriptor in chain. -} volatile *DMAD_t ; - -enum -{ - DMAD_size = sizeof (struct DMAD_s), - DMAD_count_b = 0, // in DMAD_t -> control - DMAD_count_m = 0x0003ffff, // in DMAD_t -> control - DMAD_ds_b = 20, // in DMAD_t -> control - DMAD_ds_m = 0x00300000, // in DMAD_t -> control - DMAD_ds_ethRcv_v = 0, - DMAD_ds_ethXmt_v = 0, - DMAD_ds_memToFifo_v = 0, - DMAD_ds_fifoToMem_v = 0, - DMAD_ds_pciToMem_v = 0, - DMAD_ds_memToPci_v = 0, - - DMAD_devcmd_b = 22, // in DMAD_t -> control - DMAD_devcmd_m = 0x01c00000, // in DMAD_t -> control - DMAD_devcmd_byte_v = 0, //memory-to-memory - DMAD_devcmd_halfword_v = 1, //memory-to-memory - DMAD_devcmd_word_v = 2, //memory-to-memory - DMAD_devcmd_2words_v = 3, //memory-to-memory - DMAD_devcmd_4words_v = 4, //memory-to-memory - DMAD_devcmd_6words_v = 5, //memory-to-memory - DMAD_devcmd_8words_v = 6, //memory-to-memory - DMAD_devcmd_16words_v = 7, //memory-to-memory - DMAD_cof_b = 25, // chain on finished - DMAD_cof_m = 0x02000000, // - DMAD_cod_b = 26, // chain on done - DMAD_cod_m = 0x04000000, // - DMAD_iof_b = 27, // interrupt on finished - DMAD_iof_m = 0x08000000, // - DMAD_iod_b = 28, // interrupt on done - DMAD_iod_m = 0x10000000, // - DMAD_t_b = 29, // terminated - DMAD_t_m = 0x20000000, // - DMAD_d_b = 30, // done - DMAD_d_m = 0x40000000, // - DMAD_f_b = 31, // finished - DMAD_f_m = 0x80000000, // -} ; - -/* - * DMA register (within Internal Register Map). - */ - -struct DMA_Chan_s -{ - u32 dmac ; // Control. - u32 dmas ; // Status. - u32 dmasm ; // Mask. - u32 dmadptr ; // Descriptor pointer. - u32 dmandptr ; // Next descriptor pointer. -}; - -typedef struct DMA_Chan_s volatile *DMA_Chan_t ; - -//DMA_Channels use DMACH_count instead - -enum -{ - DMAC_run_b = 0, // - DMAC_run_m = 0x00000001, // - DMAC_dm_b = 1, // done mask - DMAC_dm_m = 0x00000002, // - DMAC_mode_b = 2, // - DMAC_mode_m = 0x0000000c, // - DMAC_mode_auto_v = 0, - DMAC_mode_burst_v = 1, - DMAC_mode_transfer_v = 2, //usually used - DMAC_mode_reserved_v = 3, - DMAC_a_b = 4, // - DMAC_a_m = 0x00000010, // - - DMAS_f_b = 0, // finished (sticky) - DMAS_f_m = 0x00000001, // - DMAS_d_b = 1, // done (sticky) - DMAS_d_m = 0x00000002, // - DMAS_c_b = 2, // chain (sticky) - DMAS_c_m = 0x00000004, // - DMAS_e_b = 3, // error (sticky) - DMAS_e_m = 0x00000008, // - DMAS_h_b = 4, // halt (sticky) - DMAS_h_m = 0x00000010, // - - DMASM_f_b = 0, // finished (1=mask) - DMASM_f_m = 0x00000001, // - DMASM_d_b = 1, // done (1=mask) - DMASM_d_m = 0x00000002, // - DMASM_c_b = 2, // chain (1=mask) - DMASM_c_m = 0x00000004, // - DMASM_e_b = 3, // error (1=mask) - DMASM_e_m = 0x00000008, // - DMASM_h_b = 4, // halt (1=mask) - DMASM_h_m = 0x00000010, // -} ; - -/* - * DMA channel definitions - */ - -enum -{ - DMACH_ethRcv = 0, - DMACH_ethXmt = 1, - DMACH_memToFifo = 2, - DMACH_fifoToMem = 3, - DMACH_pciToMem = 4, - DMACH_memToPci = 5, - - DMACH_count //must be last -}; - - -typedef struct DMAC_s -{ - struct DMA_Chan_s ch [DMACH_count] ; //use ch[DMACH_] -} volatile *DMA_t ; - -#endif // __IDT_DMA_H__ - diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/dma_v.h b/target/linux/rb532/files/include/asm-mips/rc32434/dma_v.h deleted file mode 100644 index 81cb40505d..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/dma_v.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef __IDT_DMA_V_H__ -#define __IDT_DMA_V_H__ - -/******************************************************************************* - * - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * DMA register definition. - * - * File : $Id: dma.h,v 1.3 2002/06/06 18:34:03 astichte Exp $ - * - * Author : ryan.holmQVist@idt.com - * Date : 20011005 - * Update : - * $Log: dma.h,v $ - * Revision 1.3 2002/06/06 18:34:03 astichte - * Added XXX_PhysicalAddress and XXX_VirtualAddress - * - * Revision 1.2 2002/06/05 18:30:46 astichte - * Removed IDTField - * - * Revision 1.1 2002/05/29 17:33:21 sysarch - * jba File moved from vcode/include/idt/acacia - * - * - ******************************************************************************/ -#include -#include -#define DMA_CHAN_OFFSET 0x14 -#define IS_DMA_USED(X) (((X) & (DMAD_f_m | DMAD_d_m | DMAD_t_m)) != 0) -#define DMA_COUNT(count) \ - ((count) & DMAD_count_m) - -#define DMA_HALT_TIMEOUT 500 - - -static inline int rc32434_halt_dma(DMA_Chan_t ch) -{ - int timeout=1; - if (local_readl(&ch->dmac) & DMAC_run_m) { - local_writel(0, &ch->dmac); - for (timeout = DMA_HALT_TIMEOUT; timeout > 0; timeout--) { - if (local_readl(&ch->dmas) & DMAS_h_m) { - local_writel(0, &ch->dmas); - break; - } - } - } - - return timeout ? 0 : 1; -} - -static inline void rc32434_start_dma(DMA_Chan_t ch, u32 dma_addr) -{ - local_writel(0, &ch->dmandptr); - local_writel(dma_addr, &ch->dmadptr); -} - -static inline void rc32434_chain_dma(DMA_Chan_t ch, u32 dma_addr) -{ - local_writel(dma_addr, &ch->dmandptr); -} - -#endif // __IDT_DMA_V_H__ - - - - - - - diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/eth.h b/target/linux/rb532/files/include/asm-mips/rc32434/eth.h deleted file mode 100644 index 2f42976508..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/eth.h +++ /dev/null @@ -1,320 +0,0 @@ -#ifndef __IDT_ETH_H__ -#define __IDT_ETH_H__ - -/******************************************************************************* - * - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * Ethernet register definition. - * - * File : $Id: eth.h,v 1.3 2002/06/06 18:34:04 astichte Exp $ - * - * Author : Allen.Stichter@idt.com - * Date : 20020605 - * Update : - * $Log: eth.h,v $ - * Revision 1.3 2002/06/06 18:34:04 astichte - * Added XXX_PhysicalAddress and XXX_VirtualAddress - * - * Revision 1.2 2002/06/05 18:19:46 astichte - * Added - * - * Revision 1.1 2002/05/29 17:33:22 sysarch - * jba File moved from vcode/include/idt/acacia - * - ******************************************************************************/ - -enum -{ - ETH0_PhysicalAddress = 0x18060000, - ETH_PhysicalAddress = ETH0_PhysicalAddress, // Default - - ETH0_VirtualAddress = 0xb8060000, - ETH_VirtualAddress = ETH0_VirtualAddress, // Default -} ; - -typedef struct -{ - u32 ethintfc ; - u32 ethfifott ; - u32 etharc ; - u32 ethhash0 ; - u32 ethhash1 ; - u32 ethu0 [4] ; // Reserved. - u32 ethpfs ; - u32 ethmcp ; - u32 eth_u1 [10] ; // Reserved. - u32 ethspare ; - u32 eth_u2 [42] ; // Reserved. - u32 ethsal0 ; - u32 ethsah0 ; - u32 ethsal1 ; - u32 ethsah1 ; - u32 ethsal2 ; - u32 ethsah2 ; - u32 ethsal3 ; - u32 ethsah3 ; - u32 ethrbc ; - u32 ethrpc ; - u32 ethrupc ; - u32 ethrfc ; - u32 ethtbc ; - u32 ethgpf ; - u32 eth_u9 [50] ; // Reserved. - u32 ethmac1 ; - u32 ethmac2 ; - u32 ethipgt ; - u32 ethipgr ; - u32 ethclrt ; - u32 ethmaxf ; - u32 eth_u10 ; // Reserved. - u32 ethmtest ; - u32 miimcfg ; - u32 miimcmd ; - u32 miimaddr ; - u32 miimwtd ; - u32 miimrdd ; - u32 miimind ; - u32 eth_u11 ; // Reserved. - u32 eth_u12 ; // Reserved. - u32 ethcfsa0 ; - u32 ethcfsa1 ; - u32 ethcfsa2 ; -} volatile *ETH_t; - -enum -{ - ETHINTFC_en_b = 0, - ETHINTFC_en_m = 0x00000001, - ETHINTFC_its_b = 1, - ETHINTFC_its_m = 0x00000002, - ETHINTFC_rip_b = 2, - ETHINTFC_rip_m = 0x00000004, - ETHINTFC_jam_b = 3, - ETHINTFC_jam_m = 0x00000008, - ETHINTFC_ovr_b = 4, - ETHINTFC_ovr_m = 0x00000010, - ETHINTFC_und_b = 5, - ETHINTFC_und_m = 0x00000020, - ETHINTFC_iom_b = 6, - ETHINTFC_iom_m = 0x000000c0, - - ETHFIFOTT_tth_b = 0, - ETHFIFOTT_tth_m = 0x0000007f, - - ETHARC_pro_b = 0, - ETHARC_pro_m = 0x00000001, - ETHARC_am_b = 1, - ETHARC_am_m = 0x00000002, - ETHARC_afm_b = 2, - ETHARC_afm_m = 0x00000004, - ETHARC_ab_b = 3, - ETHARC_ab_m = 0x00000008, - - ETHSAL_byte5_b = 0, - ETHSAL_byte5_m = 0x000000ff, - ETHSAL_byte4_b = 8, - ETHSAL_byte4_m = 0x0000ff00, - ETHSAL_byte3_b = 16, - ETHSAL_byte3_m = 0x00ff0000, - ETHSAL_byte2_b = 24, - ETHSAL_byte2_m = 0xff000000, - - ETHSAH_byte1_b = 0, - ETHSAH_byte1_m = 0x000000ff, - ETHSAH_byte0_b = 8, - ETHSAH_byte0_m = 0x0000ff00, - - ETHGPF_ptv_b = 0, - ETHGPF_ptv_m = 0x0000ffff, - - ETHPFS_pfd_b = 0, - ETHPFS_pfd_m = 0x00000001, - - ETHCFSA0_cfsa4_b = 0, - ETHCFSA0_cfsa4_m = 0x000000ff, - ETHCFSA0_cfsa5_b = 8, - ETHCFSA0_cfsa5_m = 0x0000ff00, - - ETHCFSA1_cfsa2_b = 0, - ETHCFSA1_cfsa2_m = 0x000000ff, - ETHCFSA1_cfsa3_b = 8, - ETHCFSA1_cfsa3_m = 0x0000ff00, - - ETHCFSA2_cfsa0_b = 0, - ETHCFSA2_cfsa0_m = 0x000000ff, - ETHCFSA2_cfsa1_b = 8, - ETHCFSA2_cfsa1_m = 0x0000ff00, - - ETHMAC1_re_b = 0, - ETHMAC1_re_m = 0x00000001, - ETHMAC1_paf_b = 1, - ETHMAC1_paf_m = 0x00000002, - ETHMAC1_rfc_b = 2, - ETHMAC1_rfc_m = 0x00000004, - ETHMAC1_tfc_b = 3, - ETHMAC1_tfc_m = 0x00000008, - ETHMAC1_lb_b = 4, - ETHMAC1_lb_m = 0x00000010, - ETHMAC1_mr_b = 31, - ETHMAC1_mr_m = 0x80000000, - - ETHMAC2_fd_b = 0, - ETHMAC2_fd_m = 0x00000001, - ETHMAC2_flc_b = 1, - ETHMAC2_flc_m = 0x00000002, - ETHMAC2_hfe_b = 2, - ETHMAC2_hfe_m = 0x00000004, - ETHMAC2_dc_b = 3, - ETHMAC2_dc_m = 0x00000008, - ETHMAC2_cen_b = 4, - ETHMAC2_cen_m = 0x00000010, - ETHMAC2_pe_b = 5, - ETHMAC2_pe_m = 0x00000020, - ETHMAC2_vpe_b = 6, - ETHMAC2_vpe_m = 0x00000040, - ETHMAC2_ape_b = 7, - ETHMAC2_ape_m = 0x00000080, - ETHMAC2_ppe_b = 8, - ETHMAC2_ppe_m = 0x00000100, - ETHMAC2_lpe_b = 9, - ETHMAC2_lpe_m = 0x00000200, - ETHMAC2_nb_b = 12, - ETHMAC2_nb_m = 0x00001000, - ETHMAC2_bp_b = 13, - ETHMAC2_bp_m = 0x00002000, - ETHMAC2_ed_b = 14, - ETHMAC2_ed_m = 0x00004000, - - ETHIPGT_ipgt_b = 0, - ETHIPGT_ipgt_m = 0x0000007f, - - ETHIPGR_ipgr2_b = 0, - ETHIPGR_ipgr2_m = 0x0000007f, - ETHIPGR_ipgr1_b = 8, - ETHIPGR_ipgr1_m = 0x00007f00, - - ETHCLRT_maxret_b = 0, - ETHCLRT_maxret_m = 0x0000000f, - ETHCLRT_colwin_b = 8, - ETHCLRT_colwin_m = 0x00003f00, - - ETHMAXF_maxf_b = 0, - ETHMAXF_maxf_m = 0x0000ffff, - - ETHMTEST_tb_b = 2, - ETHMTEST_tb_m = 0x00000004, - - ETHMCP_div_b = 0, - ETHMCP_div_m = 0x000000ff, - - MIIMCFG_rsv_b = 0, - MIIMCFG_rsv_m = 0x0000000c, - - MIIMCMD_rd_b = 0, - MIIMCMD_rd_m = 0x00000001, - MIIMCMD_scn_b = 1, - MIIMCMD_scn_m = 0x00000002, - - MIIMADDR_regaddr_b = 0, - MIIMADDR_regaddr_m = 0x0000001f, - MIIMADDR_phyaddr_b = 8, - MIIMADDR_phyaddr_m = 0x00001f00, - - MIIMWTD_wdata_b = 0, - MIIMWTD_wdata_m = 0x0000ffff, - - MIIMRDD_rdata_b = 0, - MIIMRDD_rdata_m = 0x0000ffff, - - MIIMIND_bsy_b = 0, - MIIMIND_bsy_m = 0x00000001, - MIIMIND_scn_b = 1, - MIIMIND_scn_m = 0x00000002, - MIIMIND_nv_b = 2, - MIIMIND_nv_m = 0x00000004, - -} ; - -/* - * Values for the DEVCS field of the Ethernet DMA Rx and Tx descriptors. - */ -enum -{ - ETHRX_fd_b = 0, - ETHRX_fd_m = 0x00000001, - ETHRX_ld_b = 1, - ETHRX_ld_m = 0x00000002, - ETHRX_rok_b = 2, - ETHRX_rok_m = 0x00000004, - ETHRX_fm_b = 3, - ETHRX_fm_m = 0x00000008, - ETHRX_mp_b = 4, - ETHRX_mp_m = 0x00000010, - ETHRX_bp_b = 5, - ETHRX_bp_m = 0x00000020, - ETHRX_vlt_b = 6, - ETHRX_vlt_m = 0x00000040, - ETHRX_cf_b = 7, - ETHRX_cf_m = 0x00000080, - ETHRX_ovr_b = 8, - ETHRX_ovr_m = 0x00000100, - ETHRX_crc_b = 9, - ETHRX_crc_m = 0x00000200, - ETHRX_cv_b = 10, - ETHRX_cv_m = 0x00000400, - ETHRX_db_b = 11, - ETHRX_db_m = 0x00000800, - ETHRX_le_b = 12, - ETHRX_le_m = 0x00001000, - ETHRX_lor_b = 13, - ETHRX_lor_m = 0x00002000, - ETHRX_ces_b = 14, - ETHRX_ces_m = 0x00004000, - ETHRX_length_b = 16, - ETHRX_length_m = 0xffff0000, - - ETHTX_fd_b = 0, - ETHTX_fd_m = 0x00000001, - ETHTX_ld_b = 1, - ETHTX_ld_m = 0x00000002, - ETHTX_oen_b = 2, - ETHTX_oen_m = 0x00000004, - ETHTX_pen_b = 3, - ETHTX_pen_m = 0x00000008, - ETHTX_cen_b = 4, - ETHTX_cen_m = 0x00000010, - ETHTX_hen_b = 5, - ETHTX_hen_m = 0x00000020, - ETHTX_tok_b = 6, - ETHTX_tok_m = 0x00000040, - ETHTX_mp_b = 7, - ETHTX_mp_m = 0x00000080, - ETHTX_bp_b = 8, - ETHTX_bp_m = 0x00000100, - ETHTX_und_b = 9, - ETHTX_und_m = 0x00000200, - ETHTX_of_b = 10, - ETHTX_of_m = 0x00000400, - ETHTX_ed_b = 11, - ETHTX_ed_m = 0x00000800, - ETHTX_ec_b = 12, - ETHTX_ec_m = 0x00001000, - ETHTX_lc_b = 13, - ETHTX_lc_m = 0x00002000, - ETHTX_td_b = 14, - ETHTX_td_m = 0x00004000, - ETHTX_crc_b = 15, - ETHTX_crc_m = 0x00008000, - ETHTX_le_b = 16, - ETHTX_le_m = 0x00010000, - ETHTX_cc_b = 17, - ETHTX_cc_m = 0x001E0000, -} ; - -#endif // __IDT_ETH_H__ - - - - diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/eth_v.h b/target/linux/rb532/files/include/asm-mips/rc32434/eth_v.h deleted file mode 100644 index 3ea03c6725..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/eth_v.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef __IDT_ETH_V_H__ -#define __IDT_ETH_V_H__ - -/******************************************************************************* - * - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * Ethernet register definition. - * - * File : $Id: eth.h,v 1.3 2002/06/06 18:34:04 astichte Exp $ - * - * Author : Allen.Stichter@idt.com - * Date : 20020605 - * Update : - * $Log: eth.h,v $ - * Revision 1.3 2002/06/06 18:34:04 astichte - * Added XXX_PhysicalAddress and XXX_VirtualAddress - * - * Revision 1.2 2002/06/05 18:19:46 astichte - * Added - * - * Revision 1.1 2002/05/29 17:33:22 sysarch - * jba File moved from vcode/include/idt/acacia - * - ******************************************************************************/ - -#include - -#define IS_TX_TOK(X) (((X) & (1<> ETHTX_tok_b ) /* Transmit Okay */ -#define IS_TX_MP(X) (((X) & (1<> ETHTX_mp_b ) /* Multicast */ -#define IS_TX_BP(X) (((X) & (1<> ETHTX_bp_b ) /* Broadcast */ -#define IS_TX_UND_ERR(X) (((X) & (1<> ETHTX_und_b ) /* Transmit FIFO Underflow */ -#define IS_TX_OF_ERR(X) (((X) & (1<> ETHTX_of_b ) /* Oversized frame */ -#define IS_TX_ED_ERR(X) (((X) & (1<> ETHTX_ed_b ) /* Excessive deferral */ -#define IS_TX_EC_ERR(X) (((X) & (1<> ETHTX_ec_b) /* Excessive collisions */ -#define IS_TX_LC_ERR(X) (((X) & (1<> ETHTX_lc_b ) /* Late Collision */ -#define IS_TX_TD_ERR(X) (((X) & (1<> ETHTX_td_b ) /* Transmit deferred*/ -#define IS_TX_CRC_ERR(X) (((X) & (1<> ETHTX_crc_b ) /* CRC Error */ -#define IS_TX_LE_ERR(X) (((X) & (1<> ETHTX_le_b ) /* Length Error */ - -#define TX_COLLISION_COUNT(X) (((X) & ETHTX_cc_m)>>ETHTX_cc_b) /* Collision Count */ - -#define IS_RCV_ROK(X) (((X) & (1<> ETHRX_rok_b) /* Receive Okay */ -#define IS_RCV_FM(X) (((X) & (1<> ETHRX_fm_b) /* Is Filter Match */ -#define IS_RCV_MP(X) (((X) & (1<> ETHRX_mp_b) /* Is it MP */ -#define IS_RCV_BP(X) (((X) & (1<> ETHRX_bp_b) /* Is it BP */ -#define IS_RCV_VLT(X) (((X) & (1<> ETHRX_vlt_b) /* VLAN Tag Detect */ -#define IS_RCV_CF(X) (((X) & (1<> ETHRX_cf_b) /* Control Frame */ -#define IS_RCV_OVR_ERR(X) (((X) & (1<> ETHRX_ovr_b) /* Receive Overflow */ -#define IS_RCV_CRC_ERR(X) (((X) & (1<> ETHRX_crc_b) /* CRC Error */ -#define IS_RCV_CV_ERR(X) (((X) & (1<> ETHRX_cv_b) /* Code Violation */ -#define IS_RCV_DB_ERR(X) (((X) & (1<> ETHRX_db_b) /* Dribble Bits */ -#define IS_RCV_LE_ERR(X) (((X) & (1<> ETHRX_le_b) /* Length error */ -#define IS_RCV_LOR_ERR(X) (((X) & (1<> ETHRX_lor_b) /* Length Out of Range */ -#define IS_RCV_CES_ERR(X) (((X) & (1<> ETHRX_ces_b) /* Preamble error */ -#define RCVPKT_LENGTH(X) (((X) & ETHRX_length_m) >> ETHRX_length_b) /* Length of the received packet */ -#endif // __IDT_ETH_V_H__ - - - - - diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/gpio.h b/target/linux/rb532/files/include/asm-mips/rc32434/gpio.h deleted file mode 100644 index 859b9c9219..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/gpio.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * GPIO register definition. - * - * Author : ryan.holmQVist@idt.com - * Date : 20011005 - * Copyright (C) 2001, 2002 Ryan Holm - * Copyright (C) 2008 Florian Fainelli - */ - -#ifndef _RC32434_GPIO_H_ -#define _RC32434_GPIO_H_ - -#include - -struct rb500_gpio_reg { - u32 gpiofunc; /* GPIO Function Register - * gpiofunc[x]==0 bit = gpio - * func[x]==1 bit = altfunc - */ - u32 gpiocfg; /* GPIO Configuration Register - * gpiocfg[x]==0 bit = input - * gpiocfg[x]==1 bit = output - */ - u32 gpiod; /* GPIO Data Register - * gpiod[x] read/write gpio pinX status - */ - u32 gpioilevel; /* GPIO Interrupt Status Register - * interrupt level (see gpioistat) - */ - u32 gpioistat; /* Gpio Interrupt Status Register - * istat[x] = (gpiod[x] == level[x]) - * cleared in ISR (STICKY bits) - */ - u32 gpionmien; /* GPIO Non-maskable Interrupt Enable Register */ -}; - -/* UART GPIO signals */ -#define RC32434_UART0_SOUT (1 << 0) -#define RC32434_UART0_SIN (1 << 1) -#define RC32434_UART0_RTS (1 << 2) -#define RC32434_UART0_CTS (1 << 3) - -/* M & P bus GPIO signals */ -#define RC32434_MP_BIT_22 (1 << 4) -#define RC32434_MP_BIT_23 (1 << 5) -#define RC32434_MP_BIT_24 (1 << 6) -#define RC32434_MP_BIT_25 (1 << 7) - -/* CPU GPIO signals */ -#define RC32434_CPU_GPIO (1 << 8) - -/* Reserved GPIO signals */ -#define RC32434_AF_SPARE_6 (1 << 9) -#define RC32434_AF_SPARE_4 (1 << 10) -#define RC32434_AF_SPARE_3 (1 << 11) -#define RC32434_AF_SPARE_2 (1 << 12) - -/* PCI messaging unit */ -#define RC32434_PCI_MSU_GPIO (1 << 13) - -extern int rb500_gpio_get_value(unsigned gpio); -extern void rb500_gpio_set_value(unsigned gpio, int value); -extern int rb500_gpio_direction_input(unsigned gpio); -extern int rb500_gpio_direction_output(unsigned gpio, int value); -extern void rb500_gpio_set_int_level(unsigned gpio, int value); -extern int rb500_gpio_get_int_level(unsigned gpio); -extern void rb500_gpio_set_int_status(unsigned gpio, int value); -extern int rb500_gpio_get_int_status(unsigned gpio); -extern void rb500_gpio_set_func(unsigned gpio, int value); -extern int rb500_gpio_get_func(unsigned gpio); - - -/* Wrappers for the arch-neutral GPIO API */ - -static inline int gpio_request(unsigned gpio, const char *label) -{ - /* Not yet implemented */ - return 0; -} - -static inline void gpio_free(unsigned gpio) -{ - /* Not yet implemented */ -} - -static inline int gpio_direction_input(unsigned gpio) -{ - return rb500_gpio_direction_input(gpio); -} - -static inline int gpio_direction_output(unsigned gpio, int value) -{ - return rb500_gpio_direction_output(gpio, value); -} - -static inline int gpio_get_value(unsigned gpio) -{ - return rb500_gpio_get_value(gpio); -} - -static inline void gpio_set_value(unsigned gpio, int value) -{ - rb500_gpio_set_value(gpio, value); -} - -static inline int gpio_to_irq(unsigned gpio) -{ - return gpio; -} - -static inline int irq_to_gpio(unsigned irq) -{ - return irq; -} - -/* For cansleep */ -#include - -#endif /* _RC32434_GPIO_H_ */ diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/integ.h b/target/linux/rb532/files/include/asm-mips/rc32434/integ.h deleted file mode 100644 index a9e99e4be7..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/integ.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef __IDT_INTEG_H__ -#define __IDT_INTEG_H__ - -/******************************************************************************* - * - * Copyright 2002 Integrated Device Technology, Inc. - * All rights reserved. - * - * System Integrity register definition. - * - * File : $Id: integ.h,v 1.3 2002/06/06 18:34:04 astichte Exp $ - * - * Author : ryan.holmQVist@idt.com - * Date : 20011005 - * Update : - * $Log: integ.h,v $ - * Revision 1.3 2002/06/06 18:34:04 astichte - * Added XXX_PhysicalAddress and XXX_VirtualAddress - * - * Revision 1.2 2002/06/05 18:32:33 astichte - * Removed IDTField - * - * Revision 1.1 2002/05/29 17:33:22 sysarch - * jba File moved from vcode/include/idt/acacia - * - ******************************************************************************/ - -enum -{ - INTEG0_PhysicalAddress = 0x18030000, - INTEG_PhysicalAddress = INTEG0_PhysicalAddress, // Default - - INTEG0_VirtualAddress = 0xb8030000, - INTEG_VirtualAddress = INTEG0_VirtualAddress, // Default -} ; - -// if you are looing for CEA, try rst.h -typedef struct -{ - u32 filler [0xc] ; // 0x30 bytes unused. - u32 errcs ; // sticky use ERRCS_ - u32 wtcount ; // Watchdog timer count reg. - u32 wtcompare ; // Watchdog timer timeout value. - u32 wtc ; // Watchdog timer control. use WTC_ -} volatile *INTEG_t ; - -enum -{ - ERRCS_wto_b = 0, // In INTEG_t -> errcs - ERRCS_wto_m = 0x00000001, - ERRCS_wne_b = 1, // In INTEG_t -> errcs - ERRCS_wne_m = 0x00000002, - ERRCS_ucw_b = 2, // In INTEG_t -> errcs - ERRCS_ucw_m = 0x00000004, - ERRCS_ucr_b = 3, // In INTEG_t -> errcs - ERRCS_ucr_m = 0x00000008, - ERRCS_upw_b = 4, // In INTEG_t -> errcs - ERRCS_upw_m = 0x00000010, - ERRCS_upr_b = 5, // In INTEG_t -> errcs - ERRCS_upr_m = 0x00000020, - ERRCS_udw_b = 6, // In INTEG_t -> errcs - ERRCS_udw_m = 0x00000040, - ERRCS_udr_b = 7, // In INTEG_t -> errcs - ERRCS_udr_m = 0x00000080, - ERRCS_sae_b = 8, // In INTEG_t -> errcs - ERRCS_sae_m = 0x00000100, - ERRCS_wre_b = 9, // In INTEG_t -> errcs - ERRCS_wre_m = 0x00000200, - - WTC_en_b = 0, // In INTEG_t -> wtc - WTC_en_m = 0x00000001, - WTC_to_b = 1, // In INTEG_t -> wtc - WTC_to_m = 0x00000002, -} ; - -#endif // __IDT_INTEG_H__ diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/irq.h b/target/linux/rb532/files/include/asm-mips/rc32434/irq.h deleted file mode 100644 index 65cc13c920..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/irq.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __ASM_MACH_MIPS_IRQ_H -#define __ASM_MACH_MIPS_IRQ_H - -#define NR_IRQS 256 -#include_next - -#endif /* __ASM_MACH_MIPS_IRQ_H */ diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/pci.h b/target/linux/rb532/files/include/asm-mips/rc32434/pci.h deleted file mode 100644 index 585a0ae21b..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/pci.h +++ /dev/null @@ -1,692 +0,0 @@ -/************************************************************************** - * - * BRIEF MODULE DESCRIPTION - * PCI register definitio - * - * Copyright 2004 IDT Inc. (rischelp@idt.com) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * - ************************************************************************** - * May 2004 rkt, neb. - * - * Initial Release - * - * - * - ************************************************************************** - */ - -#ifndef __IDT_PCI_H__ -#define __IDT_PCI_H__ - -enum -{ - PCI0_PhysicalAddress = 0x18080000, - PCI_PhysicalAddress = PCI0_PhysicalAddress, - - PCI0_VirtualAddress = 0xB8080000, - PCI_VirtualAddress = PCI0_VirtualAddress, -} ; - -enum -{ - PCI_LbaCount = 4, // Local base addresses. -} ; - -typedef struct -{ - u32 a ; // Address. - u32 c ; // Control. - u32 m ; // mapping. -} PCI_Map_s ; - -typedef struct -{ - u32 pcic ; - u32 pcis ; - u32 pcism ; - u32 pcicfga ; - u32 pcicfgd ; - PCI_Map_s pcilba [PCI_LbaCount] ; - u32 pcidac ; - u32 pcidas ; - u32 pcidasm ; - u32 pcidad ; - u32 pcidma8c ; - u32 pcidma9c ; - u32 pcitc ; -} volatile *PCI_t ; - -// PCI messaging unit. -enum -{ - PCIM_Count = 2, -} ; -typedef struct -{ - u32 pciim [PCIM_Count] ; - u32 pciom [PCIM_Count] ; - u32 pciid ; - u32 pciiic ; - u32 pciiim ; - u32 pciiod ; - u32 pciioic ; - u32 pciioim ; -} volatile *PCIM_t ; - -/******************************************************************************* - * - * PCI Control Register - * - ******************************************************************************/ -enum -{ - PCIC_en_b = 0, - PCIC_en_m = 0x00000001, - PCIC_tnr_b = 1, - PCIC_tnr_m = 0x00000002, - PCIC_sce_b = 2, - PCIC_sce_m = 0x00000004, - PCIC_ien_b = 3, - PCIC_ien_m = 0x00000008, - PCIC_aaa_b = 4, - PCIC_aaa_m = 0x00000010, - PCIC_eap_b = 5, - PCIC_eap_m = 0x00000020, - PCIC_pcim_b = 6, - PCIC_pcim_m = 0x000001c0, - PCIC_pcim_disabled_v = 0, - PCIC_pcim_tnr_v = 1, // Satellite - target not ready - PCIC_pcim_suspend_v = 2, // Satellite - suspended CPU. - PCIC_pcim_extern_v = 3, // Host - external arbiter. - PCIC_pcim_fixed_v = 4, // Host - fixed priority arb. - PCIC_pcim_roundrobin_v = 5, // Host - round robin priority. - PCIC_pcim_reserved6_v = 6, - PCIC_pcim_reserved7_v = 7, - PCIC_igm_b = 9, - PCIC_igm_m = 0x00000200, -} ; - -/******************************************************************************* - * - * PCI Status Register - * - ******************************************************************************/ -enum { - PCIS_eed_b = 0, - PCIS_eed_m = 0x00000001, - PCIS_wr_b = 1, - PCIS_wr_m = 0x00000002, - PCIS_nmi_b = 2, - PCIS_nmi_m = 0x00000004, - PCIS_ii_b = 3, - PCIS_ii_m = 0x00000008, - PCIS_cwe_b = 4, - PCIS_cwe_m = 0x00000010, - PCIS_cre_b = 5, - PCIS_cre_m = 0x00000020, - PCIS_mdpe_b = 6, - PCIS_mdpe_m = 0x00000040, - PCIS_sta_b = 7, - PCIS_sta_m = 0x00000080, - PCIS_rta_b = 8, - PCIS_rta_m = 0x00000100, - PCIS_rma_b = 9, - PCIS_rma_m = 0x00000200, - PCIS_sse_b = 10, - PCIS_sse_m = 0x00000400, - PCIS_ose_b = 11, - PCIS_ose_m = 0x00000800, - PCIS_pe_b = 12, - PCIS_pe_m = 0x00001000, - PCIS_tae_b = 13, - PCIS_tae_m = 0x00002000, - PCIS_rle_b = 14, - PCIS_rle_m = 0x00004000, - PCIS_bme_b = 15, - PCIS_bme_m = 0x00008000, - PCIS_prd_b = 16, - PCIS_prd_m = 0x00010000, - PCIS_rip_b = 17, - PCIS_rip_m = 0x00020000, -} ; - -/******************************************************************************* - * - * PCI Status Mask Register - * - ******************************************************************************/ -enum { - PCISM_eed_b = 0, - PCISM_eed_m = 0x00000001, - PCISM_wr_b = 1, - PCISM_wr_m = 0x00000002, - PCISM_nmi_b = 2, - PCISM_nmi_m = 0x00000004, - PCISM_ii_b = 3, - PCISM_ii_m = 0x00000008, - PCISM_cwe_b = 4, - PCISM_cwe_m = 0x00000010, - PCISM_cre_b = 5, - PCISM_cre_m = 0x00000020, - PCISM_mdpe_b = 6, - PCISM_mdpe_m = 0x00000040, - PCISM_sta_b = 7, - PCISM_sta_m = 0x00000080, - PCISM_rta_b = 8, - PCISM_rta_m = 0x00000100, - PCISM_rma_b = 9, - PCISM_rma_m = 0x00000200, - PCISM_sse_b = 10, - PCISM_sse_m = 0x00000400, - PCISM_ose_b = 11, - PCISM_ose_m = 0x00000800, - PCISM_pe_b = 12, - PCISM_pe_m = 0x00001000, - PCISM_tae_b = 13, - PCISM_tae_m = 0x00002000, - PCISM_rle_b = 14, - PCISM_rle_m = 0x00004000, - PCISM_bme_b = 15, - PCISM_bme_m = 0x00008000, - PCISM_prd_b = 16, - PCISM_prd_m = 0x00010000, - PCISM_rip_b = 17, - PCISM_rip_m = 0x00020000, -} ; - -/******************************************************************************* - * - * PCI Configuration Address Register - * - ******************************************************************************/ -enum { - PCICFGA_reg_b = 2, - PCICFGA_reg_m = 0x000000fc, - PCICFGA_reg_id_v = 0x00>>2, //use PCFGID_ - PCICFGA_reg_04_v = 0x04>>2, //use PCFG04_ - PCICFGA_reg_08_v = 0x08>>2, //use PCFG08_ - PCICFGA_reg_0C_v = 0x0C>>2, //use PCFG0C_ - PCICFGA_reg_pba0_v = 0x10>>2, //use PCIPBA_ - PCICFGA_reg_pba1_v = 0x14>>2, //use PCIPBA_ - PCICFGA_reg_pba2_v = 0x18>>2, //use PCIPBA_ - PCICFGA_reg_pba3_v = 0x1c>>2, //use PCIPBA_ - PCICFGA_reg_subsystem_v = 0x2c>>2, //use PCFGSS_ - PCICFGA_reg_3C_v = 0x3C>>2, //use PCFG3C_ - PCICFGA_reg_pba0c_v = 0x44>>2, //use PCIPBAC_ - PCICFGA_reg_pba0m_v = 0x48>>2, - PCICFGA_reg_pba1c_v = 0x4c>>2, //use PCIPBAC_ - PCICFGA_reg_pba1m_v = 0x50>>2, - PCICFGA_reg_pba2c_v = 0x54>>2, //use PCIPBAC_ - PCICFGA_reg_pba2m_v = 0x58>>2, - PCICFGA_reg_pba3c_v = 0x5c>>2, //use PCIPBAC_ - PCICFGA_reg_pba3m_v = 0x60>>2, - PCICFGA_reg_pmgt_v = 0x64>>2, - PCICFGA_func_b = 8, - PCICFGA_func_m = 0x00000700, - PCICFGA_dev_b = 11, - PCICFGA_dev_m = 0x0000f800, - PCICFGA_dev_internal_v = 0, - PCICFGA_bus_b = 16, - PCICFGA_bus_m = 0x00ff0000, - PCICFGA_bus_type0_v = 0, //local bus - PCICFGA_en_b = 31, // read only - PCICFGA_en_m = 0x80000000, -} ; - -enum { - PCFGID_vendor_b = 0, - PCFGID_vendor_m = 0x0000ffff, - PCFGID_vendor_IDT_v = 0x111d, - PCFGID_device_b = 16, - PCFGID_device_m = 0xffff0000, - PCFGID_device_Korinade_v = 0x0214, - - PCFG04_command_ioena_b = 1, - PCFG04_command_ioena_m = 0x00000001, - PCFG04_command_memena_b = 2, - PCFG04_command_memena_m = 0x00000002, - PCFG04_command_bmena_b = 3, - PCFG04_command_bmena_m = 0x00000004, - PCFG04_command_mwinv_b = 5, - PCFG04_command_mwinv_m = 0x00000010, - PCFG04_command_parena_b = 7, - PCFG04_command_parena_m = 0x00000040, - PCFG04_command_serrena_b = 9, - PCFG04_command_serrena_m = 0x00000100, - PCFG04_command_fastbbena_b = 10, - PCFG04_command_fastbbena_m = 0x00000200, - PCFG04_status_b = 16, - PCFG04_status_m = 0xffff0000, - PCFG04_status_66MHz_b = 21, // 66 MHz enable - PCFG04_status_66MHz_m = 0x00200000, - PCFG04_status_fbb_b = 23, - PCFG04_status_fbb_m = 0x00800000, - PCFG04_status_mdpe_b = 24, - PCFG04_status_mdpe_m = 0x01000000, - PCFG04_status_dst_b = 25, - PCFG04_status_dst_m = 0x06000000, - PCFG04_status_sta_b = 27, - PCFG04_status_sta_m = 0x08000000, - PCFG04_status_rta_b = 28, - PCFG04_status_rta_m = 0x10000000, - PCFG04_status_rma_b = 29, - PCFG04_status_rma_m = 0x20000000, - PCFG04_status_sse_b = 30, - PCFG04_status_sse_m = 0x40000000, - PCFG04_status_pe_b = 31, - PCFG04_status_pe_m = 0x40000000, - - PCFG08_revId_b = 0, - PCFG08_revId_m = 0x000000ff, - PCFG08_classCode_b = 0, - PCFG08_classCode_m = 0xffffff00, - PCFG08_classCode_bridge_v = 06, - PCFG08_classCode_proc_v = 0x0b3000, // processor-MIPS - PCFG0C_cacheline_b = 0, - PCFG0C_cacheline_m = 0x000000ff, - PCFG0C_masterLatency_b = 8, - PCFG0C_masterLatency_m = 0x0000ff00, - PCFG0C_headerType_b = 16, - PCFG0C_headerType_m = 0x00ff0000, - PCFG0C_bist_b = 24, - PCFG0C_bist_m = 0xff000000, - - PCIPBA_msi_b = 0, - PCIPBA_msi_m = 0x00000001, - PCIPBA_p_b = 3, - PCIPBA_p_m = 0x00000004, - PCIPBA_baddr_b = 8, - PCIPBA_baddr_m = 0xffffff00, - - PCFGSS_vendorId_b = 0, - PCFGSS_vendorId_m = 0x0000ffff, - PCFGSS_id_b = 16, - PCFGSS_id_m = 0xffff0000, - - PCFG3C_interruptLine_b = 0, - PCFG3C_interruptLine_m = 0x000000ff, - PCFG3C_interruptPin_b = 8, - PCFG3C_interruptPin_m = 0x0000ff00, - PCFG3C_minGrant_b = 16, - PCFG3C_minGrant_m = 0x00ff0000, - PCFG3C_maxLat_b = 24, - PCFG3C_maxLat_m = 0xff000000, - - PCIPBAC_msi_b = 0, - PCIPBAC_msi_m = 0x00000001, - PCIPBAC_p_b = 1, - PCIPBAC_p_m = 0x00000002, - PCIPBAC_size_b = 2, - PCIPBAC_size_m = 0x0000007c, - PCIPBAC_sb_b = 7, - PCIPBAC_sb_m = 0x00000080, - PCIPBAC_pp_b = 8, - PCIPBAC_pp_m = 0x00000100, - PCIPBAC_mr_b = 9, - PCIPBAC_mr_m = 0x00000600, - PCIPBAC_mr_read_v =0, //no prefetching - PCIPBAC_mr_readLine_v =1, - PCIPBAC_mr_readMult_v =2, - PCIPBAC_mrl_b = 11, - PCIPBAC_mrl_m = 0x00000800, - PCIPBAC_mrm_b = 12, - PCIPBAC_mrm_m = 0x00001000, - PCIPBAC_trp_b = 13, - PCIPBAC_trp_m = 0x00002000, - - PCFG40_trdyTimeout_b = 0, - PCFG40_trdyTimeout_m = 0x000000ff, - PCFG40_retryLim_b = 8, - PCFG40_retryLim_m = 0x0000ff00, -}; - -/******************************************************************************* - * - * PCI Local Base Address [0|1|2|3] Register - * - ******************************************************************************/ -enum { - PCILBA_baddr_b = 0, // In PCI_t -> pcilba [] .a - PCILBA_baddr_m = 0xffffff00, -} ; -/******************************************************************************* - * - * PCI Local Base Address Control Register - * - ******************************************************************************/ -enum { - PCILBAC_msi_b = 0, // In pPci->pcilba[i].c - PCILBAC_msi_m = 0x00000001, - PCILBAC_msi_mem_v = 0, - PCILBAC_msi_io_v = 1, - PCILBAC_size_b = 2, // In pPci->pcilba[i].c - PCILBAC_size_m = 0x0000007c, - PCILBAC_sb_b = 7, // In pPci->pcilba[i].c - PCILBAC_sb_m = 0x00000080, - PCILBAC_rt_b = 8, // In pPci->pcilba[i].c - PCILBAC_rt_m = 0x00000100, - PCILBAC_rt_noprefetch_v = 0, // mem read - PCILBAC_rt_prefetch_v = 1, // mem readline -} ; - -/******************************************************************************* - * - * PCI Local Base Address [0|1|2|3] Mapping Register - * - ******************************************************************************/ -enum { - PCILBAM_maddr_b = 8, - PCILBAM_maddr_m = 0xffffff00, -} ; - -/******************************************************************************* - * - * PCI Decoupled Access Control Register - * - ******************************************************************************/ -enum { - PCIDAC_den_b = 0, - PCIDAC_den_m = 0x00000001, -} ; - -/******************************************************************************* - * - * PCI Decoupled Access Status Register - * - ******************************************************************************/ -enum { - PCIDAS_d_b = 0, - PCIDAS_d_m = 0x00000001, - PCIDAS_b_b = 1, - PCIDAS_b_m = 0x00000002, - PCIDAS_e_b = 2, - PCIDAS_e_m = 0x00000004, - PCIDAS_ofe_b = 3, - PCIDAS_ofe_m = 0x00000008, - PCIDAS_off_b = 4, - PCIDAS_off_m = 0x00000010, - PCIDAS_ife_b = 5, - PCIDAS_ife_m = 0x00000020, - PCIDAS_iff_b = 6, - PCIDAS_iff_m = 0x00000040, -} ; - -/******************************************************************************* - * - * PCI DMA Channel 8 Configuration Register - * - ******************************************************************************/ -enum -{ - PCIDMA8C_mbs_b = 0, // Maximum Burst Size. - PCIDMA8C_mbs_m = 0x00000fff, // { pcidma8c } - PCIDMA8C_our_b = 12, // Optimize Unaligned Burst Reads. - PCIDMA8C_our_m = 0x00001000, // { pcidma8c } -} ; - -/******************************************************************************* - * - * PCI DMA Channel 9 Configuration Register - * - ******************************************************************************/ -enum -{ - PCIDMA9C_mbs_b = 0, // Maximum Burst Size. - PCIDMA9C_mbs_m = 0x00000fff, // { pcidma9c } -} ; - -/******************************************************************************* - * - * PCI to Memory(DMA Channel 8) AND Memory to PCI DMA(DMA Channel 9)Descriptors - * - ******************************************************************************/ -enum { - PCIDMAD_pt_b = 22, // in DEVCMD field (descriptor) - PCIDMAD_pt_m = 0x00c00000, // preferred transaction field - // These are for reads (DMA channel 8) - PCIDMAD_devcmd_mr_v = 0, //memory read - PCIDMAD_devcmd_mrl_v = 1, //memory read line - PCIDMAD_devcmd_mrm_v = 2, //memory read multiple - PCIDMAD_devcmd_ior_v = 3, //I/O read - // These are for writes (DMA channel 9) - PCIDMAD_devcmd_mw_v = 0, //memory write - PCIDMAD_devcmd_mwi_v = 1, //memory write invalidate - PCIDMAD_devcmd_iow_v = 3, //I/O write - - // Swap byte field applies to both DMA channel 8 and 9 - PCIDMAD_sb_b = 24, // in DEVCMD field (descriptor) - PCIDMAD_sb_m = 0x01000000, // swap byte field -} ; - - -/******************************************************************************* - * - * PCI Target Control Register - * - ******************************************************************************/ -enum -{ - PCITC_rtimer_b = 0, // In PCITC_t -> pcitc - PCITC_rtimer_m = 0x000000ff, - PCITC_dtimer_b = 8, // In PCITC_t -> pcitc - PCITC_dtimer_m = 0x0000ff00, - PCITC_rdr_b = 18, // In PCITC_t -> pcitc - PCITC_rdr_m = 0x00040000, - PCITC_ddt_b = 19, // In PCITC_t -> pcitc - PCITC_ddt_m = 0x00080000, -} ; -/******************************************************************************* - * - * PCI messaging unit [applies to both inbound and outbound registers ] - * - ******************************************************************************/ -enum -{ - PCIM_m0_b = 0, // In PCIM_t -> {pci{iic,iim,ioic,ioim}} - PCIM_m0_m = 0x00000001, // inbound or outbound message 0 - PCIM_m1_b = 1, // In PCIM_t -> {pci{iic,iim,ioic,ioim}} - PCIM_m1_m = 0x00000002, // inbound or outbound message 1 - PCIM_db_b = 2, // In PCIM_t -> {pci{iic,iim,ioic,ioim}} - PCIM_db_m = 0x00000004, // inbound or outbound doorbell -}; - - - - - - -#define PCI_MSG_VirtualAddress 0xB8088010 -#define rc32434_pci ((volatile PCI_t) PCI0_VirtualAddress) -#define rc32434_pci_msg ((volatile PCIM_t) PCI_MSG_VirtualAddress) - -#define PCIM_SHFT 0x6 -#define PCIM_BIT_LEN 0x7 -#define PCIM_H_EA 0x3 -#define PCIM_H_IA_FIX 0x4 -#define PCIM_H_IA_RR 0x5 - -#define PCI_ADDR_START 0x50000000 - -#define CPUTOPCI_MEM_WIN 0x02000000 -#define CPUTOPCI_IO_WIN 0x00100000 -#define PCILBA_SIZE_SHFT 2 -#define PCILBA_SIZE_MASK 0x1F -#define SIZE_256MB 0x1C -#define SIZE_128MB 0x1B -#define SIZE_64MB 0x1A -#define SIZE_32MB 0x19 -#define SIZE_16MB 0x18 -#define SIZE_4MB 0x16 -#define SIZE_2MB 0x15 -#define SIZE_1MB 0x14 -#define KORINA_CONFIG0_ADDR 0x80000000 -#define KORINA_CONFIG1_ADDR 0x80000004 -#define KORINA_CONFIG2_ADDR 0x80000008 -#define KORINA_CONFIG3_ADDR 0x8000000C -#define KORINA_CONFIG4_ADDR 0x80000010 -#define KORINA_CONFIG5_ADDR 0x80000014 -#define KORINA_CONFIG6_ADDR 0x80000018 -#define KORINA_CONFIG7_ADDR 0x8000001C -#define KORINA_CONFIG8_ADDR 0x80000020 -#define KORINA_CONFIG9_ADDR 0x80000024 -#define KORINA_CONFIG10_ADDR 0x80000028 -#define KORINA_CONFIG11_ADDR 0x8000002C -#define KORINA_CONFIG12_ADDR 0x80000030 -#define KORINA_CONFIG13_ADDR 0x80000034 -#define KORINA_CONFIG14_ADDR 0x80000038 -#define KORINA_CONFIG15_ADDR 0x8000003C -#define KORINA_CONFIG16_ADDR 0x80000040 -#define KORINA_CONFIG17_ADDR 0x80000044 -#define KORINA_CONFIG18_ADDR 0x80000048 -#define KORINA_CONFIG19_ADDR 0x8000004C -#define KORINA_CONFIG20_ADDR 0x80000050 -#define KORINA_CONFIG21_ADDR 0x80000054 -#define KORINA_CONFIG22_ADDR 0x80000058 -#define KORINA_CONFIG23_ADDR 0x8000005C -#define KORINA_CONFIG24_ADDR 0x80000060 -#define KORINA_CONFIG25_ADDR 0x80000064 -#define KORINA_CMD (PCFG04_command_ioena_m | \ - PCFG04_command_memena_m | \ - PCFG04_command_bmena_m | \ - PCFG04_command_mwinv_m | \ - PCFG04_command_parena_m | \ - PCFG04_command_serrena_m ) - -#define KORINA_STAT (PCFG04_status_mdpe_m | \ - PCFG04_status_sta_m | \ - PCFG04_status_rta_m | \ - PCFG04_status_rma_m | \ - PCFG04_status_sse_m | \ - PCFG04_status_pe_m) - -#define KORINA_CNFG1 ((KORINA_STAT<<16)|KORINA_CMD) - -#define KORINA_REVID 0 -#define KORINA_CLASS_CODE 0 -#define KORINA_CNFG2 ((KORINA_CLASS_CODE<<8) | \ - KORINA_REVID) - -#define KORINA_CACHE_LINE_SIZE 4 -#define KORINA_MASTER_LAT 0x3c -#define KORINA_HEADER_TYPE 0 -#define KORINA_BIST 0 - -#define KORINA_CNFG3 ((KORINA_BIST << 24) | \ - (KORINA_HEADER_TYPE<<16) | \ - (KORINA_MASTER_LAT<<8) | \ - KORINA_CACHE_LINE_SIZE ) - -#define KORINA_BAR0 0x00000008 /* 128 MB Memory */ -#define KORINA_BAR1 0x18800001 /* 1 MB IO */ -#define KORINA_BAR2 0x18000001 /* 2 MB IO window for Korina - internal Registers */ -#define KORINA_BAR3 0x48000008 /* Spare 128 MB Memory */ - -#define KORINA_CNFG4 KORINA_BAR0 -#define KORINA_CNFG5 KORINA_BAR1 -#define KORINA_CNFG6 KORINA_BAR2 -#define KORINA_CNFG7 KORINA_BAR3 - -#define KORINA_SUBSYS_VENDOR_ID 0x011d -#define KORINA_SUBSYSTEM_ID 0x0214 -#define KORINA_CNFG8 0 -#define KORINA_CNFG9 0 -#define KORINA_CNFG10 0 -#define KORINA_CNFG11 ((KORINA_SUBSYS_VENDOR_ID<<16) | \ - KORINA_SUBSYSTEM_ID) -#define KORINA_INT_LINE 1 -#define KORINA_INT_PIN 1 -#define KORINA_MIN_GNT 8 -#define KORINA_MAX_LAT 0x38 -#define KORINA_CNFG12 0 -#define KORINA_CNFG13 0 -#define KORINA_CNFG14 0 -#define KORINA_CNFG15 ((KORINA_MAX_LAT<<24) | \ - (KORINA_MIN_GNT<<16) | \ - (KORINA_INT_PIN<<8) | \ - KORINA_INT_LINE) -#define KORINA_RETRY_LIMIT 0x80 -#define KORINA_TRDY_LIMIT 0x80 -#define KORINA_CNFG16 ((KORINA_RETRY_LIMIT<<8) | \ - KORINA_TRDY_LIMIT) -#define PCI_PBAxC_R 0x0 -#define PCI_PBAxC_RL 0x1 -#define PCI_PBAxC_RM 0x2 -#define SIZE_SHFT 2 - -#if defined(__MIPSEB__) -#define KORINA_PBA0C ( PCIPBAC_mrl_m | PCIPBAC_sb_m | \ - ((PCI_PBAxC_RM &0x3) << PCIPBAC_mr_b) | \ - PCIPBAC_pp_m | \ - (SIZE_128MB< - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ -#ifndef __MIPS_RB_H__ -#define __MIPS_RB_H__ -#include - -#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000)) -#define DEV0BASE 0x010000 -#define DEV0MASK 0x010004 -#define DEV0C 0x010008 -#define DEV0TC 0x01000C -#define DEV1BASE 0x010010 -#define DEV1MASK 0x010014 -#define DEV1C 0x010018 -#define DEV1TC 0x01001C -#define DEV2BASE 0x010020 -#define DEV2MASK 0x010024 -#define DEV2C 0x010028 -#define DEV2TC 0x01002C -#define DEV3BASE 0x010030 -#define DEV3MASK 0x010034 -#define DEV3C 0x010038 -#define DEV3TC 0x01003C -#define BTCS 0x010040 -#define BTCOMPARE 0x010044 -#define GPIOFUNC 0x050000 -#define GPIOCFG 0x050004 -#define GPIOD 0x050008 -#define GPIOILEVEL 0x05000C -#define GPIOISTAT 0x050010 -#define GPIONMIEN 0x050014 -#define IMASK6 0x038038 - -#define LO_WPX (1 << 0) -#define LO_ALE (1 << 1) -#define LO_CLE (1 << 2) -#define LO_CEX (1 << 3) -#define LO_FOFF (1 << 5) -#define LO_SPICS (1 << 6) -#define LO_ULED (1 << 7) - -typedef enum { - FUNC = 0x00, - CFG = 0x04, - DATA = 0x08, - ILEVEL = 0x0c, - ISTAT = 0x10, - NMIEN = 0x14 -} gpio_func; - -extern void changeLatchU5(unsigned char orMask, unsigned char nandMask); -extern unsigned get434Reg(unsigned regOffs); -extern void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val); -extern void gpio_set(gpio_func func, u32 mask, u32 value); -extern u32 gpio_get(gpio_func func); - -#define get434Reg(x) (*(volatile unsigned *) (IDT434_REG_BASE + (x))) - -struct korina_device { - char *name; - unsigned char mac[6]; - struct net_device *dev; -}; - -struct cf_device { - int gpio_pin; - void *dev; - struct gendisk *gd; -}; - -#endif diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/rc32434.h b/target/linux/rb532/files/include/asm-mips/rc32434/rc32434.h deleted file mode 100644 index f3e53e4332..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/rc32434.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - *************************************************************************** - * Definitions for IDT RC323434 CPU. - * - **************************************************************************** - * Kiran Rao - * - * Original form - **************************************************************************** - * P. Sadik Oct 08, 2003 - * - * Started revision history - * Made IDT_BUS_FREQ a kernel configuration parameter - **************************************************************************** - * P. Sadik Oct 10, 2003 - * - * Removed IDT_BUS_FREQ, since this parameter is no longer required. Instead - * idt_cpu_freq is used everywhere - **************************************************************************** - * P. Sadik Oct 20, 2003 - * - * Removed RC32434_BASE_BAUD - **************************************************************************** -*/ -#ifndef _RC32434_H_ -#define _RC32434_H_ - -#include -#include -#include - -#define RC32434_REG_BASE 0x18000000 - -#define interrupt ((volatile INT_t ) INT0_VirtualAddress) - - -#define IDT_CLOCK_MULT 2 -#define MIPS_CPU_TIMER_IRQ 7 -/* Interrupt Controller */ -#define IC_GROUP0_PEND (RC32434_REG_BASE + 0x38000) -#define IC_GROUP0_MASK (RC32434_REG_BASE + 0x38008) -#define IC_GROUP_OFFSET 0x0C - -#define NUM_INTR_GROUPS 5 -/* 16550 UARTs */ - -#define GROUP0_IRQ_BASE 8 /* GRP2 IRQ numbers start here */ -#define GROUP1_IRQ_BASE (GROUP0_IRQ_BASE + 32) /* GRP3 IRQ numbers start here */ -#define GROUP2_IRQ_BASE (GROUP1_IRQ_BASE + 32) /* GRP4 IRQ numbers start here */ -#define GROUP3_IRQ_BASE (GROUP2_IRQ_BASE + 32) /* GRP5 IRQ numbers start here */ -#define GROUP4_IRQ_BASE (GROUP3_IRQ_BASE + 32) - - -#ifdef __MIPSEB__ -#define RC32434_UART0_BASE (RC32434_REG_BASE + 0x58003) -#else -#define RC32434_UART0_BASE (RC32434_REG_BASE + 0x58000) -#endif - -#define RC32434_UART0_IRQ GROUP3_IRQ_BASE + 0 -// #define EB434_UART1_IRQ GROUP4_IRQ_BASE + 11 - -#define local_readl(addr) __raw_readl(addr) -#define local_writel(l,addr) __raw_writel(l,addr) - -/* cpu pipeline flush */ -static inline void rc32434_sync(void) -{ - __asm__ volatile ("sync"); -} - -static inline void rc32434_sync_udelay(int us) -{ - __asm__ volatile ("sync"); - udelay(us); -} - -static inline void rc32434_sync_delay(int ms) -{ - __asm__ volatile ("sync"); - mdelay(ms); -} - -/* - * C access to CLZ and CLO instructions - * (count leading zeroes/ones). - */ -static inline int rc32434_clz(unsigned long val) -{ - int ret; - __asm__ volatile ( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips32\n\t" - "clz\t%0,%1\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - : "=r" (ret) - : "r" (val)); - - return ret; -} -static inline int rc32434_clo(unsigned long val) -{ - int ret; - __asm__ volatile ( - ".set\tnoreorder\n\t" - ".set\tnoat\n\t" - ".set\tmips32\n\t" - "clo\t%0,%1\n\t" - ".set\tmips0\n\t" - ".set\tat\n\t" - ".set\treorder" - : "=r" (ret) - : "r" (val)); - - return ret; -} - -#endif /* _RC32434_H_ */ diff --git a/target/linux/rb532/files/include/asm-mips/rc32434/war.h b/target/linux/rb532/files/include/asm-mips/rc32434/war.h deleted file mode 100644 index 3ddf187e98..0000000000 --- a/target/linux/rb532/files/include/asm-mips/rc32434/war.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2002, 2004, 2007 by Ralf Baechle - */ -#ifndef __ASM_MIPS_MACH_MIPS_WAR_H -#define __ASM_MIPS_MACH_MIPS_WAR_H - -#define R4600_V1_INDEX_ICACHEOP_WAR 0 -#define R4600_V1_HIT_CACHEOP_WAR 0 -#define R4600_V2_HIT_CACHEOP_WAR 0 -#define R5432_CP0_INTERRUPT_WAR 0 -#define BCM1250_M3_WAR 0 -#define SIBYTE_1956_WAR 0 -#define MIPS4K_ICACHE_REFILL_WAR 1 -#define MIPS_CACHE_SYNC_WAR 0 -#define TX49XX_ICACHE_INDEX_INV_WAR 0 -#define RM9000_CDEX_SMP_WAR 0 -#define ICACHE_REFILLS_WORKAROUND_WAR 0 -#define R10000_LLSC_WAR 0 -#define MIPS34K_MISSED_ITLB_WAR 0 - -#endif /* __ASM_MIPS_MACH_MIPS_WAR_H */ diff --git a/target/linux/rb532/patches-2.6.23/100-rb5xx_support.patch b/target/linux/rb532/patches-2.6.23/100-rb5xx_support.patch new file mode 100644 index 0000000000..262ea61e15 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/100-rb5xx_support.patch @@ -0,0 +1,103 @@ +Index: linux-2.6.23.17/arch/mips/Kconfig +=================================================================== +--- linux-2.6.23.17.orig/arch/mips/Kconfig ++++ linux-2.6.23.17/arch/mips/Kconfig +@@ -595,6 +595,20 @@ config WR_PPMC + This enables support for the Wind River MIPS32 4KC PPMC evaluation + board, which is based on GT64120 bridge chip. + ++config MIKROTIK_RB500 ++ bool "Support for RB5xx boards" ++ select HW_HAS_PCI ++ select IRQ_CPU ++ select SYS_HAS_CPU_MIPS32_R1 ++ select SYS_SUPPORTS_LITTLE_ENDIAN ++ select SYS_SUPPORTS_32BIT_KERNEL ++ select SWAP_IO_SPACE ++ select DMA_NONCOHERENT ++ select GENERIC_GPIO ++ help ++ Support the Mikrotik(tm) Routerboard 500 series, ++ such as the RB532. ++ + endchoice + + source "arch/mips/au1000/Kconfig" +@@ -829,6 +843,7 @@ config MIPS_L1_CACHE_SHIFT + default "4" if MACH_DECSTATION + default "7" if SGI_IP27 || SNI_RM + default "4" if PMC_MSP4200_EVAL ++ default "4" if MIKROTIK_RB500 + default "5" + + config HAVE_STD_PC_SERIAL_PORT +Index: linux-2.6.23.17/arch/mips/Makefile +=================================================================== +--- linux-2.6.23.17.orig/arch/mips/Makefile ++++ linux-2.6.23.17/arch/mips/Makefile +@@ -300,6 +300,13 @@ load-$(CONFIG_LEMOTE_FULONG) +=0xfffffff + cflags-$(CONFIG_LEMOTE_FULONG) += -Iinclude/asm-mips/mach-lemote + + # ++# Routerboard 532 board ++# ++core-$(CONFIG_MIKROTIK_RB500) += arch/mips/rb500/ ++cflags-$(CONFIG_MIKROTIK_RB500) += -Iinclude/asm-mips/rc32434 ++load-$(CONFIG_MIKROTIK_RB500) += 0xffffffff80101000 ++ ++# + # For all MIPS, Inc. eval boards + # + core-$(CONFIG_MIPS_BOARDS_GEN) += arch/mips/mips-boards/generic/ +Index: linux-2.6.23.17/arch/mips/pci/Makefile +=================================================================== +--- linux-2.6.23.17.orig/arch/mips/pci/Makefile ++++ linux-2.6.23.17/arch/mips/pci/Makefile +@@ -46,3 +46,4 @@ obj-$(CONFIG_TOSHIBA_RBTX4938) += fixup- + obj-$(CONFIG_VICTOR_MPC30X) += fixup-mpc30x.o + obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capcella.o + obj-$(CONFIG_WR_PPMC) += fixup-wrppmc.o ++obj-$(CONFIG_MIKROTIK_RB500) += pci-rc32434.o ops-rc32434.o fixup-rb500.o +Index: linux-2.6.23.17/drivers/pci/Makefile +=================================================================== +--- linux-2.6.23.17.orig/drivers/pci/Makefile ++++ linux-2.6.23.17/drivers/pci/Makefile +@@ -32,6 +32,7 @@ obj-$(CONFIG_PPC32) += setup-irq.o + obj-$(CONFIG_PPC64) += setup-bus.o + obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o + obj-$(CONFIG_X86_VISWS) += setup-irq.o ++obj-$(CONFIG_MIKROTIK_RB500) += setup-irq.o + + # + # ACPI Related PCI FW Functions +Index: linux-2.6.23.17/include/asm-mips/bootinfo.h +=================================================================== +--- linux-2.6.23.17.orig/include/asm-mips/bootinfo.h ++++ linux-2.6.23.17/include/asm-mips/bootinfo.h +@@ -208,6 +208,11 @@ + #define MACH_GROUP_WINDRIVER 28 /* Windriver boards */ + #define MACH_WRPPMC 1 + ++ ++#define MACH_GROUP_MIKROTIK 29 /* Mikrotik Boards */ ++#define MACH_MIKROTIK_RB532 0 /* Mikrotik RouterBoard 532 */ ++#define MACH_MIKROTIK_RB532A 1 /* Mikrotik RouterBoard 532A */ ++ + #define CL_SIZE COMMAND_LINE_SIZE + + const char *get_system_type(void); +Index: linux-2.6.23.17/include/asm-mips/cpu.h +=================================================================== +--- linux-2.6.23.17.orig/include/asm-mips/cpu.h ++++ linux-2.6.23.17/include/asm-mips/cpu.h +@@ -217,8 +217,9 @@ + #define CPU_R14000 64 + #define CPU_LOONGSON1 65 + #define CPU_LOONGSON2 66 ++#define CPU_RC32300 67 + +-#define CPU_LAST 66 ++#define CPU_LAST 67 + + /* + * ISA Level encodings diff --git a/target/linux/rb532/patches-2.6.23/110-korina_ethernet.patch b/target/linux/rb532/patches-2.6.23/110-korina_ethernet.patch new file mode 100644 index 0000000000..acfba6c00a --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/110-korina_ethernet.patch @@ -0,0 +1,31 @@ +Index: linux-2.6.23.17/drivers/net/Kconfig +=================================================================== +--- linux-2.6.23.17.orig/drivers/net/Kconfig ++++ linux-2.6.23.17/drivers/net/Kconfig +@@ -348,6 +348,13 @@ config AX88796 + AX88796 driver, using platform bus to provide + chip detection and resources + ++config KORINA ++ tristate "Korina Local Ethernet support" ++ depends on NET_ETHERNET && ( IDT_EB434 || MIKROTIK_RB500) ++ help ++ IDT RC32434 has one local ethernet port. Say Y here to enable it. ++ To compile this driver as a module, choose M here. ++ + config MACE + tristate "MACE (Power Mac ethernet) support" + depends on PPC_PMAC && PPC32 +Index: linux-2.6.23.17/drivers/net/Makefile +=================================================================== +--- linux-2.6.23.17.orig/drivers/net/Makefile ++++ linux-2.6.23.17/drivers/net/Makefile +@@ -25,6 +25,8 @@ ucc_geth_driver-objs := ucc_geth.o ucc_g + # + obj-$(CONFIG_PLIP) += plip.o + ++obj-$(CONFIG_KORINA) += korina.o ++ + obj-$(CONFIG_ROADRUNNER) += rrunner.o + + obj-$(CONFIG_HAPPYMEAL) += sunhme.o diff --git a/target/linux/rb532/patches-2.6.23/120-cf.patch b/target/linux/rb532/patches-2.6.23/120-cf.patch new file mode 100644 index 0000000000..522195e451 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/120-cf.patch @@ -0,0 +1,31 @@ +Index: linux-2.6.23.17/drivers/block/Kconfig +=================================================================== +--- linux-2.6.23.17.orig/drivers/block/Kconfig ++++ linux-2.6.23.17/drivers/block/Kconfig +@@ -426,6 +426,14 @@ config SUNVDC + Support for virtual disk devices as a client under Sun + Logical Domains. + ++config BLK_DEV_CF_MIPS ++ bool "CF slot of RB532 board" ++ depends on MIKROTIK_RB500 ++ default y ++ help ++ The Routerboard 532 has a CF slot on it. Enable the special block ++ device driver for it. ++ + source "drivers/s390/block/Kconfig" + + config XILINX_SYSACE +Index: linux-2.6.23.17/drivers/block/Makefile +=================================================================== +--- linux-2.6.23.17.orig/drivers/block/Makefile ++++ linux-2.6.23.17/drivers/block/Makefile +@@ -21,6 +21,7 @@ obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o + obj-$(CONFIG_XILINX_SYSACE) += xsysace.o + obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o + obj-$(CONFIG_SUNVDC) += sunvdc.o ++obj-$(CONFIG_BLK_DEV_CF_MIPS) += rb500/ + + obj-$(CONFIG_BLK_DEV_UMEM) += umem.o + obj-$(CONFIG_BLK_DEV_NBD) += nbd.o diff --git a/target/linux/rb532/patches-2.6.23/140-cmdline_hack.patch b/target/linux/rb532/patches-2.6.23/140-cmdline_hack.patch new file mode 100644 index 0000000000..1e01e953e5 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/140-cmdline_hack.patch @@ -0,0 +1,15 @@ +Index: linux-2.6.23.17/arch/mips/kernel/head.S +=================================================================== +--- linux-2.6.23.17.orig/arch/mips/kernel/head.S ++++ linux-2.6.23.17/arch/mips/kernel/head.S +@@ -132,6 +132,10 @@ + + j kernel_entry + nop ++ ++EXPORT(_image_cmdline) ++ .ascii "CMDLINE:" ++ + #ifndef CONFIG_NO_EXCEPT_FILL + /* + * Reserved space for exception handlers. diff --git a/target/linux/rb532/patches-2.6.23/200-bridge_resource_hack.patch b/target/linux/rb532/patches-2.6.23/200-bridge_resource_hack.patch new file mode 100644 index 0000000000..707935539b --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/200-bridge_resource_hack.patch @@ -0,0 +1,15 @@ +Index: linux-2.6.23.17/arch/mips/pci/pci.c +=================================================================== +--- linux-2.6.23.17.orig/arch/mips/pci/pci.c ++++ linux-2.6.23.17/arch/mips/pci/pci.c +@@ -64,8 +64,10 @@ pcibios_align_resource(void *data, struc + /* + * Put everything into 0x00-0xff region modulo 0x400 + */ ++#ifndef CONFIG_MIKROTIK_RB500 + if (start & 0x300) + start = (start + 0x3ff) & ~0x3ff; ++#endif + } else if (res->flags & IORESOURCE_MEM) { + /* Make sure we start at our min on all hoses */ + if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start) diff --git a/target/linux/rb532/patches-2.6.23/220-serial_fix.patch b/target/linux/rb532/patches-2.6.23/220-serial_fix.patch new file mode 100644 index 0000000000..4af6620fb5 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/220-serial_fix.patch @@ -0,0 +1,18 @@ +Index: linux-2.6.23.17/drivers/serial/8250.c +=================================================================== +--- linux-2.6.23.17.orig/drivers/serial/8250.c ++++ linux-2.6.23.17/drivers/serial/8250.c +@@ -433,6 +433,13 @@ serial_out(struct uart_8250_port *up, in + default: + outb(value, up->port.iobase + offset); + } ++ ++#if defined(CONFIG_IDT_EB434) || defined(CONFIG_MIKROTIK_RB500) ++ __SLOW_DOWN_IO; ++ __SLOW_DOWN_IO; ++ __SLOW_DOWN_IO; ++ __SLOW_DOWN_IO; ++#endif + } + + static void diff --git a/target/linux/rb532/patches-2.6.23/240-via_rhine_performance.patch b/target/linux/rb532/patches-2.6.23/240-via_rhine_performance.patch new file mode 100644 index 0000000000..d82b6f7055 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/240-via_rhine_performance.patch @@ -0,0 +1,100 @@ +Index: linux-2.6.23.17/drivers/net/via-rhine.c +=================================================================== +--- linux-2.6.23.17.orig/drivers/net/via-rhine.c ++++ linux-2.6.23.17/drivers/net/via-rhine.c +@@ -33,6 +33,8 @@ + #define DRV_VERSION "1.4.3" + #define DRV_RELDATE "2007-03-06" + ++#define PKT_ALIGN 1 ++ + + /* A few user-configurable values. + These may be modified when a driver module is loaded. */ +@@ -40,6 +42,7 @@ + static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ + static int max_interrupt_work = 20; + ++#ifndef PKT_ALIGN + /* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ + #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ +@@ -49,6 +52,7 @@ static int rx_copybreak = 1518; + #else + static int rx_copybreak; + #endif ++#endif /* PKT_ALIGN */ + + /* Work-around for broken BIOSes: they are unable to get the chip back out of + power state D3 so PXE booting fails. bootparam(7): via-rhine.avoid_D3=1 */ +@@ -111,6 +115,7 @@ static const int multicast_filter_limit + #include + #include + #include ++#include + #include + + /* These identify the driver base version and may not be removed. */ +@@ -130,12 +135,14 @@ MODULE_LICENSE("GPL"); + + module_param(max_interrupt_work, int, 0); + module_param(debug, int, 0); +-module_param(rx_copybreak, int, 0); + module_param(avoid_D3, bool, 0); + MODULE_PARM_DESC(max_interrupt_work, "VIA Rhine maximum events handled per interrupt"); + MODULE_PARM_DESC(debug, "VIA Rhine debug level (0-7)"); +-MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames"); + MODULE_PARM_DESC(avoid_D3, "Avoid power state D3 (work-around for broken BIOSes)"); ++#ifndef PKT_ALIGN ++module_param(rx_copybreak, int, 0); ++MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames"); ++#endif + + /* + Theory of Operation +@@ -927,7 +934,7 @@ static void alloc_rbufs(struct net_devic + + /* Fill in the Rx buffers. Handle allocation failure gracefully. */ + for (i = 0; i < RX_RING_SIZE; i++) { +- struct sk_buff *skb = dev_alloc_skb(rp->rx_buf_sz); ++ struct sk_buff *skb = dev_alloc_skb(rp->rx_buf_sz + 4); + rp->rx_skbuff[i] = skb; + if (skb == NULL) + break; +@@ -1484,7 +1491,9 @@ static int rhine_rx(struct net_device *d + struct sk_buff *skb; + /* Length should omit the CRC */ + int pkt_len = data_size - 4; +- ++#ifdef PKT_ALIGN ++ int i; ++#else + /* Check if the packet is long enough to accept without + copying to a minimally-sized skbuff. */ + if (pkt_len < rx_copybreak && +@@ -1503,7 +1512,9 @@ static int rhine_rx(struct net_device *d + rp->rx_skbuff_dma[entry], + rp->rx_buf_sz, + PCI_DMA_FROMDEVICE); +- } else { ++ } else ++#endif ++ { + skb = rp->rx_skbuff[entry]; + if (skb == NULL) { + printk(KERN_ERR "%s: Inconsistent Rx " +@@ -1517,6 +1528,14 @@ static int rhine_rx(struct net_device *d + rp->rx_skbuff_dma[entry], + rp->rx_buf_sz, + PCI_DMA_FROMDEVICE); ++#ifdef PKT_ALIGN ++ /* align the data to the ip header - should be faster than copying the entire packet */ ++ for (i = pkt_len - (pkt_len % 4); i >= 0; i -= 4) { ++ put_unaligned(*((u32 *) (skb->data + i)), (u32 *) (skb->data + i + 2)); ++ } ++ skb->data += 2; ++ skb->tail += 2; ++#endif + } + skb->protocol = eth_type_trans(skb, dev); + #ifdef CONFIG_VIA_RHINE_NAPI diff --git a/target/linux/rb532/patches-2.6.23/510-rb500_led.patch b/target/linux/rb532/patches-2.6.23/510-rb500_led.patch new file mode 100644 index 0000000000..714c112a76 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/510-rb500_led.patch @@ -0,0 +1,29 @@ +Index: linux-2.6.23.17/drivers/leds/Kconfig +=================================================================== +--- linux-2.6.23.17.orig/drivers/leds/Kconfig ++++ linux-2.6.23.17/drivers/leds/Kconfig +@@ -107,6 +107,12 @@ config LEDS_GPIO + outputs. To be useful the particular board must have LEDs + and they must be connected to the GPIO lines. + ++config LEDS_RB500 ++ tristate "LED Support for RB5xx boards" ++ depends LEDS_CLASS && MIKROTIK_RB500 ++ help ++ This option enables support for the yellow user LED on RB5xx boards. ++ + comment "LED Triggers" + + config LEDS_TRIGGERS +Index: linux-2.6.23.17/drivers/leds/Makefile +=================================================================== +--- linux-2.6.23.17.orig/drivers/leds/Makefile ++++ linux-2.6.23.17/drivers/leds/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_ALIX) += leds-alix.o + obj-$(CONFIG_LEDS_H1940) += leds-h1940.o + obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o + obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o ++obj-$(CONFIG_LEDS_RB500) += leds-rb500.o + + # LED Triggers + obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/target/linux/rb532/patches-2.6.23/520-rc32434_wdt.patch b/target/linux/rb532/patches-2.6.23/520-rc32434_wdt.patch new file mode 100644 index 0000000000..32ee23adb7 --- /dev/null +++ b/target/linux/rb532/patches-2.6.23/520-rc32434_wdt.patch @@ -0,0 +1,46 @@ +Index: linux-2.6.23.17/arch/mips/kernel/time.c +=================================================================== +--- linux-2.6.23.17.orig/arch/mips/kernel/time.c ++++ linux-2.6.23.17/arch/mips/kernel/time.c +@@ -479,3 +479,5 @@ EXPORT_SYMBOL(rtc_lock); + EXPORT_SYMBOL(to_tm); + EXPORT_SYMBOL(rtc_mips_set_time); + EXPORT_SYMBOL(rtc_mips_get_time); ++EXPORT_SYMBOL(mips_hpt_frequency); ++ +Index: linux-2.6.23.17/drivers/char/watchdog/Kconfig +=================================================================== +--- linux-2.6.23.17.orig/drivers/char/watchdog/Kconfig ++++ linux-2.6.23.17/drivers/char/watchdog/Kconfig +@@ -647,6 +647,19 @@ config WATCHDOG_RTAS + To compile this driver as a module, choose M here. The module + will be called wdrtas. + ++config RC32434_WDT ++ tristate "IDT Interprise 79RC32434 SoC hardware watchdog" ++ depends on WATCHDOG && MIKROTIK_RB500 ++ help ++ This is a driver for hardware watchdog integrated in IDT Interprise ++ 79RC32434 SoC. This watchdog simply watches your kernel to make sure ++ it doesn't freeze, and if it does, it reboots your computer after a ++ certain amount of time. ++ ++ To compile this driver as a module, choose M here: the module will be ++ called rc32434_wdt. ++ ++ + # S390 Architecture + + config ZVM_WATCHDOG +Index: linux-2.6.23.17/drivers/char/watchdog/Makefile +=================================================================== +--- linux-2.6.23.17.orig/drivers/char/watchdog/Makefile ++++ linux-2.6.23.17/drivers/char/watchdog/Makefile +@@ -90,6 +90,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc + obj-$(CONFIG_INDYDOG) += indydog.o + obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o + obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o ++obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o + + # PARISC Architecture + diff --git a/target/linux/rb532/patches/100-rb5xx_support.patch b/target/linux/rb532/patches/100-rb5xx_support.patch deleted file mode 100644 index 081eced2b4..0000000000 --- a/target/linux/rb532/patches/100-rb5xx_support.patch +++ /dev/null @@ -1,101 +0,0 @@ -diff -urN linux-2.6.24.7/arch/mips/Kconfig linux-2.6.24.7.new/arch/mips/Kconfig ---- linux-2.6.24.7/arch/mips/Kconfig 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/arch/mips/Kconfig 2008-07-15 15:29:51.000000000 +0200 -@@ -687,6 +687,23 @@ - This enables support for the Wind River MIPS32 4KC PPMC evaluation - board, which is based on GT64120 bridge chip. - -+config MIKROTIK_RB500 -+ bool "Support for RB5xx boards" -+ select HW_HAS_PCI -+ select CEVT_R4K -+ select CSRC_R4K -+ select IRQ_CPU -+ select SYS_HAS_CPU_MIPS32_R1 -+ select SYS_SUPPORTS_LITTLE_ENDIAN -+ select SYS_SUPPORTS_32BIT_KERNEL -+ select SWAP_IO_SPACE -+ select DMA_NONCOHERENT -+ select GENERIC_GPIO -+ help -+ Support the Mikrotik(tm) Routerboard 500 series, -+ such as the RB532. -+ -+ - endchoice - - source "arch/mips/au1000/Kconfig" -@@ -967,6 +982,7 @@ - default "4" if MACH_DECSTATION - default "7" if SGI_IP27 || SNI_RM - default "4" if PMC_MSP4200_EVAL -+ default "4" if MIKROTIK_RB500 - default "5" - - config HAVE_STD_PC_SERIAL_PORT -diff -urN linux-2.6.24.7/arch/mips/Makefile linux-2.6.24.7.new/arch/mips/Makefile ---- linux-2.6.24.7/arch/mips/Makefile 2008-07-15 15:27:55.000000000 +0200 -+++ linux-2.6.24.7.new/arch/mips/Makefile 2008-07-15 15:30:21.000000000 +0200 -@@ -297,6 +297,13 @@ - cflags-$(CONFIG_LEMOTE_FULONG) += -Iinclude/asm-mips/mach-lemote - - # -+# Routerboard 532 board -+# -+core-$(CONFIG_MIKROTIK_RB500) += arch/mips/rb500/ -+cflags-$(CONFIG_MIKROTIK_RB500) += -Iinclude/asm-mips/rc32434 -+load-$(CONFIG_MIKROTIK_RB500) += 0xffffffff80101000 -+ -+# - # For all MIPS, Inc. eval boards - # - core-$(CONFIG_MIPS_BOARDS_GEN) += arch/mips/mips-boards/generic/ -diff -urN linux-2.6.24.7/arch/mips/pci/Makefile linux-2.6.24.7.new/arch/mips/pci/Makefile ---- linux-2.6.24.7/arch/mips/pci/Makefile 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/arch/mips/pci/Makefile 2008-07-15 15:30:46.000000000 +0200 -@@ -48,3 +48,4 @@ - obj-$(CONFIG_VICTOR_MPC30X) += fixup-mpc30x.o - obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capcella.o - obj-$(CONFIG_WR_PPMC) += fixup-wrppmc.o -+obj-$(CONFIG_MIKROTIK_RB500) += pci-rc32434.o ops-rc32434.o fixup-rb500.o -diff -urN linux-2.6.24.7/drivers/pci/Makefile linux-2.6.24.7.new/drivers/pci/Makefile ---- linux-2.6.24.7/drivers/pci/Makefile 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/drivers/pci/Makefile 2008-07-15 15:31:20.000000000 +0200 -@@ -35,6 +35,7 @@ - obj-$(CONFIG_PPC64) += setup-bus.o - obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o - obj-$(CONFIG_X86_VISWS) += setup-irq.o -+obj-$(CONFIG_MIKROTIK_RB500) += setup-irq.o - - # - # ACPI Related PCI FW Functions -diff -urN linux-2.6.24.7/include/asm-mips/bootinfo.h linux-2.6.24.7.new/include/asm-mips/bootinfo.h ---- linux-2.6.24.7/include/asm-mips/bootinfo.h 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/include/asm-mips/bootinfo.h 2008-07-15 15:32:06.000000000 +0200 -@@ -198,6 +198,14 @@ - #define MACH_GROUP_BRCM 23 /* Broadcom */ - #define MACH_BCM47XX 1 /* Broadcom BCM47XX */ - -+ -+/* -+ * Valid machtype for group Mikrotik -+ */ -+#define MACH_GROUP_MIKROTIK 29 /* Mikrotik Boards */ -+#define MACH_MIKROTIK_RB532 0 /* Mikrotik RouterBoard 532 */ -+#define MACH_MIKROTIK_RB532A 1 /* Mikrotik RouterBoard 532A */ -+ - #define CL_SIZE COMMAND_LINE_SIZE - - const char *get_system_type(void); -diff -urN linux-2.6.24.7/include/asm-mips/cpu.h linux-2.6.24.7.new/include/asm-mips/cpu.h ---- linux-2.6.24.7/include/asm-mips/cpu.h 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/include/asm-mips/cpu.h 2008-07-15 15:32:22.000000000 +0200 -@@ -196,7 +196,7 @@ - */ - CPU_4KC, CPU_4KEC, CPU_4KSC, CPU_24K, CPU_34K, CPU_74K, CPU_AU1000, - CPU_AU1100, CPU_AU1200, CPU_AU1500, CPU_AU1550, CPU_PR4450, -- CPU_BCM3302, CPU_BCM4710, -+ CPU_BCM3302, CPU_BCM4710, CPU_RC32300, - - /* - * MIPS64 class processors diff --git a/target/linux/rb532/patches/110-korina_ethernet.patch b/target/linux/rb532/patches/110-korina_ethernet.patch deleted file mode 100644 index acfba6c00a..0000000000 --- a/target/linux/rb532/patches/110-korina_ethernet.patch +++ /dev/null @@ -1,31 +0,0 @@ -Index: linux-2.6.23.17/drivers/net/Kconfig -=================================================================== ---- linux-2.6.23.17.orig/drivers/net/Kconfig -+++ linux-2.6.23.17/drivers/net/Kconfig -@@ -348,6 +348,13 @@ config AX88796 - AX88796 driver, using platform bus to provide - chip detection and resources - -+config KORINA -+ tristate "Korina Local Ethernet support" -+ depends on NET_ETHERNET && ( IDT_EB434 || MIKROTIK_RB500) -+ help -+ IDT RC32434 has one local ethernet port. Say Y here to enable it. -+ To compile this driver as a module, choose M here. -+ - config MACE - tristate "MACE (Power Mac ethernet) support" - depends on PPC_PMAC && PPC32 -Index: linux-2.6.23.17/drivers/net/Makefile -=================================================================== ---- linux-2.6.23.17.orig/drivers/net/Makefile -+++ linux-2.6.23.17/drivers/net/Makefile -@@ -25,6 +25,8 @@ ucc_geth_driver-objs := ucc_geth.o ucc_g - # - obj-$(CONFIG_PLIP) += plip.o - -+obj-$(CONFIG_KORINA) += korina.o -+ - obj-$(CONFIG_ROADRUNNER) += rrunner.o - - obj-$(CONFIG_HAPPYMEAL) += sunhme.o diff --git a/target/linux/rb532/patches/120-cf.patch b/target/linux/rb532/patches/120-cf.patch deleted file mode 100644 index 522195e451..0000000000 --- a/target/linux/rb532/patches/120-cf.patch +++ /dev/null @@ -1,31 +0,0 @@ -Index: linux-2.6.23.17/drivers/block/Kconfig -=================================================================== ---- linux-2.6.23.17.orig/drivers/block/Kconfig -+++ linux-2.6.23.17/drivers/block/Kconfig -@@ -426,6 +426,14 @@ config SUNVDC - Support for virtual disk devices as a client under Sun - Logical Domains. - -+config BLK_DEV_CF_MIPS -+ bool "CF slot of RB532 board" -+ depends on MIKROTIK_RB500 -+ default y -+ help -+ The Routerboard 532 has a CF slot on it. Enable the special block -+ device driver for it. -+ - source "drivers/s390/block/Kconfig" - - config XILINX_SYSACE -Index: linux-2.6.23.17/drivers/block/Makefile -=================================================================== ---- linux-2.6.23.17.orig/drivers/block/Makefile -+++ linux-2.6.23.17/drivers/block/Makefile -@@ -21,6 +21,7 @@ obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o - obj-$(CONFIG_XILINX_SYSACE) += xsysace.o - obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o - obj-$(CONFIG_SUNVDC) += sunvdc.o -+obj-$(CONFIG_BLK_DEV_CF_MIPS) += rb500/ - - obj-$(CONFIG_BLK_DEV_UMEM) += umem.o - obj-$(CONFIG_BLK_DEV_NBD) += nbd.o diff --git a/target/linux/rb532/patches/140-cmdline_hack.patch b/target/linux/rb532/patches/140-cmdline_hack.patch deleted file mode 100644 index 1e01e953e5..0000000000 --- a/target/linux/rb532/patches/140-cmdline_hack.patch +++ /dev/null @@ -1,15 +0,0 @@ -Index: linux-2.6.23.17/arch/mips/kernel/head.S -=================================================================== ---- linux-2.6.23.17.orig/arch/mips/kernel/head.S -+++ linux-2.6.23.17/arch/mips/kernel/head.S -@@ -132,6 +132,10 @@ - - j kernel_entry - nop -+ -+EXPORT(_image_cmdline) -+ .ascii "CMDLINE:" -+ - #ifndef CONFIG_NO_EXCEPT_FILL - /* - * Reserved space for exception handlers. diff --git a/target/linux/rb532/patches/200-bridge_resource_hack.patch b/target/linux/rb532/patches/200-bridge_resource_hack.patch deleted file mode 100644 index 707935539b..0000000000 --- a/target/linux/rb532/patches/200-bridge_resource_hack.patch +++ /dev/null @@ -1,15 +0,0 @@ -Index: linux-2.6.23.17/arch/mips/pci/pci.c -=================================================================== ---- linux-2.6.23.17.orig/arch/mips/pci/pci.c -+++ linux-2.6.23.17/arch/mips/pci/pci.c -@@ -64,8 +64,10 @@ pcibios_align_resource(void *data, struc - /* - * Put everything into 0x00-0xff region modulo 0x400 - */ -+#ifndef CONFIG_MIKROTIK_RB500 - if (start & 0x300) - start = (start + 0x3ff) & ~0x3ff; -+#endif - } else if (res->flags & IORESOURCE_MEM) { - /* Make sure we start at our min on all hoses */ - if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start) diff --git a/target/linux/rb532/patches/220-serial_fix.patch b/target/linux/rb532/patches/220-serial_fix.patch deleted file mode 100644 index 4af6620fb5..0000000000 --- a/target/linux/rb532/patches/220-serial_fix.patch +++ /dev/null @@ -1,18 +0,0 @@ -Index: linux-2.6.23.17/drivers/serial/8250.c -=================================================================== ---- linux-2.6.23.17.orig/drivers/serial/8250.c -+++ linux-2.6.23.17/drivers/serial/8250.c -@@ -433,6 +433,13 @@ serial_out(struct uart_8250_port *up, in - default: - outb(value, up->port.iobase + offset); - } -+ -+#if defined(CONFIG_IDT_EB434) || defined(CONFIG_MIKROTIK_RB500) -+ __SLOW_DOWN_IO; -+ __SLOW_DOWN_IO; -+ __SLOW_DOWN_IO; -+ __SLOW_DOWN_IO; -+#endif - } - - static void diff --git a/target/linux/rb532/patches/240-via_rhine_performance.patch b/target/linux/rb532/patches/240-via_rhine_performance.patch deleted file mode 100644 index d82b6f7055..0000000000 --- a/target/linux/rb532/patches/240-via_rhine_performance.patch +++ /dev/null @@ -1,100 +0,0 @@ -Index: linux-2.6.23.17/drivers/net/via-rhine.c -=================================================================== ---- linux-2.6.23.17.orig/drivers/net/via-rhine.c -+++ linux-2.6.23.17/drivers/net/via-rhine.c -@@ -33,6 +33,8 @@ - #define DRV_VERSION "1.4.3" - #define DRV_RELDATE "2007-03-06" - -+#define PKT_ALIGN 1 -+ - - /* A few user-configurable values. - These may be modified when a driver module is loaded. */ -@@ -40,6 +42,7 @@ - static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ - static int max_interrupt_work = 20; - -+#ifndef PKT_ALIGN - /* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1518 effectively disables this feature. */ - #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ -@@ -49,6 +52,7 @@ static int rx_copybreak = 1518; - #else - static int rx_copybreak; - #endif -+#endif /* PKT_ALIGN */ - - /* Work-around for broken BIOSes: they are unable to get the chip back out of - power state D3 so PXE booting fails. bootparam(7): via-rhine.avoid_D3=1 */ -@@ -111,6 +115,7 @@ static const int multicast_filter_limit - #include - #include - #include -+#include - #include - - /* These identify the driver base version and may not be removed. */ -@@ -130,12 +135,14 @@ MODULE_LICENSE("GPL"); - - module_param(max_interrupt_work, int, 0); - module_param(debug, int, 0); --module_param(rx_copybreak, int, 0); - module_param(avoid_D3, bool, 0); - MODULE_PARM_DESC(max_interrupt_work, "VIA Rhine maximum events handled per interrupt"); - MODULE_PARM_DESC(debug, "VIA Rhine debug level (0-7)"); --MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames"); - MODULE_PARM_DESC(avoid_D3, "Avoid power state D3 (work-around for broken BIOSes)"); -+#ifndef PKT_ALIGN -+module_param(rx_copybreak, int, 0); -+MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames"); -+#endif - - /* - Theory of Operation -@@ -927,7 +934,7 @@ static void alloc_rbufs(struct net_devic - - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { -- struct sk_buff *skb = dev_alloc_skb(rp->rx_buf_sz); -+ struct sk_buff *skb = dev_alloc_skb(rp->rx_buf_sz + 4); - rp->rx_skbuff[i] = skb; - if (skb == NULL) - break; -@@ -1484,7 +1491,9 @@ static int rhine_rx(struct net_device *d - struct sk_buff *skb; - /* Length should omit the CRC */ - int pkt_len = data_size - 4; -- -+#ifdef PKT_ALIGN -+ int i; -+#else - /* Check if the packet is long enough to accept without - copying to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak && -@@ -1503,7 +1512,9 @@ static int rhine_rx(struct net_device *d - rp->rx_skbuff_dma[entry], - rp->rx_buf_sz, - PCI_DMA_FROMDEVICE); -- } else { -+ } else -+#endif -+ { - skb = rp->rx_skbuff[entry]; - if (skb == NULL) { - printk(KERN_ERR "%s: Inconsistent Rx " -@@ -1517,6 +1528,14 @@ static int rhine_rx(struct net_device *d - rp->rx_skbuff_dma[entry], - rp->rx_buf_sz, - PCI_DMA_FROMDEVICE); -+#ifdef PKT_ALIGN -+ /* align the data to the ip header - should be faster than copying the entire packet */ -+ for (i = pkt_len - (pkt_len % 4); i >= 0; i -= 4) { -+ put_unaligned(*((u32 *) (skb->data + i)), (u32 *) (skb->data + i + 2)); -+ } -+ skb->data += 2; -+ skb->tail += 2; -+#endif - } - skb->protocol = eth_type_trans(skb, dev); - #ifdef CONFIG_VIA_RHINE_NAPI diff --git a/target/linux/rb532/patches/510-rb500_led.patch b/target/linux/rb532/patches/510-rb500_led.patch deleted file mode 100644 index 6cd7b1a1c5..0000000000 --- a/target/linux/rb532/patches/510-rb500_led.patch +++ /dev/null @@ -1,27 +0,0 @@ -diff -urN linux-2.6.24.7/drivers/leds/Kconfig linux-2.6.24.7.new/drivers/leds/Kconfig ---- linux-2.6.24.7/drivers/leds/Kconfig 2008-07-15 15:27:55.000000000 +0200 -+++ linux-2.6.24.7.new/drivers/leds/Kconfig 2008-07-15 15:35:24.000000000 +0200 -@@ -120,6 +120,12 @@ - help - This option enables support for the CM-X270 LEDs. - -+config LEDS_RB500 -+ tristate "LED Support for RB5xx boards" -+ depends on LEDS_CLASS && MIKROTIK_RB500 -+ help -+ This option enables support for the yellow user LED on RB5xx boards. -+ - comment "LED Triggers" - - config LEDS_TRIGGERS -diff -urN linux-2.6.24.7/drivers/leds/Makefile linux-2.6.24.7.new/drivers/leds/Makefile ---- linux-2.6.24.7/drivers/leds/Makefile 2008-07-15 15:27:55.000000000 +0200 -+++ linux-2.6.24.7.new/drivers/leds/Makefile 2008-07-15 15:35:38.000000000 +0200 -@@ -20,6 +20,7 @@ - obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o - obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o - obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o -+obj-$(CONFIG_LEDS_RB500) += leds-rb500.o - - # LED Triggers - obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/target/linux/rb532/patches/520-rc32434_wdt.patch b/target/linux/rb532/patches/520-rc32434_wdt.patch deleted file mode 100644 index 6e26237042..0000000000 --- a/target/linux/rb532/patches/520-rc32434_wdt.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -urN linux-2.6.24.7/arch/mips/kernel/time.c linux-2.6.24.7.new/arch/mips/kernel/time.c ---- linux-2.6.24.7/arch/mips/kernel/time.c 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/arch/mips/kernel/time.c 2008-07-15 15:38:33.000000000 +0200 -@@ -75,6 +75,7 @@ - */ - - unsigned int mips_hpt_frequency; -+EXPORT_SYMBOL(mips_hpt_frequency); - - void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock) - { -diff -urN linux-2.6.24.7/drivers/watchdog/Kconfig linux-2.6.24.7.new/drivers/watchdog/Kconfig ---- linux-2.6.24.7/drivers/watchdog/Kconfig 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/drivers/watchdog/Kconfig 2008-07-15 15:39:24.000000000 +0200 -@@ -639,6 +639,18 @@ - help - Hardware driver for the TI AR7 Watchdog Timer. - -+config RC32434_WDT -+ tristate "IDT Interprise 79RC32434 SoC hardware watchdog" -+ depends on WATCHDOG && MIKROTIK_RB500 -+ help -+ This is a driver for hardware watchdog integrated in IDT Interprise -+ 79RC32434 SoC. This watchdog simply watches your kernel to make sure -+ it doesn't freeze, and if it does, it reboots your computer after a -+ certain amount of time. -+ -+ To compile this driver as a module, choose M here: the module will be -+ called rc32434_wdt. -+ - # PARISC Architecture - - # POWERPC Architecture diff --git a/target/linux/rb532/patches/530-no_panic_pci_iomap.patch b/target/linux/rb532/patches/530-no_panic_pci_iomap.patch deleted file mode 100644 index a37b190dfe..0000000000 --- a/target/linux/rb532/patches/530-no_panic_pci_iomap.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -urN linux-2.6.24.7/arch/mips/lib/iomap-pci.c linux-2.6.24.7.new/arch/mips/lib/iomap-pci.c ---- linux-2.6.24.7/arch/mips/lib/iomap-pci.c 2008-05-07 01:22:34.000000000 +0200 -+++ linux-2.6.24.7.new/arch/mips/lib/iomap-pci.c 2008-07-15 16:24:22.000000000 +0200 -@@ -32,7 +32,7 @@ - "report it to linux-mips@linux-mips.org or your " - "vendor.\n", name); - #ifdef CONFIG_PCI_DOMAINS -- panic("To avoid data corruption io_map_base MUST be set with " -+ printk(KERN_WARNING "To avoid data corruption io_map_base MUST be set with " - "multiple PCI domains."); - #endif - }