diff options
Diffstat (limited to 'drivers')
877 files changed, 56095 insertions, 21206 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 2f557f570ade..00cf9553f740 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -107,4 +107,6 @@ source "drivers/uio/Kconfig" source "drivers/xen/Kconfig" source "drivers/staging/Kconfig" + +source "drivers/platform/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index fceb71a741c3..c1bf41737936 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -18,6 +18,9 @@ obj-$(CONFIG_ARM_AMBA) += amba/ obj-$(CONFIG_XEN) += xen/ +# regulators early, since some subsystems rely on them to initialize +obj-$(CONFIG_REGULATOR) += regulator/ + # char/ comes before serial/ etc so that the VT console is the boot-time # default. obj-y += char/ @@ -57,6 +60,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_UWB) += uwb/ +obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/ obj-$(CONFIG_PCI) += usb/ @@ -100,5 +104,5 @@ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_VIRTIO) += virtio/ -obj-$(CONFIG_REGULATOR) += regulator/ obj-$(CONFIG_STAGING) += staging/ +obj-y += platform/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b0243fd55ac0..d7f9839ba264 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -196,90 +196,6 @@ config ACPI_NUMA depends on (X86 || IA64) default y if IA64_GENERIC || IA64_SGI_SN2 -config ACPI_WMI - tristate "WMI (EXPERIMENTAL)" - depends on X86 - depends on EXPERIMENTAL - help - This driver adds support for the ACPI-WMI (Windows Management - Instrumentation) mapper device (PNP0C14) found on some systems. - - ACPI-WMI is a proprietary extension to ACPI to expose parts of the - ACPI firmware to userspace - this is done through various vendor - defined methods and data blocks in a PNP0C14 device, which are then - made available for userspace to call. - - The implementation of this in Linux currently only exposes this to - other kernel space drivers. - - This driver is a required dependency to build the firmware specific - drivers needed on many machines, including Acer and HP laptops. - - It is safe to enable this driver even if your DSDT doesn't define - any ACPI-WMI devices. - -config ACPI_ASUS - tristate "ASUS/Medion Laptop Extras" - depends on X86 - select BACKLIGHT_CLASS_DEVICE - ---help--- - This driver provides support for extra features of ACPI-compatible - ASUS laptops. As some of Medion laptops are made by ASUS, it may also - support some Medion laptops (such as 9675 for example). It makes all - the extra buttons generate standard ACPI events that go through - /proc/acpi/events, and (on some models) adds support for changing the - display brightness and output, switching the LCD backlight on and off, - and most importantly, allows you to blink those fancy LEDs intended - for reporting mail and wireless status. - - Note: display switching code is currently considered EXPERIMENTAL, - toying with these values may even lock your machine. - - All settings are changed via /proc/acpi/asus directory entries. Owner - and group for these entries can be set with asus_uid and asus_gid - parameters. - - More information and a userspace daemon for handling the extra buttons - at <http://sourceforge.net/projects/acpi4asus/>. - - If you have an ACPI-compatible ASUS laptop, say Y or M here. This - driver is still under development, so if your laptop is unsupported or - something works not quite as expected, please use the mailing list - available on the above page (acpi4asus-user@lists.sourceforge.net). - - NOTE: This driver is deprecated and will probably be removed soon, - use asus-laptop instead. - -config ACPI_TOSHIBA - tristate "Toshiba Laptop Extras" - depends on X86 && INPUT - select INPUT_POLLDEV - select NET - select RFKILL - select BACKLIGHT_CLASS_DEVICE - ---help--- - This driver adds support for access to certain system settings - on "legacy free" Toshiba laptops. These laptops can be recognized by - their lack of a BIOS setup menu and APM support. - - On these machines, all system configuration is handled through the - ACPI. This driver is required for access to controls not covered - by the general ACPI drivers, such as LCD brightness, video output, - etc. - - This driver differs from the non-ACPI Toshiba laptop driver (located - under "Processor type and features") in several aspects. - Configuration is accessed by reading and writing text files in the - /proc tree instead of by program interface to /dev. Furthermore, no - power management functions are exposed, as those are handled by the - general ACPI drivers. - - More information about this driver is available at - <http://memebeam.org/toys/ToshibaAcpiDriver>. - - If you have a legacy free Toshiba laptop (such as the Libretto L1 - series), say Y. - config ACPI_CUSTOM_DSDT_FILE string "Custom DSDT Table file to include" default "" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3c0c93300f12..d80f4cc2e0da 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -2,15 +2,8 @@ # Makefile for the Linux ACPI interpreter # -export ACPI_CFLAGS - -ACPI_CFLAGS := -Os - -ifdef CONFIG_ACPI_DEBUG - ACPI_CFLAGS += -DACPI_DEBUG_OUTPUT -endif - -EXTRA_CFLAGS += $(ACPI_CFLAGS) +ccflags-y := -Os +ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT # # ACPI Boot-Time Table Parsing @@ -22,9 +15,13 @@ obj-$(CONFIG_X86) += blacklist.o # ACPI Core Subsystem (Interpreter) # obj-y += osl.o utils.o reboot.o\ - dispatcher/ events/ executer/ hardware/ \ - namespace/ parser/ resources/ tables/ \ - utilities/ + acpica/ + +# sleep related files +obj-y += wakeup.o +obj-y += main.o +obj-$(CONFIG_ACPI_SLEEP) += proc.o + # # ACPI Bus and Device Drivers @@ -35,7 +32,6 @@ ifdef CONFIG_CPU_FREQ processor-objs += processor_perflib.o endif -obj-y += sleep/ obj-y += bus.o glue.o obj-y += scan.o # Keep EC driver first. Initialization of others depend on it. @@ -59,9 +55,6 @@ obj-y += power.o obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o -obj-$(CONFIG_ACPI_WMI) += wmi.o -obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o -obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o obj-$(CONFIG_ACPI_SBS) += sbshc.o diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile new file mode 100644 index 000000000000..3f23298ee3fd --- /dev/null +++ b/drivers/acpi/acpica/Makefile @@ -0,0 +1,44 @@ +# +# Makefile for ACPICA Core interpreter +# + +ccflags-y := -Os +ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT + +obj-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \ + dsmethod.o dsobject.o dsutils.o dswload.o dswstate.o \ + dsinit.o + +obj-y += evevent.o evregion.o evsci.o evxfevnt.o \ + evmisc.o evrgnini.o evxface.o evxfregn.o \ + evgpe.o evgpeblk.o + +obj-y += exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ + exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\ + excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \ + exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o + +obj-y += hwacpi.o hwgpe.o hwregs.o hwsleep.o hwxface.o + +obj-$(ACPI_FUTURE_USAGE) += hwtimer.o + +obj-y += nsaccess.o nsload.o nssearch.o nsxfeval.o \ + nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \ + nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \ + nsparse.o nspredef.o + +obj-$(ACPI_FUTURE_USAGE) += nsdumpdv.o + +obj-y += psargs.o psparse.o psloop.o pstree.o pswalk.o \ + psopcode.o psscope.o psutils.o psxface.o + +obj-y += rsaddr.o rscreate.o rsinfo.o rsio.o rslist.o rsmisc.o rsxface.o \ + rscalc.o rsirq.o rsmemory.o rsutils.o + +obj-$(ACPI_FUTURE_USAGE) += rsdump.o + +obj-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o + +obj-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ + utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ + utstate.o utmutex.o utobject.o utresrc.o diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h new file mode 100644 index 000000000000..3b20786cbb0d --- /dev/null +++ b/drivers/acpi/acpica/accommon.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * Name: accommon.h - Common include files for generation of ACPICA source + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACCOMMON_H__ +#define __ACCOMMON_H__ + +/* + * Common set of includes for all ACPICA source files. + * We put them here because we don't want to duplicate them + * in the the source code again and again. + * + * Note: The order of these include files is important. + */ +#include "acconfig.h" /* Global configuration constants */ +#include "acmacros.h" /* C macros */ +#include "aclocal.h" /* Internal data types */ +#include "acobject.h" /* ACPI internal object */ +#include "acstruct.h" /* Common structures */ +#include "acglobal.h" /* All global variables */ +#include "achware.h" /* Hardware defines and interfaces */ +#include "acutils.h" /* Utility interfaces */ + +#endif /* __ACCOMMON_H__ */ diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h new file mode 100644 index 000000000000..e6777fb883d2 --- /dev/null +++ b/drivers/acpi/acpica/acconfig.h @@ -0,0 +1,217 @@ +/****************************************************************************** + * + * Name: acconfig.h - Global configuration constants + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef _ACCONFIG_H +#define _ACCONFIG_H + +/****************************************************************************** + * + * Configuration options + * + *****************************************************************************/ + +/* + * ACPI_DEBUG_OUTPUT - This switch enables all the debug facilities of the + * ACPI subsystem. This includes the DEBUG_PRINT output + * statements. When disabled, all DEBUG_PRINT + * statements are compiled out. + * + * ACPI_APPLICATION - Use this switch if the subsystem is going to be run + * at the application level. + * + */ + +/* + * OS name, used for the _OS object. The _OS object is essentially obsolete, + * but there is a large base of ASL/AML code in existing machines that check + * for the string below. The use of this string usually guarantees that + * the ASL will execute down the most tested code path. Also, there is some + * code that will not execute the _OSI method unless _OS matches the string + * below. Therefore, change this string at your own risk. + */ +#define ACPI_OS_NAME "Microsoft Windows NT" + +/* Maximum objects in the various object caches */ + +#define ACPI_MAX_STATE_CACHE_DEPTH 96 /* State objects */ +#define ACPI_MAX_PARSE_CACHE_DEPTH 96 /* Parse tree objects */ +#define ACPI_MAX_EXTPARSE_CACHE_DEPTH 96 /* Parse tree objects */ +#define ACPI_MAX_OBJECT_CACHE_DEPTH 96 /* Interpreter operand objects */ +#define ACPI_MAX_NAMESPACE_CACHE_DEPTH 96 /* Namespace objects */ + +/* + * Should the subsystem abort the loading of an ACPI table if the + * table checksum is incorrect? + */ +#define ACPI_CHECKSUM_ABORT FALSE + +/****************************************************************************** + * + * Subsystem Constants + * + *****************************************************************************/ + +/* Version of ACPI supported */ + +#define ACPI_CA_SUPPORT_LEVEL 3 + +/* Maximum count for a semaphore object */ + +#define ACPI_MAX_SEMAPHORE_COUNT 256 + +/* Maximum object reference count (detects object deletion issues) */ + +#define ACPI_MAX_REFERENCE_COUNT 0x1000 + +/* Size of cached memory mapping for system memory operation region */ + +#define ACPI_SYSMEM_REGION_WINDOW_SIZE 4096 + +/* owner_id tracking. 8 entries allows for 255 owner_ids */ + +#define ACPI_NUM_OWNERID_MASKS 8 + +/* Size of the root table array is increased by this increment */ + +#define ACPI_ROOT_TABLE_SIZE_INCREMENT 4 + +/* Maximum number of While() loop iterations before forced abort */ + +#define ACPI_MAX_LOOP_ITERATIONS 0xFFFF + +/****************************************************************************** + * + * ACPI Specification constants (Do not change unless the specification changes) + * + *****************************************************************************/ + +/* Number of distinct GPE register blocks and register width */ + +#define ACPI_MAX_GPE_BLOCKS 2 +#define ACPI_GPE_REGISTER_WIDTH 8 + +/* Method info (in WALK_STATE), containing local variables and argumetns */ + +#define ACPI_METHOD_NUM_LOCALS 8 +#define ACPI_METHOD_MAX_LOCAL 7 + +#define ACPI_METHOD_NUM_ARGS 7 +#define ACPI_METHOD_MAX_ARG 6 + +/* Length of _HID, _UID, _CID, and UUID values */ + +#define ACPI_DEVICE_ID_LENGTH 0x09 +#define ACPI_MAX_CID_LENGTH 48 +#define ACPI_UUID_LENGTH 16 + +/* + * Operand Stack (in WALK_STATE), Must be large enough to contain METHOD_MAX_ARG + */ +#define ACPI_OBJ_NUM_OPERANDS 8 +#define ACPI_OBJ_MAX_OPERAND 7 + +/* Number of elements in the Result Stack frame, can be an arbitrary value */ + +#define ACPI_RESULTS_FRAME_OBJ_NUM 8 + +/* + * Maximal number of elements the Result Stack can contain, + * it may be an arbitray value not exceeding the types of + * result_size and result_count (now u8). + */ +#define ACPI_RESULTS_OBJ_NUM_MAX 255 + +/* Names within the namespace are 4 bytes long */ + +#define ACPI_NAME_SIZE 4 +#define ACPI_PATH_SEGMENT_LENGTH 5 /* 4 chars for name + 1 char for separator */ +#define ACPI_PATH_SEPARATOR '.' + +/* Sizes for ACPI table headers */ + +#define ACPI_OEM_ID_SIZE 6 +#define ACPI_OEM_TABLE_ID_SIZE 8 + +/* Constants used in searching for the RSDP in low memory */ + +#define ACPI_EBDA_PTR_LOCATION 0x0000040E /* Physical Address */ +#define ACPI_EBDA_PTR_LENGTH 2 +#define ACPI_EBDA_WINDOW_SIZE 1024 +#define ACPI_HI_RSDP_WINDOW_BASE 0x000E0000 /* Physical Address */ +#define ACPI_HI_RSDP_WINDOW_SIZE 0x00020000 +#define ACPI_RSDP_SCAN_STEP 16 + +/* Operation regions */ + +#define ACPI_NUM_PREDEFINED_REGIONS 8 +#define ACPI_USER_REGION_BEGIN 0x80 + +/* Maximum space_ids for Operation Regions */ + +#define ACPI_MAX_ADDRESS_SPACE 255 + +/* Array sizes. Used for range checking also */ + +#define ACPI_MAX_MATCH_OPCODE 5 + +/* RSDP checksums */ + +#define ACPI_RSDP_CHECKSUM_LENGTH 20 +#define ACPI_RSDP_XCHECKSUM_LENGTH 36 + +/* SMBus bidirectional buffer size */ + +#define ACPI_SMBUS_BUFFER_SIZE 34 + +/****************************************************************************** + * + * ACPI AML Debugger + * + *****************************************************************************/ + +#define ACPI_DEBUGGER_MAX_ARGS 8 /* Must be max method args + 1 */ + +#define ACPI_DEBUGGER_COMMAND_PROMPT '-' +#define ACPI_DEBUGGER_EXECUTE_PROMPT '%' + +#endif /* _ACCONFIG_H */ diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h new file mode 100644 index 000000000000..62c59df3b86c --- /dev/null +++ b/drivers/acpi/acpica/acdebug.h @@ -0,0 +1,231 @@ +/****************************************************************************** + * + * Name: acdebug.h - ACPI/AML debugger + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACDEBUG_H__ +#define __ACDEBUG_H__ + +#define ACPI_DEBUG_BUFFER_SIZE 4196 + +struct command_info { + char *name; /* Command Name */ + u8 min_args; /* Minimum arguments required */ +}; + +struct argument_info { + char *name; /* Argument Name */ +}; + +#define PARAM_LIST(pl) pl +#define DBTEST_OUTPUT_LEVEL(lvl) if (acpi_gbl_db_opt_verbose) +#define VERBOSE_PRINT(fp) DBTEST_OUTPUT_LEVEL(lvl) {\ + acpi_os_printf PARAM_LIST(fp);} + +#define EX_NO_SINGLE_STEP 1 +#define EX_SINGLE_STEP 2 + +/* + * dbxface - external debugger interfaces + */ +acpi_status acpi_db_initialize(void); + +void acpi_db_terminate(void); + +acpi_status +acpi_db_single_step(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, u32 op_type); + +/* + * dbcmds - debug commands and output routines + */ +acpi_status acpi_db_disassemble_method(char *name); + +void acpi_db_display_table_info(char *table_arg); + +void acpi_db_unload_acpi_table(char *table_arg, char *instance_arg); + +void +acpi_db_set_method_breakpoint(char *location, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +void acpi_db_set_method_call_breakpoint(union acpi_parse_object *op); + +void acpi_db_get_bus_info(void); + +void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op); + +void acpi_db_dump_namespace(char *start_arg, char *depth_arg); + +void acpi_db_dump_namespace_by_owner(char *owner_arg, char *depth_arg); + +void acpi_db_send_notify(char *name, u32 value); + +void acpi_db_set_method_data(char *type_arg, char *index_arg, char *value_arg); + +acpi_status +acpi_db_display_objects(char *obj_type_arg, char *display_count_arg); + +acpi_status acpi_db_find_name_in_namespace(char *name_arg); + +void acpi_db_set_scope(char *name); + +acpi_status acpi_db_sleep(char *object_arg); + +void acpi_db_find_references(char *object_arg); + +void acpi_db_display_locks(void); + +void acpi_db_display_resources(char *object_arg); + +void acpi_db_display_gpes(void); + +void acpi_db_check_integrity(void); + +void acpi_db_generate_gpe(char *gpe_arg, char *block_arg); + +void acpi_db_check_predefined_names(void); + +void acpi_db_batch_execute(void); + +/* + * dbdisply - debug display commands + */ +void acpi_db_display_method_info(union acpi_parse_object *op); + +void acpi_db_decode_and_display_object(char *target, char *output_type); + +void +acpi_db_display_result_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status acpi_db_display_all_methods(char *display_count_arg); + +void acpi_db_display_arguments(void); + +void acpi_db_display_locals(void); + +void acpi_db_display_results(void); + +void acpi_db_display_calling_tree(void); + +void acpi_db_display_object_type(char *object_arg); + +void +acpi_db_display_argument_object(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +void acpi_db_check_predefined_names(void); + +void acpi_db_batch_execute(void); + +/* + * dbexec - debugger control method execution + */ +void acpi_db_execute(char *name, char **args, u32 flags); + +void +acpi_db_create_execution_threads(char *num_threads_arg, + char *num_loops_arg, char *method_name_arg); + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +u32 acpi_db_get_cache_info(struct acpi_memory_list *cache); +#endif + +/* + * dbfileio - Debugger file I/O commands + */ +acpi_object_type +acpi_db_match_argument(char *user_argument, struct argument_info *arguments); + +void acpi_db_close_debug_file(void); + +void acpi_db_open_debug_file(char *name); + +acpi_status acpi_db_load_acpi_table(char *filename); + +acpi_status +acpi_db_get_table_from_file(char *filename, struct acpi_table_header **table); + +acpi_status +acpi_db_read_table_from_file(char *filename, struct acpi_table_header **table); + +/* + * dbhistry - debugger HISTORY command + */ +void acpi_db_add_to_history(char *command_line); + +void acpi_db_display_history(void); + +char *acpi_db_get_from_history(char *command_num_arg); + +/* + * dbinput - user front-end to the AML debugger + */ +acpi_status +acpi_db_command_dispatch(char *input_buffer, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context); + +/* + * dbstats - Generation and display of ACPI table statistics + */ +void acpi_db_generate_statistics(union acpi_parse_object *root, u8 is_method); + +acpi_status acpi_db_display_statistics(char *type_arg); + +/* + * dbutils - AML debugger utilities + */ +void acpi_db_set_output_destination(u32 where); + +void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level); + +void acpi_db_prep_namestring(char *name); + +struct acpi_namespace_node *acpi_db_local_ns_lookup(char *name); + +void acpi_db_uint32_to_hex_string(u32 value, char *buffer); + +#endif /* __ACDEBUG_H__ */ diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h new file mode 100644 index 000000000000..6291904be01e --- /dev/null +++ b/drivers/acpi/acpica/acdispat.h @@ -0,0 +1,345 @@ +/****************************************************************************** + * + * Name: acdispat.h - dispatcher (parser to interpreter interface) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef _ACDISPAT_H_ +#define _ACDISPAT_H_ + +#define NAMEOF_LOCAL_NTE "__L0" +#define NAMEOF_ARG_NTE "__A0" + +/* + * dsopcode - support for late evaluation + */ +acpi_status +acpi_ds_get_buffer_field_arguments(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_get_bank_field_arguments(union acpi_operand_object *obj_desc); + +acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *rgn_desc); + +acpi_status acpi_ds_get_buffer_arguments(union acpi_operand_object *obj_desc); + +acpi_status acpi_ds_get_package_arguments(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_eval_bank_field_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status acpi_ds_initialize_region(acpi_handle obj_handle); + +/* + * dsctrl - Parser/Interpreter interface, control stack routines + */ +acpi_status +acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_exec_end_control_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +/* + * dsexec - Parser/Interpreter interface, method execution callbacks + */ +acpi_status +acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, + union acpi_operand_object *result_obj); + +acpi_status +acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *state); + +/* + * dsfield - Parser/Interpreter interface for AML fields + */ +acpi_status +acpi_ds_create_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_bank_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_index_field(union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_buffer_field(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_init_field_objects(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); + +/* + * dsload - Parser/Interpreter interface, namespace load callbacks + */ +acpi_status +acpi_ds_load1_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op); + +acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number); + +/* + * dsmthdat - method data (locals/args) + */ +acpi_status +acpi_ds_store_object_to_local(u8 type, + u32 index, + union acpi_operand_object *src_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_method_data_get_entry(u16 opcode, + u32 index, + struct acpi_walk_state *walk_state, + union acpi_operand_object ***node); + +void acpi_ds_method_data_delete_all(struct acpi_walk_state *walk_state); + +u8 acpi_ds_is_method_value(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ds_method_data_get_value(u8 type, + u32 index, + struct acpi_walk_state *walk_state, + union acpi_operand_object **dest_desc); + +acpi_status +acpi_ds_method_data_init_args(union acpi_operand_object **params, + u32 max_param_count, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_method_data_get_node(u8 type, + u32 index, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **node); + +void acpi_ds_method_data_init(struct acpi_walk_state *walk_state); + +/* + * dsmethod - Parser/Interpreter interface - control method parsing + */ +acpi_status acpi_ds_parse_method(struct acpi_namespace_node *node); + +acpi_status +acpi_ds_call_control_method(struct acpi_thread_state *thread, + struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ds_restart_control_method(struct acpi_walk_state *walk_state, + union acpi_operand_object *return_desc); + +void +acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_method_error(acpi_status status, struct acpi_walk_state *walk_state); + +/* + * dsinit + */ +acpi_status +acpi_ds_initialize_objects(u32 table_index, + struct acpi_namespace_node *start_node); + +/* + * dsobject - Parser/Interpreter interface - object initialization and conversion + */ +acpi_status +acpi_ds_build_internal_buffer_obj(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 buffer_length, + union acpi_operand_object **obj_desc_ptr); + +acpi_status +acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 package_length, + union acpi_operand_object **obj_desc); + +acpi_status +acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u16 opcode, union acpi_operand_object **obj_desc); + +acpi_status +acpi_ds_create_node(struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + union acpi_parse_object *op); + +/* + * dsutils - Parser/Interpreter interface utility routines + */ +void acpi_ds_clear_implicit_return(struct acpi_walk_state *walk_state); + +u8 +acpi_ds_do_implicit_return(union acpi_operand_object *return_desc, + struct acpi_walk_state *walk_state, + u8 add_reference); + +u8 +acpi_ds_is_result_used(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); + +void +acpi_ds_delete_result_if_not_used(union acpi_parse_object *op, + union acpi_operand_object *result_obj, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_create_operand(struct acpi_walk_state *walk_state, + union acpi_parse_object *arg, u32 args_remaining); + +acpi_status +acpi_ds_create_operands(struct acpi_walk_state *walk_state, + union acpi_parse_object *first_arg); + +acpi_status acpi_ds_resolve_operands(struct acpi_walk_state *walk_state); + +void acpi_ds_clear_operands(struct acpi_walk_state *walk_state); + +acpi_status acpi_ds_evaluate_name_path(struct acpi_walk_state *walk_state); + +/* + * dswscope - Scope Stack manipulation + */ +acpi_status +acpi_ds_scope_stack_push(struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_walk_state *walk_state); + +acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state); + +void acpi_ds_scope_stack_clear(struct acpi_walk_state *walk_state); + +/* + * dswstate - parser WALK_STATE management routines + */ +acpi_status +acpi_ds_obj_stack_push(void *object, struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_obj_stack_pop(u32 pop_count, struct acpi_walk_state *walk_state); + +struct acpi_walk_state *acpi_ds_create_walk_state(acpi_owner_id owner_id, union acpi_parse_object + *origin, union acpi_operand_object + *mth_desc, struct acpi_thread_state + *thread); + +acpi_status +acpi_ds_init_aml_walk(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + struct acpi_namespace_node *method_node, + u8 * aml_start, + u32 aml_length, + struct acpi_evaluate_info *info, u8 pass_number); + +void +acpi_ds_obj_stack_pop_and_delete(u32 pop_count, + struct acpi_walk_state *walk_state); + +void acpi_ds_delete_walk_state(struct acpi_walk_state *walk_state); + +struct acpi_walk_state *acpi_ds_pop_walk_state(struct acpi_thread_state + *thread); + +void +acpi_ds_push_walk_state(struct acpi_walk_state *walk_state, + struct acpi_thread_state *thread); + +acpi_status acpi_ds_result_stack_clear(struct acpi_walk_state *walk_state); + +struct acpi_walk_state *acpi_ds_get_current_walk_state(struct acpi_thread_state + *thread); + +acpi_status +acpi_ds_result_pop(union acpi_operand_object **object, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ds_result_push(union acpi_operand_object *object, + struct acpi_walk_state *walk_state); + +#endif /* _ACDISPAT_H_ */ diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h new file mode 100644 index 000000000000..07e20135f01b --- /dev/null +++ b/drivers/acpi/acpica/acevents.h @@ -0,0 +1,218 @@ +/****************************************************************************** + * + * Name: acevents.h - Event subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACEVENTS_H__ +#define __ACEVENTS_H__ + +/* + * evevent + */ +acpi_status acpi_ev_initialize_events(void); + +acpi_status acpi_ev_install_xrupt_handlers(void); + +acpi_status acpi_ev_install_fadt_gpes(void); + +u32 acpi_ev_fixed_event_detect(void); + +/* + * evmisc + */ +u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node); + +acpi_status acpi_ev_acquire_global_lock(u16 timeout); + +acpi_status acpi_ev_release_global_lock(void); + +acpi_status acpi_ev_init_global_lock_handler(void); + +u32 acpi_ev_get_gpe_number_index(u32 gpe_number); + +acpi_status +acpi_ev_queue_notify_request(struct acpi_namespace_node *node, + u32 notify_value); + +/* + * evgpe - GPE handling and dispatch + */ +acpi_status +acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info, + u8 type); + +acpi_status +acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info, + u8 write_to_hardware); + +acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); + +struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, + u32 gpe_number); + +/* + * evgpeblk + */ +u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context); + +acpi_status +acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +acpi_status +acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, + struct acpi_generic_address *gpe_block_address, + u32 register_count, + u8 gpe_block_base_number, + u32 interrupt_number, + struct acpi_gpe_block_info **return_gpe_block); + +acpi_status +acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, + struct acpi_gpe_block_info *gpe_block); + +acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block); + +u32 +acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, + u32 gpe_number); + +u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list); + +acpi_status +acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type); + +acpi_status +acpi_ev_check_for_wake_only_gpe(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status acpi_ev_gpe_initialize(void); + +/* + * evregion - Address Space handling + */ +acpi_status acpi_ev_install_region_handlers(void); + +acpi_status acpi_ev_initialize_op_regions(void); + +acpi_status +acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, + u32 function, + acpi_physical_address address, + u32 bit_width, acpi_integer * value); + +acpi_status +acpi_ev_attach_region(union acpi_operand_object *handler_obj, + union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked); + +void +acpi_ev_detach_region(union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked); + +acpi_status +acpi_ev_install_space_handler(struct acpi_namespace_node *node, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, void *context); + +acpi_status +acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, + acpi_adr_space_type space_id); + +acpi_status +acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function); + +/* + * evregini - Region initialization and setup + */ +acpi_status +acpi_ev_system_memory_region_setup(acpi_handle handle, + u32 function, + void *handler_context, + void **region_context); + +acpi_status +acpi_ev_io_space_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_pci_config_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_cmos_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_pci_bar_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_default_region_setup(acpi_handle handle, + u32 function, + void *handler_context, void **region_context); + +acpi_status +acpi_ev_initialize_region(union acpi_operand_object *region_obj, + u8 acpi_ns_locked); + +/* + * evsci - SCI (System Control Interrupt) handling/dispatch + */ +u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context); + +u32 acpi_ev_install_sci_handler(void); + +acpi_status acpi_ev_remove_sci_handler(void); + +u32 acpi_ev_initialize_sCI(u32 program_sCI); + +void acpi_ev_terminate(void); + +#endif /* __ACEVENTS_H__ */ diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h new file mode 100644 index 000000000000..ddb40f5c68fc --- /dev/null +++ b/drivers/acpi/acpica/acglobal.h @@ -0,0 +1,394 @@ +/****************************************************************************** + * + * Name: acglobal.h - Declarations for global variables + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACGLOBAL_H__ +#define __ACGLOBAL_H__ + +/* + * Ensure that the globals are actually defined and initialized only once. + * + * The use of these macros allows a single list of globals (here) in order + * to simplify maintenance of the code. + */ +#ifdef DEFINE_ACPI_GLOBALS +#define ACPI_EXTERN +#define ACPI_INIT_GLOBAL(a,b) a=b +#else +#define ACPI_EXTERN extern +#define ACPI_INIT_GLOBAL(a,b) a +#endif + +/***************************************************************************** + * + * Runtime configuration (static defaults that can be overriden at runtime) + * + ****************************************************************************/ + +/* + * Enable "slack" in the AML interpreter? Default is FALSE, and the + * interpreter strictly follows the ACPI specification. Setting to TRUE + * allows the interpreter to ignore certain errors and/or bad AML constructs. + * + * Currently, these features are enabled by this flag: + * + * 1) Allow "implicit return" of last value in a control method + * 2) Allow access beyond the end of an operation region + * 3) Allow access to uninitialized locals/args (auto-init to integer 0) + * 4) Allow ANY object type to be a source operand for the Store() operator + * 5) Allow unresolved references (invalid target name) in package objects + * 6) Enable warning messages for behavior that is not ACPI spec compliant + */ +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); + +/* + * Automatically serialize ALL control methods? Default is FALSE, meaning + * to use the Serialized/not_serialized method flags on a per method basis. + * Only change this if the ASL code is poorly written and cannot handle + * reentrancy even though methods are marked "NotSerialized". + */ +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_all_methods_serialized, FALSE); + +/* + * Create the predefined _OSI method in the namespace? Default is TRUE + * because ACPI CA is fully compatible with other ACPI implementations. + * Changing this will revert ACPI CA (and machine ASL) to pre-OSI behavior. + */ +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_create_osi_method, TRUE); + +/* + * Disable wakeup GPEs during runtime? Default is TRUE because WAKE and + * RUNTIME GPEs should never be shared, and WAKE GPEs should typically only + * be enabled just before going to sleep. + */ +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_leave_wake_gpes_disabled, TRUE); + +/* + * Optionally use default values for the ACPI register widths. Set this to + * TRUE to use the defaults, if an FADT contains incorrect widths/lengths. + */ +ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, TRUE); + +/***************************************************************************** + * + * Debug support + * + ****************************************************************************/ + +/* Runtime configuration of debug print levels */ + +extern u32 acpi_dbg_level; +extern u32 acpi_dbg_layer; + +/* Procedure nesting level for debug output */ + +extern u32 acpi_gbl_nesting_level; + +/* Support for dynamic control method tracing mechanism */ + +ACPI_EXTERN u32 acpi_gbl_original_dbg_level; +ACPI_EXTERN u32 acpi_gbl_original_dbg_layer; +ACPI_EXTERN acpi_name acpi_gbl_trace_method_name; +ACPI_EXTERN u32 acpi_gbl_trace_dbg_level; +ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer; +ACPI_EXTERN u32 acpi_gbl_trace_flags; + +/***************************************************************************** + * + * ACPI Table globals + * + ****************************************************************************/ + +/* + * acpi_gbl_root_table_list is the master list of ACPI tables found in the + * RSDT/XSDT. + * + * acpi_gbl_FADT is a local copy of the FADT, converted to a common format. + */ +ACPI_EXTERN struct acpi_internal_rsdt acpi_gbl_root_table_list; +ACPI_EXTERN struct acpi_table_fadt acpi_gbl_FADT; +ACPI_EXTERN struct acpi_table_facs *acpi_gbl_FACS; + +/* These addresses are calculated from FADT address values */ + +ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_enable; +ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_enable; + +/* + * Handle both ACPI 1.0 and ACPI 2.0 Integer widths. The integer width is + * determined by the revision of the DSDT: If the DSDT revision is less than + * 2, use only the lower 32 bits of the internal 64-bit Integer. + */ +ACPI_EXTERN u8 acpi_gbl_integer_bit_width; +ACPI_EXTERN u8 acpi_gbl_integer_byte_width; +ACPI_EXTERN u8 acpi_gbl_integer_nybble_width; + +/***************************************************************************** + * + * Mutual exlusion within ACPICA subsystem + * + ****************************************************************************/ + +/* + * Predefined mutex objects. This array contains the + * actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs. + * (The table maps local handles to the real OS handles) + */ +ACPI_EXTERN struct acpi_mutex_info acpi_gbl_mutex_info[ACPI_NUM_MUTEX]; + +/* + * Global lock mutex is an actual AML mutex object + * Global lock semaphore works in conjunction with the HW global lock + */ +ACPI_EXTERN union acpi_operand_object *acpi_gbl_global_lock_mutex; +ACPI_EXTERN acpi_semaphore acpi_gbl_global_lock_semaphore; +ACPI_EXTERN u16 acpi_gbl_global_lock_handle; +ACPI_EXTERN u8 acpi_gbl_global_lock_acquired; +ACPI_EXTERN u8 acpi_gbl_global_lock_present; + +/* + * Spinlocks are used for interfaces that can be possibly called at + * interrupt level + */ +ACPI_EXTERN spinlock_t _acpi_gbl_gpe_lock; /* For GPE data structs and registers */ +ACPI_EXTERN spinlock_t _acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ +#define acpi_gbl_gpe_lock &_acpi_gbl_gpe_lock +#define acpi_gbl_hardware_lock &_acpi_gbl_hardware_lock + +/***************************************************************************** + * + * Miscellaneous globals + * + ****************************************************************************/ + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + +/* Lists for tracking memory allocations */ + +ACPI_EXTERN struct acpi_memory_list *acpi_gbl_global_list; +ACPI_EXTERN struct acpi_memory_list *acpi_gbl_ns_node_list; +ACPI_EXTERN u8 acpi_gbl_display_final_mem_stats; +#endif + +/* Object caches */ + +ACPI_EXTERN acpi_cache_t *acpi_gbl_namespace_cache; +ACPI_EXTERN acpi_cache_t *acpi_gbl_state_cache; +ACPI_EXTERN acpi_cache_t *acpi_gbl_ps_node_cache; +ACPI_EXTERN acpi_cache_t *acpi_gbl_ps_node_ext_cache; +ACPI_EXTERN acpi_cache_t *acpi_gbl_operand_cache; + +/* Global handlers */ + +ACPI_EXTERN struct acpi_object_notify_handler acpi_gbl_device_notify; +ACPI_EXTERN struct acpi_object_notify_handler acpi_gbl_system_notify; +ACPI_EXTERN acpi_exception_handler acpi_gbl_exception_handler; +ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler; +ACPI_EXTERN acpi_tbl_handler acpi_gbl_table_handler; +ACPI_EXTERN void *acpi_gbl_table_handler_context; +ACPI_EXTERN struct acpi_walk_state *acpi_gbl_breakpoint_walk; + +/* Owner ID support */ + +ACPI_EXTERN u32 acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS]; +ACPI_EXTERN u8 acpi_gbl_last_owner_id_index; +ACPI_EXTERN u8 acpi_gbl_next_owner_id_offset; + +/* Misc */ + +ACPI_EXTERN u32 acpi_gbl_original_mode; +ACPI_EXTERN u32 acpi_gbl_rsdp_original_location; +ACPI_EXTERN u32 acpi_gbl_ns_lookup_count; +ACPI_EXTERN u32 acpi_gbl_ps_find_count; +ACPI_EXTERN u16 acpi_gbl_pm1_enable_register_save; +ACPI_EXTERN u8 acpi_gbl_debugger_configuration; +ACPI_EXTERN u8 acpi_gbl_step_to_next_call; +ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present; +ACPI_EXTERN u8 acpi_gbl_events_initialized; +ACPI_EXTERN u8 acpi_gbl_system_awake_and_running; + +#ifndef DEFINE_ACPI_GLOBALS + +/* Other miscellaneous */ + +extern u8 acpi_gbl_shutdown; +extern u32 acpi_gbl_startup_flags; +extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT]; +extern const char *acpi_gbl_highest_dstate_names[4]; +extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; +extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; + +#endif + +/* Exception codes */ + +extern char const *acpi_gbl_exception_names_env[]; +extern char const *acpi_gbl_exception_names_pgm[]; +extern char const *acpi_gbl_exception_names_tbl[]; +extern char const *acpi_gbl_exception_names_aml[]; +extern char const *acpi_gbl_exception_names_ctrl[]; + +/***************************************************************************** + * + * Namespace globals + * + ****************************************************************************/ + +#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) +#define NUM_PREDEFINED_NAMES 10 +#else +#define NUM_PREDEFINED_NAMES 9 +#endif + +ACPI_EXTERN struct acpi_namespace_node acpi_gbl_root_node_struct; +ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_root_node; +ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_fadt_gpe_device; + +extern const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES]; +extern const struct acpi_predefined_names + acpi_gbl_pre_defined_names[NUM_PREDEFINED_NAMES]; + +#ifdef ACPI_DEBUG_OUTPUT +ACPI_EXTERN u32 acpi_gbl_current_node_count; +ACPI_EXTERN u32 acpi_gbl_current_node_size; +ACPI_EXTERN u32 acpi_gbl_max_concurrent_node_count; +ACPI_EXTERN acpi_size *acpi_gbl_entry_stack_pointer; +ACPI_EXTERN acpi_size *acpi_gbl_lowest_stack_pointer; +ACPI_EXTERN u32 acpi_gbl_deepest_nesting; +#endif + +/***************************************************************************** + * + * Interpreter globals + * + ****************************************************************************/ + +ACPI_EXTERN struct acpi_thread_state *acpi_gbl_current_walk_list; + +/* Control method single step flag */ + +ACPI_EXTERN u8 acpi_gbl_cm_single_step; + +/***************************************************************************** + * + * Hardware globals + * + ****************************************************************************/ + +extern struct acpi_bit_register_info + acpi_gbl_bit_register_info[ACPI_NUM_BITREG]; +ACPI_EXTERN u8 acpi_gbl_sleep_type_a; +ACPI_EXTERN u8 acpi_gbl_sleep_type_b; + +/***************************************************************************** + * + * Event and GPE globals + * + ****************************************************************************/ + +extern struct acpi_fixed_event_info + acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS]; +ACPI_EXTERN struct acpi_fixed_event_handler + acpi_gbl_fixed_event_handlers[ACPI_NUM_FIXED_EVENTS]; +ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; +ACPI_EXTERN struct acpi_gpe_block_info +*acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; +ACPI_EXTERN u32 acpi_current_gpe_count; + +/***************************************************************************** + * + * Debugger globals + * + ****************************************************************************/ + +ACPI_EXTERN u8 acpi_gbl_db_output_flags; + +#ifdef ACPI_DISASSEMBLER + +ACPI_EXTERN u8 acpi_gbl_db_opt_disasm; +ACPI_EXTERN u8 acpi_gbl_db_opt_verbose; +#endif + +#ifdef ACPI_DEBUGGER + +extern u8 acpi_gbl_method_executing; +extern u8 acpi_gbl_abort_method; +extern u8 acpi_gbl_db_terminate_threads; + +ACPI_EXTERN int optind; +ACPI_EXTERN char *optarg; + +ACPI_EXTERN u8 acpi_gbl_db_opt_tables; +ACPI_EXTERN u8 acpi_gbl_db_opt_stats; +ACPI_EXTERN u8 acpi_gbl_db_opt_ini_methods; + +ACPI_EXTERN char *acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]; +ACPI_EXTERN char acpi_gbl_db_line_buf[80]; +ACPI_EXTERN char acpi_gbl_db_parsed_buf[80]; +ACPI_EXTERN char acpi_gbl_db_scope_buf[40]; +ACPI_EXTERN char acpi_gbl_db_debug_filename[40]; +ACPI_EXTERN u8 acpi_gbl_db_output_to_file; +ACPI_EXTERN char *acpi_gbl_db_buffer; +ACPI_EXTERN char *acpi_gbl_db_filename; +ACPI_EXTERN u32 acpi_gbl_db_debug_level; +ACPI_EXTERN u32 acpi_gbl_db_console_debug_level; +ACPI_EXTERN struct acpi_table_header *acpi_gbl_db_table_ptr; +ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_db_scope_node; + +/* + * Statistic globals + */ +ACPI_EXTERN u16 acpi_gbl_obj_type_count[ACPI_TYPE_NS_NODE_MAX + 1]; +ACPI_EXTERN u16 acpi_gbl_node_type_count[ACPI_TYPE_NS_NODE_MAX + 1]; +ACPI_EXTERN u16 acpi_gbl_obj_type_count_misc; +ACPI_EXTERN u16 acpi_gbl_node_type_count_misc; +ACPI_EXTERN u32 acpi_gbl_num_nodes; +ACPI_EXTERN u32 acpi_gbl_num_objects; + +ACPI_EXTERN u32 acpi_gbl_size_of_parse_tree; +ACPI_EXTERN u32 acpi_gbl_size_of_method_trees; +ACPI_EXTERN u32 acpi_gbl_size_of_node_entries; +ACPI_EXTERN u32 acpi_gbl_size_of_acpi_objects; + +#endif /* ACPI_DEBUGGER */ + +#endif /* __ACGLOBAL_H__ */ diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h new file mode 100644 index 000000000000..58c69dc49ab4 --- /dev/null +++ b/drivers/acpi/acpica/achware.h @@ -0,0 +1,119 @@ +/****************************************************************************** + * + * Name: achware.h -- hardware specific interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACHWARE_H__ +#define __ACHWARE_H__ + +/* Values for the _SST predefined method */ + +#define ACPI_SST_INDICATOR_OFF 0 +#define ACPI_SST_WORKING 1 +#define ACPI_SST_WAKING 2 +#define ACPI_SST_SLEEPING 3 +#define ACPI_SST_SLEEP_CONTEXT 4 + +/* + * hwacpi - high level functions + */ +acpi_status acpi_hw_set_mode(u32 mode); + +u32 acpi_hw_get_mode(void); + +/* + * hwregs - ACPI Register I/O + */ +struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id); + +acpi_status +acpi_hw_register_read(u32 register_id, u32 * return_value); + +acpi_status acpi_hw_register_write(u32 register_id, u32 value); + +acpi_status acpi_hw_clear_acpi_status(void); + +/* + * hwgpe - GPE support + */ +acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + +acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info *gpe_event_info); + +acpi_status +acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + +acpi_status +acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, + acpi_event_status * event_status); + +acpi_status acpi_hw_disable_all_gpes(void); + +acpi_status acpi_hw_enable_all_runtime_gpes(void); + +acpi_status acpi_hw_enable_all_wakeup_gpes(void); + +acpi_status +acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, + void *context); + +#ifdef ACPI_FUTURE_USAGE +/* + * hwtimer - ACPI Timer prototypes + */ +acpi_status acpi_get_timer_resolution(u32 * resolution); + +acpi_status acpi_get_timer(u32 * ticks); + +acpi_status +acpi_get_timer_duration(u32 start_ticks, u32 end_ticks, u32 * time_elapsed); +#endif /* ACPI_FUTURE_USAGE */ + +#endif /* __ACHWARE_H__ */ diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h new file mode 100644 index 000000000000..e8db7a3143a5 --- /dev/null +++ b/drivers/acpi/acpica/acinterp.h @@ -0,0 +1,529 @@ +/****************************************************************************** + * + * Name: acinterp.h - Interpreter subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACINTERP_H__ +#define __ACINTERP_H__ + +#define ACPI_WALK_OPERANDS (&(walk_state->operands [walk_state->num_operands -1])) + +/* Macros for tables used for debug output */ + +#define ACPI_EXD_OFFSET(f) (u8) ACPI_OFFSET (union acpi_operand_object,f) +#define ACPI_EXD_NSOFFSET(f) (u8) ACPI_OFFSET (struct acpi_namespace_node,f) +#define ACPI_EXD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_exdump_info)) + +/* + * If possible, pack the following structures to byte alignment, since we + * don't care about performance for debug output. Two cases where we cannot + * pack the structures: + * + * 1) Hardware does not support misaligned memory transfers + * 2) Compiler does not support pointers within packed structures + */ +#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED)) +#pragma pack(1) +#endif + +typedef const struct acpi_exdump_info { + u8 opcode; + u8 offset; + char *name; + +} acpi_exdump_info; + +/* Values for the Opcode field above */ + +#define ACPI_EXD_INIT 0 +#define ACPI_EXD_TYPE 1 +#define ACPI_EXD_UINT8 2 +#define ACPI_EXD_UINT16 3 +#define ACPI_EXD_UINT32 4 +#define ACPI_EXD_UINT64 5 +#define ACPI_EXD_LITERAL 6 +#define ACPI_EXD_POINTER 7 +#define ACPI_EXD_ADDRESS 8 +#define ACPI_EXD_STRING 9 +#define ACPI_EXD_BUFFER 10 +#define ACPI_EXD_PACKAGE 11 +#define ACPI_EXD_FIELD 12 +#define ACPI_EXD_REFERENCE 13 + +/* restore default alignment */ + +#pragma pack() + +/* + * exconvrt - object conversion + */ +acpi_status +acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, u32 flags); + +acpi_status +acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc); + +acpi_status +acpi_ex_convert_to_string(union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, u32 type); + +/* Types for ->String conversion */ + +#define ACPI_EXPLICIT_BYTE_COPY 0x00000000 +#define ACPI_EXPLICIT_CONVERT_HEX 0x00000001 +#define ACPI_IMPLICIT_CONVERT_HEX 0x00000002 +#define ACPI_EXPLICIT_CONVERT_DECIMAL 0x00000003 + +acpi_status +acpi_ex_convert_to_target_type(acpi_object_type destination_type, + union acpi_operand_object *source_desc, + union acpi_operand_object **result_desc, + struct acpi_walk_state *walk_state); + +/* + * exfield - ACPI AML (p-code) execution - field manipulation + */ +acpi_status +acpi_ex_common_buffer_setup(union acpi_operand_object *obj_desc, + u32 buffer_length, u32 * datum_count); + +acpi_status +acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc, + acpi_integer mask, + acpi_integer field_value, + u32 field_datum_byte_offset); + +void +acpi_ex_get_buffer_datum(acpi_integer * datum, + void *buffer, + u32 buffer_length, + u32 byte_granularity, u32 buffer_offset); + +void +acpi_ex_set_buffer_datum(acpi_integer merged_datum, + void *buffer, + u32 buffer_length, + u32 byte_granularity, u32 buffer_offset); + +acpi_status +acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state, + union acpi_operand_object *obj_desc, + union acpi_operand_object **ret_buffer_desc); + +acpi_status +acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc); + +/* + * exfldio - low level field I/O + */ +acpi_status +acpi_ex_extract_from_field(union acpi_operand_object *obj_desc, + void *buffer, u32 buffer_length); + +acpi_status +acpi_ex_insert_into_field(union acpi_operand_object *obj_desc, + void *buffer, u32 buffer_length); + +acpi_status +acpi_ex_access_region(union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, + acpi_integer * value, u32 read_write); + +/* + * exmisc - misc support routines + */ +acpi_status +acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, + union acpi_operand_object **return_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_concat_template(union acpi_operand_object *obj_desc, + union acpi_operand_object *obj_desc2, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_do_concatenate(union acpi_operand_object *obj_desc, + union acpi_operand_object *obj_desc2, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_do_logical_numeric_op(u16 opcode, + acpi_integer integer0, + acpi_integer integer1, u8 * logical_result); + +acpi_status +acpi_ex_do_logical_op(u16 opcode, + union acpi_operand_object *operand0, + union acpi_operand_object *operand1, u8 * logical_result); + +acpi_integer +acpi_ex_do_math_op(u16 opcode, acpi_integer operand0, acpi_integer operand1); + +acpi_status acpi_ex_create_mutex(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_processor(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_power_resource(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_create_region(u8 * aml_start, + u32 aml_length, + u8 region_space, struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_event(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_create_method(u8 * aml_start, + u32 aml_length, struct acpi_walk_state *walk_state); + +/* + * exconfig - dynamic table load/unload + */ +acpi_status +acpi_ex_load_op(union acpi_operand_object *obj_desc, + union acpi_operand_object *target, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_load_table_op(struct acpi_walk_state *walk_state, + union acpi_operand_object **return_desc); + +acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle); + +/* + * exmutex - mutex support + */ +acpi_status +acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_acquire_mutex_object(u16 timeout, + union acpi_operand_object *obj_desc, + acpi_thread_id thread_id); + +acpi_status +acpi_ex_release_mutex(union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_release_mutex_object(union acpi_operand_object *obj_desc); + +void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread); + +void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc); + +/* + * exprep - ACPI AML execution - prep utilities + */ +acpi_status +acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc, + u8 field_flags, + u8 field_attribute, + u32 field_bit_position, u32 field_bit_length); + +acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info); + +/* + * exsystem - Interface to OS services + */ +acpi_status +acpi_ex_system_do_notify_op(union acpi_operand_object *value, + union acpi_operand_object *obj_desc); + +acpi_status acpi_ex_system_do_suspend(acpi_integer time); + +acpi_status acpi_ex_system_do_stall(u32 time); + +acpi_status acpi_ex_system_signal_event(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ex_system_wait_event(union acpi_operand_object *time, + union acpi_operand_object *obj_desc); + +acpi_status acpi_ex_system_reset_event(union acpi_operand_object *obj_desc); + +acpi_status +acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout); + +acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout); + +/* + * exoparg1 - ACPI AML execution, 1 operand + */ +acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state); + +/* + * exoparg2 - ACPI AML execution, 2 operands + */ +acpi_status acpi_ex_opcode_2A_0T_0R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state); + +/* + * exoparg3 - ACPI AML execution, 3 operands + */ +acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state); + +acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state); + +/* + * exoparg6 - ACPI AML execution, 6 operands + */ +acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state *walk_state); + +/* + * exresolv - Object resolution and get value functions + */ +acpi_status +acpi_ex_resolve_to_value(union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, + union acpi_operand_object *operand, + acpi_object_type * return_type, + union acpi_operand_object **return_desc); + +/* + * exresnte - resolve namespace node + */ +acpi_status +acpi_ex_resolve_node_to_value(struct acpi_namespace_node **stack_ptr, + struct acpi_walk_state *walk_state); + +/* + * exresop - resolve operand to value + */ +acpi_status +acpi_ex_resolve_operands(u16 opcode, + union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state); + +/* + * exdump - Interpreter debug output routines + */ +void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth); + +void +acpi_ex_dump_operands(union acpi_operand_object **operands, + const char *opcode_name, u32 num_opcodes); + +#ifdef ACPI_FUTURE_USAGE +void +acpi_ex_dump_object_descriptor(union acpi_operand_object *object, u32 flags); + +void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags); +#endif /* ACPI_FUTURE_USAGE */ + +/* + * exnames - AML namestring support + */ +acpi_status +acpi_ex_get_name_string(acpi_object_type data_type, + u8 * in_aml_address, + char **out_name_string, u32 * out_name_length); + +/* + * exstore - Object store support + */ +acpi_status +acpi_ex_store(union acpi_operand_object *val_desc, + union acpi_operand_object *dest_desc, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node, + struct acpi_walk_state *walk_state, + u8 implicit_conversion); + +#define ACPI_IMPLICIT_CONVERSION TRUE +#define ACPI_NO_IMPLICIT_CONVERSION FALSE + +/* + * exstoren - resolve/store object + */ +acpi_status +acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, + acpi_object_type target_type, + struct acpi_walk_state *walk_state); + +acpi_status +acpi_ex_store_object_to_object(union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc, + union acpi_operand_object **new_desc, + struct acpi_walk_state *walk_state); + +/* + * exstorob - store object - buffer/string + */ +acpi_status +acpi_ex_store_buffer_to_buffer(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +acpi_status +acpi_ex_store_string_to_string(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +/* + * excopy - object copy + */ +acpi_status +acpi_ex_copy_integer_to_index_field(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +acpi_status +acpi_ex_copy_integer_to_bank_field(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +acpi_status +acpi_ex_copy_data_to_named_field(union acpi_operand_object *source_desc, + struct acpi_namespace_node *node); + +acpi_status +acpi_ex_copy_integer_to_buffer_field(union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc); + +/* + * exutils - interpreter/scanner utilities + */ +void acpi_ex_enter_interpreter(void); + +void acpi_ex_exit_interpreter(void); + +void acpi_ex_reacquire_interpreter(void); + +void acpi_ex_relinquish_interpreter(void); + +void acpi_ex_truncate_for32bit_table(union acpi_operand_object *obj_desc); + +void acpi_ex_acquire_global_lock(u32 rule); + +void acpi_ex_release_global_lock(u32 rule); + +void acpi_ex_eisa_id_to_string(u32 numeric_id, char *out_string); + +void acpi_ex_unsigned_integer_to_string(acpi_integer value, char *out_string); + +/* + * exregion - default op_region handlers + */ +acpi_status +acpi_ex_system_memory_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, + void *region_context); + +acpi_status +acpi_ex_system_io_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_pci_config_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_cmos_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_pci_bar_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_embedded_controller_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, + void *region_context); + +acpi_status +acpi_ex_sm_bus_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, void *region_context); + +acpi_status +acpi_ex_data_table_space_handler(u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer * value, + void *handler_context, void *region_context); + +#endif /* __INTERP_H__ */ diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h new file mode 100644 index 000000000000..492d02761bb7 --- /dev/null +++ b/drivers/acpi/acpica/aclocal.h @@ -0,0 +1,990 @@ +/****************************************************************************** + * + * Name: aclocal.h - Internal data types used across the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACLOCAL_H__ +#define __ACLOCAL_H__ + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +#define ACPI_SERIALIZED 0xFF + +typedef u32 acpi_mutex_handle; +#define ACPI_GLOBAL_LOCK (acpi_semaphore) (-1) + +/* Total number of aml opcodes defined */ + +#define AML_NUM_OPCODES 0x7F + +/* Forward declarations */ + +struct acpi_walk_state; +struct acpi_obj_mutex; +union acpi_parse_object; + +/***************************************************************************** + * + * Mutex typedefs and structs + * + ****************************************************************************/ + +/* + * Predefined handles for the mutex objects used within the subsystem + * All mutex objects are automatically created by acpi_ut_mutex_initialize. + * + * The acquire/release ordering protocol is implied via this list. Mutexes + * with a lower value must be acquired before mutexes with a higher value. + * + * NOTE: any changes here must be reflected in the acpi_gbl_mutex_names + * table below also! + */ +#define ACPI_MTX_INTERPRETER 0 /* AML Interpreter, main lock */ +#define ACPI_MTX_NAMESPACE 1 /* ACPI Namespace */ +#define ACPI_MTX_TABLES 2 /* Data for ACPI tables */ +#define ACPI_MTX_EVENTS 3 /* Data for ACPI events */ +#define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */ +#define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */ +#define ACPI_MTX_DEBUG_CMD_COMPLETE 6 /* AML debugger */ +#define ACPI_MTX_DEBUG_CMD_READY 7 /* AML debugger */ + +#define ACPI_MAX_MUTEX 7 +#define ACPI_NUM_MUTEX ACPI_MAX_MUTEX+1 + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +#ifdef DEFINE_ACPI_GLOBALS + +/* Debug names for the mutexes above */ + +static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = { + "ACPI_MTX_Interpreter", + "ACPI_MTX_Namespace", + "ACPI_MTX_Tables", + "ACPI_MTX_Events", + "ACPI_MTX_Caches", + "ACPI_MTX_Memory", + "ACPI_MTX_CommandComplete", + "ACPI_MTX_CommandReady" +}; + +#endif +#endif + +/* + * Predefined handles for spinlocks used within the subsystem. + * These spinlocks are created by acpi_ut_mutex_initialize + */ +#define ACPI_LOCK_GPES 0 +#define ACPI_LOCK_HARDWARE 1 + +#define ACPI_MAX_LOCK 1 +#define ACPI_NUM_LOCK ACPI_MAX_LOCK+1 + +/* This Thread ID means that the mutex is not in use (unlocked) */ + +#define ACPI_MUTEX_NOT_ACQUIRED (acpi_thread_id) 0 + +/* Table for the global mutexes */ + +struct acpi_mutex_info { + acpi_mutex mutex; + u32 use_count; + acpi_thread_id thread_id; +}; + +/* Lock flag parameter for various interfaces */ + +#define ACPI_MTX_DO_NOT_LOCK 0 +#define ACPI_MTX_LOCK 1 + +/* Field access granularities */ + +#define ACPI_FIELD_BYTE_GRANULARITY 1 +#define ACPI_FIELD_WORD_GRANULARITY 2 +#define ACPI_FIELD_DWORD_GRANULARITY 4 +#define ACPI_FIELD_QWORD_GRANULARITY 8 + +#define ACPI_ENTRY_NOT_FOUND NULL + +/***************************************************************************** + * + * Namespace typedefs and structs + * + ****************************************************************************/ + +/* Operational modes of the AML interpreter/scanner */ + +typedef enum { + ACPI_IMODE_LOAD_PASS1 = 0x01, + ACPI_IMODE_LOAD_PASS2 = 0x02, + ACPI_IMODE_EXECUTE = 0x03 +} acpi_interpreter_mode; + +/* + * The Namespace Node describes a named object that appears in the AML. + * descriptor_type is used to differentiate between internal descriptors. + * + * The node is optimized for both 32-bit and 64-bit platforms: + * 20 bytes for the 32-bit case, 32 bytes for the 64-bit case. + * + * Note: The descriptor_type and Type fields must appear in the identical + * position in both the struct acpi_namespace_node and union acpi_operand_object + * structures. + */ +struct acpi_namespace_node { + union acpi_operand_object *object; /* Interpreter object */ + u8 descriptor_type; /* Differentiate object descriptor types */ + u8 type; /* ACPI Type associated with this name */ + u8 flags; /* Miscellaneous flags */ + acpi_owner_id owner_id; /* Node creator */ + union acpi_name_union name; /* ACPI Name, always 4 chars per ACPI spec */ + struct acpi_namespace_node *child; /* First child */ + struct acpi_namespace_node *peer; /* Peer. Parent if ANOBJ_END_OF_PEER_LIST set */ + + /* + * The following fields are used by the ASL compiler and disassembler only + */ +#ifdef ACPI_LARGE_NAMESPACE_NODE + union acpi_parse_object *op; + u32 value; + u32 length; +#endif +}; + +/* Namespace Node flags */ + +#define ANOBJ_END_OF_PEER_LIST 0x01 /* End-of-list, Peer field points to parent */ +#define ANOBJ_TEMPORARY 0x02 /* Node is create by a method and is temporary */ +#define ANOBJ_METHOD_ARG 0x04 /* Node is a method argument */ +#define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */ +#define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */ +#define ANOBJ_EVALUATED 0x20 /* Set on first evaluation of node */ + +#define ANOBJ_IS_EXTERNAL 0x08 /* i_aSL only: This object created via External() */ +#define ANOBJ_METHOD_NO_RETVAL 0x10 /* i_aSL only: Method has no return value */ +#define ANOBJ_METHOD_SOME_NO_RETVAL 0x20 /* i_aSL only: Method has at least one return value */ +#define ANOBJ_IS_BIT_OFFSET 0x40 /* i_aSL only: Reference is a bit offset */ +#define ANOBJ_IS_REFERENCED 0x80 /* i_aSL only: Object was referenced */ + +/* One internal RSDT for table management */ + +struct acpi_internal_rsdt { + struct acpi_table_desc *tables; + u32 count; + u32 size; + u8 flags; +}; + +/* Flags for above */ + +#define ACPI_ROOT_ORIGIN_UNKNOWN (0) /* ~ORIGIN_ALLOCATED */ +#define ACPI_ROOT_ORIGIN_ALLOCATED (1) +#define ACPI_ROOT_ALLOW_RESIZE (2) + +/* Predefined (fixed) table indexes */ + +#define ACPI_TABLE_INDEX_DSDT (0) +#define ACPI_TABLE_INDEX_FACS (1) + +struct acpi_find_context { + char *search_for; + acpi_handle *list; + u32 *count; +}; + +struct acpi_ns_search_data { + struct acpi_namespace_node *node; +}; + +/* Object types used during package copies */ + +#define ACPI_COPY_TYPE_SIMPLE 0 +#define ACPI_COPY_TYPE_PACKAGE 1 + +/* Info structure used to convert external<->internal namestrings */ + +struct acpi_namestring_info { + const char *external_name; + const char *next_external_char; + char *internal_name; + u32 length; + u32 num_segments; + u32 num_carats; + u8 fully_qualified; +}; + +/* Field creation info */ + +struct acpi_create_field_info { + struct acpi_namespace_node *region_node; + struct acpi_namespace_node *field_node; + struct acpi_namespace_node *register_node; + struct acpi_namespace_node *data_register_node; + u32 bank_value; + u32 field_bit_position; + u32 field_bit_length; + u8 field_flags; + u8 attribute; + u8 field_type; +}; + +typedef +acpi_status(*ACPI_INTERNAL_METHOD) (struct acpi_walk_state * walk_state); + +/* + * Bitmapped ACPI types. Used internally only + */ +#define ACPI_BTYPE_ANY 0x00000000 +#define ACPI_BTYPE_INTEGER 0x00000001 +#define ACPI_BTYPE_STRING 0x00000002 +#define ACPI_BTYPE_BUFFER 0x00000004 +#define ACPI_BTYPE_PACKAGE 0x00000008 +#define ACPI_BTYPE_FIELD_UNIT 0x00000010 +#define ACPI_BTYPE_DEVICE 0x00000020 +#define ACPI_BTYPE_EVENT 0x00000040 +#define ACPI_BTYPE_METHOD 0x00000080 +#define ACPI_BTYPE_MUTEX 0x00000100 +#define ACPI_BTYPE_REGION 0x00000200 +#define ACPI_BTYPE_POWER 0x00000400 +#define ACPI_BTYPE_PROCESSOR 0x00000800 +#define ACPI_BTYPE_THERMAL 0x00001000 +#define ACPI_BTYPE_BUFFER_FIELD 0x00002000 +#define ACPI_BTYPE_DDB_HANDLE 0x00004000 +#define ACPI_BTYPE_DEBUG_OBJECT 0x00008000 +#define ACPI_BTYPE_REFERENCE 0x00010000 +#define ACPI_BTYPE_RESOURCE 0x00020000 + +#define ACPI_BTYPE_COMPUTE_DATA (ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_BUFFER) + +#define ACPI_BTYPE_DATA (ACPI_BTYPE_COMPUTE_DATA | ACPI_BTYPE_PACKAGE) +#define ACPI_BTYPE_DATA_REFERENCE (ACPI_BTYPE_DATA | ACPI_BTYPE_REFERENCE | ACPI_BTYPE_DDB_HANDLE) +#define ACPI_BTYPE_DEVICE_OBJECTS (ACPI_BTYPE_DEVICE | ACPI_BTYPE_THERMAL | ACPI_BTYPE_PROCESSOR) +#define ACPI_BTYPE_OBJECTS_AND_REFS 0x0001FFFF /* ARG or LOCAL */ +#define ACPI_BTYPE_ALL_OBJECTS 0x0000FFFF + +/* + * Information structure for ACPI predefined names. + * Each entry in the table contains the following items: + * + * Name - The ACPI reserved name + * param_count - Number of arguments to the method + * expected_return_btypes - Allowed type(s) for the return value + */ +struct acpi_name_info { + char name[ACPI_NAME_SIZE]; + u8 param_count; + u8 expected_btypes; +}; + +/* + * Secondary information structures for ACPI predefined objects that return + * package objects. This structure appears as the next entry in the table + * after the NAME_INFO structure above. + * + * The reason for this is to minimize the size of the predefined name table. + */ + +/* + * Used for ACPI_PTYPE1_FIXED, ACPI_PTYPE1_VAR, ACPI_PTYPE2, + * ACPI_PTYPE2_MIN, ACPI_PTYPE2_PKG_COUNT, ACPI_PTYPE2_COUNT + */ +struct acpi_package_info { + u8 type; + u8 object_type1; + u8 count1; + u8 object_type2; + u8 count2; + u8 reserved; +}; + +/* Used for ACPI_PTYPE2_FIXED */ + +struct acpi_package_info2 { + u8 type; + u8 count; + u8 object_type[4]; +}; + +/* Used for ACPI_PTYPE1_OPTION */ + +struct acpi_package_info3 { + u8 type; + u8 count; + u8 object_type[2]; + u8 tail_object_type; + u8 reserved; +}; + +union acpi_predefined_info { + struct acpi_name_info info; + struct acpi_package_info ret_info; + struct acpi_package_info2 ret_info2; + struct acpi_package_info3 ret_info3; +}; + +/* + * Bitmapped return value types + * Note: the actual data types must be contiguous, a loop in nspredef.c + * depends on this. + */ +#define ACPI_RTYPE_ANY 0x00 +#define ACPI_RTYPE_NONE 0x01 +#define ACPI_RTYPE_INTEGER 0x02 +#define ACPI_RTYPE_STRING 0x04 +#define ACPI_RTYPE_BUFFER 0x08 +#define ACPI_RTYPE_PACKAGE 0x10 +#define ACPI_RTYPE_REFERENCE 0x20 +#define ACPI_RTYPE_ALL 0x3F + +#define ACPI_NUM_RTYPES 5 /* Number of actual object types */ + +/***************************************************************************** + * + * Event typedefs and structs + * + ****************************************************************************/ + +/* Dispatch info for each GPE -- either a method or handler, cannot be both */ + +struct acpi_handler_info { + acpi_event_handler address; /* Address of handler, if any */ + void *context; /* Context to be passed to handler */ + struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ +}; + +union acpi_gpe_dispatch_info { + struct acpi_namespace_node *method_node; /* Method node for this GPE level */ + struct acpi_handler_info *handler; +}; + +/* + * Information about a GPE, one per each GPE in an array. + * NOTE: Important to keep this struct as small as possible. + */ +struct acpi_gpe_event_info { + union acpi_gpe_dispatch_info dispatch; /* Either Method or Handler */ + struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ + u8 flags; /* Misc info about this GPE */ + u8 gpe_number; /* This GPE */ +}; + +/* Information about a GPE register pair, one per each status/enable pair in an array */ + +struct acpi_gpe_register_info { + struct acpi_generic_address status_address; /* Address of status reg */ + struct acpi_generic_address enable_address; /* Address of enable reg */ + u8 enable_for_wake; /* GPEs to keep enabled when sleeping */ + u8 enable_for_run; /* GPEs to keep enabled when running */ + u8 base_gpe_number; /* Base GPE number for this register */ +}; + +/* + * Information about a GPE register block, one per each installed block -- + * GPE0, GPE1, and one per each installed GPE Block Device. + */ +struct acpi_gpe_block_info { + struct acpi_namespace_node *node; + struct acpi_gpe_block_info *previous; + struct acpi_gpe_block_info *next; + struct acpi_gpe_xrupt_info *xrupt_block; /* Backpointer to interrupt block */ + struct acpi_gpe_register_info *register_info; /* One per GPE register pair */ + struct acpi_gpe_event_info *event_info; /* One for each GPE */ + struct acpi_generic_address block_address; /* Base address of the block */ + u32 register_count; /* Number of register pairs in block */ + u8 block_base_number; /* Base GPE number for this block */ +}; + +/* Information about GPE interrupt handlers, one per each interrupt level used for GPEs */ + +struct acpi_gpe_xrupt_info { + struct acpi_gpe_xrupt_info *previous; + struct acpi_gpe_xrupt_info *next; + struct acpi_gpe_block_info *gpe_block_list_head; /* List of GPE blocks for this xrupt */ + u32 interrupt_number; /* System interrupt number */ +}; + +struct acpi_gpe_walk_info { + struct acpi_namespace_node *gpe_device; + struct acpi_gpe_block_info *gpe_block; +}; + +struct acpi_gpe_device_info { + u32 index; + u32 next_block_base_index; + acpi_status status; + struct acpi_namespace_node *gpe_device; +}; + +typedef acpi_status(*acpi_gpe_callback) (struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + +/* Information about each particular fixed event */ + +struct acpi_fixed_event_handler { + acpi_event_handler handler; /* Address of handler. */ + void *context; /* Context to be passed to handler */ +}; + +struct acpi_fixed_event_info { + u8 status_register_id; + u8 enable_register_id; + u16 status_bit_mask; + u16 enable_bit_mask; +}; + +/* Information used during field processing */ + +struct acpi_field_info { + u8 skip_field; + u8 field_flag; + u32 pkg_length; +}; + +/***************************************************************************** + * + * Generic "state" object for stacks + * + ****************************************************************************/ + +#define ACPI_CONTROL_NORMAL 0xC0 +#define ACPI_CONTROL_CONDITIONAL_EXECUTING 0xC1 +#define ACPI_CONTROL_PREDICATE_EXECUTING 0xC2 +#define ACPI_CONTROL_PREDICATE_FALSE 0xC3 +#define ACPI_CONTROL_PREDICATE_TRUE 0xC4 + +#define ACPI_STATE_COMMON \ + void *next; \ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 flags; \ + u16 value; \ + u16 state; + + /* There are 2 bytes available here until the next natural alignment boundary */ + +struct acpi_common_state { +ACPI_STATE_COMMON}; + +/* + * Update state - used to traverse complex objects such as packages + */ +struct acpi_update_state { + ACPI_STATE_COMMON union acpi_operand_object *object; +}; + +/* + * Pkg state - used to traverse nested package structures + */ +struct acpi_pkg_state { + ACPI_STATE_COMMON u16 index; + union acpi_operand_object *source_object; + union acpi_operand_object *dest_object; + struct acpi_walk_state *walk_state; + void *this_target_obj; + u32 num_packages; +}; + +/* + * Control state - one per if/else and while constructs. + * Allows nesting of these constructs + */ +struct acpi_control_state { + ACPI_STATE_COMMON u16 opcode; + union acpi_parse_object *predicate_op; + u8 *aml_predicate_start; /* Start of if/while predicate */ + u8 *package_end; /* End of if/while block */ + u32 loop_count; /* While() loop counter */ +}; + +/* + * Scope state - current scope during namespace lookups + */ +struct acpi_scope_state { + ACPI_STATE_COMMON struct acpi_namespace_node *node; +}; + +struct acpi_pscope_state { + ACPI_STATE_COMMON u32 arg_count; /* Number of fixed arguments */ + union acpi_parse_object *op; /* Current op being parsed */ + u8 *arg_end; /* Current argument end */ + u8 *pkg_end; /* Current package end */ + u32 arg_list; /* Next argument to parse */ +}; + +/* + * Thread state - one per thread across multiple walk states. Multiple walk + * states are created when there are nested control methods executing. + */ +struct acpi_thread_state { + ACPI_STATE_COMMON u8 current_sync_level; /* Mutex Sync (nested acquire) level */ + struct acpi_walk_state *walk_state_list; /* Head of list of walk_states for this thread */ + union acpi_operand_object *acquired_mutex_list; /* List of all currently acquired mutexes */ + acpi_thread_id thread_id; /* Running thread ID */ +}; + +/* + * Result values - used to accumulate the results of nested + * AML arguments + */ +struct acpi_result_values { + ACPI_STATE_COMMON + union acpi_operand_object *obj_desc[ACPI_RESULTS_FRAME_OBJ_NUM]; +}; + +typedef +acpi_status(*acpi_parse_downwards) (struct acpi_walk_state * walk_state, + union acpi_parse_object ** out_op); + +typedef acpi_status(*acpi_parse_upwards) (struct acpi_walk_state * walk_state); + +/* + * Notify info - used to pass info to the deferred notify + * handler/dispatcher. + */ +struct acpi_notify_info { + ACPI_STATE_COMMON struct acpi_namespace_node *node; + union acpi_operand_object *handler_obj; +}; + +/* Generic state is union of structs above */ + +union acpi_generic_state { + struct acpi_common_state common; + struct acpi_control_state control; + struct acpi_update_state update; + struct acpi_scope_state scope; + struct acpi_pscope_state parse_scope; + struct acpi_pkg_state pkg; + struct acpi_thread_state thread; + struct acpi_result_values results; + struct acpi_notify_info notify; +}; + +/***************************************************************************** + * + * Interpreter typedefs and structs + * + ****************************************************************************/ + +typedef acpi_status(*ACPI_EXECUTE_OP) (struct acpi_walk_state * walk_state); + +/***************************************************************************** + * + * Parser typedefs and structs + * + ****************************************************************************/ + +/* + * AML opcode, name, and argument layout + */ +struct acpi_opcode_info { +#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG_OUTPUT) + char *name; /* Opcode name (disassembler/debug only) */ +#endif + u32 parse_args; /* Grammar/Parse time arguments */ + u32 runtime_args; /* Interpret time arguments */ + u16 flags; /* Misc flags */ + u8 object_type; /* Corresponding internal object type */ + u8 class; /* Opcode class */ + u8 type; /* Opcode type */ +}; + +union acpi_parse_value { + acpi_integer integer; /* Integer constant (Up to 64 bits) */ + struct uint64_struct integer64; /* Structure overlay for 2 32-bit Dwords */ + u32 size; /* bytelist or field size */ + char *string; /* NULL terminated string */ + u8 *buffer; /* buffer or string */ + char *name; /* NULL terminated string */ + union acpi_parse_object *arg; /* arguments and contained ops */ +}; + +#ifdef ACPI_DISASSEMBLER +#define ACPI_DISASM_ONLY_MEMBERS(a) a; +#else +#define ACPI_DISASM_ONLY_MEMBERS(a) +#endif + +#define ACPI_PARSE_COMMON \ + union acpi_parse_object *parent; /* Parent op */\ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 flags; /* Type of Op */\ + u16 aml_opcode; /* AML opcode */\ + u32 aml_offset; /* Offset of declaration in AML */\ + union acpi_parse_object *next; /* Next op */\ + struct acpi_namespace_node *node; /* For use by interpreter */\ + union acpi_parse_value value; /* Value or args associated with the opcode */\ + u8 arg_list_length; /* Number of elements in the arg list */\ + ACPI_DISASM_ONLY_MEMBERS (\ + u8 disasm_flags; /* Used during AML disassembly */\ + u8 disasm_opcode; /* Subtype used for disassembly */\ + char aml_op_name[16]) /* Op name (debug only) */ + +#define ACPI_DASM_BUFFER 0x00 +#define ACPI_DASM_RESOURCE 0x01 +#define ACPI_DASM_STRING 0x02 +#define ACPI_DASM_UNICODE 0x03 +#define ACPI_DASM_EISAID 0x04 +#define ACPI_DASM_MATCHOP 0x05 +#define ACPI_DASM_LNOT_PREFIX 0x06 +#define ACPI_DASM_LNOT_SUFFIX 0x07 +#define ACPI_DASM_IGNORE 0x08 + +/* + * Generic operation (for example: If, While, Store) + */ +struct acpi_parse_obj_common { +ACPI_PARSE_COMMON}; + +/* + * Extended Op for named ops (Scope, Method, etc.), deferred ops (Methods and op_regions), + * and bytelists. + */ +struct acpi_parse_obj_named { + ACPI_PARSE_COMMON u8 *path; + u8 *data; /* AML body or bytelist data */ + u32 length; /* AML length */ + u32 name; /* 4-byte name or zero if no name */ +}; + +/* This version is used by the i_aSL compiler only */ + +#define ACPI_MAX_PARSEOP_NAME 20 + +struct acpi_parse_obj_asl { + ACPI_PARSE_COMMON union acpi_parse_object *child; + union acpi_parse_object *parent_method; + char *filename; + char *external_name; + char *namepath; + char name_seg[4]; + u32 extra_value; + u32 column; + u32 line_number; + u32 logical_line_number; + u32 logical_byte_offset; + u32 end_line; + u32 end_logical_line; + u32 acpi_btype; + u32 aml_length; + u32 aml_subtree_length; + u32 final_aml_length; + u32 final_aml_offset; + u32 compile_flags; + u16 parse_opcode; + u8 aml_opcode_length; + u8 aml_pkg_len_bytes; + u8 extra; + char parse_op_name[ACPI_MAX_PARSEOP_NAME]; +}; + +union acpi_parse_object { + struct acpi_parse_obj_common common; + struct acpi_parse_obj_named named; + struct acpi_parse_obj_asl asl; +}; + +/* + * Parse state - one state per parser invocation and each control + * method. + */ +struct acpi_parse_state { + u8 *aml_start; /* First AML byte */ + u8 *aml; /* Next AML byte */ + u8 *aml_end; /* (last + 1) AML byte */ + u8 *pkg_start; /* Current package begin */ + u8 *pkg_end; /* Current package end */ + union acpi_parse_object *start_op; /* Root of parse tree */ + struct acpi_namespace_node *start_node; + union acpi_generic_state *scope; /* Current scope */ + union acpi_parse_object *start_scope; + u32 aml_size; +}; + +/* Parse object flags */ + +#define ACPI_PARSEOP_GENERIC 0x01 +#define ACPI_PARSEOP_NAMED 0x02 +#define ACPI_PARSEOP_DEFERRED 0x04 +#define ACPI_PARSEOP_BYTELIST 0x08 +#define ACPI_PARSEOP_IN_STACK 0x10 +#define ACPI_PARSEOP_TARGET 0x20 +#define ACPI_PARSEOP_IN_CACHE 0x80 + +/* Parse object disasm_flags */ + +#define ACPI_PARSEOP_IGNORE 0x01 +#define ACPI_PARSEOP_PARAMLIST 0x02 +#define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 +#define ACPI_PARSEOP_SPECIAL 0x10 + +/***************************************************************************** + * + * Hardware (ACPI registers) and PNP + * + ****************************************************************************/ + +struct acpi_bit_register_info { + u8 parent_register; + u8 bit_position; + u16 access_bit_mask; +}; + +/* + * Some ACPI registers have bits that must be ignored -- meaning that they + * must be preserved. + */ +#define ACPI_PM1_STATUS_PRESERVED_BITS 0x0800 /* Bit 11 */ +#define ACPI_PM1_CONTROL_PRESERVED_BITS 0x0200 /* Bit 9 (whatever) */ + +/* + * Register IDs + * These are the full ACPI registers + */ +#define ACPI_REGISTER_PM1_STATUS 0x01 +#define ACPI_REGISTER_PM1_ENABLE 0x02 +#define ACPI_REGISTER_PM1_CONTROL 0x03 +#define ACPI_REGISTER_PM1A_CONTROL 0x04 +#define ACPI_REGISTER_PM1B_CONTROL 0x05 +#define ACPI_REGISTER_PM2_CONTROL 0x06 +#define ACPI_REGISTER_PM_TIMER 0x07 +#define ACPI_REGISTER_PROCESSOR_BLOCK 0x08 +#define ACPI_REGISTER_SMI_COMMAND_BLOCK 0x09 + +/* Masks used to access the bit_registers */ + +#define ACPI_BITMASK_TIMER_STATUS 0x0001 +#define ACPI_BITMASK_BUS_MASTER_STATUS 0x0010 +#define ACPI_BITMASK_GLOBAL_LOCK_STATUS 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200 +#define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ +#define ACPI_BITMASK_WAKE_STATUS 0x8000 + +#define ACPI_BITMASK_ALL_FIXED_STATUS (\ + ACPI_BITMASK_TIMER_STATUS | \ + ACPI_BITMASK_BUS_MASTER_STATUS | \ + ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ + ACPI_BITMASK_POWER_BUTTON_STATUS | \ + ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ + ACPI_BITMASK_RT_CLOCK_STATUS | \ + ACPI_BITMASK_WAKE_STATUS) + +#define ACPI_BITMASK_TIMER_ENABLE 0x0001 +#define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200 +#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */ + +#define ACPI_BITMASK_SCI_ENABLE 0x0001 +#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002 +#define ACPI_BITMASK_GLOBAL_LOCK_RELEASE 0x0004 +#define ACPI_BITMASK_SLEEP_TYPE_X 0x1C00 +#define ACPI_BITMASK_SLEEP_ENABLE 0x2000 + +#define ACPI_BITMASK_ARB_DISABLE 0x0001 + +/* Raw bit position of each bit_register */ + +#define ACPI_BITPOSITION_TIMER_STATUS 0x00 +#define ACPI_BITPOSITION_BUS_MASTER_STATUS 0x04 +#define ACPI_BITPOSITION_GLOBAL_LOCK_STATUS 0x05 +#define ACPI_BITPOSITION_POWER_BUTTON_STATUS 0x08 +#define ACPI_BITPOSITION_SLEEP_BUTTON_STATUS 0x09 +#define ACPI_BITPOSITION_RT_CLOCK_STATUS 0x0A +#define ACPI_BITPOSITION_PCIEXP_WAKE_STATUS 0x0E /* ACPI 3.0 */ +#define ACPI_BITPOSITION_WAKE_STATUS 0x0F + +#define ACPI_BITPOSITION_TIMER_ENABLE 0x00 +#define ACPI_BITPOSITION_GLOBAL_LOCK_ENABLE 0x05 +#define ACPI_BITPOSITION_POWER_BUTTON_ENABLE 0x08 +#define ACPI_BITPOSITION_SLEEP_BUTTON_ENABLE 0x09 +#define ACPI_BITPOSITION_RT_CLOCK_ENABLE 0x0A +#define ACPI_BITPOSITION_PCIEXP_WAKE_DISABLE 0x0E /* ACPI 3.0 */ + +#define ACPI_BITPOSITION_SCI_ENABLE 0x00 +#define ACPI_BITPOSITION_BUS_MASTER_RLD 0x01 +#define ACPI_BITPOSITION_GLOBAL_LOCK_RELEASE 0x02 +#define ACPI_BITPOSITION_SLEEP_TYPE_X 0x0A +#define ACPI_BITPOSITION_SLEEP_ENABLE 0x0D + +#define ACPI_BITPOSITION_ARB_DISABLE 0x00 + +/***************************************************************************** + * + * Resource descriptors + * + ****************************************************************************/ + +/* resource_type values */ + +#define ACPI_ADDRESS_TYPE_MEMORY_RANGE 0 +#define ACPI_ADDRESS_TYPE_IO_RANGE 1 +#define ACPI_ADDRESS_TYPE_BUS_NUMBER_RANGE 2 + +/* Resource descriptor types and masks */ + +#define ACPI_RESOURCE_NAME_LARGE 0x80 +#define ACPI_RESOURCE_NAME_SMALL 0x00 + +#define ACPI_RESOURCE_NAME_SMALL_MASK 0x78 /* Bits 6:3 contain the type */ +#define ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK 0x07 /* Bits 2:0 contain the length */ +#define ACPI_RESOURCE_NAME_LARGE_MASK 0x7F /* Bits 6:0 contain the type */ + +/* + * Small resource descriptor "names" as defined by the ACPI specification. + * Note: Bits 2:0 are used for the descriptor length + */ +#define ACPI_RESOURCE_NAME_IRQ 0x20 +#define ACPI_RESOURCE_NAME_DMA 0x28 +#define ACPI_RESOURCE_NAME_START_DEPENDENT 0x30 +#define ACPI_RESOURCE_NAME_END_DEPENDENT 0x38 +#define ACPI_RESOURCE_NAME_IO 0x40 +#define ACPI_RESOURCE_NAME_FIXED_IO 0x48 +#define ACPI_RESOURCE_NAME_RESERVED_S1 0x50 +#define ACPI_RESOURCE_NAME_RESERVED_S2 0x58 +#define ACPI_RESOURCE_NAME_RESERVED_S3 0x60 +#define ACPI_RESOURCE_NAME_RESERVED_S4 0x68 +#define ACPI_RESOURCE_NAME_VENDOR_SMALL 0x70 +#define ACPI_RESOURCE_NAME_END_TAG 0x78 + +/* + * Large resource descriptor "names" as defined by the ACPI specification. + * Note: includes the Large Descriptor bit in bit[7] + */ +#define ACPI_RESOURCE_NAME_MEMORY24 0x81 +#define ACPI_RESOURCE_NAME_GENERIC_REGISTER 0x82 +#define ACPI_RESOURCE_NAME_RESERVED_L1 0x83 +#define ACPI_RESOURCE_NAME_VENDOR_LARGE 0x84 +#define ACPI_RESOURCE_NAME_MEMORY32 0x85 +#define ACPI_RESOURCE_NAME_FIXED_MEMORY32 0x86 +#define ACPI_RESOURCE_NAME_ADDRESS32 0x87 +#define ACPI_RESOURCE_NAME_ADDRESS16 0x88 +#define ACPI_RESOURCE_NAME_EXTENDED_IRQ 0x89 +#define ACPI_RESOURCE_NAME_ADDRESS64 0x8A +#define ACPI_RESOURCE_NAME_EXTENDED_ADDRESS64 0x8B +#define ACPI_RESOURCE_NAME_LARGE_MAX 0x8B + +/***************************************************************************** + * + * Miscellaneous + * + ****************************************************************************/ + +#define ACPI_ASCII_ZERO 0x30 + +/***************************************************************************** + * + * Debugger + * + ****************************************************************************/ + +struct acpi_db_method_info { + acpi_handle main_thread_gate; + acpi_handle thread_complete_gate; + u32 *threads; + u32 num_threads; + u32 num_created; + u32 num_completed; + + char *name; + u32 flags; + u32 num_loops; + char pathname[128]; + char **args; + + /* + * Arguments to be passed to method for the command + * Threads - + * the Number of threads, ID of current thread and + * Index of current thread inside all them created. + */ + char init_args; + char *arguments[4]; + char num_threads_str[11]; + char id_of_thread_str[11]; + char index_of_thread_str[11]; +}; + +struct acpi_integrity_info { + u32 nodes; + u32 objects; +}; + +#define ACPI_DB_REDIRECTABLE_OUTPUT 0x01 +#define ACPI_DB_CONSOLE_OUTPUT 0x02 +#define ACPI_DB_DUPLICATE_OUTPUT 0x03 + +/***************************************************************************** + * + * Debug + * + ****************************************************************************/ + +/* Entry for a memory allocation (debug only) */ + +#define ACPI_MEM_MALLOC 0 +#define ACPI_MEM_CALLOC 1 +#define ACPI_MAX_MODULE_NAME 16 + +#define ACPI_COMMON_DEBUG_MEM_HEADER \ + struct acpi_debug_mem_block *previous; \ + struct acpi_debug_mem_block *next; \ + u32 size; \ + u32 component; \ + u32 line; \ + char module[ACPI_MAX_MODULE_NAME]; \ + u8 alloc_type; + +struct acpi_debug_mem_header { +ACPI_COMMON_DEBUG_MEM_HEADER}; + +struct acpi_debug_mem_block { + ACPI_COMMON_DEBUG_MEM_HEADER u64 user_space; +}; + +#define ACPI_MEM_LIST_GLOBAL 0 +#define ACPI_MEM_LIST_NSNODE 1 +#define ACPI_MEM_LIST_MAX 1 +#define ACPI_NUM_MEM_LISTS 2 + +#endif /* __ACLOCAL_H__ */ diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h new file mode 100644 index 000000000000..9c127e8e2d6d --- /dev/null +++ b/drivers/acpi/acpica/acmacros.h @@ -0,0 +1,577 @@ +/****************************************************************************** + * + * Name: acmacros.h - C macros for the entire subsystem. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACMACROS_H__ +#define __ACMACROS_H__ + +/* + * Extract data using a pointer. Any more than a byte and we + * get into potential aligment issues -- see the STORE macros below. + * Use with care. + */ +#define ACPI_GET8(ptr) *ACPI_CAST_PTR (u8, ptr) +#define ACPI_GET16(ptr) *ACPI_CAST_PTR (u16, ptr) +#define ACPI_GET32(ptr) *ACPI_CAST_PTR (u32, ptr) +#define ACPI_GET64(ptr) *ACPI_CAST_PTR (u64, ptr) +#define ACPI_SET8(ptr) *ACPI_CAST_PTR (u8, ptr) +#define ACPI_SET16(ptr) *ACPI_CAST_PTR (u16, ptr) +#define ACPI_SET32(ptr) *ACPI_CAST_PTR (u32, ptr) +#define ACPI_SET64(ptr) *ACPI_CAST_PTR (u64, ptr) + +/* + * printf() format helpers + */ + +/* Split 64-bit integer into two 32-bit values. Use with %8.8_x%8.8_x */ + +#define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i), ACPI_LODWORD(i) + +#if ACPI_MACHINE_WIDTH == 64 +#define ACPI_FORMAT_NATIVE_UINT(i) ACPI_FORMAT_UINT64(i) +#else +#define ACPI_FORMAT_NATIVE_UINT(i) 0, (i) +#endif + +/* + * Macros for moving data around to/from buffers that are possibly unaligned. + * If the hardware supports the transfer of unaligned data, just do the store. + * Otherwise, we have to move one byte at a time. + */ +#ifdef ACPI_BIG_ENDIAN +/* + * Macros for big-endian machines + */ + +/* These macros reverse the bytes during the move, converting little-endian to big endian */ + + /* Big Endian <== Little Endian */ + /* Hi...Lo Lo...Hi */ +/* 16-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_16_TO_16(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[0];} + +#define ACPI_MOVE_16_TO_32(d, s) {(*(u32 *)(void *)(d))=0;\ + ((u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[1];\ + ((u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[0];} + +#define ACPI_MOVE_16_TO_64(d, s) {(*(u64 *)(void *)(d))=0;\ + ((u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\ + ((u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];} + +/* 32-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ + +#define ACPI_MOVE_32_TO_32(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[3];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[0];} + +#define ACPI_MOVE_32_TO_64(d, s) {(*(u64 *)(void *)(d))=0;\ + ((u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[3];\ + ((u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[2];\ + ((u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\ + ((u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];} + +/* 64-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ + +#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */ + +#define ACPI_MOVE_64_TO_64(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[7];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[6];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[5];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[4];\ + (( u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[3];\ + (( u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[0];} +#else +/* + * Macros for little-endian machines + */ + +#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED + +/* The hardware supports unaligned transfers, just do the little-endian move */ + +/* 16-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_16_TO_16(d, s) *(u16 *)(void *)(d) = *(u16 *)(void *)(s) +#define ACPI_MOVE_16_TO_32(d, s) *(u32 *)(void *)(d) = *(u16 *)(void *)(s) +#define ACPI_MOVE_16_TO_64(d, s) *(u64 *)(void *)(d) = *(u16 *)(void *)(s) + +/* 32-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ +#define ACPI_MOVE_32_TO_32(d, s) *(u32 *)(void *)(d) = *(u32 *)(void *)(s) +#define ACPI_MOVE_32_TO_64(d, s) *(u64 *)(void *)(d) = *(u32 *)(void *)(s) + +/* 64-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ +#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */ +#define ACPI_MOVE_64_TO_64(d, s) *(u64 *)(void *)(d) = *(u64 *)(void *)(s) + +#else +/* + * The hardware does not support unaligned transfers. We must move the + * data one byte at a time. These macros work whether the source or + * the destination (or both) is/are unaligned. (Little-endian move) + */ + +/* 16-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_16_TO_16(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];} + +#define ACPI_MOVE_16_TO_32(d, s) {(*(u32 *)(void *)(d)) = 0; ACPI_MOVE_16_TO_16(d, s);} +#define ACPI_MOVE_16_TO_64(d, s) {(*(u64 *)(void *)(d)) = 0; ACPI_MOVE_16_TO_16(d, s);} + +/* 32-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_32_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ + +#define ACPI_MOVE_32_TO_32(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[3];} + +#define ACPI_MOVE_32_TO_64(d, s) {(*(u64 *)(void *)(d)) = 0; ACPI_MOVE_32_TO_32(d, s);} + +/* 64-bit source, 16/32/64 destination */ + +#define ACPI_MOVE_64_TO_16(d, s) ACPI_MOVE_16_TO_16(d, s) /* Truncate to 16 */ +#define ACPI_MOVE_64_TO_32(d, s) ACPI_MOVE_32_TO_32(d, s) /* Truncate to 32 */ +#define ACPI_MOVE_64_TO_64(d, s) {(( u8 *)(void *)(d))[0] = ((u8 *)(void *)(s))[0];\ + (( u8 *)(void *)(d))[1] = ((u8 *)(void *)(s))[1];\ + (( u8 *)(void *)(d))[2] = ((u8 *)(void *)(s))[2];\ + (( u8 *)(void *)(d))[3] = ((u8 *)(void *)(s))[3];\ + (( u8 *)(void *)(d))[4] = ((u8 *)(void *)(s))[4];\ + (( u8 *)(void *)(d))[5] = ((u8 *)(void *)(s))[5];\ + (( u8 *)(void *)(d))[6] = ((u8 *)(void *)(s))[6];\ + (( u8 *)(void *)(d))[7] = ((u8 *)(void *)(s))[7];} +#endif +#endif + +/* Macros based on machine integer width */ + +#if ACPI_MACHINE_WIDTH == 32 +#define ACPI_MOVE_SIZE_TO_16(d, s) ACPI_MOVE_32_TO_16(d, s) + +#elif ACPI_MACHINE_WIDTH == 64 +#define ACPI_MOVE_SIZE_TO_16(d, s) ACPI_MOVE_64_TO_16(d, s) + +#else +#error unknown ACPI_MACHINE_WIDTH +#endif + +/* + * Fast power-of-two math macros for non-optimized compilers + */ +#define _ACPI_DIV(value, power_of2) ((u32) ((value) >> (power_of2))) +#define _ACPI_MUL(value, power_of2) ((u32) ((value) << (power_of2))) +#define _ACPI_MOD(value, divisor) ((u32) ((value) & ((divisor) -1))) + +#define ACPI_DIV_2(a) _ACPI_DIV(a, 1) +#define ACPI_MUL_2(a) _ACPI_MUL(a, 1) +#define ACPI_MOD_2(a) _ACPI_MOD(a, 2) + +#define ACPI_DIV_4(a) _ACPI_DIV(a, 2) +#define ACPI_MUL_4(a) _ACPI_MUL(a, 2) +#define ACPI_MOD_4(a) _ACPI_MOD(a, 4) + +#define ACPI_DIV_8(a) _ACPI_DIV(a, 3) +#define ACPI_MUL_8(a) _ACPI_MUL(a, 3) +#define ACPI_MOD_8(a) _ACPI_MOD(a, 8) + +#define ACPI_DIV_16(a) _ACPI_DIV(a, 4) +#define ACPI_MUL_16(a) _ACPI_MUL(a, 4) +#define ACPI_MOD_16(a) _ACPI_MOD(a, 16) + +#define ACPI_DIV_32(a) _ACPI_DIV(a, 5) +#define ACPI_MUL_32(a) _ACPI_MUL(a, 5) +#define ACPI_MOD_32(a) _ACPI_MOD(a, 32) + +/* + * Rounding macros (Power of two boundaries only) + */ +#define ACPI_ROUND_DOWN(value, boundary) (((acpi_size)(value)) & \ + (~(((acpi_size) boundary)-1))) + +#define ACPI_ROUND_UP(value, boundary) ((((acpi_size)(value)) + \ + (((acpi_size) boundary)-1)) & \ + (~(((acpi_size) boundary)-1))) + +/* Note: sizeof(acpi_size) evaluates to either 4 or 8 (32- vs 64-bit mode) */ + +#define ACPI_ROUND_DOWN_TO_32BIT(a) ACPI_ROUND_DOWN(a, 4) +#define ACPI_ROUND_DOWN_TO_64BIT(a) ACPI_ROUND_DOWN(a, 8) +#define ACPI_ROUND_DOWN_TO_NATIVE_WORD(a) ACPI_ROUND_DOWN(a, sizeof(acpi_size)) + +#define ACPI_ROUND_UP_TO_32BIT(a) ACPI_ROUND_UP(a, 4) +#define ACPI_ROUND_UP_TO_64BIT(a) ACPI_ROUND_UP(a, 8) +#define ACPI_ROUND_UP_TO_NATIVE_WORD(a) ACPI_ROUND_UP(a, sizeof(acpi_size)) + +#define ACPI_ROUND_BITS_UP_TO_BYTES(a) ACPI_DIV_8((a) + 7) +#define ACPI_ROUND_BITS_DOWN_TO_BYTES(a) ACPI_DIV_8((a)) + +#define ACPI_ROUND_UP_TO_1K(a) (((a) + 1023) >> 10) + +/* Generic (non-power-of-two) rounding */ + +#define ACPI_ROUND_UP_TO(value, boundary) (((value) + ((boundary)-1)) / (boundary)) + +#define ACPI_IS_MISALIGNED(value) (((acpi_size) value) & (sizeof(acpi_size)-1)) + +/* + * Bitmask creation + * Bit positions start at zero. + * MASK_BITS_ABOVE creates a mask starting AT the position and above + * MASK_BITS_BELOW creates a mask starting one bit BELOW the position + */ +#define ACPI_MASK_BITS_ABOVE(position) (~((ACPI_INTEGER_MAX) << ((u32) (position)))) +#define ACPI_MASK_BITS_BELOW(position) ((ACPI_INTEGER_MAX) << ((u32) (position))) + +/* Bitfields within ACPI registers */ + +#define ACPI_REGISTER_PREPARE_BITS(val, pos, mask) ((val << pos) & mask) +#define ACPI_REGISTER_INSERT_VALUE(reg, pos, mask, val) reg = (reg & (~(mask))) | ACPI_REGISTER_PREPARE_BITS(val, pos, mask) + +#define ACPI_INSERT_BITS(target, mask, source) target = ((target & (~(mask))) | (source & mask)) + +/* + * A struct acpi_namespace_node can appear in some contexts + * where a pointer to a union acpi_operand_object can also + * appear. This macro is used to distinguish them. + * + * The "Descriptor" field is the first field in both structures. + */ +#define ACPI_GET_DESCRIPTOR_TYPE(d) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type) +#define ACPI_SET_DESCRIPTOR_TYPE(d, t) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type = t) + +/* Macro to test the object type */ + +#define ACPI_GET_OBJECT_TYPE(d) (((union acpi_operand_object *)(void *)(d))->common.type) + +/* + * Macros for the master AML opcode table + */ +#if defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT) +#define ACPI_OP(name, Pargs, Iargs, obj_type, class, type, flags) \ + {name, (u32)(Pargs), (u32)(Iargs), (u32)(flags), obj_type, class, type} +#else +#define ACPI_OP(name, Pargs, Iargs, obj_type, class, type, flags) \ + {(u32)(Pargs), (u32)(Iargs), (u32)(flags), obj_type, class, type} +#endif + +#define ARG_TYPE_WIDTH 5 +#define ARG_1(x) ((u32)(x)) +#define ARG_2(x) ((u32)(x) << (1 * ARG_TYPE_WIDTH)) +#define ARG_3(x) ((u32)(x) << (2 * ARG_TYPE_WIDTH)) +#define ARG_4(x) ((u32)(x) << (3 * ARG_TYPE_WIDTH)) +#define ARG_5(x) ((u32)(x) << (4 * ARG_TYPE_WIDTH)) +#define ARG_6(x) ((u32)(x) << (5 * ARG_TYPE_WIDTH)) + +#define ARGI_LIST1(a) (ARG_1(a)) +#define ARGI_LIST2(a, b) (ARG_1(b)|ARG_2(a)) +#define ARGI_LIST3(a, b, c) (ARG_1(c)|ARG_2(b)|ARG_3(a)) +#define ARGI_LIST4(a, b, c, d) (ARG_1(d)|ARG_2(c)|ARG_3(b)|ARG_4(a)) +#define ARGI_LIST5(a, b, c, d, e) (ARG_1(e)|ARG_2(d)|ARG_3(c)|ARG_4(b)|ARG_5(a)) +#define ARGI_LIST6(a, b, c, d, e, f) (ARG_1(f)|ARG_2(e)|ARG_3(d)|ARG_4(c)|ARG_5(b)|ARG_6(a)) + +#define ARGP_LIST1(a) (ARG_1(a)) +#define ARGP_LIST2(a, b) (ARG_1(a)|ARG_2(b)) +#define ARGP_LIST3(a, b, c) (ARG_1(a)|ARG_2(b)|ARG_3(c)) +#define ARGP_LIST4(a, b, c, d) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)) +#define ARGP_LIST5(a, b, c, d, e) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)) +#define ARGP_LIST6(a, b, c, d, e, f) (ARG_1(a)|ARG_2(b)|ARG_3(c)|ARG_4(d)|ARG_5(e)|ARG_6(f)) + +#define GET_CURRENT_ARG_TYPE(list) (list & ((u32) 0x1F)) +#define INCREMENT_ARG_LIST(list) (list >>= ((u32) ARG_TYPE_WIDTH)) + +/* + * Ascii error messages can be configured out + */ +#ifndef ACPI_NO_ERROR_MESSAGES + +/* + * Error reporting. Callers module and line number are inserted by AE_INFO, + * the plist contains a set of parens to allow variable-length lists. + * These macros are used for both the debug and non-debug versions of the code. + */ +#define ACPI_ERROR_NAMESPACE(s, e) acpi_ns_report_error (AE_INFO, s, e); +#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ns_report_method_error (AE_INFO, s, n, p, e); + +#else + +/* No error messages */ + +#define ACPI_ERROR_NAMESPACE(s, e) +#define ACPI_ERROR_METHOD(s, n, p, e) +#endif /* ACPI_NO_ERROR_MESSAGES */ + +/* + * Debug macros that are conditionally compiled + */ +#ifdef ACPI_DEBUG_OUTPUT + +/* + * Function entry tracing + */ +#ifdef CONFIG_ACPI_DEBUG_FUNC_TRACE + +#define ACPI_FUNCTION_TRACE(a) ACPI_FUNCTION_NAME(a) \ + acpi_ut_trace(ACPI_DEBUG_PARAMETERS) +#define ACPI_FUNCTION_TRACE_PTR(a, b) ACPI_FUNCTION_NAME(a) \ + acpi_ut_trace_ptr(ACPI_DEBUG_PARAMETERS, (void *)b) +#define ACPI_FUNCTION_TRACE_U32(a, b) ACPI_FUNCTION_NAME(a) \ + acpi_ut_trace_u32(ACPI_DEBUG_PARAMETERS, (u32)b) +#define ACPI_FUNCTION_TRACE_STR(a, b) ACPI_FUNCTION_NAME(a) \ + acpi_ut_trace_str(ACPI_DEBUG_PARAMETERS, (char *)b) + +#define ACPI_FUNCTION_ENTRY() acpi_ut_track_stack_ptr() + +/* + * Function exit tracing. + * WARNING: These macros include a return statement. This is usually considered + * bad form, but having a separate exit macro is very ugly and difficult to maintain. + * One of the FUNCTION_TRACE macros above must be used in conjunction with these macros + * so that "_AcpiFunctionName" is defined. + * + * Note: the DO_WHILE0 macro is used to prevent some compilers from complaining + * about these constructs. + */ +#ifdef ACPI_USE_DO_WHILE_0 +#define ACPI_DO_WHILE0(a) do a while(0) +#else +#define ACPI_DO_WHILE0(a) a +#endif + +#define return_VOID ACPI_DO_WHILE0 ({ \ + acpi_ut_exit (ACPI_DEBUG_PARAMETERS); \ + return;}) +/* + * There are two versions of most of the return macros. The default version is + * safer, since it avoids side-effects by guaranteeing that the argument will + * not be evaluated twice. + * + * A less-safe version of the macros is provided for optional use if the + * compiler uses excessive CPU stack (for example, this may happen in the + * debug case if code optimzation is disabled.) + */ +#ifndef ACPI_SIMPLE_RETURN_MACROS + +#define return_ACPI_STATUS(s) ACPI_DO_WHILE0 ({ \ + register acpi_status _s = (s); \ + acpi_ut_status_exit (ACPI_DEBUG_PARAMETERS, _s); \ + return (_s); }) +#define return_PTR(s) ACPI_DO_WHILE0 ({ \ + register void *_s = (void *) (s); \ + acpi_ut_ptr_exit (ACPI_DEBUG_PARAMETERS, (u8 *) _s); \ + return (_s); }) +#define return_VALUE(s) ACPI_DO_WHILE0 ({ \ + register acpi_integer _s = (s); \ + acpi_ut_value_exit (ACPI_DEBUG_PARAMETERS, _s); \ + return (_s); }) +#define return_UINT8(s) ACPI_DO_WHILE0 ({ \ + register u8 _s = (u8) (s); \ + acpi_ut_value_exit (ACPI_DEBUG_PARAMETERS, (acpi_integer) _s); \ + return (_s); }) +#define return_UINT32(s) ACPI_DO_WHILE0 ({ \ + register u32 _s = (u32) (s); \ + acpi_ut_value_exit (ACPI_DEBUG_PARAMETERS, (acpi_integer) _s); \ + return (_s); }) +#else /* Use original less-safe macros */ + +#define return_ACPI_STATUS(s) ACPI_DO_WHILE0 ({ \ + acpi_ut_status_exit (ACPI_DEBUG_PARAMETERS, (s)); \ + return((s)); }) +#define return_PTR(s) ACPI_DO_WHILE0 ({ \ + acpi_ut_ptr_exit (ACPI_DEBUG_PARAMETERS, (u8 *) (s)); \ + return((s)); }) +#define return_VALUE(s) ACPI_DO_WHILE0 ({ \ + acpi_ut_value_exit (ACPI_DEBUG_PARAMETERS, (acpi_integer) (s)); \ + return((s)); }) +#define return_UINT8(s) return_VALUE(s) +#define return_UINT32(s) return_VALUE(s) + +#endif /* ACPI_SIMPLE_RETURN_MACROS */ + +#else /* !CONFIG_ACPI_DEBUG_FUNC_TRACE */ + +#define ACPI_FUNCTION_TRACE(a) +#define ACPI_FUNCTION_TRACE_PTR(a,b) +#define ACPI_FUNCTION_TRACE_U32(a,b) +#define ACPI_FUNCTION_TRACE_STR(a,b) +#define ACPI_FUNCTION_EXIT +#define ACPI_FUNCTION_STATUS_EXIT(s) +#define ACPI_FUNCTION_VALUE_EXIT(s) +#define ACPI_FUNCTION_TRACE(a) +#define ACPI_FUNCTION_ENTRY() + +#define return_VOID return +#define return_ACPI_STATUS(s) return(s) +#define return_VALUE(s) return(s) +#define return_UINT8(s) return(s) +#define return_UINT32(s) return(s) +#define return_PTR(s) return(s) + +#endif /* CONFIG_ACPI_DEBUG_FUNC_TRACE */ + +/* Conditional execution */ + +#define ACPI_DEBUG_EXEC(a) a +#define ACPI_NORMAL_EXEC(a) + +#define ACPI_DEBUG_DEFINE(a) a; +#define ACPI_DEBUG_ONLY_MEMBERS(a) a; +#define _VERBOSE_STRUCTURES + +/* Stack and buffer dumping */ + +#define ACPI_DUMP_STACK_ENTRY(a) acpi_ex_dump_operand((a), 0) +#define ACPI_DUMP_OPERANDS(a, b, c) acpi_ex_dump_operands(a, b, c) + +#define ACPI_DUMP_ENTRY(a, b) acpi_ns_dump_entry (a, b) +#define ACPI_DUMP_PATHNAME(a, b, c, d) acpi_ns_dump_pathname(a, b, c, d) +#define ACPI_DUMP_RESOURCE_LIST(a) acpi_rs_dump_resource_list(a) +#define ACPI_DUMP_BUFFER(a, b) acpi_ut_dump_buffer((u8 *) a, b, DB_BYTE_DISPLAY, _COMPONENT) + +#else +/* + * This is the non-debug case -- make everything go away, + * leaving no executable debug code! + */ +#define ACPI_DEBUG_EXEC(a) +#define ACPI_NORMAL_EXEC(a) a; + +#define ACPI_DEBUG_DEFINE(a) do { } while(0) +#define ACPI_DEBUG_ONLY_MEMBERS(a) do { } while(0) +#define ACPI_FUNCTION_TRACE(a) do { } while(0) +#define ACPI_FUNCTION_TRACE_PTR(a, b) do { } while(0) +#define ACPI_FUNCTION_TRACE_U32(a, b) do { } while(0) +#define ACPI_FUNCTION_TRACE_STR(a, b) do { } while(0) +#define ACPI_FUNCTION_EXIT do { } while(0) +#define ACPI_FUNCTION_STATUS_EXIT(s) do { } while(0) +#define ACPI_FUNCTION_VALUE_EXIT(s) do { } while(0) +#define ACPI_FUNCTION_ENTRY() do { } while(0) +#define ACPI_DUMP_STACK_ENTRY(a) do { } while(0) +#define ACPI_DUMP_OPERANDS(a, b, c) do { } while(0) +#define ACPI_DUMP_ENTRY(a, b) do { } while(0) +#define ACPI_DUMP_TABLES(a, b) do { } while(0) +#define ACPI_DUMP_PATHNAME(a, b, c, d) do { } while(0) +#define ACPI_DUMP_RESOURCE_LIST(a) do { } while(0) +#define ACPI_DUMP_BUFFER(a, b) do { } while(0) + +#define return_VOID return +#define return_ACPI_STATUS(s) return(s) +#define return_VALUE(s) return(s) +#define return_UINT8(s) return(s) +#define return_UINT32(s) return(s) +#define return_PTR(s) return(s) + +#endif /* ACPI_DEBUG_OUTPUT */ + +/* + * Some code only gets executed when the debugger is built in. + * Note that this is entirely independent of whether the + * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not. + */ +#ifdef ACPI_DEBUGGER +#define ACPI_DEBUGGER_EXEC(a) a +#else +#define ACPI_DEBUGGER_EXEC(a) +#endif + +#ifdef ACPI_DEBUG_OUTPUT +/* + * 1) Set name to blanks + * 2) Copy the object name + */ +#define ACPI_ADD_OBJECT_NAME(a,b) ACPI_MEMSET (a->common.name, ' ', sizeof (a->common.name));\ + ACPI_STRNCPY (a->common.name, acpi_gbl_ns_type_names[b], sizeof (a->common.name)) +#else + +#define ACPI_ADD_OBJECT_NAME(a,b) +#endif + +/* + * Memory allocation tracking (DEBUG ONLY) + */ +#define ACPI_MEM_PARAMETERS _COMPONENT, _acpi_module_name, __LINE__ + +#ifndef ACPI_DBG_TRACK_ALLOCATIONS + +/* Memory allocation */ + +#ifndef ACPI_ALLOCATE +#define ACPI_ALLOCATE(a) acpi_ut_allocate((acpi_size)(a), ACPI_MEM_PARAMETERS) +#endif +#ifndef ACPI_ALLOCATE_ZEROED +#define ACPI_ALLOCATE_ZEROED(a) acpi_ut_allocate_zeroed((acpi_size)(a), ACPI_MEM_PARAMETERS) +#endif +#ifndef ACPI_FREE +#define ACPI_FREE(a) acpio_os_free(a) +#endif +#define ACPI_MEM_TRACKING(a) + +#else + +/* Memory allocation */ + +#define ACPI_ALLOCATE(a) acpi_ut_allocate_and_track((acpi_size)(a), ACPI_MEM_PARAMETERS) +#define ACPI_ALLOCATE_ZEROED(a) acpi_ut_allocate_zeroed_and_track((acpi_size)(a), ACPI_MEM_PARAMETERS) +#define ACPI_FREE(a) acpi_ut_free_and_track(a, ACPI_MEM_PARAMETERS) +#define ACPI_MEM_TRACKING(a) a + +#endif /* ACPI_DBG_TRACK_ALLOCATIONS */ + +/* Preemption point */ +#ifndef ACPI_PREEMPTION_POINT +#define ACPI_PREEMPTION_POINT() /* no preemption */ +#endif + +#endif /* ACMACROS_H */ diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h new file mode 100644 index 000000000000..46cb5b46d280 --- /dev/null +++ b/drivers/acpi/acpica/acnamesp.h @@ -0,0 +1,324 @@ +/****************************************************************************** + * + * Name: acnamesp.h - Namespace subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACNAMESP_H__ +#define __ACNAMESP_H__ + +/* To search the entire name space, pass this as search_base */ + +#define ACPI_NS_ALL ((acpi_handle)0) + +/* + * Elements of acpi_ns_properties are bit significant + * and should be one-to-one with values of acpi_object_type + */ +#define ACPI_NS_NORMAL 0 +#define ACPI_NS_NEWSCOPE 1 /* a definition of this type opens a name scope */ +#define ACPI_NS_LOCAL 2 /* suppress search of enclosing scopes */ + +/* Flags for acpi_ns_lookup, acpi_ns_search_and_enter */ + +#define ACPI_NS_NO_UPSEARCH 0 +#define ACPI_NS_SEARCH_PARENT 0x01 +#define ACPI_NS_DONT_OPEN_SCOPE 0x02 +#define ACPI_NS_NO_PEER_SEARCH 0x04 +#define ACPI_NS_ERROR_IF_FOUND 0x08 +#define ACPI_NS_PREFIX_IS_SCOPE 0x10 +#define ACPI_NS_EXTERNAL 0x20 +#define ACPI_NS_TEMPORARY 0x40 + +/* Flags for acpi_ns_walk_namespace */ + +#define ACPI_NS_WALK_NO_UNLOCK 0 +#define ACPI_NS_WALK_UNLOCK 0x01 +#define ACPI_NS_WALK_TEMP_NODES 0x02 + +/* + * nsinit - Namespace initialization + */ +acpi_status acpi_ns_initialize_objects(void); + +acpi_status acpi_ns_initialize_devices(void); + +/* + * nsload - Namespace loading + */ +acpi_status acpi_ns_load_namespace(void); + +acpi_status +acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node); + +/* + * nswalk - walk the namespace + */ +acpi_status +acpi_ns_walk_namespace(acpi_object_type type, + acpi_handle start_object, + u32 max_depth, + u32 flags, + acpi_walk_callback user_function, + void *context, void **return_value); + +struct acpi_namespace_node *acpi_ns_get_next_node(acpi_object_type type, struct acpi_namespace_node + *parent, struct acpi_namespace_node + *child); + +/* + * nsparse - table parsing + */ +acpi_status +acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node); + +acpi_status +acpi_ns_one_complete_parse(u32 pass_number, + u32 table_index, + struct acpi_namespace_node *start_node); + +/* + * nsaccess - Top-level namespace access + */ +acpi_status acpi_ns_root_initialize(void); + +acpi_status +acpi_ns_lookup(union acpi_generic_state *scope_info, + char *name, + acpi_object_type type, + acpi_interpreter_mode interpreter_mode, + u32 flags, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **ret_node); + +/* + * nsalloc - Named object allocation/deallocation + */ +struct acpi_namespace_node *acpi_ns_create_node(u32 name); + +void acpi_ns_delete_node(struct acpi_namespace_node *node); + +void +acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_handle); + +void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id); + +void acpi_ns_detach_object(struct acpi_namespace_node *node); + +void acpi_ns_delete_children(struct acpi_namespace_node *parent); + +int acpi_ns_compare_names(char *name1, char *name2); + +/* + * nsdump - Namespace dump/print utilities + */ +#ifdef ACPI_FUTURE_USAGE +void acpi_ns_dump_tables(acpi_handle search_base, u32 max_depth); +#endif /* ACPI_FUTURE_USAGE */ + +void acpi_ns_dump_entry(acpi_handle handle, u32 debug_level); + +void +acpi_ns_dump_pathname(acpi_handle handle, char *msg, u32 level, u32 component); + +void acpi_ns_print_pathname(u32 num_segments, char *pathname); + +acpi_status +acpi_ns_dump_one_object(acpi_handle obj_handle, + u32 level, void *context, void **return_value); + +#ifdef ACPI_FUTURE_USAGE +void +acpi_ns_dump_objects(acpi_object_type type, + u8 display_type, + u32 max_depth, + acpi_owner_id owner_id, acpi_handle start_handle); +#endif /* ACPI_FUTURE_USAGE */ + +/* + * nseval - Namespace evaluation functions + */ +acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info); + +/* + * nspredef - Support for predefined/reserved names + */ +acpi_status +acpi_ns_check_predefined_names(struct acpi_namespace_node *node, + u32 user_param_count, + acpi_status return_status, + union acpi_operand_object **return_object); + +const union acpi_predefined_info *acpi_ns_check_for_predefined_name(struct + acpi_namespace_node + *node); + +void +acpi_ns_check_parameter_count(char *pathname, + struct acpi_namespace_node *node, + u32 user_param_count, + const union acpi_predefined_info *info); + +/* + * nsnames - Name and Scope manipulation + */ +u32 acpi_ns_opens_scope(acpi_object_type type); + +acpi_status +acpi_ns_build_external_path(struct acpi_namespace_node *node, + acpi_size size, char *name_buffer); + +char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node); + +char *acpi_ns_name_of_current_scope(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ns_handle_to_pathname(acpi_handle target_handle, + struct acpi_buffer *buffer); + +u8 +acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for); + +acpi_status +acpi_ns_get_node(struct acpi_namespace_node *prefix_node, + const char *external_pathname, + u32 flags, struct acpi_namespace_node **out_node); + +acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node); + +/* + * nsobject - Object management for namespace nodes + */ +acpi_status +acpi_ns_attach_object(struct acpi_namespace_node *node, + union acpi_operand_object *object, acpi_object_type type); + +union acpi_operand_object *acpi_ns_get_attached_object(struct + acpi_namespace_node + *node); + +union acpi_operand_object *acpi_ns_get_secondary_object(union + acpi_operand_object + *obj_desc); + +acpi_status +acpi_ns_attach_data(struct acpi_namespace_node *node, + acpi_object_handler handler, void *data); + +acpi_status +acpi_ns_detach_data(struct acpi_namespace_node *node, + acpi_object_handler handler); + +acpi_status +acpi_ns_get_attached_data(struct acpi_namespace_node *node, + acpi_object_handler handler, void **data); + +/* + * nssearch - Namespace searching and entry + */ +acpi_status +acpi_ns_search_and_enter(u32 entry_name, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + acpi_interpreter_mode interpreter_mode, + acpi_object_type type, + u32 flags, struct acpi_namespace_node **ret_node); + +acpi_status +acpi_ns_search_one_scope(u32 entry_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **ret_node); + +void +acpi_ns_install_node(struct acpi_walk_state *walk_state, + struct acpi_namespace_node *parent_node, + struct acpi_namespace_node *node, acpi_object_type type); + +/* + * nsutils - Utility functions + */ +u8 acpi_ns_valid_root_prefix(char prefix); + +acpi_object_type acpi_ns_get_type(struct acpi_namespace_node *node); + +u32 acpi_ns_local(acpi_object_type type); + +void +acpi_ns_report_error(const char *module_name, + u32 line_number, + const char *internal_name, acpi_status lookup_status); + +void +acpi_ns_report_method_error(const char *module_name, + u32 line_number, + const char *message, + struct acpi_namespace_node *node, + const char *path, acpi_status lookup_status); + +void +acpi_ns_print_node_pathname(struct acpi_namespace_node *node, const char *msg); + +acpi_status acpi_ns_build_internal_name(struct acpi_namestring_info *info); + +void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info); + +acpi_status +acpi_ns_internalize_name(const char *dotted_name, char **converted_name); + +acpi_status +acpi_ns_externalize_name(u32 internal_name_length, + const char *internal_name, + u32 * converted_name_length, char **converted_name); + +struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle); + +acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node); + +void acpi_ns_terminate(void); + +struct acpi_namespace_node *acpi_ns_get_parent_node(struct acpi_namespace_node + *node); + +struct acpi_namespace_node *acpi_ns_get_next_valid_node(struct + acpi_namespace_node + *node); + +#endif /* __ACNAMESP_H__ */ diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h new file mode 100644 index 000000000000..eb6f038b03d9 --- /dev/null +++ b/drivers/acpi/acpica/acobject.h @@ -0,0 +1,446 @@ + +/****************************************************************************** + * + * Name: acobject.h - Definition of union acpi_operand_object (Internal object only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef _ACOBJECT_H +#define _ACOBJECT_H + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +/* + * The union acpi_operand_object is used to pass AML operands from the dispatcher + * to the interpreter, and to keep track of the various handlers such as + * address space handlers and notify handlers. The object is a constant + * size in order to allow it to be cached and reused. + * + * Note: The object is optimized to be aligned and will not work if it is + * byte-packed. + */ +#if ACPI_MACHINE_WIDTH == 64 +#pragma pack(8) +#else +#pragma pack(4) +#endif + +/******************************************************************************* + * + * Common Descriptors + * + ******************************************************************************/ + +/* + * Common area for all objects. + * + * descriptor_type is used to differentiate between internal descriptors, and + * must be in the same place across all descriptors + * + * Note: The descriptor_type and Type fields must appear in the identical + * position in both the struct acpi_namespace_node and union acpi_operand_object + * structures. + */ +#define ACPI_OBJECT_COMMON_HEADER \ + union acpi_operand_object *next_object; /* Objects linked to parent NS node */\ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 type; /* acpi_object_type */\ + u16 reference_count; /* For object deletion management */\ + u8 flags; + /* + * Note: There are 3 bytes available here before the + * next natural alignment boundary (for both 32/64 cases) + */ + +/* Values for Flag byte above */ + +#define AOPOBJ_AML_CONSTANT 0x01 +#define AOPOBJ_STATIC_POINTER 0x02 +#define AOPOBJ_DATA_VALID 0x04 +#define AOPOBJ_OBJECT_INITIALIZED 0x08 +#define AOPOBJ_SETUP_COMPLETE 0x10 +#define AOPOBJ_SINGLE_DATUM 0x20 +#define AOPOBJ_INVALID 0x40 /* Used if host OS won't allow an op_region address */ + +/****************************************************************************** + * + * Basic data types + * + *****************************************************************************/ + +struct acpi_object_common { +ACPI_OBJECT_COMMON_HEADER}; + +struct acpi_object_integer { + ACPI_OBJECT_COMMON_HEADER u8 fill[3]; /* Prevent warning on some compilers */ + acpi_integer value; +}; + +/* + * Note: The String and Buffer object must be identical through the Pointer + * and length elements. There is code that depends on this. + * + * Fields common to both Strings and Buffers + */ +#define ACPI_COMMON_BUFFER_INFO(_type) \ + _type *pointer; \ + u32 length; + +struct acpi_object_string { /* Null terminated, ASCII characters only */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(char) /* String in AML stream or allocated string */ +}; + +struct acpi_object_buffer { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(u8) /* Buffer in AML stream or allocated buffer */ + u32 aml_length; + u8 *aml_start; + struct acpi_namespace_node *node; /* Link back to parent node */ +}; + +struct acpi_object_package { + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Link back to parent node */ + union acpi_operand_object **elements; /* Array of pointers to acpi_objects */ + u8 *aml_start; + u32 aml_length; + u32 count; /* # of elements in package */ +}; + +/****************************************************************************** + * + * Complex data types + * + *****************************************************************************/ + +struct acpi_object_event { + ACPI_OBJECT_COMMON_HEADER acpi_semaphore os_semaphore; /* Actual OS synchronization object */ +}; + +struct acpi_object_mutex { + ACPI_OBJECT_COMMON_HEADER u8 sync_level; /* 0-15, specified in Mutex() call */ + u16 acquisition_depth; /* Allow multiple Acquires, same thread */ + acpi_mutex os_mutex; /* Actual OS synchronization object */ + acpi_thread_id thread_id; /* Current owner of the mutex */ + struct acpi_thread_state *owner_thread; /* Current owner of the mutex */ + union acpi_operand_object *prev; /* Link for list of acquired mutexes */ + union acpi_operand_object *next; /* Link for list of acquired mutexes */ + struct acpi_namespace_node *node; /* Containing namespace node */ + u8 original_sync_level; /* Owner's original sync level (0-15) */ +}; + +struct acpi_object_region { + ACPI_OBJECT_COMMON_HEADER u8 space_id; + struct acpi_namespace_node *node; /* Containing namespace node */ + union acpi_operand_object *handler; /* Handler for region access */ + union acpi_operand_object *next; + acpi_physical_address address; + u32 length; +}; + +struct acpi_object_method { + ACPI_OBJECT_COMMON_HEADER u8 method_flags; + u8 param_count; + u8 sync_level; + union acpi_operand_object *mutex; + u8 *aml_start; + ACPI_INTERNAL_METHOD implementation; + u32 aml_length; + u8 thread_count; + acpi_owner_id owner_id; +}; + +/****************************************************************************** + * + * Objects that can be notified. All share a common notify_info area. + * + *****************************************************************************/ + +/* + * Common fields for objects that support ASL notifications + */ +#define ACPI_COMMON_NOTIFY_INFO \ + union acpi_operand_object *system_notify; /* Handler for system notifies */\ + union acpi_operand_object *device_notify; /* Handler for driver notifies */\ + union acpi_operand_object *handler; /* Handler for Address space */ + +struct acpi_object_notify_common { /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */ +ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; + +struct acpi_object_device { + ACPI_OBJECT_COMMON_HEADER + ACPI_COMMON_NOTIFY_INFO struct acpi_gpe_block_info *gpe_block; +}; + +struct acpi_object_power_resource { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO u32 system_level; + u32 resource_order; +}; + +struct acpi_object_processor { + ACPI_OBJECT_COMMON_HEADER + /* The next two fields take advantage of the 3-byte space before NOTIFY_INFO */ + u8 proc_id; + u8 length; + ACPI_COMMON_NOTIFY_INFO acpi_io_address address; +}; + +struct acpi_object_thermal_zone { +ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; + +/****************************************************************************** + * + * Fields. All share a common header/info field. + * + *****************************************************************************/ + +/* + * Common bitfield for the field objects + * "Field Datum" -- a datum from the actual field object + * "Buffer Datum" -- a datum from a user buffer, read from or to be written to the field + */ +#define ACPI_COMMON_FIELD_INFO \ + u8 field_flags; /* Access, update, and lock bits */\ + u8 attribute; /* From access_as keyword */\ + u8 access_byte_width; /* Read/Write size in bytes */\ + struct acpi_namespace_node *node; /* Link back to parent node */\ + u32 bit_length; /* Length of field in bits */\ + u32 base_byte_offset; /* Byte offset within containing object */\ + u32 value; /* Value to store into the Bank or Index register */\ + u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ + u8 access_bit_width; /* Read/Write size in bits (8-64) */ + +struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */ +}; + +struct acpi_object_region_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing op_region object */ +}; + +struct acpi_object_bank_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing op_region object */ + union acpi_operand_object *bank_obj; /* bank_select Register object */ +}; + +struct acpi_object_index_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO + /* + * No "RegionObj" pointer needed since the Index and Data registers + * are each field definitions unto themselves. + */ + union acpi_operand_object *index_obj; /* Index register */ + union acpi_operand_object *data_obj; /* Data register */ +}; + +/* The buffer_field is different in that it is part of a Buffer, not an op_region */ + +struct acpi_object_buffer_field { + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *buffer_obj; /* Containing Buffer object */ +}; + +/****************************************************************************** + * + * Objects for handlers + * + *****************************************************************************/ + +struct acpi_object_notify_handler { + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Parent device */ + acpi_notify_handler handler; + void *context; +}; + +struct acpi_object_addr_handler { + ACPI_OBJECT_COMMON_HEADER u8 space_id; + u8 handler_flags; + acpi_adr_space_handler handler; + struct acpi_namespace_node *node; /* Parent device */ + void *context; + acpi_adr_space_setup setup; + union acpi_operand_object *region_list; /* regions using this handler */ + union acpi_operand_object *next; +}; + +/* Flags for address handler (handler_flags) */ + +#define ACPI_ADDR_HANDLER_DEFAULT_INSTALLED 0x01 + +/****************************************************************************** + * + * Special internal objects + * + *****************************************************************************/ + +/* + * The Reference object is used for these opcodes: + * Arg[0-6], Local[0-7], index_op, name_op, ref_of_op, load_op, load_table_op, debug_op + * The Reference.Class differentiates these types. + */ +struct acpi_object_reference { + ACPI_OBJECT_COMMON_HEADER u8 class; /* Reference Class */ + u8 target_type; /* Used for Index Op */ + u8 reserved; + void *object; /* name_op=>HANDLE to obj, index_op=>union acpi_operand_object */ + struct acpi_namespace_node *node; /* ref_of or Namepath */ + union acpi_operand_object **where; /* Target of Index */ + u32 value; /* Used for Local/Arg/Index/ddb_handle */ +}; + +/* Values for Reference.Class above */ + +typedef enum { + ACPI_REFCLASS_LOCAL = 0, /* Method local */ + ACPI_REFCLASS_ARG = 1, /* Method argument */ + ACPI_REFCLASS_REFOF = 2, /* Result of ref_of() TBD: Split to Ref/Node and Ref/operand_obj? */ + ACPI_REFCLASS_INDEX = 3, /* Result of Index() */ + ACPI_REFCLASS_TABLE = 4, /* ddb_handle - Load(), load_table() */ + ACPI_REFCLASS_NAME = 5, /* Reference to a named object */ + ACPI_REFCLASS_DEBUG = 6, /* Debug object */ + + ACPI_REFCLASS_MAX = 6 +} ACPI_REFERENCE_CLASSES; + +/* + * Extra object is used as additional storage for types that + * have AML code in their declarations (term_args) that must be + * evaluated at run time. + * + * Currently: Region and field_unit types + */ +struct acpi_object_extra { + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ + void *region_context; /* Region-specific data */ + u8 *aml_start; + u32 aml_length; +}; + +/* Additional data that can be attached to namespace nodes */ + +struct acpi_object_data { + ACPI_OBJECT_COMMON_HEADER acpi_object_handler handler; + void *pointer; +}; + +/* Structure used when objects are cached for reuse */ + +struct acpi_object_cache_list { + ACPI_OBJECT_COMMON_HEADER union acpi_operand_object *next; /* Link for object cache and internal lists */ +}; + +/****************************************************************************** + * + * union acpi_operand_object Descriptor - a giant union of all of the above + * + *****************************************************************************/ + +union acpi_operand_object { + struct acpi_object_common common; + struct acpi_object_integer integer; + struct acpi_object_string string; + struct acpi_object_buffer buffer; + struct acpi_object_package package; + struct acpi_object_event event; + struct acpi_object_method method; + struct acpi_object_mutex mutex; + struct acpi_object_region region; + struct acpi_object_notify_common common_notify; + struct acpi_object_device device; + struct acpi_object_power_resource power_resource; + struct acpi_object_processor processor; + struct acpi_object_thermal_zone thermal_zone; + struct acpi_object_field_common common_field; + struct acpi_object_region_field field; + struct acpi_object_buffer_field buffer_field; + struct acpi_object_bank_field bank_field; + struct acpi_object_index_field index_field; + struct acpi_object_notify_handler notify; + struct acpi_object_addr_handler address_space; + struct acpi_object_reference reference; + struct acpi_object_extra extra; + struct acpi_object_data data; + struct acpi_object_cache_list cache; + + /* + * Add namespace node to union in order to simplify code that accepts both + * ACPI_OPERAND_OBJECTs and ACPI_NAMESPACE_NODEs. The structures share + * a common descriptor_type field in order to differentiate them. + */ + struct acpi_namespace_node node; +}; + +/****************************************************************************** + * + * union acpi_descriptor - objects that share a common descriptor identifier + * + *****************************************************************************/ + +/* Object descriptor types */ + +#define ACPI_DESC_TYPE_CACHED 0x01 /* Used only when object is cached */ +#define ACPI_DESC_TYPE_STATE 0x02 +#define ACPI_DESC_TYPE_STATE_UPDATE 0x03 +#define ACPI_DESC_TYPE_STATE_PACKAGE 0x04 +#define ACPI_DESC_TYPE_STATE_CONTROL 0x05 +#define ACPI_DESC_TYPE_STATE_RPSCOPE 0x06 +#define ACPI_DESC_TYPE_STATE_PSCOPE 0x07 +#define ACPI_DESC_TYPE_STATE_WSCOPE 0x08 +#define ACPI_DESC_TYPE_STATE_RESULT 0x09 +#define ACPI_DESC_TYPE_STATE_NOTIFY 0x0A +#define ACPI_DESC_TYPE_STATE_THREAD 0x0B +#define ACPI_DESC_TYPE_WALK 0x0C +#define ACPI_DESC_TYPE_PARSER 0x0D +#define ACPI_DESC_TYPE_OPERAND 0x0E +#define ACPI_DESC_TYPE_NAMED 0x0F +#define ACPI_DESC_TYPE_MAX 0x0F + +struct acpi_common_descriptor { + void *common_pointer; + u8 descriptor_type; /* To differentiate various internal objs */ +}; + +union acpi_descriptor { + struct acpi_common_descriptor common; + union acpi_operand_object object; + struct acpi_namespace_node node; + union acpi_parse_object op; +}; + +#pragma pack() + +#endif /* _ACOBJECT_H */ diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h new file mode 100644 index 000000000000..dfdf63327885 --- /dev/null +++ b/drivers/acpi/acpica/acopcode.h @@ -0,0 +1,323 @@ +/****************************************************************************** + * + * Name: acopcode.h - AML opcode information for the AML parser and interpreter + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACOPCODE_H__ +#define __ACOPCODE_H__ + +#define MAX_EXTENDED_OPCODE 0x88 +#define NUM_EXTENDED_OPCODE (MAX_EXTENDED_OPCODE + 1) +#define MAX_INTERNAL_OPCODE +#define NUM_INTERNAL_OPCODE (MAX_INTERNAL_OPCODE + 1) + +/* Used for non-assigned opcodes */ + +#define _UNK 0x6B + +/* + * Reserved ASCII characters. Do not use any of these for + * internal opcodes, since they are used to differentiate + * name strings from AML opcodes + */ +#define _ASC 0x6C +#define _NAM 0x6C +#define _PFX 0x6D + +/* + * All AML opcodes and the parse-time arguments for each. Used by the AML + * parser Each list is compressed into a 32-bit number and stored in the + * master opcode table (in psopcode.c). + */ +#define ARGP_ACCESSFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_ACQUIRE_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_WORDDATA) +#define ARGP_ADD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_ALIAS_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_NAME) +#define ARGP_ARG0 ARG_NONE +#define ARGP_ARG1 ARG_NONE +#define ARGP_ARG2 ARG_NONE +#define ARGP_ARG3 ARG_NONE +#define ARGP_ARG4 ARG_NONE +#define ARGP_ARG5 ARG_NONE +#define ARGP_ARG6 ARG_NONE +#define ARGP_BANK_FIELD_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_TERMARG, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_BIT_AND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NAND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_OR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_XOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BREAK_OP ARG_NONE +#define ARGP_BREAK_POINT_OP ARG_NONE +#define ARGP_BUFFER_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_BYTELIST) +#define ARGP_BYTE_OP ARGP_LIST1 (ARGP_BYTEDATA) +#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME) +#define ARGP_CONTINUE_OP ARG_NONE +#define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME) +#define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_BYTE_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_DWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_FIELD_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_QWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_WORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_DATA_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_DEBUG_OP ARG_NONE +#define ARGP_DECREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_DEREF_OF_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_DEVICE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_DIVIDE_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET, ARGP_TARGET) +#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA) +#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST) +#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME) +#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG) +#define ARGP_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FIND_SET_RIGHT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FROM_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_IF_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_INCREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_INDEX_FIELD_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_INDEX_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_LAND_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATEREQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESS_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESSEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LNOT_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_LNOTEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOAD_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_SUPERNAME) +#define ARGP_LOAD_TABLE_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOCAL0 ARG_NONE +#define ARGP_LOCAL1 ARG_NONE +#define ARGP_LOCAL2 ARG_NONE +#define ARGP_LOCAL3 ARG_NONE +#define ARGP_LOCAL4 ARG_NONE +#define ARGP_LOCAL5 ARG_NONE +#define ARGP_LOCAL6 ARG_NONE +#define ARGP_LOCAL7 ARG_NONE +#define ARGP_LOR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_MATCH_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_METHOD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMLIST) +#define ARGP_METHODCALL_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_MID_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MOD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MULTIPLY_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MUTEX_OP ARGP_LIST2 (ARGP_NAME, ARGP_BYTEDATA) +#define ARGP_NAME_OP ARGP_LIST2 (ARGP_NAME, ARGP_DATAOBJ) +#define ARGP_NAMEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NOOP_OP ARG_NONE +#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_ONE_OP ARG_NONE +#define ARGP_ONES_OP ARG_NONE +#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST) +#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST) +#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST) +#define ARGP_QWORD_OP ARGP_LIST1 (ARGP_QWORDDATA) +#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_RESET_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_REVISION_OP ARG_NONE +#define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST) +#define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_SIZE_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_SLEEP_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_STALL_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_STATICSTRING_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_STORE_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SUPERNAME) +#define ARGP_STRING_OP ARGP_LIST1 (ARGP_CHARLIST) +#define ARGP_SUBTRACT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_THERMAL_ZONE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_TIMER_OP ARG_NONE +#define ARGP_TO_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_BUFFER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_DEC_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_HEX_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_INTEGER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_STRING_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TYPE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_UNLOAD_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_VAR_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_DATAOBJLIST) +#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_WHILE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_WORD_OP ARGP_LIST1 (ARGP_WORDDATA) +#define ARGP_ZERO_OP ARG_NONE + +/* + * All AML opcodes and the runtime arguments for each. Used by the AML + * interpreter Each list is compressed into a 32-bit number and stored + * in the master opcode table (in psopcode.c). + * + * (Used by prep_operands procedure and the ASL Compiler) + */ +#define ARGI_ACCESSFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_ACQUIRE_OP ARGI_LIST2 (ARGI_MUTEX, ARGI_INTEGER) +#define ARGI_ADD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_ALIAS_OP ARGI_INVALID_OPCODE +#define ARGI_ARG0 ARG_NONE +#define ARGI_ARG1 ARG_NONE +#define ARGI_ARG2 ARG_NONE +#define ARGI_ARG3 ARG_NONE +#define ARGI_ARG4 ARG_NONE +#define ARGI_ARG5 ARG_NONE +#define ARGI_ARG6 ARG_NONE +#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NOT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_OR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_XOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BREAK_OP ARG_NONE +#define ARGI_BREAK_POINT_OP ARG_NONE +#define ARGI_BUFFER_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_BYTE_OP ARGI_INVALID_OPCODE +#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE +#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA, ARGI_TARGETREF) +#define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF) +#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF) +#define ARGI_CONTINUE_OP ARGI_INVALID_OPCODE +#define ARGI_COPY_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_SIMPLE_TARGET) +#define ARGI_CREATE_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_BYTE_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_DWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_FIELD_OP ARGI_LIST4 (ARGI_BUFFER, ARGI_INTEGER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_QWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_WORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_DATA_REGION_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_STRING) +#define ARGI_DEBUG_OP ARG_NONE +#define ARGI_DECREMENT_OP ARGI_LIST1 (ARGI_TARGETREF) +#define ARGI_DEREF_OF_OP ARGI_LIST1 (ARGI_REF_OR_STRING) +#define ARGI_DEVICE_OP ARGI_INVALID_OPCODE +#define ARGI_DIVIDE_OP ARGI_LIST4 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF, ARGI_TARGETREF) +#define ARGI_DWORD_OP ARGI_INVALID_OPCODE +#define ARGI_ELSE_OP ARGI_INVALID_OPCODE +#define ARGI_EVENT_OP ARGI_INVALID_OPCODE +#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_FIND_SET_RIGHT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_FROM_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_IF_OP ARGI_INVALID_OPCODE +#define ARGI_INCREMENT_OP ARGI_LIST1 (ARGI_TARGETREF) +#define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_INDEX_OP ARGI_LIST3 (ARGI_COMPLEXOBJ, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_LAND_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATEREQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LLESSEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LNOT_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_LNOTEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LOAD_OP ARGI_LIST2 (ARGI_REGION_OR_BUFFER,ARGI_TARGETREF) +#define ARGI_LOAD_TABLE_OP ARGI_LIST6 (ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_ANYTYPE) +#define ARGI_LOCAL0 ARG_NONE +#define ARGI_LOCAL1 ARG_NONE +#define ARGI_LOCAL2 ARG_NONE +#define ARGI_LOCAL3 ARG_NONE +#define ARGI_LOCAL4 ARG_NONE +#define ARGI_LOCAL5 ARG_NONE +#define ARGI_LOCAL6 ARG_NONE +#define ARGI_LOCAL7 ARG_NONE +#define ARGI_LOR_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_MATCH_OP ARGI_LIST6 (ARGI_PACKAGE, ARGI_INTEGER, ARGI_COMPUTEDATA, ARGI_INTEGER,ARGI_COMPUTEDATA,ARGI_INTEGER) +#define ARGI_METHOD_OP ARGI_INVALID_OPCODE +#define ARGI_METHODCALL_OP ARGI_INVALID_OPCODE +#define ARGI_MID_OP ARGI_LIST4 (ARGI_BUFFER_OR_STRING,ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MOD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MULTIPLY_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MUTEX_OP ARGI_INVALID_OPCODE +#define ARGI_NAME_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE +#define ARGI_NOOP_OP ARG_NONE +#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_DEVICE_REF, ARGI_INTEGER) +#define ARGI_ONE_OP ARG_NONE +#define ARGI_ONES_OP ARG_NONE +#define ARGI_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_POWER_RES_OP ARGI_INVALID_OPCODE +#define ARGI_PROCESSOR_OP ARGI_INVALID_OPCODE +#define ARGI_QWORD_OP ARGI_INVALID_OPCODE +#define ARGI_REF_OF_OP ARGI_LIST1 (ARGI_OBJECT_REF) +#define ARGI_REGION_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_RELEASE_OP ARGI_LIST1 (ARGI_MUTEX) +#define ARGI_RESERVEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_RESET_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_RETURN_OP ARGI_INVALID_OPCODE +#define ARGI_REVISION_OP ARG_NONE +#define ARGI_SCOPE_OP ARGI_INVALID_OPCODE +#define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_SIZE_OF_OP ARGI_LIST1 (ARGI_DATAOBJECT) +#define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE +#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_TARGETREF) +#define ARGI_STRING_OP ARGI_INVALID_OPCODE +#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE +#define ARGI_TIMER_OP ARG_NONE +#define ARGI_TO_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_TO_BUFFER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_DEC_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE) +#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE) +#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER) +#define ARGI_WHILE_OP ARGI_INVALID_OPCODE +#define ARGI_WORD_OP ARGI_INVALID_OPCODE +#define ARGI_ZERO_OP ARG_NONE + +#endif /* __ACOPCODE_H__ */ diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h new file mode 100644 index 000000000000..23ee0fbf5619 --- /dev/null +++ b/drivers/acpi/acpica/acparser.h @@ -0,0 +1,234 @@ +/****************************************************************************** + * + * Module Name: acparser.h - AML Parser subcomponent prototypes and defines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACPARSER_H__ +#define __ACPARSER_H__ + +#define OP_HAS_RETURN_VALUE 1 + +/* Variable number of arguments. This field must be 32 bits */ + +#define ACPI_VAR_ARGS ACPI_UINT32_MAX + +#define ACPI_PARSE_DELETE_TREE 0x0001 +#define ACPI_PARSE_NO_TREE_DELETE 0x0000 +#define ACPI_PARSE_TREE_MASK 0x0001 + +#define ACPI_PARSE_LOAD_PASS1 0x0010 +#define ACPI_PARSE_LOAD_PASS2 0x0020 +#define ACPI_PARSE_EXECUTE 0x0030 +#define ACPI_PARSE_MODE_MASK 0x0030 + +#define ACPI_PARSE_DEFERRED_OP 0x0100 +#define ACPI_PARSE_DISASSEMBLE 0x0200 + +/****************************************************************************** + * + * Parser interfaces + * + *****************************************************************************/ + +/* + * psxface - Parser external interfaces + */ +acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info); + +/* + * psargs - Parse AML opcode arguments + */ +u8 *acpi_ps_get_next_package_end(struct acpi_parse_state *parser_state); + +char *acpi_ps_get_next_namestring(struct acpi_parse_state *parser_state); + +void +acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state, + u32 arg_type, union acpi_parse_object *arg); + +acpi_status +acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + union acpi_parse_object *arg, u8 method_call); + +acpi_status +acpi_ps_get_next_arg(struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + u32 arg_type, union acpi_parse_object **return_arg); + +/* + * psfind + */ +union acpi_parse_object *acpi_ps_find_name(union acpi_parse_object *scope, + u32 name, u32 opcode); + +union acpi_parse_object *acpi_ps_get_parent(union acpi_parse_object *op); + +/* + * psopcode - AML Opcode information + */ +const struct acpi_opcode_info *acpi_ps_get_opcode_info(u16 opcode); + +char *acpi_ps_get_opcode_name(u16 opcode); + +u8 acpi_ps_get_argument_count(u32 op_type); + +/* + * psparse - top level parsing routines + */ +acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state); + +u32 acpi_ps_get_opcode_size(u32 opcode); + +u16 acpi_ps_peek_opcode(struct acpi_parse_state *state); + +acpi_status +acpi_ps_complete_this_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op); + +acpi_status +acpi_ps_next_parse_state(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + acpi_status callback_status); + +/* + * psloop - main parse loop + */ +acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state); + +/* + * psscope - Scope stack management routines + */ +acpi_status +acpi_ps_init_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object *root); + +union acpi_parse_object *acpi_ps_get_parent_scope(struct acpi_parse_state + *state); + +u8 acpi_ps_has_completed_scope(struct acpi_parse_state *parser_state); + +void +acpi_ps_pop_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object **op, + u32 * arg_list, u32 * arg_count); + +acpi_status +acpi_ps_push_scope(struct acpi_parse_state *parser_state, + union acpi_parse_object *op, + u32 remaining_args, u32 arg_count); + +void acpi_ps_cleanup_scope(struct acpi_parse_state *state); + +/* + * pstree - parse tree manipulation routines + */ +void +acpi_ps_append_arg(union acpi_parse_object *op, union acpi_parse_object *arg); + +union acpi_parse_object *acpi_ps_find(union acpi_parse_object *scope, + char *path, u16 opcode, u32 create); + +union acpi_parse_object *acpi_ps_get_arg(union acpi_parse_object *op, u32 argn); + +#ifdef ACPI_FUTURE_USAGE +union acpi_parse_object *acpi_ps_get_depth_next(union acpi_parse_object *origin, + union acpi_parse_object *op); +#endif /* ACPI_FUTURE_USAGE */ + +/* + * pswalk - parse tree walk routines + */ +acpi_status +acpi_ps_walk_parsed_aml(union acpi_parse_object *start_op, + union acpi_parse_object *end_op, + union acpi_operand_object *mth_desc, + struct acpi_namespace_node *start_node, + union acpi_operand_object **params, + union acpi_operand_object **caller_return_desc, + acpi_owner_id owner_id, + acpi_parse_downwards descending_callback, + acpi_parse_upwards ascending_callback); + +acpi_status +acpi_ps_get_next_walk_op(struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + acpi_parse_upwards ascending_callback); + +acpi_status acpi_ps_delete_completed_op(struct acpi_walk_state *walk_state); + +void acpi_ps_delete_parse_tree(union acpi_parse_object *root); + +/* + * psutils - parser utilities + */ +union acpi_parse_object *acpi_ps_create_scope_op(void); + +void acpi_ps_init_op(union acpi_parse_object *op, u16 opcode); + +union acpi_parse_object *acpi_ps_alloc_op(u16 opcode); + +void acpi_ps_free_op(union acpi_parse_object *op); + +u8 acpi_ps_is_leading_char(u32 c); + +u8 acpi_ps_is_prefix_char(u32 c); + +#ifdef ACPI_FUTURE_USAGE +u32 acpi_ps_get_name(union acpi_parse_object *op); +#endif /* ACPI_FUTURE_USAGE */ + +void acpi_ps_set_name(union acpi_parse_object *op, u32 name); + +/* + * psdump - display parser tree + */ +u32 +acpi_ps_sprint_path(char *buffer_start, + u32 buffer_size, union acpi_parse_object *op); + +u32 +acpi_ps_sprint_op(char *buffer_start, + u32 buffer_size, union acpi_parse_object *op); + +void acpi_ps_show(union acpi_parse_object *op); + +#endif /* __ACPARSER_H__ */ diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h new file mode 100644 index 000000000000..16a9ca9a66e4 --- /dev/null +++ b/drivers/acpi/acpica/acpredef.h @@ -0,0 +1,371 @@ +/****************************************************************************** + * + * Name: acpredef - Information table for ACPI predefined methods and objects + * $Revision: 1.1 $ + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACPREDEF_H__ +#define __ACPREDEF_H__ + +/****************************************************************************** + * + * Return Package types + * + * 1) PTYPE1 packages do not contain sub-packages. + * + * ACPI_PTYPE1_FIXED: Fixed length, 1 or 2 object types: + * object type + * count + * object type + * count + * + * ACPI_PTYPE1_VAR: Variable length: + * object type (Int/Buf/Ref) + * + * ACPI_PTYPE1_OPTION: Package has some required and some optional elements: + * Used for _PRW + * + * + * 2) PTYPE2 packages contain a variable number of sub-packages. Each of the + * different types describe the contents of each of the sub-packages. + * + * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types: + * object type + * count + * object type + * count + * + * ACPI_PTYPE2_COUNT: Each subpackage has a count as first element: + * object type + * + * ACPI_PTYPE2_PKG_COUNT: Count of subpackages at start, 1 or 2 object types: + * object type + * count + * object type + * count + * + * ACPI_PTYPE2_FIXED: Each subpackage is of fixed length: + * Used for _PRT + * + * ACPI_PTYPE2_MIN: Each subpackage has a variable but minimum length + * Used for _HPX + * + *****************************************************************************/ + +enum acpi_return_package_types { + ACPI_PTYPE1_FIXED = 1, + ACPI_PTYPE1_VAR = 2, + ACPI_PTYPE1_OPTION = 3, + ACPI_PTYPE2 = 4, + ACPI_PTYPE2_COUNT = 5, + ACPI_PTYPE2_PKG_COUNT = 6, + ACPI_PTYPE2_FIXED = 7, + ACPI_PTYPE2_MIN = 8 +}; + +/* + * Predefined method/object information table. + * + * These are the names that can actually be evaluated via acpi_evaluate_object. + * Not present in this table are the following: + * + * 1) Predefined/Reserved names that are never evaluated via acpi_evaluate_object: + * _Lxx and _Exx GPE methods + * _Qxx EC methods + * _T_x compiler temporary variables + * + * 2) Predefined names that never actually exist within the AML code: + * Predefined resource descriptor field names + * + * 3) Predefined names that are implemented within ACPICA: + * _OSI + * + * 4) Some predefined names that are not documented within the ACPI spec. + * _WDG, _WED + * + * The main entries in the table each contain the following items: + * + * Name - The ACPI reserved name + * param_count - Number of arguments to the method + * expected_btypes - Allowed type(s) for the return value. + * 0 means that no return value is expected. + * + * For methods that return packages, the next entry in the table contains + * information about the expected structure of the package. This information + * is saved here (rather than in a separate table) in order to minimize the + * overall size of the stored data. + */ +static const union acpi_predefined_info predefined_names[] = { + {.info = {"_AC0", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC1", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC2", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC3", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC4", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC5", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC6", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC7", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC8", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AC9", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_ADR", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_AL0", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL1", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL2", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL3", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL4", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL5", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL6", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL7", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL8", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_AL9", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_ALC", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_ALI", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_ALP", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_ALR", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 2, 0, 0, 0}}, /* variable (Pkgs) each 2 (Ints) */ + {.info = {"_ALT", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_BBN", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_BCL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Ints) */ + {.info = {"_BCM", 1, 0}}, + {.info = {"_BDN", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_BFS", 1, 0}}, + {.info = {"_BIF", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, + 9, + ACPI_RTYPE_STRING | ACPI_RTYPE_BUFFER, 4, 0}}, /* fixed (9 Int),(4 Str) */ + {.info = {"_BLT", 3, 0}}, + {.info = {"_BMC", 1, 0}}, + {.info = {"_BMD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* fixed (5 Int) */ + {.info = {"_BQC", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_BST", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0}}, /* fixed (4 Int) */ + {.info = {"_BTM", 1, ACPI_RTYPE_INTEGER}}, + {.info = {"_BTP", 1, 0}}, + {.info = {"_CBA", 0, ACPI_RTYPE_INTEGER}}, /* see PCI firmware spec 3.0 */ + {.info = {"_CID", 0, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | ACPI_RTYPE_PACKAGE}}, + {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING, 0, 0, 0, 0}}, /* variable (Ints/Strs) */ + {.info = {"_CRS", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_CRT", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_CSD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (1 Int(n), n-1 Int) */ + {.info = {"_CST", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_PKG_COUNT, + ACPI_RTYPE_BUFFER, 1, + ACPI_RTYPE_INTEGER, 3, 0}}, /* variable (1 Int(n), n Pkg (1 Buf/3 Int) */ + {.info = {"_DCK", 1, ACPI_RTYPE_INTEGER}}, + {.info = {"_DCS", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_DDC", 1, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER}}, + {.info = {"_DDN", 0, ACPI_RTYPE_STRING}}, + {.info = {"_DGS", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_DIS", 0, 0}}, + {.info = {"_DMA", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_DOD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Ints) */ + {.info = {"_DOS", 1, 0}}, + {.info = {"_DSM", 4, ACPI_RTYPE_ALL}}, /* Must return a type, but it can be of any type */ + {.info = {"_DSS", 1, 0}}, + {.info = {"_DSW", 3, 0}}, + {.info = {"_EC_", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_EDL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_EJ0", 1, 0}}, + {.info = {"_EJ1", 1, 0}}, + {.info = {"_EJ2", 1, 0}}, + {.info = {"_EJ3", 1, 0}}, + {.info = {"_EJ4", 1, 0}}, + {.info = {"_EJD", 0, ACPI_RTYPE_STRING}}, + {.info = {"_FDE", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_FDI", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 16, 0, 0, 0}}, /* fixed (16 Int) */ + {.info = {"_FDM", 1, 0}}, + {.info = {"_FIX", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Ints) */ + {.info = {"_GLK", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_GPD", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_GPE", 0, ACPI_RTYPE_INTEGER}}, /* _GPE method, not _GPE scope */ + {.info = {"_GSB", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_GTF", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_GTM", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_GTS", 1, 0}}, + {.info = {"_HID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, + {.info = {"_HOT", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_HPP", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0}}, /* fixed (4 Int) */ + + /* + * For _HPX, a single package is returned, containing a variable number of sub-packages. + * Each sub-package contains a PCI record setting. There are several different type of + * record settings, of different lengths, but all elements of all settings are Integers. + */ + {.info = {"_HPX", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* variable (Pkgs) each (var Ints) */ + {.info = {"_IFT", 0, ACPI_RTYPE_INTEGER}}, /* see IPMI spec */ + {.info = {"_INI", 0, 0}}, + {.info = {"_IRC", 0, 0}}, + {.info = {"_LCK", 1, 0}}, + {.info = {"_LID", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_MAT", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_MLS", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_STRING, 2, 0, 0, 0}}, /* variable (Pkgs) each (2 Str) */ + {.info = {"_MSG", 1, 0}}, + {.info = {"_OFF", 0, 0}}, + {.info = {"_ON_", 0, 0}}, + {.info = {"_OS_", 0, ACPI_RTYPE_STRING}}, + {.info = {"_OSC", 4, ACPI_RTYPE_BUFFER}}, + {.info = {"_OST", 3, 0}}, + {.info = {"_PCL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_PCT", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0}}, /* fixed (2 Buf) */ + {.info = {"_PDC", 1, 0}}, + {.info = {"_PIC", 1, 0}}, + {.info = {"_PLD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_BUFFER, 0, 0, 0, 0}}, /* variable (Bufs) */ + {.info = {"_PPC", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_PPE", 0, ACPI_RTYPE_INTEGER}}, /* see dig64 spec */ + {.info = {"_PR0", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_PR1", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_PR2", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_PRS", 0, ACPI_RTYPE_BUFFER}}, + + /* + * For _PRT, many BIOSs reverse the 2nd and 3rd Package elements. This bug is so prevalent that there + * is code in the ACPICA Resource Manager to detect this and switch them back. For now, do not allow + * and issue a warning. To allow this and eliminate the warning, add the ACPI_RTYPE_REFERENCE + * type to the 2nd element (index 1) in the statement below. + */ + {.info = {"_PRT", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_FIXED, 4, + ACPI_RTYPE_INTEGER, + ACPI_RTYPE_INTEGER, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_REFERENCE, ACPI_RTYPE_INTEGER}}, /* variable (Pkgs) each (4): Int,Int,Int/Ref,Int */ + + {.info = {"_PRW", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_OPTION, 2, + ACPI_RTYPE_INTEGER | + ACPI_RTYPE_PACKAGE, + ACPI_RTYPE_INTEGER, ACPI_RTYPE_REFERENCE, 0}}, /* variable (Pkgs) each: Pkg/Int,Int,[variable Refs] (Pkg is Ref/Int) */ + + {.info = {"_PS0", 0, 0}}, + {.info = {"_PS1", 0, 0}}, + {.info = {"_PS2", 0, 0}}, + {.info = {"_PS3", 0, 0}}, + {.info = {"_PSC", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_PSD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 0, 0, 0, 0}}, /* variable (Pkgs) each (5 Int) with count */ + {.info = {"_PSL", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_PSR", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_PSS", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 6, 0, 0, 0}}, /* variable (Pkgs) each (6 Int) */ + {.info = {"_PSV", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_PSW", 1, 0}}, + {.info = {"_PTC", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_BUFFER, 2, 0, 0, 0}}, /* fixed (2 Buf) */ + {.info = {"_PTS", 1, 0}}, + {.info = {"_PXM", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_REG", 2, 0}}, + {.info = {"_REV", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_RMV", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_ROM", 2, ACPI_RTYPE_BUFFER}}, + {.info = {"_RTV", 0, ACPI_RTYPE_INTEGER}}, + + /* + * For _S0_ through _S5_, the ACPI spec defines a return Package containing 1 Integer, + * but most DSDTs have it wrong - 2,3, or 4 integers. Allow this by making the objects "variable length", + * but all elements must be Integers. + */ + {.info = {"_S0_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ + {.info = {"_S1_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ + {.info = {"_S2_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ + {.info = {"_S3_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ + {.info = {"_S4_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ + {.info = {"_S5_", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER, 1, 0, 0, 0}}, /* fixed (1 Int) */ + + {.info = {"_S1D", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S2D", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S3D", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S4D", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S0W", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S1W", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S2W", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S3W", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_S4W", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_SBS", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_SCP", 0x13, 0}}, /* Acpi 1.0 allowed 1 arg. Acpi 3.0 expanded to 3 args. Allow both. */ + /* Note: the 3-arg definition may be removed for ACPI 4.0 */ + {.info = {"_SDD", 1, 0}}, + {.info = {"_SEG", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_SLI", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_SPD", 1, ACPI_RTYPE_INTEGER}}, + {.info = {"_SRS", 1, 0}}, + {.info = {"_SRV", 0, ACPI_RTYPE_INTEGER}}, /* see IPMI spec */ + {.info = {"_SST", 1, 0}}, + {.info = {"_STA", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_STM", 3, 0}}, + {.info = {"_STR", 0, ACPI_RTYPE_BUFFER}}, + {.info = {"_SUN", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_SWS", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TC1", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TC2", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TMP", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TPC", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TPT", 1, 0}}, + {.info = {"_TRT", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_REFERENCE, 2, + ACPI_RTYPE_INTEGER, 6, 0}}, /* variable (Pkgs) each 2_ref/6_int */ + {.info = {"_TSD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* variable (Pkgs) each 5_int with count */ + {.info = {"_TSP", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TSS", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE2, ACPI_RTYPE_INTEGER, 5, 0, 0, 0}}, /* variable (Pkgs) each 5_int */ + {.info = {"_TST", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_TTS", 1, 0}}, + {.info = {"_TZD", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0}}, /* variable (Refs) */ + {.info = {"_TZM", 0, ACPI_RTYPE_REFERENCE}}, + {.info = {"_TZP", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_UID", 0, ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING}}, + {.info = {"_UPC", 0, ACPI_RTYPE_PACKAGE}}, {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0}}, /* fixed (4 Int) */ + {.info = {"_UPD", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_UPP", 0, ACPI_RTYPE_INTEGER}}, + {.info = {"_VPO", 0, ACPI_RTYPE_INTEGER}}, + + /* Acpi 1.0 defined _WAK with no return value. Later, it was changed to return a package */ + + {.info = {"_WAK", 1, ACPI_RTYPE_NONE | ACPI_RTYPE_INTEGER | ACPI_RTYPE_PACKAGE}}, + {.ret_info = {ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0}}, /* fixed (2 Int), but is optional */ + {.ret_info = {0, 0, 0, 0, 0, 0}} /* Table terminator */ +}; + +#if 0 + /* Not implemented */ + +{ +"_WDG", 0, ACPI_RTYPE_BUFFER}, /* MS Extension */ + +{ +"_WED", 1, ACPI_RTYPE_PACKAGE}, /* MS Extension */ + + /* This is an internally implemented control method, no need to check */ +{ +"_OSI", 1, ACPI_RTYPE_INTEGER}, + + /* TBD: */ + _PRT - currently ignore reversed entries.attempt to fix here ? + think about code that attempts to fix package elements like _BIF, etc. +#endif +#endif diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h new file mode 100644 index 000000000000..eef5bd7a59fa --- /dev/null +++ b/drivers/acpi/acpica/acresrc.h @@ -0,0 +1,336 @@ +/****************************************************************************** + * + * Name: acresrc.h - Resource Manager function prototypes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACRESRC_H__ +#define __ACRESRC_H__ + +/* Need the AML resource descriptor structs */ + +#include "amlresrc.h" + +/* + * If possible, pack the following structures to byte alignment, since we + * don't care about performance for debug output. Two cases where we cannot + * pack the structures: + * + * 1) Hardware does not support misaligned memory transfers + * 2) Compiler does not support pointers within packed structures + */ +#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED)) +#pragma pack(1) +#endif + +/* + * Individual entry for the resource conversion tables + */ +typedef const struct acpi_rsconvert_info { + u8 opcode; + u8 resource_offset; + u8 aml_offset; + u8 value; + +} acpi_rsconvert_info; + +/* Resource conversion opcodes */ + +#define ACPI_RSC_INITGET 0 +#define ACPI_RSC_INITSET 1 +#define ACPI_RSC_FLAGINIT 2 +#define ACPI_RSC_1BITFLAG 3 +#define ACPI_RSC_2BITFLAG 4 +#define ACPI_RSC_COUNT 5 +#define ACPI_RSC_COUNT16 6 +#define ACPI_RSC_LENGTH 7 +#define ACPI_RSC_MOVE8 8 +#define ACPI_RSC_MOVE16 9 +#define ACPI_RSC_MOVE32 10 +#define ACPI_RSC_MOVE64 11 +#define ACPI_RSC_SET8 12 +#define ACPI_RSC_DATA8 13 +#define ACPI_RSC_ADDRESS 14 +#define ACPI_RSC_SOURCE 15 +#define ACPI_RSC_SOURCEX 16 +#define ACPI_RSC_BITMASK 17 +#define ACPI_RSC_BITMASK16 18 +#define ACPI_RSC_EXIT_NE 19 +#define ACPI_RSC_EXIT_LE 20 +#define ACPI_RSC_EXIT_EQ 21 + +/* Resource Conversion sub-opcodes */ + +#define ACPI_RSC_COMPARE_AML_LENGTH 0 +#define ACPI_RSC_COMPARE_VALUE 1 + +#define ACPI_RSC_TABLE_SIZE(d) (sizeof (d) / sizeof (struct acpi_rsconvert_info)) + +#define ACPI_RS_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_resource,f) +#define AML_OFFSET(f) (u8) ACPI_OFFSET (union aml_resource,f) + +typedef const struct acpi_rsdump_info { + u8 opcode; + u8 offset; + char *name; + const char **pointer; + +} acpi_rsdump_info; + +/* Values for the Opcode field above */ + +#define ACPI_RSD_TITLE 0 +#define ACPI_RSD_LITERAL 1 +#define ACPI_RSD_STRING 2 +#define ACPI_RSD_UINT8 3 +#define ACPI_RSD_UINT16 4 +#define ACPI_RSD_UINT32 5 +#define ACPI_RSD_UINT64 6 +#define ACPI_RSD_1BITFLAG 7 +#define ACPI_RSD_2BITFLAG 8 +#define ACPI_RSD_SHORTLIST 9 +#define ACPI_RSD_LONGLIST 10 +#define ACPI_RSD_DWORDLIST 11 +#define ACPI_RSD_ADDRESS 12 +#define ACPI_RSD_SOURCE 13 + +/* restore default alignment */ + +#pragma pack() + +/* Resource tables indexed by internal resource type */ + +extern const u8 acpi_gbl_aml_resource_sizes[]; +extern struct acpi_rsconvert_info *acpi_gbl_set_resource_dispatch[]; + +/* Resource tables indexed by raw AML resource descriptor type */ + +extern const u8 acpi_gbl_resource_struct_sizes[]; +extern struct acpi_rsconvert_info *acpi_gbl_get_resource_dispatch[]; + +struct acpi_vendor_walk_info { + struct acpi_vendor_uuid *uuid; + struct acpi_buffer *buffer; + acpi_status status; +}; + +/* + * rscreate + */ +acpi_status +acpi_rs_create_resource_list(union acpi_operand_object *aml_buffer, + struct acpi_buffer *output_buffer); + +acpi_status +acpi_rs_create_aml_resources(struct acpi_resource *linked_list_buffer, + struct acpi_buffer *output_buffer); + +acpi_status +acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, + struct acpi_buffer *output_buffer); + +/* + * rsutils + */ + +acpi_status +acpi_rs_get_prt_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_crs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_get_method_data(acpi_handle handle, + char *path, struct acpi_buffer *ret_buffer); + +acpi_status +acpi_rs_set_srs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); + +/* + * rscalc + */ +acpi_status +acpi_rs_get_list_length(u8 * aml_buffer, + u32 aml_buffer_length, acpi_size * size_needed); + +acpi_status +acpi_rs_get_aml_length(struct acpi_resource *linked_list_buffer, + acpi_size * size_needed); + +acpi_status +acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, + acpi_size * buffer_size_needed); + +acpi_status +acpi_rs_convert_aml_to_resources(u8 * aml, + u32 length, + u32 offset, u8 resource_index, void **context); + +acpi_status +acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, + acpi_size aml_size_needed, u8 * output_buffer); + +/* + * rsaddr + */ +void +acpi_rs_set_address_common(union aml_resource *aml, + struct acpi_resource *resource); + +u8 +acpi_rs_get_address_common(struct acpi_resource *resource, + union aml_resource *aml); + +/* + * rsmisc + */ +acpi_status +acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, + union aml_resource *aml, + struct acpi_rsconvert_info *info); + +acpi_status +acpi_rs_convert_resource_to_aml(struct acpi_resource *resource, + union aml_resource *aml, + struct acpi_rsconvert_info *info); + +/* + * rsutils + */ +void +acpi_rs_move_data(void *destination, + void *source, u16 item_count, u8 move_type); + +u8 acpi_rs_decode_bitmask(u16 mask, u8 * list); + +u16 acpi_rs_encode_bitmask(u8 * list, u8 count); + +acpi_rs_length +acpi_rs_get_resource_source(acpi_rs_length resource_length, + acpi_rs_length minimum_length, + struct acpi_resource_source *resource_source, + union aml_resource *aml, char *string_ptr); + +acpi_rsdesc_size +acpi_rs_set_resource_source(union aml_resource *aml, + acpi_rs_length minimum_length, + struct acpi_resource_source *resource_source); + +void +acpi_rs_set_resource_header(u8 descriptor_type, + acpi_rsdesc_size total_length, + union aml_resource *aml); + +void +acpi_rs_set_resource_length(acpi_rsdesc_size total_length, + union aml_resource *aml); + +/* + * rsdump + */ +void acpi_rs_dump_resource_list(struct acpi_resource *resource); + +void acpi_rs_dump_irq_list(u8 * route_table); + +/* + * Resource conversion tables + */ +extern struct acpi_rsconvert_info acpi_rs_convert_dma[]; +extern struct acpi_rsconvert_info acpi_rs_convert_end_dpf[]; +extern struct acpi_rsconvert_info acpi_rs_convert_io[]; +extern struct acpi_rsconvert_info acpi_rs_convert_fixed_io[]; +extern struct acpi_rsconvert_info acpi_rs_convert_end_tag[]; +extern struct acpi_rsconvert_info acpi_rs_convert_memory24[]; +extern struct acpi_rsconvert_info acpi_rs_convert_generic_reg[]; +extern struct acpi_rsconvert_info acpi_rs_convert_memory32[]; +extern struct acpi_rsconvert_info acpi_rs_convert_fixed_memory32[]; +extern struct acpi_rsconvert_info acpi_rs_convert_address32[]; +extern struct acpi_rsconvert_info acpi_rs_convert_address16[]; +extern struct acpi_rsconvert_info acpi_rs_convert_ext_irq[]; +extern struct acpi_rsconvert_info acpi_rs_convert_address64[]; +extern struct acpi_rsconvert_info acpi_rs_convert_ext_address64[]; + +/* These resources require separate get/set tables */ + +extern struct acpi_rsconvert_info acpi_rs_get_irq[]; +extern struct acpi_rsconvert_info acpi_rs_get_start_dpf[]; +extern struct acpi_rsconvert_info acpi_rs_get_vendor_small[]; +extern struct acpi_rsconvert_info acpi_rs_get_vendor_large[]; + +extern struct acpi_rsconvert_info acpi_rs_set_irq[]; +extern struct acpi_rsconvert_info acpi_rs_set_start_dpf[]; +extern struct acpi_rsconvert_info acpi_rs_set_vendor[]; + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +/* + * rsinfo + */ +extern struct acpi_rsdump_info *acpi_gbl_dump_resource_dispatch[]; + +/* + * rsdump + */ +extern struct acpi_rsdump_info acpi_rs_dump_irq[]; +extern struct acpi_rsdump_info acpi_rs_dump_dma[]; +extern struct acpi_rsdump_info acpi_rs_dump_start_dpf[]; +extern struct acpi_rsdump_info acpi_rs_dump_end_dpf[]; +extern struct acpi_rsdump_info acpi_rs_dump_io[]; +extern struct acpi_rsdump_info acpi_rs_dump_fixed_io[]; +extern struct acpi_rsdump_info acpi_rs_dump_vendor[]; +extern struct acpi_rsdump_info acpi_rs_dump_end_tag[]; +extern struct acpi_rsdump_info acpi_rs_dump_memory24[]; +extern struct acpi_rsdump_info acpi_rs_dump_memory32[]; +extern struct acpi_rsdump_info acpi_rs_dump_fixed_memory32[]; +extern struct acpi_rsdump_info acpi_rs_dump_address16[]; +extern struct acpi_rsdump_info acpi_rs_dump_address32[]; +extern struct acpi_rsdump_info acpi_rs_dump_address64[]; +extern struct acpi_rsdump_info acpi_rs_dump_ext_address64[]; +extern struct acpi_rsdump_info acpi_rs_dump_ext_irq[]; +extern struct acpi_rsdump_info acpi_rs_dump_generic_reg[]; +#endif + +#endif /* __ACRESRC_H__ */ diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h new file mode 100644 index 000000000000..7980a26bad35 --- /dev/null +++ b/drivers/acpi/acpica/acstruct.h @@ -0,0 +1,228 @@ +/****************************************************************************** + * + * Name: acstruct.h - Internal structs + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACSTRUCT_H__ +#define __ACSTRUCT_H__ + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +/***************************************************************************** + * + * Tree walking typedefs and structs + * + ****************************************************************************/ + +/* + * Walk state - current state of a parse tree walk. Used for both a leisurely + * stroll through the tree (for whatever reason), and for control method + * execution. + */ +#define ACPI_NEXT_OP_DOWNWARD 1 +#define ACPI_NEXT_OP_UPWARD 2 + +/* + * Groups of definitions for walk_type used for different implementations of + * walkers (never simultaneously) - flags for interpreter: + */ +#define ACPI_WALK_NON_METHOD 0 +#define ACPI_WALK_METHOD 0x01 +#define ACPI_WALK_METHOD_RESTART 0x02 + +/* Flags for i_aSL compiler only */ + +#define ACPI_WALK_CONST_REQUIRED 0x10 +#define ACPI_WALK_CONST_OPTIONAL 0x20 + +struct acpi_walk_state { + struct acpi_walk_state *next; /* Next walk_state in list */ + u8 descriptor_type; /* To differentiate various internal objs */ + u8 walk_type; + u16 opcode; /* Current AML opcode */ + u8 next_op_info; /* Info about next_op */ + u8 num_operands; /* Stack pointer for Operands[] array */ + u8 operand_index; /* Index into operand stack, to be used by acpi_ds_obj_stack_push */ + acpi_owner_id owner_id; /* Owner of objects created during the walk */ + u8 last_predicate; /* Result of last predicate */ + u8 current_result; + u8 return_used; + u8 scope_depth; + u8 pass_number; /* Parse pass during table load */ + u8 result_size; /* Total elements for the result stack */ + u8 result_count; /* Current number of occupied elements of result stack */ + u32 aml_offset; + u32 arg_types; + u32 method_breakpoint; /* For single stepping */ + u32 user_breakpoint; /* User AML breakpoint */ + u32 parse_flags; + + struct acpi_parse_state parser_state; /* Current state of parser */ + u32 prev_arg_types; + u32 arg_count; /* push for fixed or var args */ + + struct acpi_namespace_node arguments[ACPI_METHOD_NUM_ARGS]; /* Control method arguments */ + struct acpi_namespace_node local_variables[ACPI_METHOD_NUM_LOCALS]; /* Control method locals */ + union acpi_operand_object *operands[ACPI_OBJ_NUM_OPERANDS + 1]; /* Operands passed to the interpreter (+1 for NULL terminator) */ + union acpi_operand_object **params; + + u8 *aml_last_while; + union acpi_operand_object **caller_return_desc; + union acpi_generic_state *control_state; /* List of control states (nested IFs) */ + struct acpi_namespace_node *deferred_node; /* Used when executing deferred opcodes */ + union acpi_operand_object *implicit_return_obj; + struct acpi_namespace_node *method_call_node; /* Called method Node */ + union acpi_parse_object *method_call_op; /* method_call Op if running a method */ + union acpi_operand_object *method_desc; /* Method descriptor if running a method */ + struct acpi_namespace_node *method_node; /* Method node if running a method. */ + union acpi_parse_object *op; /* Current parser op */ + const struct acpi_opcode_info *op_info; /* Info on current opcode */ + union acpi_parse_object *origin; /* Start of walk [Obsolete] */ + union acpi_operand_object *result_obj; + union acpi_generic_state *results; /* Stack of accumulated results */ + union acpi_operand_object *return_desc; /* Return object, if any */ + union acpi_generic_state *scope_info; /* Stack of nested scopes */ + union acpi_parse_object *prev_op; /* Last op that was processed */ + union acpi_parse_object *next_op; /* next op to be processed */ + struct acpi_thread_state *thread; + acpi_parse_downwards descending_callback; + acpi_parse_upwards ascending_callback; +}; + +/* Info used by acpi_ps_init_objects */ + +struct acpi_init_walk_info { + u16 method_count; + u16 device_count; + u16 op_region_count; + u16 field_count; + u16 buffer_count; + u16 package_count; + u16 op_region_init; + u16 field_init; + u16 buffer_init; + u16 package_init; + u16 object_count; + acpi_owner_id owner_id; + u32 table_index; +}; + +struct acpi_get_devices_info { + acpi_walk_callback user_function; + void *context; + const char *hid; +}; + +union acpi_aml_operands { + union acpi_operand_object *operands[7]; + + struct { + struct acpi_object_integer *type; + struct acpi_object_integer *code; + struct acpi_object_integer *argument; + + } fatal; + + struct { + union acpi_operand_object *source; + struct acpi_object_integer *index; + union acpi_operand_object *target; + + } index; + + struct { + union acpi_operand_object *source; + struct acpi_object_integer *index; + struct acpi_object_integer *length; + union acpi_operand_object *target; + + } mid; +}; + +/* + * Structure used to pass object evaluation parameters. + * Purpose is to reduce CPU stack use. + */ +struct acpi_evaluate_info { + struct acpi_namespace_node *prefix_node; + char *pathname; + union acpi_operand_object *obj_desc; + union acpi_operand_object **parameters; + struct acpi_namespace_node *resolved_node; + union acpi_operand_object *return_object; + u8 param_count; + u8 pass_number; + u8 return_object_type; + u8 flags; +}; + +/* Values for Flags above */ + +#define ACPI_IGNORE_RETURN_VALUE 1 + +/* Info used by acpi_ns_initialize_devices */ + +struct acpi_device_walk_info { + u16 device_count; + u16 num_STA; + u16 num_INI; + struct acpi_table_desc *table_desc; + struct acpi_evaluate_info *evaluate_info; +}; + +/* TBD: [Restructure] Merge with struct above */ + +struct acpi_walk_info { + u32 debug_level; + u32 count; + acpi_owner_id owner_id; + u8 display_type; +}; + +/* Display Types */ + +#define ACPI_DISPLAY_SUMMARY (u8) 0 +#define ACPI_DISPLAY_OBJECTS (u8) 1 +#define ACPI_DISPLAY_MASK (u8) 1 + +#define ACPI_DISPLAY_SHORT (u8) 2 + +#endif diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h new file mode 100644 index 000000000000..7ce6e33c7f78 --- /dev/null +++ b/drivers/acpi/acpica/actables.h @@ -0,0 +1,117 @@ +/****************************************************************************** + * + * Name: actables.h - ACPI table management + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __ACTABLES_H__ +#define __ACTABLES_H__ + +acpi_status acpi_allocate_root_table(u32 initial_table_count); + +/* + * tbfadt - FADT parse/convert/validate + */ +void acpi_tb_parse_fadt(u32 table_index, u8 flags); + +void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length); + +/* + * tbfind - find ACPI table + */ +acpi_status +acpi_tb_find_table(char *signature, + char *oem_id, char *oem_table_id, u32 *table_index); + +/* + * tbinstal - Table removal and deletion + */ +acpi_status acpi_tb_resize_root_table_list(void); + +acpi_status acpi_tb_verify_table(struct acpi_table_desc *table_desc); + +acpi_status +acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index); + +acpi_status +acpi_tb_store_table(acpi_physical_address address, + struct acpi_table_header *table, + u32 length, u8 flags, u32 *table_index); + +void acpi_tb_delete_table(struct acpi_table_desc *table_desc); + +void acpi_tb_terminate(void); + +void acpi_tb_delete_namespace_by_owner(u32 table_index); + +acpi_status acpi_tb_allocate_owner_id(u32 table_index); + +acpi_status acpi_tb_release_owner_id(u32 table_index); + +acpi_status acpi_tb_get_owner_id(u32 table_index, acpi_owner_id *owner_id); + +u8 acpi_tb_is_table_loaded(u32 table_index); + +void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded); + +/* + * tbutils - table manager utilities + */ +acpi_status acpi_tb_initialize_facs(void); + +u8 acpi_tb_tables_loaded(void); + +void +acpi_tb_print_table_header(acpi_physical_address address, + struct acpi_table_header *header); + +u8 acpi_tb_checksum(u8 *buffer, u32 length); + +acpi_status +acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length); + +void +acpi_tb_install_table(acpi_physical_address address, + u8 flags, char *signature, u32 table_index); + +acpi_status +acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags); + +#endif /* __ACTABLES_H__ */ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h new file mode 100644 index 000000000000..80d8813484fe --- /dev/null +++ b/drivers/acpi/acpica/acutils.h @@ -0,0 +1,549 @@ +/****************************************************************************** + * + * Name: acutils.h -- prototypes for the common (subsystem-wide) procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef _ACUTILS_H +#define _ACUTILS_H + +extern const u8 acpi_gbl_resource_aml_sizes[]; + +/* Strings used by the disassembler and debugger resource dump routines */ + +#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) + +extern const char *acpi_gbl_bm_decode[]; +extern const char *acpi_gbl_config_decode[]; +extern const char *acpi_gbl_consume_decode[]; +extern const char *acpi_gbl_dec_decode[]; +extern const char *acpi_gbl_he_decode[]; +extern const char *acpi_gbl_io_decode[]; +extern const char *acpi_gbl_ll_decode[]; +extern const char *acpi_gbl_max_decode[]; +extern const char *acpi_gbl_mem_decode[]; +extern const char *acpi_gbl_min_decode[]; +extern const char *acpi_gbl_mtp_decode[]; +extern const char *acpi_gbl_rng_decode[]; +extern const char *acpi_gbl_rw_decode[]; +extern const char *acpi_gbl_shr_decode[]; +extern const char *acpi_gbl_siz_decode[]; +extern const char *acpi_gbl_trs_decode[]; +extern const char *acpi_gbl_ttp_decode[]; +extern const char *acpi_gbl_typ_decode[]; +#endif + +/* Types for Resource descriptor entries */ + +#define ACPI_INVALID_RESOURCE 0 +#define ACPI_FIXED_LENGTH 1 +#define ACPI_VARIABLE_LENGTH 2 +#define ACPI_SMALL_VARIABLE_LENGTH 3 + +typedef +acpi_status(*acpi_walk_aml_callback) (u8 * aml, + u32 length, + u32 offset, + u8 resource_index, void **context); + +typedef +acpi_status(*acpi_pkg_callback) (u8 object_type, + union acpi_operand_object * source_object, + union acpi_generic_state * state, + void *context); + +struct acpi_pkg_info { + u8 *free_space; + acpi_size length; + u32 object_space; + u32 num_packages; +}; + +#define REF_INCREMENT (u16) 0 +#define REF_DECREMENT (u16) 1 +#define REF_FORCE_DELETE (u16) 2 + +/* acpi_ut_dump_buffer */ + +#define DB_BYTE_DISPLAY 1 +#define DB_WORD_DISPLAY 2 +#define DB_DWORD_DISPLAY 4 +#define DB_QWORD_DISPLAY 8 + +/* + * utglobal - Global data structures and procedures + */ +acpi_status acpi_ut_init_globals(void); + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +char *acpi_ut_get_mutex_name(u32 mutex_id); + +const char *acpi_ut_get_notify_name(u32 notify_value); + +#endif + +char *acpi_ut_get_type_name(acpi_object_type type); + +char *acpi_ut_get_node_name(void *object); + +char *acpi_ut_get_descriptor_name(void *object); + +const char *acpi_ut_get_reference_name(union acpi_operand_object *object); + +char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc); + +char *acpi_ut_get_region_name(u8 space_id); + +char *acpi_ut_get_event_name(u32 event_id); + +char acpi_ut_hex_to_ascii_char(acpi_integer integer, u32 position); + +u8 acpi_ut_valid_object_type(acpi_object_type type); + +/* + * utinit - miscellaneous initialization and shutdown + */ +acpi_status acpi_ut_hardware_initialize(void); + +void acpi_ut_subsystem_shutdown(void); + +/* + * utclib - Local implementations of C library functions + */ +#ifndef ACPI_USE_SYSTEM_CLIBRARY + +acpi_size acpi_ut_strlen(const char *string); + +char *acpi_ut_strcpy(char *dst_string, const char *src_string); + +char *acpi_ut_strncpy(char *dst_string, + const char *src_string, acpi_size count); + +int acpi_ut_memcmp(const char *buffer1, const char *buffer2, acpi_size count); + +int acpi_ut_strncmp(const char *string1, const char *string2, acpi_size count); + +int acpi_ut_strcmp(const char *string1, const char *string2); + +char *acpi_ut_strcat(char *dst_string, const char *src_string); + +char *acpi_ut_strncat(char *dst_string, + const char *src_string, acpi_size count); + +u32 acpi_ut_strtoul(const char *string, char **terminator, u32 base); + +char *acpi_ut_strstr(char *string1, char *string2); + +void *acpi_ut_memcpy(void *dest, const void *src, acpi_size count); + +void *acpi_ut_memset(void *dest, u8 value, acpi_size count); + +int acpi_ut_to_upper(int c); + +int acpi_ut_to_lower(int c); + +extern const u8 _acpi_ctype[]; + +#define _ACPI_XA 0x00 /* extra alphabetic - not supported */ +#define _ACPI_XS 0x40 /* extra space */ +#define _ACPI_BB 0x00 /* BEL, BS, etc. - not supported */ +#define _ACPI_CN 0x20 /* CR, FF, HT, NL, VT */ +#define _ACPI_DI 0x04 /* '0'-'9' */ +#define _ACPI_LO 0x02 /* 'a'-'z' */ +#define _ACPI_PU 0x10 /* punctuation */ +#define _ACPI_SP 0x08 /* space */ +#define _ACPI_UP 0x01 /* 'A'-'Z' */ +#define _ACPI_XD 0x80 /* '0'-'9', 'A'-'F', 'a'-'f' */ + +#define ACPI_IS_DIGIT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_DI)) +#define ACPI_IS_SPACE(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_SP)) +#define ACPI_IS_XDIGIT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_XD)) +#define ACPI_IS_UPPER(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_UP)) +#define ACPI_IS_LOWER(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO)) +#define ACPI_IS_PRINT(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP | _ACPI_DI | _ACPI_SP | _ACPI_PU)) +#define ACPI_IS_ALPHA(c) (_acpi_ctype[(unsigned char)(c)] & (_ACPI_LO | _ACPI_UP)) + +#endif /* ACPI_USE_SYSTEM_CLIBRARY */ + +/* + * utcopy - Object construction and conversion interfaces + */ +acpi_status +acpi_ut_build_simple_object(union acpi_operand_object *obj, + union acpi_object *user_obj, + u8 * data_space, u32 * buffer_space_used); + +acpi_status +acpi_ut_build_package_object(union acpi_operand_object *obj, + u8 * buffer, u32 * space_used); + +acpi_status +acpi_ut_copy_iobject_to_eobject(union acpi_operand_object *obj, + struct acpi_buffer *ret_buffer); + +acpi_status +acpi_ut_copy_eobject_to_iobject(union acpi_object *obj, + union acpi_operand_object **internal_obj); + +acpi_status +acpi_ut_copy_isimple_to_isimple(union acpi_operand_object *source_obj, + union acpi_operand_object *dest_obj); + +acpi_status +acpi_ut_copy_iobject_to_iobject(union acpi_operand_object *source_desc, + union acpi_operand_object **dest_desc, + struct acpi_walk_state *walk_state); + +/* + * utcreate - Object creation + */ +acpi_status +acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action); + +/* + * utdebug - Debug interfaces + */ +void acpi_ut_init_stack_ptr_trace(void); + +void acpi_ut_track_stack_ptr(void); + +void +acpi_ut_trace(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id); + +void +acpi_ut_trace_ptr(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, void *pointer); + +void +acpi_ut_trace_u32(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u32 integer); + +void +acpi_ut_trace_str(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, char *string); + +void +acpi_ut_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id); + +void +acpi_ut_status_exit(u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, acpi_status status); + +void +acpi_ut_value_exit(u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, acpi_integer value); + +void +acpi_ut_ptr_exit(u32 line_number, + const char *function_name, + const char *module_name, u32 component_id, u8 *ptr); + +void acpi_ut_dump_buffer(u8 * buffer, u32 count, u32 display, u32 component_id); + +void acpi_ut_dump_buffer2(u8 * buffer, u32 count, u32 display); + +void acpi_ut_report_error(char *module_name, u32 line_number); + +void acpi_ut_report_info(char *module_name, u32 line_number); + +void acpi_ut_report_warning(char *module_name, u32 line_number); + +/* + * utdelete - Object deletion and reference counts + */ +void acpi_ut_add_reference(union acpi_operand_object *object); + +void acpi_ut_remove_reference(union acpi_operand_object *object); + +void acpi_ut_delete_internal_package_object(union acpi_operand_object *object); + +void acpi_ut_delete_internal_simple_object(union acpi_operand_object *object); + +void acpi_ut_delete_internal_object_list(union acpi_operand_object **obj_list); + +/* + * uteval - object evaluation + */ +acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state); + +acpi_status +acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, + char *path, + u32 expected_return_btypes, + union acpi_operand_object **return_desc); + +acpi_status +acpi_ut_evaluate_numeric_object(char *object_name, + struct acpi_namespace_node *device_node, + acpi_integer * address); + +acpi_status +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpica_device_id *hid); + +acpi_status +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpi_compatible_id_list **return_cid_list); + +acpi_status +acpi_ut_execute_STA(struct acpi_namespace_node *device_node, + u32 * status_flags); + +acpi_status +acpi_ut_execute_UID(struct acpi_namespace_node *device_node, + struct acpica_device_id *uid); + +acpi_status +acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest); + +/* + * utobject - internal object create/delete/cache routines + */ +union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char + *module_name, + u32 line_number, + u32 component_id, + acpi_object_type + type); + +void *acpi_ut_allocate_object_desc_dbg(const char *module_name, + u32 line_number, u32 component_id); + +#define acpi_ut_create_internal_object(t) acpi_ut_create_internal_object_dbg (_acpi_module_name,__LINE__,_COMPONENT,t) +#define acpi_ut_allocate_object_desc() acpi_ut_allocate_object_desc_dbg (_acpi_module_name,__LINE__,_COMPONENT) + +void acpi_ut_delete_object_desc(union acpi_operand_object *object); + +u8 acpi_ut_valid_internal_object(void *object); + +union acpi_operand_object *acpi_ut_create_package_object(u32 count); + +union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size); + +union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size); + +acpi_status +acpi_ut_get_object_size(union acpi_operand_object *obj, acpi_size * obj_length); + +/* + * utstate - Generic state creation/cache routines + */ +void +acpi_ut_push_generic_state(union acpi_generic_state **list_head, + union acpi_generic_state *state); + +union acpi_generic_state *acpi_ut_pop_generic_state(union acpi_generic_state + **list_head); + +union acpi_generic_state *acpi_ut_create_generic_state(void); + +struct acpi_thread_state *acpi_ut_create_thread_state(void); + +union acpi_generic_state *acpi_ut_create_update_state(union acpi_operand_object + *object, u16 action); + +union acpi_generic_state *acpi_ut_create_pkg_state(void *internal_object, + void *external_object, + u16 index); + +acpi_status +acpi_ut_create_update_state_and_push(union acpi_operand_object *object, + u16 action, + union acpi_generic_state **state_list); + +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_ut_create_pkg_state_and_push(void *internal_object, + void *external_object, + u16 index, + union acpi_generic_state **state_list); +#endif /* ACPI_FUTURE_USAGE */ + +union acpi_generic_state *acpi_ut_create_control_state(void); + +void acpi_ut_delete_generic_state(union acpi_generic_state *state); + +/* + * utmath + */ +acpi_status +acpi_ut_divide(acpi_integer in_dividend, + acpi_integer in_divisor, + acpi_integer * out_quotient, acpi_integer * out_remainder); + +acpi_status +acpi_ut_short_divide(acpi_integer in_dividend, + u32 divisor, + acpi_integer * out_quotient, u32 * out_remainder); + +/* + * utmisc + */ +const char *acpi_ut_validate_exception(acpi_status status); + +u8 acpi_ut_is_aml_table(struct acpi_table_header *table); + +acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id); + +void acpi_ut_release_owner_id(acpi_owner_id * owner_id); + +acpi_status +acpi_ut_walk_package_tree(union acpi_operand_object *source_object, + void *target_object, + acpi_pkg_callback walk_callback, void *context); + +void acpi_ut_strupr(char *src_string); + +void acpi_ut_print_string(char *string, u8 max_length); + +u8 acpi_ut_valid_acpi_name(u32 name); + +acpi_name acpi_ut_repair_name(char *name); + +u8 acpi_ut_valid_acpi_char(char character, u32 position); + +acpi_status +acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer); + +/* Values for Base above (16=Hex, 10=Decimal) */ + +#define ACPI_ANY_BASE 0 + +u32 acpi_ut_dword_byte_swap(u32 value); + +void acpi_ut_set_integer_width(u8 revision); + +#ifdef ACPI_DEBUG_OUTPUT +void +acpi_ut_display_init_pathname(u8 type, + struct acpi_namespace_node *obj_handle, + char *path); +#endif + +/* + * utresrc + */ +acpi_status +acpi_ut_walk_aml_resources(u8 * aml, + acpi_size aml_length, + acpi_walk_aml_callback user_function, + void **context); + +acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index); + +u32 acpi_ut_get_descriptor_length(void *aml); + +u16 acpi_ut_get_resource_length(void *aml); + +u8 acpi_ut_get_resource_header_length(void *aml); + +u8 acpi_ut_get_resource_type(void *aml); + +acpi_status +acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, + u8 ** end_tag); + +/* + * utmutex - mutex support + */ +acpi_status acpi_ut_mutex_initialize(void); + +void acpi_ut_mutex_terminate(void); + +acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id); + +acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id); + +/* + * utalloc - memory allocation and object caching + */ +acpi_status acpi_ut_create_caches(void); + +acpi_status acpi_ut_delete_caches(void); + +acpi_status acpi_ut_validate_buffer(struct acpi_buffer *buffer); + +acpi_status +acpi_ut_initialize_buffer(struct acpi_buffer *buffer, + acpi_size required_length); + +void *acpi_ut_allocate(acpi_size size, + u32 component, const char *module, u32 line); + +void *acpi_ut_allocate_zeroed(acpi_size size, + u32 component, const char *module, u32 line); + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +void *acpi_ut_allocate_and_track(acpi_size size, + u32 component, const char *module, u32 line); + +void *acpi_ut_allocate_zeroed_and_track(acpi_size size, + u32 component, + const char *module, u32 line); + +void +acpi_ut_free_and_track(void *address, + u32 component, const char *module, u32 line); + +#ifdef ACPI_FUTURE_USAGE +void acpi_ut_dump_allocation_info(void); +#endif /* ACPI_FUTURE_USAGE */ + +void acpi_ut_dump_allocations(u32 component, const char *module); + +acpi_status +acpi_ut_create_list(char *list_name, + u16 object_size, struct acpi_memory_list **return_cache); + +#endif + +#endif /* _ACUTILS_H */ diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h new file mode 100644 index 000000000000..ff851c5df698 --- /dev/null +++ b/drivers/acpi/acpica/amlcode.h @@ -0,0 +1,494 @@ +/****************************************************************************** + * + * Name: amlcode.h - Definitions for AML, as included in "definition blocks" + * Declarations and definitions contained herein are derived + * directly from the ACPI specification. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#ifndef __AMLCODE_H__ +#define __AMLCODE_H__ + +/* primary opcodes */ + +#define AML_NULL_CHAR (u16) 0x00 + +#define AML_ZERO_OP (u16) 0x00 +#define AML_ONE_OP (u16) 0x01 +#define AML_UNASSIGNED (u16) 0x02 +#define AML_ALIAS_OP (u16) 0x06 +#define AML_NAME_OP (u16) 0x08 +#define AML_BYTE_OP (u16) 0x0a +#define AML_WORD_OP (u16) 0x0b +#define AML_DWORD_OP (u16) 0x0c +#define AML_STRING_OP (u16) 0x0d +#define AML_QWORD_OP (u16) 0x0e /* ACPI 2.0 */ +#define AML_SCOPE_OP (u16) 0x10 +#define AML_BUFFER_OP (u16) 0x11 +#define AML_PACKAGE_OP (u16) 0x12 +#define AML_VAR_PACKAGE_OP (u16) 0x13 /* ACPI 2.0 */ +#define AML_METHOD_OP (u16) 0x14 +#define AML_DUAL_NAME_PREFIX (u16) 0x2e +#define AML_MULTI_NAME_PREFIX_OP (u16) 0x2f +#define AML_NAME_CHAR_SUBSEQ (u16) 0x30 +#define AML_NAME_CHAR_FIRST (u16) 0x41 +#define AML_EXTENDED_OP_PREFIX (u16) 0x5b +#define AML_ROOT_PREFIX (u16) 0x5c +#define AML_PARENT_PREFIX (u16) 0x5e +#define AML_LOCAL_OP (u16) 0x60 +#define AML_LOCAL0 (u16) 0x60 +#define AML_LOCAL1 (u16) 0x61 +#define AML_LOCAL2 (u16) 0x62 +#define AML_LOCAL3 (u16) 0x63 +#define AML_LOCAL4 (u16) 0x64 +#define AML_LOCAL5 (u16) 0x65 +#define AML_LOCAL6 (u16) 0x66 +#define AML_LOCAL7 (u16) 0x67 +#define AML_ARG_OP (u16) 0x68 +#define AML_ARG0 (u16) 0x68 +#define AML_ARG1 (u16) 0x69 +#define AML_ARG2 (u16) 0x6a +#define AML_ARG3 (u16) 0x6b +#define AML_ARG4 (u16) 0x6c +#define AML_ARG5 (u16) 0x6d +#define AML_ARG6 (u16) 0x6e +#define AML_STORE_OP (u16) 0x70 +#define AML_REF_OF_OP (u16) 0x71 +#define AML_ADD_OP (u16) 0x72 +#define AML_CONCAT_OP (u16) 0x73 +#define AML_SUBTRACT_OP (u16) 0x74 +#define AML_INCREMENT_OP (u16) 0x75 +#define AML_DECREMENT_OP (u16) 0x76 +#define AML_MULTIPLY_OP (u16) 0x77 +#define AML_DIVIDE_OP (u16) 0x78 +#define AML_SHIFT_LEFT_OP (u16) 0x79 +#define AML_SHIFT_RIGHT_OP (u16) 0x7a +#define AML_BIT_AND_OP (u16) 0x7b +#define AML_BIT_NAND_OP (u16) 0x7c +#define AML_BIT_OR_OP (u16) 0x7d +#define AML_BIT_NOR_OP (u16) 0x7e +#define AML_BIT_XOR_OP (u16) 0x7f +#define AML_BIT_NOT_OP (u16) 0x80 +#define AML_FIND_SET_LEFT_BIT_OP (u16) 0x81 +#define AML_FIND_SET_RIGHT_BIT_OP (u16) 0x82 +#define AML_DEREF_OF_OP (u16) 0x83 +#define AML_CONCAT_RES_OP (u16) 0x84 /* ACPI 2.0 */ +#define AML_MOD_OP (u16) 0x85 /* ACPI 2.0 */ +#define AML_NOTIFY_OP (u16) 0x86 +#define AML_SIZE_OF_OP (u16) 0x87 +#define AML_INDEX_OP (u16) 0x88 +#define AML_MATCH_OP (u16) 0x89 +#define AML_CREATE_DWORD_FIELD_OP (u16) 0x8a +#define AML_CREATE_WORD_FIELD_OP (u16) 0x8b +#define AML_CREATE_BYTE_FIELD_OP (u16) 0x8c +#define AML_CREATE_BIT_FIELD_OP (u16) 0x8d +#define AML_TYPE_OP (u16) 0x8e +#define AML_CREATE_QWORD_FIELD_OP (u16) 0x8f /* ACPI 2.0 */ +#define AML_LAND_OP (u16) 0x90 +#define AML_LOR_OP (u16) 0x91 +#define AML_LNOT_OP (u16) 0x92 +#define AML_LEQUAL_OP (u16) 0x93 +#define AML_LGREATER_OP (u16) 0x94 +#define AML_LLESS_OP (u16) 0x95 +#define AML_TO_BUFFER_OP (u16) 0x96 /* ACPI 2.0 */ +#define AML_TO_DECSTRING_OP (u16) 0x97 /* ACPI 2.0 */ +#define AML_TO_HEXSTRING_OP (u16) 0x98 /* ACPI 2.0 */ +#define AML_TO_INTEGER_OP (u16) 0x99 /* ACPI 2.0 */ +#define AML_TO_STRING_OP (u16) 0x9c /* ACPI 2.0 */ +#define AML_COPY_OP (u16) 0x9d /* ACPI 2.0 */ +#define AML_MID_OP (u16) 0x9e /* ACPI 2.0 */ +#define AML_CONTINUE_OP (u16) 0x9f /* ACPI 2.0 */ +#define AML_IF_OP (u16) 0xa0 +#define AML_ELSE_OP (u16) 0xa1 +#define AML_WHILE_OP (u16) 0xa2 +#define AML_NOOP_OP (u16) 0xa3 +#define AML_RETURN_OP (u16) 0xa4 +#define AML_BREAK_OP (u16) 0xa5 +#define AML_BREAK_POINT_OP (u16) 0xcc +#define AML_ONES_OP (u16) 0xff + +/* prefixed opcodes */ + +#define AML_EXTENDED_OPCODE (u16) 0x5b00 /* prefix for 2-byte opcodes */ + +#define AML_MUTEX_OP (u16) 0x5b01 +#define AML_EVENT_OP (u16) 0x5b02 +#define AML_SHIFT_RIGHT_BIT_OP (u16) 0x5b10 +#define AML_SHIFT_LEFT_BIT_OP (u16) 0x5b11 +#define AML_COND_REF_OF_OP (u16) 0x5b12 +#define AML_CREATE_FIELD_OP (u16) 0x5b13 +#define AML_LOAD_TABLE_OP (u16) 0x5b1f /* ACPI 2.0 */ +#define AML_LOAD_OP (u16) 0x5b20 +#define AML_STALL_OP (u16) 0x5b21 +#define AML_SLEEP_OP (u16) 0x5b22 +#define AML_ACQUIRE_OP (u16) 0x5b23 +#define AML_SIGNAL_OP (u16) 0x5b24 +#define AML_WAIT_OP (u16) 0x5b25 +#define AML_RESET_OP (u16) 0x5b26 +#define AML_RELEASE_OP (u16) 0x5b27 +#define AML_FROM_BCD_OP (u16) 0x5b28 +#define AML_TO_BCD_OP (u16) 0x5b29 +#define AML_UNLOAD_OP (u16) 0x5b2a +#define AML_REVISION_OP (u16) 0x5b30 +#define AML_DEBUG_OP (u16) 0x5b31 +#define AML_FATAL_OP (u16) 0x5b32 +#define AML_TIMER_OP (u16) 0x5b33 /* ACPI 3.0 */ +#define AML_REGION_OP (u16) 0x5b80 +#define AML_FIELD_OP (u16) 0x5b81 +#define AML_DEVICE_OP (u16) 0x5b82 +#define AML_PROCESSOR_OP (u16) 0x5b83 +#define AML_POWER_RES_OP (u16) 0x5b84 +#define AML_THERMAL_ZONE_OP (u16) 0x5b85 +#define AML_INDEX_FIELD_OP (u16) 0x5b86 +#define AML_BANK_FIELD_OP (u16) 0x5b87 +#define AML_DATA_REGION_OP (u16) 0x5b88 /* ACPI 2.0 */ + +/* + * Combination opcodes (actually two one-byte opcodes) + * Used by the disassembler and i_aSL compiler + */ +#define AML_LGREATEREQUAL_OP (u16) 0x9295 +#define AML_LLESSEQUAL_OP (u16) 0x9294 +#define AML_LNOTEQUAL_OP (u16) 0x9293 + +/* + * Internal opcodes + * Use only "Unknown" AML opcodes, don't attempt to use + * any valid ACPI ASCII values (A-Z, 0-9, '-') + */ +#define AML_INT_NAMEPATH_OP (u16) 0x002d +#define AML_INT_NAMEDFIELD_OP (u16) 0x0030 +#define AML_INT_RESERVEDFIELD_OP (u16) 0x0031 +#define AML_INT_ACCESSFIELD_OP (u16) 0x0032 +#define AML_INT_BYTELIST_OP (u16) 0x0033 +#define AML_INT_STATICSTRING_OP (u16) 0x0034 +#define AML_INT_METHODCALL_OP (u16) 0x0035 +#define AML_INT_RETURN_VALUE_OP (u16) 0x0036 +#define AML_INT_EVAL_SUBTREE_OP (u16) 0x0037 + +#define ARG_NONE 0x0 + +/* + * Argument types for the AML Parser + * Each field in the arg_types u32 is 5 bits, allowing for a maximum of 6 arguments. + * There can be up to 31 unique argument types + * Zero is reserved as end-of-list indicator + */ +#define ARGP_BYTEDATA 0x01 +#define ARGP_BYTELIST 0x02 +#define ARGP_CHARLIST 0x03 +#define ARGP_DATAOBJ 0x04 +#define ARGP_DATAOBJLIST 0x05 +#define ARGP_DWORDDATA 0x06 +#define ARGP_FIELDLIST 0x07 +#define ARGP_NAME 0x08 +#define ARGP_NAMESTRING 0x09 +#define ARGP_OBJLIST 0x0A +#define ARGP_PKGLENGTH 0x0B +#define ARGP_SUPERNAME 0x0C +#define ARGP_TARGET 0x0D +#define ARGP_TERMARG 0x0E +#define ARGP_TERMLIST 0x0F +#define ARGP_WORDDATA 0x10 +#define ARGP_QWORDDATA 0x11 +#define ARGP_SIMPLENAME 0x12 + +/* + * Resolved argument types for the AML Interpreter + * Each field in the arg_types u32 is 5 bits, allowing for a maximum of 6 arguments. + * There can be up to 31 unique argument types (0 is end-of-arg-list indicator) + * + * Note1: These values are completely independent from the ACPI_TYPEs + * i.e., ARGI_INTEGER != ACPI_TYPE_INTEGER + * + * Note2: If and when 5 bits becomes insufficient, it would probably be best + * to convert to a 6-byte array of argument types, allowing 8 bits per argument. + */ + +/* Single, simple types */ + +#define ARGI_ANYTYPE 0x01 /* Don't care */ +#define ARGI_PACKAGE 0x02 +#define ARGI_EVENT 0x03 +#define ARGI_MUTEX 0x04 +#define ARGI_DDBHANDLE 0x05 + +/* Interchangeable types (via implicit conversion) */ + +#define ARGI_INTEGER 0x06 +#define ARGI_STRING 0x07 +#define ARGI_BUFFER 0x08 +#define ARGI_BUFFER_OR_STRING 0x09 /* Used by MID op only */ +#define ARGI_COMPUTEDATA 0x0A /* Buffer, String, or Integer */ + +/* Reference objects */ + +#define ARGI_INTEGER_REF 0x0B +#define ARGI_OBJECT_REF 0x0C +#define ARGI_DEVICE_REF 0x0D +#define ARGI_REFERENCE 0x0E +#define ARGI_TARGETREF 0x0F /* Target, subject to implicit conversion */ +#define ARGI_FIXED_TARGET 0x10 /* Target, no implicit conversion */ +#define ARGI_SIMPLE_TARGET 0x11 /* Name, Local, Arg -- no implicit conversion */ + +/* Multiple/complex types */ + +#define ARGI_DATAOBJECT 0x12 /* Buffer, String, package or reference to a Node - Used only by size_of operator */ +#define ARGI_COMPLEXOBJ 0x13 /* Buffer, String, or package (Used by INDEX op only) */ +#define ARGI_REF_OR_STRING 0x14 /* Reference or String (Used by DEREFOF op only) */ +#define ARGI_REGION_OR_BUFFER 0x15 /* Used by LOAD op only */ +#define ARGI_DATAREFOBJ 0x16 + +/* Note: types above can expand to 0x1F maximum */ + +#define ARGI_INVALID_OPCODE 0xFFFFFFFF + +/* + * hash offsets + */ +#define AML_EXTOP_HASH_OFFSET 22 +#define AML_LNOT_HASH_OFFSET 19 + +/* + * opcode groups and types + */ +#define OPGRP_NAMED 0x01 +#define OPGRP_FIELD 0x02 +#define OPGRP_BYTELIST 0x04 + +/* + * Opcode information + */ + +/* Opcode flags */ + +#define AML_LOGICAL 0x0001 +#define AML_LOGICAL_NUMERIC 0x0002 +#define AML_MATH 0x0004 +#define AML_CREATE 0x0008 +#define AML_FIELD 0x0010 +#define AML_DEFER 0x0020 +#define AML_NAMED 0x0040 +#define AML_NSNODE 0x0080 +#define AML_NSOPCODE 0x0100 +#define AML_NSOBJECT 0x0200 +#define AML_HAS_RETVAL 0x0400 +#define AML_HAS_TARGET 0x0800 +#define AML_HAS_ARGS 0x1000 +#define AML_CONSTANT 0x2000 +#define AML_NO_OPERAND_RESOLVE 0x4000 + +/* Convenient flag groupings */ + +#define AML_FLAGS_EXEC_0A_0T_1R AML_HAS_RETVAL +#define AML_FLAGS_EXEC_1A_0T_0R AML_HAS_ARGS /* Monadic1 */ +#define AML_FLAGS_EXEC_1A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL /* Monadic2 */ +#define AML_FLAGS_EXEC_1A_1T_0R AML_HAS_ARGS | AML_HAS_TARGET +#define AML_FLAGS_EXEC_1A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL /* monadic2_r */ +#define AML_FLAGS_EXEC_2A_0T_0R AML_HAS_ARGS /* Dyadic1 */ +#define AML_FLAGS_EXEC_2A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL /* Dyadic2 */ +#define AML_FLAGS_EXEC_2A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL /* dyadic2_r */ +#define AML_FLAGS_EXEC_2A_2T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL +#define AML_FLAGS_EXEC_3A_0T_0R AML_HAS_ARGS +#define AML_FLAGS_EXEC_3A_1T_1R AML_HAS_ARGS | AML_HAS_TARGET | AML_HAS_RETVAL +#define AML_FLAGS_EXEC_6A_0T_1R AML_HAS_ARGS | AML_HAS_RETVAL + +/* + * The opcode Type is used in a dispatch table, do not change + * without updating the table. + */ +#define AML_TYPE_EXEC_0A_0T_1R 0x00 +#define AML_TYPE_EXEC_1A_0T_0R 0x01 /* Monadic1 */ +#define AML_TYPE_EXEC_1A_0T_1R 0x02 /* Monadic2 */ +#define AML_TYPE_EXEC_1A_1T_0R 0x03 +#define AML_TYPE_EXEC_1A_1T_1R 0x04 /* monadic2_r */ +#define AML_TYPE_EXEC_2A_0T_0R 0x05 /* Dyadic1 */ +#define AML_TYPE_EXEC_2A_0T_1R 0x06 /* Dyadic2 */ +#define AML_TYPE_EXEC_2A_1T_1R 0x07 /* dyadic2_r */ +#define AML_TYPE_EXEC_2A_2T_1R 0x08 +#define AML_TYPE_EXEC_3A_0T_0R 0x09 +#define AML_TYPE_EXEC_3A_1T_1R 0x0A +#define AML_TYPE_EXEC_6A_0T_1R 0x0B +/* End of types used in dispatch table */ + +#define AML_TYPE_LITERAL 0x0B +#define AML_TYPE_CONSTANT 0x0C +#define AML_TYPE_METHOD_ARGUMENT 0x0D +#define AML_TYPE_LOCAL_VARIABLE 0x0E +#define AML_TYPE_DATA_TERM 0x0F + +/* Generic for an op that returns a value */ + +#define AML_TYPE_METHOD_CALL 0x10 + +/* Misc */ + +#define AML_TYPE_CREATE_FIELD 0x11 +#define AML_TYPE_CREATE_OBJECT 0x12 +#define AML_TYPE_CONTROL 0x13 +#define AML_TYPE_NAMED_NO_OBJ 0x14 +#define AML_TYPE_NAMED_FIELD 0x15 +#define AML_TYPE_NAMED_SIMPLE 0x16 +#define AML_TYPE_NAMED_COMPLEX 0x17 +#define AML_TYPE_RETURN 0x18 + +#define AML_TYPE_UNDEFINED 0x19 +#define AML_TYPE_BOGUS 0x1A + +/* AML Package Length encodings */ + +#define ACPI_AML_PACKAGE_TYPE1 0x40 +#define ACPI_AML_PACKAGE_TYPE2 0x4000 +#define ACPI_AML_PACKAGE_TYPE3 0x400000 +#define ACPI_AML_PACKAGE_TYPE4 0x40000000 + +/* + * Opcode classes + */ +#define AML_CLASS_EXECUTE 0x00 +#define AML_CLASS_CREATE 0x01 +#define AML_CLASS_ARGUMENT 0x02 +#define AML_CLASS_NAMED_OBJECT 0x03 +#define AML_CLASS_CONTROL 0x04 +#define AML_CLASS_ASCII 0x05 +#define AML_CLASS_PREFIX 0x06 +#define AML_CLASS_INTERNAL 0x07 +#define AML_CLASS_RETURN_VALUE 0x08 +#define AML_CLASS_METHOD_CALL 0x09 +#define AML_CLASS_UNKNOWN 0x0A + +/* Predefined Operation Region space_iDs */ + +typedef enum { + REGION_MEMORY = 0, + REGION_IO, + REGION_PCI_CONFIG, + REGION_EC, + REGION_SMBUS, + REGION_CMOS, + REGION_PCI_BAR, + REGION_DATA_TABLE, /* Internal use only */ + REGION_FIXED_HW = 0x7F +} AML_REGION_TYPES; + +/* Comparison operation codes for match_op operator */ + +typedef enum { + MATCH_MTR = 0, + MATCH_MEQ = 1, + MATCH_MLE = 2, + MATCH_MLT = 3, + MATCH_MGE = 4, + MATCH_MGT = 5 +} AML_MATCH_OPERATOR; + +#define MAX_MATCH_OPERATOR 5 + +/* + * field_flags + * + * This byte is extracted from the AML and includes three separate + * pieces of information about the field: + * 1) The field access type + * 2) The field update rule + * 3) The lock rule for the field + * + * Bits 00 - 03 : access_type (any_acc, byte_acc, etc.) + * 04 : lock_rule (1 == Lock) + * 05 - 06 : update_rule + */ +#define AML_FIELD_ACCESS_TYPE_MASK 0x0F +#define AML_FIELD_LOCK_RULE_MASK 0x10 +#define AML_FIELD_UPDATE_RULE_MASK 0x60 + +/* 1) Field Access Types */ + +typedef enum { + AML_FIELD_ACCESS_ANY = 0x00, + AML_FIELD_ACCESS_BYTE = 0x01, + AML_FIELD_ACCESS_WORD = 0x02, + AML_FIELD_ACCESS_DWORD = 0x03, + AML_FIELD_ACCESS_QWORD = 0x04, /* ACPI 2.0 */ + AML_FIELD_ACCESS_BUFFER = 0x05 /* ACPI 2.0 */ +} AML_ACCESS_TYPE; + +/* 2) Field Lock Rules */ + +typedef enum { + AML_FIELD_LOCK_NEVER = 0x00, + AML_FIELD_LOCK_ALWAYS = 0x10 +} AML_LOCK_RULE; + +/* 3) Field Update Rules */ + +typedef enum { + AML_FIELD_UPDATE_PRESERVE = 0x00, + AML_FIELD_UPDATE_WRITE_AS_ONES = 0x20, + AML_FIELD_UPDATE_WRITE_AS_ZEROS = 0x40 +} AML_UPDATE_RULE; + +/* + * Field Access Attributes. + * This byte is extracted from the AML via the + * access_as keyword + */ +typedef enum { + AML_FIELD_ATTRIB_SMB_QUICK = 0x02, + AML_FIELD_ATTRIB_SMB_SEND_RCV = 0x04, + AML_FIELD_ATTRIB_SMB_BYTE = 0x06, + AML_FIELD_ATTRIB_SMB_WORD = 0x08, + AML_FIELD_ATTRIB_SMB_BLOCK = 0x0A, + AML_FIELD_ATTRIB_SMB_WORD_CALL = 0x0C, + AML_FIELD_ATTRIB_SMB_BLOCK_CALL = 0x0D +} AML_ACCESS_ATTRIBUTE; + +/* Bit fields in method_flags byte */ + +#define AML_METHOD_ARG_COUNT 0x07 +#define AML_METHOD_SERIALIZED 0x08 +#define AML_METHOD_SYNCH_LEVEL 0xF0 + +/* METHOD_FLAGS_ARG_COUNT is not used internally, define additional flags */ + +#define AML_METHOD_INTERNAL_ONLY 0x01 +#define AML_METHOD_RESERVED1 0x02 +#define AML_METHOD_RESERVED2 0x04 + +#endif /* __AMLCODE_H__ */ diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h new file mode 100644 index 000000000000..7b070e42b7c5 --- /dev/null +++ b/drivers/acpi/acpica/amlresrc.h @@ -0,0 +1,311 @@ + +/****************************************************************************** + * + * Module Name: amlresrc.h - AML resource descriptors + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +/* acpisrc:struct_defs -- for acpisrc conversion */ + +#ifndef __AMLRESRC_H +#define __AMLRESRC_H + +/* + * Resource descriptor tags, as defined in the ACPI specification. + * Used to symbolically reference fields within a descriptor. + */ +#define ACPI_RESTAG_ADDRESS "_ADR" +#define ACPI_RESTAG_ALIGNMENT "_ALN" +#define ACPI_RESTAG_ADDRESSSPACE "_ASI" +#define ACPI_RESTAG_ACCESSSIZE "_ASZ" +#define ACPI_RESTAG_TYPESPECIFICATTRIBUTES "_ATT" +#define ACPI_RESTAG_BASEADDRESS "_BAS" +#define ACPI_RESTAG_BUSMASTER "_BM_" /* Master(1), Slave(0) */ +#define ACPI_RESTAG_DECODE "_DEC" +#define ACPI_RESTAG_DMA "_DMA" +#define ACPI_RESTAG_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */ +#define ACPI_RESTAG_GRANULARITY "_GRA" +#define ACPI_RESTAG_INTERRUPT "_INT" +#define ACPI_RESTAG_INTERRUPTLEVEL "_LL_" /* active_lo(1), active_hi(0) */ +#define ACPI_RESTAG_INTERRUPTSHARE "_SHR" /* Shareable(1), no_share(0) */ +#define ACPI_RESTAG_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */ +#define ACPI_RESTAG_LENGTH "_LEN" +#define ACPI_RESTAG_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */ +#define ACPI_RESTAG_MEMTYPE "_MEM" /* non_cache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */ +#define ACPI_RESTAG_MAXADDR "_MAX" +#define ACPI_RESTAG_MINADDR "_MIN" +#define ACPI_RESTAG_MAXTYPE "_MAF" +#define ACPI_RESTAG_MINTYPE "_MIF" +#define ACPI_RESTAG_REGISTERBITOFFSET "_RBO" +#define ACPI_RESTAG_REGISTERBITWIDTH "_RBW" +#define ACPI_RESTAG_RANGETYPE "_RNG" +#define ACPI_RESTAG_READWRITETYPE "_RW_" /* read_only(0), Writeable (1) */ +#define ACPI_RESTAG_TRANSLATION "_TRA" +#define ACPI_RESTAG_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */ +#define ACPI_RESTAG_TYPE "_TTP" /* Translation(1), Static (0) */ +#define ACPI_RESTAG_XFERTYPE "_SIZ" /* 8(0), 8_and16(1), 16(2) */ + +/* Default sizes for "small" resource descriptors */ + +#define ASL_RDESC_IRQ_SIZE 0x02 +#define ASL_RDESC_DMA_SIZE 0x02 +#define ASL_RDESC_ST_DEPEND_SIZE 0x00 +#define ASL_RDESC_END_DEPEND_SIZE 0x00 +#define ASL_RDESC_IO_SIZE 0x07 +#define ASL_RDESC_FIXED_IO_SIZE 0x03 +#define ASL_RDESC_END_TAG_SIZE 0x01 + +struct asl_resource_node { + u32 buffer_length; + void *buffer; + struct asl_resource_node *next; +}; + +/* Macros used to generate AML resource length fields */ + +#define ACPI_AML_SIZE_LARGE(r) (sizeof (r) - sizeof (struct aml_resource_large_header)) +#define ACPI_AML_SIZE_SMALL(r) (sizeof (r) - sizeof (struct aml_resource_small_header)) + +/* + * Resource descriptors defined in the ACPI specification. + * + * Packing/alignment must be BYTE because these descriptors + * are used to overlay the raw AML byte stream. + */ +#pragma pack(1) + +/* + * SMALL descriptors + */ +#define AML_RESOURCE_SMALL_HEADER_COMMON \ + u8 descriptor_type; + +struct aml_resource_small_header { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_irq { + AML_RESOURCE_SMALL_HEADER_COMMON u16 irq_mask; + u8 flags; +}; + +struct aml_resource_irq_noflags { + AML_RESOURCE_SMALL_HEADER_COMMON u16 irq_mask; +}; + +struct aml_resource_dma { + AML_RESOURCE_SMALL_HEADER_COMMON u8 dma_channel_mask; + u8 flags; +}; + +struct aml_resource_start_dependent { + AML_RESOURCE_SMALL_HEADER_COMMON u8 flags; +}; + +struct aml_resource_start_dependent_noprio { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_end_dependent { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_io { + AML_RESOURCE_SMALL_HEADER_COMMON u8 flags; + u16 minimum; + u16 maximum; + u8 alignment; + u8 address_length; +}; + +struct aml_resource_fixed_io { + AML_RESOURCE_SMALL_HEADER_COMMON u16 address; + u8 address_length; +}; + +struct aml_resource_vendor_small { +AML_RESOURCE_SMALL_HEADER_COMMON}; + +struct aml_resource_end_tag { + AML_RESOURCE_SMALL_HEADER_COMMON u8 checksum; +}; + +/* + * LARGE descriptors + */ +#define AML_RESOURCE_LARGE_HEADER_COMMON \ + u8 descriptor_type;\ + u16 resource_length; + +struct aml_resource_large_header { +AML_RESOURCE_LARGE_HEADER_COMMON}; + +struct aml_resource_memory24 { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u16 minimum; + u16 maximum; + u16 alignment; + u16 address_length; +}; + +struct aml_resource_vendor_large { +AML_RESOURCE_LARGE_HEADER_COMMON}; + +struct aml_resource_memory32 { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u32 minimum; + u32 maximum; + u32 alignment; + u32 address_length; +}; + +struct aml_resource_fixed_memory32 { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u32 address; + u32 address_length; +}; + +#define AML_RESOURCE_ADDRESS_COMMON \ + u8 resource_type; \ + u8 flags; \ + u8 specific_flags; + +struct aml_resource_address { +AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_ADDRESS_COMMON}; + +struct aml_resource_extended_address64 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u8 revision_iD; + u8 reserved; + u64 granularity; + u64 minimum; + u64 maximum; + u64 translation_offset; + u64 address_length; + u64 type_specific; +}; + +#define AML_RESOURCE_EXTENDED_ADDRESS_REVISION 1 /* ACPI 3.0 */ + +struct aml_resource_address64 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u64 granularity; + u64 minimum; + u64 maximum; + u64 translation_offset; + u64 address_length; +}; + +struct aml_resource_address32 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u32 granularity; + u32 minimum; + u32 maximum; + u32 translation_offset; + u32 address_length; +}; + +struct aml_resource_address16 { + AML_RESOURCE_LARGE_HEADER_COMMON + AML_RESOURCE_ADDRESS_COMMON u16 granularity; + u16 minimum; + u16 maximum; + u16 translation_offset; + u16 address_length; +}; + +struct aml_resource_extended_irq { + AML_RESOURCE_LARGE_HEADER_COMMON u8 flags; + u8 interrupt_count; + u32 interrupts[1]; + /* res_source_index, res_source optional fields follow */ +}; + +struct aml_resource_generic_register { + AML_RESOURCE_LARGE_HEADER_COMMON u8 address_space_id; + u8 bit_width; + u8 bit_offset; + u8 access_size; /* ACPI 3.0, was previously Reserved */ + u64 address; +}; + +/* restore default alignment */ + +#pragma pack() + +/* Union of all resource descriptors, so we can allocate the worst case */ + +union aml_resource { + /* Descriptor headers */ + + u8 descriptor_type; + struct aml_resource_small_header small_header; + struct aml_resource_large_header large_header; + + /* Small resource descriptors */ + + struct aml_resource_irq irq; + struct aml_resource_dma dma; + struct aml_resource_start_dependent start_dpf; + struct aml_resource_end_dependent end_dpf; + struct aml_resource_io io; + struct aml_resource_fixed_io fixed_io; + struct aml_resource_vendor_small vendor_small; + struct aml_resource_end_tag end_tag; + + /* Large resource descriptors */ + + struct aml_resource_memory24 memory24; + struct aml_resource_generic_register generic_reg; + struct aml_resource_vendor_large vendor_large; + struct aml_resource_memory32 memory32; + struct aml_resource_fixed_memory32 fixed_memory32; + struct aml_resource_address16 address16; + struct aml_resource_address32 address32; + struct aml_resource_address64 address64; + struct aml_resource_extended_address64 ext_address64; + struct aml_resource_extended_irq extended_irq; + + /* Utility overlays */ + + struct aml_resource_address address; + u32 dword_item; + u16 word_item; + u8 byte_item; +}; + +#endif diff --git a/drivers/acpi/dispatcher/dsfield.c b/drivers/acpi/acpica/dsfield.c index f988a5e7d2b4..53e27bc5a734 100644 --- a/drivers/acpi/dispatcher/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -42,11 +42,12 @@ */ #include <acpi/acpi.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/acparser.h> +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acparser.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsfield") diff --git a/drivers/acpi/dispatcher/dsinit.c b/drivers/acpi/acpica/dsinit.c index 949f7c75029e..eb144b13d8fa 100644 --- a/drivers/acpi/dispatcher/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acdispat.h> -#include <acpi/acnamesp.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "actables.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsinit") diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 279a5a60a0dd..14b8b8ed8023 100644 --- a/drivers/acpi/dispatcher/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -42,11 +42,14 @@ */ #include <acpi/acpi.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#ifdef ACPI_DISASSEMBLER #include <acpi/acdisasm.h> +#endif #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsmethod") @@ -412,6 +415,9 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread, if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { status = obj_desc->method.implementation(next_walk_state); + if (status == AE_OK) { + status = AE_CTRL_TERMINATE; + } } return_ACPI_STATUS(status); diff --git a/drivers/acpi/dispatcher/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index d03f81bd1bcb..da0f5468184c 100644 --- a/drivers/acpi/dispatcher/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acdispat.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsmthdat") diff --git a/drivers/acpi/dispatcher/dsobject.c b/drivers/acpi/acpica/dsobject.c index 4f08e599d07e..15c628e6aa00 100644 --- a/drivers/acpi/dispatcher/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -42,11 +42,12 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsobject") diff --git a/drivers/acpi/dispatcher/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index 69fae5905bb8..0c3b4dd60e8a 100644 --- a/drivers/acpi/dispatcher/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -43,13 +43,14 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acevents.h" +#include "actables.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsopcode") @@ -1140,10 +1141,29 @@ acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, op->common.aml_opcode, walk_state)); switch (op->common.aml_opcode) { - case AML_IF_OP: case AML_WHILE_OP: /* + * If this is an additional iteration of a while loop, continue. + * There is no need to allocate a new control state. + */ + if (walk_state->control_state) { + if (walk_state->control_state->control.aml_predicate_start + == (walk_state->parser_state.aml - 1)) { + + /* Reset the state to start-of-loop */ + + walk_state->control_state->common.state = + ACPI_CONTROL_CONDITIONAL_EXECUTING; + break; + } + } + + /*lint -fallthrough */ + + case AML_IF_OP: + + /* * IF/WHILE: Create a new control state to manage these * constructs. We need to manage these as a stack, in order * to handle nesting. @@ -1243,13 +1263,36 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", op)); - if (walk_state->control_state->common.value) { + control_state = walk_state->control_state; + if (control_state->common.value) { - /* Predicate was true, go back and evaluate it again! */ + /* Predicate was true, the body of the loop was just executed */ + /* + * This loop counter mechanism allows the interpreter to escape + * possibly infinite loops. This can occur in poorly written AML + * when the hardware does not respond within a while loop and the + * loop does not implement a timeout. + */ + control_state->control.loop_count++; + if (control_state->control.loop_count > + ACPI_MAX_LOOP_ITERATIONS) { + status = AE_AML_INFINITE_LOOP; + break; + } + + /* + * Go back and evaluate the predicate and maybe execute the loop + * another time + */ status = AE_CTRL_PENDING; + walk_state->aml_last_while = + control_state->control.aml_predicate_start; + break; } + /* Predicate was false, terminate this while loop */ + ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[WHILE_OP] termination! Op=%p\n", op)); @@ -1257,9 +1300,6 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, control_state = acpi_ut_pop_generic_state(&walk_state->control_state); - - walk_state->aml_last_while = - control_state->control.aml_predicate_start; acpi_ut_delete_generic_state(control_state); break; diff --git a/drivers/acpi/dispatcher/dsutils.c b/drivers/acpi/acpica/dsutils.c index b398982f0d8b..dabc23a46176 100644 --- a/drivers/acpi/dispatcher/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -42,12 +42,13 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/acdebug.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acdebug.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dsutils") diff --git a/drivers/acpi/dispatcher/dswexec.c b/drivers/acpi/acpica/dswexec.c index 396fe12078cd..350e6656bc89 100644 --- a/drivers/acpi/dispatcher/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -43,12 +43,13 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/acdebug.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acdebug.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dswexec") diff --git a/drivers/acpi/dispatcher/dswload.c b/drivers/acpi/acpica/dswload.c index dff7a3e445a8..3023ceaa8d54 100644 --- a/drivers/acpi/dispatcher/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -42,12 +42,13 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acevents.h" #ifdef ACPI_ASL_COMPILER #include <acpi/acdisasm.h> diff --git a/drivers/acpi/dispatcher/dswscope.c b/drivers/acpi/acpica/dswscope.c index 9e6073265873..908645e72f03 100644 --- a/drivers/acpi/dispatcher/dswscope.c +++ b/drivers/acpi/acpica/dswscope.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acdispat.h> +#include "accommon.h" +#include "acdispat.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dswscope") diff --git a/drivers/acpi/dispatcher/dswstate.c b/drivers/acpi/acpica/dswstate.c index b00d4af791aa..40f92bf7dce5 100644 --- a/drivers/acpi/dispatcher/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acdispat.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acnamesp.h" #define _COMPONENT ACPI_DISPATCHER ACPI_MODULE_NAME("dswstate") diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/acpica/evevent.c index c56c5c6ea77b..803edd9e3f6a 100644 --- a/drivers/acpi/events/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acevents.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evevent") @@ -72,8 +73,8 @@ acpi_status acpi_ev_initialize_events(void) /* * Initialize the Fixed and General Purpose Events. This is done prior to - * enabling SCIs to prevent interrupts from occurring before the handlers are - * installed. + * enabling SCIs to prevent interrupts from occurring before the handlers + * are installed. */ status = acpi_ev_fixed_event_initialize(); if (ACPI_FAILURE(status)) { @@ -192,8 +193,8 @@ static acpi_status acpi_ev_fixed_event_initialize(void) acpi_status status; /* - * Initialize the structure that keeps track of fixed event handlers - * and enable the fixed events. + * Initialize the structure that keeps track of fixed event handlers and + * enable the fixed events. */ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { acpi_gbl_fixed_event_handlers[i].handler = NULL; @@ -237,7 +238,7 @@ u32 acpi_ev_fixed_event_detect(void) /* * Read the fixed feature status and enable registers, as all the cases - * depend on their values. Ignore errors here. + * depend on their values. Ignore errors here. */ (void)acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &fixed_status); (void)acpi_hw_register_read(ACPI_REGISTER_PM1_ENABLE, &fixed_enable); @@ -291,8 +292,8 @@ static u32 acpi_ev_fixed_event_dispatch(u32 event) status_register_id, 1); /* - * Make sure we've got a handler. If not, report an error. - * The event is disabled to prevent further interrupts. + * Make sure we've got a handler. If not, report an error. The event is + * disabled to prevent further interrupts. */ if (NULL == acpi_gbl_fixed_event_handlers[event].handler) { (void)acpi_set_register(acpi_gbl_fixed_event_info[event]. diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/acpica/evgpe.c index f45c74fe745e..f345ced36477 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evgpe") @@ -125,7 +126,7 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info, (1 << (gpe_event_info->gpe_number - gpe_register_info->base_gpe_number)); - /* 1) Disable case. Simply clear all enable bits */ + /* 1) Disable case. Simply clear all enable bits */ if (type == ACPI_GPE_DISABLE) { ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, @@ -134,7 +135,7 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info, return_ACPI_STATUS(AE_OK); } - /* 2) Enable case. Set/Clear the appropriate enable bits */ + /* 2) Enable case. Set/Clear the appropriate enable bits */ switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { case ACPI_GPE_TYPE_WAKE: @@ -295,7 +296,7 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) * * FUNCTION: acpi_ev_get_gpe_event_info * - * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 + * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 * gpe_number - Raw GPE number * * RETURN: A GPE event_info struct. NULL if not a valid GPE @@ -372,7 +373,7 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, * * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED * - * DESCRIPTION: Detect if any GP events have occurred. This function is + * DESCRIPTION: Detect if any GP events have occurred. This function is * executed at interrupt level. * ******************************************************************************/ @@ -400,8 +401,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) /* * We need to obtain the GPE lock for both the data structs and registers - * Note: Not necessary to obtain the hardware lock, since the GPE registers - * are owned by the gpe_lock. + * Note: Not necessary to obtain the hardware lock, since the GPE + * registers are owned by the gpe_lock. */ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); @@ -410,9 +411,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) gpe_block = gpe_xrupt_list->gpe_block_list_head; while (gpe_block) { /* - * Read all of the 8-bit GPE status and enable registers - * in this GPE block, saving all of them. - * Find all currently active GP events. + * Read all of the 8-bit GPE status and enable registers in this GPE + * block, saving all of them. Find all currently active GP events. */ for (i = 0; i < gpe_block->register_count; i++) { @@ -423,10 +423,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) /* Read the Status Register */ status = - acpi_hw_low_level_read(ACPI_GPE_REGISTER_WIDTH, - &status_reg, - &gpe_register_info-> - status_address); + acpi_read(&status_reg, + &gpe_register_info->status_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } @@ -434,10 +432,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) /* Read the Enable Register */ status = - acpi_hw_low_level_read(ACPI_GPE_REGISTER_WIDTH, - &enable_reg, - &gpe_register_info-> - enable_address); + acpi_read(&enable_reg, + &gpe_register_info->enable_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } @@ -527,8 +523,8 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) (void)acpi_ev_enable_gpe(gpe_event_info, FALSE); /* - * Take a snapshot of the GPE info for this level - we copy the - * info to prevent a race condition with remove_handler/remove_block. + * Take a snapshot of the GPE info for this level - we copy the info to + * prevent a race condition with remove_handler/remove_block. */ ACPI_MEMCPY(&local_gpe_event_info, gpe_event_info, sizeof(struct acpi_gpe_event_info)); @@ -539,8 +535,8 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) } /* - * Must check for control method type dispatch one more - * time to avoid race with ev_gpe_install_handler + * Must check for control method type dispatch one more time to avoid a + * race with ev_gpe_install_handler */ if ((local_gpe_event_info.flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) { @@ -584,8 +580,8 @@ static void acpi_ev_asynch_enable_gpe(void *context) if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_LEVEL_TRIGGERED) { /* - * GPE is level-triggered, we clear the GPE status bit after - * handling the event. + * GPE is level-triggered, we clear the GPE status bit after handling + * the event. */ status = acpi_hw_clear_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { @@ -624,7 +620,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) acpi_os_gpe_count(gpe_number); /* - * If edge-triggered, clear the GPE status bit now. Note that + * If edge-triggered, clear the GPE status bit now. Note that * level-triggered events are cleared after the GPE is serviced. */ if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == @@ -650,7 +646,8 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) /* * Invoke the installed handler (at interrupt level) - * Ignore return status for now. TBD: leave GPE disabled on error? + * Ignore return status for now. + * TBD: leave GPE disabled on error? */ (void)gpe_event_info->dispatch.handler->address(gpe_event_info-> dispatch. @@ -708,7 +705,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) gpe_number)); /* - * Disable the GPE. The GPE will remain disabled until the ACPI + * Disable the GPE. The GPE will remain disabled until the ACPICA * Core Subsystem is restarted, or a handler is installed. */ status = acpi_ev_disable_gpe(gpe_event_info); diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 73c058e2f5c2..484cc0565d5b 100644 --- a/drivers/acpi/events/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evgpeblk") @@ -124,6 +125,7 @@ u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) * FUNCTION: acpi_ev_walk_gpe_list * * PARAMETERS: gpe_walk_callback - Routine called for each GPE block + * Context - Value passed to callback * * RETURN: Status * @@ -131,7 +133,8 @@ u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) * ******************************************************************************/ -acpi_status acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback) +acpi_status +acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) { struct acpi_gpe_block_info *gpe_block; struct acpi_gpe_xrupt_info *gpe_xrupt_info; @@ -154,8 +157,13 @@ acpi_status acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback) /* One callback per GPE block */ - status = gpe_walk_callback(gpe_xrupt_info, gpe_block); + status = + gpe_walk_callback(gpe_xrupt_info, gpe_block, + context); if (ACPI_FAILURE(status)) { + if (status == AE_CTRL_END) { /* Callback abort */ + status = AE_OK; + } goto unlock_and_exit; } @@ -186,7 +194,8 @@ acpi_status acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback) acpi_status acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block) + struct acpi_gpe_block_info *gpe_block, + void *context) { struct acpi_gpe_event_info *gpe_event_info; u32 i; @@ -309,17 +318,17 @@ acpi_ev_save_method_info(acpi_handle obj_handle, (gpe_block->block_base_number + (gpe_block->register_count * 8)))) { /* - * Not valid for this GPE block, just ignore it - * However, it may be valid for a different GPE block, since GPE0 and GPE1 - * methods both appear under \_GPE. + * Not valid for this GPE block, just ignore it. However, it may be + * valid for a different GPE block, since GPE0 and GPE1 methods both + * appear under \_GPE. */ return_ACPI_STATUS(AE_OK); } /* - * Now we can add this information to the gpe_event_info block - * for use during dispatch of this GPE. Default type is RUNTIME, although - * this may change when the _PRW methods are executed later. + * Now we can add this information to the gpe_event_info block for use + * during dispatch of this GPE. Default type is RUNTIME, although this may + * change when the _PRW methods are executed later. */ gpe_event_info = &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; @@ -394,8 +403,8 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, gpe_block = gpe_info->gpe_block; /* - * The _PRW object must return a package, we are only interested - * in the first element + * The _PRW object must return a package, we are only interested in the + * first element */ obj_desc = pkg_desc->package.elements[0]; @@ -434,7 +443,7 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, /* * Is this GPE within this block? * - * TRUE iff these conditions are true: + * TRUE if and only if these conditions are true: * 1) The GPE devices match. * 2) The GPE index(number) is within the range of the Gpe Block * associated with the GPE device. @@ -457,6 +466,7 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, if (ACPI_FAILURE(status)) { goto cleanup; } + status = acpi_ev_update_gpe_enable_masks(gpe_event_info, ACPI_GPE_DISABLE); @@ -476,9 +486,9 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle, * RETURN: A GPE interrupt block * * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt - * block per unique interrupt level used for GPEs. - * Should be called only when the GPE lists are semaphore locked - * and not subject to change. + * block per unique interrupt level used for GPEs. Should be + * called only when the GPE lists are semaphore locked and not + * subject to change. * ******************************************************************************/ @@ -608,8 +618,9 @@ acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) * * FUNCTION: acpi_ev_install_gpe_block * - * PARAMETERS: gpe_block - New GPE block - * interrupt_number - Xrupt to be associated with this GPE block + * PARAMETERS: gpe_block - New GPE block + * interrupt_number - Xrupt to be associated with this + * GPE block * * RETURN: Status * @@ -666,7 +677,7 @@ acpi_ev_install_gpe_block(struct acpi_gpe_block_info *gpe_block, * * FUNCTION: acpi_ev_delete_gpe_block * - * PARAMETERS: gpe_block - Existing GPE block + * PARAMETERS: gpe_block - Existing GPE block * * RETURN: Status * @@ -688,7 +699,8 @@ acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block) /* Disable all GPEs in this block */ - status = acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block); + status = + acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block, NULL); if (!gpe_block->previous && !gpe_block->next) { @@ -715,6 +727,9 @@ acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block) acpi_os_release_lock(acpi_gbl_gpe_lock, flags); } + acpi_current_gpe_count -= + gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH; + /* Free the gpe_block */ ACPI_FREE(gpe_block->register_info); @@ -786,9 +801,9 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block) /* * Initialize the GPE Register and Event structures. A goal of these - * tables is to hide the fact that there are two separate GPE register sets - * in a given GPE hardware block, the status registers occupy the first half, - * and the enable registers occupy the second half. + * tables is to hide the fact that there are two separate GPE register + * sets in a given GPE hardware block, the status registers occupy the + * first half, and the enable registers occupy the second half. */ this_register = gpe_register_info; this_event = gpe_event_info; @@ -816,10 +831,8 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block) ACPI_GPE_REGISTER_WIDTH; this_register->enable_address.bit_width = ACPI_GPE_REGISTER_WIDTH; - this_register->status_address.bit_offset = - ACPI_GPE_REGISTER_WIDTH; - this_register->enable_address.bit_offset = - ACPI_GPE_REGISTER_WIDTH; + this_register->status_address.bit_offset = 0; + this_register->enable_address.bit_offset = 0; /* Init the event_info for each GPE within this register */ @@ -832,18 +845,14 @@ acpi_ev_create_gpe_info_blocks(struct acpi_gpe_block_info *gpe_block) /* Disable all GPEs within this register */ - status = acpi_hw_low_level_write(ACPI_GPE_REGISTER_WIDTH, 0x00, - &this_register-> - enable_address); + status = acpi_write(0x00, &this_register->enable_address); if (ACPI_FAILURE(status)) { goto error_exit; } /* Clear any pending GPE events within this register */ - status = acpi_hw_low_level_write(ACPI_GPE_REGISTER_WIDTH, 0xFF, - &this_register-> - status_address); + status = acpi_write(0xFF, &this_register->status_address); if (ACPI_FAILURE(status)) { goto error_exit; } @@ -956,6 +965,9 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, gpe_device->name.ascii, gpe_block->register_count, interrupt_number)); + /* Update global count of currently available GPEs */ + + acpi_current_gpe_count += register_count * ACPI_GPE_REGISTER_WIDTH; return_ACPI_STATUS(AE_OK); } @@ -1055,7 +1067,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device, /* Enable all valid runtime GPEs found above */ - status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block); + status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block, NULL); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Could not enable GPEs in GpeBlock %p", gpe_block)); diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/acpica/evmisc.c index 1d5670be729a..5f893057bcc6 100644 --- a/drivers/acpi/events/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -42,18 +42,15 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evmisc") -/* Pointer to FACS needed for the Global Lock */ -static struct acpi_table_facs *facs = NULL; - /* Local prototypes */ - static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context); static u32 acpi_ev_global_lock_handler(void *context); @@ -152,7 +149,9 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, break; default: + /* All other types are not supported */ + return (AE_TYPE); } } @@ -193,9 +192,8 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, acpi_ut_delete_generic_state(notify_info); } } else { - /* - * There is no notify handler (per-device or system) for this device. - */ + /* There is no notify handler (per-device or system) for this device */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No notify handler for Notify (%4.4s, %X) node %p\n", acpi_ut_get_node_name(node), notify_value, @@ -229,9 +227,8 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) ACPI_FUNCTION_ENTRY(); /* - * We will invoke a global notify handler if installed. - * This is done _before_ we invoke the per-device handler attached - * to the device. + * We will invoke a global notify handler if installed. This is done + * _before_ we invoke the per-device handler attached to the device. */ if (notify_info->notify.value <= ACPI_MAX_SYS_NOTIFY) { @@ -299,7 +296,7 @@ static u32 acpi_ev_global_lock_handler(void *context) * If we don't get it now, it will be marked pending and we will * take another interrupt when it becomes free. */ - ACPI_ACQUIRE_GLOBAL_LOCK(facs, acquired); + ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); if (acquired) { /* Got the lock, now wake all threads waiting for it */ @@ -336,34 +333,27 @@ acpi_status acpi_ev_init_global_lock_handler(void) ACPI_FUNCTION_TRACE(ev_init_global_lock_handler); - status = acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, - ACPI_CAST_INDIRECT_PTR(struct - acpi_table_header, - &facs)); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } + /* Attempt installation of the global lock handler */ - acpi_gbl_global_lock_present = TRUE; status = acpi_install_fixed_event_handler(ACPI_EVENT_GLOBAL, acpi_ev_global_lock_handler, NULL); /* - * If the global lock does not exist on this platform, the attempt - * to enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick) - * Map to AE_OK, but mark global lock as not present. - * Any attempt to actually use the global lock will be flagged - * with an error. + * If the global lock does not exist on this platform, the attempt to + * enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick). + * Map to AE_OK, but mark global lock as not present. Any attempt to + * actually use the global lock will be flagged with an error. */ if (status == AE_NO_HARDWARE_RESPONSE) { ACPI_ERROR((AE_INFO, "No response from Global Lock hardware, disabling lock")); acpi_gbl_global_lock_present = FALSE; - status = AE_OK; + return_ACPI_STATUS(AE_OK); } + acpi_gbl_global_lock_present = TRUE; return_ACPI_STATUS(status); } @@ -462,8 +452,8 @@ acpi_status acpi_ev_acquire_global_lock(u16 timeout) } /* - * Make sure that a global lock actually exists. If not, just treat - * the lock as a standard mutex. + * Make sure that a global lock actually exists. If not, just treat the + * lock as a standard mutex. */ if (!acpi_gbl_global_lock_present) { acpi_gbl_global_lock_acquired = TRUE; @@ -472,7 +462,7 @@ acpi_status acpi_ev_acquire_global_lock(u16 timeout) /* Attempt to acquire the actual hardware lock */ - ACPI_ACQUIRE_GLOBAL_LOCK(facs, acquired); + ACPI_ACQUIRE_GLOBAL_LOCK(acpi_gbl_FACS, acquired); if (acquired) { /* We got the lock */ @@ -536,7 +526,7 @@ acpi_status acpi_ev_release_global_lock(void) /* Allow any thread to release the lock */ - ACPI_RELEASE_GLOBAL_LOCK(facs, pending); + ACPI_RELEASE_GLOBAL_LOCK(acpi_gbl_FACS, pending); /* * If the pending bit was set, we must write GBL_RLS to the control @@ -582,8 +572,8 @@ void acpi_ev_terminate(void) if (acpi_gbl_events_initialized) { /* - * Disable all event-related functionality. - * In all cases, on error, print a message but obviously we don't abort. + * Disable all event-related functionality. In all cases, on error, + * print a message but obviously we don't abort. */ /* Disable all fixed events */ @@ -599,7 +589,7 @@ void acpi_ev_terminate(void) /* Disable all GPEs in all GPE blocks */ - status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block); + status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL); /* Remove SCI handler */ @@ -617,7 +607,7 @@ void acpi_ev_terminate(void) /* Deallocate all handler objects installed within GPE info structs */ - status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers); + status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers, NULL); /* Return to original mode if necessary */ diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/acpica/evregion.c index 236fbd1ca438..665c0887ab4d 100644 --- a/drivers/acpi/events/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -42,22 +42,15 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evregion") -#define ACPI_NUM_DEFAULT_SPACES 4 -static u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPACES] = { - ACPI_ADR_SPACE_SYSTEM_MEMORY, - ACPI_ADR_SPACE_SYSTEM_IO, - ACPI_ADR_SPACE_PCI_CONFIG, - ACPI_ADR_SPACE_DATA_TABLE -}; /* Local prototypes */ - static acpi_status acpi_ev_reg_run(acpi_handle obj_handle, u32 level, void *context, void **return_value); @@ -66,6 +59,17 @@ static acpi_status acpi_ev_install_handler(acpi_handle obj_handle, u32 level, void *context, void **return_value); +/* These are the address spaces that will get default handlers */ + +#define ACPI_NUM_DEFAULT_SPACES 4 + +static u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPACES] = { + ACPI_ADR_SPACE_SYSTEM_MEMORY, + ACPI_ADR_SPACE_SYSTEM_IO, + ACPI_ADR_SPACE_PCI_CONFIG, + ACPI_ADR_SPACE_DATA_TABLE +}; + /******************************************************************************* * * FUNCTION: acpi_ev_install_region_handlers @@ -91,18 +95,19 @@ acpi_status acpi_ev_install_region_handlers(void) } /* - * All address spaces (PCI Config, EC, SMBus) are scope dependent - * and registration must occur for a specific device. + * All address spaces (PCI Config, EC, SMBus) are scope dependent and + * registration must occur for a specific device. * - * In the case of the system memory and IO address spaces there is currently - * no device associated with the address space. For these we use the root. + * In the case of the system memory and IO address spaces there is + * currently no device associated with the address space. For these we + * use the root. * - * We install the default PCI config space handler at the root so - * that this space is immediately available even though the we have - * not enumerated all the PCI Root Buses yet. This is to conform - * to the ACPI specification which states that the PCI config - * space must be always available -- even though we are nowhere - * near ready to find the PCI root buses at this point. + * We install the default PCI config space handler at the root so that + * this space is immediately available even though the we have not + * enumerated all the PCI Root Buses yet. This is to conform to the ACPI + * specification which states that the PCI config space must be always + * available -- even though we are nowhere near ready to find the PCI root + * buses at this point. * * NOTE: We ignore AE_ALREADY_EXISTS because this means that a handler * has already been installed (via acpi_install_address_space_handler). @@ -160,12 +165,11 @@ acpi_status acpi_ev_initialize_op_regions(void) return_ACPI_STATUS(status); } - /* - * Run the _REG methods for op_regions in each default address space - */ - for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + /* Run the _REG methods for op_regions in each default address space */ - /* TBD: Make sure handler is the DEFAULT handler, otherwise + for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + /* + * TBD: Make sure handler is the DEFAULT handler, otherwise * _REG will have already been run. */ status = acpi_ev_execute_reg_methods(acpi_gbl_root_node, @@ -318,13 +322,13 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, } /* - * It may be the case that the region has never been initialized + * It may be the case that the region has never been initialized. * Some types of regions require special init code */ if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { - /* - * This region has not been initialized yet, do it - */ + + /* This region has not been initialized yet, do it */ + region_setup = handler_desc->address_space.setup; if (!region_setup) { @@ -339,9 +343,9 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, } /* - * We must exit the interpreter because the region - * setup will potentially execute control methods - * (e.g., _REG method for this region) + * We must exit the interpreter because the region setup will + * potentially execute control methods (for example, the _REG method + * for this region) */ acpi_ex_exit_interpreter(); @@ -364,9 +368,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, return_ACPI_STATUS(status); } - /* - * Region initialization may have been completed by region_setup - */ + /* Region initialization may have been completed by region_setup */ + if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { region_obj->region.flags |= AOPOBJ_SETUP_COMPLETE; @@ -521,8 +524,8 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj, } /* - * If the region has been activated, call the setup handler - * with the deactivate notification + * If the region has been activated, call the setup handler with + * the deactivate notification */ if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) { region_setup = handler_obj->address_space.setup; @@ -668,8 +671,8 @@ acpi_ev_install_handler(acpi_handle obj_handle, } /* - * We only care about regions.and objects - * that are allowed to have address space handlers + * We only care about regions and objects that are allowed to have + * address space handlers */ if ((node->type != ACPI_TYPE_DEVICE) && (node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) { @@ -710,9 +713,9 @@ acpi_ev_install_handler(acpi_handle obj_handle, /* * Since the object we found it on was a device, then it * means that someone has already installed a handler for - * the branch of the namespace from this device on. Just + * the branch of the namespace from this device on. Just * bail out telling the walk routine to not traverse this - * branch. This preserves the scoping rule for handlers. + * branch. This preserves the scoping rule for handlers. */ return (AE_CTRL_DEPTH); } @@ -723,9 +726,8 @@ acpi_ev_install_handler(acpi_handle obj_handle, } /* - * As long as the device didn't have a handler for this - * space we don't care about it. We just ignore it and - * proceed. + * As long as the device didn't have a handler for this space we + * don't care about it. We just ignore it and proceed. */ return (AE_OK); } @@ -733,16 +735,14 @@ acpi_ev_install_handler(acpi_handle obj_handle, /* Object is a Region */ if (obj_desc->region.space_id != handler_obj->address_space.space_id) { - /* - * This region is for a different address space - * -- just ignore it - */ + + /* This region is for a different address space, just ignore it */ + return (AE_OK); } /* - * Now we have a region and it is for the handler's address - * space type. + * Now we have a region and it is for the handler's address space type. * * First disconnect region for any previous handler (if any) */ @@ -786,9 +786,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node, ACPI_FUNCTION_TRACE(ev_install_space_handler); /* - * This registration is valid for only the types below - * and the root. This is where the default handlers - * get placed. + * This registration is valid for only the types below and the root. This + * is where the default handlers get placed. */ if ((node->type != ACPI_TYPE_DEVICE) && (node->type != ACPI_TYPE_PROCESSOR) && @@ -848,8 +847,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node, obj_desc = acpi_ns_get_attached_object(node); if (obj_desc) { /* - * The attached device object already exists. - * Make sure the handler is not already installed. + * The attached device object already exists. Make sure the handler + * is not already installed. */ handler_obj = obj_desc->device.handler; @@ -864,8 +863,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node, handler) { /* * It is (relatively) OK to attempt to install the SAME - * handler twice. This can easily happen - * with PCI_Config space. + * handler twice. This can easily happen with the + * PCI_Config space. */ status = AE_SAME_HANDLER; goto unlock_and_exit; @@ -925,9 +924,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node, /* * Install the handler * - * At this point there is no existing handler. - * Just allocate the object for the handler and link it - * into the list. + * At this point there is no existing handler. Just allocate the object + * for the handler and link it into the list. */ handler_obj = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_ADDRESS_HANDLER); @@ -1000,11 +998,10 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, ACPI_FUNCTION_TRACE(ev_execute_reg_methods); /* - * Run all _REG methods for all Operation Regions for this - * space ID. This is a separate walk in order to handle any - * interdependencies between regions and _REG methods. (i.e. handlers - * must be installed for all regions of this Space ID before we - * can run any _REG methods) + * Run all _REG methods for all Operation Regions for this space ID. This + * is a separate walk in order to handle any interdependencies between + * regions and _REG methods. (i.e. handlers must be installed for all + * regions of this Space ID before we can run any _REG methods) */ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, @@ -1042,8 +1039,8 @@ acpi_ev_reg_run(acpi_handle obj_handle, } /* - * We only care about regions.and objects - * that are allowed to have address space handlers + * We only care about regions.and objects that are allowed to have address + * space handlers */ if ((node->type != ACPI_TYPE_REGION) && (node != acpi_gbl_root_node)) { return (AE_OK); @@ -1062,10 +1059,9 @@ acpi_ev_reg_run(acpi_handle obj_handle, /* Object is a Region */ if (obj_desc->region.space_id != space_id) { - /* - * This region is for a different address space - * -- just ignore it - */ + + /* This region is for a different address space, just ignore it */ + return (AE_OK); } diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 6b94b38df07d..f3f1fb45c3dc 100644 --- a/drivers/acpi/events/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evrgnini") @@ -233,9 +234,9 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, if (ACPI_FAILURE(status)) { if (status == AE_SAME_HANDLER) { /* - * It is OK if the handler is already installed on the root - * bridge. Still need to return a context object for the - * new PCI_Config operation region, however. + * It is OK if the handler is already installed on the + * root bridge. Still need to return a context object + * for the new PCI_Config operation region, however. */ status = AE_OK; } else { @@ -272,8 +273,8 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, } /* - * For PCI_Config space access, we need the segment, bus, - * device and function numbers. Acquire them here. + * For PCI_Config space access, we need the segment, bus, device and + * function numbers. Acquire them here. * * Find the parent device object. (This allows the operation region to be * within a subscope under the device, such as a control method.) @@ -289,16 +290,16 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, } /* - * Get the PCI device and function numbers from the _ADR object - * contained in the parent's scope. + * Get the PCI device and function numbers from the _ADR object contained + * in the parent's scope. */ status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, pci_device_node, &pci_value); /* - * The default is zero, and since the allocation above zeroed - * the data, just do nothing on failure. + * The default is zero, and since the allocation above zeroed the data, + * just do nothing on failure. */ if (ACPI_SUCCESS(status)) { pci_id->device = ACPI_HIWORD(ACPI_LODWORD(pci_value)); @@ -382,9 +383,8 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) struct acpi_compatible_id_list *cid; u32 i; - /* - * Get the _HID and check for a PCI Root Bridge - */ + /* Get the _HID and check for a PCI Root Bridge */ + status = acpi_ut_execute_HID(node, &hid); if (ACPI_FAILURE(status)) { return (FALSE); @@ -394,10 +394,8 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) return (TRUE); } - /* - * The _HID did not match. - * Get the _CID and check for a PCI Root Bridge - */ + /* The _HID did not match. Get the _CID and check for a PCI Root Bridge */ + status = acpi_ut_execute_CID(node, &cid); if (ACPI_FAILURE(status)) { return (FALSE); @@ -516,9 +514,9 @@ acpi_ev_default_region_setup(acpi_handle handle, * Get the appropriate address space handler for a newly * created region. * - * This also performs address space specific initialization. For + * This also performs address space specific initialization. For * example, PCI regions must have an _ADR object that contains - * a PCI address in the scope of the definition. This address is + * a PCI address in the scope of the definition. This address is * required to perform an access to PCI config space. * * MUTEX: Interpreter should be unlocked, because we may run the _REG @@ -572,7 +570,7 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj, if (ACPI_SUCCESS(status)) { /* * The _REG method is optional and there can be only one per region - * definition. This will be executed when the handler is attached + * definition. This will be executed when the handler is attached * or removed */ region_obj2->extra.method_REG = method_node; @@ -670,10 +668,8 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj, } } - /* - * This node does not have the handler we need; - * Pop up one level - */ + /* This node does not have the handler we need; Pop up one level */ + node = acpi_ns_get_parent_node(node); } diff --git a/drivers/acpi/events/evsci.c b/drivers/acpi/acpica/evsci.c index 2a8b77877610..567b356c85af 100644 --- a/drivers/acpi/events/evsci.c +++ b/drivers/acpi/acpica/evsci.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acevents.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evsci") @@ -115,10 +116,8 @@ u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context) * if this interrupt handler is installed, ACPI is enabled. */ - /* - * GPEs: - * Check for and dispatch any GPEs that have occurred - */ + /* GPEs: Check for and dispatch any GPEs that have occurred */ + interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list); return_UINT32(interrupt_handled); @@ -158,11 +157,11 @@ u32 acpi_ev_install_sci_handler(void) * RETURN: E_OK if handler uninstalled OK, E_ERROR if handler was not * installed to begin with * - * DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be + * DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be * taken. * * Note: It doesn't seem important to disable all events or set the event - * enable registers to their original values. The OS should disable + * enable registers to their original values. The OS should disable * the SCI interrupt level when the handler is removed, so no more * events will come in. * diff --git a/drivers/acpi/events/evxface.c b/drivers/acpi/acpica/evxface.c index 94a6efe020be..3aca9010a11e 100644 --- a/drivers/acpi/events/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" +#include "acinterp.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evxface") @@ -267,7 +268,7 @@ acpi_install_notify_handler(acpi_handle device, /* * Root Object: * Registering a notify handler on the root object indicates that the - * caller wishes to receive notifications for all objects. Note that + * caller wishes to receive notifications for all objects. Note that * only one <external> global handler can be regsitered (per notify type). */ if (device == ACPI_ROOT_OBJECT) { diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 41554f736b68..35485e4b60a6 100644 --- a/drivers/acpi/events/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -42,13 +42,19 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "actables.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evxfevnt") +/* Local prototypes */ +acpi_status +acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context); + /******************************************************************************* * * FUNCTION: acpi_enable @@ -60,6 +66,7 @@ ACPI_MODULE_NAME("evxfevnt") * DESCRIPTION: Transfers the system into ACPI mode. * ******************************************************************************/ + acpi_status acpi_enable(void) { acpi_status status = AE_OK; @@ -161,8 +168,8 @@ acpi_status acpi_enable_event(u32 event, u32 flags) } /* - * Enable the requested fixed event (by writing a one to the - * enable register bit) + * Enable the requested fixed event (by writing a one to the enable + * register bit) */ status = acpi_set_register(acpi_gbl_fixed_event_info[event]. @@ -343,8 +350,8 @@ acpi_status acpi_disable_event(u32 event, u32 flags) } /* - * Disable the requested fixed event (by writing a zero to the - * enable register bit) + * Disable the requested fixed event (by writing a zero to the enable + * register bit) */ status = acpi_set_register(acpi_gbl_fixed_event_info[event]. @@ -396,8 +403,8 @@ acpi_status acpi_clear_event(u32 event) } /* - * Clear the requested fixed event (By writing a one to the - * status register bit) + * Clear the requested fixed event (By writing a one to the status + * register bit) */ status = acpi_set_register(acpi_gbl_fixed_event_info[event]. @@ -717,3 +724,148 @@ acpi_status acpi_remove_gpe_block(acpi_handle gpe_device) } ACPI_EXPORT_SYMBOL(acpi_remove_gpe_block) + +/******************************************************************************* + * + * FUNCTION: acpi_get_gpe_device + * + * PARAMETERS: Index - System GPE index (0-current_gpe_count) + * gpe_device - Where the parent GPE Device is returned + * + * RETURN: Status + * + * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL + * gpe device indicates that the gpe number is contained in one of + * the FADT-defined gpe blocks. Otherwise, the GPE block device. + * + ******************************************************************************/ +acpi_status +acpi_get_gpe_device(u32 index, acpi_handle *gpe_device) +{ + struct acpi_gpe_device_info info; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_gpe_device); + + if (!gpe_device) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (index >= acpi_current_gpe_count) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + + /* Setup and walk the GPE list */ + + info.index = index; + info.status = AE_NOT_EXIST; + info.gpe_device = NULL; + info.next_block_base_index = 0; + + status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + *gpe_device = info.gpe_device; + return_ACPI_STATUS(info.status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_gpe_device) + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_device + * + * PARAMETERS: GPE_WALK_CALLBACK + * + * RETURN: Status + * + * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE + * block device. NULL if the GPE is one of the FADT-defined GPEs. + * + ******************************************************************************/ +acpi_status +acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) +{ + struct acpi_gpe_device_info *info = context; + + /* Increment Index by the number of GPEs in this block */ + + info->next_block_base_index += + (gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH); + + if (info->index < info->next_block_base_index) { + /* + * The GPE index is within this block, get the node. Leave the node + * NULL for the FADT-defined GPEs + */ + if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) { + info->gpe_device = gpe_block->node; + } + + info->status = AE_OK; + return (AE_CTRL_END); + } + + return (AE_OK); +} + +/****************************************************************************** + * + * FUNCTION: acpi_disable_all_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Disable and clear all GPEs in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_disable_all_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_disable_all_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_disable_all_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_enable_all_runtime_gpes + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks + * + ******************************************************************************/ + +acpi_status acpi_enable_all_runtime_gpes(void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_enable_all_runtime_gpes); + + status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_hw_enable_all_runtime_gpes(); + (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); + + return_ACPI_STATUS(status); +} diff --git a/drivers/acpi/events/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index e8750807e57d..479e7a3721be 100644 --- a/drivers/acpi/events/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -43,8 +43,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evxfregn") diff --git a/drivers/acpi/executer/exconfig.c b/drivers/acpi/acpica/exconfig.c index 74da6fa52ef1..932bbc26aa04 100644 --- a/drivers/acpi/executer/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -42,10 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/actables.h> -#include <acpi/acdispat.h> +#include "accommon.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "actables.h" +#include "acdispat.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exconfig") diff --git a/drivers/acpi/executer/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index 1d1f35adddde..0be10188316e 100644 --- a/drivers/acpi/executer/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exconvrt") diff --git a/drivers/acpi/executer/excreate.c b/drivers/acpi/acpica/excreate.c index ad09696d5069..a57ad2564ab0 100644 --- a/drivers/acpi/executer/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("excreate") diff --git a/drivers/acpi/executer/exdump.c b/drivers/acpi/acpica/exdump.c index d087a7d28aa5..aa313574b0df 100644 --- a/drivers/acpi/executer/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exdump") diff --git a/drivers/acpi/executer/exfield.c b/drivers/acpi/acpica/exfield.c index 3e440d84226a..a352d0233857 100644 --- a/drivers/acpi/executer/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exfield") diff --git a/drivers/acpi/executer/exfldio.c b/drivers/acpi/acpica/exfldio.c index 9ff9d1f4615d..ef58ac4e687b 100644 --- a/drivers/acpi/executer/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -42,10 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/acevents.h> -#include <acpi/acdispat.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acevents.h" +#include "acdispat.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exfldio") @@ -498,14 +499,13 @@ acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, return_ACPI_STATUS(status); } - ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, - "I/O to Data Register: ValuePtr %p\n", - value)); - if (read_write == ACPI_READ) { /* Read the datum from the data_register */ + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Read from Data Register\n")); + status = acpi_ex_extract_from_field(obj_desc->index_field. data_obj, value, @@ -513,6 +513,10 @@ acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, } else { /* Write the datum to the data_register */ + ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, + "Write to Data Register: Value %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(*value))); + status = acpi_ex_insert_into_field(obj_desc->index_field. data_obj, value, diff --git a/drivers/acpi/executer/exmisc.c b/drivers/acpi/acpica/exmisc.c index efb191340059..6b0747ac683b 100644 --- a/drivers/acpi/executer/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -43,9 +43,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/amlresrc.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "amlresrc.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exmisc") diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/acpica/exmutex.c index a8bf3d713e28..d301c1f363ef 100644 --- a/drivers/acpi/executer/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -43,8 +43,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acinterp.h" +#include "acevents.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exmutex") diff --git a/drivers/acpi/executer/exnames.c b/drivers/acpi/acpica/exnames.c index 817e67be3697..ffdae122d94a 100644 --- a/drivers/acpi/executer/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -43,8 +43,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exnames") diff --git a/drivers/acpi/executer/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index f622f9eac8a1..b530480cc7d5 100644 --- a/drivers/acpi/executer/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -43,11 +43,12 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exoparg1") diff --git a/drivers/acpi/executer/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index 368def5dffce..0b4f513ca885 100644 --- a/drivers/acpi/executer/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -42,10 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acinterp.h> -#include <acpi/acevents.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acparser.h" +#include "acinterp.h" +#include "acevents.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exoparg2") diff --git a/drivers/acpi/executer/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 9cb4197681af..c6520bbf882b 100644 --- a/drivers/acpi/executer/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -43,9 +43,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acinterp.h" +#include "acparser.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exoparg3") diff --git a/drivers/acpi/executer/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index 67d48737af53..ae43f7670a6c 100644 --- a/drivers/acpi/executer/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -43,9 +43,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acinterp.h" +#include "acparser.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exoparg6") diff --git a/drivers/acpi/executer/exprep.c b/drivers/acpi/acpica/exprep.c index a7dc87ecee37..a226f74d4a5c 100644 --- a/drivers/acpi/executer/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -43,9 +43,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exprep") diff --git a/drivers/acpi/executer/exregion.c b/drivers/acpi/acpica/exregion.c index 7a41c409ae4d..76ec8ff903b8 100644 --- a/drivers/acpi/executer/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acinterp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exregion") diff --git a/drivers/acpi/executer/exresnte.c b/drivers/acpi/acpica/exresnte.c index 423ad3635f3d..a063a74006f6 100644 --- a/drivers/acpi/executer/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -43,9 +43,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exresnte") diff --git a/drivers/acpi/executer/exresolv.c b/drivers/acpi/acpica/exresolv.c index 60e8c47128e9..f6105a6d6126 100644 --- a/drivers/acpi/executer/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -43,10 +43,11 @@ */ #include <acpi/acpi.h> -#include <acpi/amlcode.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "amlcode.h" +#include "acdispat.h" +#include "acinterp.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exresolv") diff --git a/drivers/acpi/executer/exresop.c b/drivers/acpi/acpica/exresop.c index 0bb82593da72..3c3802764bfb 100644 --- a/drivers/acpi/executer/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -43,10 +43,11 @@ */ #include <acpi/acpi.h> -#include <acpi/amlcode.h> -#include <acpi/acparser.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "amlcode.h" +#include "acparser.h" +#include "acinterp.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exresop") diff --git a/drivers/acpi/executer/exstore.c b/drivers/acpi/acpica/exstore.c index 1c118ba78adb..e35e9b4f6a4e 100644 --- a/drivers/acpi/executer/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -43,10 +43,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exstore") diff --git a/drivers/acpi/executer/exstoren.c b/drivers/acpi/acpica/exstoren.c index eef61a00803e..145d15305f70 100644 --- a/drivers/acpi/executer/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -44,8 +44,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exstoren") diff --git a/drivers/acpi/executer/exstorob.c b/drivers/acpi/acpica/exstorob.c index 9a75ff09fb0c..67340cc70142 100644 --- a/drivers/acpi/executer/exstorob.c +++ b/drivers/acpi/acpica/exstorob.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acinterp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exstorob") diff --git a/drivers/acpi/executer/exsystem.c b/drivers/acpi/acpica/exsystem.c index 68990f1df371..3d00b9357233 100644 --- a/drivers/acpi/executer/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acinterp.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exsystem") diff --git a/drivers/acpi/executer/exutils.c b/drivers/acpi/acpica/exutils.c index 86c03880b523..32b85d68e756 100644 --- a/drivers/acpi/executer/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -59,8 +59,9 @@ #define DEFINE_AML_GLOBALS #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acinterp.h" +#include "amlcode.h" #define _COMPONENT ACPI_EXECUTER ACPI_MODULE_NAME("exutils") diff --git a/drivers/acpi/hardware/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index 816894ea839e..a9d4fea4167f 100644 --- a/drivers/acpi/hardware/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -43,6 +43,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" #define _COMPONENT ACPI_HARDWARE ACPI_MODULE_NAME("hwacpi") diff --git a/drivers/acpi/hardware/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 0b80db9d9197..2013b66745d2 100644 --- a/drivers/acpi/hardware/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acevents.h" #define _COMPONENT ACPI_HARDWARE ACPI_MODULE_NAME("hwgpe") @@ -51,7 +52,8 @@ ACPI_MODULE_NAME("hwgpe") /* Local prototypes */ static acpi_status acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block); + struct acpi_gpe_block_info *gpe_block, + void *context); /****************************************************************************** * @@ -80,8 +82,7 @@ acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) /* Get current value of the enable register that contains this GPE */ - status = acpi_hw_low_level_read(ACPI_GPE_REGISTER_WIDTH, &enable_mask, - &gpe_register_info->enable_address); + status = acpi_read(&enable_mask, &gpe_register_info->enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -95,9 +96,7 @@ acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) /* Write the updated enable mask */ - status = acpi_hw_low_level_write(ACPI_GPE_REGISTER_WIDTH, enable_mask, - &gpe_register_info->enable_address); - + status = acpi_write(enable_mask, &gpe_register_info->enable_address); return (status); } @@ -132,8 +131,8 @@ acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info * gpe_event_info) /* Write the entire GPE (runtime) enable register */ - status = acpi_hw_low_level_write(8, gpe_register_info->enable_for_run, - &gpe_register_info->enable_address); + status = acpi_write(gpe_register_info->enable_for_run, + &gpe_register_info->enable_address); return (status); } @@ -166,9 +165,8 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) * Write a one to the appropriate bit in the status register to * clear this GPE. */ - status = acpi_hw_low_level_write(8, register_bit, - &gpe_event_info->register_info-> - status_address); + status = acpi_write(register_bit, + &gpe_event_info->register_info->status_address); return (status); } @@ -227,9 +225,7 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, /* GPE currently active (status bit == 1)? */ - status = - acpi_hw_low_level_read(8, &in_byte, - &gpe_register_info->status_address); + status = acpi_read(&in_byte, &gpe_register_info->status_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } @@ -260,8 +256,8 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, ******************************************************************************/ acpi_status -acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, - struct acpi_gpe_block_info * gpe_block) +acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) { u32 i; acpi_status status; @@ -272,9 +268,9 @@ acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, /* Disable all GPEs in this register */ - status = acpi_hw_low_level_write(8, 0x00, - &gpe_block->register_info[i]. - enable_address); + status = + acpi_write(0x00, + &gpe_block->register_info[i].enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -297,8 +293,8 @@ acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, ******************************************************************************/ acpi_status -acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, - struct acpi_gpe_block_info * gpe_block) +acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) { u32 i; acpi_status status; @@ -309,9 +305,9 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, /* Clear status on all GPEs in this register */ - status = acpi_hw_low_level_write(8, 0xFF, - &gpe_block->register_info[i]. - status_address); + status = + acpi_write(0xFF, + &gpe_block->register_info[i].status_address); if (ACPI_FAILURE(status)) { return (status); } @@ -335,8 +331,8 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, ******************************************************************************/ acpi_status -acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, - struct acpi_gpe_block_info * gpe_block) +acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block, void *context) { u32 i; acpi_status status; @@ -352,12 +348,9 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, /* Enable all "runtime" GPEs in this register */ - status = - acpi_hw_low_level_write(8, - gpe_block->register_info[i]. - enable_for_run, - &gpe_block->register_info[i]. - enable_address); + status = acpi_write(gpe_block->register_info[i].enable_for_run, + &gpe_block->register_info[i]. + enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -382,7 +375,8 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info * gpe_xrupt_info, static acpi_status acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, - struct acpi_gpe_block_info *gpe_block) + struct acpi_gpe_block_info *gpe_block, + void *context) { u32 i; acpi_status status; @@ -396,11 +390,9 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Enable all "wake" GPEs in this register */ - status = acpi_hw_low_level_write(8, - gpe_block->register_info[i]. - enable_for_wake, - &gpe_block->register_info[i]. - enable_address); + status = acpi_write(gpe_block->register_info[i].enable_for_wake, + &gpe_block->register_info[i]. + enable_address); if (ACPI_FAILURE(status)) { return (status); } @@ -427,8 +419,8 @@ acpi_status acpi_hw_disable_all_gpes(void) ACPI_FUNCTION_TRACE(hw_disable_all_gpes); - status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block); - status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block); + status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL); + status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); return_ACPI_STATUS(status); } @@ -450,7 +442,7 @@ acpi_status acpi_hw_enable_all_runtime_gpes(void) ACPI_FUNCTION_TRACE(hw_enable_all_runtime_gpes); - status = acpi_ev_walk_gpe_list(acpi_hw_enable_runtime_gpe_block); + status = acpi_ev_walk_gpe_list(acpi_hw_enable_runtime_gpe_block, NULL); return_ACPI_STATUS(status); } @@ -472,6 +464,6 @@ acpi_status acpi_hw_enable_all_wakeup_gpes(void) ACPI_FUNCTION_TRACE(hw_enable_all_wakeup_gpes); - status = acpi_ev_walk_gpe_list(acpi_hw_enable_wakeup_gpe_block); + status = acpi_ev_walk_gpe_list(acpi_hw_enable_wakeup_gpe_block, NULL); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c new file mode 100644 index 000000000000..4dc43b018517 --- /dev/null +++ b/drivers/acpi/acpica/hwregs.c @@ -0,0 +1,353 @@ + +/******************************************************************************* + * + * Module Name: hwregs - Read/write access functions for the various ACPI + * control and status registers. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2008, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" + +#define _COMPONENT ACPI_HARDWARE +ACPI_MODULE_NAME("hwregs") + +/******************************************************************************* + * + * FUNCTION: acpi_hw_clear_acpi_status + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Clears all fixed and general purpose status bits + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ +acpi_status acpi_hw_clear_acpi_status(void) +{ + acpi_status status; + acpi_cpu_flags lock_flags = 0; + + ACPI_FUNCTION_TRACE(hw_clear_acpi_status); + + ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %04X\n", + ACPI_BITMASK_ALL_FIXED_STATUS, + (u16) acpi_gbl_FADT.xpm1a_event_block.address)); + + lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); + + status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, + ACPI_BITMASK_ALL_FIXED_STATUS); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + + /* Clear the fixed events */ + + if (acpi_gbl_FADT.xpm1b_event_block.address) { + status = acpi_write(ACPI_BITMASK_ALL_FIXED_STATUS, + &acpi_gbl_FADT.xpm1b_event_block); + if (ACPI_FAILURE(status)) { + goto unlock_and_exit; + } + } + + /* Clear the GPE Bits in all GPE registers in all GPE blocks */ + + status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL); + + unlock_and_exit: + acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_hw_get_register_bit_mask + * + * PARAMETERS: register_id - Index of ACPI Register to access + * + * RETURN: The bitmask to be used when accessing the register + * + * DESCRIPTION: Map register_id into a register bitmask. + * + ******************************************************************************/ + +struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) +{ + ACPI_FUNCTION_ENTRY(); + + if (register_id > ACPI_BITREG_MAX) { + ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: %X", + register_id)); + return (NULL); + } + + return (&acpi_gbl_bit_register_info[register_id]); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_register_read + * + * PARAMETERS: register_id - ACPI Register ID + * return_value - Where the register value is returned + * + * RETURN: Status and the value read. + * + * DESCRIPTION: Read from the specified ACPI register + * + ******************************************************************************/ +acpi_status +acpi_hw_register_read(u32 register_id, u32 * return_value) +{ + u32 value1 = 0; + u32 value2 = 0; + acpi_status status; + + ACPI_FUNCTION_TRACE(hw_register_read); + + switch (register_id) { + case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ + + status = acpi_read(&value1, &acpi_gbl_FADT.xpm1a_event_block); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* PM1B is optional */ + + status = acpi_read(&value2, &acpi_gbl_FADT.xpm1b_event_block); + value1 |= value2; + break; + + case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ + + status = acpi_read(&value1, &acpi_gbl_xpm1a_enable); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* PM1B is optional */ + + status = acpi_read(&value2, &acpi_gbl_xpm1b_enable); + value1 |= value2; + break; + + case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ + + status = acpi_read(&value1, &acpi_gbl_FADT.xpm1a_control_block); + if (ACPI_FAILURE(status)) { + goto exit; + } + + status = acpi_read(&value2, &acpi_gbl_FADT.xpm1b_control_block); + value1 |= value2; + break; + + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + + status = acpi_read(&value1, &acpi_gbl_FADT.xpm2_control_block); + break; + + case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ + + status = acpi_read(&value1, &acpi_gbl_FADT.xpm_timer_block); + break; + + case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + + status = + acpi_os_read_port(acpi_gbl_FADT.smi_command, &value1, 8); + break; + + default: + ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); + status = AE_BAD_PARAMETER; + break; + } + + exit: + + if (ACPI_SUCCESS(status)) { + *return_value = value1; + } + + return_ACPI_STATUS(status); +} + +/****************************************************************************** + * + * FUNCTION: acpi_hw_register_write + * + * PARAMETERS: register_id - ACPI Register ID + * Value - The value to write + * + * RETURN: Status + * + * DESCRIPTION: Write to the specified ACPI register + * + * NOTE: In accordance with the ACPI specification, this function automatically + * preserves the value of the following bits, meaning that these bits cannot be + * changed via this interface: + * + * PM1_CONTROL[0] = SCI_EN + * PM1_CONTROL[9] + * PM1_STATUS[11] + * + * ACPI References: + * 1) Hardware Ignored Bits: When software writes to a register with ignored + * bit fields, it preserves the ignored bit fields + * 2) SCI_EN: OSPM always preserves this bit position + * + ******************************************************************************/ + +acpi_status acpi_hw_register_write(u32 register_id, u32 value) +{ + acpi_status status; + u32 read_value; + + ACPI_FUNCTION_TRACE(hw_register_write); + + switch (register_id) { + case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ + + /* Perform a read first to preserve certain bits (per ACPI spec) */ + + status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, + &read_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Insert the bits to be preserved */ + + ACPI_INSERT_BITS(value, ACPI_PM1_STATUS_PRESERVED_BITS, + read_value); + + /* Now we can write the data */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm1a_event_block); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* PM1B is optional */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm1b_event_block); + break; + + case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ + + status = acpi_write(value, &acpi_gbl_xpm1a_enable); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* PM1B is optional */ + + status = acpi_write(value, &acpi_gbl_xpm1b_enable); + break; + + case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ + + /* + * Perform a read first to preserve certain bits (per ACPI spec) + */ + status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, + &read_value); + if (ACPI_FAILURE(status)) { + goto exit; + } + + /* Insert the bits to be preserved */ + + ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS, + read_value); + + /* Now we can write the data */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm1a_control_block); + if (ACPI_FAILURE(status)) { + goto exit; + } + + status = acpi_write(value, &acpi_gbl_FADT.xpm1b_control_block); + break; + + case ACPI_REGISTER_PM1A_CONTROL: /* 16-bit access */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm1a_control_block); + break; + + case ACPI_REGISTER_PM1B_CONTROL: /* 16-bit access */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm1b_control_block); + break; + + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm2_control_block); + break; + + case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ + + status = acpi_write(value, &acpi_gbl_FADT.xpm_timer_block); + break; + + case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + + /* SMI_CMD is currently always in IO space */ + + status = + acpi_os_write_port(acpi_gbl_FADT.smi_command, value, 8); + break; + + default: + status = AE_BAD_PARAMETER; + break; + } + + exit: + return_ACPI_STATUS(status); +} diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 25dccdf179b9..a2af2a4f2f26 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "actables.h" #define _COMPONENT ACPI_HARDWARE ACPI_MODULE_NAME("hwsleep") @@ -52,31 +53,19 @@ ACPI_MODULE_NAME("hwsleep") * * FUNCTION: acpi_set_firmware_waking_vector * - * PARAMETERS: physical_address - Physical address of ACPI real mode + * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode * entry point. * * RETURN: Status * - * DESCRIPTION: Access function for the firmware_waking_vector field in FACS + * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS * ******************************************************************************/ acpi_status -acpi_set_firmware_waking_vector(acpi_physical_address physical_address) +acpi_set_firmware_waking_vector(u32 physical_address) { - struct acpi_table_facs *facs; - acpi_status status; - ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector); - /* Get the FACS */ - - status = acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, - ACPI_CAST_INDIRECT_PTR(struct - acpi_table_header, - &facs)); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } /* * According to the ACPI specification 2.0c and later, the 64-bit @@ -85,10 +74,16 @@ acpi_set_firmware_waking_vector(acpi_physical_address physical_address) * Protected Mode. Some systems (for example HP dv5-1004nr) are known * to fail to resume if the 64-bit vector is used. */ - if (facs->version >= 1) - facs->xfirmware_waking_vector = 0; - facs->firmware_waking_vector = (u32)physical_address; + /* Set the 32-bit vector */ + + acpi_gbl_FACS->firmware_waking_vector = physical_address; + + /* Clear the 64-bit vector if it exists */ + + if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) { + acpi_gbl_FACS->xfirmware_waking_vector = 0; + } return_ACPI_STATUS(AE_OK); } @@ -97,48 +92,39 @@ ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector) /******************************************************************************* * - * FUNCTION: acpi_get_firmware_waking_vector + * FUNCTION: acpi_set_firmware_waking_vector64 * - * PARAMETERS: *physical_address - Where the contents of - * the firmware_waking_vector field of - * the FACS will be returned. + * PARAMETERS: physical_address - 64-bit physical address of ACPI protected + * mode entry point. * - * RETURN: Status, vector + * RETURN: Status * - * DESCRIPTION: Access function for the firmware_waking_vector field in FACS + * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if + * it exists in the table. * ******************************************************************************/ -#ifdef ACPI_FUTURE_USAGE acpi_status -acpi_get_firmware_waking_vector(acpi_physical_address * physical_address) +acpi_set_firmware_waking_vector64(u64 physical_address) { - struct acpi_table_facs *facs; - acpi_status status; + ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64); - ACPI_FUNCTION_TRACE(acpi_get_firmware_waking_vector); - - if (!physical_address) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - /* Get the FACS */ + /* Determine if the 64-bit vector actually exists */ - status = acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, - ACPI_CAST_INDIRECT_PTR(struct - acpi_table_header, - &facs)); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) { + return_ACPI_STATUS(AE_NOT_EXIST); } - /* Get the vector */ - *physical_address = (acpi_physical_address)facs->firmware_waking_vector; + /* Clear 32-bit vector, set the 64-bit X_ vector */ + + acpi_gbl_FACS->firmware_waking_vector = 0; + acpi_gbl_FACS->xfirmware_waking_vector = physical_address; return_ACPI_STATUS(AE_OK); } -ACPI_EXPORT_SYMBOL(acpi_get_firmware_waking_vector) -#endif +ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64) + /******************************************************************************* * * FUNCTION: acpi_enter_sleep_state_prep diff --git a/drivers/acpi/hardware/hwtimer.c b/drivers/acpi/acpica/hwtimer.c index b53d575491b9..b7f522c8f023 100644 --- a/drivers/acpi/hardware/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -43,6 +43,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" #define _COMPONENT ACPI_HARDWARE ACPI_MODULE_NAME("hwtimer") diff --git a/drivers/acpi/hardware/hwregs.c b/drivers/acpi/acpica/hwxface.c index ddf792adcf96..ae597c0ab53f 100644 --- a/drivers/acpi/hardware/hwregs.c +++ b/drivers/acpi/acpica/hwxface.c @@ -1,10 +1,9 @@ -/******************************************************************************* +/****************************************************************************** * - * Module Name: hwregs - Read/write access functions for the various ACPI - * control and status registers. + * Module Name: hwxface - Public ACPICA hardware interfaces * - ******************************************************************************/ + *****************************************************************************/ /* * Copyright (C) 2000 - 2008, Intel Corp. @@ -44,209 +43,208 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_HARDWARE -ACPI_MODULE_NAME("hwregs") +ACPI_MODULE_NAME("hwxface") -/******************************************************************************* +/****************************************************************************** * - * FUNCTION: acpi_hw_clear_acpi_status + * FUNCTION: acpi_reset * * PARAMETERS: None * - * RETURN: None + * RETURN: Status * - * DESCRIPTION: Clears all fixed and general purpose status bits - * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * DESCRIPTION: Set reset register in memory or IO space. Note: Does not + * support reset register in PCI config space, this must be + * handled separately. * ******************************************************************************/ -acpi_status acpi_hw_clear_acpi_status(void) +acpi_status acpi_reset(void) { + struct acpi_generic_address *reset_reg; acpi_status status; - acpi_cpu_flags lock_flags = 0; - ACPI_FUNCTION_TRACE(hw_clear_acpi_status); + ACPI_FUNCTION_TRACE(acpi_reset); - ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %04X\n", - ACPI_BITMASK_ALL_FIXED_STATUS, - (u16) acpi_gbl_FADT.xpm1a_event_block.address)); + reset_reg = &acpi_gbl_FADT.reset_register; - lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); + /* Check if the reset register is supported */ - status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, - ACPI_BITMASK_ALL_FIXED_STATUS); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || + !reset_reg->address) { + return_ACPI_STATUS(AE_NOT_EXIST); } - /* Clear the fixed events */ - - if (acpi_gbl_FADT.xpm1b_event_block.address) { - status = - acpi_hw_low_level_write(16, ACPI_BITMASK_ALL_FIXED_STATUS, - &acpi_gbl_FADT.xpm1b_event_block); - if (ACPI_FAILURE(status)) { - goto unlock_and_exit; - } - } - - /* Clear the GPE Bits in all GPE registers in all GPE blocks */ - - status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block); + /* Write the reset value to the reset register */ - unlock_and_exit: - acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); + status = acpi_write(acpi_gbl_FADT.reset_value, reset_reg); return_ACPI_STATUS(status); } -/******************************************************************************* +ACPI_EXPORT_SYMBOL(acpi_reset) + +/****************************************************************************** * - * FUNCTION: acpi_get_sleep_type_data + * FUNCTION: acpi_read * - * PARAMETERS: sleep_state - Numeric sleep state - * *sleep_type_a - Where SLP_TYPa is returned - * *sleep_type_b - Where SLP_TYPb is returned + * PARAMETERS: Value - Where the value is returned + * Reg - GAS register structure * - * RETURN: Status - ACPI status + * RETURN: Status * - * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested sleep - * state. + * DESCRIPTION: Read from either memory or IO space. * ******************************************************************************/ - -acpi_status -acpi_get_sleep_type_data(u8 sleep_state, u8 * sleep_type_a, u8 * sleep_type_b) +acpi_status acpi_read(u32 *value, struct acpi_generic_address *reg) { - acpi_status status = AE_OK; - struct acpi_evaluate_info *info; - - ACPI_FUNCTION_TRACE(acpi_get_sleep_type_data); - - /* Validate parameters */ - - if ((sleep_state > ACPI_S_STATES_MAX) || !sleep_type_a || !sleep_type_b) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } + u32 width; + u64 address; + acpi_status status; - /* Allocate the evaluation information block */ + ACPI_FUNCTION_NAME(acpi_read); - info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); - if (!info) { - return_ACPI_STATUS(AE_NO_MEMORY); + /* + * Must have a valid pointer to a GAS structure, and + * a non-zero address within. However, don't return an error + * because the PM1A/B code must not fail if B isn't present. + */ + if (!reg) { + return (AE_OK); } - info->pathname = - ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]); - - /* Evaluate the namespace object containing the values for this state */ - - status = acpi_ns_evaluate(info); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "%s while evaluating SleepState [%s]\n", - acpi_format_exception(status), - info->pathname)); + /* Get a local copy of the address. Handles possible alignment issues */ - goto cleanup; + ACPI_MOVE_64_TO_64(&address, ®->address); + if (!address) { + return (AE_OK); } - /* Must have a return object */ + /* Supported widths are 8/16/32 */ - if (!info->return_object) { - ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]", - info->pathname)); - status = AE_NOT_EXIST; + width = reg->bit_width; + if ((width != 8) && (width != 16) && (width != 32)) { + return (AE_SUPPORT); } - /* It must be of type Package */ + /* Initialize entire 32-bit return value to zero */ - else if (ACPI_GET_OBJECT_TYPE(info->return_object) != ACPI_TYPE_PACKAGE) { - ACPI_ERROR((AE_INFO, - "Sleep State return object is not a Package")); - status = AE_AML_OPERAND_TYPE; - } + *value = 0; /* - * The package must have at least two elements. NOTE (March 2005): This - * goes against the current ACPI spec which defines this object as a - * package with one encoded DWORD element. However, existing practice - * by BIOS vendors seems to be to have 2 or more elements, at least - * one per sleep type (A/B). + * Two address spaces supported: Memory or IO. + * PCI_Config is not supported here because the GAS struct is insufficient */ - else if (info->return_object->package.count < 2) { - ACPI_ERROR((AE_INFO, - "Sleep State return package does not have at least two elements")); - status = AE_AML_NO_OPERAND; - } + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: - /* The first two elements must both be of type Integer */ + status = acpi_os_read_memory((acpi_physical_address) address, + value, width); + break; - else if ((ACPI_GET_OBJECT_TYPE(info->return_object->package.elements[0]) - != ACPI_TYPE_INTEGER) || - (ACPI_GET_OBJECT_TYPE(info->return_object->package.elements[1]) - != ACPI_TYPE_INTEGER)) { - ACPI_ERROR((AE_INFO, - "Sleep State return package elements are not both Integers (%s, %s)", - acpi_ut_get_object_type_name(info->return_object-> - package.elements[0]), - acpi_ut_get_object_type_name(info->return_object-> - package.elements[1]))); - status = AE_AML_OPERAND_TYPE; - } else { - /* Valid _Sx_ package size, type, and value */ + case ACPI_ADR_SPACE_SYSTEM_IO: - *sleep_type_a = (u8) - (info->return_object->package.elements[0])->integer.value; - *sleep_type_b = (u8) - (info->return_object->package.elements[1])->integer.value; - } + status = + acpi_os_read_port((acpi_io_address) address, value, width); + break; - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "While evaluating SleepState [%s], bad Sleep object %p type %s", - info->pathname, info->return_object, - acpi_ut_get_object_type_name(info-> - return_object))); + default: + ACPI_ERROR((AE_INFO, + "Unsupported address space: %X", reg->space_id)); + return (AE_BAD_PARAMETER); } - acpi_ut_remove_reference(info->return_object); + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", + *value, width, ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); - cleanup: - ACPI_FREE(info); - return_ACPI_STATUS(status); + return (status); } -ACPI_EXPORT_SYMBOL(acpi_get_sleep_type_data) +ACPI_EXPORT_SYMBOL(acpi_read) -/******************************************************************************* +/****************************************************************************** * - * FUNCTION: acpi_hw_get_register_bit_mask + * FUNCTION: acpi_write * - * PARAMETERS: register_id - Index of ACPI Register to access + * PARAMETERS: Value - To be written + * Reg - GAS register structure * - * RETURN: The bitmask to be used when accessing the register + * RETURN: Status * - * DESCRIPTION: Map register_id into a register bitmask. + * DESCRIPTION: Write to either memory or IO space. * ******************************************************************************/ -struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) +acpi_status acpi_write(u32 value, struct acpi_generic_address *reg) { - ACPI_FUNCTION_ENTRY(); + u32 width; + u64 address; + acpi_status status; - if (register_id > ACPI_BITREG_MAX) { - ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: %X", - register_id)); - return (NULL); + ACPI_FUNCTION_NAME(acpi_write); + + /* + * Must have a valid pointer to a GAS structure, and + * a non-zero address within. However, don't return an error + * because the PM1A/B code must not fail if B isn't present. + */ + if (!reg) { + return (AE_OK); } - return (&acpi_gbl_bit_register_info[register_id]); + /* Get a local copy of the address. Handles possible alignment issues */ + + ACPI_MOVE_64_TO_64(&address, ®->address); + if (!address) { + return (AE_OK); + } + + /* Supported widths are 8/16/32 */ + + width = reg->bit_width; + if ((width != 8) && (width != 16) && (width != 32)) { + return (AE_SUPPORT); + } + + /* + * Two address spaces supported: Memory or IO. + * PCI_Config is not supported here because the GAS struct is insufficient + */ + switch (reg->space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + + status = acpi_os_write_memory((acpi_physical_address) address, + value, width); + break; + + case ACPI_ADR_SPACE_SYSTEM_IO: + + status = acpi_os_write_port((acpi_io_address) address, value, + width); + break; + + default: + ACPI_ERROR((AE_INFO, + "Unsupported address space: %X", reg->space_id)); + return (AE_BAD_PARAMETER); + } + + ACPI_DEBUG_PRINT((ACPI_DB_IO, + "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", + value, width, ACPI_FORMAT_UINT64(address), + acpi_ut_get_region_name(reg->space_id))); + + return (status); } +ACPI_EXPORT_SYMBOL(acpi_write) + /******************************************************************************* * - * FUNCTION: acpi_get_register + * FUNCTION: acpi_get_register_unlocked * * PARAMETERS: register_id - ID of ACPI bit_register to access * return_value - Value that was read from the register @@ -254,17 +252,16 @@ struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) * RETURN: Status and the value read from specified Register. Value * returned is normalized to bit0 (is shifted all the way right) * - * DESCRIPTION: ACPI bit_register read function. + * DESCRIPTION: ACPI bit_register read function. Does not acquire the HW lock. * ******************************************************************************/ - -acpi_status acpi_get_register_unlocked(u32 register_id, u32 * return_value) +acpi_status acpi_get_register_unlocked(u32 register_id, u32 *return_value) { u32 register_value = 0; struct acpi_bit_register_info *bit_reg_info; acpi_status status; - ACPI_FUNCTION_TRACE(acpi_get_register); + ACPI_FUNCTION_TRACE(acpi_get_register_unlocked); /* Get the info structure corresponding to the requested ACPI Register */ @@ -296,14 +293,31 @@ acpi_status acpi_get_register_unlocked(u32 register_id, u32 * return_value) return_ACPI_STATUS(status); } -acpi_status acpi_get_register(u32 register_id, u32 * return_value) +ACPI_EXPORT_SYMBOL(acpi_get_register_unlocked) + +/******************************************************************************* + * + * FUNCTION: acpi_get_register + * + * PARAMETERS: register_id - ID of ACPI bit_register to access + * return_value - Value that was read from the register + * + * RETURN: Status and the value read from specified Register. Value + * returned is normalized to bit0 (is shifted all the way right) + * + * DESCRIPTION: ACPI bit_register read function. + * + ******************************************************************************/ +acpi_status acpi_get_register(u32 register_id, u32 *return_value) { acpi_status status; acpi_cpu_flags flags; + flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); status = acpi_get_register_unlocked(register_id, return_value); acpi_os_release_lock(acpi_gbl_hardware_lock, flags); - return status; + + return (status); } ACPI_EXPORT_SYMBOL(acpi_get_register) @@ -370,8 +384,9 @@ acpi_status acpi_set_register(u32 register_id, u32 value) bit_reg_info-> access_bit_mask); if (value) { - status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, - (u16) value); + status = + acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, + (u16) value); register_value = 0; } break; @@ -459,399 +474,120 @@ acpi_status acpi_set_register(u32 register_id, u32 value) ACPI_EXPORT_SYMBOL(acpi_set_register) -/****************************************************************************** +/******************************************************************************* * - * FUNCTION: acpi_hw_register_read + * FUNCTION: acpi_get_sleep_type_data * - * PARAMETERS: register_id - ACPI Register ID - * return_value - Where the register value is returned + * PARAMETERS: sleep_state - Numeric sleep state + * *sleep_type_a - Where SLP_TYPa is returned + * *sleep_type_b - Where SLP_TYPb is returned * - * RETURN: Status and the value read. + * RETURN: Status - ACPI status * - * DESCRIPTION: Read from the specified ACPI register + * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested sleep + * state. * ******************************************************************************/ acpi_status -acpi_hw_register_read(u32 register_id, u32 * return_value) +acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) { - u32 value1 = 0; - u32 value2 = 0; - acpi_status status; - - ACPI_FUNCTION_TRACE(hw_register_read); - - switch (register_id) { - case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ - - status = - acpi_hw_low_level_read(16, &value1, - &acpi_gbl_FADT.xpm1a_event_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ - - status = - acpi_hw_low_level_read(16, &value2, - &acpi_gbl_FADT.xpm1b_event_block); - value1 |= value2; - break; - - case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ - - status = - acpi_hw_low_level_read(16, &value1, &acpi_gbl_xpm1a_enable); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ - - status = - acpi_hw_low_level_read(16, &value2, &acpi_gbl_xpm1b_enable); - value1 |= value2; - break; - - case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ - - status = - acpi_hw_low_level_read(16, &value1, - &acpi_gbl_FADT.xpm1a_control_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - status = - acpi_hw_low_level_read(16, &value2, - &acpi_gbl_FADT.xpm1b_control_block); - value1 |= value2; - break; - - case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ - - status = - acpi_hw_low_level_read(8, &value1, - &acpi_gbl_FADT.xpm2_control_block); - break; - - case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ - - status = - acpi_hw_low_level_read(32, &value1, - &acpi_gbl_FADT.xpm_timer_block); - break; + acpi_status status = AE_OK; + struct acpi_evaluate_info *info; - case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + ACPI_FUNCTION_TRACE(acpi_get_sleep_type_data); - status = - acpi_os_read_port(acpi_gbl_FADT.smi_command, &value1, 8); - break; + /* Validate parameters */ - default: - ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); - status = AE_BAD_PARAMETER; - break; + if ((sleep_state > ACPI_S_STATES_MAX) || !sleep_type_a || !sleep_type_b) { + return_ACPI_STATUS(AE_BAD_PARAMETER); } - exit: + /* Allocate the evaluation information block */ - if (ACPI_SUCCESS(status)) { - *return_value = value1; + info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); + if (!info) { + return_ACPI_STATUS(AE_NO_MEMORY); } - return_ACPI_STATUS(status); -} - -/****************************************************************************** - * - * FUNCTION: acpi_hw_register_write - * - * PARAMETERS: register_id - ACPI Register ID - * Value - The value to write - * - * RETURN: Status - * - * DESCRIPTION: Write to the specified ACPI register - * - * NOTE: In accordance with the ACPI specification, this function automatically - * preserves the value of the following bits, meaning that these bits cannot be - * changed via this interface: - * - * PM1_CONTROL[0] = SCI_EN - * PM1_CONTROL[9] - * PM1_STATUS[11] - * - * ACPI References: - * 1) Hardware Ignored Bits: When software writes to a register with ignored - * bit fields, it preserves the ignored bit fields - * 2) SCI_EN: OSPM always preserves this bit position - * - ******************************************************************************/ - -acpi_status acpi_hw_register_write(u32 register_id, u32 value) -{ - acpi_status status; - u32 read_value; - - ACPI_FUNCTION_TRACE(hw_register_write); - - switch (register_id) { - case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ - - /* Perform a read first to preserve certain bits (per ACPI spec) */ - - status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, - &read_value); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* Insert the bits to be preserved */ - - ACPI_INSERT_BITS(value, ACPI_PM1_STATUS_PRESERVED_BITS, - read_value); - - /* Now we can write the data */ - - status = - acpi_hw_low_level_write(16, value, - &acpi_gbl_FADT.xpm1a_event_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ - - status = - acpi_hw_low_level_write(16, value, - &acpi_gbl_FADT.xpm1b_event_block); - break; - - case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ - - status = - acpi_hw_low_level_write(16, value, &acpi_gbl_xpm1a_enable); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* PM1B is optional */ - - status = - acpi_hw_low_level_write(16, value, &acpi_gbl_xpm1b_enable); - break; - - case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ - - /* - * Perform a read first to preserve certain bits (per ACPI spec) - */ - status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, - &read_value); - if (ACPI_FAILURE(status)) { - goto exit; - } - - /* Insert the bits to be preserved */ - - ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS, - read_value); - - /* Now we can write the data */ - - status = - acpi_hw_low_level_write(16, value, - &acpi_gbl_FADT.xpm1a_control_block); - if (ACPI_FAILURE(status)) { - goto exit; - } - - status = - acpi_hw_low_level_write(16, value, - &acpi_gbl_FADT.xpm1b_control_block); - break; - - case ACPI_REGISTER_PM1A_CONTROL: /* 16-bit access */ - - status = - acpi_hw_low_level_write(16, value, - &acpi_gbl_FADT.xpm1a_control_block); - break; - - case ACPI_REGISTER_PM1B_CONTROL: /* 16-bit access */ - - status = - acpi_hw_low_level_write(16, value, - &acpi_gbl_FADT.xpm1b_control_block); - break; - - case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ - - status = - acpi_hw_low_level_write(8, value, - &acpi_gbl_FADT.xpm2_control_block); - break; - - case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ - - status = - acpi_hw_low_level_write(32, value, - &acpi_gbl_FADT.xpm_timer_block); - break; - - case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + info->pathname = + ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]); - /* SMI_CMD is currently always in IO space */ + /* Evaluate the namespace object containing the values for this state */ - status = - acpi_os_write_port(acpi_gbl_FADT.smi_command, value, 8); - break; + status = acpi_ns_evaluate(info); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "%s while evaluating SleepState [%s]\n", + acpi_format_exception(status), + info->pathname)); - default: - status = AE_BAD_PARAMETER; - break; + goto cleanup; } - exit: - return_ACPI_STATUS(status); -} - -/****************************************************************************** - * - * FUNCTION: acpi_hw_low_level_read - * - * PARAMETERS: Width - 8, 16, or 32 - * Value - Where the value is returned - * Reg - GAS register structure - * - * RETURN: Status - * - * DESCRIPTION: Read from either memory or IO space. - * - ******************************************************************************/ - -acpi_status -acpi_hw_low_level_read(u32 width, u32 * value, struct acpi_generic_address *reg) -{ - u64 address; - acpi_status status; - - ACPI_FUNCTION_NAME(hw_low_level_read); + /* Must have a return object */ - /* - * Must have a valid pointer to a GAS structure, and - * a non-zero address within. However, don't return an error - * because the PM1A/B code must not fail if B isn't present. - */ - if (!reg) { - return (AE_OK); + if (!info->return_object) { + ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]", + info->pathname)); + status = AE_NOT_EXIST; } - /* Get a local copy of the address. Handles possible alignment issues */ + /* It must be of type Package */ - ACPI_MOVE_64_TO_64(&address, ®->address); - if (!address) { - return (AE_OK); + else if (ACPI_GET_OBJECT_TYPE(info->return_object) != ACPI_TYPE_PACKAGE) { + ACPI_ERROR((AE_INFO, + "Sleep State return object is not a Package")); + status = AE_AML_OPERAND_TYPE; } - *value = 0; /* - * Two address spaces supported: Memory or IO. - * PCI_Config is not supported here because the GAS struct is insufficient + * The package must have at least two elements. NOTE (March 2005): This + * goes against the current ACPI spec which defines this object as a + * package with one encoded DWORD element. However, existing practice + * by BIOS vendors seems to be to have 2 or more elements, at least + * one per sleep type (A/B). */ - switch (reg->space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - - status = acpi_os_read_memory((acpi_physical_address) address, - value, width); - break; - - case ACPI_ADR_SPACE_SYSTEM_IO: - - status = - acpi_os_read_port((acpi_io_address) address, value, width); - break; - - default: + else if (info->return_object->package.count < 2) { ACPI_ERROR((AE_INFO, - "Unsupported address space: %X", reg->space_id)); - return (AE_BAD_PARAMETER); + "Sleep State return package does not have at least two elements")); + status = AE_AML_NO_OPERAND; } - ACPI_DEBUG_PRINT((ACPI_DB_IO, - "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", - *value, width, ACPI_FORMAT_UINT64(address), - acpi_ut_get_region_name(reg->space_id))); - - return (status); -} - -/****************************************************************************** - * - * FUNCTION: acpi_hw_low_level_write - * - * PARAMETERS: Width - 8, 16, or 32 - * Value - To be written - * Reg - GAS register structure - * - * RETURN: Status - * - * DESCRIPTION: Write to either memory or IO space. - * - ******************************************************************************/ - -acpi_status -acpi_hw_low_level_write(u32 width, u32 value, struct acpi_generic_address * reg) -{ - u64 address; - acpi_status status; - - ACPI_FUNCTION_NAME(hw_low_level_write); - - /* - * Must have a valid pointer to a GAS structure, and - * a non-zero address within. However, don't return an error - * because the PM1A/B code must not fail if B isn't present. - */ - if (!reg) { - return (AE_OK); - } + /* The first two elements must both be of type Integer */ - /* Get a local copy of the address. Handles possible alignment issues */ + else if ((ACPI_GET_OBJECT_TYPE(info->return_object->package.elements[0]) + != ACPI_TYPE_INTEGER) || + (ACPI_GET_OBJECT_TYPE(info->return_object->package.elements[1]) + != ACPI_TYPE_INTEGER)) { + ACPI_ERROR((AE_INFO, + "Sleep State return package elements are not both Integers (%s, %s)", + acpi_ut_get_object_type_name(info->return_object-> + package.elements[0]), + acpi_ut_get_object_type_name(info->return_object-> + package.elements[1]))); + status = AE_AML_OPERAND_TYPE; + } else { + /* Valid _Sx_ package size, type, and value */ - ACPI_MOVE_64_TO_64(&address, ®->address); - if (!address) { - return (AE_OK); + *sleep_type_a = (u8) + (info->return_object->package.elements[0])->integer.value; + *sleep_type_b = (u8) + (info->return_object->package.elements[1])->integer.value; } - /* - * Two address spaces supported: Memory or IO. - * PCI_Config is not supported here because the GAS struct is insufficient - */ - switch (reg->space_id) { - case ACPI_ADR_SPACE_SYSTEM_MEMORY: - - status = acpi_os_write_memory((acpi_physical_address) address, - value, width); - break; - - case ACPI_ADR_SPACE_SYSTEM_IO: - - status = acpi_os_write_port((acpi_io_address) address, value, - width); - break; - - default: - ACPI_ERROR((AE_INFO, - "Unsupported address space: %X", reg->space_id)); - return (AE_BAD_PARAMETER); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While evaluating SleepState [%s], bad Sleep object %p type %s", + info->pathname, info->return_object, + acpi_ut_get_object_type_name(info-> + return_object))); } - ACPI_DEBUG_PRINT((ACPI_DB_IO, - "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", - value, width, ACPI_FORMAT_UINT64(address), - acpi_ut_get_region_name(reg->space_id))); + acpi_ut_remove_reference(info->return_object); - return (status); + cleanup: + ACPI_FREE(info); + return_ACPI_STATUS(status); } + +ACPI_EXPORT_SYMBOL(acpi_get_sleep_type_data) diff --git a/drivers/acpi/namespace/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index c39a7f68b889..88303ebe924c 100644 --- a/drivers/acpi/namespace/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> -#include <acpi/acdispat.h> +#include "accommon.h" +#include "amlcode.h" +#include "acnamesp.h" +#include "acdispat.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsaccess") @@ -165,12 +166,9 @@ acpi_status acpi_ns_root_initialize(void) obj_desc->method.method_flags = AML_METHOD_INTERNAL_ONLY; - -#ifndef ACPI_DUMP_APP obj_desc->method.implementation = acpi_ut_osi_implementation; #endif -#endif break; case ACPI_TYPE_INTEGER: @@ -521,11 +519,11 @@ acpi_ns_lookup(union acpi_generic_state *scope_info, } /* - * Search namespace for each segment of the name. Loop through and + * Search namespace for each segment of the name. Loop through and * verify (or add to the namespace) each name segment. * * The object type is significant only at the last name - * segment. (We don't care about the types along the path, only + * segment. (We don't care about the types along the path, only * the type of the final target object.) */ this_search_type = ACPI_TYPE_ANY; @@ -591,6 +589,10 @@ acpi_ns_lookup(union acpi_generic_state *scope_info, * segments). */ if (this_node->type == ACPI_TYPE_LOCAL_ALIAS) { + if (!this_node->object) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + if (acpi_ns_opens_scope (((struct acpi_namespace_node *)this_node-> object)->type)) { diff --git a/drivers/acpi/namespace/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index 3a1740ac2edc..f976d848fe82 100644 --- a/drivers/acpi/namespace/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsalloc") diff --git a/drivers/acpi/namespace/nsdump.c b/drivers/acpi/acpica/nsdump.c index cc0ae39440e4..0da33c8e9ba2 100644 --- a/drivers/acpi/namespace/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsdump") diff --git a/drivers/acpi/namespace/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index 428f50fde11a..41994fe7fbb8 100644 --- a/drivers/acpi/namespace/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -42,6 +42,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" /* TBD: This entire module is apparently obsolete and should be removed */ @@ -49,7 +50,7 @@ ACPI_MODULE_NAME("nsdumpdv") #ifdef ACPI_OBSOLETE_FUNCTIONS #if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) -#include <acpi/acnamesp.h> +#include "acnamesp.h" /******************************************************************************* * * FUNCTION: acpi_ns_dump_one_device diff --git a/drivers/acpi/namespace/nseval.c b/drivers/acpi/acpica/nseval.c index 4cdf03ac2b46..0f3d5f9b5966 100644 --- a/drivers/acpi/namespace/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acparser.h" +#include "acinterp.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nseval") @@ -89,6 +90,7 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) /* Initialize the return value to an invalid object */ info->return_object = NULL; + info->param_count = 0; /* * Get the actual namespace node for the target object. Handles these cases: @@ -141,41 +143,17 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) return_ACPI_STATUS(AE_NULL_OBJECT); } - /* - * Calculate the number of arguments being passed to the method - */ + /* Count the number of arguments being passed to the method */ - info->param_count = 0; if (info->parameters) { - while (info->parameters[info->param_count]) + while (info->parameters[info->param_count]) { + if (info->param_count > ACPI_METHOD_MAX_ARG) { + return_ACPI_STATUS(AE_LIMIT); + } info->param_count++; + } } - /* - * Warning if too few or too many arguments have been passed by the - * caller. We don't want to abort here with an error because an - * incorrect number of arguments may not cause the method to fail. - * However, the method will fail if there are too few arguments passed - * and the method attempts to use one of the missing ones. - */ - - if (info->param_count < info->obj_desc->method.param_count) { - ACPI_WARNING((AE_INFO, - "Insufficient arguments - " - "method [%4.4s] needs %d, found %d", - acpi_ut_get_node_name(info->resolved_node), - info->obj_desc->method.param_count, - info->param_count)); - } else if (info->param_count > - info->obj_desc->method.param_count) { - ACPI_WARNING((AE_INFO, - "Excess arguments - " - "method [%4.4s] needs %d, found %d", - acpi_ut_get_node_name(info-> - resolved_node), - info->obj_desc->method.param_count, - info->param_count)); - } ACPI_DUMP_PATHNAME(info->resolved_node, "Execute Method:", ACPI_LV_INFO, _COMPONENT); @@ -264,32 +242,13 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) } } - /* Validation of return values for ACPI-predefined methods and objects */ - - if ((status == AE_OK) || (status == AE_CTRL_RETURN_VALUE)) { - /* - * If this is the first evaluation, check the return value. This - * ensures that any warnings will only be emitted during the very - * first evaluation of the object. - */ - if (!(node->flags & ANOBJ_EVALUATED)) { - /* - * Check for a predefined ACPI name. If found, validate the - * returned object. - * - * Note: Ignore return status for now, emit warnings if there are - * problems with the returned object. May change later to abort - * the method on invalid return object. - */ - (void)acpi_ns_check_predefined_names(node, - info-> - return_object); - } - - /* Mark the node as having been evaluated */ - - node->flags |= ANOBJ_EVALUATED; - } + /* + * Check input argument count against the ASL-defined count for a method. + * Also check predefined names: argument count and return value against + * the ACPI specification. Some incorrect return value types are repaired. + */ + (void)acpi_ns_check_predefined_names(node, info->param_count, + status, &info->return_object); /* Check if there is a return value that must be dealt with */ diff --git a/drivers/acpi/namespace/nsinit.c b/drivers/acpi/acpica/nsinit.c index e4c57510d798..13501cb81863 100644 --- a/drivers/acpi/namespace/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acdispat.h" +#include "acinterp.h" #include <linux/nmi.h> #define _COMPONENT ACPI_NAMESPACE diff --git a/drivers/acpi/namespace/nsload.c b/drivers/acpi/acpica/nsload.c index a4a412b7c029..a0ba9e12379e 100644 --- a/drivers/acpi/namespace/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acdispat.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acdispat.h" +#include "actables.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsload") diff --git a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/acpica/nsnames.c index 42a39a7c96e9..ae3dc10a7e81 100644 --- a/drivers/acpi/namespace/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "amlcode.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsnames") diff --git a/drivers/acpi/namespace/nsobject.c b/drivers/acpi/acpica/nsobject.c index 15fe09e24f71..08a97a57f8f9 100644 --- a/drivers/acpi/namespace/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsobject") diff --git a/drivers/acpi/namespace/nsparse.c b/drivers/acpi/acpica/nsparse.c index a82271a9dbb3..b9e8d0070b6f 100644 --- a/drivers/acpi/namespace/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -42,10 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acparser.h> -#include <acpi/acdispat.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acparser.h" +#include "acdispat.h" +#include "actables.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsparse") diff --git a/drivers/acpi/namespace/nspredef.c b/drivers/acpi/acpica/nspredef.c index 0f17cf0898c9..452703290d35 100644 --- a/drivers/acpi/namespace/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -43,8 +43,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acpredef.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acpredef.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nspredef") @@ -72,7 +73,7 @@ ACPI_MODULE_NAME("nspredef") /* Local prototypes */ static acpi_status acpi_ns_check_package(char *pathname, - union acpi_operand_object *return_object, + union acpi_operand_object **return_object_ptr, const union acpi_predefined_info *predefined); static acpi_status @@ -82,13 +83,18 @@ acpi_ns_check_package_elements(char *pathname, static acpi_status acpi_ns_check_object_type(char *pathname, - union acpi_operand_object *return_object, + union acpi_operand_object **return_object_ptr, u32 expected_btypes, u32 package_index); static acpi_status acpi_ns_check_reference(char *pathname, union acpi_operand_object *return_object); +static acpi_status +acpi_ns_repair_object(u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr); + /* * Names for the types that can be returned by the predefined objects. * Used for warning messages. Must be in the same order as the ACPI_RTYPEs @@ -108,8 +114,8 @@ static const char *acpi_rtype_names[] = { * FUNCTION: acpi_ns_check_predefined_names * * PARAMETERS: Node - Namespace node for the method/object - * return_object - Object returned from the evaluation of this - * method/object + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object * * RETURN: Status * @@ -119,8 +125,11 @@ static const char *acpi_rtype_names[] = { acpi_status acpi_ns_check_predefined_names(struct acpi_namespace_node *node, - union acpi_operand_object *return_object) + u32 user_param_count, + acpi_status return_status, + union acpi_operand_object **return_object_ptr) { + union acpi_operand_object *return_object = *return_object_ptr; acpi_status status = AE_OK; const union acpi_predefined_info *predefined; char *pathname; @@ -128,12 +137,6 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, /* Match the name for this method/object against the predefined list */ predefined = acpi_ns_check_for_predefined_name(node); - if (!predefined) { - - /* Name was not one of the predefined names */ - - return (AE_OK); - } /* Get the full pathname to the object, for use in error messages */ @@ -143,10 +146,37 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, } /* - * Check that the parameter count for this method is in accordance - * with the ACPI specification. + * Check that the parameter count for this method matches the ASL + * definition. For predefined names, ensure that both the caller and + * the method itself are in accordance with the ACPI specification. */ - acpi_ns_check_parameter_count(pathname, node, predefined); + acpi_ns_check_parameter_count(pathname, node, user_param_count, + predefined); + + /* If not a predefined name, we cannot validate the return object */ + + if (!predefined) { + goto exit; + } + + /* If the method failed, we cannot validate the return object */ + + if ((return_status != AE_OK) && (return_status != AE_CTRL_RETURN_VALUE)) { + goto exit; + } + + /* + * Only validate the return value on the first successful evaluation of + * the method. This ensures that any warnings will only be emitted during + * the very first evaluation of the method/object. + */ + if (node->flags & ANOBJ_EVALUATED) { + goto exit; + } + + /* Mark the node as having been successfully evaluated */ + + node->flags |= ANOBJ_EVALUATED; /* * If there is no return value, check if we require a return value for @@ -171,7 +201,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * We have a return value, but if one wasn't expected, just exit, this is * not a problem * - * For example, if "Implicit return value" is enabled, methods will + * For example, if the "Implicit Return" feature is enabled, methods will * always return a value */ if (!predefined->info.expected_btypes) { @@ -182,7 +212,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * Check that the type of the return object is what is expected for * this predefined name */ - status = acpi_ns_check_object_type(pathname, return_object, + status = acpi_ns_check_object_type(pathname, return_object_ptr, predefined->info.expected_btypes, ACPI_NOT_PACKAGE); if (ACPI_FAILURE(status)) { @@ -193,11 +223,12 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, if (ACPI_GET_OBJECT_TYPE(return_object) == ACPI_TYPE_PACKAGE) { status = - acpi_ns_check_package(pathname, return_object, predefined); + acpi_ns_check_package(pathname, return_object_ptr, + predefined); } exit: - if (pathname) { + if (pathname != predefined->info.name) { ACPI_FREE(pathname); } @@ -210,6 +241,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * * PARAMETERS: Pathname - Full pathname to the node (for error msgs) * Node - Namespace node for the method/object + * user_param_count - Number of args passed in by the caller * Predefined - Pointer to entry in predefined name table * * RETURN: None @@ -223,32 +255,76 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, void acpi_ns_check_parameter_count(char *pathname, struct acpi_namespace_node *node, + u32 user_param_count, const union acpi_predefined_info *predefined) { u32 param_count; u32 required_params_current; u32 required_params_old; - /* - * Check that the ASL-defined parameter count is what is expected for - * this predefined name. - * - * Methods have 0-7 parameters. All other types have zero. - */ + /* Methods have 0-7 parameters. All other types have zero. */ + param_count = 0; if (node->type == ACPI_TYPE_METHOD) { param_count = node->object->method.param_count; } - /* Validate parameter count - allow two different legal counts (_SCP) */ + /* Argument count check for non-predefined methods/objects */ + + if (!predefined) { + /* + * Warning if too few or too many arguments have been passed by the + * caller. An incorrect number of arguments may not cause the method + * to fail. However, the method will fail if there are too few + * arguments and the method attempts to use one of the missing ones. + */ + if (user_param_count < param_count) { + ACPI_WARNING((AE_INFO, + "%s: Insufficient arguments - needs %d, found %d", + pathname, param_count, user_param_count)); + } else if (user_param_count > param_count) { + ACPI_WARNING((AE_INFO, + "%s: Excess arguments - needs %d, found %d", + pathname, param_count, user_param_count)); + } + return; + } + + /* Allow two different legal argument counts (_SCP, etc.) */ required_params_current = predefined->info.param_count & 0x0F; required_params_old = predefined->info.param_count >> 4; + if (user_param_count != ACPI_UINT32_MAX) { + + /* Validate the user-supplied parameter count */ + + if ((user_param_count != required_params_current) && + (user_param_count != required_params_old)) { + ACPI_WARNING((AE_INFO, + "%s: Parameter count mismatch - caller passed %d, ACPI requires %d", + pathname, user_param_count, + required_params_current)); + } + } + + /* + * Only validate the argument count on the first successful evaluation of + * the method. This ensures that any warnings will only be emitted during + * the very first evaluation of the method/object. + */ + if (node->flags & ANOBJ_EVALUATED) { + return; + } + + /* + * Check that the ASL-defined parameter count is what is expected for + * this predefined name. + */ if ((param_count != required_params_current) && (param_count != required_params_old)) { ACPI_WARNING((AE_INFO, - "%s: Parameter count mismatch - ASL declared %d, expected %d", + "%s: Parameter count mismatch - ASL declared %d, ACPI requires %d", pathname, param_count, required_params_current)); } } @@ -307,8 +383,8 @@ const union acpi_predefined_info *acpi_ns_check_for_predefined_name(struct * FUNCTION: acpi_ns_check_package * * PARAMETERS: Pathname - Full pathname to the node (for error msgs) - * return_object - Object returned from the evaluation of a - * method or object + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object * Predefined - Pointer to entry in predefined name table * * RETURN: Status @@ -320,9 +396,10 @@ const union acpi_predefined_info *acpi_ns_check_for_predefined_name(struct static acpi_status acpi_ns_check_package(char *pathname, - union acpi_operand_object *return_object, + union acpi_operand_object **return_object_ptr, const union acpi_predefined_info *predefined) { + union acpi_operand_object *return_object = *return_object_ptr; const union acpi_predefined_info *package; union acpi_operand_object *sub_package; union acpi_operand_object **elements; @@ -408,7 +485,7 @@ acpi_ns_check_package(char *pathname, * elements must be of the same type */ for (i = 0; i < count; i++) { - status = acpi_ns_check_object_type(pathname, *elements, + status = acpi_ns_check_object_type(pathname, elements, package->ret_info. object_type1, i); if (ACPI_FAILURE(status)) { @@ -441,7 +518,7 @@ acpi_ns_check_package(char *pathname, status = acpi_ns_check_object_type(pathname, - *elements, + elements, package-> ret_info3. object_type[i], @@ -454,7 +531,7 @@ acpi_ns_check_package(char *pathname, status = acpi_ns_check_object_type(pathname, - *elements, + elements, package-> ret_info3. tail_object_type, @@ -471,7 +548,7 @@ acpi_ns_check_package(char *pathname, /* First element is the (Integer) count of sub-packages to follow */ - status = acpi_ns_check_object_type(pathname, *elements, + status = acpi_ns_check_object_type(pathname, elements, ACPI_RTYPE_INTEGER, 0); if (ACPI_FAILURE(status)) { return (status); @@ -509,7 +586,7 @@ acpi_ns_check_package(char *pathname, /* Each sub-object must be of type Package */ status = - acpi_ns_check_object_type(pathname, sub_package, + acpi_ns_check_object_type(pathname, &sub_package, ACPI_RTYPE_PACKAGE, i); if (ACPI_FAILURE(status)) { return (status); @@ -567,12 +644,8 @@ acpi_ns_check_package(char *pathname, for (j = 0; j < expected_count; j++) { status = acpi_ns_check_object_type(pathname, - sub_elements - [j], - package-> - ret_info2. - object_type - [j], j); + &sub_elements[j], + package->ret_info2.object_type[j], j); if (ACPI_FAILURE(status)) { return (status); } @@ -611,7 +684,7 @@ acpi_ns_check_package(char *pathname, status = acpi_ns_check_object_type(pathname, - *sub_elements, + sub_elements, ACPI_RTYPE_INTEGER, 0); if (ACPI_FAILURE(status)) { @@ -708,7 +781,7 @@ acpi_ns_check_package_elements(char *pathname, * The second group can have a count of zero. */ for (i = 0; i < count1; i++) { - status = acpi_ns_check_object_type(pathname, *this_element, + status = acpi_ns_check_object_type(pathname, this_element, type1, i); if (ACPI_FAILURE(status)) { return (status); @@ -717,7 +790,7 @@ acpi_ns_check_package_elements(char *pathname, } for (i = 0; i < count2; i++) { - status = acpi_ns_check_object_type(pathname, *this_element, + status = acpi_ns_check_object_type(pathname, this_element, type2, (i + count1)); if (ACPI_FAILURE(status)) { return (status); @@ -733,8 +806,8 @@ acpi_ns_check_package_elements(char *pathname, * FUNCTION: acpi_ns_check_object_type * * PARAMETERS: Pathname - Full pathname to the node (for error msgs) - * return_object - Object return from the execution of this - * method/object + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object * expected_btypes - Bitmap of expected return type(s) * package_index - Index of object within parent package (if * applicable - ACPI_NOT_PACKAGE otherwise) @@ -748,9 +821,10 @@ acpi_ns_check_package_elements(char *pathname, static acpi_status acpi_ns_check_object_type(char *pathname, - union acpi_operand_object *return_object, + union acpi_operand_object **return_object_ptr, u32 expected_btypes, u32 package_index) { + union acpi_operand_object *return_object = *return_object_ptr; acpi_status status = AE_OK; u32 return_btype; char type_buffer[48]; /* Room for 5 types */ @@ -814,6 +888,14 @@ acpi_ns_check_object_type(char *pathname, /* Is the object one of the expected types? */ if (!(return_btype & expected_btypes)) { + + /* Type mismatch -- attempt repair of the returned object */ + + status = acpi_ns_repair_object(expected_btypes, package_index, + return_object_ptr); + if (ACPI_SUCCESS(status)) { + return (status); + } goto type_error_exit; } @@ -898,3 +980,86 @@ acpi_ns_check_reference(char *pathname, return (AE_AML_OPERAND_TYPE); } + +/******************************************************************************* + * + * FUNCTION: acpi_ns_repair_object + * + * PARAMETERS: Pathname - Full pathname to the node (for error msgs) + * package_index - Used to determine if target is in a package + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if repair was successful. + * + * DESCRIPTION: Attempt to repair/convert a return object of a type that was + * not expected. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_repair_object(u32 expected_btypes, + u32 package_index, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *new_object; + acpi_size length; + + switch (ACPI_GET_OBJECT_TYPE(return_object)) { + case ACPI_TYPE_BUFFER: + + if (!(expected_btypes & ACPI_RTYPE_STRING)) { + return (AE_AML_OPERAND_TYPE); + } + + /* + * Have a Buffer, expected a String, convert. Use a to_string + * conversion, no transform performed on the buffer data. The best + * example of this is the _BIF method, where the string data from + * the battery is often (incorrectly) returned as buffer object(s). + */ + length = 0; + while ((length < return_object->buffer.length) && + (return_object->buffer.pointer[length])) { + length++; + } + + /* Allocate a new string object */ + + new_object = acpi_ut_create_string_object(length); + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* + * Copy the raw buffer data with no transform. String is already NULL + * terminated at Length+1. + */ + ACPI_MEMCPY(new_object->string.pointer, + return_object->buffer.pointer, length); + + /* Install the new return object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_object; + + /* + * If the object is a package element, we need to: + * 1. Decrement the reference count of the orignal object, it was + * incremented when building the package + * 2. Increment the reference count of the new object, it will be + * decremented when releasing the package + */ + if (package_index != ACPI_NOT_PACKAGE) { + acpi_ut_remove_reference(return_object); + acpi_ut_add_reference(new_object); + } + return (AE_OK); + + default: + break; + } + + return (AE_AML_OPERAND_TYPE); +} diff --git a/drivers/acpi/namespace/nssearch.c b/drivers/acpi/acpica/nssearch.c index a9a80bf811b3..6fea13f3f52d 100644 --- a/drivers/acpi/namespace/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nssearch") diff --git a/drivers/acpi/namespace/nsutils.c b/drivers/acpi/acpica/nsutils.c index b0817e1127b1..3e1149bf4aa5 100644 --- a/drivers/acpi/namespace/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -43,9 +43,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/amlcode.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acnamesp.h" +#include "amlcode.h" +#include "actables.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsutils") @@ -314,9 +315,15 @@ void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info) * * strlen() + 1 covers the first name_seg, which has no path separator */ - if (acpi_ns_valid_root_prefix(next_external_char[0])) { + if (acpi_ns_valid_root_prefix(*next_external_char)) { info->fully_qualified = TRUE; next_external_char++; + + /* Skip redundant root_prefix, like \\_SB.PCI0.SBRG.EC0 */ + + while (acpi_ns_valid_root_prefix(*next_external_char)) { + next_external_char++; + } } else { /* * Handle Carat prefixes diff --git a/drivers/acpi/namespace/nswalk.c b/drivers/acpi/acpica/nswalk.c index 3c905ce26d7d..200895fa2728 100644 --- a/drivers/acpi/namespace/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nswalk") diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index a085cc39c055..22a7171ac1ed 100644 --- a/drivers/acpi/namespace/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -43,8 +43,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfeval") diff --git a/drivers/acpi/namespace/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 5efa4e7ddb0b..9589fea24997 100644 --- a/drivers/acpi/namespace/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfname") diff --git a/drivers/acpi/namespace/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index 2b375ee80cef..1c7efc15225f 100644 --- a/drivers/acpi/namespace/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -43,7 +43,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfobj") diff --git a/drivers/acpi/parser/psargs.c b/drivers/acpi/acpica/psargs.c index d830b29b85b1..b161f3544b51 100644 --- a/drivers/acpi/parser/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -42,10 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> -#include <acpi/acdispat.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" +#include "acnamesp.h" +#include "acdispat.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psargs") diff --git a/drivers/acpi/parser/psloop.c b/drivers/acpi/acpica/psloop.c index 4647039a0d8a..c5f6ce19a401 100644 --- a/drivers/acpi/parser/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -50,9 +50,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acdispat.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "amlcode.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psloop") diff --git a/drivers/acpi/parser/psopcode.c b/drivers/acpi/acpica/psopcode.c index f425ab30eae8..3bc3a60194d6 100644 --- a/drivers/acpi/parser/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acopcode.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acparser.h" +#include "acopcode.h" +#include "amlcode.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psopcode") diff --git a/drivers/acpi/parser/psparse.c b/drivers/acpi/acpica/psparse.c index 68e932f215ea..70838e9b608c 100644 --- a/drivers/acpi/parser/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -51,11 +51,12 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acdispat.h> -#include <acpi/amlcode.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "amlcode.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psparse") @@ -447,10 +448,22 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state) walk_state, walk_state->parser_state.aml, walk_state->parser_state.aml_size)); + if (!walk_state->parser_state.aml) { + return_ACPI_STATUS(AE_NULL_OBJECT); + } + /* Create and initialize a new thread state */ thread = acpi_ut_create_thread_state(); if (!thread) { + if (walk_state->method_desc) { + + /* Executing a control method - additional cleanup */ + + acpi_ds_terminate_control_method( + walk_state->method_desc, walk_state); + } + acpi_ds_delete_walk_state(walk_state); return_ACPI_STATUS(AE_NO_MEMORY); } diff --git a/drivers/acpi/parser/psscope.c b/drivers/acpi/acpica/psscope.c index ee50e67c9443..2feca5ca9581 100644 --- a/drivers/acpi/parser/psscope.c +++ b/drivers/acpi/acpica/psscope.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> +#include "accommon.h" +#include "acparser.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psscope") diff --git a/drivers/acpi/parser/pstree.c b/drivers/acpi/acpica/pstree.c index 1dd355ddd182..4d3389118ec3 100644 --- a/drivers/acpi/parser/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("pstree") diff --git a/drivers/acpi/parser/psutils.c b/drivers/acpi/acpica/psutils.c index 7cf1f65cd5bb..e636e078ad3d 100644 --- a/drivers/acpi/parser/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/amlcode.h> +#include "accommon.h" +#include "acparser.h" +#include "amlcode.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psutils") diff --git a/drivers/acpi/parser/pswalk.c b/drivers/acpi/acpica/pswalk.c index 8b86ad5a3201..78b8b791f2ae 100644 --- a/drivers/acpi/parser/pswalk.c +++ b/drivers/acpi/acpica/pswalk.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> +#include "accommon.h" +#include "acparser.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("pswalk") diff --git a/drivers/acpi/parser/psxface.c b/drivers/acpi/acpica/psxface.c index 270469aae842..ff06032c0f06 100644 --- a/drivers/acpi/parser/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -42,9 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acparser.h> -#include <acpi/acdispat.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psxface") @@ -278,6 +280,38 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) goto cleanup; } + /* Invoke an internal method if necessary */ + + if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { + status = info->obj_desc->method.implementation(walk_state); + info->return_object = walk_state->return_desc; + + /* Cleanup states */ + + acpi_ds_scope_stack_clear(walk_state); + acpi_ps_cleanup_scope(&walk_state->parser_state); + acpi_ds_terminate_control_method(walk_state->method_desc, + walk_state); + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + /* + * Start method evaluation with an implicit return of zero. + * This is done for Windows compatibility. + */ + if (acpi_gbl_enable_interpreter_slack) { + walk_state->implicit_return_obj = + acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!walk_state->implicit_return_obj) { + status = AE_NO_MEMORY; + acpi_ds_delete_walk_state(walk_state); + goto cleanup; + } + + walk_state->implicit_return_obj->integer.value = 0; + } + /* Parse the AML */ status = acpi_ps_parse_aml(walk_state); diff --git a/drivers/acpi/resources/rsaddr.c b/drivers/acpi/acpica/rsaddr.c index 7f96332822bf..1e437bfd8db5 100644 --- a/drivers/acpi/resources/rsaddr.c +++ b/drivers/acpi/acpica/rsaddr.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsaddr") diff --git a/drivers/acpi/resources/rscalc.c b/drivers/acpi/acpica/rscalc.c index 8eaaecf92009..52865ee6bc77 100644 --- a/drivers/acpi/resources/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acresrc.h" +#include "acnamesp.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rscalc") diff --git a/drivers/acpi/resources/rscreate.c b/drivers/acpi/acpica/rscreate.c index 08b8d73e6ee5..61566b1a0616 100644 --- a/drivers/acpi/resources/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acresrc.h" +#include "acnamesp.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rscreate") diff --git a/drivers/acpi/resources/rsdump.c b/drivers/acpi/acpica/rsdump.c index 6bbbb7b8941a..3f0ca5a12d34 100644 --- a/drivers/acpi/resources/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsdump") diff --git a/drivers/acpi/resources/rsinfo.c b/drivers/acpi/acpica/rsinfo.c index 3f0a1fedbe0e..77b25fdb459c 100644 --- a/drivers/acpi/resources/rsinfo.c +++ b/drivers/acpi/acpica/rsinfo.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsinfo") diff --git a/drivers/acpi/resources/rsio.c b/drivers/acpi/acpica/rsio.c index b66d42e7402e..35a49aa95609 100644 --- a/drivers/acpi/resources/rsio.c +++ b/drivers/acpi/acpica/rsio.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsio") diff --git a/drivers/acpi/resources/rsirq.c b/drivers/acpi/acpica/rsirq.c index a8805efc0366..2e0256983aa6 100644 --- a/drivers/acpi/resources/rsirq.c +++ b/drivers/acpi/acpica/rsirq.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsirq") diff --git a/drivers/acpi/resources/rslist.c b/drivers/acpi/acpica/rslist.c index b78c7e797a19..1b1dbc69f087 100644 --- a/drivers/acpi/resources/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rslist") diff --git a/drivers/acpi/resources/rsmemory.c b/drivers/acpi/acpica/rsmemory.c index 63b21abd90bb..ddc76cebdc92 100644 --- a/drivers/acpi/resources/rsmemory.c +++ b/drivers/acpi/acpica/rsmemory.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsmemory") diff --git a/drivers/acpi/resources/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index 96a6c0353255..5bc49a553284 100644 --- a/drivers/acpi/resources/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsmisc") diff --git a/drivers/acpi/resources/rsutils.c b/drivers/acpi/acpica/rsutils.c index f7b3bcd59ba7..bc03d5966829 100644 --- a/drivers/acpi/resources/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acresrc.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acresrc.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsutils") diff --git a/drivers/acpi/resources/rsxface.c b/drivers/acpi/acpica/rsxface.c index f59f4c4e034c..69a2aa5b5d83 100644 --- a/drivers/acpi/resources/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acresrc.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acresrc.h" +#include "acnamesp.h" #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsxface") diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 2817158fb6a1..3636e4f8fb73 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -42,15 +42,16 @@ */ #include <acpi/acpi.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "actables.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbfadt") /* Local prototypes */ -static void inline +static inline void acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, - u8 byte_width, u64 address); + u8 space_id, u8 byte_width, u64 address); static void acpi_tb_convert_fadt(void); @@ -60,9 +61,10 @@ static void acpi_tb_validate_fadt(void); typedef struct acpi_fadt_info { char *name; - u8 target; - u8 source; + u8 address64; + u8 address32; u8 length; + u8 default_length; u8 type; } acpi_fadt_info; @@ -71,37 +73,61 @@ typedef struct acpi_fadt_info { #define ACPI_FADT_SEPARATE_LENGTH 2 static struct acpi_fadt_info fadt_info_table[] = { - {"Pm1aEventBlock", ACPI_FADT_OFFSET(xpm1a_event_block), + {"Pm1aEventBlock", + ACPI_FADT_OFFSET(xpm1a_event_block), ACPI_FADT_OFFSET(pm1a_event_block), - ACPI_FADT_OFFSET(pm1_event_length), ACPI_FADT_REQUIRED}, + ACPI_FADT_OFFSET(pm1_event_length), + ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */ + ACPI_FADT_REQUIRED}, - {"Pm1bEventBlock", ACPI_FADT_OFFSET(xpm1b_event_block), + {"Pm1bEventBlock", + ACPI_FADT_OFFSET(xpm1b_event_block), ACPI_FADT_OFFSET(pm1b_event_block), - ACPI_FADT_OFFSET(pm1_event_length), 0}, + ACPI_FADT_OFFSET(pm1_event_length), + ACPI_PM1_REGISTER_WIDTH * 2, /* Enable + Status register */ + 0}, - {"Pm1aControlBlock", ACPI_FADT_OFFSET(xpm1a_control_block), + {"Pm1aControlBlock", + ACPI_FADT_OFFSET(xpm1a_control_block), ACPI_FADT_OFFSET(pm1a_control_block), - ACPI_FADT_OFFSET(pm1_control_length), ACPI_FADT_REQUIRED}, + ACPI_FADT_OFFSET(pm1_control_length), + ACPI_PM1_REGISTER_WIDTH, + ACPI_FADT_REQUIRED}, - {"Pm1bControlBlock", ACPI_FADT_OFFSET(xpm1b_control_block), + {"Pm1bControlBlock", + ACPI_FADT_OFFSET(xpm1b_control_block), ACPI_FADT_OFFSET(pm1b_control_block), - ACPI_FADT_OFFSET(pm1_control_length), 0}, + ACPI_FADT_OFFSET(pm1_control_length), + ACPI_PM1_REGISTER_WIDTH, + 0}, - {"Pm2ControlBlock", ACPI_FADT_OFFSET(xpm2_control_block), + {"Pm2ControlBlock", + ACPI_FADT_OFFSET(xpm2_control_block), ACPI_FADT_OFFSET(pm2_control_block), - ACPI_FADT_OFFSET(pm2_control_length), ACPI_FADT_SEPARATE_LENGTH}, + ACPI_FADT_OFFSET(pm2_control_length), + ACPI_PM2_REGISTER_WIDTH, + ACPI_FADT_SEPARATE_LENGTH}, - {"PmTimerBlock", ACPI_FADT_OFFSET(xpm_timer_block), + {"PmTimerBlock", + ACPI_FADT_OFFSET(xpm_timer_block), ACPI_FADT_OFFSET(pm_timer_block), - ACPI_FADT_OFFSET(pm_timer_length), ACPI_FADT_REQUIRED}, + ACPI_FADT_OFFSET(pm_timer_length), + ACPI_PM_TIMER_WIDTH, + ACPI_FADT_REQUIRED}, - {"Gpe0Block", ACPI_FADT_OFFSET(xgpe0_block), + {"Gpe0Block", + ACPI_FADT_OFFSET(xgpe0_block), ACPI_FADT_OFFSET(gpe0_block), - ACPI_FADT_OFFSET(gpe0_block_length), ACPI_FADT_SEPARATE_LENGTH}, + ACPI_FADT_OFFSET(gpe0_block_length), + 0, + ACPI_FADT_SEPARATE_LENGTH}, - {"Gpe1Block", ACPI_FADT_OFFSET(xgpe1_block), + {"Gpe1Block", + ACPI_FADT_OFFSET(xgpe1_block), ACPI_FADT_OFFSET(gpe1_block), - ACPI_FADT_OFFSET(gpe1_block_length), ACPI_FADT_SEPARATE_LENGTH} + ACPI_FADT_OFFSET(gpe1_block_length), + 0, + ACPI_FADT_SEPARATE_LENGTH} }; #define ACPI_FADT_INFO_ENTRIES (sizeof (fadt_info_table) / sizeof (struct acpi_fadt_info)) @@ -122,9 +148,9 @@ static struct acpi_fadt_info fadt_info_table[] = { * ******************************************************************************/ -static void inline +static inline void acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, - u8 byte_width, u64 address) + u8 space_id, u8 byte_width, u64 address) { /* @@ -135,10 +161,10 @@ acpi_tb_init_generic_address(struct acpi_generic_address *generic_address, /* All other fields are byte-wide */ - generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO; - generic_address->bit_width = byte_width << 3; + generic_address->space_id = space_id; + generic_address->bit_width = (u8)ACPI_MUL_8(byte_width); generic_address->bit_offset = 0; - generic_address->access_width = 0; + generic_address->access_width = 0; /* Access width ANY */ } /******************************************************************************* @@ -225,7 +251,8 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) */ if (length > sizeof(struct acpi_table_fadt)) { ACPI_WARNING((AE_INFO, - "FADT (revision %u) is longer than ACPI 2.0 version, truncating length 0x%X to 0x%zX", + "FADT (revision %u) is longer than ACPI 2.0 version, " + "truncating length 0x%X to 0x%zX", table->revision, (unsigned)length, sizeof(struct acpi_table_fadt))); } @@ -244,7 +271,6 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) * 2) Validate some of the important values within the FADT */ acpi_tb_convert_fadt(); - acpi_tb_validate_fadt(); } /******************************************************************************* @@ -278,22 +304,36 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length) static void acpi_tb_convert_fadt(void) { - u8 pm1_register_length; - struct acpi_generic_address *target; + u8 pm1_register_bit_width; + u8 pm1_register_byte_width; + struct acpi_generic_address *target64; u32 i; /* Update the local FADT table header length */ acpi_gbl_FADT.header.length = sizeof(struct acpi_table_fadt); - /* Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary */ - + /* + * Expand the 32-bit FACS and DSDT addresses to 64-bit as necessary. + * Later code will always use the X 64-bit field. Also, check for an + * address mismatch between the 32-bit and 64-bit address fields + * (FIRMWARE_CTRL/X_FIRMWARE_CTRL, DSDT/X_DSDT) which would indicate + * the presence of two FACS or two DSDT tables. + */ if (!acpi_gbl_FADT.Xfacs) { acpi_gbl_FADT.Xfacs = (u64) acpi_gbl_FADT.facs; + } else if (acpi_gbl_FADT.facs && + (acpi_gbl_FADT.Xfacs != (u64) acpi_gbl_FADT.facs)) { + ACPI_WARNING((AE_INFO, + "32/64 FACS address mismatch in FADT - two FACS tables!")); } if (!acpi_gbl_FADT.Xdsdt) { acpi_gbl_FADT.Xdsdt = (u64) acpi_gbl_FADT.dsdt; + } else if (acpi_gbl_FADT.dsdt && + (acpi_gbl_FADT.Xdsdt != (u64) acpi_gbl_FADT.dsdt)) { + ACPI_WARNING((AE_INFO, + "32/64 DSDT address mismatch in FADT - two DSDT tables!")); } /* @@ -312,18 +352,23 @@ static void acpi_tb_convert_fadt(void) } /* - * Expand the ACPI 1.0 32-bit V1.0 addresses to the ACPI 2.0 64-bit "X" - * generic address structures as necessary. + * Expand the ACPI 1.0 32-bit addresses to the ACPI 2.0 64-bit "X" + * generic address structures as necessary. Later code will always use + * the 64-bit address structures. */ for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { - target = + target64 = ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT, - fadt_info_table[i].target); + fadt_info_table[i].address64); - /* Expand only if the X target is null */ + /* Expand only if the 64-bit X target is null */ - if (!target->address) { - acpi_tb_init_generic_address(target, + if (!target64->address) { + + /* The space_id is always I/O for the 32-bit legacy address fields */ + + acpi_tb_init_generic_address(target64, + ACPI_ADR_SPACE_SYSTEM_IO, *ACPI_ADD_PTR(u8, &acpi_gbl_FADT, fadt_info_table @@ -332,11 +377,64 @@ static void acpi_tb_convert_fadt(void) &acpi_gbl_FADT, fadt_info_table [i]. - source)); + address32)); + } + } + + /* Validate FADT values now, before we make any changes */ + + acpi_tb_validate_fadt(); + + /* + * Optionally check all register lengths against the default values and + * update them if they are incorrect. + */ + if (acpi_gbl_use_default_register_widths) { + for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { + target64 = + ACPI_ADD_PTR(struct acpi_generic_address, + &acpi_gbl_FADT, + fadt_info_table[i].address64); + + /* + * If a valid register (Address != 0) and the (default_length > 0) + * (Not a GPE register), then check the width against the default. + */ + if ((target64->address) && + (fadt_info_table[i].default_length > 0) && + (fadt_info_table[i].default_length != + target64->bit_width)) { + ACPI_WARNING((AE_INFO, + "Invalid length for %s: %d, using default %d", + fadt_info_table[i].name, + target64->bit_width, + fadt_info_table[i]. + default_length)); + + /* Incorrect size, set width to the default */ + + target64->bit_width = + fadt_info_table[i].default_length; + } } } /* + * Get the length of the individual PM1 registers (enable and status). + * Each register is defined to be (event block length / 2). + */ + pm1_register_bit_width = + (u8)ACPI_DIV_2(acpi_gbl_FADT.xpm1a_event_block.bit_width); + pm1_register_byte_width = (u8)ACPI_DIV_8(pm1_register_bit_width); + + /* + * Adjust the lengths of the PM1 Event Blocks so that they can be used to + * access the PM1 status register(s). Use (width / 2) + */ + acpi_gbl_FADT.xpm1a_event_block.bit_width = pm1_register_bit_width; + acpi_gbl_FADT.xpm1b_event_block.bit_width = pm1_register_bit_width; + + /* * Calculate separate GAS structs for the PM1 Enable registers. * These addresses do not appear (directly) in the FADT, so it is * useful to calculate them once, here. @@ -356,14 +454,14 @@ static void acpi_tb_convert_fadt(void) " PM1_EVT_LEN (%u)\n", acpi_gbl_FADT.xpm1a_event_block.bit_width, acpi_gbl_FADT.pm1_event_length); - pm1_register_length = (u8) ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length); /* The PM1A register block is required */ acpi_tb_init_generic_address(&acpi_gbl_xpm1a_enable, - pm1_register_length, + acpi_gbl_FADT.xpm1a_event_block.space_id, + pm1_register_byte_width, (acpi_gbl_FADT.xpm1a_event_block.address + - pm1_register_length)); + pm1_register_byte_width)); /* Don't forget to copy space_id of the GAS */ acpi_gbl_xpm1a_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id; @@ -379,9 +477,10 @@ static void acpi_tb_convert_fadt(void) acpi_gbl_FADT.xpm1b_event_block.bit_width, acpi_gbl_FADT.pm1_event_length); acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable, - pm1_register_length, + acpi_gbl_FADT.xpm1b_event_block.space_id, + pm1_register_byte_width, (acpi_gbl_FADT.xpm1b_event_block. - address + pm1_register_length)); + address + pm1_register_byte_width)); /* Don't forget to copy space_id of the GAS */ acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1b_event_block.space_id; @@ -411,26 +510,63 @@ static void acpi_tb_convert_fadt(void) static void acpi_tb_validate_fadt(void) { + char *name; u32 *address32; struct acpi_generic_address *address64; u8 length; u32 i; - /* Examine all of the 64-bit extended address fields (X fields) */ + /* + * Check for FACS and DSDT address mismatches. An address mismatch between + * the 32-bit and 64-bit address fields (FIRMWARE_CTRL/X_FIRMWARE_CTRL and + * DSDT/X_DSDT) would indicate the presence of two FACS or two DSDT tables. + */ + if (acpi_gbl_FADT.facs && + (acpi_gbl_FADT.Xfacs != (u64) acpi_gbl_FADT.facs)) { + ACPI_WARNING((AE_INFO, + "32/64X FACS address mismatch in FADT - " + "two FACS tables! %8.8X/%8.8X%8.8X", + acpi_gbl_FADT.facs, + ACPI_FORMAT_UINT64(acpi_gbl_FADT.Xfacs))); + } - for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { + if (acpi_gbl_FADT.dsdt && + (acpi_gbl_FADT.Xdsdt != (u64) acpi_gbl_FADT.dsdt)) { + ACPI_WARNING((AE_INFO, + "32/64X DSDT address mismatch in FADT - " + "two DSDT tables! %8.8X/%8.8X%8.8X", + acpi_gbl_FADT.dsdt, + ACPI_FORMAT_UINT64(acpi_gbl_FADT.Xdsdt))); + } - /* Generate pointers to the 32-bit and 64-bit addresses and get the length */ + /* Examine all of the 64-bit extended address fields (X fields) */ - address64 = - ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT, - fadt_info_table[i].target); + for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) { + /* + * Generate pointers to the 32-bit and 64-bit addresses, get the + * register length (width), and the register name + */ + address64 = ACPI_ADD_PTR(struct acpi_generic_address, + &acpi_gbl_FADT, + fadt_info_table[i].address64); address32 = ACPI_ADD_PTR(u32, &acpi_gbl_FADT, - fadt_info_table[i].source); + fadt_info_table[i].address32); length = *ACPI_ADD_PTR(u8, &acpi_gbl_FADT, fadt_info_table[i].length); + name = fadt_info_table[i].name; + + /* + * For each extended field, check for length mismatch between the + * legacy length field and the corresponding 64-bit X length field. + */ + if (address64 && (address64->bit_width != ACPI_MUL_8(length))) { + ACPI_WARNING((AE_INFO, + "32/64X length mismatch in %s: %d/%d", + name, ACPI_MUL_8(length), + address64->bit_width)); + } if (fadt_info_table[i].type & ACPI_FADT_REQUIRED) { /* @@ -439,8 +575,8 @@ static void acpi_tb_validate_fadt(void) */ if (!address64->address || !length) { ACPI_ERROR((AE_INFO, - "Required field \"%s\" has zero address and/or length: %8.8X%8.8X/%X", - fadt_info_table[i].name, + "Required field %s has zero address and/or length: %8.8X%8.8X/%X", + name, ACPI_FORMAT_UINT64(address64-> address), length)); @@ -453,8 +589,8 @@ static void acpi_tb_validate_fadt(void) if ((address64->address && !length) || (!address64->address && length)) { ACPI_WARNING((AE_INFO, - "Optional field \"%s\" has zero address or length: %8.8X%8.8X/%X", - fadt_info_table[i].name, + "Optional field %s has zero address or length: %8.8X%8.8X/%X", + name, ACPI_FORMAT_UINT64(address64-> address), length)); @@ -466,8 +602,8 @@ static void acpi_tb_validate_fadt(void) if (address64->address && *address32 && (address64->address != (u64) * address32)) { ACPI_ERROR((AE_INFO, - "32/64X address mismatch in \"%s\": [%8.8X] [%8.8X%8.8X], using 64X", - fadt_info_table[i].name, *address32, + "32/64X address mismatch in %s: %8.8X/%8.8X%8.8X, using 64X", + name, *address32, ACPI_FORMAT_UINT64(address64->address))); } } diff --git a/drivers/acpi/tables/tbfind.c b/drivers/acpi/acpica/tbfind.c index 531584defbb8..1054dfd49207 100644 --- a/drivers/acpi/tables/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "actables.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbfind") diff --git a/drivers/acpi/tables/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 18747ce8dd2f..37374b21969d 100644 --- a/drivers/acpi/tables/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acnamesp.h" +#include "actables.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbinstal") diff --git a/drivers/acpi/tables/tbutils.c b/drivers/acpi/acpica/tbutils.c index 0cc92ef5236f..9684cc827930 100644 --- a/drivers/acpi/tables/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "actables.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbutils") @@ -113,6 +114,30 @@ acpi_tb_check_xsdt(acpi_physical_address address) /******************************************************************************* * + * FUNCTION: acpi_tb_initialize_facs + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a permanent mapping for the FADT and save it in a global + * for accessing the Global Lock and Firmware Waking Vector + * + ******************************************************************************/ + +acpi_status acpi_tb_initialize_facs(void) +{ + acpi_status status; + + status = acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, + ACPI_CAST_INDIRECT_PTR(struct + acpi_table_header, + &acpi_gbl_FACS)); + return status; +} + +/******************************************************************************* + * * FUNCTION: acpi_tb_tables_loaded * * PARAMETERS: None @@ -420,7 +445,8 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags) /* Differentiate between RSDT and XSDT root tables */ - if (rsdp->revision > 1 && rsdp->xsdt_physical_address) { + if (rsdp->revision > 1 && rsdp->xsdt_physical_address + && !acpi_rsdt_forced) { /* * Root table is an XSDT (64-bit physical addresses). We must use the * XSDT if the revision is > 1 and the XSDT pointer is present, as per diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/acpica/tbxface.c index fd7770aa1061..c3e841f3cde9 100644 --- a/drivers/acpi/tables/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -43,8 +43,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acnamesp.h" +#include "actables.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbxface") diff --git a/drivers/acpi/tables/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index 2d157e0f98d2..b7fc8dd43341 100644 --- a/drivers/acpi/tables/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "actables.h" #define _COMPONENT ACPI_TABLES ACPI_MODULE_NAME("tbxfroot") diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/acpica/utalloc.c index 241c535c1753..7580f6b3069e 100644 --- a/drivers/acpi/utilities/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acdebug.h> +#include "accommon.h" +#include "acdebug.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utalloc") diff --git a/drivers/acpi/utilities/utcopy.c b/drivers/acpi/acpica/utcopy.c index 5b2f7c27b705..b0dcfd3c872a 100644 --- a/drivers/acpi/utilities/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_UTILITIES diff --git a/drivers/acpi/utilities/utdebug.c b/drivers/acpi/acpica/utdebug.c index fd66ecb6741e..38821f53042c 100644 --- a/drivers/acpi/utilities/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -42,6 +42,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utdebug") @@ -136,7 +137,7 @@ static const char *acpi_ut_trim_function_name(const char *function_name) /******************************************************************************* * - * FUNCTION: acpi_ut_debug_print + * FUNCTION: acpi_debug_print * * PARAMETERS: requested_debug_level - Requested debug print level * line_number - Caller's line number (for error output) @@ -154,11 +155,11 @@ static const char *acpi_ut_trim_function_name(const char *function_name) ******************************************************************************/ void ACPI_INTERNAL_VAR_XFACE -acpi_ut_debug_print(u32 requested_debug_level, - u32 line_number, - const char *function_name, - const char *module_name, - u32 component_id, const char *format, ...) +acpi_debug_print(u32 requested_debug_level, + u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, const char *format, ...) { acpi_thread_id thread_id; va_list args; @@ -205,11 +206,11 @@ acpi_ut_debug_print(u32 requested_debug_level, va_end(args); } -ACPI_EXPORT_SYMBOL(acpi_ut_debug_print) +ACPI_EXPORT_SYMBOL(acpi_debug_print) /******************************************************************************* * - * FUNCTION: acpi_ut_debug_print_raw + * FUNCTION: acpi_debug_print_raw * * PARAMETERS: requested_debug_level - Requested debug print level * line_number - Caller's line number @@ -226,11 +227,11 @@ ACPI_EXPORT_SYMBOL(acpi_ut_debug_print) * ******************************************************************************/ void ACPI_INTERNAL_VAR_XFACE -acpi_ut_debug_print_raw(u32 requested_debug_level, - u32 line_number, - const char *function_name, - const char *module_name, - u32 component_id, const char *format, ...) +acpi_debug_print_raw(u32 requested_debug_level, + u32 line_number, + const char *function_name, + const char *module_name, + u32 component_id, const char *format, ...) { va_list args; @@ -244,7 +245,7 @@ acpi_ut_debug_print_raw(u32 requested_debug_level, va_end(args); } -ACPI_EXPORT_SYMBOL(acpi_ut_debug_print_raw) +ACPI_EXPORT_SYMBOL(acpi_debug_print_raw) /******************************************************************************* * @@ -270,9 +271,9 @@ acpi_ut_trace(u32 line_number, acpi_gbl_nesting_level++; acpi_ut_track_stack_ptr(); - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s\n", acpi_gbl_fn_entry_str); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s\n", acpi_gbl_fn_entry_str); } ACPI_EXPORT_SYMBOL(acpi_ut_trace) @@ -301,10 +302,9 @@ acpi_ut_trace_ptr(u32 line_number, acpi_gbl_nesting_level++; acpi_ut_track_stack_ptr(); - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s %p\n", acpi_gbl_fn_entry_str, - pointer); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s %p\n", acpi_gbl_fn_entry_str, pointer); } /******************************************************************************* @@ -333,10 +333,9 @@ acpi_ut_trace_str(u32 line_number, acpi_gbl_nesting_level++; acpi_ut_track_stack_ptr(); - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s %s\n", acpi_gbl_fn_entry_str, - string); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s %s\n", acpi_gbl_fn_entry_str, string); } /******************************************************************************* @@ -365,10 +364,9 @@ acpi_ut_trace_u32(u32 line_number, acpi_gbl_nesting_level++; acpi_ut_track_stack_ptr(); - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s %08X\n", acpi_gbl_fn_entry_str, - integer); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s %08X\n", acpi_gbl_fn_entry_str, integer); } /******************************************************************************* @@ -393,9 +391,9 @@ acpi_ut_exit(u32 line_number, const char *module_name, u32 component_id) { - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s\n", acpi_gbl_fn_exit_str); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s\n", acpi_gbl_fn_exit_str); acpi_gbl_nesting_level--; } @@ -426,17 +424,16 @@ acpi_ut_status_exit(u32 line_number, { if (ACPI_SUCCESS(status)) { - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s %s\n", - acpi_gbl_fn_exit_str, - acpi_format_exception(status)); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s %s\n", acpi_gbl_fn_exit_str, + acpi_format_exception(status)); } else { - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s ****Exception****: %s\n", - acpi_gbl_fn_exit_str, - acpi_format_exception(status)); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, + component_id, "%s ****Exception****: %s\n", + acpi_gbl_fn_exit_str, + acpi_format_exception(status)); } acpi_gbl_nesting_level--; @@ -467,10 +464,10 @@ acpi_ut_value_exit(u32 line_number, u32 component_id, acpi_integer value) { - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s %8.8X%8.8X\n", - acpi_gbl_fn_exit_str, ACPI_FORMAT_UINT64(value)); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s %8.8X%8.8X\n", acpi_gbl_fn_exit_str, + ACPI_FORMAT_UINT64(value)); acpi_gbl_nesting_level--; } @@ -499,9 +496,9 @@ acpi_ut_ptr_exit(u32 line_number, const char *module_name, u32 component_id, u8 *ptr) { - acpi_ut_debug_print(ACPI_LV_FUNCTIONS, - line_number, function_name, module_name, - component_id, "%s %p\n", acpi_gbl_fn_exit_str, ptr); + acpi_debug_print(ACPI_LV_FUNCTIONS, + line_number, function_name, module_name, component_id, + "%s %p\n", acpi_gbl_fn_exit_str, ptr); acpi_gbl_nesting_level--; } diff --git a/drivers/acpi/utilities/utdelete.c b/drivers/acpi/acpica/utdelete.c index d197c6b29e17..a0be9e39531e 100644 --- a/drivers/acpi/utilities/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acinterp.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> +#include "accommon.h" +#include "acinterp.h" +#include "acnamesp.h" +#include "acevents.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utdelete") diff --git a/drivers/acpi/utilities/uteval.c b/drivers/acpi/acpica/uteval.c index 352747e49c7a..da9450bc60f7 100644 --- a/drivers/acpi/utilities/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -42,8 +42,9 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acinterp.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acinterp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("uteval") @@ -129,7 +130,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) /* The interface is supported */ - return_ACPI_STATUS(AE_CTRL_TERMINATE); + return_ACPI_STATUS(AE_OK); } } @@ -143,13 +144,13 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) /* The interface is supported */ - return_ACPI_STATUS(AE_CTRL_TERMINATE); + return_ACPI_STATUS(AE_OK); } /* The interface is not supported */ return_desc->integer.value = 0; - return_ACPI_STATUS(AE_CTRL_TERMINATE); + return_ACPI_STATUS(AE_OK); } /******************************************************************************* diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/acpica/utglobal.c index 17ed5ac840f7..a3ab9d9da299 100644 --- a/drivers/acpi/utilities/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -44,11 +44,11 @@ #define DEFINE_ACPI_GLOBALS #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" -ACPI_EXPORT_SYMBOL(acpi_gbl_FADT) #define _COMPONENT ACPI_UTILITIES - ACPI_MODULE_NAME("utglobal") +ACPI_MODULE_NAME("utglobal") /******************************************************************************* * @@ -352,7 +352,7 @@ const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = { "PCI_Config", "EmbeddedControl", "SMBus", - "CMOS", + "SystemCMOS", "PCIBARTarget", "DataTable" }; @@ -756,6 +756,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_gpe_xrupt_list_head = NULL; acpi_gbl_gpe_fadt_blocks[0] = NULL; acpi_gbl_gpe_fadt_blocks[1] = NULL; + acpi_current_gpe_count = 0; /* Global handlers */ @@ -771,6 +772,7 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_global_lock_mutex = NULL; acpi_gbl_global_lock_acquired = FALSE; acpi_gbl_global_lock_handle = 0; + acpi_gbl_global_lock_present = FALSE; /* Miscellaneous variables */ @@ -815,5 +817,7 @@ acpi_status acpi_ut_init_globals(void) return_ACPI_STATUS(AE_OK); } +ACPI_EXPORT_SYMBOL(acpi_gbl_FADT) ACPI_EXPORT_SYMBOL(acpi_dbg_level) ACPI_EXPORT_SYMBOL(acpi_dbg_layer) +ACPI_EXPORT_SYMBOL(acpi_current_gpe_count) diff --git a/drivers/acpi/utilities/utinit.c b/drivers/acpi/acpica/utinit.c index cae515fc02d3..a54ca84eb362 100644 --- a/drivers/acpi/utilities/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -42,9 +42,10 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acevents.h> -#include <acpi/actables.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acevents.h" +#include "actables.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utinit") diff --git a/drivers/acpi/utilities/utmath.c b/drivers/acpi/acpica/utmath.c index c927324fdd26..c9f682d640ef 100644 --- a/drivers/acpi/utilities/utmath.c +++ b/drivers/acpi/acpica/utmath.c @@ -42,6 +42,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utmath") diff --git a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/acpica/utmisc.c index 9089a158a874..c1f7f4e1a72d 100644 --- a/drivers/acpi/utilities/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -44,7 +44,8 @@ #include <linux/module.h> #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utmisc") @@ -1016,7 +1017,7 @@ acpi_ut_walk_package_tree(union acpi_operand_object * source_object, /******************************************************************************* * - * FUNCTION: acpi_ut_error, acpi_ut_warning, acpi_ut_info + * FUNCTION: acpi_error, acpi_exception, acpi_warning, acpi_info * * PARAMETERS: module_name - Caller's module name (for error output) * line_number - Caller's line number (for error output) @@ -1029,7 +1030,7 @@ acpi_ut_walk_package_tree(union acpi_operand_object * source_object, ******************************************************************************/ void ACPI_INTERNAL_VAR_XFACE -acpi_ut_error(const char *module_name, u32 line_number, const char *format, ...) +acpi_error(const char *module_name, u32 line_number, const char *format, ...) { va_list args; @@ -1042,8 +1043,8 @@ acpi_ut_error(const char *module_name, u32 line_number, const char *format, ...) } void ACPI_INTERNAL_VAR_XFACE -acpi_ut_exception(const char *module_name, - u32 line_number, acpi_status status, const char *format, ...) +acpi_exception(const char *module_name, + u32 line_number, acpi_status status, const char *format, ...) { va_list args; @@ -1056,11 +1057,8 @@ acpi_ut_exception(const char *module_name, va_end(args); } -EXPORT_SYMBOL(acpi_ut_exception); - void ACPI_INTERNAL_VAR_XFACE -acpi_ut_warning(const char *module_name, - u32 line_number, const char *format, ...) +acpi_warning(const char *module_name, u32 line_number, const char *format, ...) { va_list args; @@ -1073,7 +1071,7 @@ acpi_ut_warning(const char *module_name, } void ACPI_INTERNAL_VAR_XFACE -acpi_ut_info(const char *module_name, u32 line_number, const char *format, ...) +acpi_info(const char *module_name, u32 line_number, const char *format, ...) { va_list args; @@ -1088,3 +1086,8 @@ acpi_ut_info(const char *module_name, u32 line_number, const char *format, ...) acpi_os_printf("\n"); va_end(args); } + +ACPI_EXPORT_SYMBOL(acpi_error) +ACPI_EXPORT_SYMBOL(acpi_exception) +ACPI_EXPORT_SYMBOL(acpi_warning) +ACPI_EXPORT_SYMBOL(acpi_info) diff --git a/drivers/acpi/utilities/utmutex.c b/drivers/acpi/acpica/utmutex.c index 7331dde9e1b3..14eb52c4d647 100644 --- a/drivers/acpi/utilities/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -42,6 +42,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utmutex") diff --git a/drivers/acpi/utilities/utobject.c b/drivers/acpi/acpica/utobject.c index 4bef3cfbaccb..fd5ea7543e5b 100644 --- a/drivers/acpi/utilities/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/acnamesp.h> +#include "accommon.h" +#include "acnamesp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utobject") diff --git a/drivers/acpi/utilities/utresrc.c b/drivers/acpi/acpica/utresrc.c index c3e3e1308edc..91b7c00236f4 100644 --- a/drivers/acpi/utilities/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -42,7 +42,8 @@ */ #include <acpi/acpi.h> -#include <acpi/amlresrc.h> +#include "accommon.h" +#include "amlresrc.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utresrc") diff --git a/drivers/acpi/utilities/utstate.c b/drivers/acpi/acpica/utstate.c index 63a6d3d77d88..0440c958f5a4 100644 --- a/drivers/acpi/utilities/utstate.c +++ b/drivers/acpi/acpica/utstate.c @@ -42,6 +42,7 @@ */ #include <acpi/acpi.h> +#include "accommon.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utstate") diff --git a/drivers/acpi/utilities/utxface.c b/drivers/acpi/acpica/utxface.c index c198a4d40583..078a22728c6b 100644 --- a/drivers/acpi/utilities/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -42,9 +42,11 @@ */ #include <acpi/acpi.h> -#include <acpi/acevents.h> -#include <acpi/acnamesp.h> -#include <acpi/acdebug.h> +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +#include "acdebug.h" +#include "actables.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utxface") @@ -148,6 +150,16 @@ acpi_status acpi_enable_subsystem(u32 flags) } /* + * Obtain a permanent mapping for the FACS. This is required for the + * Global Lock and the Firmware Waking Vector + */ + status = acpi_tb_initialize_facs(); + if (ACPI_FAILURE(status)) { + ACPI_WARNING((AE_INFO, "Could not map the FACS table")); + return_ACPI_STATUS(status); + } + + /* * Install the default op_region handlers. These are installed unless * other handlers have already been installed via the * install_address_space_handler interface. diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 1423b0c0cd2e..65132f920459 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -471,7 +471,7 @@ static void sysfs_remove_battery(struct acpi_battery *battery) static int acpi_battery_update(struct acpi_battery *battery) { - int result; + int result, old_present = acpi_battery_present(battery); result = acpi_battery_get_status(battery); if (result) return result; @@ -482,7 +482,8 @@ static int acpi_battery_update(struct acpi_battery *battery) return 0; } #endif - if (!battery->update_time) { + if (!battery->update_time || + old_present != acpi_battery_present(battery)) { result = acpi_battery_get_info(battery); if (result) return result; diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c index 307963bd1043..332fe4b21708 100644 --- a/drivers/acpi/cm_sbs.c +++ b/drivers/acpi/cm_sbs.c @@ -27,9 +27,6 @@ #include <linux/seq_file.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#include <acpi/acmacros.h> -#include <acpi/actypes.h> -#include <acpi/acutils.h> ACPI_MODULE_NAME("cm_sbs"); #define ACPI_AC_CLASS "ac_adapter" diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index c48396892008..20223cbd0d1c 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -9,7 +9,6 @@ #include <linux/moduleparam.h> #include <asm/uaccess.h> #include <acpi/acpi_drivers.h> -#include <acpi/acglobal.h> #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("debug"); diff --git a/drivers/acpi/dispatcher/Makefile b/drivers/acpi/dispatcher/Makefile deleted file mode 100644 index eb7e602a83cd..000000000000 --- a/drivers/acpi/dispatcher/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \ - dsmethod.o dsobject.o dsutils.o dswload.o dswstate.o \ - dsinit.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 30f3ef236ecb..8dfcbb8aff73 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -42,7 +42,6 @@ #include <asm/io.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#include <acpi/actypes.h> #define ACPI_EC_CLASS "embedded_controller" #define ACPI_EC_DEVICE_NAME "Embedded Controller" @@ -370,7 +369,7 @@ unlock: * Note: samsung nv5000 doesn't work with ec burst mode. * http://bugzilla.kernel.org/show_bug.cgi?id=4980 */ -int acpi_ec_burst_enable(struct acpi_ec *ec) +static int acpi_ec_burst_enable(struct acpi_ec *ec) { u8 d; struct transaction t = {.command = ACPI_EC_BURST_ENABLE, @@ -380,7 +379,7 @@ int acpi_ec_burst_enable(struct acpi_ec *ec) return acpi_ec_transaction(ec, &t, 0); } -int acpi_ec_burst_disable(struct acpi_ec *ec) +static int acpi_ec_burst_disable(struct acpi_ec *ec) { struct transaction t = {.command = ACPI_EC_BURST_DISABLE, .wdata = NULL, .rdata = NULL, @@ -756,10 +755,15 @@ static acpi_status acpi_ec_register_query_methods(acpi_handle handle, u32 level, void *context, void **return_value) { - struct acpi_namespace_node *node = handle; + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; struct acpi_ec *ec = context; int value = 0; - if (sscanf(node->name.ascii, "_Q%x", &value) == 1) { + acpi_status status; + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) { acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); } return AE_OK; @@ -978,9 +982,9 @@ static const struct acpi_device_id ec_device_ids[] = { int __init acpi_ec_ecdt_probe(void) { - int ret; acpi_status status; struct acpi_table_ecdt *ecdt_ptr; + acpi_handle dummy; boot_ec = make_acpi_ec(); if (!boot_ec) @@ -1006,30 +1010,31 @@ int __init acpi_ec_ecdt_probe(void) boot_ec->gpe = ecdt_ptr->gpe; boot_ec->handle = ACPI_ROOT_OBJECT; acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle); - } else { - /* This workaround is needed only on some broken machines, - * which require early EC, but fail to provide ECDT */ - acpi_handle x; - printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); - status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, - boot_ec, NULL); - /* Check that acpi_get_devices actually find something */ - if (ACPI_FAILURE(status) || !boot_ec->handle) - goto error; - /* We really need to limit this workaround, the only ASUS, - * which needs it, has fake EC._INI method, so use it as flag. - * Keep boot_ec struct as it will be needed soon. - */ - if (ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", &x))) - return -ENODEV; + /* Add some basic check against completely broken table */ + if (boot_ec->data_addr != boot_ec->command_addr) + goto install; + /* fall through */ } - - ret = ec_install_handlers(boot_ec); - if (!ret) { + /* This workaround is needed only on some broken machines, + * which require early EC, but fail to provide ECDT */ + printk(KERN_DEBUG PREFIX "Look up EC in DSDT\n"); + status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, + boot_ec, NULL); + /* Check that acpi_get_devices actually find something */ + if (ACPI_FAILURE(status) || !boot_ec->handle) + goto error; + /* We really need to limit this workaround, the only ASUS, + * which needs it, has fake EC._INI method, so use it as flag. + * Keep boot_ec struct as it will be needed soon. + */ + if (ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", &dummy))) + return -ENODEV; +install: + if (!ec_install_handlers(boot_ec)) { first_ec = boot_ec; return 0; } - error: +error: kfree(boot_ec); boot_ec = NULL; return -ENODEV; diff --git a/drivers/acpi/events/Makefile b/drivers/acpi/events/Makefile deleted file mode 100644 index d29f2ee449cc..000000000000 --- a/drivers/acpi/events/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := evevent.o evregion.o evsci.o evxfevnt.o \ - evmisc.o evrgnini.o evxface.o evxfregn.o \ - evgpe.o evgpeblk.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/executer/Makefile b/drivers/acpi/executer/Makefile deleted file mode 100644 index e09998aa012f..000000000000 --- a/drivers/acpi/executer/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ - exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\ - excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \ - exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/hardware/Makefile b/drivers/acpi/hardware/Makefile deleted file mode 100644 index 438ad373b9ad..000000000000 --- a/drivers/acpi/hardware/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := hwacpi.o hwgpe.o hwregs.o hwsleep.o - -obj-$(ACPI_FUTURE_USAGE) += hwtimer.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/main.c index 28a691cc625e..7e3c609cbef2 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/main.c @@ -101,13 +101,26 @@ void __init acpi_old_suspend_ordering(void) * cases. */ static bool set_sci_en_on_resume; +/* + * The ACPI specification wants us to save NVS memory regions during hibernation + * and to restore them during the subsequent resume. However, it is not certain + * if this mechanism is going to work on all machines, so we allow the user to + * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line + * option. + */ +static bool s4_no_nvs; + +void __init acpi_s4_no_nvs(void) +{ + s4_no_nvs = true; +} /** * acpi_pm_disable_gpes - Disable the GPEs. */ static int acpi_pm_disable_gpes(void) { - acpi_hw_disable_all_gpes(); + acpi_disable_all_gpes(); return 0; } @@ -135,7 +148,7 @@ static int acpi_pm_prepare(void) int error = __acpi_pm_prepare(); if (!error) - acpi_hw_disable_all_gpes(); + acpi_disable_all_gpes(); return error; } @@ -267,7 +280,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. * acpi_leave_sleep_state will reenable specific GPEs later */ - acpi_hw_disable_all_gpes(); + acpi_disable_all_gpes(); local_irq_restore(flags); printk(KERN_DEBUG "Back to C!\n"); @@ -394,9 +407,25 @@ void __init acpi_no_s4_hw_signature(void) static int acpi_hibernation_begin(void) { - acpi_target_sleep_state = ACPI_STATE_S4; - acpi_sleep_tts_switch(acpi_target_sleep_state); - return 0; + int error; + + error = s4_no_nvs ? 0 : hibernate_nvs_alloc(); + if (!error) { + acpi_target_sleep_state = ACPI_STATE_S4; + acpi_sleep_tts_switch(acpi_target_sleep_state); + } + + return error; +} + +static int acpi_hibernation_pre_snapshot(void) +{ + int error = acpi_pm_prepare(); + + if (!error) + hibernate_nvs_save(); + + return error; } static int acpi_hibernation_enter(void) @@ -417,6 +446,12 @@ static int acpi_hibernation_enter(void) return ACPI_SUCCESS(status) ? 0 : -EFAULT; } +static void acpi_hibernation_finish(void) +{ + hibernate_nvs_free(); + acpi_pm_finish(); +} + static void acpi_hibernation_leave(void) { /* @@ -432,18 +467,20 @@ static void acpi_hibernation_leave(void) "cannot resume!\n"); panic("ACPI S4 hardware signature mismatch"); } + /* Restore the NVS memory area */ + hibernate_nvs_restore(); } static void acpi_pm_enable_gpes(void) { - acpi_hw_enable_all_runtime_gpes(); + acpi_enable_all_runtime_gpes(); } static struct platform_hibernation_ops acpi_hibernation_ops = { .begin = acpi_hibernation_begin, .end = acpi_pm_end, - .pre_snapshot = acpi_pm_prepare, - .finish = acpi_pm_finish, + .pre_snapshot = acpi_hibernation_pre_snapshot, + .finish = acpi_hibernation_finish, .prepare = acpi_pm_prepare, .enter = acpi_hibernation_enter, .leave = acpi_hibernation_leave, @@ -469,8 +506,22 @@ static int acpi_hibernation_begin_old(void) error = acpi_sleep_prepare(ACPI_STATE_S4); + if (!error) { + if (!s4_no_nvs) + error = hibernate_nvs_alloc(); + if (!error) + acpi_target_sleep_state = ACPI_STATE_S4; + } + return error; +} + +static int acpi_hibernation_pre_snapshot_old(void) +{ + int error = acpi_pm_disable_gpes(); + if (!error) - acpi_target_sleep_state = ACPI_STATE_S4; + hibernate_nvs_save(); + return error; } @@ -481,8 +532,8 @@ static int acpi_hibernation_begin_old(void) static struct platform_hibernation_ops acpi_hibernation_ops_old = { .begin = acpi_hibernation_begin_old, .end = acpi_pm_end, - .pre_snapshot = acpi_pm_disable_gpes, - .finish = acpi_pm_finish, + .pre_snapshot = acpi_hibernation_pre_snapshot_old, + .finish = acpi_hibernation_finish, .prepare = acpi_pm_disable_gpes, .enter = acpi_hibernation_enter, .leave = acpi_hibernation_leave, @@ -622,7 +673,7 @@ static void acpi_power_off_prepare(void) { /* Prepare to power off the system */ acpi_sleep_prepare(ACPI_STATE_S5); - acpi_hw_disable_all_gpes(); + acpi_disable_all_gpes(); } static void acpi_power_off(void) @@ -671,7 +722,7 @@ int __init acpi_sleep_init(void) sleep_states[ACPI_STATE_S4] = 1; printk(" S4"); if (!nosigcheck) { - acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS, + acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs); if (facs) s4_hardware_signature = diff --git a/drivers/acpi/namespace/Makefile b/drivers/acpi/namespace/Makefile deleted file mode 100644 index 371a2daf837f..000000000000 --- a/drivers/acpi/namespace/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := nsaccess.o nsload.o nssearch.o nsxfeval.o \ - nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \ - nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \ - nsparse.o nspredef.o - -obj-$(ACPI_FUTURE_USAGE) += nsdumpdv.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 25ceae9191ef..c5e292aab0e3 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -29,7 +29,6 @@ #include <linux/errno.h> #include <linux/acpi.h> #include <acpi/acpi_bus.h> -#include <acpi/acmacros.h> #define ACPI_NUMA 0x80000000 #define _COMPONENT ACPI_NUMA diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index c8111424dcb8..6729a4992f2b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -726,7 +726,7 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC); if (!dpc) - return_ACPI_STATUS(AE_NO_MEMORY); + return AE_NO_MEMORY; dpc->function = function; dpc->context = context; @@ -747,7 +747,7 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, status = AE_ERROR; kfree(dpc); } - return_ACPI_STATUS(status); + return status; } acpi_status acpi_os_execute(acpi_execute_type type, diff --git a/drivers/acpi/parser/Makefile b/drivers/acpi/parser/Makefile deleted file mode 100644 index db24ee09cf11..000000000000 --- a/drivers/acpi/parser/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := psargs.o psparse.o psloop.o pstree.o pswalk.o \ - psopcode.o psscope.o psutils.o psxface.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 4b252ea0e952..95650f83ce2e 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -99,7 +99,7 @@ acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id) */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %s has PCI address %02x:%02x:%02x.%02x\n", + "Device %s has PCI address %04x:%02x:%02x.%d\n", acpi_device_bid(device), id->segment, id->bus, id->device, id->function)); @@ -111,12 +111,11 @@ EXPORT_SYMBOL(acpi_get_pci_id); int acpi_pci_bind(struct acpi_device *device) { int result = 0; - acpi_status status = AE_OK; - struct acpi_pci_data *data = NULL; - struct acpi_pci_data *pdata = NULL; - char *pathname = NULL; - struct acpi_buffer buffer = { 0, NULL }; - acpi_handle handle = NULL; + acpi_status status; + struct acpi_pci_data *data; + struct acpi_pci_data *pdata; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_handle handle; struct pci_dev *dev; struct pci_bus *bus; @@ -124,21 +123,18 @@ int acpi_pci_bind(struct acpi_device *device) if (!device || !device->parent) return -EINVAL; - pathname = kzalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - buffer.length = ACPI_PATHNAME_MAX; - buffer.pointer = pathname; - data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); - if (!data) { - kfree(pathname); + if (!data) return -ENOMEM; + + status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + if (ACPI_FAILURE(status)) { + kfree(data); + return -ENODEV; } - acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n", - pathname)); + (char *)buffer.pointer)); /* * Segment & Bus @@ -166,7 +162,7 @@ int acpi_pci_bind(struct acpi_device *device) data->id.device = device->pnp.bus_address >> 16; data->id.function = device->pnp.bus_address & 0xFFFF; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %02x:%02x:%02x.%02x\n", + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %04x:%02x:%02x.%d\n", data->id.segment, data->id.bus, data->id.device, data->id.function)); @@ -196,7 +192,7 @@ int acpi_pci_bind(struct acpi_device *device) } if (!data->dev) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %02x:%02x:%02x.%02x not present in PCI namespace\n", + "Device %04x:%02x:%02x.%d not present in PCI namespace\n", data->id.segment, data->id.bus, data->id.device, data->id.function)); result = -ENODEV; @@ -204,7 +200,7 @@ int acpi_pci_bind(struct acpi_device *device) } if (!data->dev->bus) { printk(KERN_ERR PREFIX - "Device %02x:%02x:%02x.%02x has invalid 'bus' field\n", + "Device %04x:%02x:%02x.%d has invalid 'bus' field\n", data->id.segment, data->id.bus, data->id.device, data->id.function); result = -ENODEV; @@ -219,7 +215,7 @@ int acpi_pci_bind(struct acpi_device *device) */ if (data->dev->subordinate) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %02x:%02x:%02x.%02x is a PCI bridge\n", + "Device %04x:%02x:%02x.%d is a PCI bridge\n", data->id.segment, data->id.bus, data->id.device, data->id.function)); data->bus = data->dev->subordinate; @@ -262,7 +258,7 @@ int acpi_pci_bind(struct acpi_device *device) } end: - kfree(pathname); + kfree(buffer.pointer); if (result) kfree(data); @@ -272,25 +268,21 @@ int acpi_pci_bind(struct acpi_device *device) static int acpi_pci_unbind(struct acpi_device *device) { int result = 0; - acpi_status status = AE_OK; - struct acpi_pci_data *data = NULL; - char *pathname = NULL; - struct acpi_buffer buffer = { 0, NULL }; + acpi_status status; + struct acpi_pci_data *data; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; if (!device || !device->parent) return -EINVAL; - pathname = kzalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; + status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; - buffer.length = ACPI_PATHNAME_MAX; - buffer.pointer = pathname; - acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n", - pathname)); - kfree(pathname); + (char *) buffer.pointer)); + kfree(buffer.pointer); status = acpi_get_data(device->handle, acpi_pci_data_handler, @@ -322,50 +314,44 @@ acpi_pci_bind_root(struct acpi_device *device, struct acpi_pci_id *id, struct pci_bus *bus) { int result = 0; - acpi_status status = AE_OK; + acpi_status status; struct acpi_pci_data *data = NULL; - char *pathname = NULL; - struct acpi_buffer buffer = { 0, NULL }; - - pathname = kzalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - - buffer.length = ACPI_PATHNAME_MAX; - buffer.pointer = pathname; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; if (!device || !id || !bus) { - kfree(pathname); return -EINVAL; } data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); - if (!data) { - kfree(pathname); + if (!data) return -ENOMEM; - } data->id = *id; data->bus = bus; device->ops.bind = acpi_pci_bind; device->ops.unbind = acpi_pci_unbind; - acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + if (ACPI_FAILURE(status)) { + kfree (data); + return -ENODEV; + } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to " - "%02x:%02x\n", pathname, id->segment, id->bus)); + "%04x:%02x\n", (char *)buffer.pointer, + id->segment, id->bus)); status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "Unable to attach ACPI-PCI context to device %s", - pathname)); + (char *)buffer.pointer)); result = -ENODEV; goto end; } end: - kfree(pathname); + kfree(buffer.pointer); if (result != 0) kfree(data); diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index bf79d83bdfbb..891bdf6679f3 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -4,6 +4,8 @@ * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> + * (c) Copyright 2008 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -41,29 +43,36 @@ #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_irq"); -static struct acpi_prt_list acpi_prt; +struct acpi_prt_entry { + struct list_head list; + struct acpi_pci_id id; + u8 pin; + acpi_handle link; + u32 index; /* GSI, or link _CRS index */ +}; + +static LIST_HEAD(acpi_prt_list); static DEFINE_SPINLOCK(acpi_prt_lock); +static inline char pin_name(int pin) +{ + return 'A' + pin - 1; +} + /* -------------------------------------------------------------------------- PCI IRQ Routing Table (PRT) Support -------------------------------------------------------------------------- */ -static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment, - int bus, - int device, int pin) +static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin) { - struct acpi_prt_entry *entry = NULL; - - if (!acpi_prt.count) - return NULL; + struct acpi_prt_entry *entry; + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); - /* - * Parse through all PRT entries looking for a match on the specified - * PCI device's segment, bus, device, and pin (don't care about func). - * - */ spin_lock(&acpi_prt_lock); - list_for_each_entry(entry, &acpi_prt.entries, node) { + list_for_each_entry(entry, &acpi_prt_list, list) { if ((segment == entry->id.segment) && (bus == entry->id.bus) && (device == entry->id.device) @@ -72,7 +81,6 @@ static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(int segment, return entry; } } - spin_unlock(&acpi_prt_lock); return NULL; } @@ -124,25 +132,27 @@ struct prt_quirk { char *actual_source; }; +#define PCI_INTX_PIN(c) (c - 'A' + 1) + /* * These systems have incorrect _PRT entries. The BIOS claims the PCI * interrupt at the listed segment/bus/device/pin is connected to the first * link device, but it is actually connected to the second. */ static struct prt_quirk prt_quirks[] = { - { medion_md9580, 0, 0, 9, 'A', + { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'), "\\_SB_.PCI0.ISA_.LNKA", "\\_SB_.PCI0.ISA_.LNKB"}, - { dell_optiplex, 0, 0, 0xd, 'A', + { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'), "\\_SB_.LNKB", "\\_SB_.LNKA"}, - { hp_t5710, 0, 0, 1, 'A', + { hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'), "\\_SB_.PCI0.LNK1", "\\_SB_.PCI0.LNK3"}, }; -static void -do_prt_fixups(struct acpi_prt_entry *entry, struct acpi_pci_routing_table *prt) +static void do_prt_fixups(struct acpi_prt_entry *entry, + struct acpi_pci_routing_table *prt) { int i; struct prt_quirk *quirk; @@ -158,42 +168,43 @@ do_prt_fixups(struct acpi_prt_entry *entry, struct acpi_pci_routing_table *prt) entry->id.segment == quirk->segment && entry->id.bus == quirk->bus && entry->id.device == quirk->device && - entry->pin + 'A' == quirk->pin && + entry->pin == quirk->pin && !strcmp(prt->source, quirk->source) && strlen(prt->source) >= strlen(quirk->actual_source)) { printk(KERN_WARNING PREFIX "firmware reports " "%04x:%02x:%02x PCI INT %c connected to %s; " "changing to %s\n", entry->id.segment, entry->id.bus, - entry->id.device, 'A' + entry->pin, + entry->id.device, pin_name(entry->pin), prt->source, quirk->actual_source); strcpy(prt->source, quirk->actual_source); } } } -static int -acpi_pci_irq_add_entry(acpi_handle handle, - int segment, int bus, struct acpi_pci_routing_table *prt) +static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, + struct acpi_pci_routing_table *prt) { - struct acpi_prt_entry *entry = NULL; - - - if (!prt) - return -EINVAL; + struct acpi_prt_entry *entry; entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); if (!entry) return -ENOMEM; + /* + * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses + * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert + * it here. + */ entry->id.segment = segment; entry->id.bus = bus; entry->id.device = (prt->address >> 16) & 0xFFFF; - entry->id.function = prt->address & 0xFFFF; - entry->pin = prt->pin; + entry->pin = prt->pin + 1; do_prt_fixups(entry, prt); + entry->index = prt->source_index; + /* * Type 1: Dynamic * --------------- @@ -207,10 +218,9 @@ acpi_pci_irq_add_entry(acpi_handle handle, * (e.g. exists somewhere 'below' this _PRT entry in the ACPI * namespace). */ - if (prt->source[0]) { - acpi_get_handle(handle, prt->source, &entry->link.handle); - entry->link.index = prt->source_index; - } + if (prt->source[0]) + acpi_get_handle(handle, prt->source, &entry->link); + /* * Type 2: Static * -------------- @@ -218,84 +228,38 @@ acpi_pci_irq_add_entry(acpi_handle handle, * the IRQ value, which is hardwired to specific interrupt inputs on * the interrupt controller. */ - else - entry->link.index = prt->source_index; ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, - " %02X:%02X:%02X[%c] -> %s[%d]\n", + " %04x:%02x:%02x[%c] -> %s[%d]\n", entry->id.segment, entry->id.bus, - entry->id.device, ('A' + entry->pin), prt->source, - entry->link.index)); + entry->id.device, pin_name(entry->pin), + prt->source, entry->index)); spin_lock(&acpi_prt_lock); - list_add_tail(&entry->node, &acpi_prt.entries); - acpi_prt.count++; + list_add_tail(&entry->list, &acpi_prt_list); spin_unlock(&acpi_prt_lock); return 0; } -static void -acpi_pci_irq_del_entry(int segment, int bus, struct acpi_prt_entry *entry) -{ - if (segment == entry->id.segment && bus == entry->id.bus) { - acpi_prt.count--; - list_del(&entry->node); - kfree(entry); - } -} - int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) { - acpi_status status = AE_OK; - char *pathname = NULL; - struct acpi_buffer buffer = { 0, NULL }; - struct acpi_pci_routing_table *prt = NULL; - struct acpi_pci_routing_table *entry = NULL; - static int first_time = 1; - - - pathname = kzalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); - if (!pathname) - return -ENOMEM; - - if (first_time) { - acpi_prt.count = 0; - INIT_LIST_HEAD(&acpi_prt.entries); - first_time = 0; - } - - /* - * NOTE: We're given a 'handle' to the _PRT object's parent device - * (either a PCI root bridge or PCI-PCI bridge). - */ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_pci_routing_table *entry; - buffer.length = ACPI_PATHNAME_MAX; - buffer.pointer = pathname; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", - pathname); + (char *) buffer.pointer); - /* - * Evaluate this _PRT and add its entries to our global list (acpi_prt). - */ + kfree(buffer.pointer); - buffer.length = 0; + buffer.length = ACPI_ALLOCATE_BUFFER; buffer.pointer = NULL; - kfree(pathname); - status = acpi_get_irq_routing_table(handle, &buffer); - if (status != AE_BUFFER_OVERFLOW) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]", - acpi_format_exception(status))); - return -ENODEV; - } - - prt = kzalloc(buffer.length, GFP_KERNEL); - if (!prt) { - return -ENOMEM; - } - buffer.pointer = prt; status = acpi_get_irq_routing_table(handle, &buffer); if (ACPI_FAILURE(status)) { @@ -305,36 +269,30 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) return -ENODEV; } - entry = prt; - + entry = buffer.pointer; while (entry && (entry->length > 0)) { acpi_pci_irq_add_entry(handle, segment, bus, entry); entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } - kfree(prt); - + kfree(buffer.pointer); return 0; } void acpi_pci_irq_del_prt(int segment, int bus) { - struct list_head *node = NULL, *n = NULL; - struct acpi_prt_entry *entry = NULL; - - if (!acpi_prt.count) { - return; - } + struct acpi_prt_entry *entry, *tmp; printk(KERN_DEBUG - "ACPI: Delete PCI Interrupt Routing Table for %x:%x\n", segment, - bus); + "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", + segment, bus); spin_lock(&acpi_prt_lock); - list_for_each_safe(node, n, &acpi_prt.entries) { - entry = list_entry(node, struct acpi_prt_entry, node); - - acpi_pci_irq_del_entry(segment, bus, entry); + list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { + if (segment == entry->id.segment && bus == entry->id.bus) { + list_del(&entry->list); + kfree(entry); + } } spin_unlock(&acpi_prt_lock); } @@ -342,162 +300,26 @@ void acpi_pci_irq_del_prt(int segment, int bus) /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ -typedef int (*irq_lookup_func) (struct acpi_prt_entry *, int *, int *, char **); - -static int -acpi_pci_allocate_irq(struct acpi_prt_entry *entry, - int *triggering, int *polarity, char **link) -{ - int irq; - - - if (entry->link.handle) { - irq = acpi_pci_link_allocate_irq(entry->link.handle, - entry->link.index, triggering, - polarity, link); - if (irq < 0) { - printk(KERN_WARNING PREFIX - "Invalid IRQ link routing entry\n"); - return -1; - } - } else { - irq = entry->link.index; - *triggering = ACPI_LEVEL_SENSITIVE; - *polarity = ACPI_ACTIVE_LOW; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); - return irq; -} - -static int -acpi_pci_free_irq(struct acpi_prt_entry *entry, - int *triggering, int *polarity, char **link) -{ - int irq; - - if (entry->link.handle) { - irq = acpi_pci_link_free_irq(entry->link.handle); - } else { - irq = entry->link.index; - } - return irq; -} - -#ifdef CONFIG_X86_IO_APIC -extern int noioapicquirk; - -static int bridge_has_boot_interrupt_variant(struct pci_bus *bus) +static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) { - struct pci_bus *bus_it; - - for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { - if (!bus_it->self) - return 0; - - printk(KERN_INFO "vendor=%04x device=%04x\n", bus_it->self->vendor, - bus_it->self->device); - - if (bus_it->self->irq_reroute_variant) - return bus_it->self->irq_reroute_variant; - } - return 0; -} -#endif /* CONFIG_X86_IO_APIC */ - -/* - * acpi_pci_irq_lookup - * success: return IRQ >= 0 - * failure: return -1 - */ -static int -acpi_pci_irq_lookup(struct pci_bus *bus, - int device, - int pin, - int *triggering, - int *polarity, char **link, irq_lookup_func func) -{ - struct acpi_prt_entry *entry = NULL; - int segment = pci_domain_nr(bus); - int bus_nr = bus->number; - int ret; - - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Searching for PRT entry for %02x:%02x:%02x[%c]\n", - segment, bus_nr, device, ('A' + pin))); - - entry = acpi_pci_irq_find_prt_entry(segment, bus_nr, device, pin); - if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PRT entry not found\n")); - return -1; - } - - ret = func(entry, triggering, polarity, link); - -#ifdef CONFIG_X86_IO_APIC - /* - * Some chipsets (e.g. intel 6700PXH) generate a legacy INTx when the - * IRQ entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel - * does during interrupt handling). When this INTx generation cannot be - * disabled, we reroute these interrupts to their legacy equivalent to - * get rid of spurious interrupts. - */ - if (!noioapicquirk) { - switch (bridge_has_boot_interrupt_variant(bus)) { - case 0: - /* no rerouting necessary */ - break; - - case INTEL_IRQ_REROUTE_VARIANT: - /* - * Remap according to INTx routing table in 6700PXH - * specs, intel order number 302628-002, section - * 2.15.2. Other chipsets (80332, ...) have the same - * mapping and are handled here as well. - */ - printk(KERN_INFO "pci irq %d -> rerouted to legacy " - "irq %d\n", ret, (ret % 4) + 16); - ret = (ret % 4) + 16; - break; - - default: - printk(KERN_INFO "not rerouting irq %d to legacy irq: " - "unknown mapping\n", ret); - break; - } + struct acpi_prt_entry *entry; + struct pci_dev *bridge; + u8 bridge_pin, orig_pin = pin; + + entry = acpi_pci_irq_find_prt_entry(dev, pin); + if (entry) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n", + pci_name(dev), pin_name(pin))); + return entry; } -#endif /* CONFIG_X86_IO_APIC */ - - return ret; -} - -/* - * acpi_pci_irq_derive - * success: return IRQ >= 0 - * failure: return < 0 - */ -static int -acpi_pci_irq_derive(struct pci_dev *dev, - int pin, - int *triggering, - int *polarity, char **link, irq_lookup_func func) -{ - struct pci_dev *bridge = dev; - int irq = -1; - u8 bridge_pin = 0, orig_pin = pin; - - - if (!dev) - return -EINVAL; /* * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ - while (irq < 0 && bridge->bus->self) { - pin = (pin + PCI_SLOT(bridge->devfn)) % 4; - bridge = bridge->bus->self; + bridge = dev->bus->self; + while (bridge) { + pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { /* PC card has the same IRQ as its cardbridge */ @@ -506,50 +328,40 @@ acpi_pci_irq_derive(struct pci_dev *dev, ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No interrupt pin configured for device %s\n", pci_name(bridge))); - return -1; + return NULL; } - /* Pin is from 0 to 3 */ - bridge_pin--; pin = bridge_pin; } - irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), - pin, triggering, polarity, - link, func); - } + entry = acpi_pci_irq_find_prt_entry(bridge, pin); + if (entry) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Derived GSI for %s INT %c from %s\n", + pci_name(dev), pin_name(orig_pin), + pci_name(bridge))); + return entry; + } - if (irq < 0) { - dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n", - 'A' + orig_pin); - return -1; + dev = bridge; + bridge = dev->bus->self; } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derive IRQ %d for device %s from %s\n", - irq, pci_name(dev), pci_name(bridge))); - - return irq; + dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n", + pin_name(orig_pin)); + return NULL; } -/* - * acpi_pci_irq_enable - * success: return 0 - * failure: return < 0 - */ - int acpi_pci_irq_enable(struct pci_dev *dev) { - int irq = 0; - u8 pin = 0; + struct acpi_prt_entry *entry; + int gsi; + u8 pin; int triggering = ACPI_LEVEL_SENSITIVE; int polarity = ACPI_ACTIVE_LOW; char *link = NULL; char link_desc[16]; int rc; - - if (!dev) - return -EINVAL; - pin = dev->pin; if (!pin) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -557,31 +369,9 @@ int acpi_pci_irq_enable(struct pci_dev *dev) pci_name(dev))); return 0; } - pin--; - - if (!dev->bus) { - dev_err(&dev->dev, "invalid (NULL) 'bus' field\n"); - return -ENODEV; - } - - /* - * First we check the PCI IRQ routing table (PRT) for an IRQ. PRT - * values override any BIOS-assigned IRQs set during boot. - */ - irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, - &triggering, &polarity, &link, - acpi_pci_allocate_irq); - - /* - * If no PRT entry was found, we'll try to derive an IRQ from the - * device's parent bridge. - */ - if (irq < 0) - irq = acpi_pci_irq_derive(dev, pin, &triggering, - &polarity, &link, - acpi_pci_allocate_irq); - if (irq < 0) { + entry = acpi_pci_irq_lookup(dev, pin); + if (!entry) { /* * IDE legacy mode controller IRQs are magic. Why do compat * extensions always make such a nasty mess. @@ -590,12 +380,24 @@ int acpi_pci_irq_enable(struct pci_dev *dev) (dev->class & 0x05) == 0) return 0; } + + if (entry) { + if (entry->link) + gsi = acpi_pci_link_allocate_irq(entry->link, + entry->index, + &triggering, &polarity, + &link); + else + gsi = entry->index; + } else + gsi = -1; + /* * No IRQ known to the ACPI subsystem - maybe the BIOS / * driver reported one, then use it. Exit in any case. */ - if (irq < 0) { - dev_warn(&dev->dev, "PCI INT %c: no GSI", 'A' + pin); + if (gsi < 0) { + dev_warn(&dev->dev, "PCI INT %c: no GSI", pin_name(pin)); /* Interrupt Line values above 0xF are forbidden */ if (dev->irq > 0 && (dev->irq <= 0xF)) { printk(" - using IRQ %d\n", dev->irq); @@ -608,10 +410,10 @@ int acpi_pci_irq_enable(struct pci_dev *dev) } } - rc = acpi_register_gsi(irq, triggering, polarity); + rc = acpi_register_gsi(gsi, triggering, polarity); if (rc < 0) { dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", - 'A' + pin); + pin_name(pin)); return rc; } dev->irq = rc; @@ -622,7 +424,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) link_desc[0] = '\0'; dev_info(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n", - 'A' + pin, link_desc, irq, + pin_name(pin), link_desc, gsi, (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); @@ -636,42 +438,28 @@ void __attribute__ ((weak)) acpi_unregister_gsi(u32 i) void acpi_pci_irq_disable(struct pci_dev *dev) { - int gsi = 0; - u8 pin = 0; - int triggering = ACPI_LEVEL_SENSITIVE; - int polarity = ACPI_ACTIVE_LOW; - - - if (!dev || !dev->bus) - return; + struct acpi_prt_entry *entry; + int gsi; + u8 pin; pin = dev->pin; if (!pin) return; - pin--; - /* - * First we check the PCI IRQ routing table (PRT) for an IRQ. - */ - gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, - &triggering, &polarity, NULL, - acpi_pci_free_irq); - /* - * If no PRT entry was found, we'll try to derive an IRQ from the - * device's parent bridge. - */ - if (gsi < 0) - gsi = acpi_pci_irq_derive(dev, pin, - &triggering, &polarity, NULL, - acpi_pci_free_irq); - if (gsi < 0) + entry = acpi_pci_irq_lookup(dev, pin); + if (!entry) return; + if (entry->link) + gsi = acpi_pci_link_free_irq(entry->link); + else + gsi = entry->index; + /* * TBD: It might be worth clearing dev->irq by magic constant * (e.g. PCI_UNDEFINED_IRQ). */ - dev_info(&dev->dev, "PCI INT %c disabled\n", 'A' + pin); + dev_info(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); acpi_unregister_gsi(gsi); } diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index e52ad91ce2dc..1c6e73c7865e 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -796,10 +796,6 @@ static int irqrouter_resume(struct sys_device *dev) struct list_head *node = NULL; struct acpi_pci_link *link = NULL; - - /* Make sure SCI is enabled again (Apple firmware bug?) */ - acpi_set_register(ACPI_BITREG_SCI_ENABLE, 1); - list_for_each(node, &acpi_link.entries) { link = list_entry(node, struct acpi_pci_link, node); if (!link) { @@ -912,7 +908,7 @@ static int __init acpi_irq_nobalance_set(char *str) __setup("acpi_irq_nobalance", acpi_irq_nobalance_set); -int __init acpi_irq_balance_set(char *str) +static int __init acpi_irq_balance_set(char *str) { acpi_irq_balance = 1; return 1; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 642554b1b60c..5b38a026d122 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -31,6 +31,7 @@ #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <linux/acpi.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> @@ -193,6 +194,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) unsigned long long value = 0; acpi_handle handle = NULL; struct acpi_device *child; + u32 flags, base_flags; if (!device) @@ -210,6 +212,13 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) device->ops.bind = acpi_pci_bind; + /* + * All supported architectures that use ACPI have support for + * PCI domains, so we indicate this in _OSC support capabilities. + */ + flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + pci_acpi_osc_support(device->handle, flags); + /* * Segment * ------- @@ -335,6 +344,17 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) list_for_each_entry(child, &device->children, node) acpi_pci_bridge_scan(child); + /* Indicate support for various _OSC capabilities. */ + if (pci_ext_cfg_avail(root->bus->self)) + flags |= OSC_EXT_PCI_CONFIG_SUPPORT; + if (pcie_aspm_enabled()) + flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | + OSC_CLOCK_PWR_CAPABILITY_SUPPORT; + if (pci_msi_enabled()) + flags |= OSC_MSI_SUPPORT; + if (flags != base_flags) + pci_acpi_osc_support(device->handle, flags); + end: if (result) { if (!list_empty(&root->node)) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index bb7d50dd2818..c926e7d4a0d6 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -139,6 +139,8 @@ static int acpi_power_get_state(acpi_handle handle, int *state) { acpi_status status = AE_OK; unsigned long long sta = 0; + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; if (!handle || !state) @@ -151,8 +153,10 @@ static int acpi_power_get_state(acpi_handle handle, int *state) *state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON: ACPI_POWER_RESOURCE_STATE_OFF; + acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n", - acpi_ut_get_node_name(handle), + node_name, *state ? "on" : "off")); return 0; diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/proc.c index 4dbc2271acf5..428c911dba08 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/proc.c @@ -28,8 +28,6 @@ static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) { int i; - ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show"); - for (i = 0; i <= ACPI_STATE_S5; i++) { if (sleep_states[i]) { seq_printf(seq, "S%d ", i); @@ -86,49 +84,44 @@ acpi_system_write_sleep(struct file *file, #ifdef HAVE_ACPI_LEGACY_ALARM +static u32 cmos_bcd_read(int offset, int rtc_control); + static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) { u32 sec, min, hr; u32 day, mo, yr, cent = 0; + u32 today = 0; unsigned char rtc_control = 0; unsigned long flags; - ACPI_FUNCTION_TRACE("acpi_system_alarm_seq_show"); - spin_lock_irqsave(&rtc_lock, flags); - sec = CMOS_READ(RTC_SECONDS_ALARM); - min = CMOS_READ(RTC_MINUTES_ALARM); - hr = CMOS_READ(RTC_HOURS_ALARM); rtc_control = CMOS_READ(RTC_CONTROL); + sec = cmos_bcd_read(RTC_SECONDS_ALARM, rtc_control); + min = cmos_bcd_read(RTC_MINUTES_ALARM, rtc_control); + hr = cmos_bcd_read(RTC_HOURS_ALARM, rtc_control); /* If we ever get an FACP with proper values... */ - if (acpi_gbl_FADT.day_alarm) + if (acpi_gbl_FADT.day_alarm) { /* ACPI spec: only low 6 its should be cared */ day = CMOS_READ(acpi_gbl_FADT.day_alarm) & 0x3F; - else - day = CMOS_READ(RTC_DAY_OF_MONTH); + if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + day = bcd2bin(day); + } else + day = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control); if (acpi_gbl_FADT.month_alarm) - mo = CMOS_READ(acpi_gbl_FADT.month_alarm); - else - mo = CMOS_READ(RTC_MONTH); + mo = cmos_bcd_read(acpi_gbl_FADT.month_alarm, rtc_control); + else { + mo = cmos_bcd_read(RTC_MONTH, rtc_control); + today = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control); + } if (acpi_gbl_FADT.century) - cent = CMOS_READ(acpi_gbl_FADT.century); + cent = cmos_bcd_read(acpi_gbl_FADT.century, rtc_control); - yr = CMOS_READ(RTC_YEAR); + yr = cmos_bcd_read(RTC_YEAR, rtc_control); spin_unlock_irqrestore(&rtc_lock, flags); - if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - sec = bcd2bin(sec); - min = bcd2bin(min); - hr = bcd2bin(hr); - day = bcd2bin(day); - mo = bcd2bin(mo); - yr = bcd2bin(yr); - cent = bcd2bin(cent); - } - /* we're trusting the FADT (see above) */ if (!acpi_gbl_FADT.century) /* If we're not trusting the FADT, we should at least make it @@ -153,6 +146,20 @@ static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) else yr += cent * 100; + /* + * Show correct dates for alarms up to a month into the future. + * This solves issues for nearly all situations with the common + * 30-day alarm clocks in PC hardware. + */ + if (day < today) { + if (mo < 12) { + mo += 1; + } else { + mo = 1; + yr += 1; + } + } + seq_printf(seq, "%4.4u-", yr); (mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo); (day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day); @@ -227,13 +234,11 @@ acpi_system_write_alarm(struct file *file, int adjust = 0; unsigned char rtc_control = 0; - ACPI_FUNCTION_TRACE("acpi_system_write_alarm"); - if (count > sizeof(alarm_string) - 1) - return_VALUE(-EINVAL); + return -EINVAL; if (copy_from_user(alarm_string, buffer, count)) - return_VALUE(-EFAULT); + return -EFAULT; alarm_string[count] = '\0'; @@ -334,7 +339,7 @@ acpi_system_write_alarm(struct file *file, result = 0; end: - return_VALUE(result ? result : count); + return result ? result : count; } #endif /* HAVE_ACPI_LEGACY_ALARM */ diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index a6b662c00b67..93f91142d7ad 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -42,7 +42,7 @@ void acpi_reboot(void) case ACPI_ADR_SPACE_SYSTEM_MEMORY: case ACPI_ADR_SPACE_SYSTEM_IO: printk(KERN_DEBUG "ACPI MEMORY or I/O RESET_REG.\n"); - acpi_hw_low_level_write(8, reset_value, rr); + acpi_reset(); break; } /* Wait ten seconds */ diff --git a/drivers/acpi/resources/Makefile b/drivers/acpi/resources/Makefile deleted file mode 100644 index 8de4f69dfa09..000000000000 --- a/drivers/acpi/resources/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := rsaddr.o rscreate.o rsinfo.o rsio.o rslist.o rsmisc.o rsxface.o \ - rscalc.o rsirq.o rsmemory.o rsutils.o - -obj-$(ACPI_FUTURE_USAGE) += rsdump.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index e53e590252c0..0619734895b2 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -10,7 +10,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#include <acpi/actypes.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 39b7233c3485..c54d7b6c4066 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -10,7 +10,6 @@ #include <linux/kthread.h> #include <acpi/acpi_drivers.h> -#include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ #define _COMPONENT ACPI_BUS_COMPONENT ACPI_MODULE_NAME("scan"); diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep.h index cfaf8f5b0a14..cfaf8f5b0a14 100644 --- a/drivers/acpi/sleep/sleep.h +++ b/drivers/acpi/sleep.h diff --git a/drivers/acpi/sleep/Makefile b/drivers/acpi/sleep/Makefile deleted file mode 100644 index f1fb888c2d29..000000000000 --- a/drivers/acpi/sleep/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-y := wakeup.o -obj-y += main.o -obj-$(CONFIG_ACPI_SLEEP) += proc.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 6e4107f82403..391d0358a592 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -192,65 +192,6 @@ static struct attribute_group interrupt_stats_attr_group = { }; static struct kobj_attribute *counter_attrs; -static int count_num_gpes(void) -{ - int count = 0; - struct acpi_gpe_xrupt_info *gpe_xrupt_info; - struct acpi_gpe_block_info *gpe_block; - acpi_cpu_flags flags; - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; - while (gpe_xrupt_info) { - gpe_block = gpe_xrupt_info->gpe_block_list_head; - while (gpe_block) { - count += gpe_block->register_count * - ACPI_GPE_REGISTER_WIDTH; - gpe_block = gpe_block->next; - } - gpe_xrupt_info = gpe_xrupt_info->next; - } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - - return count; -} - -static int get_gpe_device(int index, acpi_handle *handle) -{ - struct acpi_gpe_xrupt_info *gpe_xrupt_info; - struct acpi_gpe_block_info *gpe_block; - acpi_cpu_flags flags; - struct acpi_namespace_node *node; - - flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); - - gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; - while (gpe_xrupt_info) { - gpe_block = gpe_xrupt_info->gpe_block_list_head; - node = gpe_block->node; - while (gpe_block) { - index -= gpe_block->register_count * - ACPI_GPE_REGISTER_WIDTH; - if (index < 0) { - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - /* return NULL if it's FADT GPE */ - if (node->type != ACPI_TYPE_DEVICE) - *handle = NULL; - else - *handle = node; - return 0; - } - node = gpe_block->node; - gpe_block = gpe_block->next; - } - gpe_xrupt_info = gpe_xrupt_info->next; - } - acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - - return -ENODEV; -} - static void delete_gpe_attr_array(void) { struct event_counter *tmp = all_counters; @@ -309,7 +250,7 @@ static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) goto end; if (index < num_gpes) { - result = get_gpe_device(index, handle); + result = acpi_get_gpe_device(index, handle); if (result) { ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, "Invalid GPE 0x%x\n", index)); @@ -436,7 +377,7 @@ void acpi_irq_stats_init(void) if (all_counters) return; - num_gpes = count_num_gpes(); + num_gpes = acpi_current_gpe_count; num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA; all_attrs = kzalloc(sizeof(struct attribute *) * (num_counters + 1), diff --git a/drivers/acpi/tables/Makefile b/drivers/acpi/tables/Makefile deleted file mode 100644 index 7385efa61622..000000000000 --- a/drivers/acpi/tables/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/utilities/Makefile b/drivers/acpi/utilities/Makefile deleted file mode 100644 index 88eff14c4894..000000000000 --- a/drivers/acpi/utilities/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for all Linux ACPI interpreter subdirectories -# - -obj-y := utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ - utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ - utstate.o utmutex.o utobject.o utcache.o utresrc.o - -EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/utilities/utcache.c b/drivers/acpi/utilities/utcache.c deleted file mode 100644 index 245fa80cf600..000000000000 --- a/drivers/acpi/utilities/utcache.c +++ /dev/null @@ -1,314 +0,0 @@ -/****************************************************************************** - * - * Module Name: utcache - local cache allocation routines - * - *****************************************************************************/ - -/* - * Copyright (C) 2000 - 2008, Intel Corp. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * 3. Neither the names of the above-listed copyright holders nor the names - * of any contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. - */ - -#include <acpi/acpi.h> - -#define _COMPONENT ACPI_UTILITIES -ACPI_MODULE_NAME("utcache") -#ifdef ACPI_USE_LOCAL_CACHE -/******************************************************************************* - * - * FUNCTION: acpi_os_create_cache - * - * PARAMETERS: cache_name - Ascii name for the cache - * object_size - Size of each cached object - * max_depth - Maximum depth of the cache (in objects) - * return_cache - Where the new cache object is returned - * - * RETURN: Status - * - * DESCRIPTION: Create a cache object - * - ******************************************************************************/ -acpi_status -acpi_os_create_cache(char *cache_name, - u16 object_size, - u16 max_depth, struct acpi_memory_list ** return_cache) -{ - struct acpi_memory_list *cache; - - ACPI_FUNCTION_ENTRY(); - - if (!cache_name || !return_cache || (object_size < 16)) { - return (AE_BAD_PARAMETER); - } - - /* Create the cache object */ - - cache = acpi_os_allocate(sizeof(struct acpi_memory_list)); - if (!cache) { - return (AE_NO_MEMORY); - } - - /* Populate the cache object and return it */ - - ACPI_MEMSET(cache, 0, sizeof(struct acpi_memory_list)); - cache->link_offset = 8; - cache->list_name = cache_name; - cache->object_size = object_size; - cache->max_depth = max_depth; - - *return_cache = cache; - return (AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_purge_cache - * - * PARAMETERS: Cache - Handle to cache object - * - * RETURN: Status - * - * DESCRIPTION: Free all objects within the requested cache. - * - ******************************************************************************/ - -acpi_status acpi_os_purge_cache(struct acpi_memory_list * cache) -{ - char *next; - - ACPI_FUNCTION_ENTRY(); - - if (!cache) { - return (AE_BAD_PARAMETER); - } - - /* Walk the list of objects in this cache */ - - while (cache->list_head) { - - /* Delete and unlink one cached state object */ - - next = *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *)cache-> - list_head)[cache-> - link_offset]))); - ACPI_FREE(cache->list_head); - - cache->list_head = next; - cache->current_depth--; - } - - return (AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_delete_cache - * - * PARAMETERS: Cache - Handle to cache object - * - * RETURN: Status - * - * DESCRIPTION: Free all objects within the requested cache and delete the - * cache object. - * - ******************************************************************************/ - -acpi_status acpi_os_delete_cache(struct acpi_memory_list * cache) -{ - acpi_status status; - - ACPI_FUNCTION_ENTRY(); - - /* Purge all objects in the cache */ - - status = acpi_os_purge_cache(cache); - if (ACPI_FAILURE(status)) { - return (status); - } - - /* Now we can delete the cache object */ - - ACPI_FREE(cache); - return (AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_release_object - * - * PARAMETERS: Cache - Handle to cache object - * Object - The object to be released - * - * RETURN: None - * - * DESCRIPTION: Release an object to the specified cache. If cache is full, - * the object is deleted. - * - ******************************************************************************/ - -acpi_status -acpi_os_release_object(struct acpi_memory_list * cache, void *object) -{ - acpi_status status; - - ACPI_FUNCTION_ENTRY(); - - if (!cache || !object) { - return (AE_BAD_PARAMETER); - } - - /* If cache is full, just free this object */ - - if (cache->current_depth >= cache->max_depth) { - ACPI_FREE(object); - ACPI_MEM_TRACKING(cache->total_freed++); - } - - /* Otherwise put this object back into the cache */ - - else { - status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); - if (ACPI_FAILURE(status)) { - return (status); - } - - /* Mark the object as cached */ - - ACPI_MEMSET(object, 0xCA, cache->object_size); - ACPI_SET_DESCRIPTOR_TYPE(object, ACPI_DESC_TYPE_CACHED); - - /* Put the object at the head of the cache list */ - - *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *)object)[cache-> - link_offset]))) = - cache->list_head; - cache->list_head = object; - cache->current_depth++; - - (void)acpi_ut_release_mutex(ACPI_MTX_CACHES); - } - - return (AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_os_acquire_object - * - * PARAMETERS: Cache - Handle to cache object - * - * RETURN: the acquired object. NULL on error - * - * DESCRIPTION: Get an object from the specified cache. If cache is empty, - * the object is allocated. - * - ******************************************************************************/ - -void *acpi_os_acquire_object(struct acpi_memory_list *cache) -{ - acpi_status status; - void *object; - - ACPI_FUNCTION_NAME(os_acquire_object); - - if (!cache) { - return (NULL); - } - - status = acpi_ut_acquire_mutex(ACPI_MTX_CACHES); - if (ACPI_FAILURE(status)) { - return (NULL); - } - - ACPI_MEM_TRACKING(cache->requests++); - - /* Check the cache first */ - - if (cache->list_head) { - - /* There is an object available, use it */ - - object = cache->list_head; - cache->list_head = *(ACPI_CAST_INDIRECT_PTR(char, - &(((char *) - object)[cache-> - link_offset]))); - - cache->current_depth--; - - ACPI_MEM_TRACKING(cache->hits++); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Object %p from %s cache\n", object, - cache->list_name)); - - status = acpi_ut_release_mutex(ACPI_MTX_CACHES); - if (ACPI_FAILURE(status)) { - return (NULL); - } - - /* Clear (zero) the previously used Object */ - - ACPI_MEMSET(object, 0, cache->object_size); - } else { - /* The cache is empty, create a new object */ - - ACPI_MEM_TRACKING(cache->total_allocated++); - -#ifdef ACPI_DBG_TRACK_ALLOCATIONS - if ((cache->total_allocated - cache->total_freed) > - cache->max_occupied) { - cache->max_occupied = - cache->total_allocated - cache->total_freed; - } -#endif - - /* Avoid deadlock with ACPI_ALLOCATE_ZEROED */ - - status = acpi_ut_release_mutex(ACPI_MTX_CACHES); - if (ACPI_FAILURE(status)) { - return (NULL); - } - - object = ACPI_ALLOCATE_ZEROED(cache->object_size); - if (!object) { - return (NULL); - } - } - - return (object); -} -#endif /* ACPI_USE_LOCAL_CACHE */ diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index baa441929720..f261737636da 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -36,6 +36,7 @@ #include <linux/backlight.h> #include <linux/thermal.h> #include <linux/video_output.h> +#include <linux/sort.h> #include <asm/uaccess.h> #include <acpi/acpi_bus.h> @@ -481,6 +482,7 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) int status = AE_OK; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; + int state; arg0.integer.value = level; @@ -489,6 +491,10 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) status = acpi_evaluate_object(device->dev->handle, "_BCM", &args, NULL); device->brightness->curr = level; + for (state = 2; state < device->brightness->count; state++) + if (level == device->brightness->levels[state]) + device->backlight->props.brightness = state - 2; + return status; } @@ -626,6 +632,16 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) } /* + * Simple comparison function used to sort backlight levels. + */ + +static int +acpi_video_cmp_level(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +/* * Arg: * device : video output device (LCD, CRT, ..) * @@ -676,6 +692,10 @@ acpi_video_init_brightness(struct acpi_video_device *device) count++; } + /* don't sort the first two brightness levels */ + sort(&br->levels[2], count - 2, sizeof(br->levels[2]), + acpi_video_cmp_level, NULL); + if (count < 2) goto out_free_levels; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index f022eb6f5637..50e3d2dbf3af 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -234,7 +234,7 @@ EXPORT_SYMBOL(acpi_video_display_switch_support); * To force that backlight or display output switching is processed by vendor * specific acpi drivers or video.ko driver. */ -int __init acpi_backlight(char *str) +static int __init acpi_backlight(char *str) { if (str == NULL || *str == '\0') return 1; @@ -250,7 +250,7 @@ int __init acpi_backlight(char *str) } __setup("acpi_backlight=", acpi_backlight); -int __init acpi_display_output(char *str) +static int __init acpi_display_output(char *str) { if (str == NULL || *str == '\0') return 1; diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/wakeup.c index dea4c23df764..2d34806d45dd 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/wakeup.c @@ -8,7 +8,6 @@ #include <acpi/acpi_drivers.h> #include <linux/kernel.h> #include <linux/types.h> -#include <acpi/acevents.h> #include "sleep.h" #define _COMPONENT ACPI_SYSTEM_COMPONENT @@ -28,8 +27,6 @@ void acpi_enable_wakeup_device_prep(u8 sleep_state) { struct list_head *node, *next; - ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_prep"); - spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = container_of(node, @@ -61,7 +58,6 @@ void acpi_enable_wakeup_device(u8 sleep_state) * Caution: this routine must be invoked when interrupt is disabled * Refer ACPI2.0: P212 */ - ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device"); spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = @@ -103,8 +99,6 @@ void acpi_disable_wakeup_device(u8 sleep_state) { struct list_head *node, *next; - ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device"); - spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 6b94fb7be5f2..00c46e0b40e4 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -12,9 +12,10 @@ #include <linux/device.h> #include <linux/string.h> #include <linux/slab.h> +#include <linux/io.h> #include <linux/amba/bus.h> -#include <asm/io.h> +#include <asm/irq.h> #include <asm/sizes.h> #define to_amba_device(d) container_of(d, struct amba_device, dev) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 656448c7fef9..96039671e3b9 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -105,7 +105,7 @@ enum { board_ahci_ign_iferr = 2, board_ahci_sb600 = 3, board_ahci_mv = 4, - board_ahci_sb700 = 5, + board_ahci_sb700 = 5, /* for SB700 and SB800 */ board_ahci_mcp65 = 6, board_ahci_nopmp = 7, @@ -439,7 +439,7 @@ static const struct ata_port_info ahci_port_info[] = { .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, }, - /* board_ahci_sb700 */ + /* board_ahci_sb700, for SB700 and SB800 */ { AHCI_HFLAGS (AHCI_HFLAG_IGN_SERR_INTERNAL), .flags = AHCI_FLAG_COMMON, @@ -2446,6 +2446,8 @@ static void ahci_print_info(struct ata_host *host) speed_s = "1.5"; else if (speed == 2) speed_s = "3"; + else if (speed == 3) + speed_s = "6"; else speed_s = "?"; @@ -2610,6 +2612,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) (pdev->revision == 0xa1 || pdev->revision == 0xa2)) hpriv->flags |= AHCI_HFLAG_NO_MSI; + /* SB800 does NOT need the workaround to ignore SERR_INTERNAL */ + if (board_id == board_ahci_sb700 && pdev->revision >= 0x40) + hpriv->flags &= ~AHCI_HFLAG_IGN_SERR_INTERNAL; + if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev)) pci_intx(pdev, 1); @@ -2654,6 +2660,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) host->iomap = pcim_iomap_table(pdev); host->private_data = hpriv; + if (!(hpriv->cap & HOST_CAP_SSS)) + host->flags |= ATA_HOST_PARALLEL_SCAN; + if (pi.flags & ATA_FLAG_EM) ahci_reset_em(host); diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 5fdf1678d0cc..887d8f46a287 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -154,11 +154,13 @@ struct piix_map_db { struct piix_host_priv { const int *map; + u32 saved_iocfg; void __iomem *sidpr; }; static int piix_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +static void piix_remove_one(struct pci_dev *pdev); static int piix_pata_prereset(struct ata_link *link, unsigned long deadline); static void piix_set_piomode(struct ata_port *ap, struct ata_device *adev); static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev); @@ -296,7 +298,7 @@ static struct pci_driver piix_pci_driver = { .name = DRV_NAME, .id_table = piix_pci_tbl, .probe = piix_init_one, - .remove = ata_pci_remove_one, + .remove = piix_remove_one, #ifdef CONFIG_PM .suspend = piix_pci_device_suspend, .resume = piix_pci_device_resume, @@ -308,7 +310,7 @@ static struct scsi_host_template piix_sht = { }; static struct ata_port_operations piix_pata_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_bmdma32_port_ops, .cable_detect = ata_cable_40wire, .set_piomode = piix_set_piomode, .set_dmamode = piix_set_dmamode, @@ -610,8 +612,9 @@ static const struct ich_laptop ich_laptop[] = { static int ich_pata_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct piix_host_priv *hpriv = ap->host->private_data; const struct ich_laptop *lap = &ich_laptop[0]; - u8 tmp, mask; + u8 mask; /* Check for specials - Acer Aspire 5602WLMi */ while (lap->device) { @@ -625,8 +628,7 @@ static int ich_pata_cable_detect(struct ata_port *ap) /* check BIOS cable detect results */ mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; - pci_read_config_byte(pdev, PIIX_IOCFG, &tmp); - if ((tmp & mask) == 0) + if ((hpriv->saved_iocfg & mask) == 0) return ATA_CBL_PATA40; return ATA_CBL_PATA80; } @@ -1350,7 +1352,7 @@ static int __devinit piix_init_sidpr(struct ata_host *host) return 0; } -static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) +static void piix_iocfg_bit18_quirk(struct ata_host *host) { static const struct dmi_system_id sysids[] = { { @@ -1367,7 +1369,8 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) { } /* terminate list */ }; - u32 iocfg; + struct pci_dev *pdev = to_pci_dev(host->dev); + struct piix_host_priv *hpriv = host->private_data; if (!dmi_check_system(sysids)) return; @@ -1376,12 +1379,11 @@ static void piix_iocfg_bit18_quirk(struct pci_dev *pdev) * seem to use it to disable a channel. Clear the bit on the * affected systems. */ - pci_read_config_dword(pdev, PIIX_IOCFG, &iocfg); - if (iocfg & (1 << 18)) { + if (hpriv->saved_iocfg & (1 << 18)) { dev_printk(KERN_INFO, &pdev->dev, "applying IOCFG bit18 quirk\n"); - iocfg &= ~(1 << 18); - pci_write_config_dword(pdev, PIIX_IOCFG, iocfg); + pci_write_config_dword(pdev, PIIX_IOCFG, + hpriv->saved_iocfg & ~(1 << 18)); } } @@ -1430,6 +1432,17 @@ static int __devinit piix_init_one(struct pci_dev *pdev, if (rc) return rc; + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) + return -ENOMEM; + + /* Save IOCFG, this will be used for cable detection, quirk + * detection and restoration on detach. This is necessary + * because some ACPI implementations mess up cable related + * bits on _STM. Reported on kernel bz#11879. + */ + pci_read_config_dword(pdev, PIIX_IOCFG, &hpriv->saved_iocfg); + /* ICH6R may be driven by either ata_piix or ahci driver * regardless of BIOS configuration. Make sure AHCI mode is * off. @@ -1441,10 +1454,6 @@ static int __devinit piix_init_one(struct pci_dev *pdev, } /* SATA map init can change port_info, do it before prepping host */ - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - if (port_flags & ATA_FLAG_SATA) hpriv->map = piix_init_sata_map(pdev, port_info, piix_map_db_table[ent->driver_data]); @@ -1463,7 +1472,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev, } /* apply IOCFG bit18 quirk */ - piix_iocfg_bit18_quirk(pdev); + piix_iocfg_bit18_quirk(host); /* On ICH5, some BIOSen disable the interrupt using the * PCI_COMMAND_INTX_DISABLE bit added in PCI 2.3. @@ -1488,6 +1497,16 @@ static int __devinit piix_init_one(struct pci_dev *pdev, return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht); } +static void piix_remove_one(struct pci_dev *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct piix_host_priv *hpriv = host->private_data; + + pci_write_config_dword(pdev, PIIX_IOCFG, hpriv->saved_iocfg); + + ata_pci_remove_one(pdev); +} + static int __init piix_init(void) { int rc; diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index ef02e488d468..6273d98d00eb 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -19,12 +19,6 @@ #include "libata.h" #include <acpi/acpi_bus.h> -#include <acpi/acnames.h> -#include <acpi/acnamesp.h> -#include <acpi/acparser.h> -#include <acpi/acexcep.h> -#include <acpi/acmacros.h> -#include <acpi/actypes.h> enum { ATA_ACPI_FILTER_SETXFER = 1 << 0, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index fecca4223f8e..71218d76d75e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -56,6 +56,7 @@ #include <linux/workqueue.h> #include <linux/scatterlist.h> #include <linux/io.h> +#include <linux/async.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_host.h> @@ -1006,6 +1007,7 @@ static const char *sata_spd_string(unsigned int spd) static const char * const spd_str[] = { "1.5 Gbps", "3.0 Gbps", + "6.0 Gbps", }; if (spd == 0 || (spd - 1) >= ARRAY_SIZE(spd_str)) @@ -1999,6 +2001,10 @@ unsigned int ata_pio_need_iordy(const struct ata_device *adev) as the caller should know this */ if (adev->link->ap->flags & ATA_FLAG_NO_IORDY) return 0; + /* CF spec. r4.1 Table 22 says no iordy on PIO5 and PIO6. */ + if (ata_id_is_cfa(adev->id) + && (adev->pio_mode == XFER_PIO_5 || adev->pio_mode == XFER_PIO_6)) + return 0; /* PIO3 and higher it is mandatory */ if (adev->pio_mode > XFER_PIO_2) return 1; @@ -4550,7 +4556,7 @@ void ata_sg_clean(struct ata_queued_cmd *qc) struct scatterlist *sg = qc->sg; int dir = qc->dma_dir; - WARN_ON(sg == NULL); + WARN_ON_ONCE(sg == NULL); VPRINTK("unmapping %u sg elements\n", qc->n_elem); @@ -4770,7 +4776,7 @@ void ata_qc_free(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; unsigned int tag; - WARN_ON(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ + WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ qc->flags = 0; tag = qc->tag; @@ -4785,8 +4791,8 @@ void __ata_qc_complete(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct ata_link *link = qc->dev->link; - WARN_ON(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ - WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE)); + WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */ + WARN_ON_ONCE(!(qc->flags & ATA_QCFLAG_ACTIVE)); if (likely(qc->flags & ATA_QCFLAG_DMAMAP)) ata_sg_clean(qc); @@ -4872,7 +4878,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc) struct ata_device *dev = qc->dev; struct ata_eh_info *ehi = &dev->link->eh_info; - WARN_ON(ap->pflags & ATA_PFLAG_FROZEN); + WARN_ON_ONCE(ap->pflags & ATA_PFLAG_FROZEN); if (unlikely(qc->err_mask)) qc->flags |= ATA_QCFLAG_FAILED; @@ -4994,16 +5000,16 @@ void ata_qc_issue(struct ata_queued_cmd *qc) * check is skipped for old EH because it reuses active qc to * request ATAPI sense. */ - WARN_ON(ap->ops->error_handler && ata_tag_valid(link->active_tag)); + WARN_ON_ONCE(ap->ops->error_handler && ata_tag_valid(link->active_tag)); if (ata_is_ncq(prot)) { - WARN_ON(link->sactive & (1 << qc->tag)); + WARN_ON_ONCE(link->sactive & (1 << qc->tag)); if (!link->sactive) ap->nr_active_links++; link->sactive |= 1 << qc->tag; } else { - WARN_ON(link->sactive); + WARN_ON_ONCE(link->sactive); ap->nr_active_links++; link->active_tag = qc->tag; @@ -5909,6 +5915,65 @@ void ata_host_init(struct ata_host *host, struct device *dev, host->ops = ops; } + +static void async_port_probe(void *data, async_cookie_t cookie) +{ + int rc; + struct ata_port *ap = data; + + /* + * If we're not allowed to scan this host in parallel, + * we need to wait until all previous scans have completed + * before going further. + * Jeff Garzik says this is only within a controller, so we + * don't need to wait for port 0, only for later ports. + */ + if (!(ap->host->flags & ATA_HOST_PARALLEL_SCAN) && ap->port_no != 0) + async_synchronize_cookie(cookie); + + /* probe */ + if (ap->ops->error_handler) { + struct ata_eh_info *ehi = &ap->link.eh_info; + unsigned long flags; + + ata_port_probe(ap); + + /* kick EH for boot probing */ + spin_lock_irqsave(ap->lock, flags); + + ehi->probe_mask |= ATA_ALL_DEVICES; + ehi->action |= ATA_EH_RESET | ATA_EH_LPM; + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; + + ap->pflags &= ~ATA_PFLAG_INITIALIZING; + ap->pflags |= ATA_PFLAG_LOADING; + ata_port_schedule_eh(ap); + + spin_unlock_irqrestore(ap->lock, flags); + + /* wait for EH to finish */ + ata_port_wait_eh(ap); + } else { + DPRINTK("ata%u: bus probe begin\n", ap->print_id); + rc = ata_bus_probe(ap); + DPRINTK("ata%u: bus probe end\n", ap->print_id); + + if (rc) { + /* FIXME: do something useful here? + * Current libata behavior will + * tear down everything when + * the module is removed + * or the h/w is unplugged. + */ + } + } + + /* in order to keep device order, we need to synchronize at this point */ + async_synchronize_cookie(cookie); + + ata_scsi_scan_host(ap, 1); + +} /** * ata_host_register - register initialized ATA host * @host: ATA host to register @@ -5988,52 +6053,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) DPRINTK("probe begin\n"); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - - /* probe */ - if (ap->ops->error_handler) { - struct ata_eh_info *ehi = &ap->link.eh_info; - unsigned long flags; - - ata_port_probe(ap); - - /* kick EH for boot probing */ - spin_lock_irqsave(ap->lock, flags); - - ehi->probe_mask |= ATA_ALL_DEVICES; - ehi->action |= ATA_EH_RESET | ATA_EH_LPM; - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - - ap->pflags &= ~ATA_PFLAG_INITIALIZING; - ap->pflags |= ATA_PFLAG_LOADING; - ata_port_schedule_eh(ap); - - spin_unlock_irqrestore(ap->lock, flags); - - /* wait for EH to finish */ - ata_port_wait_eh(ap); - } else { - DPRINTK("ata%u: bus probe begin\n", ap->print_id); - rc = ata_bus_probe(ap); - DPRINTK("ata%u: bus probe end\n", ap->print_id); - - if (rc) { - /* FIXME: do something useful here? - * Current libata behavior will - * tear down everything when - * the module is removed - * or the h/w is unplugged. - */ - } - } - } - - /* probes are done, now scan each port's disk(s) */ - DPRINTK("host probe begin\n"); - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - ata_scsi_scan_host(ap, 1); + async_schedule(async_port_probe, ap); } + DPRINTK("probe end\n"); return 0; } diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 9033d164c4ec..0eae9b453556 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -66,6 +66,7 @@ const struct ata_port_operations ata_sff_port_ops = { .port_start = ata_sff_port_start, }; +EXPORT_SYMBOL_GPL(ata_sff_port_ops); const struct ata_port_operations ata_bmdma_port_ops = { .inherits = &ata_sff_port_ops, @@ -77,6 +78,14 @@ const struct ata_port_operations ata_bmdma_port_ops = { .bmdma_stop = ata_bmdma_stop, .bmdma_status = ata_bmdma_status, }; +EXPORT_SYMBOL_GPL(ata_bmdma_port_ops); + +const struct ata_port_operations ata_bmdma32_port_ops = { + .inherits = &ata_bmdma_port_ops, + + .sff_data_xfer = ata_sff_data_xfer32, +}; +EXPORT_SYMBOL_GPL(ata_bmdma32_port_ops); /** * ata_fill_sg - Fill PCI IDE PRD table @@ -166,8 +175,9 @@ static void ata_fill_sg_dumb(struct ata_queued_cmd *qc) blen = len & 0xffff; ap->prd[pi].addr = cpu_to_le32(addr); if (blen == 0) { - /* Some PATA chipsets like the CS5530 can't - cope with 0x0000 meaning 64K as the spec says */ + /* Some PATA chipsets like the CS5530 can't + cope with 0x0000 meaning 64K as the spec + says */ ap->prd[pi].flags_len = cpu_to_le32(0x8000); blen = 0x8000; ap->prd[++pi].addr = cpu_to_le32(addr + 0x8000); @@ -200,6 +210,7 @@ void ata_sff_qc_prep(struct ata_queued_cmd *qc) ata_fill_sg(qc); } +EXPORT_SYMBOL_GPL(ata_sff_qc_prep); /** * ata_sff_dumb_qc_prep - Prepare taskfile for submission @@ -217,6 +228,7 @@ void ata_sff_dumb_qc_prep(struct ata_queued_cmd *qc) ata_fill_sg_dumb(qc); } +EXPORT_SYMBOL_GPL(ata_sff_dumb_qc_prep); /** * ata_sff_check_status - Read device status reg & clear interrupt @@ -233,6 +245,7 @@ u8 ata_sff_check_status(struct ata_port *ap) { return ioread8(ap->ioaddr.status_addr); } +EXPORT_SYMBOL_GPL(ata_sff_check_status); /** * ata_sff_altstatus - Read device alternate status reg @@ -275,7 +288,7 @@ static u8 ata_sff_irq_status(struct ata_port *ap) status = ata_sff_altstatus(ap); /* Not us: We are busy */ if (status & ATA_BUSY) - return status; + return status; } /* Clear INTRQ latch */ status = ap->ops->sff_check_status(ap); @@ -319,6 +332,7 @@ void ata_sff_pause(struct ata_port *ap) ata_sff_sync(ap); ndelay(400); } +EXPORT_SYMBOL_GPL(ata_sff_pause); /** * ata_sff_dma_pause - Pause before commencing DMA @@ -327,7 +341,7 @@ void ata_sff_pause(struct ata_port *ap) * Perform I/O fencing and ensure sufficient cycle delays occur * for the HDMA1:0 transition */ - + void ata_sff_dma_pause(struct ata_port *ap) { if (ap->ops->sff_check_altstatus || ap->ioaddr.altstatus_addr) { @@ -341,6 +355,7 @@ void ata_sff_dma_pause(struct ata_port *ap) corruption. */ BUG(); } +EXPORT_SYMBOL_GPL(ata_sff_dma_pause); /** * ata_sff_busy_sleep - sleep until BSY clears, or timeout @@ -396,6 +411,7 @@ int ata_sff_busy_sleep(struct ata_port *ap, return 0; } +EXPORT_SYMBOL_GPL(ata_sff_busy_sleep); static int ata_sff_check_ready(struct ata_link *link) { @@ -422,6 +438,7 @@ int ata_sff_wait_ready(struct ata_link *link, unsigned long deadline) { return ata_wait_ready(link, deadline, ata_sff_check_ready); } +EXPORT_SYMBOL_GPL(ata_sff_wait_ready); /** * ata_sff_dev_select - Select device 0/1 on ATA bus @@ -449,6 +466,7 @@ void ata_sff_dev_select(struct ata_port *ap, unsigned int device) iowrite8(tmp, ap->ioaddr.device_addr); ata_sff_pause(ap); /* needed; also flushes, for mmio */ } +EXPORT_SYMBOL_GPL(ata_sff_dev_select); /** * ata_dev_select - Select device 0/1 on ATA bus @@ -513,6 +531,7 @@ u8 ata_sff_irq_on(struct ata_port *ap) return tmp; } +EXPORT_SYMBOL_GPL(ata_sff_irq_on); /** * ata_sff_irq_clear - Clear PCI IDE BMDMA interrupt. @@ -534,6 +553,7 @@ void ata_sff_irq_clear(struct ata_port *ap) iowrite8(ioread8(mmio + ATA_DMA_STATUS), mmio + ATA_DMA_STATUS); } +EXPORT_SYMBOL_GPL(ata_sff_irq_clear); /** * ata_sff_tf_load - send taskfile registers to host controller @@ -558,7 +578,7 @@ void ata_sff_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) } if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { - WARN_ON(!ioaddr->ctl_addr); + WARN_ON_ONCE(!ioaddr->ctl_addr); iowrite8(tf->hob_feature, ioaddr->feature_addr); iowrite8(tf->hob_nsect, ioaddr->nsect_addr); iowrite8(tf->hob_lbal, ioaddr->lbal_addr); @@ -593,6 +613,7 @@ void ata_sff_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) ata_wait_idle(ap); } +EXPORT_SYMBOL_GPL(ata_sff_tf_load); /** * ata_sff_tf_read - input device's ATA taskfile shadow registers @@ -630,9 +651,10 @@ void ata_sff_tf_read(struct ata_port *ap, struct ata_taskfile *tf) iowrite8(tf->ctl, ioaddr->ctl_addr); ap->last_ctl = tf->ctl; } else - WARN_ON(1); + WARN_ON_ONCE(1); } } +EXPORT_SYMBOL_GPL(ata_sff_tf_read); /** * ata_sff_exec_command - issue ATA command to host controller @@ -652,6 +674,7 @@ void ata_sff_exec_command(struct ata_port *ap, const struct ata_taskfile *tf) iowrite8(tf->command, ap->ioaddr.command_addr); ata_sff_pause(ap); } +EXPORT_SYMBOL_GPL(ata_sff_exec_command); /** * ata_tf_to_host - issue ATA taskfile to host controller @@ -717,6 +740,53 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf, return words << 1; } +EXPORT_SYMBOL_GPL(ata_sff_data_xfer); + +/** + * ata_sff_data_xfer32 - Transfer data by PIO + * @dev: device to target + * @buf: data buffer + * @buflen: buffer length + * @rw: read/write + * + * Transfer data from/to the device data register by PIO using 32bit + * I/O operations. + * + * LOCKING: + * Inherited from caller. + * + * RETURNS: + * Bytes consumed. + */ + +unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf, + unsigned int buflen, int rw) +{ + struct ata_port *ap = dev->link->ap; + void __iomem *data_addr = ap->ioaddr.data_addr; + unsigned int words = buflen >> 2; + int slop = buflen & 3; + + /* Transfer multiple of 4 bytes */ + if (rw == READ) + ioread32_rep(data_addr, buf, words); + else + iowrite32_rep(data_addr, buf, words); + + if (unlikely(slop)) { + __le32 pad; + if (rw == READ) { + pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr)); + memcpy(buf + buflen - slop, &pad, slop); + } else { + memcpy(&pad, buf + buflen - slop, slop); + iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr); + } + words++; + } + return words << 2; +} +EXPORT_SYMBOL_GPL(ata_sff_data_xfer32); /** * ata_sff_data_xfer_noirq - Transfer data by PIO @@ -746,6 +816,7 @@ unsigned int ata_sff_data_xfer_noirq(struct ata_device *dev, unsigned char *buf, return consumed; } +EXPORT_SYMBOL_GPL(ata_sff_data_xfer_noirq); /** * ata_pio_sector - Transfer a sector of data. @@ -820,7 +891,7 @@ static void ata_pio_sectors(struct ata_queued_cmd *qc) /* READ/WRITE MULTIPLE */ unsigned int nsect; - WARN_ON(qc->dev->multi_count == 0); + WARN_ON_ONCE(qc->dev->multi_count == 0); nsect = min((qc->nbytes - qc->curbytes) / qc->sect_size, qc->dev->multi_count); @@ -847,7 +918,7 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc) { /* send SCSI cdb */ DPRINTK("send cdb\n"); - WARN_ON(qc->dev->cdb_len < 12); + WARN_ON_ONCE(qc->dev->cdb_len < 12); ap->ops->sff_data_xfer(qc->dev, qc->cdb, qc->dev->cdb_len, 1); ata_sff_sync(ap); @@ -922,13 +993,15 @@ next_sg: buf = kmap_atomic(page, KM_IRQ0); /* do the actual data transfer */ - consumed = ap->ops->sff_data_xfer(dev, buf + offset, count, rw); + consumed = ap->ops->sff_data_xfer(dev, buf + offset, + count, rw); kunmap_atomic(buf, KM_IRQ0); local_irq_restore(flags); } else { buf = page_address(page); - consumed = ap->ops->sff_data_xfer(dev, buf + offset, count, rw); + consumed = ap->ops->sff_data_xfer(dev, buf + offset, + count, rw); } bytes -= min(bytes, consumed); @@ -941,7 +1014,7 @@ next_sg: } /* consumed can be larger than count only for the last transfer */ - WARN_ON(qc->cursg && count != consumed); + WARN_ON_ONCE(qc->cursg && count != consumed); if (bytes) goto next_sg; @@ -1013,18 +1086,19 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc) * RETURNS: * 1 if ok in workqueue, 0 otherwise. */ -static inline int ata_hsm_ok_in_wq(struct ata_port *ap, struct ata_queued_cmd *qc) +static inline int ata_hsm_ok_in_wq(struct ata_port *ap, + struct ata_queued_cmd *qc) { if (qc->tf.flags & ATA_TFLAG_POLLING) return 1; if (ap->hsm_task_state == HSM_ST_FIRST) { if (qc->tf.protocol == ATA_PROT_PIO && - (qc->tf.flags & ATA_TFLAG_WRITE)) + (qc->tf.flags & ATA_TFLAG_WRITE)) return 1; if (ata_is_atapi(qc->tf.protocol) && - !(qc->dev->flags & ATA_DFLAG_CDB_INTR)) + !(qc->dev->flags & ATA_DFLAG_CDB_INTR)) return 1; } @@ -1098,13 +1172,13 @@ int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, unsigned long flags = 0; int poll_next; - WARN_ON((qc->flags & ATA_QCFLAG_ACTIVE) == 0); + WARN_ON_ONCE((qc->flags & ATA_QCFLAG_ACTIVE) == 0); /* Make sure ata_sff_qc_issue() does not throw things * like DMA polling into the workqueue. Notice that * in_wq is not equivalent to (qc->tf.flags & ATA_TFLAG_POLLING). */ - WARN_ON(in_wq != ata_hsm_ok_in_wq(ap, qc)); + WARN_ON_ONCE(in_wq != ata_hsm_ok_in_wq(ap, qc)); fsm_start: DPRINTK("ata%u: protocol %d task_state %d (dev_stat 0x%X)\n", @@ -1313,7 +1387,7 @@ fsm_start: DPRINTK("ata%u: dev %u command complete, drv_stat 0x%x\n", ap->print_id, qc->dev->devno, status); - WARN_ON(qc->err_mask & (AC_ERR_DEV | AC_ERR_HSM)); + WARN_ON_ONCE(qc->err_mask & (AC_ERR_DEV | AC_ERR_HSM)); ap->hsm_task_state = HSM_ST_IDLE; @@ -1338,6 +1412,7 @@ fsm_start: return poll_next; } +EXPORT_SYMBOL_GPL(ata_sff_hsm_move); void ata_pio_task(struct work_struct *work) { @@ -1348,7 +1423,7 @@ void ata_pio_task(struct work_struct *work) int poll_next; fsm_start: - WARN_ON(ap->hsm_task_state == HSM_ST_IDLE); + WARN_ON_ONCE(ap->hsm_task_state == HSM_ST_IDLE); /* * This is purely heuristic. This is a fast path. @@ -1437,7 +1512,7 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) break; case ATA_PROT_DMA: - WARN_ON(qc->tf.flags & ATA_TFLAG_POLLING); + WARN_ON_ONCE(qc->tf.flags & ATA_TFLAG_POLLING); ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */ ap->ops->bmdma_setup(qc); /* set up bmdma */ @@ -1489,7 +1564,7 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) break; case ATAPI_PROT_DMA: - WARN_ON(qc->tf.flags & ATA_TFLAG_POLLING); + WARN_ON_ONCE(qc->tf.flags & ATA_TFLAG_POLLING); ap->ops->sff_tf_load(ap, &qc->tf); /* load tf registers */ ap->ops->bmdma_setup(qc); /* set up bmdma */ @@ -1501,12 +1576,13 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) break; default: - WARN_ON(1); + WARN_ON_ONCE(1); return AC_ERR_SYSTEM; } return 0; } +EXPORT_SYMBOL_GPL(ata_sff_qc_issue); /** * ata_sff_qc_fill_rtf - fill result TF using ->sff_tf_read @@ -1526,6 +1602,7 @@ bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc) qc->ap->ops->sff_tf_read(qc->ap, &qc->result_tf); return true; } +EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf); /** * ata_sff_host_intr - Handle host interrupt for given (port, task) @@ -1623,6 +1700,7 @@ idle_irq: #endif return 0; /* irq not handled */ } +EXPORT_SYMBOL_GPL(ata_sff_host_intr); /** * ata_sff_interrupt - Default ATA host interrupt handler @@ -1667,6 +1745,7 @@ irqreturn_t ata_sff_interrupt(int irq, void *dev_instance) return IRQ_RETVAL(handled); } +EXPORT_SYMBOL_GPL(ata_sff_interrupt); /** * ata_sff_freeze - Freeze SFF controller port @@ -1695,6 +1774,7 @@ void ata_sff_freeze(struct ata_port *ap) ap->ops->sff_irq_clear(ap); } +EXPORT_SYMBOL_GPL(ata_sff_freeze); /** * ata_sff_thaw - Thaw SFF controller port @@ -1712,6 +1792,7 @@ void ata_sff_thaw(struct ata_port *ap) ap->ops->sff_irq_clear(ap); ap->ops->sff_irq_on(ap); } +EXPORT_SYMBOL_GPL(ata_sff_thaw); /** * ata_sff_prereset - prepare SFF link for reset @@ -1753,6 +1834,7 @@ int ata_sff_prereset(struct ata_link *link, unsigned long deadline) return 0; } +EXPORT_SYMBOL_GPL(ata_sff_prereset); /** * ata_devchk - PATA device presence detection @@ -1865,6 +1947,7 @@ unsigned int ata_sff_dev_classify(struct ata_device *dev, int present, return class; } +EXPORT_SYMBOL_GPL(ata_sff_dev_classify); /** * ata_sff_wait_after_reset - wait for devices to become ready after reset @@ -1941,6 +2024,7 @@ int ata_sff_wait_after_reset(struct ata_link *link, unsigned int devmask, return ret; } +EXPORT_SYMBOL_GPL(ata_sff_wait_after_reset); static int ata_bus_softreset(struct ata_port *ap, unsigned int devmask, unsigned long deadline) @@ -2013,6 +2097,7 @@ int ata_sff_softreset(struct ata_link *link, unsigned int *classes, DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]); return 0; } +EXPORT_SYMBOL_GPL(ata_sff_softreset); /** * sata_sff_hardreset - reset host port via SATA phy reset @@ -2045,6 +2130,7 @@ int sata_sff_hardreset(struct ata_link *link, unsigned int *class, DPRINTK("EXIT, class=%u\n", *class); return rc; } +EXPORT_SYMBOL_GPL(sata_sff_hardreset); /** * ata_sff_postreset - SFF postreset callback @@ -2080,6 +2166,7 @@ void ata_sff_postreset(struct ata_link *link, unsigned int *classes) if (ap->ioaddr.ctl_addr) iowrite8(ap->ctl, ap->ioaddr.ctl_addr); } +EXPORT_SYMBOL_GPL(ata_sff_postreset); /** * ata_sff_error_handler - Stock error handler for BMDMA controller @@ -2152,6 +2239,7 @@ void ata_sff_error_handler(struct ata_port *ap) ata_do_eh(ap, ap->ops->prereset, softreset, hardreset, ap->ops->postreset); } +EXPORT_SYMBOL_GPL(ata_sff_error_handler); /** * ata_sff_post_internal_cmd - Stock post_internal_cmd for SFF controller @@ -2174,6 +2262,7 @@ void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc) spin_unlock_irqrestore(ap->lock, flags); } +EXPORT_SYMBOL_GPL(ata_sff_post_internal_cmd); /** * ata_sff_port_start - Set port up for dma. @@ -2194,6 +2283,7 @@ int ata_sff_port_start(struct ata_port *ap) return ata_port_start(ap); return 0; } +EXPORT_SYMBOL_GPL(ata_sff_port_start); /** * ata_sff_std_ports - initialize ioaddr with standard port offsets. @@ -2219,6 +2309,7 @@ void ata_sff_std_ports(struct ata_ioports *ioaddr) ioaddr->status_addr = ioaddr->cmd_addr + ATA_REG_STATUS; ioaddr->command_addr = ioaddr->cmd_addr + ATA_REG_CMD; } +EXPORT_SYMBOL_GPL(ata_sff_std_ports); unsigned long ata_bmdma_mode_filter(struct ata_device *adev, unsigned long xfer_mask) @@ -2230,6 +2321,7 @@ unsigned long ata_bmdma_mode_filter(struct ata_device *adev, xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); return xfer_mask; } +EXPORT_SYMBOL_GPL(ata_bmdma_mode_filter); /** * ata_bmdma_setup - Set up PCI IDE BMDMA transaction @@ -2258,6 +2350,7 @@ void ata_bmdma_setup(struct ata_queued_cmd *qc) /* issue r/w command */ ap->ops->sff_exec_command(ap, &qc->tf); } +EXPORT_SYMBOL_GPL(ata_bmdma_setup); /** * ata_bmdma_start - Start a PCI IDE BMDMA transaction @@ -2290,6 +2383,7 @@ void ata_bmdma_start(struct ata_queued_cmd *qc) * unneccessarily delayed for MMIO */ } +EXPORT_SYMBOL_GPL(ata_bmdma_start); /** * ata_bmdma_stop - Stop PCI IDE BMDMA transfer @@ -2314,6 +2408,7 @@ void ata_bmdma_stop(struct ata_queued_cmd *qc) /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ ata_sff_dma_pause(ap); } +EXPORT_SYMBOL_GPL(ata_bmdma_stop); /** * ata_bmdma_status - Read PCI IDE BMDMA status @@ -2330,6 +2425,7 @@ u8 ata_bmdma_status(struct ata_port *ap) { return ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); } +EXPORT_SYMBOL_GPL(ata_bmdma_status); /** * ata_bus_reset - reset host port and associated ATA channel @@ -2422,6 +2518,7 @@ err_out: DPRINTK("EXIT\n"); } +EXPORT_SYMBOL_GPL(ata_bus_reset); #ifdef CONFIG_PCI @@ -2449,6 +2546,7 @@ int ata_pci_bmdma_clear_simplex(struct pci_dev *pdev) return -EOPNOTSUPP; return 0; } +EXPORT_SYMBOL_GPL(ata_pci_bmdma_clear_simplex); /** * ata_pci_bmdma_init - acquire PCI BMDMA resources and init ATA host @@ -2501,11 +2599,12 @@ int ata_pci_bmdma_init(struct ata_host *host) host->flags |= ATA_HOST_SIMPLEX; ata_port_desc(ap, "bmdma 0x%llx", - (unsigned long long)pci_resource_start(pdev, 4) + 8 * i); + (unsigned long long)pci_resource_start(pdev, 4) + 8 * i); } return 0; } +EXPORT_SYMBOL_GPL(ata_pci_bmdma_init); static int ata_resources_present(struct pci_dev *pdev, int port) { @@ -2513,7 +2612,7 @@ static int ata_resources_present(struct pci_dev *pdev, int port) /* Check the PCI resources for this channel are enabled */ port = port * 2; - for (i = 0; i < 2; i ++) { + for (i = 0; i < 2; i++) { if (pci_resource_start(pdev, port + i) == 0 || pci_resource_len(pdev, port + i) == 0) return 0; @@ -2598,6 +2697,7 @@ int ata_pci_sff_init_host(struct ata_host *host) return 0; } +EXPORT_SYMBOL_GPL(ata_pci_sff_init_host); /** * ata_pci_sff_prepare_host - helper to prepare native PCI ATA host @@ -2615,7 +2715,7 @@ int ata_pci_sff_init_host(struct ata_host *host) * 0 on success, -errno otherwise. */ int ata_pci_sff_prepare_host(struct pci_dev *pdev, - const struct ata_port_info * const * ppi, + const struct ata_port_info * const *ppi, struct ata_host **r_host) { struct ata_host *host; @@ -2645,17 +2745,18 @@ int ata_pci_sff_prepare_host(struct pci_dev *pdev, *r_host = host; return 0; - err_bmdma: +err_bmdma: /* This is necessary because PCI and iomap resources are * merged and releasing the top group won't release the * acquired resources if some of those have been acquired * before entering this function. */ pcim_iounmap_regions(pdev, 0xf); - err_out: +err_out: devres_release_group(&pdev->dev, NULL); return rc; } +EXPORT_SYMBOL_GPL(ata_pci_sff_prepare_host); /** * ata_pci_sff_activate_host - start SFF host, request IRQ and register it @@ -2741,7 +2842,7 @@ int ata_pci_sff_activate_host(struct ata_host *host, } rc = ata_host_register(host, sht); - out: +out: if (rc == 0) devres_remove_group(dev, NULL); else @@ -2749,6 +2850,7 @@ int ata_pci_sff_activate_host(struct ata_host *host, return rc; } +EXPORT_SYMBOL_GPL(ata_pci_sff_activate_host); /** * ata_pci_sff_init_one - Initialize/register PCI IDE host controller @@ -2776,7 +2878,7 @@ int ata_pci_sff_activate_host(struct ata_host *host, * Zero on success, negative on errno-based value on error. */ int ata_pci_sff_init_one(struct pci_dev *pdev, - const struct ata_port_info * const * ppi, + const struct ata_port_info * const *ppi, struct scsi_host_template *sht, void *host_priv) { struct device *dev = &pdev->dev; @@ -2815,7 +2917,7 @@ int ata_pci_sff_init_one(struct pci_dev *pdev, pci_set_master(pdev); rc = ata_pci_sff_activate_host(host, ata_sff_interrupt, sht); - out: +out: if (rc == 0) devres_remove_group(&pdev->dev, NULL); else @@ -2823,54 +2925,7 @@ int ata_pci_sff_init_one(struct pci_dev *pdev, return rc; } +EXPORT_SYMBOL_GPL(ata_pci_sff_init_one); #endif /* CONFIG_PCI */ -EXPORT_SYMBOL_GPL(ata_sff_port_ops); -EXPORT_SYMBOL_GPL(ata_bmdma_port_ops); -EXPORT_SYMBOL_GPL(ata_sff_qc_prep); -EXPORT_SYMBOL_GPL(ata_sff_dumb_qc_prep); -EXPORT_SYMBOL_GPL(ata_sff_dev_select); -EXPORT_SYMBOL_GPL(ata_sff_check_status); -EXPORT_SYMBOL_GPL(ata_sff_dma_pause); -EXPORT_SYMBOL_GPL(ata_sff_pause); -EXPORT_SYMBOL_GPL(ata_sff_busy_sleep); -EXPORT_SYMBOL_GPL(ata_sff_wait_ready); -EXPORT_SYMBOL_GPL(ata_sff_tf_load); -EXPORT_SYMBOL_GPL(ata_sff_tf_read); -EXPORT_SYMBOL_GPL(ata_sff_exec_command); -EXPORT_SYMBOL_GPL(ata_sff_data_xfer); -EXPORT_SYMBOL_GPL(ata_sff_data_xfer_noirq); -EXPORT_SYMBOL_GPL(ata_sff_irq_on); -EXPORT_SYMBOL_GPL(ata_sff_irq_clear); -EXPORT_SYMBOL_GPL(ata_sff_hsm_move); -EXPORT_SYMBOL_GPL(ata_sff_qc_issue); -EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf); -EXPORT_SYMBOL_GPL(ata_sff_host_intr); -EXPORT_SYMBOL_GPL(ata_sff_interrupt); -EXPORT_SYMBOL_GPL(ata_sff_freeze); -EXPORT_SYMBOL_GPL(ata_sff_thaw); -EXPORT_SYMBOL_GPL(ata_sff_prereset); -EXPORT_SYMBOL_GPL(ata_sff_dev_classify); -EXPORT_SYMBOL_GPL(ata_sff_wait_after_reset); -EXPORT_SYMBOL_GPL(ata_sff_softreset); -EXPORT_SYMBOL_GPL(sata_sff_hardreset); -EXPORT_SYMBOL_GPL(ata_sff_postreset); -EXPORT_SYMBOL_GPL(ata_sff_error_handler); -EXPORT_SYMBOL_GPL(ata_sff_post_internal_cmd); -EXPORT_SYMBOL_GPL(ata_sff_port_start); -EXPORT_SYMBOL_GPL(ata_sff_std_ports); -EXPORT_SYMBOL_GPL(ata_bmdma_mode_filter); -EXPORT_SYMBOL_GPL(ata_bmdma_setup); -EXPORT_SYMBOL_GPL(ata_bmdma_start); -EXPORT_SYMBOL_GPL(ata_bmdma_stop); -EXPORT_SYMBOL_GPL(ata_bmdma_status); -EXPORT_SYMBOL_GPL(ata_bus_reset); -#ifdef CONFIG_PCI -EXPORT_SYMBOL_GPL(ata_pci_bmdma_clear_simplex); -EXPORT_SYMBOL_GPL(ata_pci_bmdma_init); -EXPORT_SYMBOL_GPL(ata_pci_sff_init_host); -EXPORT_SYMBOL_GPL(ata_pci_sff_prepare_host); -EXPORT_SYMBOL_GPL(ata_pci_sff_activate_host); -EXPORT_SYMBOL_GPL(ata_pci_sff_init_one); -#endif /* CONFIG_PCI */ diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index e2e332d8ff95..8b77a9802df1 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -13,12 +13,6 @@ #include <linux/device.h> #include <scsi/scsi_host.h> #include <acpi/acpi_bus.h> -#include <acpi/acnames.h> -#include <acpi/acnamesp.h> -#include <acpi/acparser.h> -#include <acpi/acexcep.h> -#include <acpi/acmacros.h> -#include <acpi/actypes.h> #include <linux/libata.h> #include <linux/ata.h> diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index 73c466e452ca..a7999c19f0c9 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -19,7 +19,9 @@ * * TODO/CHECK * Cannot have ATAPI on both master & slave for rev < c2 (???) but - * otherwise should do atapi DMA. + * otherwise should do atapi DMA (For now for old we do PIO only for + * ATAPI) + * Review Sunblade workaround. */ #include <linux/kernel.h> @@ -33,12 +35,14 @@ #include <linux/dmi.h> #define DRV_NAME "pata_ali" -#define DRV_VERSION "0.7.5" +#define DRV_VERSION "0.7.8" static int ali_atapi_dma = 0; module_param_named(atapi_dma, ali_atapi_dma, int, 0644); MODULE_PARM_DESC(atapi_dma, "Enable ATAPI DMA (0=disable, 1=enable)"); +static struct pci_dev *isa_bridge; + /* * Cable special cases */ @@ -147,8 +151,7 @@ static void ali_fifo_control(struct ata_port *ap, struct ata_device *adev, int o pci_read_config_byte(pdev, pio_fifo, &fifo); fifo &= ~(0x0F << shift); - if (on) - fifo |= (on << shift); + fifo |= (on << shift); pci_write_config_byte(pdev, pio_fifo, fifo); } @@ -337,6 +340,23 @@ static int ali_check_atapi_dma(struct ata_queued_cmd *qc) return 0; } +static void ali_c2_c3_postreset(struct ata_link *link, unsigned int *classes) +{ + u8 r; + int port_bit = 4 << link->ap->port_no; + + /* If our bridge is an ALI 1533 then do the extra work */ + if (isa_bridge) { + /* Tristate and re-enable the bus signals */ + pci_read_config_byte(isa_bridge, 0x58, &r); + r &= ~port_bit; + pci_write_config_byte(isa_bridge, 0x58, r); + r |= port_bit; + pci_write_config_byte(isa_bridge, 0x58, r); + } + ata_sff_postreset(link, classes); +} + static struct scsi_host_template ali_sht = { ATA_BMDMA_SHT(DRV_NAME), }; @@ -349,10 +369,11 @@ static struct ata_port_operations ali_early_port_ops = { .inherits = &ata_sff_port_ops, .cable_detect = ata_cable_40wire, .set_piomode = ali_set_piomode, + .sff_data_xfer = ata_sff_data_xfer32, }; static const struct ata_port_operations ali_dma_base_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_bmdma32_port_ops, .set_piomode = ali_set_piomode, .set_dmamode = ali_set_dmamode, }; @@ -377,6 +398,17 @@ static struct ata_port_operations ali_c2_port_ops = { .check_atapi_dma = ali_check_atapi_dma, .cable_detect = ali_c2_cable_detect, .dev_config = ali_lock_sectors, + .postreset = ali_c2_c3_postreset, +}; + +/* + * Port operations for DMA capable ALi with cable detect + */ +static struct ata_port_operations ali_c4_port_ops = { + .inherits = &ali_dma_base_ops, + .check_atapi_dma = ali_check_atapi_dma, + .cable_detect = ali_c2_cable_detect, + .dev_config = ali_lock_sectors, }; /* @@ -401,52 +433,49 @@ static struct ata_port_operations ali_c5_port_ops = { static void ali_init_chipset(struct pci_dev *pdev) { u8 tmp; - struct pci_dev *north, *isa_bridge; + struct pci_dev *north; /* * The chipset revision selects the driver operations and * mode data. */ - if (pdev->revision >= 0x20 && pdev->revision < 0xC2) { - /* 1543-E/F, 1543C-C, 1543C-D, 1543C-E */ - pci_read_config_byte(pdev, 0x4B, &tmp); - /* Clear CD-ROM DMA write bit */ - tmp &= 0x7F; - pci_write_config_byte(pdev, 0x4B, tmp); - } else if (pdev->revision >= 0xC2) { - /* Enable cable detection logic */ + if (pdev->revision <= 0x20) { + pci_read_config_byte(pdev, 0x53, &tmp); + tmp |= 0x03; + pci_write_config_byte(pdev, 0x53, tmp); + } else { + pci_read_config_byte(pdev, 0x4a, &tmp); + pci_write_config_byte(pdev, 0x4a, tmp | 0x20); pci_read_config_byte(pdev, 0x4B, &tmp); - pci_write_config_byte(pdev, 0x4B, tmp | 0x08); - } - north = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); - isa_bridge = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); - - if (north && north->vendor == PCI_VENDOR_ID_AL && isa_bridge) { - /* Configure the ALi bridge logic. For non ALi rely on BIOS. - Set the south bridge enable bit */ - pci_read_config_byte(isa_bridge, 0x79, &tmp); - if (pdev->revision == 0xC2) - pci_write_config_byte(isa_bridge, 0x79, tmp | 0x04); - else if (pdev->revision > 0xC2 && pdev->revision < 0xC5) - pci_write_config_byte(isa_bridge, 0x79, tmp | 0x02); - } - if (pdev->revision >= 0x20) { + if (pdev->revision < 0xC2) + /* 1543-E/F, 1543C-C, 1543C-D, 1543C-E */ + /* Clear CD-ROM DMA write bit */ + tmp &= 0x7F; + /* Cable and UDMA */ + pci_write_config_byte(pdev, 0x4B, tmp | 0x09); /* * CD_ROM DMA on (0x53 bit 0). Enable this even if we want * to use PIO. 0x53 bit 1 (rev 20 only) - enable FIFO control * via 0x54/55. */ pci_read_config_byte(pdev, 0x53, &tmp); - if (pdev->revision <= 0x20) - tmp &= ~0x02; if (pdev->revision >= 0xc7) tmp |= 0x03; else tmp |= 0x01; /* CD_ROM enable for DMA */ pci_write_config_byte(pdev, 0x53, tmp); } - pci_dev_put(isa_bridge); + north = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); + if (north && north->vendor == PCI_VENDOR_ID_AL && isa_bridge) { + /* Configure the ALi bridge logic. For non ALi rely on BIOS. + Set the south bridge enable bit */ + pci_read_config_byte(isa_bridge, 0x79, &tmp); + if (pdev->revision == 0xC2) + pci_write_config_byte(isa_bridge, 0x79, tmp | 0x04); + else if (pdev->revision > 0xC2 && pdev->revision < 0xC5) + pci_write_config_byte(isa_bridge, 0x79, tmp | 0x02); + } pci_dev_put(north); ata_pci_bmdma_clear_simplex(pdev); } @@ -503,7 +532,7 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .pio_mask = 0x1f, .mwdma_mask = 0x07, .udma_mask = ATA_UDMA5, - .port_ops = &ali_c2_port_ops + .port_ops = &ali_c4_port_ops }; /* Revision 0xC5 is UDMA133 with LBA48 DMA */ static const struct ata_port_info info_c5 = { @@ -516,7 +545,6 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) const struct ata_port_info *ppi[] = { NULL, NULL }; u8 tmp; - struct pci_dev *isa_bridge; int rc; rc = pcim_enable_device(pdev); @@ -543,14 +571,12 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) ali_init_chipset(pdev); - isa_bridge = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); if (isa_bridge && pdev->revision >= 0x20 && pdev->revision < 0xC2) { /* Are we paired with a UDMA capable chip */ pci_read_config_byte(isa_bridge, 0x5E, &tmp); if ((tmp & 0x1E) == 0x12) ppi[0] = &info_20_udma; } - pci_dev_put(isa_bridge); return ata_pci_sff_init_one(pdev, ppi, &ali_sht, NULL); } @@ -590,13 +616,20 @@ static struct pci_driver ali_pci_driver = { static int __init ali_init(void) { - return pci_register_driver(&ali_pci_driver); + int ret; + isa_bridge = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + + ret = pci_register_driver(&ali_pci_driver); + if (ret < 0) + pci_dev_put(isa_bridge); + return ret; } static void __exit ali_exit(void) { pci_unregister_driver(&ali_pci_driver); + pci_dev_put(isa_bridge); } diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index 0ec9c7d9fe9d..63719ab9ea44 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -24,7 +24,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_amd" -#define DRV_VERSION "0.3.10" +#define DRV_VERSION "0.3.11" /** * timing_setup - shared timing computation and load @@ -345,7 +345,7 @@ static struct scsi_host_template amd_sht = { }; static const struct ata_port_operations amd_base_port_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_bmdma32_port_ops, .prereset = amd_pre_reset, }; diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index e0c4f05d7d57..65c28e5a6cd7 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -30,7 +30,7 @@ #define DRV_VERSION "0.6.2" struct hpt_clock { - u8 xfer_speed; + u8 xfer_mode; u32 timing; }; @@ -189,28 +189,6 @@ static unsigned long hpt366_filter(struct ata_device *adev, unsigned long mask) return ata_bmdma_mode_filter(adev, mask); } -/** - * hpt36x_find_mode - reset the hpt36x bus - * @ap: ATA port - * @speed: transfer mode - * - * Return the 32bit register programming information for this channel - * that matches the speed provided. - */ - -static u32 hpt36x_find_mode(struct ata_port *ap, int speed) -{ - struct hpt_clock *clocks = ap->host->private_data; - - while(clocks->xfer_speed) { - if (clocks->xfer_speed == speed) - return clocks->timing; - clocks++; - } - BUG(); - return 0xffffffffU; /* silence compiler warning */ -} - static int hpt36x_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); @@ -226,25 +204,16 @@ static int hpt36x_cable_detect(struct ata_port *ap) return ATA_CBL_PATA80; } -/** - * hpt366_set_piomode - PIO setup - * @ap: ATA interface - * @adev: device on the interface - * - * Perform PIO mode setup. - */ - -static void hpt366_set_piomode(struct ata_port *ap, struct ata_device *adev) +static void hpt366_set_mode(struct ata_port *ap, struct ata_device *adev, + u8 mode) { + struct hpt_clock *clocks = ap->host->private_data; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u32 addr1, addr2; - u32 reg; - u32 mode; + u32 addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); + u32 addr2 = 0x51 + 4 * ap->port_no; + u32 mask, reg; u8 fast; - addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); - addr2 = 0x51 + 4 * ap->port_no; - /* Fast interrupt prediction disable, hold off interrupt disable */ pci_read_config_byte(pdev, addr2, &fast); if (fast & 0x80) { @@ -252,12 +221,43 @@ static void hpt366_set_piomode(struct ata_port *ap, struct ata_device *adev) pci_write_config_byte(pdev, addr2, fast); } + /* determine timing mask and find matching clock entry */ + if (mode < XFER_MW_DMA_0) + mask = 0xc1f8ffff; + else if (mode < XFER_UDMA_0) + mask = 0x303800ff; + else + mask = 0x30070000; + + while (clocks->xfer_mode) { + if (clocks->xfer_mode == mode) + break; + clocks++; + } + if (!clocks->xfer_mode) + BUG(); + + /* + * Combine new mode bits with old config bits and disable + * on-chip PIO FIFO/buffer (and PIO MST mode as well) to avoid + * problems handling I/O errors later. + */ pci_read_config_dword(pdev, addr1, ®); - mode = hpt36x_find_mode(ap, adev->pio_mode); - mode &= ~0x8000000; /* No FIFO in PIO */ - mode &= ~0x30070000; /* Leave config bits alone */ - reg &= 0x30070000; /* Strip timing bits */ - pci_write_config_dword(pdev, addr1, reg | mode); + reg = ((reg & ~mask) | (clocks->timing & mask)) & ~0xc0000000; + pci_write_config_dword(pdev, addr1, reg); +} + +/** + * hpt366_set_piomode - PIO setup + * @ap: ATA interface + * @adev: device on the interface + * + * Perform PIO mode setup. + */ + +static void hpt366_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + hpt366_set_mode(ap, adev, adev->pio_mode); } /** @@ -271,28 +271,7 @@ static void hpt366_set_piomode(struct ata_port *ap, struct ata_device *adev) static void hpt366_set_dmamode(struct ata_port *ap, struct ata_device *adev) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u32 addr1, addr2; - u32 reg; - u32 mode; - u8 fast; - - addr1 = 0x40 + 4 * (adev->devno + 2 * ap->port_no); - addr2 = 0x51 + 4 * ap->port_no; - - /* Fast interrupt prediction disable, hold off interrupt disable */ - pci_read_config_byte(pdev, addr2, &fast); - if (fast & 0x80) { - fast &= ~0x80; - pci_write_config_byte(pdev, addr2, fast); - } - - pci_read_config_dword(pdev, addr1, ®); - mode = hpt36x_find_mode(ap, adev->dma_mode); - mode |= 0x8000000; /* FIFO in MWDMA or UDMA */ - mode &= ~0xC0000000; /* Leave config bits alone */ - reg &= 0xC0000000; /* Strip timing bits */ - pci_write_config_dword(pdev, addr1, reg | mode); + hpt366_set_mode(ap, adev, adev->dma_mode); } static struct scsi_host_template hpt36x_sht = { diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index f11a320337c0..f19cc645881a 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -23,7 +23,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt3x3" -#define DRV_VERSION "0.5.3" +#define DRV_VERSION "0.6.1" /** * hpt3x3_set_piomode - PIO setup @@ -80,14 +80,48 @@ static void hpt3x3_set_dmamode(struct ata_port *ap, struct ata_device *adev) r2 &= ~(0x11 << dn); /* Clear MWDMA and UDMA bits */ if (adev->dma_mode >= XFER_UDMA_0) - r2 |= (0x10 << dn); /* Ultra mode */ + r2 |= (0x01 << dn); /* Ultra mode */ else - r2 |= (0x01 << dn); /* MWDMA */ + r2 |= (0x10 << dn); /* MWDMA */ pci_write_config_dword(pdev, 0x44, r1); pci_write_config_dword(pdev, 0x48, r2); } -#endif /* CONFIG_PATA_HPT3X3_DMA */ + +/** + * hpt3x3_freeze - DMA workaround + * @ap: port to freeze + * + * When freezing an HPT3x3 we must stop any pending DMA before + * writing to the control register or the chip will hang + */ + +static void hpt3x3_freeze(struct ata_port *ap) +{ + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + iowrite8(ioread8(mmio + ATA_DMA_CMD) & ~ ATA_DMA_START, + mmio + ATA_DMA_CMD); + ata_sff_dma_pause(ap); + ata_sff_freeze(ap); +} + +/** + * hpt3x3_bmdma_setup - DMA workaround + * @qc: Queued command + * + * When issuing BMDMA we must clean up the error/active bits in + * software on this device + */ + +static void hpt3x3_bmdma_setup(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + u8 r = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + r |= ATA_DMA_INTR | ATA_DMA_ERR; + iowrite8(r, ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + return ata_bmdma_setup(qc); +} /** * hpt3x3_atapi_dma - ATAPI DMA check @@ -101,18 +135,23 @@ static int hpt3x3_atapi_dma(struct ata_queued_cmd *qc) return 1; } +#endif /* CONFIG_PATA_HPT3X3_DMA */ + static struct scsi_host_template hpt3x3_sht = { ATA_BMDMA_SHT(DRV_NAME), }; static struct ata_port_operations hpt3x3_port_ops = { .inherits = &ata_bmdma_port_ops, - .check_atapi_dma= hpt3x3_atapi_dma, .cable_detect = ata_cable_40wire, .set_piomode = hpt3x3_set_piomode, #if defined(CONFIG_PATA_HPT3X3_DMA) .set_dmamode = hpt3x3_set_dmamode, + .bmdma_setup = hpt3x3_bmdma_setup, + .check_atapi_dma= hpt3x3_atapi_dma, + .freeze = hpt3x3_freeze, #endif + }; /** diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index 7c8faa48b5f3..aa576cac4d17 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -35,7 +35,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_mpiix" -#define DRV_VERSION "0.7.6" +#define DRV_VERSION "0.7.7" enum { IDETIM = 0x6C, /* IDE control register */ @@ -146,6 +146,7 @@ static struct ata_port_operations mpiix_port_ops = { .cable_detect = ata_cable_40wire, .set_piomode = mpiix_set_piomode, .prereset = mpiix_pre_reset, + .sff_data_xfer = ata_sff_data_xfer32, }; static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 6afa07a37648..d8d743af3225 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -186,7 +186,7 @@ EXPORT_SYMBOL_GPL(__pata_platform_probe); * A platform bus ATA device has been unplugged. Perform the needed * cleanup. Also called on module unload for any active devices. */ -int __devexit __pata_platform_remove(struct device *dev) +int __pata_platform_remove(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index 83580a59db58..9e764e5747e6 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -32,7 +32,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_sil680" -#define DRV_VERSION "0.4.8" +#define DRV_VERSION "0.4.9" #define SIL680_MMIO_BAR 5 @@ -195,7 +195,7 @@ static struct scsi_host_template sil680_sht = { }; static struct ata_port_operations sil680_port_ops = { - .inherits = &ata_bmdma_port_ops, + .inherits = &ata_bmdma32_port_ops, .cable_detect = sil680_cable_detect, .set_piomode = sil680_set_piomode, .set_dmamode = sil680_set_dmamode, diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index ccee930f1e12..2590c2279fa7 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -51,13 +51,6 @@ struct sil24_sge { __le32 flags; }; -/* - * Port multiplier - */ -struct sil24_port_multiplier { - __le32 diag; - __le32 sactive; -}; enum { SIL24_HOST_BAR = 0, diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 088885ed51b9..e1c7611e9144 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -64,7 +64,7 @@ #include <linux/jiffies.h> #include "iphase.h" #include "suni.h" -#define swap(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) +#define swap_byte_order(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) #define PRIV(dev) ((struct suni_priv *) dev->phy_data) @@ -1306,7 +1306,7 @@ static void rx_dle_intr(struct atm_dev *dev) // get real pkt length pwang_test trailer = (struct cpcs_trailer*)((u_char *)skb->data + skb->len - sizeof(*trailer)); - length = swap(trailer->length); + length = swap_byte_order(trailer->length); if ((length > iadev->rx_buf_sz) || (length > (skb->len - sizeof(struct cpcs_trailer)))) { @@ -2995,7 +2995,7 @@ static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb) { skb->len, PCI_DMA_TODEVICE); wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) | buf_desc_ptr->buf_start_lo; - /* wr_ptr->bytes = swap(total_len); didn't seem to affect ?? */ + /* wr_ptr->bytes = swap_byte_order(total_len); didn't seem to affect?? */ wr_ptr->bytes = skb->len; /* hw bug - DLEs of 0x2d, 0x2e, 0x2f cause DMA lockup */ diff --git a/drivers/base/base.h b/drivers/base/base.h index b676f8f801f8..0a5f055dffba 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -63,32 +63,6 @@ struct class_private { #define to_class(obj) \ container_of(obj, struct class_private, class_subsys.kobj) -/** - * struct device_private - structure to hold the private to the driver core portions of the device structure. - * - * @klist_children - klist containing all children of this device - * @knode_parent - node in sibling list - * @knode_driver - node in driver list - * @knode_bus - node in bus list - * @device - pointer back to the struct class that this structure is - * associated with. - * - * Nothing outside of the driver core should ever touch these fields. - */ -struct device_private { - struct klist klist_children; - struct klist_node knode_parent; - struct klist_node knode_driver; - struct klist_node knode_bus; - struct device *device; -}; -#define to_device_private_parent(obj) \ - container_of(obj, struct device_private, knode_parent) -#define to_device_private_driver(obj) \ - container_of(obj, struct device_private, knode_driver) -#define to_device_private_bus(obj) \ - container_of(obj, struct device_private, knode_bus) - /* initialisation functions */ extern int devices_init(void); extern int buses_init(void); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 0f0a50444672..83f32b891fa9 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -253,14 +253,7 @@ static ssize_t store_drivers_probe(struct bus_type *bus, static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); - struct device *dev = NULL; - struct device_private *dev_prv; - - if (n) { - dev_prv = to_device_private_bus(n); - dev = dev_prv->device; - } - return dev; + return n ? container_of(n, struct device, knode_bus) : NULL; } /** @@ -293,7 +286,7 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start, return -EINVAL; klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->p->knode_bus : NULL)); + (start ? &start->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); @@ -327,7 +320,7 @@ struct device *bus_find_device(struct bus_type *bus, return NULL; klist_iter_init_node(&bus->p->klist_devices, &i, - (start ? &start->p->knode_bus : NULL)); + (start ? &start->knode_bus : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; @@ -514,8 +507,7 @@ void bus_attach_device(struct device *dev) ret = device_attach(dev); WARN_ON(ret < 0); if (ret >= 0) - klist_add_tail(&dev->p->knode_bus, - &bus->p->klist_devices); + klist_add_tail(&dev->knode_bus, &bus->p->klist_devices); } } @@ -536,8 +528,8 @@ void bus_remove_device(struct device *dev) sysfs_remove_link(&dev->bus->p->devices_kset->kobj, dev_name(dev)); device_remove_attrs(dev->bus, dev); - if (klist_node_attached(&dev->p->knode_bus)) - klist_del(&dev->p->knode_bus); + if (klist_node_attached(&dev->knode_bus)) + klist_del(&dev->knode_bus); pr_debug("bus: '%s': remove device %s\n", dev->bus->name, dev_name(dev)); @@ -839,16 +831,14 @@ static void bus_remove_attrs(struct bus_type *bus) static void klist_devices_get(struct klist_node *n) { - struct device_private *dev_prv = to_device_private_bus(n); - struct device *dev = dev_prv->device; + struct device *dev = container_of(n, struct device, knode_bus); get_device(dev); } static void klist_devices_put(struct klist_node *n) { - struct device_private *dev_prv = to_device_private_bus(n); - struct device *dev = dev_prv->device; + struct device *dev = container_of(n, struct device, knode_bus); put_device(dev); } @@ -1003,20 +993,18 @@ static void device_insertion_sort_klist(struct device *a, struct list_head *list { struct list_head *pos; struct klist_node *n; - struct device_private *dev_prv; struct device *b; list_for_each(pos, list) { n = container_of(pos, struct klist_node, n_node); - dev_prv = to_device_private_bus(n); - b = dev_prv->device; + b = container_of(n, struct device, knode_bus); if (compare(a, b) <= 0) { - list_move_tail(&a->p->knode_bus.n_node, - &b->p->knode_bus.n_node); + list_move_tail(&a->knode_bus.n_node, + &b->knode_bus.n_node); return; } } - list_move_tail(&a->p->knode_bus.n_node, list); + list_move_tail(&a->knode_bus.n_node, list); } void bus_sort_breadthfirst(struct bus_type *bus, @@ -1026,7 +1014,6 @@ void bus_sort_breadthfirst(struct bus_type *bus, LIST_HEAD(sorted_devices); struct list_head *pos, *tmp; struct klist_node *n; - struct device_private *dev_prv; struct device *dev; struct klist *device_klist; @@ -1035,8 +1022,7 @@ void bus_sort_breadthfirst(struct bus_type *bus, spin_lock(&device_klist->k_lock); list_for_each_safe(pos, tmp, &device_klist->k_list) { n = container_of(pos, struct klist_node, n_node); - dev_prv = to_device_private_bus(n); - dev = dev_prv->device; + dev = container_of(n, struct device, knode_bus); device_insertion_sort_klist(dev, &sorted_devices, compare); } list_splice(&sorted_devices, &device_klist->k_list); diff --git a/drivers/base/core.c b/drivers/base/core.c index 61df508fa62b..8079afca4972 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -109,7 +109,6 @@ static struct sysfs_ops dev_sysfs_ops = { static void device_release(struct kobject *kobj) { struct device *dev = to_dev(kobj); - struct device_private *p = dev->p; if (dev->release) dev->release(dev); @@ -121,7 +120,6 @@ static void device_release(struct kobject *kobj) WARN(1, KERN_ERR "Device '%s' does not have a release() " "function, it is broken and must be fixed.\n", dev_name(dev)); - kfree(p); } static struct kobj_type device_ktype = { @@ -509,16 +507,14 @@ EXPORT_SYMBOL_GPL(device_schedule_callback_owner); static void klist_children_get(struct klist_node *n) { - struct device_private *p = to_device_private_parent(n); - struct device *dev = p->device; + struct device *dev = container_of(n, struct device, knode_parent); get_device(dev); } static void klist_children_put(struct klist_node *n) { - struct device_private *p = to_device_private_parent(n); - struct device *dev = p->device; + struct device *dev = container_of(n, struct device, knode_parent); put_device(dev); } @@ -540,15 +536,9 @@ static void klist_children_put(struct klist_node *n) */ void device_initialize(struct device *dev) { - dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); - if (!dev->p) { - WARN_ON(1); - return; - } - dev->p->device = dev; dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); - klist_init(&dev->p->klist_children, klist_children_get, + klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); @@ -932,8 +922,7 @@ int device_add(struct device *dev) kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); if (parent) - klist_add_tail(&dev->p->knode_parent, - &parent->p->klist_children); + klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { mutex_lock(&dev->class->p->class_mutex); @@ -1047,7 +1036,7 @@ void device_del(struct device *dev) device_pm_remove(dev); dpm_sysfs_remove(dev); if (parent) - klist_del(&dev->p->knode_parent); + klist_del(&dev->knode_parent); if (MAJOR(dev->devt)) { device_remove_sys_dev_entry(dev); device_remove_file(dev, &devt_attr); @@ -1108,14 +1097,7 @@ void device_unregister(struct device *dev) static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); - struct device *dev = NULL; - struct device_private *p; - - if (n) { - p = to_device_private_parent(n); - dev = p->device; - } - return dev; + return n ? container_of(n, struct device, knode_parent) : NULL; } /** @@ -1137,7 +1119,7 @@ int device_for_each_child(struct device *parent, void *data, struct device *child; int error = 0; - klist_iter_init(&parent->p->klist_children, &i); + klist_iter_init(&parent->klist_children, &i); while ((child = next_device(&i)) && !error) error = fn(child, data); klist_iter_exit(&i); @@ -1168,7 +1150,7 @@ struct device *device_find_child(struct device *parent, void *data, if (!parent) return NULL; - klist_iter_init(&parent->p->klist_children, &i); + klist_iter_init(&parent->klist_children, &i); while ((child = next_device(&i))) if (match(child, data) && get_device(child)) break; @@ -1582,10 +1564,9 @@ int device_move(struct device *dev, struct device *new_parent) old_parent = dev->parent; dev->parent = new_parent; if (old_parent) - klist_remove(&dev->p->knode_parent); + klist_remove(&dev->knode_parent); if (new_parent) { - klist_add_tail(&dev->p->knode_parent, - &new_parent->p->klist_children); + klist_add_tail(&dev->knode_parent, &new_parent->klist_children); set_dev_node(dev, dev_to_node(new_parent)); } @@ -1597,11 +1578,11 @@ int device_move(struct device *dev, struct device *new_parent) device_move_class_links(dev, new_parent, old_parent); if (!kobject_move(&dev->kobj, &old_parent->kobj)) { if (new_parent) - klist_remove(&dev->p->knode_parent); + klist_remove(&dev->knode_parent); dev->parent = old_parent; if (old_parent) { - klist_add_tail(&dev->p->knode_parent, - &old_parent->p->klist_children); + klist_add_tail(&dev->knode_parent, + &old_parent->klist_children); set_dev_node(dev, dev_to_node(old_parent)); } } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6fdaf76f033f..315bed8d5e7f 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -28,7 +28,7 @@ static void driver_bound(struct device *dev) { - if (klist_node_attached(&dev->p->knode_driver)) { + if (klist_node_attached(&dev->knode_driver)) { printk(KERN_WARNING "%s: device %s already bound\n", __func__, kobject_name(&dev->kobj)); return; @@ -41,7 +41,7 @@ static void driver_bound(struct device *dev) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); - klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); + klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices); } static int driver_sysfs_add(struct device *dev) @@ -310,7 +310,7 @@ static void __device_release_driver(struct device *dev) drv->remove(dev); devres_release_all(dev); dev->driver = NULL; - klist_remove(&dev->p->knode_driver); + klist_remove(&dev->knode_driver); } } @@ -340,7 +340,6 @@ EXPORT_SYMBOL_GPL(device_release_driver); */ void driver_detach(struct device_driver *drv) { - struct device_private *dev_prv; struct device *dev; for (;;) { @@ -349,10 +348,8 @@ void driver_detach(struct device_driver *drv) spin_unlock(&drv->p->klist_devices.k_lock); break; } - dev_prv = list_entry(drv->p->klist_devices.k_list.prev, - struct device_private, - knode_driver.n_node); - dev = dev_prv->device; + dev = list_entry(drv->p->klist_devices.k_list.prev, + struct device, knode_driver.n_node); get_device(dev); spin_unlock(&drv->p->klist_devices.k_lock); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index b76cc69f1106..1e2bda780e48 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -19,14 +19,7 @@ static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); - struct device *dev = NULL; - struct device_private *dev_prv; - - if (n) { - dev_prv = to_device_private_driver(n); - dev = dev_prv->device; - } - return dev; + return n ? container_of(n, struct device, knode_driver) : NULL; } /** @@ -49,7 +42,7 @@ int driver_for_each_device(struct device_driver *drv, struct device *start, return -EINVAL; klist_iter_init_node(&drv->p->klist_devices, &i, - start ? &start->p->knode_driver : NULL); + start ? &start->knode_driver : NULL); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); @@ -83,7 +76,7 @@ struct device *driver_find_device(struct device_driver *drv, return NULL; klist_iter_init_node(&drv->p->klist_devices, &i, - (start ? &start->p->knode_driver : NULL)); + (start ? &start->knode_driver : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; diff --git a/drivers/base/topology.c b/drivers/base/topology.c index a8bc1cbcfa7c..a778fb52b11f 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -28,6 +28,7 @@ #include <linux/mm.h> #include <linux/cpu.h> #include <linux/module.h> +#include <linux/hardirq.h> #include <linux/topology.h> #define define_one_ro(_name) \ diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 953c0b83d758..5861e33efe63 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -153,7 +153,7 @@ static int vdc_send_attr(struct vio_driver_state *vio) pkt.vdisk_block_size = port->vdisk_block_size; pkt.max_xfer_size = port->max_xfer_size; - viodbg(HS, "SEND ATTR xfer_mode[0x%x] blksz[%u] max_xfer[%lu]\n", + viodbg(HS, "SEND ATTR xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n", pkt.xfer_mode, pkt.vdisk_block_size, pkt.max_xfer_size); return vio_ldc_send(&port->vio, &pkt, sizeof(pkt)); @@ -164,8 +164,8 @@ static int vdc_handle_attr(struct vio_driver_state *vio, void *arg) struct vdc_port *port = to_vdc_port(vio); struct vio_disk_attr_info *pkt = arg; - viodbg(HS, "GOT ATTR stype[0x%x] ops[%lx] disk_size[%lu] disk_type[%x] " - "xfer_mode[0x%x] blksz[%u] max_xfer[%lu]\n", + viodbg(HS, "GOT ATTR stype[0x%x] ops[%llx] disk_size[%llu] disk_type[%x] " + "xfer_mode[0x%x] blksz[%u] max_xfer[%llu]\n", pkt->tag.stype, pkt->operations, pkt->vdisk_size, pkt->vdisk_type, pkt->xfer_mode, pkt->vdisk_block_size, @@ -753,7 +753,7 @@ static int __devinit vdc_port_probe(struct vio_dev *vdev, err = -ENODEV; if ((vdev->dev_no << PARTITION_SHIFT) & ~(u64)MINORMASK) { - printk(KERN_ERR PFX "Port id [%lu] too large.\n", + printk(KERN_ERR PFX "Port id [%llu] too large.\n", vdev->dev_no); goto err_out_release_mdesc; } diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 048d71d244d7..12fb816db7b0 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -1579,7 +1579,7 @@ static void ub_reset_task(struct work_struct *work) struct ub_dev *sc = container_of(work, struct ub_dev, reset_work); unsigned long flags; struct ub_lun *lun; - int lkr, rc; + int rc; if (!sc->reset) { printk(KERN_WARNING "%s: Running reset unrequested\n", @@ -1597,10 +1597,11 @@ static void ub_reset_task(struct work_struct *work) } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) { ; } else { - if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) { + rc = usb_lock_device_for_reset(sc->dev, sc->intf); + if (rc < 0) { printk(KERN_NOTICE "%s: usb_lock_device_for_reset failed (%d)\n", - sc->name, lkr); + sc->name, rc); } else { rc = usb_reset_device(sc->dev); if (rc < 0) { @@ -1608,9 +1609,7 @@ static void ub_reset_task(struct work_struct *work) "usb_lock_device_for_reset failed (%d)\n", sc->name, rc); } - - if (lkr) - usb_unlock_device(sc->dev); + usb_unlock_device(sc->dev); } } diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 35914b6e1d2a..f5be8081cd81 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -616,6 +616,7 @@ config HVC_ISERIES default y select HVC_DRIVER select HVC_IRQ + select VIOPATH help iSeries machines support a hypervisor virtual console. diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 91cdb35a9204..0afc8b82212e 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -44,7 +44,7 @@ static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) static unsigned char q[sizeof(unsigned long) * 2] __attribute__((aligned(sizeof(unsigned long)))); static int qlen = 0; - unsigned long got; + u64 got; again: if (qlen) { @@ -63,7 +63,7 @@ again: } } if (beat_get_term_char(vtermno, &got, - ((unsigned long *)q), ((unsigned long *)q) + 1) == 0) { + ((u64 *)q), ((u64 *)q) + 1) == 0) { qlen = got; goto again; } diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 5ea7d7713fca..a53496828b76 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -1,26 +1,30 @@ /* - * hvc_iucv.c - z/VM IUCV back-end for the Hypervisor Console (HVC) + * hvc_iucv.c - z/VM IUCV hypervisor console (HVC) device driver * - * This back-end for HVC provides terminal access via + * This HVC device driver provides terminal access using * z/VM IUCV communication paths. * - * Copyright IBM Corp. 2008. + * Copyright IBM Corp. 2008 * * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> */ #define KMSG_COMPONENT "hvc_iucv" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include <linux/types.h> #include <asm/ebcdic.h> +#include <linux/delay.h> +#include <linux/init.h> #include <linux/mempool.h> #include <linux/module.h> #include <linux/tty.h> +#include <linux/wait.h> #include <net/iucv/iucv.h> #include "hvc_console.h" -/* HVC backend for z/VM IUCV */ +/* General device driver settings */ #define HVC_IUCV_MAGIC 0xc9e4c3e5 #define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS #define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4) @@ -33,14 +37,14 @@ #define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */ #define MSG_TYPE_DATA 0x10 /* Terminal data */ -#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) struct iucv_tty_msg { u8 version; /* Message version */ u8 type; /* Message type */ -#define MSG_MAX_DATALEN (~(u16)0) +#define MSG_MAX_DATALEN ((u16)(~0)) u16 datalen; /* Payload length */ u8 data[]; /* Payload buffer */ } __attribute__((packed)); +#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) enum iucv_state_t { IUCV_DISCONN = 0, @@ -54,19 +58,26 @@ enum tty_state_t { }; struct hvc_iucv_private { - struct hvc_struct *hvc; /* HVC console struct reference */ + struct hvc_struct *hvc; /* HVC struct reference */ u8 srv_name[8]; /* IUCV service name (ebcdic) */ + unsigned char is_console; /* Linux console usage flag */ enum iucv_state_t iucv_state; /* IUCV connection status */ enum tty_state_t tty_state; /* TTY status */ struct iucv_path *path; /* IUCV path pointer */ spinlock_t lock; /* hvc_iucv_private lock */ +#define SNDBUF_SIZE (PAGE_SIZE) /* must be < MSG_MAX_DATALEN */ + void *sndbuf; /* send buffer */ + size_t sndbuf_len; /* length of send buffer */ +#define QUEUE_SNDBUF_DELAY (HZ / 25) + struct delayed_work sndbuf_work; /* work: send iucv msg(s) */ + wait_queue_head_t sndbuf_waitq; /* wait for send completion */ struct list_head tty_outqueue; /* outgoing IUCV messages */ struct list_head tty_inqueue; /* incoming IUCV messages */ }; struct iucv_tty_buffer { struct list_head list; /* list pointer */ - struct iucv_message msg; /* store an incoming IUCV message */ + struct iucv_message msg; /* store an IUCV message */ size_t offset; /* data buffer offset */ struct iucv_tty_msg *mbuf; /* buffer to store input/output data */ }; @@ -78,11 +89,12 @@ static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); -/* Kernel module parameters */ -static unsigned long hvc_iucv_devices; +/* Kernel module parameter: use one terminal device as default */ +static unsigned long hvc_iucv_devices = 1; /* Array of allocated hvc iucv tty lines... */ static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; +#define IUCV_HVC_CON_IDX (0) /* Kmem cache and mempool for iucv_tty_buffer elements */ static struct kmem_cache *hvc_iucv_buffer_cache; @@ -112,7 +124,7 @@ struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) } /** - * alloc_tty_buffer() - Returns a new struct iucv_tty_buffer element. + * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element. * @size: Size of the internal buffer used to store data. * @flags: Memory allocation flags passed to mempool. * @@ -120,7 +132,6 @@ struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) * allocates an internal data buffer with the specified size @size. * Note: The total message size arises from the internal buffer size and the * members of the iucv_tty_msg structure. - * * The function returns NULL if memory allocation has failed. */ static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) @@ -130,7 +141,7 @@ static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) bufp = mempool_alloc(hvc_iucv_mempool, flags); if (!bufp) return NULL; - memset(bufp, 0, sizeof(struct iucv_tty_buffer)); + memset(bufp, 0, sizeof(*bufp)); if (size > 0) { bufp->msg.length = MSG_SIZE(size); @@ -149,9 +160,6 @@ static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) /** * destroy_tty_buffer() - destroy struct iucv_tty_buffer element. * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL. - * - * The destroy_tty_buffer() function frees the internal data buffer and returns - * the struct iucv_tty_buffer element back to the mempool for freeing. */ static void destroy_tty_buffer(struct iucv_tty_buffer *bufp) { @@ -161,11 +169,7 @@ static void destroy_tty_buffer(struct iucv_tty_buffer *bufp) /** * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element. - * @list: List head pointer to a list containing struct iucv_tty_buffer - * elements. - * - * Calls destroy_tty_buffer() for each struct iucv_tty_buffer element in the - * list @list. + * @list: List containing struct iucv_tty_buffer elements. */ static void destroy_tty_buffer_list(struct list_head *list) { @@ -178,24 +182,24 @@ static void destroy_tty_buffer_list(struct list_head *list) } /** - * hvc_iucv_write() - Receive IUCV message write data to HVC console buffer. - * @priv: Pointer to hvc_iucv_private structure. - * @buf: HVC console buffer for writing received terminal data. - * @count: HVC console buffer size. + * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer. + * @priv: Pointer to struct hvc_iucv_private + * @buf: HVC buffer for writing received terminal data. + * @count: HVC buffer size. * @has_more_data: Pointer to an int variable. * * The function picks up pending messages from the input queue and receives * the message data that is then written to the specified buffer @buf. - * If the buffer size @count is less than the data message size, then the + * If the buffer size @count is less than the data message size, the * message is kept on the input queue and @has_more_data is set to 1. - * If the message data has been entirely written, the message is removed from + * If all message data has been written, the message is removed from * the input queue. * * The function returns the number of bytes written to the terminal, zero if * there are no pending data messages available or if there is no established * IUCV path. * If the IUCV path has been severed, then -EPIPE is returned to cause a - * hang up (that is issued by the HVC console layer). + * hang up (that is issued by the HVC layer). */ static int hvc_iucv_write(struct hvc_iucv_private *priv, char *buf, int count, int *has_more_data) @@ -204,12 +208,12 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv, int written; int rc; - /* Immediately return if there is no IUCV connection */ + /* immediately return if there is no IUCV connection */ if (priv->iucv_state == IUCV_DISCONN) return 0; - /* If the IUCV path has been severed, return -EPIPE to inform the - * hvc console layer to hang up the tty device. */ + /* if the IUCV path has been severed, return -EPIPE to inform the + * HVC layer to hang up the tty device. */ if (priv->iucv_state == IUCV_SEVERED) return -EPIPE; @@ -217,7 +221,7 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv, if (list_empty(&priv->tty_inqueue)) return 0; - /* receive a iucv message and flip data to the tty (ldisc) */ + /* receive an iucv message and flip data to the tty (ldisc) */ rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list); written = 0; @@ -260,7 +264,7 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv, case MSG_TYPE_WINSIZE: if (rb->mbuf->datalen != sizeof(struct winsize)) break; - hvc_resize(priv->hvc, *((struct winsize *)rb->mbuf->data)); + hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); break; case MSG_TYPE_ERROR: /* ignored ... */ @@ -284,10 +288,9 @@ out_written: * @buf: Pointer to a buffer to store data * @count: Size of buffer available for writing * - * The hvc_console thread calls this method to read characters from - * the terminal backend. If an IUCV communication path has been established, - * pending IUCV messages are received and data is copied into buffer @buf - * up to @count bytes. + * The HVC thread calls this method to read characters from the back-end. + * If an IUCV communication path has been established, pending IUCV messages + * are received and data is copied into buffer @buf up to @count bytes. * * Locking: The routine gets called under an irqsave() spinlock; and * the routine locks the struct hvc_iucv_private->lock to call @@ -318,66 +321,122 @@ static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) } /** - * hvc_iucv_send() - Send an IUCV message containing terminal data. + * hvc_iucv_queue() - Buffer terminal data for sending. * @priv: Pointer to struct hvc_iucv_private instance. * @buf: Buffer containing data to send. - * @size: Size of buffer and amount of data to send. + * @count: Size of buffer and amount of data to send. + * + * The function queues data for sending. To actually send the buffered data, + * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY). + * The function returns the number of data bytes that has been buffered. * - * If an IUCV communication path is established, the function copies the buffer - * data to a newly allocated struct iucv_tty_buffer element, sends the data and - * puts the element to the outqueue. + * If the device is not connected, data is ignored and the function returns + * @count. + * If the buffer is full, the function returns 0. + * If an existing IUCV communicaton path has been severed, -EPIPE is returned + * (that can be passed to HVC layer to cause a tty hangup). + */ +static int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf, + int count) +{ + size_t len; + + if (priv->iucv_state == IUCV_DISCONN) + return count; /* ignore data */ + + if (priv->iucv_state == IUCV_SEVERED) + return -EPIPE; + + len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len); + if (!len) + return 0; + + memcpy(priv->sndbuf + priv->sndbuf_len, buf, len); + priv->sndbuf_len += len; + + if (priv->iucv_state == IUCV_CONNECTED) + schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY); + + return len; +} + +/** + * hvc_iucv_send() - Send an IUCV message containing terminal data. + * @priv: Pointer to struct hvc_iucv_private instance. * - * If there is no IUCV communication path established, the function returns 0. - * If an existing IUCV communicaton path has been severed, the function returns - * -EPIPE (can be passed to HVC layer to cause a tty hangup). + * If an IUCV communication path has been established, the buffered output data + * is sent via an IUCV message and the number of bytes sent is returned. + * Returns 0 if there is no established IUCV communication path or + * -EPIPE if an existing IUCV communicaton path has been severed. */ -static int hvc_iucv_send(struct hvc_iucv_private *priv, const char *buf, - int count) +static int hvc_iucv_send(struct hvc_iucv_private *priv) { struct iucv_tty_buffer *sb; - int rc; - u16 len; + int rc, len; if (priv->iucv_state == IUCV_SEVERED) return -EPIPE; if (priv->iucv_state == IUCV_DISCONN) - return 0; + return -EIO; - len = min_t(u16, MSG_MAX_DATALEN, count); + if (!priv->sndbuf_len) + return 0; /* allocate internal buffer to store msg data and also compute total * message length */ - sb = alloc_tty_buffer(len, GFP_ATOMIC); + sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC); if (!sb) return -ENOMEM; - sb->mbuf->datalen = len; - memcpy(sb->mbuf->data, buf, len); + memcpy(sb->mbuf->data, priv->sndbuf, priv->sndbuf_len); + sb->mbuf->datalen = (u16) priv->sndbuf_len; + sb->msg.length = MSG_SIZE(sb->mbuf->datalen); list_add_tail(&sb->list, &priv->tty_outqueue); rc = __iucv_message_send(priv->path, &sb->msg, 0, 0, (void *) sb->mbuf, sb->msg.length); if (rc) { + /* drop the message here; however we might want to handle + * 0x03 (msg limit reached) by trying again... */ list_del(&sb->list); destroy_tty_buffer(sb); - len = 0; } + len = priv->sndbuf_len; + priv->sndbuf_len = 0; return len; } /** + * hvc_iucv_sndbuf_work() - Send buffered data over IUCV + * @work: Work structure. + * + * This work queue function sends buffered output data over IUCV and, + * if not all buffered data could be sent, reschedules itself. + */ +static void hvc_iucv_sndbuf_work(struct work_struct *work) +{ + struct hvc_iucv_private *priv; + + priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work); + if (!priv) + return; + + spin_lock_bh(&priv->lock); + hvc_iucv_send(priv); + spin_unlock_bh(&priv->lock); +} + +/** * hvc_iucv_put_chars() - HVC put_chars operation. * @vtermno: HVC virtual terminal number. * @buf: Pointer to an buffer to read data from * @count: Size of buffer available for reading * - * The hvc_console thread calls this method to write characters from - * to the terminal backend. - * The function calls hvc_iucv_send() under the lock of the - * struct hvc_iucv_private instance that corresponds to the tty @vtermno. + * The HVC thread calls this method to write characters to the back-end. + * The function calls hvc_iucv_queue() to queue terminal data for sending. * * Locking: The method gets called under an irqsave() spinlock; and * locks struct hvc_iucv_private->lock. @@ -385,7 +444,7 @@ static int hvc_iucv_send(struct hvc_iucv_private *priv, const char *buf, static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) { struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); - int sent; + int queued; if (count <= 0) return 0; @@ -394,10 +453,10 @@ static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) return -ENODEV; spin_lock(&priv->lock); - sent = hvc_iucv_send(priv, buf, count); + queued = hvc_iucv_queue(priv, buf, count); spin_unlock(&priv->lock); - return sent; + return queued; } /** @@ -406,7 +465,7 @@ static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) * @id: Additional data (originally passed to hvc_alloc): the index of an struct * hvc_iucv_private instance. * - * The function sets the tty state to TTY_OPEN for the struct hvc_iucv_private + * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private * instance that is derived from @id. Always returns 0. * * Locking: struct hvc_iucv_private->lock, spin_lock_bh @@ -427,12 +486,8 @@ static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id) } /** - * hvc_iucv_cleanup() - Clean up function if the tty portion is finally closed. + * hvc_iucv_cleanup() - Clean up and reset a z/VM IUCV HVC instance. * @priv: Pointer to the struct hvc_iucv_private instance. - * - * The functions severs the established IUCV communication path (if any), and - * destroy struct iucv_tty_buffer elements from the in- and outqueue. Finally, - * the functions resets the states to TTY_CLOSED and IUCV_DISCONN. */ static void hvc_iucv_cleanup(struct hvc_iucv_private *priv) { @@ -441,25 +496,62 @@ static void hvc_iucv_cleanup(struct hvc_iucv_private *priv) priv->tty_state = TTY_CLOSED; priv->iucv_state = IUCV_DISCONN; + + priv->sndbuf_len = 0; } /** - * hvc_iucv_notifier_hangup() - HVC notifier for tty hangups. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): the index of an struct - * hvc_iucv_private instance. + * tty_outqueue_empty() - Test if the tty outq is empty + * @priv: Pointer to struct hvc_iucv_private instance. + */ +static inline int tty_outqueue_empty(struct hvc_iucv_private *priv) +{ + int rc; + + spin_lock_bh(&priv->lock); + rc = list_empty(&priv->tty_outqueue); + spin_unlock_bh(&priv->lock); + + return rc; +} + +/** + * flush_sndbuf_sync() - Flush send buffer and wait for completion + * @priv: Pointer to struct hvc_iucv_private instance. * - * This routine notifies the HVC backend that a tty hangup (carrier loss, - * virtual or otherwise) has occured. + * The routine cancels a pending sndbuf work, calls hvc_iucv_send() + * to flush any buffered terminal output data and waits for completion. + */ +static void flush_sndbuf_sync(struct hvc_iucv_private *priv) +{ + int sync_wait; + + cancel_delayed_work_sync(&priv->sndbuf_work); + + spin_lock_bh(&priv->lock); + hvc_iucv_send(priv); /* force sending buffered data */ + sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */ + spin_unlock_bh(&priv->lock); + + if (sync_wait) + wait_event_timeout(priv->sndbuf_waitq, + tty_outqueue_empty(priv), HZ); +} + +/** + * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): + * the index of an struct hvc_iucv_private instance. * - * The HVC backend for z/VM IUCV ignores virtual hangups (vhangup()), to keep - * an existing IUCV communication path established. + * This routine notifies the HVC back-end that a tty hangup (carrier loss, + * virtual or otherwise) has occured. + * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup()) + * to keep an existing IUCV communication path established. * (Background: vhangup() is called from user space (by getty or login) to * disable writing to the tty by other applications). - * - * If the tty has been opened (e.g. getty) and an established IUCV path has been - * severed (we caused the tty hangup in that case), then the functions invokes - * hvc_iucv_cleanup() to clean up. + * If the tty has been opened and an established IUCV path has been severed + * (we caused the tty hangup), the function calls hvc_iucv_cleanup(). * * Locking: struct hvc_iucv_private->lock */ @@ -471,12 +563,12 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) if (!priv) return; + flush_sndbuf_sync(priv); + spin_lock_bh(&priv->lock); /* NOTE: If the hangup was scheduled by ourself (from the iucv - * path_servered callback [IUCV_SEVERED]), then we have to - * finally clean up the tty backend structure and set state to - * TTY_CLOSED. - * + * path_servered callback [IUCV_SEVERED]), we have to clean up + * our structure and to set state to TTY_CLOSED. * If the tty was hung up otherwise (e.g. vhangup()), then we * ignore this hangup and keep an established IUCV path open... * (...the reason is that we are not able to connect back to the @@ -494,10 +586,9 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * - * This routine notifies the HVC backend that the last tty device file - * descriptor has been closed. - * The function calls hvc_iucv_cleanup() to clean up the struct hvc_iucv_private - * instance. + * This routine notifies the HVC back-end that the last tty device fd has been + * closed. The function calls hvc_iucv_cleanup() to clean up the struct + * hvc_iucv_private instance. * * Locking: struct hvc_iucv_private->lock */ @@ -510,6 +601,8 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) if (!priv) return; + flush_sndbuf_sync(priv); + spin_lock_bh(&priv->lock); path = priv->path; /* save reference to IUCV path */ priv->path = NULL; @@ -527,20 +620,18 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) /** * hvc_iucv_path_pending() - IUCV handler to process a connection request. * @path: Pending path (struct iucv_path) - * @ipvmid: Originator z/VM system identifier + * @ipvmid: z/VM system identifier of originator * @ipuser: User specified data for this path * (AF_IUCV: port/service name and originator port) * - * The function uses the @ipuser data to check to determine if the pending - * path belongs to a terminal managed by this HVC backend. - * If the check is successful, then an additional check is done to ensure - * that a terminal cannot be accessed multiple times (only one connection - * to a terminal is allowed). In that particular case, the pending path is - * severed. If it is the first connection, the pending path is accepted and - * associated to the struct hvc_iucv_private. The iucv state is updated to - * reflect that a communication path has been established. + * The function uses the @ipuser data to determine if the pending path belongs + * to a terminal managed by this device driver. + * If the path belongs to this driver, ensure that the terminal is not accessed + * multiple times (only one connection to a terminal is allowed). + * If the terminal is not yet connected, the pending path is accepted and is + * associated to the appropriate struct hvc_iucv_private instance. * - * Returns 0 if the path belongs to a terminal managed by the this HVC backend; + * Returns 0 if @path belongs to a terminal managed by the this device driver; * otherwise returns -ENODEV in order to dispatch this path to other handlers. * * Locking: struct hvc_iucv_private->lock @@ -559,7 +650,6 @@ static int hvc_iucv_path_pending(struct iucv_path *path, priv = hvc_iucv_table[i]; break; } - if (!priv) return -ENODEV; @@ -588,6 +678,9 @@ static int hvc_iucv_path_pending(struct iucv_path *path, priv->path = path; priv->iucv_state = IUCV_CONNECTED; + /* flush buffered output data... */ + schedule_delayed_work(&priv->sndbuf_work, 5); + out_path_handled: spin_unlock(&priv->lock); return 0; @@ -603,8 +696,7 @@ out_path_handled: * sets the iucv state to IUCV_SEVERED for the associated struct * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty * hangup (hvc_iucv_get_chars() / hvc_iucv_write()). - * - * If tty portion of the HVC is closed then clean up the outqueue in addition. + * If tty portion of the HVC is closed, clean up the outqueue. * * Locking: struct hvc_iucv_private->lock */ @@ -615,15 +707,25 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) spin_lock(&priv->lock); priv->iucv_state = IUCV_SEVERED; - /* NOTE: If the tty has not yet been opened by a getty program - * (e.g. to see console messages), then cleanup the - * hvc_iucv_private structure to allow re-connects. + /* If the tty has not yet been opened, clean up the hvc_iucv_private + * structure to allow re-connects. + * This is also done for our console device because console hangups + * are handled specially and no notifier is called by HVC. + * The tty session is active (TTY_OPEN) and ready for re-connects... * - * If the tty has been opened, the get_chars() callback returns - * -EPIPE to signal the hvc console layer to hang up the tty. */ + * If it has been opened, let get_chars() return -EPIPE to signal the + * HVC layer to hang up the tty. + * If so, we need to wake up the HVC thread to call get_chars()... + */ priv->path = NULL; if (priv->tty_state == TTY_CLOSED) hvc_iucv_cleanup(priv); + else + if (priv->is_console) { + hvc_iucv_cleanup(priv); + priv->tty_state = TTY_OPENED; + } else + hvc_kick(); spin_unlock(&priv->lock); /* finally sever path (outside of priv->lock due to lock ordering) */ @@ -636,9 +738,9 @@ static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) * @path: Pending path (struct iucv_path) * @msg: Pointer to the IUCV message * - * The function stores an incoming message on the input queue for later + * The function puts an incoming message on the input queue for later * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). - * However, if the tty has not yet been opened, the message is rejected. + * If the tty has not yet been opened, the message is rejected. * * Locking: struct hvc_iucv_private->lock */ @@ -648,6 +750,12 @@ static void hvc_iucv_msg_pending(struct iucv_path *path, struct hvc_iucv_private *priv = path->private; struct iucv_tty_buffer *rb; + /* reject messages that exceed max size of iucv_tty_msg->datalen */ + if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) { + iucv_message_reject(path, msg); + return; + } + spin_lock(&priv->lock); /* reject messages if tty has not yet been opened */ @@ -656,7 +764,7 @@ static void hvc_iucv_msg_pending(struct iucv_path *path, goto unlock_return; } - /* allocate buffer an empty buffer element */ + /* allocate tty buffer to save iucv msg only */ rb = alloc_tty_buffer(0, GFP_ATOMIC); if (!rb) { iucv_message_reject(path, msg); @@ -666,7 +774,7 @@ static void hvc_iucv_msg_pending(struct iucv_path *path, list_add_tail(&rb->list, &priv->tty_inqueue); - hvc_kick(); /* wakup hvc console thread */ + hvc_kick(); /* wake up hvc thread */ unlock_return: spin_unlock(&priv->lock); @@ -677,10 +785,10 @@ unlock_return: * @path: Pending path (struct iucv_path) * @msg: Pointer to the IUCV message * - * The function is called upon completion of message delivery and the - * message is removed from the outqueue. Additional delivery information - * can be found in msg->audit: rejected messages (0x040000 (IPADRJCT)) and - * purged messages (0x010000 (IPADPGNR)). + * The function is called upon completion of message delivery to remove the + * message from the outqueue. Additional delivery information can be found + * msg->audit: rejected messages (0x040000 (IPADRJCT)), and + * purged messages (0x010000 (IPADPGNR)). * * Locking: struct hvc_iucv_private->lock */ @@ -697,6 +805,7 @@ static void hvc_iucv_msg_complete(struct iucv_path *path, list_move(&ent->list, &list_remove); break; } + wake_up(&priv->sndbuf_waitq); spin_unlock(&priv->lock); destroy_tty_buffer_list(&list_remove); } @@ -713,13 +822,14 @@ static struct hv_ops hvc_iucv_ops = { /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance - * @id: hvc_iucv_table index + * @id: hvc_iucv_table index + * @is_console: Flag if the instance is used as Linux console * - * This function allocates a new hvc_iucv_private struct and put the - * instance into hvc_iucv_table at index @id. + * This function allocates a new hvc_iucv_private structure and stores + * the instance in hvc_iucv_table at index @id. * Returns 0 on success; otherwise non-zero. */ -static int __init hvc_iucv_alloc(int id) +static int __init hvc_iucv_alloc(int id, unsigned int is_console) { struct hvc_iucv_private *priv; char name[9]; @@ -732,18 +842,33 @@ static int __init hvc_iucv_alloc(int id) spin_lock_init(&priv->lock); INIT_LIST_HEAD(&priv->tty_outqueue); INIT_LIST_HEAD(&priv->tty_inqueue); + INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work); + init_waitqueue_head(&priv->sndbuf_waitq); + + priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL); + if (!priv->sndbuf) { + kfree(priv); + return -ENOMEM; + } + + /* set console flag */ + priv->is_console = is_console; - /* Finally allocate hvc */ - priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, - HVC_IUCV_MAGIC + id, &hvc_iucv_ops, PAGE_SIZE); + /* finally allocate hvc */ + priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ + HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); if (IS_ERR(priv->hvc)) { rc = PTR_ERR(priv->hvc); + free_page((unsigned long) priv->sndbuf); kfree(priv); return rc; } + /* notify HVC thread instead of using polling */ + priv->hvc->irq_requested = 1; + /* setup iucv related information */ - snprintf(name, 9, "ihvc%-4d", id); + snprintf(name, 9, "lnxhvc%-2d", id); memcpy(priv->srv_name, name, 8); ASCEBC(priv->srv_name, 8); @@ -752,15 +877,16 @@ static int __init hvc_iucv_alloc(int id) } /** - * hvc_iucv_init() - Initialization of HVC backend for z/VM IUCV + * hvc_iucv_init() - z/VM IUCV HVC device driver initialization */ static int __init hvc_iucv_init(void) { - int rc, i; + int rc; + unsigned int i; if (!MACHINE_IS_VM) { - pr_warning("The z/VM IUCV Hypervisor console cannot be " - "used without z/VM.\n"); + pr_info("The z/VM IUCV HVC device driver cannot " + "be used without z/VM\n"); return -ENODEV; } @@ -774,26 +900,33 @@ static int __init hvc_iucv_init(void) sizeof(struct iucv_tty_buffer), 0, 0, NULL); if (!hvc_iucv_buffer_cache) { - pr_err("Not enough memory for driver initialization " - "(rs=%d).\n", 1); + pr_err("Allocating memory failed with reason code=%d\n", 1); return -ENOMEM; } hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, hvc_iucv_buffer_cache); if (!hvc_iucv_mempool) { - pr_err("Not enough memory for driver initialization " - "(rs=%d).\n", 2); + pr_err("Allocating memory failed with reason code=%d\n", 2); kmem_cache_destroy(hvc_iucv_buffer_cache); return -ENOMEM; } + /* register the first terminal device as console + * (must be done before allocating hvc terminal devices) */ + rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops); + if (rc) { + pr_err("Registering HVC terminal device as " + "Linux console failed\n"); + goto out_error_memory; + } + /* allocate hvc_iucv_private structs */ for (i = 0; i < hvc_iucv_devices; i++) { - rc = hvc_iucv_alloc(i); + rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0); if (rc) { - pr_err("Could not create new z/VM IUCV HVC backend " - "rc=%d.\n", rc); + pr_err("Creating a new HVC terminal device " + "failed with error code=%d\n", rc); goto out_error_hvc; } } @@ -801,7 +934,8 @@ static int __init hvc_iucv_init(void) /* register IUCV callback handler */ rc = iucv_register(&hvc_iucv_handler, 0); if (rc) { - pr_err("Could not register iucv handler (rc=%d).\n", rc); + pr_err("Registering IUCV handlers failed with error code=%d\n", + rc); goto out_error_iucv; } @@ -816,22 +950,13 @@ out_error_hvc: hvc_remove(hvc_iucv_table[i]->hvc); kfree(hvc_iucv_table[i]); } +out_error_memory: mempool_destroy(hvc_iucv_mempool); kmem_cache_destroy(hvc_iucv_buffer_cache); return rc; } /** - * hvc_iucv_console_init() - Early console initialization - */ -static int __init hvc_iucv_console_init(void) -{ - if (!MACHINE_IS_VM || !hvc_iucv_devices) - return -ENODEV; - return hvc_instantiate(HVC_IUCV_MAGIC, 0, &hvc_iucv_ops); -} - -/** * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter * @val: Parameter value (numeric) */ @@ -841,10 +966,5 @@ static int __init hvc_iucv_config(char *val) } -module_init(hvc_iucv_init); -console_initcall(hvc_iucv_console_init); +device_initcall(hvc_iucv_init); __setup("hvc_iucv=", hvc_iucv_config); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("HVC back-end for z/VM IUCV."); -MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>"); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 8859aeac2d25..9b3e09cd41f9 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -482,7 +482,7 @@ static void n2rng_dump_test_buffer(struct n2rng *np) int i; for (i = 0; i < SELFTEST_BUFFER_WORDS; i++) - dev_err(&np->op->dev, "Test buffer slot %d [0x%016lx]\n", + dev_err(&np->op->dev, "Test buffer slot %d [0x%016llx]\n", i, np->test_buffer[i]); } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 112a6ba9a96f..146c97613da0 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -32,7 +32,7 @@ /* These are global because they are accessed in tty_io.c */ #ifdef CONFIG_UNIX98_PTYS -struct tty_driver *ptm_driver; +static struct tty_driver *ptm_driver; static struct tty_driver *pts_driver; #endif diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 20d6efb6324e..e0d0f8b2696b 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -48,9 +48,10 @@ * CONFIG_HPET_EMULATE_RTC * 1.12a Maciej W. Rozycki: Handle memory-mapped chips properly. * 1.12ac Alan Cox: Allow read access to the day of week register + * 1.12b David John: Remove calls to the BKL. */ -#define RTC_VERSION "1.12ac" +#define RTC_VERSION "1.12b" /* * Note that *all* calls to CMOS_READ and CMOS_WRITE are done with @@ -73,7 +74,6 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/spinlock.h> -#include <linux/smp_lock.h> #include <linux/sysctl.h> #include <linux/wait.h> #include <linux/bcd.h> @@ -182,8 +182,8 @@ static int rtc_proc_open(struct inode *inode, struct file *file); /* * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is - * protected by the big kernel lock. However, ioctl can still disable the timer - * in rtc_status and then with del_timer after the interrupt has read + * protected by the spin lock rtc_lock. However, ioctl can still disable the + * timer in rtc_status and then with del_timer after the interrupt has read * rtc_status but before mod_timer is called, which would then reenable the * timer (but you would need to have an awful timing before you'd trip on it) */ @@ -720,9 +720,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; - lock_kernel(); ret = rtc_do_ioctl(cmd, arg, 0); - unlock_kernel(); return ret; } @@ -731,12 +729,8 @@ static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * Also clear the previous interrupt data on an open, and clean * up things on a close. */ - -/* We use rtc_lock to protect against concurrent opens. So the BKL is not - * needed here. Or anywhere else in this driver. */ static int rtc_open(struct inode *inode, struct file *file) { - lock_kernel(); spin_lock_irq(&rtc_lock); if (rtc_status & RTC_IS_OPEN) @@ -746,12 +740,10 @@ static int rtc_open(struct inode *inode, struct file *file) rtc_irq_data = 0; spin_unlock_irq(&rtc_lock); - unlock_kernel(); return 0; out_busy: spin_unlock_irq(&rtc_lock); - unlock_kernel(); return -EBUSY; } @@ -800,7 +792,6 @@ no_irq: } #ifdef RTC_IRQ -/* Called without the kernel lock - fine */ static unsigned int rtc_poll(struct file *file, poll_table *wait) { unsigned long l; diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 68f052b42ed7..ed306eb1057f 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -23,8 +23,6 @@ #include <linux/security.h> #include <linux/module.h> #include <acpi/acpi.h> -#include <acpi/actypes.h> -#include <acpi/actbl.h> #include "tpm.h" #define TCG_EVENT_NAME_LEN_MAX 255 diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index ab18c1e7b115..70efba2ee053 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -273,12 +273,23 @@ static void tpm_nsc_remove(struct device *dev) } } -static struct device_driver nsc_drv = { - .name = "tpm_nsc", - .bus = &platform_bus_type, - .owner = THIS_MODULE, - .suspend = tpm_pm_suspend, - .resume = tpm_pm_resume, +static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg) +{ + return tpm_pm_suspend(&dev->dev, msg); +} + +static int tpm_nsc_resume(struct platform_device *dev) +{ + return tpm_pm_resume(&dev->dev); +} + +static struct platform_driver nsc_drv = { + .suspend = tpm_nsc_suspend, + .resume = tpm_nsc_resume, + .driver = { + .name = "tpm_nsc", + .owner = THIS_MODULE, + }, }; static int __init init_nsc(void) @@ -297,7 +308,7 @@ static int __init init_nsc(void) return -ENODEV; } - err = driver_register(&nsc_drv); + err = platform_driver_register(&nsc_drv); if (err) return err; @@ -308,17 +319,15 @@ static int __init init_nsc(void) /* enable the DPM module */ tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01); - pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + pdev = platform_device_alloc("tpm_nscl0", -1); if (!pdev) { rc = -ENOMEM; goto err_unreg_drv; } - pdev->name = "tpm_nscl0"; - pdev->id = -1; pdev->num_resources = 0; + pdev->dev.driver = &nsc_drv.driver; pdev->dev.release = tpm_nsc_remove; - pdev->dev.driver = &nsc_drv; if ((rc = platform_device_register(pdev)) < 0) goto err_free_dev; @@ -377,7 +386,7 @@ err_unreg_dev: err_free_dev: kfree(pdev); err_unreg_drv: - driver_unregister(&nsc_drv); + platform_driver_unregister(&nsc_drv); return rc; } @@ -390,7 +399,7 @@ static void __exit cleanup_nsc(void) pdev = NULL; } - driver_unregister(&nsc_drv); + platform_driver_unregister(&nsc_drv); } module_init(init_nsc); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 80014213fb53..7900bd63b36d 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -969,8 +969,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) * Takes the console sem and the called methods then take the tty * termios_mutex and the tty ctrl_lock in that order. */ - -int vt_resize(struct tty_struct *tty, struct winsize *ws) +static int vt_resize(struct tty_struct *tty, struct winsize *ws) { struct vc_data *vc = tty->driver_data; int ret; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 01dde80597f7..b55cb67435bd 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -584,12 +584,12 @@ out: return i; } -static ssize_t show_cpus(cpumask_t mask, char *buf) +static ssize_t show_cpus(const struct cpumask *mask, char *buf) { ssize_t i = 0; unsigned int cpu; - for_each_cpu_mask_nr(cpu, mask) { + for_each_cpu(cpu, mask) { if (i) i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " "); i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u", cpu); @@ -606,7 +606,7 @@ static ssize_t show_cpus(cpumask_t mask, char *buf) */ static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf) { - if (cpus_empty(policy->related_cpus)) + if (cpumask_empty(policy->related_cpus)) return show_cpus(policy->cpus, buf); return show_cpus(policy->related_cpus, buf); } @@ -806,9 +806,20 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) ret = -ENOMEM; goto nomem_out; } + if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL)) { + kfree(policy); + ret = -ENOMEM; + goto nomem_out; + } + if (!alloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) { + free_cpumask_var(policy->cpus); + kfree(policy); + ret = -ENOMEM; + goto nomem_out; + } policy->cpu = cpu; - policy->cpus = cpumask_of_cpu(cpu); + cpumask_copy(policy->cpus, cpumask_of(cpu)); /* Initially set CPU itself as the policy_cpu */ per_cpu(policy_cpu, cpu) = cpu; @@ -843,7 +854,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) } #endif - for_each_cpu_mask_nr(j, policy->cpus) { + for_each_cpu(j, policy->cpus) { if (cpu == j) continue; @@ -861,7 +872,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) goto err_out_driver_exit; spin_lock_irqsave(&cpufreq_driver_lock, flags); - managed_policy->cpus = policy->cpus; + cpumask_copy(managed_policy->cpus, policy->cpus); per_cpu(cpufreq_cpu_data, cpu) = managed_policy; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -916,14 +927,14 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) } spin_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu_mask_nr(j, policy->cpus) { + for_each_cpu(j, policy->cpus) { per_cpu(cpufreq_cpu_data, j) = policy; per_cpu(policy_cpu, j) = policy->cpu; } spin_unlock_irqrestore(&cpufreq_driver_lock, flags); /* symlink affected CPUs */ - for_each_cpu_mask_nr(j, policy->cpus) { + for_each_cpu(j, policy->cpus) { if (j == cpu) continue; if (!cpu_online(j)) @@ -963,7 +974,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) err_out_unregister: spin_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu_mask_nr(j, policy->cpus) + for_each_cpu(j, policy->cpus) per_cpu(cpufreq_cpu_data, j) = NULL; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -1024,7 +1035,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) */ if (unlikely(cpu != data->cpu)) { dprintk("removing link\n"); - cpu_clear(cpu, data->cpus); + cpumask_clear_cpu(cpu, data->cpus); spin_unlock_irqrestore(&cpufreq_driver_lock, flags); sysfs_remove_link(&sys_dev->kobj, "cpufreq"); cpufreq_cpu_put(data); @@ -1045,8 +1056,8 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) * per_cpu(cpufreq_cpu_data) while holding the lock, and remove * the sysfs links afterwards. */ - if (unlikely(cpus_weight(data->cpus) > 1)) { - for_each_cpu_mask_nr(j, data->cpus) { + if (unlikely(cpumask_weight(data->cpus) > 1)) { + for_each_cpu(j, data->cpus) { if (j == cpu) continue; per_cpu(cpufreq_cpu_data, j) = NULL; @@ -1055,8 +1066,8 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) spin_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (unlikely(cpus_weight(data->cpus) > 1)) { - for_each_cpu_mask_nr(j, data->cpus) { + if (unlikely(cpumask_weight(data->cpus) > 1)) { + for_each_cpu(j, data->cpus) { if (j == cpu) continue; dprintk("removing link for cpu %u\n", j); @@ -1090,7 +1101,10 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) if (cpufreq_driver->exit) cpufreq_driver->exit(data); + free_cpumask_var(data->related_cpus); + free_cpumask_var(data->cpus); kfree(data); + per_cpu(cpufreq_cpu_data, cpu) = NULL; cpufreq_debug_enable_ratelimit(); return 0; diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index e2657837d954..0320962c4ec5 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -498,7 +498,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } - for_each_cpu_mask_nr(j, policy->cpus) { + for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); j_dbs_info->cur_policy = policy; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 2ab3c12b88af..6a2b036c9389 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -400,7 +400,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* Get Absolute Load - in terms of freq */ max_load_freq = 0; - for_each_cpu_mask_nr(j, policy->cpus) { + for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; cputime64_t cur_wall_time, cur_idle_time; unsigned int idle_time, wall_time; @@ -568,7 +568,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } - for_each_cpu_mask_nr(j, policy->cpus) { + for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cpu_dbs_info, j); j_dbs_info->cur_policy = policy; diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 8d7cf3f31450..f1df59f59a37 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -15,12 +15,14 @@ #include <linux/tick.h> #define BREAK_FUZZ 4 /* 4 us */ +#define PRED_HISTORY_PCT 50 struct menu_device { int last_state_idx; unsigned int expected_us; unsigned int predicted_us; + unsigned int current_predicted_us; unsigned int last_measured_us; unsigned int elapsed_us; }; @@ -47,6 +49,12 @@ static int menu_select(struct cpuidle_device *dev) data->expected_us = (u32) ktime_to_ns(tick_nohz_get_sleep_length()) / 1000; + /* Recalculate predicted_us based on prediction_history_pct */ + data->predicted_us *= PRED_HISTORY_PCT; + data->predicted_us += (100 - PRED_HISTORY_PCT) * + data->current_predicted_us; + data->predicted_us /= 100; + /* find the deepest idle state that satisfies our constraints */ for (i = CPUIDLE_DRIVER_STATE_START + 1; i < dev->state_count; i++) { struct cpuidle_state *s = &dev->states[i]; @@ -97,7 +105,7 @@ static void menu_reflect(struct cpuidle_device *dev) measured_us = -1; /* Predict time until next break event */ - data->predicted_us = max(measured_us, data->last_measured_us); + data->current_predicted_us = max(measured_us, data->last_measured_us); if (last_idle_us + BREAK_FUZZ < data->expected_us - target->exit_latency) { diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index d883e1b8bb8c..55433849bfa6 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -270,6 +270,6 @@ static void __exit dca_exit(void) dca_sysfs_exit(); } -subsys_initcall(dca_init); +arch_initcall(dca_init); module_exit(dca_exit); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 904e57558bb5..e34b06420816 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -33,7 +33,6 @@ config INTEL_IOATDMA config INTEL_IOP_ADMA tristate "Intel IOP ADMA support" depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IOP13XX - select ASYNC_CORE select DMA_ENGINE help Enable support for the Intel(R) IOP Series RAID engines. @@ -59,7 +58,6 @@ config FSL_DMA config MV_XOR bool "Marvell XOR engine support" depends on PLAT_ORION - select ASYNC_CORE select DMA_ENGINE ---help--- Enable support for the Marvell XOR engine. diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 657996517374..403dbe781122 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -31,32 +31,18 @@ * * LOCKING: * - * The subsystem keeps two global lists, dma_device_list and dma_client_list. - * Both of these are protected by a mutex, dma_list_mutex. + * The subsystem keeps a global list of dma_device structs it is protected by a + * mutex, dma_list_mutex. + * + * A subsystem can get access to a channel by calling dmaengine_get() followed + * by dma_find_channel(), or if it has need for an exclusive channel it can call + * dma_request_channel(). Once a channel is allocated a reference is taken + * against its corresponding driver to disable removal. * * Each device has a channels list, which runs unlocked but is never modified * once the device is registered, it's just setup by the driver. * - * Each client is responsible for keeping track of the channels it uses. See - * the definition of dma_event_callback in dmaengine.h. - * - * Each device has a kref, which is initialized to 1 when the device is - * registered. A kref_get is done for each device registered. When the - * device is released, the corresponding kref_put is done in the release - * method. Every time one of the device's channels is allocated to a client, - * a kref_get occurs. When the channel is freed, the corresponding kref_put - * happens. The device's release function does a completion, so - * unregister_device does a remove event, device_unregister, a kref_put - * for the first reference, then waits on the completion for all other - * references to finish. - * - * Each channel has an open-coded implementation of Rusty Russell's "bigref," - * with a kref and a per_cpu local_t. A dma_chan_get is called when a client - * signals that it wants to use a channel, and dma_chan_put is called when - * a channel is removed or a client using it is unregistered. A client can - * take extra references per outstanding transaction, as is the case with - * the NET DMA client. The release function does a kref_put on the device. - * -ChrisL, DanW + * See Documentation/dmaengine.txt for more details */ #include <linux/init.h> @@ -70,54 +56,85 @@ #include <linux/rcupdate.h> #include <linux/mutex.h> #include <linux/jiffies.h> +#include <linux/rculist.h> +#include <linux/idr.h> static DEFINE_MUTEX(dma_list_mutex); static LIST_HEAD(dma_device_list); -static LIST_HEAD(dma_client_list); +static long dmaengine_ref_count; +static struct idr dma_idr; /* --- sysfs implementation --- */ +/** + * dev_to_dma_chan - convert a device pointer to the its sysfs container object + * @dev - device node + * + * Must be called under dma_list_mutex + */ +static struct dma_chan *dev_to_dma_chan(struct device *dev) +{ + struct dma_chan_dev *chan_dev; + + chan_dev = container_of(dev, typeof(*chan_dev), device); + return chan_dev->chan; +} + static ssize_t show_memcpy_count(struct device *dev, struct device_attribute *attr, char *buf) { - struct dma_chan *chan = to_dma_chan(dev); + struct dma_chan *chan; unsigned long count = 0; int i; + int err; - for_each_possible_cpu(i) - count += per_cpu_ptr(chan->local, i)->memcpy_count; + mutex_lock(&dma_list_mutex); + chan = dev_to_dma_chan(dev); + if (chan) { + for_each_possible_cpu(i) + count += per_cpu_ptr(chan->local, i)->memcpy_count; + err = sprintf(buf, "%lu\n", count); + } else + err = -ENODEV; + mutex_unlock(&dma_list_mutex); - return sprintf(buf, "%lu\n", count); + return err; } static ssize_t show_bytes_transferred(struct device *dev, struct device_attribute *attr, char *buf) { - struct dma_chan *chan = to_dma_chan(dev); + struct dma_chan *chan; unsigned long count = 0; int i; + int err; - for_each_possible_cpu(i) - count += per_cpu_ptr(chan->local, i)->bytes_transferred; + mutex_lock(&dma_list_mutex); + chan = dev_to_dma_chan(dev); + if (chan) { + for_each_possible_cpu(i) + count += per_cpu_ptr(chan->local, i)->bytes_transferred; + err = sprintf(buf, "%lu\n", count); + } else + err = -ENODEV; + mutex_unlock(&dma_list_mutex); - return sprintf(buf, "%lu\n", count); + return err; } static ssize_t show_in_use(struct device *dev, struct device_attribute *attr, char *buf) { - struct dma_chan *chan = to_dma_chan(dev); - int in_use = 0; - - if (unlikely(chan->slow_ref) && - atomic_read(&chan->refcount.refcount) > 1) - in_use = 1; - else { - if (local_read(&(per_cpu_ptr(chan->local, - get_cpu())->refcount)) > 0) - in_use = 1; - put_cpu(); - } + struct dma_chan *chan; + int err; - return sprintf(buf, "%d\n", in_use); + mutex_lock(&dma_list_mutex); + chan = dev_to_dma_chan(dev); + if (chan) + err = sprintf(buf, "%d\n", chan->client_count); + else + err = -ENODEV; + mutex_unlock(&dma_list_mutex); + + return err; } static struct device_attribute dma_attrs[] = { @@ -127,76 +144,110 @@ static struct device_attribute dma_attrs[] = { __ATTR_NULL }; -static void dma_async_device_cleanup(struct kref *kref); - -static void dma_dev_release(struct device *dev) +static void chan_dev_release(struct device *dev) { - struct dma_chan *chan = to_dma_chan(dev); - kref_put(&chan->device->refcount, dma_async_device_cleanup); + struct dma_chan_dev *chan_dev; + + chan_dev = container_of(dev, typeof(*chan_dev), device); + if (atomic_dec_and_test(chan_dev->idr_ref)) { + mutex_lock(&dma_list_mutex); + idr_remove(&dma_idr, chan_dev->dev_id); + mutex_unlock(&dma_list_mutex); + kfree(chan_dev->idr_ref); + } + kfree(chan_dev); } static struct class dma_devclass = { .name = "dma", .dev_attrs = dma_attrs, - .dev_release = dma_dev_release, + .dev_release = chan_dev_release, }; /* --- client and device registration --- */ -#define dma_chan_satisfies_mask(chan, mask) \ - __dma_chan_satisfies_mask((chan), &(mask)) +#define dma_device_satisfies_mask(device, mask) \ + __dma_device_satisfies_mask((device), &(mask)) static int -__dma_chan_satisfies_mask(struct dma_chan *chan, dma_cap_mask_t *want) +__dma_device_satisfies_mask(struct dma_device *device, dma_cap_mask_t *want) { dma_cap_mask_t has; - bitmap_and(has.bits, want->bits, chan->device->cap_mask.bits, + bitmap_and(has.bits, want->bits, device->cap_mask.bits, DMA_TX_TYPE_END); return bitmap_equal(want->bits, has.bits, DMA_TX_TYPE_END); } +static struct module *dma_chan_to_owner(struct dma_chan *chan) +{ + return chan->device->dev->driver->owner; +} + /** - * dma_client_chan_alloc - try to allocate channels to a client - * @client: &dma_client + * balance_ref_count - catch up the channel reference count + * @chan - channel to balance ->client_count versus dmaengine_ref_count * - * Called with dma_list_mutex held. + * balance_ref_count must be called under dma_list_mutex */ -static void dma_client_chan_alloc(struct dma_client *client) +static void balance_ref_count(struct dma_chan *chan) { - struct dma_device *device; - struct dma_chan *chan; - int desc; /* allocated descriptor count */ - enum dma_state_client ack; + struct module *owner = dma_chan_to_owner(chan); - /* Find a channel */ - list_for_each_entry(device, &dma_device_list, global_node) { - /* Does the client require a specific DMA controller? */ - if (client->slave && client->slave->dma_dev - && client->slave->dma_dev != device->dev) - continue; + while (chan->client_count < dmaengine_ref_count) { + __module_get(owner); + chan->client_count++; + } +} - list_for_each_entry(chan, &device->channels, device_node) { - if (!dma_chan_satisfies_mask(chan, client->cap_mask)) - continue; +/** + * dma_chan_get - try to grab a dma channel's parent driver module + * @chan - channel to grab + * + * Must be called under dma_list_mutex + */ +static int dma_chan_get(struct dma_chan *chan) +{ + int err = -ENODEV; + struct module *owner = dma_chan_to_owner(chan); + + if (chan->client_count) { + __module_get(owner); + err = 0; + } else if (try_module_get(owner)) + err = 0; + + if (err == 0) + chan->client_count++; + + /* allocate upon first client reference */ + if (chan->client_count == 1 && err == 0) { + int desc_cnt = chan->device->device_alloc_chan_resources(chan); + + if (desc_cnt < 0) { + err = desc_cnt; + chan->client_count = 0; + module_put(owner); + } else if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask)) + balance_ref_count(chan); + } - desc = chan->device->device_alloc_chan_resources( - chan, client); - if (desc >= 0) { - ack = client->event_callback(client, - chan, - DMA_RESOURCE_AVAILABLE); + return err; +} - /* we are done once this client rejects - * an available resource - */ - if (ack == DMA_ACK) { - dma_chan_get(chan); - chan->client_count++; - } else if (ack == DMA_NAK) - return; - } - } - } +/** + * dma_chan_put - drop a reference to a dma channel's parent driver module + * @chan - channel to release + * + * Must be called under dma_list_mutex + */ +static void dma_chan_put(struct dma_chan *chan) +{ + if (!chan->client_count) + return; /* this channel failed alloc_chan_resources */ + chan->client_count--; + module_put(dma_chan_to_owner(chan)); + if (chan->client_count == 0) + chan->device->device_free_chan_resources(chan); } enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) @@ -218,138 +269,342 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) EXPORT_SYMBOL(dma_sync_wait); /** - * dma_chan_cleanup - release a DMA channel's resources - * @kref: kernel reference structure that contains the DMA channel device + * dma_cap_mask_all - enable iteration over all operation types + */ +static dma_cap_mask_t dma_cap_mask_all; + +/** + * dma_chan_tbl_ent - tracks channel allocations per core/operation + * @chan - associated channel for this entry + */ +struct dma_chan_tbl_ent { + struct dma_chan *chan; +}; + +/** + * channel_table - percpu lookup table for memory-to-memory offload providers */ -void dma_chan_cleanup(struct kref *kref) +static struct dma_chan_tbl_ent *channel_table[DMA_TX_TYPE_END]; + +static int __init dma_channel_table_init(void) { - struct dma_chan *chan = container_of(kref, struct dma_chan, refcount); - chan->device->device_free_chan_resources(chan); - kref_put(&chan->device->refcount, dma_async_device_cleanup); + enum dma_transaction_type cap; + int err = 0; + + bitmap_fill(dma_cap_mask_all.bits, DMA_TX_TYPE_END); + + /* 'interrupt', 'private', and 'slave' are channel capabilities, + * but are not associated with an operation so they do not need + * an entry in the channel_table + */ + clear_bit(DMA_INTERRUPT, dma_cap_mask_all.bits); + clear_bit(DMA_PRIVATE, dma_cap_mask_all.bits); + clear_bit(DMA_SLAVE, dma_cap_mask_all.bits); + + for_each_dma_cap_mask(cap, dma_cap_mask_all) { + channel_table[cap] = alloc_percpu(struct dma_chan_tbl_ent); + if (!channel_table[cap]) { + err = -ENOMEM; + break; + } + } + + if (err) { + pr_err("dmaengine: initialization failure\n"); + for_each_dma_cap_mask(cap, dma_cap_mask_all) + if (channel_table[cap]) + free_percpu(channel_table[cap]); + } + + return err; } -EXPORT_SYMBOL(dma_chan_cleanup); +arch_initcall(dma_channel_table_init); -static void dma_chan_free_rcu(struct rcu_head *rcu) +/** + * dma_find_channel - find a channel to carry out the operation + * @tx_type: transaction type + */ +struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type) { - struct dma_chan *chan = container_of(rcu, struct dma_chan, rcu); - int bias = 0x7FFFFFFF; - int i; - for_each_possible_cpu(i) - bias -= local_read(&per_cpu_ptr(chan->local, i)->refcount); - atomic_sub(bias, &chan->refcount.refcount); - kref_put(&chan->refcount, dma_chan_cleanup); + struct dma_chan *chan; + int cpu; + + WARN_ONCE(dmaengine_ref_count == 0, + "client called %s without a reference", __func__); + + cpu = get_cpu(); + chan = per_cpu_ptr(channel_table[tx_type], cpu)->chan; + put_cpu(); + + return chan; } +EXPORT_SYMBOL(dma_find_channel); -static void dma_chan_release(struct dma_chan *chan) +/** + * dma_issue_pending_all - flush all pending operations across all channels + */ +void dma_issue_pending_all(void) { - atomic_add(0x7FFFFFFF, &chan->refcount.refcount); - chan->slow_ref = 1; - call_rcu(&chan->rcu, dma_chan_free_rcu); + struct dma_device *device; + struct dma_chan *chan; + + WARN_ONCE(dmaengine_ref_count == 0, + "client called %s without a reference", __func__); + + rcu_read_lock(); + list_for_each_entry_rcu(device, &dma_device_list, global_node) { + if (dma_has_cap(DMA_PRIVATE, device->cap_mask)) + continue; + list_for_each_entry(chan, &device->channels, device_node) + if (chan->client_count) + device->device_issue_pending(chan); + } + rcu_read_unlock(); } +EXPORT_SYMBOL(dma_issue_pending_all); /** - * dma_chans_notify_available - broadcast available channels to the clients + * nth_chan - returns the nth channel of the given capability + * @cap: capability to match + * @n: nth channel desired + * + * Defaults to returning the channel with the desired capability and the + * lowest reference count when 'n' cannot be satisfied. Must be called + * under dma_list_mutex. */ -static void dma_clients_notify_available(void) +static struct dma_chan *nth_chan(enum dma_transaction_type cap, int n) { - struct dma_client *client; + struct dma_device *device; + struct dma_chan *chan; + struct dma_chan *ret = NULL; + struct dma_chan *min = NULL; - mutex_lock(&dma_list_mutex); + list_for_each_entry(device, &dma_device_list, global_node) { + if (!dma_has_cap(cap, device->cap_mask) || + dma_has_cap(DMA_PRIVATE, device->cap_mask)) + continue; + list_for_each_entry(chan, &device->channels, device_node) { + if (!chan->client_count) + continue; + if (!min) + min = chan; + else if (chan->table_count < min->table_count) + min = chan; + + if (n-- == 0) { + ret = chan; + break; /* done */ + } + } + if (ret) + break; /* done */ + } - list_for_each_entry(client, &dma_client_list, global_node) - dma_client_chan_alloc(client); + if (!ret) + ret = min; - mutex_unlock(&dma_list_mutex); + if (ret) + ret->table_count++; + + return ret; } /** - * dma_chans_notify_available - tell the clients that a channel is going away - * @chan: channel on its way out + * dma_channel_rebalance - redistribute the available channels + * + * Optimize for cpu isolation (each cpu gets a dedicated channel for an + * operation type) in the SMP case, and operation isolation (avoid + * multi-tasking channels) in the non-SMP case. Must be called under + * dma_list_mutex. */ -static void dma_clients_notify_removed(struct dma_chan *chan) +static void dma_channel_rebalance(void) { - struct dma_client *client; - enum dma_state_client ack; + struct dma_chan *chan; + struct dma_device *device; + int cpu; + int cap; + int n; - mutex_lock(&dma_list_mutex); + /* undo the last distribution */ + for_each_dma_cap_mask(cap, dma_cap_mask_all) + for_each_possible_cpu(cpu) + per_cpu_ptr(channel_table[cap], cpu)->chan = NULL; + + list_for_each_entry(device, &dma_device_list, global_node) { + if (dma_has_cap(DMA_PRIVATE, device->cap_mask)) + continue; + list_for_each_entry(chan, &device->channels, device_node) + chan->table_count = 0; + } - list_for_each_entry(client, &dma_client_list, global_node) { - ack = client->event_callback(client, chan, - DMA_RESOURCE_REMOVED); + /* don't populate the channel_table if no clients are available */ + if (!dmaengine_ref_count) + return; - /* client was holding resources for this channel so - * free it - */ - if (ack == DMA_ACK) { - dma_chan_put(chan); - chan->client_count--; + /* redistribute available channels */ + n = 0; + for_each_dma_cap_mask(cap, dma_cap_mask_all) + for_each_online_cpu(cpu) { + if (num_possible_cpus() > 1) + chan = nth_chan(cap, n++); + else + chan = nth_chan(cap, -1); + + per_cpu_ptr(channel_table[cap], cpu)->chan = chan; + } +} + +static struct dma_chan *private_candidate(dma_cap_mask_t *mask, struct dma_device *dev, + dma_filter_fn fn, void *fn_param) +{ + struct dma_chan *chan; + + if (!__dma_device_satisfies_mask(dev, mask)) { + pr_debug("%s: wrong capabilities\n", __func__); + return NULL; + } + /* devices with multiple channels need special handling as we need to + * ensure that all channels are either private or public. + */ + if (dev->chancnt > 1 && !dma_has_cap(DMA_PRIVATE, dev->cap_mask)) + list_for_each_entry(chan, &dev->channels, device_node) { + /* some channels are already publicly allocated */ + if (chan->client_count) + return NULL; } + + list_for_each_entry(chan, &dev->channels, device_node) { + if (chan->client_count) { + pr_debug("%s: %s busy\n", + __func__, dma_chan_name(chan)); + continue; + } + if (fn && !fn(chan, fn_param)) { + pr_debug("%s: %s filter said false\n", + __func__, dma_chan_name(chan)); + continue; + } + return chan; } - mutex_unlock(&dma_list_mutex); + return NULL; } /** - * dma_async_client_register - register a &dma_client - * @client: ptr to a client structure with valid 'event_callback' and 'cap_mask' + * dma_request_channel - try to allocate an exclusive channel + * @mask: capabilities that the channel must satisfy + * @fn: optional callback to disposition available channels + * @fn_param: opaque parameter to pass to dma_filter_fn */ -void dma_async_client_register(struct dma_client *client) +struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param) { - /* validate client data */ - BUG_ON(dma_has_cap(DMA_SLAVE, client->cap_mask) && - !client->slave); + struct dma_device *device, *_d; + struct dma_chan *chan = NULL; + int err; + /* Find a channel */ + mutex_lock(&dma_list_mutex); + list_for_each_entry_safe(device, _d, &dma_device_list, global_node) { + chan = private_candidate(mask, device, fn, fn_param); + if (chan) { + /* Found a suitable channel, try to grab, prep, and + * return it. We first set DMA_PRIVATE to disable + * balance_ref_count as this channel will not be + * published in the general-purpose allocator + */ + dma_cap_set(DMA_PRIVATE, device->cap_mask); + err = dma_chan_get(chan); + + if (err == -ENODEV) { + pr_debug("%s: %s module removed\n", __func__, + dma_chan_name(chan)); + list_del_rcu(&device->global_node); + } else if (err) + pr_err("dmaengine: failed to get %s: (%d)\n", + dma_chan_name(chan), err); + else + break; + chan = NULL; + } + } + mutex_unlock(&dma_list_mutex); + + pr_debug("%s: %s (%s)\n", __func__, chan ? "success" : "fail", + chan ? dma_chan_name(chan) : NULL); + + return chan; +} +EXPORT_SYMBOL_GPL(__dma_request_channel); + +void dma_release_channel(struct dma_chan *chan) +{ mutex_lock(&dma_list_mutex); - list_add_tail(&client->global_node, &dma_client_list); + WARN_ONCE(chan->client_count != 1, + "chan reference count %d != 1\n", chan->client_count); + dma_chan_put(chan); mutex_unlock(&dma_list_mutex); } -EXPORT_SYMBOL(dma_async_client_register); +EXPORT_SYMBOL_GPL(dma_release_channel); /** - * dma_async_client_unregister - unregister a client and free the &dma_client - * @client: &dma_client to free - * - * Force frees any allocated DMA channels, frees the &dma_client memory + * dmaengine_get - register interest in dma_channels */ -void dma_async_client_unregister(struct dma_client *client) +void dmaengine_get(void) { - struct dma_device *device; + struct dma_device *device, *_d; struct dma_chan *chan; - enum dma_state_client ack; - - if (!client) - return; + int err; mutex_lock(&dma_list_mutex); - /* free all channels the client is holding */ - list_for_each_entry(device, &dma_device_list, global_node) - list_for_each_entry(chan, &device->channels, device_node) { - ack = client->event_callback(client, chan, - DMA_RESOURCE_REMOVED); + dmaengine_ref_count++; - if (ack == DMA_ACK) { - dma_chan_put(chan); - chan->client_count--; - } + /* try to grab channels */ + list_for_each_entry_safe(device, _d, &dma_device_list, global_node) { + if (dma_has_cap(DMA_PRIVATE, device->cap_mask)) + continue; + list_for_each_entry(chan, &device->channels, device_node) { + err = dma_chan_get(chan); + if (err == -ENODEV) { + /* module removed before we could use it */ + list_del_rcu(&device->global_node); + break; + } else if (err) + pr_err("dmaengine: failed to get %s: (%d)\n", + dma_chan_name(chan), err); } + } - list_del(&client->global_node); + /* if this is the first reference and there were channels + * waiting we need to rebalance to get those channels + * incorporated into the channel table + */ + if (dmaengine_ref_count == 1) + dma_channel_rebalance(); mutex_unlock(&dma_list_mutex); } -EXPORT_SYMBOL(dma_async_client_unregister); +EXPORT_SYMBOL(dmaengine_get); /** - * dma_async_client_chan_request - send all available channels to the - * client that satisfy the capability mask - * @client - requester + * dmaengine_put - let dma drivers be removed when ref_count == 0 */ -void dma_async_client_chan_request(struct dma_client *client) +void dmaengine_put(void) { + struct dma_device *device; + struct dma_chan *chan; + mutex_lock(&dma_list_mutex); - dma_client_chan_alloc(client); + dmaengine_ref_count--; + BUG_ON(dmaengine_ref_count < 0); + /* drop channel references */ + list_for_each_entry(device, &dma_device_list, global_node) { + if (dma_has_cap(DMA_PRIVATE, device->cap_mask)) + continue; + list_for_each_entry(chan, &device->channels, device_node) + dma_chan_put(chan); + } mutex_unlock(&dma_list_mutex); } -EXPORT_SYMBOL(dma_async_client_chan_request); +EXPORT_SYMBOL(dmaengine_put); /** * dma_async_device_register - registers DMA devices found @@ -357,9 +612,9 @@ EXPORT_SYMBOL(dma_async_client_chan_request); */ int dma_async_device_register(struct dma_device *device) { - static int id; int chancnt = 0, rc; struct dma_chan* chan; + atomic_t *idr_ref; if (!device) return -ENODEV; @@ -386,57 +641,83 @@ int dma_async_device_register(struct dma_device *device) BUG_ON(!device->device_issue_pending); BUG_ON(!device->dev); - init_completion(&device->done); - kref_init(&device->refcount); - + idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL); + if (!idr_ref) + return -ENOMEM; + atomic_set(idr_ref, 0); + idr_retry: + if (!idr_pre_get(&dma_idr, GFP_KERNEL)) + return -ENOMEM; mutex_lock(&dma_list_mutex); - device->dev_id = id++; + rc = idr_get_new(&dma_idr, NULL, &device->dev_id); mutex_unlock(&dma_list_mutex); + if (rc == -EAGAIN) + goto idr_retry; + else if (rc != 0) + return rc; /* represent channels in sysfs. Probably want devs too */ list_for_each_entry(chan, &device->channels, device_node) { chan->local = alloc_percpu(typeof(*chan->local)); if (chan->local == NULL) continue; + chan->dev = kzalloc(sizeof(*chan->dev), GFP_KERNEL); + if (chan->dev == NULL) { + free_percpu(chan->local); + continue; + } chan->chan_id = chancnt++; - chan->dev.class = &dma_devclass; - chan->dev.parent = device->dev; - dev_set_name(&chan->dev, "dma%dchan%d", + chan->dev->device.class = &dma_devclass; + chan->dev->device.parent = device->dev; + chan->dev->chan = chan; + chan->dev->idr_ref = idr_ref; + chan->dev->dev_id = device->dev_id; + atomic_inc(idr_ref); + dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id); - rc = device_register(&chan->dev); + rc = device_register(&chan->dev->device); if (rc) { - chancnt--; free_percpu(chan->local); chan->local = NULL; goto err_out; } - - /* One for the channel, one of the class device */ - kref_get(&device->refcount); - kref_get(&device->refcount); - kref_init(&chan->refcount); chan->client_count = 0; - chan->slow_ref = 0; - INIT_RCU_HEAD(&chan->rcu); } + device->chancnt = chancnt; mutex_lock(&dma_list_mutex); - list_add_tail(&device->global_node, &dma_device_list); + /* take references on public channels */ + if (dmaengine_ref_count && !dma_has_cap(DMA_PRIVATE, device->cap_mask)) + list_for_each_entry(chan, &device->channels, device_node) { + /* if clients are already waiting for channels we need + * to take references on their behalf + */ + if (dma_chan_get(chan) == -ENODEV) { + /* note we can only get here for the first + * channel as the remaining channels are + * guaranteed to get a reference + */ + rc = -ENODEV; + mutex_unlock(&dma_list_mutex); + goto err_out; + } + } + list_add_tail_rcu(&device->global_node, &dma_device_list); + dma_channel_rebalance(); mutex_unlock(&dma_list_mutex); - dma_clients_notify_available(); - return 0; err_out: list_for_each_entry(chan, &device->channels, device_node) { if (chan->local == NULL) continue; - kref_put(&device->refcount, dma_async_device_cleanup); - device_unregister(&chan->dev); - chancnt--; + mutex_lock(&dma_list_mutex); + chan->dev->chan = NULL; + mutex_unlock(&dma_list_mutex); + device_unregister(&chan->dev->device); free_percpu(chan->local); } return rc; @@ -444,37 +725,30 @@ err_out: EXPORT_SYMBOL(dma_async_device_register); /** - * dma_async_device_cleanup - function called when all references are released - * @kref: kernel reference object - */ -static void dma_async_device_cleanup(struct kref *kref) -{ - struct dma_device *device; - - device = container_of(kref, struct dma_device, refcount); - complete(&device->done); -} - -/** - * dma_async_device_unregister - unregisters DMA devices + * dma_async_device_unregister - unregister a DMA device * @device: &dma_device + * + * This routine is called by dma driver exit routines, dmaengine holds module + * references to prevent it being called while channels are in use. */ void dma_async_device_unregister(struct dma_device *device) { struct dma_chan *chan; mutex_lock(&dma_list_mutex); - list_del(&device->global_node); + list_del_rcu(&device->global_node); + dma_channel_rebalance(); mutex_unlock(&dma_list_mutex); list_for_each_entry(chan, &device->channels, device_node) { - dma_clients_notify_removed(chan); - device_unregister(&chan->dev); - dma_chan_release(chan); + WARN_ONCE(chan->client_count, + "%s called while %d clients hold a reference\n", + __func__, chan->client_count); + mutex_lock(&dma_list_mutex); + chan->dev->chan = NULL; + mutex_unlock(&dma_list_mutex); + device_unregister(&chan->dev->device); } - - kref_put(&device->refcount, dma_async_device_cleanup); - wait_for_completion(&device->done); } EXPORT_SYMBOL(dma_async_device_unregister); @@ -626,10 +900,96 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, } EXPORT_SYMBOL(dma_async_tx_descriptor_init); +/* dma_wait_for_async_tx - spin wait for a transaction to complete + * @tx: in-flight transaction to wait on + * + * This routine assumes that tx was obtained from a call to async_memcpy, + * async_xor, async_memset, etc which ensures that tx is "in-flight" (prepped + * and submitted). Walking the parent chain is only meant to cover for DMA + * drivers that do not implement the DMA_INTERRUPT capability and may race with + * the driver's descriptor cleanup routine. + */ +enum dma_status +dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx) +{ + enum dma_status status; + struct dma_async_tx_descriptor *iter; + struct dma_async_tx_descriptor *parent; + + if (!tx) + return DMA_SUCCESS; + + WARN_ONCE(tx->parent, "%s: speculatively walking dependency chain for" + " %s\n", __func__, dma_chan_name(tx->chan)); + + /* poll through the dependency chain, return when tx is complete */ + do { + iter = tx; + + /* find the root of the unsubmitted dependency chain */ + do { + parent = iter->parent; + if (!parent) + break; + else + iter = parent; + } while (parent); + + /* there is a small window for ->parent == NULL and + * ->cookie == -EBUSY + */ + while (iter->cookie == -EBUSY) + cpu_relax(); + + status = dma_sync_wait(iter->chan, iter->cookie); + } while (status == DMA_IN_PROGRESS || (iter != tx)); + + return status; +} +EXPORT_SYMBOL_GPL(dma_wait_for_async_tx); + +/* dma_run_dependencies - helper routine for dma drivers to process + * (start) dependent operations on their target channel + * @tx: transaction with dependencies + */ +void dma_run_dependencies(struct dma_async_tx_descriptor *tx) +{ + struct dma_async_tx_descriptor *dep = tx->next; + struct dma_async_tx_descriptor *dep_next; + struct dma_chan *chan; + + if (!dep) + return; + + chan = dep->chan; + + /* keep submitting up until a channel switch is detected + * in that case we will be called again as a result of + * processing the interrupt from async_tx_channel_switch + */ + for (; dep; dep = dep_next) { + spin_lock_bh(&dep->lock); + dep->parent = NULL; + dep_next = dep->next; + if (dep_next && dep_next->chan == chan) + dep->next = NULL; /* ->next will be submitted */ + else + dep_next = NULL; /* submit current dep and terminate */ + spin_unlock_bh(&dep->lock); + + dep->tx_submit(dep); + } + + chan->device->device_issue_pending(chan); +} +EXPORT_SYMBOL_GPL(dma_run_dependencies); + static int __init dma_bus_init(void) { + idr_init(&dma_idr); mutex_init(&dma_list_mutex); return class_register(&dma_devclass); } -subsys_initcall(dma_bus_init); +arch_initcall(dma_bus_init); + diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index ed9636bfb54a..3603f1ea5b28 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -35,7 +35,7 @@ MODULE_PARM_DESC(threads_per_chan, static unsigned int max_channels; module_param(max_channels, uint, S_IRUGO); -MODULE_PARM_DESC(nr_channels, +MODULE_PARM_DESC(max_channels, "Maximum number of channels to use (default: all)"); /* @@ -71,7 +71,7 @@ struct dmatest_chan { /* * These are protected by dma_list_mutex since they're only used by - * the DMA client event callback + * the DMA filter function callback */ static LIST_HEAD(dmatest_channels); static unsigned int nr_channels; @@ -80,7 +80,7 @@ static bool dmatest_match_channel(struct dma_chan *chan) { if (test_channel[0] == '\0') return true; - return strcmp(dev_name(&chan->dev), test_channel) == 0; + return strcmp(dma_chan_name(chan), test_channel) == 0; } static bool dmatest_match_device(struct dma_device *device) @@ -215,7 +215,6 @@ static int dmatest_func(void *data) smp_rmb(); chan = thread->chan; - dma_chan_get(chan); while (!kthread_should_stop()) { total_tests++; @@ -293,7 +292,6 @@ static int dmatest_func(void *data) } ret = 0; - dma_chan_put(chan); kfree(thread->dstbuf); err_dstbuf: kfree(thread->srcbuf); @@ -319,21 +317,16 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) kfree(dtc); } -static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) +static int dmatest_add_channel(struct dma_chan *chan) { struct dmatest_chan *dtc; struct dmatest_thread *thread; unsigned int i; - /* Have we already been told about this channel? */ - list_for_each_entry(dtc, &dmatest_channels, node) - if (dtc->chan == chan) - return DMA_DUP; - dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); if (!dtc) { - pr_warning("dmatest: No memory for %s\n", dev_name(&chan->dev)); - return DMA_NAK; + pr_warning("dmatest: No memory for %s\n", dma_chan_name(chan)); + return -ENOMEM; } dtc->chan = chan; @@ -343,16 +336,16 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); if (!thread) { pr_warning("dmatest: No memory for %s-test%u\n", - dev_name(&chan->dev), i); + dma_chan_name(chan), i); break; } thread->chan = dtc->chan; smp_wmb(); thread->task = kthread_run(dmatest_func, thread, "%s-test%u", - dev_name(&chan->dev), i); + dma_chan_name(chan), i); if (IS_ERR(thread->task)) { pr_warning("dmatest: Failed to run thread %s-test%u\n", - dev_name(&chan->dev), i); + dma_chan_name(chan), i); kfree(thread); break; } @@ -362,86 +355,62 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) list_add_tail(&thread->node, &dtc->threads); } - pr_info("dmatest: Started %u threads using %s\n", i, dev_name(&chan->dev)); + pr_info("dmatest: Started %u threads using %s\n", i, dma_chan_name(chan)); list_add_tail(&dtc->node, &dmatest_channels); nr_channels++; - return DMA_ACK; -} - -static enum dma_state_client dmatest_remove_channel(struct dma_chan *chan) -{ - struct dmatest_chan *dtc, *_dtc; - - list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) { - if (dtc->chan == chan) { - list_del(&dtc->node); - dmatest_cleanup_channel(dtc); - pr_debug("dmatest: lost channel %s\n", - dev_name(&chan->dev)); - return DMA_ACK; - } - } - - return DMA_DUP; + return 0; } -/* - * Start testing threads as new channels are assigned to us, and kill - * them when the channels go away. - * - * When we unregister the client, all channels are removed so this - * will also take care of cleaning things up when the module is - * unloaded. - */ -static enum dma_state_client -dmatest_event(struct dma_client *client, struct dma_chan *chan, - enum dma_state state) +static bool filter(struct dma_chan *chan, void *param) { - enum dma_state_client ack = DMA_NAK; - - switch (state) { - case DMA_RESOURCE_AVAILABLE: - if (!dmatest_match_channel(chan) - || !dmatest_match_device(chan->device)) - ack = DMA_DUP; - else if (max_channels && nr_channels >= max_channels) - ack = DMA_NAK; - else - ack = dmatest_add_channel(chan); - break; - - case DMA_RESOURCE_REMOVED: - ack = dmatest_remove_channel(chan); - break; - - default: - pr_info("dmatest: Unhandled event %u (%s)\n", - state, dev_name(&chan->dev)); - break; - } - - return ack; + if (!dmatest_match_channel(chan) || !dmatest_match_device(chan->device)) + return false; + else + return true; } -static struct dma_client dmatest_client = { - .event_callback = dmatest_event, -}; - static int __init dmatest_init(void) { - dma_cap_set(DMA_MEMCPY, dmatest_client.cap_mask); - dma_async_client_register(&dmatest_client); - dma_async_client_chan_request(&dmatest_client); + dma_cap_mask_t mask; + struct dma_chan *chan; + int err = 0; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + for (;;) { + chan = dma_request_channel(mask, filter, NULL); + if (chan) { + err = dmatest_add_channel(chan); + if (err == 0) + continue; + else { + dma_release_channel(chan); + break; /* add_channel failed, punt */ + } + } else + break; /* no more channels available */ + if (max_channels && nr_channels >= max_channels) + break; /* we have all we need */ + } - return 0; + return err; } -module_init(dmatest_init); +/* when compiled-in wait for drivers to load first */ +late_initcall(dmatest_init); static void __exit dmatest_exit(void) { - dma_async_client_unregister(&dmatest_client); + struct dmatest_chan *dtc, *_dtc; + + list_for_each_entry_safe(dtc, _dtc, &dmatest_channels, node) { + list_del(&dtc->node); + dmatest_cleanup_channel(dtc); + pr_debug("dmatest: dropped channel %s\n", + dma_chan_name(dtc->chan)); + dma_release_channel(dtc->chan); + } } module_exit(dmatest_exit); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 0778d99aea7c..6b702cc46b3d 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -70,6 +70,15 @@ * the controller, though. */ +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} +static struct device *chan2parent(struct dma_chan *chan) +{ + return chan->dev->device.parent; +} + static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) { return list_entry(dwc->active_list.next, struct dw_desc, desc_node); @@ -93,12 +102,12 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) ret = desc; break; } - dev_dbg(&dwc->chan.dev, "desc %p not ACKed\n", desc); + dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); i++; } spin_unlock_bh(&dwc->lock); - dev_vdbg(&dwc->chan.dev, "scanned %u descriptors on freelist\n", i); + dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); return ret; } @@ -108,10 +117,10 @@ static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) struct dw_desc *child; list_for_each_entry(child, &desc->txd.tx_list, desc_node) - dma_sync_single_for_cpu(dwc->chan.dev.parent, + dma_sync_single_for_cpu(chan2parent(&dwc->chan), child->txd.phys, sizeof(child->lli), DMA_TO_DEVICE); - dma_sync_single_for_cpu(dwc->chan.dev.parent, + dma_sync_single_for_cpu(chan2parent(&dwc->chan), desc->txd.phys, sizeof(desc->lli), DMA_TO_DEVICE); } @@ -129,11 +138,11 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) spin_lock_bh(&dwc->lock); list_for_each_entry(child, &desc->txd.tx_list, desc_node) - dev_vdbg(&dwc->chan.dev, + dev_vdbg(chan2dev(&dwc->chan), "moving child desc %p to freelist\n", child); list_splice_init(&desc->txd.tx_list, &dwc->free_list); - dev_vdbg(&dwc->chan.dev, "moving desc %p to freelist\n", desc); + dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); list_add(&desc->desc_node, &dwc->free_list); spin_unlock_bh(&dwc->lock); } @@ -163,9 +172,9 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) /* ASSERT: channel is idle */ if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(&dwc->chan.dev, + dev_err(chan2dev(&dwc->chan), "BUG: Attempted to start non-idle channel\n"); - dev_err(&dwc->chan.dev, + dev_err(chan2dev(&dwc->chan), " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", channel_readl(dwc, SAR), channel_readl(dwc, DAR), @@ -193,7 +202,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) void *param; struct dma_async_tx_descriptor *txd = &desc->txd; - dev_vdbg(&dwc->chan.dev, "descriptor %u complete\n", txd->cookie); + dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); dwc->completed = txd->cookie; callback = txd->callback; @@ -208,11 +217,11 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc) * mapped before they were submitted... */ if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) - dma_unmap_page(dwc->chan.dev.parent, desc->lli.dar, desc->len, - DMA_FROM_DEVICE); + dma_unmap_page(chan2parent(&dwc->chan), desc->lli.dar, + desc->len, DMA_FROM_DEVICE); if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) - dma_unmap_page(dwc->chan.dev.parent, desc->lli.sar, desc->len, - DMA_TO_DEVICE); + dma_unmap_page(chan2parent(&dwc->chan), desc->lli.sar, + desc->len, DMA_TO_DEVICE); /* * The API requires that no submissions are done from a @@ -228,7 +237,7 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) LIST_HEAD(list); if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(&dwc->chan.dev, + dev_err(chan2dev(&dwc->chan), "BUG: XFER bit set, but channel not idle!\n"); /* Try to continue after resetting the channel... */ @@ -273,7 +282,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) return; } - dev_vdbg(&dwc->chan.dev, "scan_descriptors: llp=0x%x\n", llp); + dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%x\n", llp); list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { if (desc->lli.llp == llp) @@ -292,7 +301,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_descriptor_complete(dwc, desc); } - dev_err(&dwc->chan.dev, + dev_err(chan2dev(&dwc->chan), "BUG: All descriptors done, but channel not idle!\n"); /* Try to continue after resetting the channel... */ @@ -308,7 +317,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) { - dev_printk(KERN_CRIT, &dwc->chan.dev, + dev_printk(KERN_CRIT, chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); @@ -342,9 +351,9 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) * controller flagged an error instead of scribbling over * random memory locations. */ - dev_printk(KERN_CRIT, &dwc->chan.dev, + dev_printk(KERN_CRIT, chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); - dev_printk(KERN_CRIT, &dwc->chan.dev, + dev_printk(KERN_CRIT, chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->txd.tx_list, desc_node) @@ -442,12 +451,12 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) * for DMA. But this is hard to do in a race-free manner. */ if (list_empty(&dwc->active_list)) { - dev_vdbg(&tx->chan->dev, "tx_submit: started %u\n", + dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n", desc->txd.cookie); dwc_dostart(dwc, desc); list_add_tail(&desc->desc_node, &dwc->active_list); } else { - dev_vdbg(&tx->chan->dev, "tx_submit: queued %u\n", + dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u\n", desc->txd.cookie); list_add_tail(&desc->desc_node, &dwc->queue); @@ -472,11 +481,11 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, unsigned int dst_width; u32 ctllo; - dev_vdbg(&chan->dev, "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n", + dev_vdbg(chan2dev(chan), "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n", dest, src, len, flags); if (unlikely(!len)) { - dev_dbg(&chan->dev, "prep_dma_memcpy: length is zero!\n"); + dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n"); return NULL; } @@ -516,7 +525,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan->dev.parent, + dma_sync_single_for_device(chan2parent(chan), prev->txd.phys, sizeof(prev->lli), DMA_TO_DEVICE); list_add_tail(&desc->desc_node, @@ -531,7 +540,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan->dev.parent, + dma_sync_single_for_device(chan2parent(chan), prev->txd.phys, sizeof(prev->lli), DMA_TO_DEVICE); @@ -562,15 +571,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct scatterlist *sg; size_t total_len = 0; - dev_vdbg(&chan->dev, "prep_dma_slave\n"); + dev_vdbg(chan2dev(chan), "prep_dma_slave\n"); if (unlikely(!dws || !sg_len)) return NULL; - reg_width = dws->slave.reg_width; + reg_width = dws->reg_width; prev = first = NULL; - sg_len = dma_map_sg(chan->dev.parent, sgl, sg_len, direction); + sg_len = dma_map_sg(chan2parent(chan), sgl, sg_len, direction); switch (direction) { case DMA_TO_DEVICE: @@ -579,7 +588,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | DWC_CTLL_DST_FIX | DWC_CTLL_SRC_INC | DWC_CTLL_FC_M2P); - reg = dws->slave.tx_reg; + reg = dws->tx_reg; for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len; @@ -587,7 +596,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc = dwc_desc_get(dwc); if (!desc) { - dev_err(&chan->dev, + dev_err(chan2dev(chan), "not enough descriptors available\n"); goto err_desc_get; } @@ -607,7 +616,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan->dev.parent, + dma_sync_single_for_device(chan2parent(chan), prev->txd.phys, sizeof(prev->lli), DMA_TO_DEVICE); @@ -625,7 +634,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | DWC_CTLL_SRC_FIX | DWC_CTLL_FC_P2M); - reg = dws->slave.rx_reg; + reg = dws->rx_reg; for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; u32 len; @@ -633,7 +642,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc = dwc_desc_get(dwc); if (!desc) { - dev_err(&chan->dev, + dev_err(chan2dev(chan), "not enough descriptors available\n"); goto err_desc_get; } @@ -653,7 +662,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan->dev.parent, + dma_sync_single_for_device(chan2parent(chan), prev->txd.phys, sizeof(prev->lli), DMA_TO_DEVICE); @@ -673,7 +682,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan->dev.parent, + dma_sync_single_for_device(chan2parent(chan), prev->txd.phys, sizeof(prev->lli), DMA_TO_DEVICE); @@ -758,29 +767,21 @@ static void dwc_issue_pending(struct dma_chan *chan) spin_unlock_bh(&dwc->lock); } -static int dwc_alloc_chan_resources(struct dma_chan *chan, - struct dma_client *client) +static int dwc_alloc_chan_resources(struct dma_chan *chan) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma *dw = to_dw_dma(chan->device); struct dw_desc *desc; - struct dma_slave *slave; struct dw_dma_slave *dws; int i; u32 cfghi; u32 cfglo; - dev_vdbg(&chan->dev, "alloc_chan_resources\n"); - - /* Channels doing slave DMA can only handle one client. */ - if (dwc->dws || client->slave) { - if (chan->client_count) - return -EBUSY; - } + dev_vdbg(chan2dev(chan), "alloc_chan_resources\n"); /* ASSERT: channel is idle */ if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_dbg(&chan->dev, "DMA channel not idle?\n"); + dev_dbg(chan2dev(chan), "DMA channel not idle?\n"); return -EIO; } @@ -789,23 +790,17 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan, cfghi = DWC_CFGH_FIFO_MODE; cfglo = 0; - slave = client->slave; - if (slave) { + dws = dwc->dws; + if (dws) { /* * We need controller-specific data to set up slave * transfers. */ - BUG_ON(!slave->dma_dev || slave->dma_dev != dw->dma.dev); - - dws = container_of(slave, struct dw_dma_slave, slave); + BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); - dwc->dws = dws; cfghi = dws->cfg_hi; cfglo = dws->cfg_lo; - } else { - dwc->dws = NULL; } - channel_writel(dwc, CFG_LO, cfglo); channel_writel(dwc, CFG_HI, cfghi); @@ -822,7 +817,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan, desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); if (!desc) { - dev_info(&chan->dev, + dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); spin_lock_bh(&dwc->lock); break; @@ -832,7 +827,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan, desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; INIT_LIST_HEAD(&desc->txd.tx_list); - desc->txd.phys = dma_map_single(chan->dev.parent, &desc->lli, + desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, sizeof(desc->lli), DMA_TO_DEVICE); dwc_desc_put(dwc, desc); @@ -847,7 +842,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan, spin_unlock_bh(&dwc->lock); - dev_dbg(&chan->dev, + dev_dbg(chan2dev(chan), "alloc_chan_resources allocated %d descriptors\n", i); return i; @@ -860,7 +855,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) struct dw_desc *desc, *_desc; LIST_HEAD(list); - dev_dbg(&chan->dev, "free_chan_resources (descs allocated=%u)\n", + dev_dbg(chan2dev(chan), "free_chan_resources (descs allocated=%u)\n", dwc->descs_allocated); /* ASSERT: channel is idle */ @@ -881,13 +876,13 @@ static void dwc_free_chan_resources(struct dma_chan *chan) spin_unlock_bh(&dwc->lock); list_for_each_entry_safe(desc, _desc, &list, desc_node) { - dev_vdbg(&chan->dev, " freeing descriptor %p\n", desc); - dma_unmap_single(chan->dev.parent, desc->txd.phys, + dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); + dma_unmap_single(chan2parent(chan), desc->txd.phys, sizeof(desc->lli), DMA_TO_DEVICE); kfree(desc); } - dev_vdbg(&chan->dev, "free_chan_resources done\n"); + dev_vdbg(chan2dev(chan), "free_chan_resources done\n"); } /*----------------------------------------------------------------------*/ diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 0b95dcce447e..ca70a21afc68 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -366,8 +366,7 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( * * Return - The number of descriptors allocated. */ -static int fsl_dma_alloc_chan_resources(struct dma_chan *chan, - struct dma_client *client) +static int fsl_dma_alloc_chan_resources(struct dma_chan *chan) { struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan); @@ -823,7 +822,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, */ WARN_ON(fdev->feature != new_fsl_chan->feature); - new_fsl_chan->dev = &new_fsl_chan->common.dev; + new_fsl_chan->dev = &new_fsl_chan->common.dev->device; new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start, new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1); diff --git a/drivers/dma/ioat.c b/drivers/dma/ioat.c index 9b16a3af9a0a..4105d6575b64 100644 --- a/drivers/dma/ioat.c +++ b/drivers/dma/ioat.c @@ -75,60 +75,10 @@ static int ioat_dca_enabled = 1; module_param(ioat_dca_enabled, int, 0644); MODULE_PARM_DESC(ioat_dca_enabled, "control support of dca service (default: 1)"); -static int ioat_setup_functionality(struct pci_dev *pdev, void __iomem *iobase) -{ - struct ioat_device *device = pci_get_drvdata(pdev); - u8 version; - int err = 0; - - version = readb(iobase + IOAT_VER_OFFSET); - switch (version) { - case IOAT_VER_1_2: - device->dma = ioat_dma_probe(pdev, iobase); - if (device->dma && ioat_dca_enabled) - device->dca = ioat_dca_init(pdev, iobase); - break; - case IOAT_VER_2_0: - device->dma = ioat_dma_probe(pdev, iobase); - if (device->dma && ioat_dca_enabled) - device->dca = ioat2_dca_init(pdev, iobase); - break; - case IOAT_VER_3_0: - device->dma = ioat_dma_probe(pdev, iobase); - if (device->dma && ioat_dca_enabled) - device->dca = ioat3_dca_init(pdev, iobase); - break; - default: - err = -ENODEV; - break; - } - if (!device->dma) - err = -ENODEV; - return err; -} - -static void ioat_shutdown_functionality(struct pci_dev *pdev) -{ - struct ioat_device *device = pci_get_drvdata(pdev); - - dev_err(&pdev->dev, "Removing dma and dca services\n"); - if (device->dca) { - unregister_dca_provider(device->dca); - free_dca_provider(device->dca); - device->dca = NULL; - } - - if (device->dma) { - ioat_dma_remove(device->dma); - device->dma = NULL; - } -} - static struct pci_driver ioat_pci_driver = { .name = "ioatdma", .id_table = ioat_pci_tbl, .probe = ioat_probe, - .shutdown = ioat_shutdown_functionality, .remove = __devexit_p(ioat_remove), }; @@ -179,7 +129,29 @@ static int __devinit ioat_probe(struct pci_dev *pdev, pci_set_master(pdev); - err = ioat_setup_functionality(pdev, iobase); + switch (readb(iobase + IOAT_VER_OFFSET)) { + case IOAT_VER_1_2: + device->dma = ioat_dma_probe(pdev, iobase); + if (device->dma && ioat_dca_enabled) + device->dca = ioat_dca_init(pdev, iobase); + break; + case IOAT_VER_2_0: + device->dma = ioat_dma_probe(pdev, iobase); + if (device->dma && ioat_dca_enabled) + device->dca = ioat2_dca_init(pdev, iobase); + break; + case IOAT_VER_3_0: + device->dma = ioat_dma_probe(pdev, iobase); + if (device->dma && ioat_dca_enabled) + device->dca = ioat3_dca_init(pdev, iobase); + break; + default: + err = -ENODEV; + break; + } + if (!device->dma) + err = -ENODEV; + if (err) goto err_version; @@ -198,17 +170,21 @@ err_enable_device: return err; } -/* - * It is unsafe to remove this module: if removed while a requested - * dma is outstanding, esp. from tcp, it is possible to hang while - * waiting for something that will never finish. However, if you're - * feeling lucky, this usually works just fine. - */ static void __devexit ioat_remove(struct pci_dev *pdev) { struct ioat_device *device = pci_get_drvdata(pdev); - ioat_shutdown_functionality(pdev); + dev_err(&pdev->dev, "Removing dma and dca services\n"); + if (device->dca) { + unregister_dca_provider(device->dca); + free_dca_provider(device->dca); + device->dca = NULL; + } + + if (device->dma) { + ioat_dma_remove(device->dma); + device->dma = NULL; + } kfree(device); } diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 6607fdd00b1c..b3759c4b6536 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -734,8 +734,7 @@ static void ioat2_dma_massage_chan_desc(struct ioat_dma_chan *ioat_chan) * ioat_dma_alloc_chan_resources - returns the number of allocated descriptors * @chan: the channel to be filled out */ -static int ioat_dma_alloc_chan_resources(struct dma_chan *chan, - struct dma_client *client) +static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) { struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); struct ioat_desc_sw *desc; @@ -1341,12 +1340,11 @@ static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan) */ #define IOAT_TEST_SIZE 2000 -DECLARE_COMPLETION(test_completion); static void ioat_dma_test_callback(void *dma_async_param) { - printk(KERN_ERR "ioatdma: ioat_dma_test_callback(%p)\n", - dma_async_param); - complete(&test_completion); + struct completion *cmp = dma_async_param; + + complete(cmp); } /** @@ -1363,6 +1361,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) dma_addr_t dma_dest, dma_src; dma_cookie_t cookie; int err = 0; + struct completion cmp; src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); if (!src) @@ -1381,7 +1380,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (device->common.device_alloc_chan_resources(dma_chan, NULL) < 1) { + if (device->common.device_alloc_chan_resources(dma_chan) < 1) { dev_err(&device->pdev->dev, "selftest cannot allocate chan resource\n"); err = -ENODEV; @@ -1402,8 +1401,9 @@ static int ioat_dma_self_test(struct ioatdma_device *device) } async_tx_ack(tx); + init_completion(&cmp); tx->callback = ioat_dma_test_callback; - tx->callback_param = (void *)0x8086; + tx->callback_param = &cmp; cookie = tx->tx_submit(tx); if (cookie < 0) { dev_err(&device->pdev->dev, @@ -1413,7 +1413,7 @@ static int ioat_dma_self_test(struct ioatdma_device *device) } device->common.device_issue_pending(dma_chan); - wait_for_completion_timeout(&test_completion, msecs_to_jiffies(3000)); + wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); if (device->common.device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 6be317262200..ea5440dd10dc 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -24,7 +24,6 @@ #include <linux/init.h> #include <linux/module.h> -#include <linux/async_tx.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/spinlock.h> @@ -116,7 +115,7 @@ iop_adma_run_tx_complete_actions(struct iop_adma_desc_slot *desc, } /* run dependent operations */ - async_tx_run_dependencies(&desc->async_tx); + dma_run_dependencies(&desc->async_tx); return cookie; } @@ -270,8 +269,6 @@ static void __iop_adma_slot_cleanup(struct iop_adma_chan *iop_chan) break; } - BUG_ON(!seen_current); - if (cookie > 0) { iop_chan->completed_cookie = cookie; pr_debug("\tcompleted cookie %d\n", cookie); @@ -471,8 +468,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan); * greater than 2x the number slots needed to satisfy a device->max_xor * request. * */ -static int iop_adma_alloc_chan_resources(struct dma_chan *chan, - struct dma_client *client) +static int iop_adma_alloc_chan_resources(struct dma_chan *chan) { char *hw_desc; int idx; @@ -866,7 +862,7 @@ static int __devinit iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan) < 1) { err = -ENODEV; goto out; } @@ -964,7 +960,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (iop_adma_alloc_chan_resources(dma_chan, NULL) < 1) { + if (iop_adma_alloc_chan_resources(dma_chan) < 1) { err = -ENODEV; goto out; } @@ -1115,26 +1111,13 @@ static int __devexit iop_adma_remove(struct platform_device *dev) struct iop_adma_device *device = platform_get_drvdata(dev); struct dma_chan *chan, *_chan; struct iop_adma_chan *iop_chan; - int i; struct iop_adma_platform_data *plat_data = dev->dev.platform_data; dma_async_device_unregister(&device->common); - for (i = 0; i < 3; i++) { - unsigned int irq; - irq = platform_get_irq(dev, i); - free_irq(irq, device); - } - dma_free_coherent(&dev->dev, plat_data->pool_size, device->dma_desc_pool_virt, device->dma_desc_pool); - do { - struct resource *res; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start); - } while (0); - list_for_each_entry_safe(chan, _chan, &device->common.channels, device_node) { iop_chan = to_iop_adma_chan(chan); @@ -1255,7 +1238,6 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) spin_lock_init(&iop_chan->lock); INIT_LIST_HEAD(&iop_chan->chain); INIT_LIST_HEAD(&iop_chan->all_slots); - INIT_RCU_HEAD(&iop_chan->common.rcu); iop_chan->common.device = dma_dev; list_add_tail(&iop_chan->common.device_node, &dma_dev->channels); @@ -1431,16 +1413,12 @@ static int __init iop_adma_init (void) return platform_driver_register(&iop_adma_driver); } -/* it's currently unsafe to unload this module */ -#if 0 static void __exit iop_adma_exit (void) { platform_driver_unregister(&iop_adma_driver); return; } module_exit(iop_adma_exit); -#endif - module_init(iop_adma_init); MODULE_AUTHOR("Intel Corporation"); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index bcda17426411..d35cbd1ff0b3 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -18,7 +18,6 @@ #include <linux/init.h> #include <linux/module.h> -#include <linux/async_tx.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/spinlock.h> @@ -340,7 +339,7 @@ mv_xor_run_tx_complete_actions(struct mv_xor_desc_slot *desc, } /* run dependent operations */ - async_tx_run_dependencies(&desc->async_tx); + dma_run_dependencies(&desc->async_tx); return cookie; } @@ -607,8 +606,7 @@ submit_done: } /* returns the number of allocated descriptors */ -static int mv_xor_alloc_chan_resources(struct dma_chan *chan, - struct dma_client *client) +static int mv_xor_alloc_chan_resources(struct dma_chan *chan) { char *hw_desc; int idx; @@ -958,7 +956,7 @@ static int __devinit mv_xor_memcpy_self_test(struct mv_xor_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { + if (mv_xor_alloc_chan_resources(dma_chan) < 1) { err = -ENODEV; goto out; } @@ -1053,7 +1051,7 @@ mv_xor_xor_self_test(struct mv_xor_device *device) dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); - if (mv_xor_alloc_chan_resources(dma_chan, NULL) < 1) { + if (mv_xor_alloc_chan_resources(dma_chan) < 1) { err = -ENODEV; goto out; } @@ -1221,7 +1219,6 @@ static int __devinit mv_xor_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mv_chan->chain); INIT_LIST_HEAD(&mv_chan->completed_slots); INIT_LIST_HEAD(&mv_chan->all_slots); - INIT_RCU_HEAD(&mv_chan->common.rcu); mv_chan->common.device = dma_dev; list_add_tail(&mv_chan->common.device_node, &dma_dev->channels); diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index 799f94424c8a..6bd91a15d5e6 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c @@ -209,6 +209,8 @@ fw_card_bm_work(struct work_struct *work) unsigned long flags; int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode; bool do_reset = false; + bool root_device_is_running; + bool root_device_is_cmc; __be32 lock_data[2]; spin_lock_irqsave(&card->lock, flags); @@ -224,8 +226,9 @@ fw_card_bm_work(struct work_struct *work) generation = card->generation; root_device = root_node->data; - if (root_device) - fw_device_get(root_device); + root_device_is_running = root_device && + atomic_read(&root_device->state) == FW_DEVICE_RUNNING; + root_device_is_cmc = root_device && root_device->cmc; root_id = root_node->node_id; grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10)); @@ -308,14 +311,14 @@ fw_card_bm_work(struct work_struct *work) * config rom. In either case, pick another root. */ new_root_id = local_node->node_id; - } else if (atomic_read(&root_device->state) != FW_DEVICE_RUNNING) { + } else if (!root_device_is_running) { /* * If we haven't probed this device yet, bail out now * and let's try again once that's done. */ spin_unlock_irqrestore(&card->lock, flags); goto out; - } else if (root_device->cmc) { + } else if (root_device_is_cmc) { /* * FIXME: I suppose we should set the cmstr bit in the * STATE_CLEAR register of this node, as described in @@ -362,8 +365,6 @@ fw_card_bm_work(struct work_struct *work) fw_core_initiate_bus_reset(card, 1); } out: - if (root_device) - fw_device_put(root_device); fw_node_put(root_node); fw_node_put(local_node); out_put_card: diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index c173be383725..2af5a8d1e012 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c @@ -159,7 +159,8 @@ static void fw_device_release(struct device *dev) /* * Take the card lock so we don't set this to NULL while a - * FW_NODE_UPDATED callback is being handled. + * FW_NODE_UPDATED callback is being handled or while the + * bus manager work looks at this node. */ spin_lock_irqsave(&card->lock, flags); device->node->data = NULL; @@ -695,12 +696,13 @@ static void fw_device_init(struct work_struct *work) return; } - err = -ENOMEM; + device_initialize(&device->device); fw_device_get(device); down_write(&fw_device_rwsem); - if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) - err = idr_get_new(&fw_device_idr, device, &minor); + err = idr_pre_get(&fw_device_idr, GFP_KERNEL) ? + idr_get_new(&fw_device_idr, device, &minor) : + -ENOMEM; up_write(&fw_device_rwsem); if (err < 0) @@ -911,13 +913,14 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) /* * Do minimal intialization of the device here, the - * rest will happen in fw_device_init(). We need the - * card and node so we can read the config rom and we - * need to do device_initialize() now so - * device_for_each_child() in FW_NODE_UPDATED is - * doesn't freak out. + * rest will happen in fw_device_init(). + * + * Attention: A lot of things, even fw_device_get(), + * cannot be done before fw_device_init() finished! + * You can basically just check device->state and + * schedule work until then, but only while holding + * card->lock. */ - device_initialize(&device->device); atomic_set(&device->state, FW_DEVICE_INITIALIZING); device->card = fw_card_get(card); device->node = fw_node_get(node); diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 50a071f1c945..777fba48d2d3 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -238,11 +238,11 @@ static ssize_t host_control_on_shutdown_store(struct device *dev, } /** - * smi_request: generate SMI request + * dcdbas_smi_request: generate SMI request * * Called with smi_data_lock. */ -static int smi_request(struct smi_cmd *smi_cmd) +int dcdbas_smi_request(struct smi_cmd *smi_cmd) { cpumask_t old_mask; int ret = 0; @@ -309,14 +309,14 @@ static ssize_t smi_request_store(struct device *dev, switch (val) { case 2: /* Raw SMI */ - ret = smi_request(smi_cmd); + ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; break; case 1: /* Calling Interface SMI */ smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); - ret = smi_request(smi_cmd); + ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; break; @@ -333,6 +333,7 @@ out: mutex_unlock(&smi_data_lock); return ret; } +EXPORT_SYMBOL(dcdbas_smi_request); /** * host_control_smi: generate host control SMI diff --git a/drivers/firmware/dcdbas.h b/drivers/firmware/dcdbas.h index 87bc3417de27..ca3cb0a54ab6 100644 --- a/drivers/firmware/dcdbas.h +++ b/drivers/firmware/dcdbas.h @@ -101,5 +101,7 @@ struct apm_cmd { } __attribute__ ((packed)) parameters; } __attribute__ ((packed)); +int dcdbas_smi_request(struct smi_cmd *smi_cmd); + #endif /* _DCDBAS_H_ */ diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 3bf8ee120d42..261b9aa3f248 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -56,9 +56,9 @@ struct memmap_attribute { ssize_t (*show)(struct firmware_map_entry *entry, char *buf); }; -struct memmap_attribute memmap_start_attr = __ATTR_RO(start); -struct memmap_attribute memmap_end_attr = __ATTR_RO(end); -struct memmap_attribute memmap_type_attr = __ATTR_RO(type); +static struct memmap_attribute memmap_start_attr = __ATTR_RO(start); +static struct memmap_attribute memmap_end_attr = __ATTR_RO(end); +static struct memmap_attribute memmap_type_attr = __ATTR_RO(type); /* * These are default attributes that are added for every memmap entry. diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 53c87254be4c..5b2cbb778162 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -210,7 +210,7 @@ static int drm_mode_object_get(struct drm_device *dev, int ret; WARN(!mutex_is_locked(&dev->mode_config.mutex), - "%s called w/o mode_config lock\n", __FUNCTION__); + "%s called w/o mode_config lock\n", __func__); again: if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { DRM_ERROR("Ran out memory getting a mode number\n"); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 14afc23a0e24..1384d6686555 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1445,7 +1445,7 @@ static void i915_write_fence_reg(struct drm_i915_fence_reg *reg) if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || (obj_priv->gtt_offset & (obj->size - 1))) { - WARN(1, "%s: object not 1M or size aligned\n", __FUNCTION__); + WARN(1, "%s: object not 1M or size aligned\n", __func__); return; } @@ -1478,7 +1478,7 @@ static void i830_write_fence_reg(struct drm_i915_fence_reg *reg) if ((obj_priv->gtt_offset & ~I915_FENCE_START_MASK) || (obj_priv->gtt_offset & (obj->size - 1))) { - WARN(1, "%s: object not 1M or size aligned\n", __FUNCTION__); + WARN(1, "%s: object not 1M or size aligned\n", __func__); return; } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 03cb494af1c5..f0a0f72238ab 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -102,7 +102,7 @@ static void hid_reset(struct work_struct *work) struct usbhid_device *usbhid = container_of(work, struct usbhid_device, reset_work); struct hid_device *hid = usbhid->hid; - int rc_lock, rc = 0; + int rc = 0; if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { dev_dbg(&usbhid->intf->dev, "clear halt\n"); @@ -113,11 +113,10 @@ static void hid_reset(struct work_struct *work) else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { dev_dbg(&usbhid->intf->dev, "resetting device\n"); - rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); - if (rc_lock >= 0) { + rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); + if (rc == 0) { rc = usb_reset_device(hid_to_usb_dev(hid)); - if (rc_lock) - usb_unlock_device(hid_to_usb_dev(hid)); + usb_unlock_device(hid_to_usb_dev(hid)); } clear_bit(HID_RESET_PENDING, &usbhid->iofl); } diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8fd124eff646..19cb1ace3eb4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o -obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o +obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c new file mode 100644 index 000000000000..bf8d40580577 --- /dev/null +++ b/drivers/hwmon/hp_accel.c @@ -0,0 +1,265 @@ +/* + * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008 Pavel Machek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/kthread.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/poll.h> +#include <linux/freezer.h> +#include <linux/version.h> +#include <linux/uaccess.h> +#include <acpi/acpi_drivers.h> +#include <asm/atomic.h> +#include "lis3lv02d.h" + +#define DRIVER_NAME "lis3lv02d" +#define ACPI_MDPS_CLASS "accelerometer" + + +/* For automatic insertion of the module */ +static struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + + +/** + * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. + * @handle: the handle of the device + * + * Returns AE_OK on success. + */ +acpi_status lis3lv02d_acpi_init(acpi_handle handle) +{ + return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @handle: the handle of the device + * @reg: the register to read + * @ret: result of the operation + * + * Returns AE_OK on success. + */ +acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) +{ + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); + *ret = lret; + return status; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @handle: the handle of the device + * @reg: the register to write to + * @val: the value to write + * + * Returns AE_OK on success. + */ +acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) +{ + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + return acpi_evaluate_integer(handle, "ALWR", &args, &ret); +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + adev.ac = *((struct axis_conversion *)dmi->driver_data); + printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; +static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; +static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; +static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + { NULL, } +/* Laptop models without axis info (yet): + * "NC651xx" "HP Compaq 651" + * "NC671xx" "HP Compaq 671" + * "NC6910" "HP Compaq 6910" + * HP Compaq 8710x Notebook PC / Mobile Workstation + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + + +static int lis3lv02d_add(struct acpi_device *device) +{ + u8 val; + + if (!device) + return -EINVAL; + + adev.device = device; + adev.init = lis3lv02d_acpi_init; + adev.read = lis3lv02d_acpi_read; + adev.write = lis3lv02d_acpi_write; + strcpy(acpi_device_name(device), DRIVER_NAME); + strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); + device->driver_data = &adev; + + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); + if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { + printk(KERN_ERR DRIVER_NAME + ": Accelerometer chip not LIS3LV02D{L,Q}\n"); + } + + /* If possible use a "standard" axes order */ + if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " + "using default axes configuration\n"); + adev.ac = lis3lv02d_axis_normal; + } + + return lis3lv02d_init_device(&adev); +} + +static int lis3lv02d_remove(struct acpi_device *device, int type) +{ + if (!device) + return -EINVAL; + + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(device->handle); + + return lis3lv02d_remove_fs(); +} + + +#ifdef CONFIG_PM +static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(device->handle); + return 0; +} + +static int lis3lv02d_resume(struct acpi_device *device) +{ + /* put back the device in the right state (ACPI might turn it on) */ + mutex_lock(&adev.lock); + if (adev.usage > 0) + lis3lv02d_poweron(device->handle); + else + lis3lv02d_poweroff(device->handle); + mutex_unlock(&adev.lock); + return 0; +} +#else +#define lis3lv02d_suspend NULL +#define lis3lv02d_resume NULL +#endif + +/* For the HP MDPS aka 3D Driveguard */ +static struct acpi_driver lis3lv02d_driver = { + .name = DRIVER_NAME, + .class = ACPI_MDPS_CLASS, + .ids = lis3lv02d_device_ids, + .ops = { + .add = lis3lv02d_add, + .remove = lis3lv02d_remove, + .suspend = lis3lv02d_suspend, + .resume = lis3lv02d_resume, + } +}; + +static int __init lis3lv02d_init_module(void) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + + ret = acpi_bus_register_driver(&lis3lv02d_driver); + if (ret < 0) + return ret; + + printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); + + return 0; +} + +static void __exit lis3lv02d_exit_module(void) +{ + acpi_bus_unregister_driver(&lis3lv02d_driver); +} + +MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS"); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); +MODULE_LICENSE("GPL"); + +module_init(lis3lv02d_init_module); +module_exit(lis3lv02d_exit_module); + diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index c002144c76bc..219d2d0d5a62 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -3,6 +3,7 @@ * * Copyright (C) 2007-2008 Yan Burman * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008 Pavel Machek * * 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 @@ -39,7 +40,6 @@ #include "lis3lv02d.h" #define DRIVER_NAME "lis3lv02d" -#define ACPI_MDPS_CLASS "accelerometer" /* joystick device poll interval in milliseconds */ #define MDPS_POLL_INTERVAL 50 @@ -55,100 +55,17 @@ /* Maximum value our axis may get for the input device (signed 12 bits) */ #define MDPS_MAX_VAL 2048 -struct axis_conversion { - s8 x; - s8 y; - s8 z; -}; - -struct acpi_lis3lv02d { - struct acpi_device *device; /* The ACPI device */ - struct input_dev *idev; /* input device */ - struct task_struct *kthread; /* kthread for input */ - struct mutex lock; - struct platform_device *pdev; /* platform device */ - atomic_t count; /* interrupt count after last read */ - int xcalib; /* calibrated null value for x */ - int ycalib; /* calibrated null value for y */ - int zcalib; /* calibrated null value for z */ - unsigned char is_on; /* whether the device is on or off */ - unsigned char usage; /* usage counter */ - struct axis_conversion ac; /* hw -> logical axis */ -}; +struct acpi_lis3lv02d adev; +EXPORT_SYMBOL_GPL(adev); -static struct acpi_lis3lv02d adev; - -static int lis3lv02d_remove_fs(void); static int lis3lv02d_add_fs(struct acpi_device *device); -/* For automatic insertion of the module */ -static struct acpi_device_id lis3lv02d_device_ids[] = { - {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); - -/** - * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. - * @handle: the handle of the device - * - * Returns AE_OK on success. - */ -static inline acpi_status lis3lv02d_acpi_init(acpi_handle handle) -{ - return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); -} - -/** - * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @handle: the handle of the device - * @reg: the register to read - * @ret: result of the operation - * - * Returns AE_OK on success. - */ -static acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) -{ - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long lret; - acpi_status status; - - arg0.integer.value = reg; - - status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); - *ret = lret; - return status; -} - -/** - * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @handle: the handle of the device - * @reg: the register to write to - * @val: the value to write - * - * Returns AE_OK on success. - */ -static acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) -{ - unsigned long long ret; /* Not used when writting */ - union acpi_object in_obj[2]; - struct acpi_object_list args = { 2, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = val; - - return acpi_evaluate_integer(handle, "ALWR", &args, &ret); -} - static s16 lis3lv02d_read_16(acpi_handle handle, int reg) { u8 lo, hi; - lis3lv02d_acpi_read(handle, reg, &lo); - lis3lv02d_acpi_read(handle, reg + 1, &hi); + adev.read(handle, reg, &lo); + adev.read(handle, reg + 1, &hi); /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ return (s16)((hi << 8) | lo); } @@ -190,54 +107,31 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) *z = lis3lv02d_get_axis(adev.ac.z, position); } -static inline void lis3lv02d_poweroff(acpi_handle handle) +void lis3lv02d_poweroff(acpi_handle handle) { adev.is_on = 0; /* disable X,Y,Z axis and power down */ - lis3lv02d_acpi_write(handle, CTRL_REG1, 0x00); + adev.write(handle, CTRL_REG1, 0x00); } +EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); -static void lis3lv02d_poweron(acpi_handle handle) +void lis3lv02d_poweron(acpi_handle handle) { u8 val; adev.is_on = 1; - lis3lv02d_acpi_init(handle); - lis3lv02d_acpi_write(handle, FF_WU_CFG, 0); + adev.init(handle); + adev.write(handle, FF_WU_CFG, 0); /* * BDU: LSB and MSB values are not updated until both have been read. * So the value read will always be correct. * IEN: Interrupt for free-fall and DD, not for data-ready. */ - lis3lv02d_acpi_read(handle, CTRL_REG2, &val); + adev.read(handle, CTRL_REG2, &val); val |= CTRL2_BDU | CTRL2_IEN; - lis3lv02d_acpi_write(handle, CTRL_REG2, val); -} - -#ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) -{ - /* make sure the device is off when we suspend */ - lis3lv02d_poweroff(device->handle); - return 0; -} - -static int lis3lv02d_resume(struct acpi_device *device) -{ - /* put back the device in the right state (ACPI might turn it on) */ - mutex_lock(&adev.lock); - if (adev.usage > 0) - lis3lv02d_poweron(device->handle); - else - lis3lv02d_poweroff(device->handle); - mutex_unlock(&adev.lock); - return 0; + adev.write(handle, CTRL_REG2, val); } -#else -#define lis3lv02d_suspend NULL -#define lis3lv02d_resume NULL -#endif - +EXPORT_SYMBOL_GPL(lis3lv02d_poweron); /* * To be called before starting to use the device. It makes sure that the @@ -315,7 +209,7 @@ static inline void lis3lv02d_calibrate_joystick(void) lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); } -static int lis3lv02d_joystick_enable(void) +int lis3lv02d_joystick_enable(void) { int err; @@ -349,8 +243,9 @@ static int lis3lv02d_joystick_enable(void) return err; } +EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); -static void lis3lv02d_joystick_disable(void) +void lis3lv02d_joystick_disable(void) { if (!adev.idev) return; @@ -358,13 +253,13 @@ static void lis3lv02d_joystick_disable(void) input_unregister_device(adev.idev); adev.idev = NULL; } - +EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); /* * Initialise the accelerometer and the various subsystems. * Should be rather independant of the bus system. */ -static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) +int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) { mutex_init(&dev->lock); lis3lv02d_add_fs(dev->device); @@ -376,93 +271,7 @@ static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) lis3lv02d_decrease_use(dev); return 0; } - -static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) -{ - adev.ac = *((struct axis_conversion *)dmi->driver_data); - printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); - - return 1; -} - -/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). - * If the value is negative, the opposite of the hw value is used. */ -static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; -static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; - -#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} -static struct dmi_system_id lis3lv02d_dmi_ids[] = { - /* product names are truncated to match all kinds of a same model */ - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), - { NULL, } -/* Laptop models without axis info (yet): - * "NC651xx" "HP Compaq 651" - * "NC671xx" "HP Compaq 671" - * "NC6910" "HP Compaq 6910" - * HP Compaq 8710x Notebook PC / Mobile Workstation - * "NC2400" "HP Compaq nc2400" - * "NX74x0" "HP Compaq nx74" - * "NX6325" "HP Compaq nx6325" - * "NC4400" "HP Compaq nc4400" - */ -}; - -static int lis3lv02d_add(struct acpi_device *device) -{ - u8 val; - - if (!device) - return -EINVAL; - - adev.device = device; - strcpy(acpi_device_name(device), DRIVER_NAME); - strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); - device->driver_data = &adev; - - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); - if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { - printk(KERN_ERR DRIVER_NAME - ": Accelerometer chip not LIS3LV02D{L,Q}\n"); - } - - /* If possible use a "standard" axes order */ - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { - printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " - "using default axes configuration\n"); - adev.ac = lis3lv02d_axis_normal; - } - - return lis3lv02d_init_device(&adev); -} - -static int lis3lv02d_remove(struct acpi_device *device, int type) -{ - if (!device) - return -EINVAL; - - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(device->handle); - - return lis3lv02d_remove_fs(); -} - +EXPORT_SYMBOL_GPL(lis3lv02d_init_device); /* Sysfs stuff */ static ssize_t lis3lv02d_position_show(struct device *dev, @@ -501,7 +310,7 @@ static ssize_t lis3lv02d_rate_show(struct device *dev, int val; lis3lv02d_increase_use(&adev); - lis3lv02d_acpi_read(adev.device->handle, CTRL_REG1, &ctrl); + adev.read(adev.device->handle, CTRL_REG1, &ctrl); lis3lv02d_decrease_use(&adev); val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); @@ -523,6 +332,7 @@ static struct attribute_group lis3lv02d_attribute_group = { .attrs = lis3lv02d_attributes }; + static int lis3lv02d_add_fs(struct acpi_device *device) { adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); @@ -532,50 +342,15 @@ static int lis3lv02d_add_fs(struct acpi_device *device) return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); } -static int lis3lv02d_remove_fs(void) +int lis3lv02d_remove_fs(void) { sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); platform_device_unregister(adev.pdev); return 0; } - -/* For the HP MDPS aka 3D Driveguard */ -static struct acpi_driver lis3lv02d_driver = { - .name = DRIVER_NAME, - .class = ACPI_MDPS_CLASS, - .ids = lis3lv02d_device_ids, - .ops = { - .add = lis3lv02d_add, - .remove = lis3lv02d_remove, - .suspend = lis3lv02d_suspend, - .resume = lis3lv02d_resume, - } -}; - -static int __init lis3lv02d_init_module(void) -{ - int ret; - - if (acpi_disabled) - return -ENODEV; - - ret = acpi_bus_register_driver(&lis3lv02d_driver); - if (ret < 0) - return ret; - - printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); - - return 0; -} - -static void __exit lis3lv02d_exit_module(void) -{ - acpi_bus_unregister_driver(&lis3lv02d_driver); -} +EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); MODULE_AUTHOR("Yan Burman and Eric Piel"); MODULE_LICENSE("GPL"); -module_init(lis3lv02d_init_module); -module_exit(lis3lv02d_exit_module); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 330cfc60e948..223f1c0763bb 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -23,7 +23,7 @@ * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to * be connected via SPI. There exists also several similar chips (such as LIS302DL or * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. - * They can also be connected via I²C. + * They can also be connected via I²C. */ #define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ @@ -147,3 +147,36 @@ enum lis3lv02d_dd_src { DD_SRC_IA = 0x40, }; +struct axis_conversion { + s8 x; + s8 y; + s8 z; +}; + +struct acpi_lis3lv02d { + struct acpi_device *device; /* The ACPI device */ + acpi_status (*init) (acpi_handle handle); + acpi_status (*write) (acpi_handle handle, int reg, u8 val); + acpi_status (*read) (acpi_handle handle, int reg, u8 *ret); + + struct input_dev *idev; /* input device */ + struct task_struct *kthread; /* kthread for input */ + struct mutex lock; + struct platform_device *pdev; /* platform device */ + atomic_t count; /* interrupt count after last read */ + int xcalib; /* calibrated null value for x */ + int ycalib; /* calibrated null value for y */ + int zcalib; /* calibrated null value for z */ + unsigned char is_on; /* whether the device is on or off */ + unsigned char usage; /* usage counter */ + struct axis_conversion ac; /* hw -> logical axis */ +}; + +int lis3lv02d_init_device(struct acpi_lis3lv02d *dev); +int lis3lv02d_joystick_enable(void); +void lis3lv02d_joystick_disable(void); +void lis3lv02d_poweroff(acpi_handle handle); +void lis3lv02d_poweron(acpi_handle handle); +int lis3lv02d_remove_fs(void); + +extern struct acpi_lis3lv02d adev; diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 864ac561fdbb..59c3d23f5bdc 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -114,18 +114,6 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. -config ISP1301_OMAP - tristate "Philips ISP1301 with OMAP OTG" - depends on ARCH_OMAP_OTG - help - If you say yes here you get support for the Philips ISP1301 - USB-On-The-Go transceiver working with the OMAP OTG controller. - The ISP1301 is used in products including H2 and H3 development - boards for Texas Instruments OMAP processors. - - This driver can also be built as a module. If so, the module - will be called isp1301_omap. - config SENSORS_MAX6875 tristate "Maxim MAX6875 Power supply supervisor" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 8b95f41a5001..83accaaf8164 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o -obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index 2f9e941968d6..d8f295bdad76 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -18,12 +18,6 @@ #include <linux/dmi.h> #include <acpi/acpi_bus.h> -#include <acpi/acnames.h> -#include <acpi/acnamesp.h> -#include <acpi/acparser.h> -#include <acpi/acexcep.h> -#include <acpi/acmacros.h> -#include <acpi/actypes.h> #define REGS_PER_GTF 7 struct taskfile_array { diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c index a074bfd5f825..1a919df809f8 100644 --- a/drivers/ieee1394/eth1394.c +++ b/drivers/ieee1394/eth1394.c @@ -245,12 +245,6 @@ static int ether1394_stop(struct net_device *dev) return 0; } -/* Return statistics to the caller */ -static struct net_device_stats *ether1394_stats(struct net_device *dev) -{ - return &(((struct eth1394_priv *)netdev_priv(dev))->stats); -} - /* FIXME: What to do if we timeout? I think a host reset is probably in order, * so that's what we do. Should we increment the stat counters too? */ static void ether1394_tx_timeout(struct net_device *dev) @@ -516,16 +510,19 @@ static const struct header_ops ether1394_header_ops = { .parse = ether1394_header_parse, }; +static const struct net_device_ops ether1394_netdev_ops = { + .ndo_open = ether1394_open, + .ndo_stop = ether1394_stop, + .ndo_start_xmit = ether1394_tx, + .ndo_tx_timeout = ether1394_tx_timeout, + .ndo_change_mtu = ether1394_change_mtu, +}; + static void ether1394_init_dev(struct net_device *dev) { - dev->open = ether1394_open; - dev->stop = ether1394_stop; - dev->hard_start_xmit = ether1394_tx; - dev->get_stats = ether1394_stats; - dev->tx_timeout = ether1394_tx_timeout; - dev->change_mtu = ether1394_change_mtu; dev->header_ops = ðer1394_header_ops; + dev->netdev_ops = ðer1394_netdev_ops; SET_ETHTOOL_OPS(dev, ðtool_ops); @@ -1075,7 +1072,7 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid " "lookup failure: " NODE_BUS_FMT, NODE_BUS_ARGS(priv->host, srcid)); - priv->stats.rx_dropped++; + dev->stats.rx_dropped++; return -1; } ud = node->ud; @@ -1098,7 +1095,7 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, skb = dev_alloc_skb(len + dev->hard_header_len + 15); if (unlikely(!skb)) { ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); - priv->stats.rx_dropped++; + dev->stats.rx_dropped++; return -1; } skb_reserve(skb, (dev->hard_header_len + 15) & ~15); @@ -1217,15 +1214,15 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, spin_lock_irqsave(&priv->lock, flags); if (!skb->protocol) { - priv->stats.rx_errors++; - priv->stats.rx_dropped++; + dev->stats.rx_errors++; + dev->stats.rx_dropped++; dev_kfree_skb_any(skb); } else if (netif_rx(skb) == NET_RX_DROP) { - priv->stats.rx_errors++; - priv->stats.rx_dropped++; + dev->stats.rx_errors++; + dev->stats.rx_dropped++; } else { - priv->stats.rx_packets++; - priv->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; } spin_unlock_irqrestore(&priv->lock, flags); @@ -1234,8 +1231,6 @@ bad_proto: if (netif_queue_stopped(dev)) netif_wake_queue(dev); - dev->last_rx = jiffies; - return 0; } @@ -1509,17 +1504,18 @@ static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len) static void ether1394_dg_complete(struct packet_task *ptask, int fail) { struct sk_buff *skb = ptask->skb; - struct eth1394_priv *priv = netdev_priv(skb->dev); + struct net_device *dev = skb->dev; + struct eth1394_priv *priv = netdev_priv(dev); unsigned long flags; /* Statistics */ spin_lock_irqsave(&priv->lock, flags); if (fail) { - priv->stats.tx_dropped++; - priv->stats.tx_errors++; + dev->stats.tx_dropped++; + dev->stats.tx_errors++; } else { - priv->stats.tx_bytes += skb->len; - priv->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; } spin_unlock_irqrestore(&priv->lock, flags); @@ -1696,8 +1692,8 @@ fail: dev_kfree_skb(skb); spin_lock_irqsave(&priv->lock, flags); - priv->stats.tx_dropped++; - priv->stats.tx_errors++; + dev->stats.tx_dropped++; + dev->stats.tx_errors++; spin_unlock_irqrestore(&priv->lock, flags); /* diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h index e1b5ea80f623..d53bac47b86f 100644 --- a/drivers/ieee1394/eth1394.h +++ b/drivers/ieee1394/eth1394.h @@ -54,7 +54,6 @@ enum eth1394_bc_states { ETHER1394_BC_ERROR, /* Private structure for our ethernet driver */ struct eth1394_priv { - struct net_device_stats stats; /* Device stats */ struct hpsb_host *host; /* The card for this dev */ u16 bc_maxpayload; /* Max broadcast payload */ u8 bc_sspd; /* Max broadcast speed */ diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index a812db243477..6ba57e91d7ab 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -2705,7 +2705,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) sizeof(struct ietf_mpa_frame)); - /* notify OF layer that accept event was successfull */ + /* notify OF layer that accept event was successful */ cm_id->add_ref(cm_id); cm_event.event = IW_CM_EVENT_ESTABLISHED; diff --git a/drivers/input/mouse/pxa930_trkball.c b/drivers/input/mouse/pxa930_trkball.c index a0f45c4fc198..d297accf9a7f 100644 --- a/drivers/input/mouse/pxa930_trkball.c +++ b/drivers/input/mouse/pxa930_trkball.c @@ -186,7 +186,7 @@ static int __devinit pxa930_trkball_probe(struct platform_device *pdev) error = request_irq(irq, pxa930_trkball_interrupt, IRQF_DISABLED, pdev->name, trkball); if (error) { - dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + dev_err(&pdev->dev, "failed to request irq: %d\n", error); goto failed_free_io; } @@ -227,7 +227,7 @@ failed_free_io: iounmap(trkball->mmio_base); failed: kfree(trkball); - return ret; + return error; } static int __devexit pxa930_trkball_remove(struct platform_device *pdev) diff --git a/drivers/isdn/hardware/eicon/debuglib.h b/drivers/isdn/hardware/eicon/debuglib.h index 016410cf2273..8ea587783e14 100644 --- a/drivers/isdn/hardware/eicon/debuglib.h +++ b/drivers/isdn/hardware/eicon/debuglib.h @@ -235,7 +235,7 @@ typedef void ( * DbgOld) (unsigned short, char *, va_list) ; typedef void ( * DbgEv) (unsigned short, unsigned long, va_list) ; typedef void ( * DbgIrq) (unsigned short, int, char *, va_list) ; typedef struct _DbgHandle_ -{ char Registered ; /* driver successfull registered */ +{ char Registered ; /* driver successfully registered */ #define DBG_HANDLE_REG_NEW 0x01 /* this (new) structure */ #define DBG_HANDLE_REG_OLD 0x7f /* old structure (see below) */ char Version; /* version of this structure */ diff --git a/drivers/isdn/hardware/eicon/os_4bri.c b/drivers/isdn/hardware/eicon/os_4bri.c index 7b4ec3f60dbf..c964b8d91ada 100644 --- a/drivers/isdn/hardware/eicon/os_4bri.c +++ b/drivers/isdn/hardware/eicon/os_4bri.c @@ -997,7 +997,7 @@ diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter, diva_xdi_display_adapter_features(IoAdapter->ANum); for (i = 0; i < IoAdapter->tasks; i++) { - DBG_LOG(("A(%d) %s adapter successfull started", + DBG_LOG(("A(%d) %s adapter successfully started", IoAdapter->QuadroList->QuadroAdapter[i]->ANum, (IoAdapter->tasks == 1) ? "BRI 2.0" : "4BRI")) diva_xdi_didd_register_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum); diff --git a/drivers/isdn/hardware/eicon/os_bri.c b/drivers/isdn/hardware/eicon/os_bri.c index f31bba5b16ff..08f01993f46b 100644 --- a/drivers/isdn/hardware/eicon/os_bri.c +++ b/drivers/isdn/hardware/eicon/os_bri.c @@ -736,7 +736,7 @@ diva_bri_start_adapter(PISDN_ADAPTER IoAdapter, IoAdapter->Properties.Features = (word) features; diva_xdi_display_adapter_features(IoAdapter->ANum); - DBG_LOG(("A(%d) BRI adapter successfull started", IoAdapter->ANum)) + DBG_LOG(("A(%d) BRI adapter successfully started", IoAdapter->ANum)) /* Register with DIDD */ diff --git a/drivers/isdn/hardware/eicon/os_pri.c b/drivers/isdn/hardware/eicon/os_pri.c index 903356547b79..5d65405c75f4 100644 --- a/drivers/isdn/hardware/eicon/os_pri.c +++ b/drivers/isdn/hardware/eicon/os_pri.c @@ -513,7 +513,7 @@ diva_pri_start_adapter(PISDN_ADAPTER IoAdapter, diva_xdi_display_adapter_features(IoAdapter->ANum); - DBG_LOG(("A(%d) PRI adapter successfull started", IoAdapter->ANum)) + DBG_LOG(("A(%d) PRI adapter successfully started", IoAdapter->ANum)) /* Register with DIDD */ diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index 14793480c453..fd112ae252cf 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -23,3 +23,10 @@ config MISDN_HFCMULTI * HFC-8S (8 S/T interfaces on one chip) * HFC-E1 (E1 interface for 2Mbit ISDN) +config MISDN_HFCUSB + tristate "Support for HFC-S USB based TAs" + depends on USB + help + Enable support for USB ISDN TAs with Cologne Chip AG's + HFC-S USB ISDN Controller + diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile index 1e7ca5332ad7..b0403526bbba 100644 --- a/drivers/isdn/hardware/mISDN/Makefile +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o +obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 7bbf7300593d..663b77f578be 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -2,10 +2,6 @@ * see notice in hfc_multi.c */ -extern void ztdummy_extern_interrupt(void); -extern void ztdummy_register_interrupt(void); -extern int ztdummy_unregister_interrupt(void); - #define DEBUG_HFCMULTI_FIFO 0x00010000 #define DEBUG_HFCMULTI_CRC 0x00020000 #define DEBUG_HFCMULTI_INIT 0x00040000 @@ -13,6 +9,7 @@ extern int ztdummy_unregister_interrupt(void); #define DEBUG_HFCMULTI_MODE 0x00100000 #define DEBUG_HFCMULTI_MSG 0x00200000 #define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_FILL 0x00800000 #define DEBUG_HFCMULTI_SYNC 0x01000000 #define DEBUG_HFCMULTI_DTMF 0x02000000 #define DEBUG_HFCMULTI_LOCK 0x80000000 @@ -170,6 +167,8 @@ struct hfc_multi { u_long chip; /* chip configuration */ int masterclk; /* port that provides master clock -1=off */ + unsigned char silence;/* silence byte */ + unsigned char silence_data[128];/* silence block */ int dtmf; /* flag that dtmf is currently in process */ int Flen; /* F-buffer size */ int Zlen; /* Z-buffer size (must be int for calculation)*/ @@ -198,6 +197,9 @@ struct hfc_multi { spinlock_t lock; /* the lock */ + struct mISDNclock *iclock; /* isdn clock support */ + int iclock_on; + /* * the channel index is counted from 0, regardless where the channel * is located on the hfc-channel. diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h index 5783d22a18fe..3132ddc99fcd 100644 --- a/drivers/isdn/hardware/mISDN/hfc_pci.h +++ b/drivers/isdn/hardware/mISDN/hfc_pci.h @@ -26,7 +26,7 @@ * change mask and threshold simultaneously */ #define HFCPCI_BTRANS_THRESHOLD 128 -#define HFCPCI_BTRANS_MAX 256 +#define HFCPCI_FILLEMPTY 64 #define HFCPCI_BTRANS_THRESMASK 0x00 /* defines for PCI config */ diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index c63e2f49da8a..97f4708b3879 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -133,6 +133,12 @@ * Give the value of the clock control register (A_ST_CLK_DLY) * of the S/T interfaces in TE mode. * This register is needed for the TBR3 certification, so don't change it. + * + * clock: + * NOTE: only one clock value must be given once + * Selects interface with clock source for mISDN and applications. + * Set to card number starting with 1. Set to -1 to disable. + * By default, the first card is used as clock source. */ /* @@ -140,7 +146,7 @@ * #define HFC_REGISTER_DEBUG */ -static const char *hfcmulti_revision = "2.02"; +#define HFC_MULTI_VERSION "2.03" #include <linux/module.h> #include <linux/pci.h> @@ -165,10 +171,6 @@ static LIST_HEAD(HFClist); static spinlock_t HFClock; /* global hfc list lock */ static void ph_state_change(struct dchannel *); -static void (*hfc_interrupt)(void); -static void (*register_interrupt)(void); -static int (*unregister_interrupt)(void); -static int interrupt_registered; static struct hfc_multi *syncmaster; static int plxsd_master; /* if we have a master card (yet) */ @@ -184,7 +186,6 @@ static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; #define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ #define CLKDEL_NT 0x6c /* CLKDEL in NT mode (0x60 MUST be included!) */ -static u_char silence = 0xff; /* silence by LAW */ #define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ #define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ @@ -195,12 +196,13 @@ static u_char silence = 0xff; /* silence by LAW */ */ static uint type[MAX_CARDS]; -static uint pcm[MAX_CARDS]; -static uint dslot[MAX_CARDS]; +static int pcm[MAX_CARDS]; +static int dslot[MAX_CARDS]; static uint iomode[MAX_CARDS]; static uint port[MAX_PORTS]; static uint debug; static uint poll; +static int clock; static uint timer; static uint clockdelay_te = CLKDEL_TE; static uint clockdelay_nt = CLKDEL_NT; @@ -209,14 +211,16 @@ static int HFC_cnt, Port_cnt, PCM_cnt = 99; MODULE_AUTHOR("Andreas Eversberg"); MODULE_LICENSE("GPL"); +MODULE_VERSION(HFC_MULTI_VERSION); module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(clock, int, S_IRUGO | S_IWUSR); module_param(timer, uint, S_IRUGO | S_IWUSR); module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); +module_param_array(dslot, int, NULL, S_IRUGO | S_IWUSR); module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); @@ -1419,19 +1423,6 @@ controller_fail: HFC_outb(hc, R_TI_WD, poll_timer); hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; - /* - * set up 125us interrupt, only if function pointer is available - * and module parameter timer is set - */ - if (timer && hfc_interrupt && register_interrupt) { - /* only one chip should use this interrupt */ - timer = 0; - interrupt_registered = 1; - hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK; - /* deactivate other interrupts in ztdummy */ - register_interrupt(); - } - /* set E1 state machine IRQ */ if (hc->type == 1) hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; @@ -1991,6 +1982,17 @@ next_frame: return; /* no data */ } + /* "fill fifo if empty" feature */ + if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags) + && !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) { + if (debug & DEBUG_HFCMULTI_FILL) + printk(KERN_DEBUG "%s: buffer empty, so we have " + "underrun\n", __func__); + /* fill buffer, to prevent future underrun */ + hc->write_fifo(hc, hc->silence_data, poll >> 1); + Zspace -= (poll >> 1); + } + /* if audio data and connected slot */ if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) && slot_tx >= 0) { @@ -2027,7 +2029,6 @@ next_frame: __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, temp ? "HDLC":"TRANS"); - /* Have to prep the audio data */ hc->write_fifo(hc, d, ii - i); *idxp = ii; @@ -2066,7 +2067,7 @@ next_frame: * no more data at all. this prevents sending an undefined value. */ if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); } @@ -2583,7 +2584,6 @@ hfcmulti_interrupt(int intno, void *dev_id) static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, iq5 = 0, iq6 = 0, iqcnt = 0; #endif - static int count; struct hfc_multi *hc = dev_id; struct dchannel *dch; u_char r_irq_statech, status, r_irq_misc, r_irq_oview; @@ -2637,6 +2637,7 @@ hfcmulti_interrupt(int intno, void *dev_id) iqcnt = 0; } #endif + if (!r_irq_statech && !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | V_MISC_IRQSTA | V_FR_IRQSTA))) { @@ -2657,6 +2658,7 @@ hfcmulti_interrupt(int intno, void *dev_id) if (status & V_MISC_IRQSTA) { /* misc IRQ */ r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */ if (r_irq_misc & V_STA_IRQ) { if (hc->type == 1) { /* state machine */ @@ -2691,23 +2693,20 @@ hfcmulti_interrupt(int intno, void *dev_id) plxsd_checksync(hc, 0); } } - if (r_irq_misc & V_TI_IRQ) + if (r_irq_misc & V_TI_IRQ) { + if (hc->iclock_on) + mISDN_clock_update(hc->iclock, poll, NULL); handle_timer_irq(hc); + } if (r_irq_misc & V_DTMF_IRQ) { - /* -> DTMF IRQ */ hfcmulti_dtmf(hc); } - /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable */ if (r_irq_misc & V_IRQ_PROC) { - /* IRQ every 125us */ - count++; - /* generate 1kHz signal */ - if (count == 8) { - if (hfc_interrupt) - hfc_interrupt(); - count = 0; - } + static int irq_proc_cnt; + if (!irq_proc_cnt++) + printk(KERN_WARNING "%s: got V_IRQ_PROC -" + " this should not happen\n", __func__); } } @@ -2954,7 +2953,7 @@ mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + ((ch % 4) * 4)) << 1); HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); @@ -2969,7 +2968,7 @@ mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); /* enable RX fifo */ HFC_outb(hc, R_FIFO, (ch<<1)|1); HFC_wait(hc); @@ -3461,7 +3460,7 @@ channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) switch (cq->op) { case MISDN_CTRL_GETOP: cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP - | MISDN_CTRL_RX_OFF; + | MISDN_CTRL_RX_OFF | MISDN_CTRL_FILL_EMPTY; break; case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ hc->chan[bch->slot].rx_off = !!cq->p1; @@ -3476,6 +3475,12 @@ channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", __func__, bch->nr, hc->chan[bch->slot].rx_off); break; + case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */ + test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d " + "off=%d)\n", __func__, bch->nr, !!cq->p1); + break; case MISDN_CTRL_HW_FEATURES: /* fill features structure */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_FEATURE request\n", @@ -3992,6 +3997,7 @@ open_bchannel(struct hfc_multi *hc, struct dchannel *dch, } if (test_and_set_bit(FLG_OPEN, &bch->Flags)) return -EBUSY; /* b-channel can be only open once */ + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); bch->ch.protocol = rq->protocol; hc->chan[ch].rx_off = 0; rq->ch = &bch->ch; @@ -4081,6 +4087,15 @@ hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) return err; } +static int +clockctl(void *priv, int enable) +{ + struct hfc_multi *hc = priv; + + hc->iclock_on = enable; + return 0; +} + /* * initialize the card */ @@ -4495,10 +4510,14 @@ release_card(struct hfc_multi *hc) printk(KERN_WARNING "%s: release card (%d) entered\n", __func__, hc->id); + /* unregister clock source */ + if (hc->iclock) + mISDN_unregister_clock(hc->iclock); + + /* disable irq */ spin_lock_irqsave(&hc->lock, flags); disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); - udelay(1000); /* dimm leds */ @@ -4699,7 +4718,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) } else hc->chan[hc->dslot].jitter = 2; /* default */ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); - ret = mISDN_register_device(&dch->dev, name); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; hc->created[0] = 1; @@ -4807,9 +4826,9 @@ init_multi_port(struct hfc_multi *hc, int pt) test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i + 2].cfg); } - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d", + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", hc->type, HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, name); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; hc->created[pt] = 1; @@ -4828,6 +4847,7 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) struct hfc_multi *hc; u_long flags; u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + int i; if (HFC_cnt >= MAX_CARDS) { printk(KERN_ERR "too many cards (max=%d).\n", @@ -4861,11 +4881,11 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) hc->id = HFC_cnt; hc->pcm = pcm[HFC_cnt]; hc->io_mode = iomode[HFC_cnt]; - if (dslot[HFC_cnt] < 0) { + if (dslot[HFC_cnt] < 0 && hc->type == 1) { hc->dslot = 0; printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " "31 B-channels\n"); - } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) { + } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32 && hc->type == 1) { hc->dslot = dslot[HFC_cnt]; printk(KERN_INFO "HFC-E1 card has alternating D-channel on " "time slot %d\n", dslot[HFC_cnt]); @@ -4876,9 +4896,17 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) hc->masterclk = -1; if (type[HFC_cnt] & 0x100) { test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); - silence = 0xff; /* ulaw silence */ + hc->silence = 0xff; /* ulaw silence */ } else - silence = 0x2a; /* alaw silence */ + hc->silence = 0x2a; /* alaw silence */ + if ((poll >> 1) > sizeof(hc->silence_data)) { + printk(KERN_ERR "HFCMULTI error: silence_data too small, " + "please fix\n"); + return -EINVAL; + } + for (i = 0; i < (poll >> 1); i++) + hc->silence_data[i] = hc->silence; + if (!(type[HFC_cnt] & 0x200)) test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); @@ -4945,9 +4973,7 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) switch (m->dip_type) { case DIP_4S: /* - * get DIP Setting for beroNet 1S/2S/4S cards - * check if Port Jumper config matches - * module param 'protocol' + * Get DIP setting for beroNet 1S/2S/4S cards * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + * GPI 19/23 (R_GPI_IN2)) */ @@ -4966,9 +4992,8 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) break; case DIP_8S: /* - * get DIP Setting for beroNet 8S0+ cards - * - * enable PCI auxbridge function + * Get DIP Setting for beroNet 8S0+ cards + * Enable PCI auxbridge function */ HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); /* prepare access to auxport */ @@ -5003,6 +5028,10 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) list_add_tail(&hc->list, &HFClist); spin_unlock_irqrestore(&HFClock, flags); + /* use as clock source */ + if (clock == HFC_cnt + 1) + hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); + /* initialize hardware */ ret_err = init_card(hc); if (ret_err) { @@ -5137,8 +5166,7 @@ static struct pci_device_id hfmultipci_ids[] __devinitdata = { { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, - /* IOB8ST Recording */ + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, @@ -5188,18 +5216,16 @@ hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct hm_map *m = (struct hm_map *)ent->driver_data; int ret; - if (m == NULL) { - if (ent->vendor == PCI_VENDOR_ID_CCD) - if (ent->device == PCI_DEVICE_ID_CCD_HFC4S || - ent->device == PCI_DEVICE_ID_CCD_HFC8S || - ent->device == PCI_DEVICE_ID_CCD_HFCE1) - printk(KERN_ERR - "unknown HFC multiport controller " - "(vendor:%x device:%x subvendor:%x " - "subdevice:%x) Please contact the " - "driver maintainer for support.\n", - ent->vendor, ent->device, - ent->subvendor, ent->subdevice); + if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && ( + ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1)) { + printk(KERN_ERR + "Unknown HFC multiport controller (vendor:%x device:%x " + "subvendor:%x subdevice:%x)\n", ent->vendor, ent->device, + ent->subvendor, ent->subdevice); + printk(KERN_ERR + "Please contact the driver maintainer for support.\n"); return -ENODEV; } ret = hfcmulti_init(pdev, ent); @@ -5222,22 +5248,9 @@ HFCmulti_cleanup(void) { struct hfc_multi *card, *next; - /* unload interrupt function symbol */ - if (hfc_interrupt) - symbol_put(ztdummy_extern_interrupt); - if (register_interrupt) - symbol_put(ztdummy_register_interrupt); - if (unregister_interrupt) { - if (interrupt_registered) { - interrupt_registered = 0; - unregister_interrupt(); - } - symbol_put(ztdummy_unregister_interrupt); - } - + /* get rid of all devices of this driver */ list_for_each_entry_safe(card, next, &HFClist, list) release_card(card); - /* get rid of all devices of this driver */ pci_unregister_driver(&hfcmultipci_driver); } @@ -5246,8 +5259,10 @@ HFCmulti_init(void) { int err; + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION); + #ifdef IRQ_DEBUG - printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__); + printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__); #endif spin_lock_init(&HFClock); @@ -5256,22 +5271,11 @@ HFCmulti_init(void) if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: init entered\n", __func__); - hfc_interrupt = symbol_get(ztdummy_extern_interrupt); - register_interrupt = symbol_get(ztdummy_register_interrupt); - unregister_interrupt = symbol_get(ztdummy_unregister_interrupt); - printk(KERN_INFO "mISDN: HFC-multi driver %s\n", - hfcmulti_revision); - switch (poll) { case 0: poll_timer = 6; poll = 128; break; - /* - * wenn dieses break nochmal verschwindet, - * gibt es heisse ohren :-) - * "without the break you will get hot ears ???" - */ case 8: poll_timer = 2; break; @@ -5298,20 +5302,12 @@ HFCmulti_init(void) } + if (!clock) + clock = 1; + err = pci_register_driver(&hfcmultipci_driver); if (err < 0) { printk(KERN_ERR "error registering pci driver: %x\n", err); - if (hfc_interrupt) - symbol_put(ztdummy_extern_interrupt); - if (register_interrupt) - symbol_put(ztdummy_register_interrupt); - if (unregister_interrupt) { - if (interrupt_registered) { - interrupt_registered = 0; - unregister_interrupt(); - } - symbol_put(ztdummy_unregister_interrupt); - } return err; } return 0; diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index cd8302af40eb..917bf41a293b 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -23,6 +23,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * Module options: + * + * debug: + * NOTE: only one poll value must be given for all cards + * See hfc_pci.h for debug flags. + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * A value of 128 will use controller's interrupt. Other values will + * use kernel timer, because the controller will not allow lower values + * than 128. + * Also note that the value depends on the kernel timer frequency. + * If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible. + * If the kernel uses 100 Hz, steps of 80 samples are possible. + * If the kernel uses 300 Hz, steps of about 26 samples are possible. + * */ #include <linux/module.h> @@ -34,16 +53,16 @@ static const char *hfcpci_revision = "2.0"; -#define MAX_CARDS 8 static int HFC_cnt; static uint debug; +static uint poll, tics; +struct timer_list hfc_tl; +u32 hfc_jiffies; MODULE_AUTHOR("Karsten Keil"); MODULE_LICENSE("GPL"); module_param(debug, uint, 0); - -static LIST_HEAD(HFClist); -static DEFINE_RWLOCK(HFClock); +module_param(poll, uint, S_IRUGO | S_IWUSR); enum { HFC_CCD_2BD0, @@ -114,7 +133,6 @@ struct hfcPCI_hw { struct hfc_pci { - struct list_head list; u_char subtype; u_char chanlimit; u_char initdone; @@ -520,9 +538,9 @@ receive_dmsg(struct hfc_pci *hc) } /* - * check for transparent receive data and read max one threshold size if avail + * check for transparent receive data and read max one 'poll' size if avail */ -static int +static void hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) { __le16 *z1r, *z2r; @@ -534,17 +552,19 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); if (!fcnt) - return 0; /* no data avail */ + return; /* no data avail */ if (fcnt <= 0) fcnt += B_FIFO_SIZE; /* bytes actually buffered */ - if (fcnt > HFCPCI_BTRANS_THRESHOLD) - fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ - new_z2 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + if (fcnt > MAX_DATA_SIZE) { /* flush, if oversized */ + *z2r = cpu_to_le16(new_z2); /* new position */ + return; + } + bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC); if (bch->rx_skb) { ptr = skb_put(bch->rx_skb, fcnt); @@ -569,7 +589,6 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) printk(KERN_WARNING "HFCPCI: receive out of memory\n"); *z2r = cpu_to_le16(new_z2); /* new position */ - return 1; } /* @@ -580,12 +599,11 @@ main_rec_hfcpci(struct bchannel *bch) { struct hfc_pci *hc = bch->hw; int rcnt, real_fifo; - int receive, count = 5; + int receive = 0, count = 5; struct bzfifo *bz; u_char *bdata; struct zt *zp; - if ((bch->nr & 2) && (!hc->hw.bswapped)) { bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; @@ -625,9 +643,10 @@ Begin: receive = 1; else receive = 0; - } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) - receive = hfcpci_empty_fifo_trans(bch, bz, bdata); - else + } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + hfcpci_empty_fifo_trans(bch, bz, bdata); + return; + } else receive = 0; if (count && receive) goto Begin; @@ -751,11 +770,41 @@ hfcpci_fill_fifo(struct bchannel *bch) /* fcnt contains available bytes in fifo */ fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send (bytes in fifo) */ + + /* "fill fifo if empty" feature */ + if (test_bit(FLG_FILLEMPTY, &bch->Flags) && !fcnt) { + /* printk(KERN_DEBUG "%s: buffer empty, so we have " + "underrun\n", __func__); */ + /* fill buffer, to prevent future underrun */ + count = HFCPCI_FILLEMPTY; + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fillempty " + "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + fcnt += count; + if (maxlen > count) + maxlen = count; /* limit size */ + memset(dst, 0x2a, maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + memset(dst, 0x2a, count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + } + next_t_frame: count = bch->tx_skb->len - bch->tx_idx; - /* maximum fill shall be HFCPCI_BTRANS_MAX */ - if (count > HFCPCI_BTRANS_MAX - fcnt) - count = HFCPCI_BTRANS_MAX - fcnt; + /* maximum fill shall be poll*2 */ + if (count > (poll << 1) - fcnt) + count = (poll << 1) - fcnt; if (count <= 0) return; /* data is suitable for fifo */ @@ -1135,37 +1184,37 @@ hfcpci_int(int intno, void *dev_id) val &= ~0x80; Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); } - if (val & 0x08) { + if (val & 0x08) { /* B1 rx */ bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); if (bch) main_rec_hfcpci(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); } - if (val & 0x10) { + if (val & 0x10) { /* B2 rx */ bch = Sel_BCS(hc, 2); if (bch) main_rec_hfcpci(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); } - if (val & 0x01) { + if (val & 0x01) { /* B1 tx */ bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); if (bch) tx_birq(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); } - if (val & 0x02) { + if (val & 0x02) { /* B2 tx */ bch = Sel_BCS(hc, 2); if (bch) tx_birq(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); } - if (val & 0x20) + if (val & 0x20) /* D rx */ receive_dmsg(hc); - if (val & 0x04) { /* dframe transmitted */ + if (val & 0x04) { /* D tx */ if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) del_timer(&hc->dch.timer); tx_dirq(&hc->dch); @@ -1283,14 +1332,16 @@ mode_hfcpci(struct bchannel *bch, int bc, int protocol) } if (fifo2 & 2) { hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; - hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + - HFCPCI_INTS_B2REC); + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + + HFCPCI_INTS_B2REC); hc->hw.ctmt |= 2; hc->hw.conn &= ~0x18; } else { hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; - hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + - HFCPCI_INTS_B1REC); + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + + HFCPCI_INTS_B1REC); hc->hw.ctmt |= 1; hc->hw.conn &= ~0x03; } @@ -1398,7 +1449,8 @@ set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) if (chan & 2) { hc->hw.sctrl_r |= SCTRL_B2_ENA; hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; - hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; hc->hw.ctmt |= 2; hc->hw.conn &= ~0x18; #ifdef REVERSE_BITORDER @@ -1407,7 +1459,8 @@ set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) } else { hc->hw.sctrl_r |= SCTRL_B1_ENA; hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; - hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; hc->hw.ctmt |= 1; hc->hw.conn &= ~0x03; #ifdef REVERSE_BITORDER @@ -1481,11 +1534,17 @@ deactivate_bchannel(struct bchannel *bch) static int channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) { - int ret = 0; + int ret = 0; switch (cq->op) { case MISDN_CTRL_GETOP: - cq->op = 0; + cq->op = MISDN_CTRL_FILL_EMPTY; + break; + case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */ + test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d " + "off=%d)\n", __func__, bch->nr, !!cq->p1); break; default: printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op); @@ -1859,6 +1918,10 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, hc->dch.dev.id, __builtin_return_address(0)); if (rq->protocol == ISDN_P_NONE) return -EINVAL; + if (rq->adr.channel == 1) { + /* TODO: E-Channel */ + return -EINVAL; + } if (!hc->initdone) { if (rq->protocol == ISDN_P_TE_S0) { err = create_l1(&hc->dch, hfc_l1callback); @@ -1874,6 +1937,11 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, if (rq->protocol != ch->protocol) { if (hc->hw.protocol == ISDN_P_TE_S0) l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } hc->hw.protocol = rq->protocol; ch->protocol = rq->protocol; hfcpci_setmode(hc); @@ -1903,6 +1971,7 @@ open_bchannel(struct hfc_pci *hc, struct channel_req *rq) bch = &hc->bch[rq->adr.channel - 1]; if (test_and_set_bit(FLG_OPEN, &bch->Flags)) return -EBUSY; /* b-channel can be only open once */ + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); bch->ch.protocol = rq->protocol; rq->ch = &bch->ch; /* TODO: E-channel */ if (!try_module_get(THIS_MODULE)) @@ -1928,7 +1997,8 @@ hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) switch (cmd) { case OPEN_CHANNEL: rq = arg; - if (rq->adr.channel == 0) + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) err = open_dchannel(hc, ch, rq); else err = open_bchannel(hc, rq); @@ -2027,7 +2097,6 @@ release_card(struct hfc_pci *hc) { mISDN_freebchannel(&hc->bch[1]); mISDN_freebchannel(&hc->bch[0]); mISDN_freedchannel(&hc->dch); - list_del(&hc->list); pci_set_drvdata(hc->pdev, NULL); kfree(hc); } @@ -2037,12 +2106,8 @@ setup_card(struct hfc_pci *card) { int err = -EINVAL; u_int i; - u_long flags; char name[MISDN_MAX_IDLEN]; - if (HFC_cnt >= MAX_CARDS) - return -EINVAL; /* maybe better value */ - card->dch.debug = debug; spin_lock_init(&card->lock); mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); @@ -2068,13 +2133,10 @@ setup_card(struct hfc_pci *card) if (err) goto error; snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); - err = mISDN_register_device(&card->dch.dev, name); + err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); if (err) goto error; HFC_cnt++; - write_lock_irqsave(&HFClock, flags); - list_add_tail(&card->list, &HFClist); - write_unlock_irqrestore(&HFClock, flags); printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); return 0; error: @@ -2210,15 +2272,12 @@ static void __devexit hfc_remove_pci(struct pci_dev *pdev) { struct hfc_pci *card = pci_get_drvdata(pdev); - u_long flags; - if (card) { - write_lock_irqsave(&HFClock, flags); + if (card) release_card(card); - write_unlock_irqrestore(&HFClock, flags); - } else + else if (debug) - printk(KERN_WARNING "%s: drvdata allready removed\n", + printk(KERN_WARNING "%s: drvdata already removed\n", __func__); } @@ -2230,25 +2289,97 @@ static struct pci_driver hfc_driver = { .id_table = hfc_ids, }; +static int +_hfcpci_softirq(struct device *dev, void *arg) +{ + struct hfc_pci *hc = dev_get_drvdata(dev); + struct bchannel *bch; + if (hc == NULL) + return 0; + + if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { + spin_lock(&hc->lock); + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); + if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + spin_unlock(&hc->lock); + } + return 0; +} + +static void +hfcpci_softirq(void *arg) +{ + (void) driver_for_each_device(&hfc_driver.driver, NULL, arg, + _hfcpci_softirq); + + /* if next event would be in the past ... */ + if ((s32)(hfc_jiffies + tics - jiffies) <= 0) + hfc_jiffies = jiffies + 1; + else + hfc_jiffies += tics; + hfc_tl.expires = hfc_jiffies; + add_timer(&hfc_tl); +} + static int __init HFC_init(void) { int err; + if (!poll) + poll = HFCPCI_BTRANS_THRESHOLD; + + if (poll != HFCPCI_BTRANS_THRESHOLD) { + tics = (poll * HZ) / 8000; + if (tics < 1) + tics = 1; + poll = (tics * 8000) / HZ; + if (poll > 256 || poll < 8) { + printk(KERN_ERR "%s: Wrong poll value %d not in range " + "of 8..256.\n", __func__, poll); + err = -EINVAL; + return err; + } + } + if (poll != HFCPCI_BTRANS_THRESHOLD) { + printk(KERN_INFO "%s: Using alternative poll value of %d\n", + __func__, poll); + hfc_tl.function = (void *)hfcpci_softirq; + hfc_tl.data = 0; + init_timer(&hfc_tl); + hfc_tl.expires = jiffies + tics; + hfc_jiffies = hfc_tl.expires; + add_timer(&hfc_tl); + } else + tics = 0; /* indicate the use of controller's timer */ + err = pci_register_driver(&hfc_driver); + if (err) { + if (timer_pending(&hfc_tl)) + del_timer(&hfc_tl); + } + return err; } static void __exit HFC_cleanup(void) { - struct hfc_pci *card, *next; + if (timer_pending(&hfc_tl)) + del_timer(&hfc_tl); - list_for_each_entry_safe(card, next, &HFClist, list) { - release_card(card); - } pci_unregister_driver(&hfc_driver); } module_init(HFC_init); module_exit(HFC_cleanup); + +MODULE_DEVICE_TABLE(pci, hfc_ids); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c new file mode 100644 index 000000000000..ba6925fbf38a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -0,0 +1,2196 @@ +/* hfcsusb.c + * mISDN driver for Colognechip HFC-S USB chip + * + * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) + * Copyright 2008 by Martin Bachem (info@bachem-it.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, 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. + * + * + * module params + * debug=<n>, default=0, with n=0xHHHHGGGG + * H - l1 driver flags described in hfcsusb.h + * G - common mISDN debug flags described at mISDNhw.h + * + * poll=<n>, default 128 + * n : burst size of PH_DATA_IND at transparent rx data + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/usb.h> +#include <linux/mISDNhw.h> +#include "hfcsusb.h" + +const char *hfcsusb_rev = "Revision: 0.3.3 (socket), 2008-11-05"; + +static unsigned int debug; +static int poll = DEFAULT_TRANSP_BURST_SZ; + +static LIST_HEAD(HFClist); +static DEFINE_RWLOCK(HFClock); + + +MODULE_AUTHOR("Martin Bachem"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, int, 0); + +static int hfcsusb_cnt; + +/* some function prototypes */ +static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command); +static void release_hw(struct hfcsusb *hw); +static void reset_hfcsusb(struct hfcsusb *hw); +static void setPortMode(struct hfcsusb *hw); +static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); +static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); +static int hfcsusb_setup_bch(struct bchannel *bch, int protocol); +static void deactivate_bchannel(struct bchannel *bch); +static void hfcsusb_ph_info(struct hfcsusb *hw); + +/* start next background transfer for control channel */ +static void +ctrl_start_transfer(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + if (hw->ctrl_cnt) { + hw->ctrl_urb->pipe = hw->ctrl_out_pipe; + hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write; + hw->ctrl_urb->transfer_buffer = NULL; + hw->ctrl_urb->transfer_buffer_length = 0; + hw->ctrl_write.wIndex = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg); + hw->ctrl_write.wValue = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val); + + usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC); + } +} + +/* + * queue a control transfer request to write HFC-S USB + * chip register using CTRL resuest queue + */ +static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val) +{ + struct ctrl_buf *buf; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n", + hw->name, __func__, reg, val); + + spin_lock(&hw->ctrl_lock); + if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE) + return 1; + buf = &hw->ctrl_buff[hw->ctrl_in_idx]; + buf->hfcs_reg = reg; + buf->reg_val = val; + if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_in_idx = 0; + if (++hw->ctrl_cnt == 1) + ctrl_start_transfer(hw); + spin_unlock(&hw->ctrl_lock); + + return 0; +} + +/* control completion routine handling background control cmds */ +static void +ctrl_complete(struct urb *urb) +{ + struct hfcsusb *hw = (struct hfcsusb *) urb->context; + struct ctrl_buf *buf; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + urb->dev = hw->dev; + if (hw->ctrl_cnt) { + buf = &hw->ctrl_buff[hw->ctrl_out_idx]; + hw->ctrl_cnt--; /* decrement actual count */ + if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_out_idx = 0; /* pointer wrap */ + + ctrl_start_transfer(hw); /* start next transfer */ + } +} + +/* handle LED bits */ +static void +set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on) +{ + if (set_on) { + if (led_bits < 0) + hw->led_state &= ~abs(led_bits); + else + hw->led_state |= led_bits; + } else { + if (led_bits < 0) + hw->led_state |= abs(led_bits); + else + hw->led_state &= ~led_bits; + } +} + +/* handle LED requests */ +static void +handle_led(struct hfcsusb *hw, int event) +{ + struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *) + hfcsusb_idtab[hw->vend_idx].driver_info; + __u8 tmpled; + + if (driver_info->led_scheme == LED_OFF) + return; + tmpled = hw->led_state; + + switch (event) { + case LED_POWER_ON: + set_led_bit(hw, driver_info->led_bits[0], 1); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_POWER_OFF: + set_led_bit(hw, driver_info->led_bits[0], 0); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_S0_ON: + set_led_bit(hw, driver_info->led_bits[1], 1); + break; + case LED_S0_OFF: + set_led_bit(hw, driver_info->led_bits[1], 0); + break; + case LED_B1_ON: + set_led_bit(hw, driver_info->led_bits[2], 1); + break; + case LED_B1_OFF: + set_led_bit(hw, driver_info->led_bits[2], 0); + break; + case LED_B2_ON: + set_led_bit(hw, driver_info->led_bits[3], 1); + break; + case LED_B2_OFF: + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + } + + if (hw->led_state != tmpled) { + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n", + hw->name, __func__, + HFCUSB_P_DATA, hw->led_state); + + write_reg(hw, HFCUSB_P_DATA, hw->led_state); + } +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfcsusb *hw = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u_long flags; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hw->lock, flags); + ret = bchannel_senddata(bch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n", + hw->name, __func__, ret); + if (ret > 0) { + /* + * other l1 drivers don't send early confirms on + * transp data, but hfcsusb does because tx_next + * skb is needed in tx_iso_complete() + */ + queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); + ret = 0; + } + return ret; + case PH_ACTIVATE_REQ: + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hfcsusb_start_endpoint(hw, bch->nr); + ret = hfcsusb_setup_bch(bch, ch->protocol); + } else + ret = 0; + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * send full D/B channel status information + * as MPH_INFORMATION_IND + */ +static void +hfcsusb_ph_info(struct hfcsusb *hw) +{ + struct ph_info *phi; + struct dchannel *dch = &hw->dch; + int i; + + phi = kzalloc(sizeof(struct ph_info) + + dch->dev.nrbchan * sizeof(struct ph_info_ch), GFP_ATOMIC); + phi->dch.ch.protocol = hw->protocol; + phi->dch.ch.Flags = dch->Flags; + phi->dch.state = dch->state; + phi->dch.num_bch = dch->dev.nrbchan; + for (i = 0; i < dch->dev.nrbchan; i++) { + phi->bch[i].protocol = hw->bch[i].ch.protocol; + phi->bch[i].Flags = hw->bch[i].Flags; + } + _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, + sizeof(struct ph_info_dch) + dch->dev.nrbchan * + sizeof(struct ph_info_ch), phi, GFP_ATOMIC); +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcusb_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct hfcsusb *hw = dch->hw; + int ret = -EINVAL; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n", + hw->name, __func__); + + spin_lock_irqsave(&hw->lock, flags); + ret = dchannel_senddata(dch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (ret > 0) { + ret = 0; + queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); + } + break; + + case PH_ACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n", + hw->name, __func__, + (hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE"); + + if (hw->protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + _queue_data(&dch->dev.D, + PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } else { + hfcsusb_ph_command(hw, + HFC_L1_ACTIVATE_NT); + test_and_set_bit(FLG_L2_ACTIVATED, + &dch->Flags); + } + } else { + hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); + ret = l1_event(dch->l1, hh->prim); + } + break; + + case PH_DEACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n", + hw->name, __func__); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + + if (hw->protocol == ISDN_P_NT_S0) { + hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); + spin_lock_irqsave(&hw->lock, flags); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + } else + ret = l1_event(dch->l1, hh->prim); + break; + case MPH_INFORMATION_REQ: + hfcsusb_ph_info(hw); + ret = 0; + break; + } + + return ret; +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s cmd 0x%x\n", + hw->name, __func__, cmd); + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + case HW_RESET_REQ: + case HW_POWERUP_REQ: + break; + + case HW_DEACT_REQ: + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown cmd %x\n", + hw->name, __func__, cmd); + return -1; + } + hfcsusb_ph_info(hw); + return 0; +} + +static int +open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", + hw->name, __func__, hw->dch.dev.id, rq->adr.channel, + __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags); + test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags); + hfcsusb_start_endpoint(hw, HFC_CHAN_D); + + /* E-Channel logging */ + if (rq->adr.channel == 1) { + if (hw->fifos[HFCUSB_PCM_RX].pipe) { + hfcsusb_start_endpoint(hw, HFC_CHAN_E); + set_bit(FLG_ACTIVE, &hw->ech.Flags); + _queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } else + return -EINVAL; + } + + if (!hw->initdone) { + hw->protocol = rq->protocol; + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hw->dch, hfc_l1callback); + if (err) + return err; + } + setPortMode(hw); + ch->protocol = rq->protocol; + hw->initdone = 1; + } else { + if (rq->protocol != ch->protocol) + return -EPROTONOSUPPORT; + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7))) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s: cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +open_bchannel(struct hfcsusb *hw, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s B%i\n", + hw->name, __func__, rq->adr.channel); + + bch = &hw->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + + /* start USB endpoint for bchannel */ + if (rq->adr.channel == 1) + hfcsusb_start_endpoint(hw, HFC_CHAN_B1); + else + hfcsusb_start_endpoint(hw, HFC_CHAN_B2); + + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s:cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n", + hw->name, __func__, (cq->op), (cq->channel)); + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT; + break; + default: + printk(KERN_WARNING "%s: %s: unknown Op %x\n", + hw->name, __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfcsusb *hw = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: cmd:%x %p\n", + hw->name, __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) + err = open_dchannel(hw, ch, rq); + else + err = open_bchannel(hw, rq); + if (!err) + hw->open++; + break; + case CLOSE_CHANNEL: + hw->open--; + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG + "%s: %s: dev(%d) close from %p (open %d)\n", + hw->name, __func__, hw->dch.dev.id, + __builtin_return_address(0), hw->open); + if (!hw->open) { + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + handle_led(hw, LED_POWER_ON); + } + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hw, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown command %x\n", + hw->name, __func__, cmd); + return -EINVAL; + } + return err; +} + +/* + * S0 TE state change event handler + */ +static void +ph_state_te(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_TE_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__, + HFC_TE_LAYER1_STATES[dch->state]); + else + printk(KERN_DEBUG "%s: %s: TE F%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } + if (dch->state == 7) + handle_led(hw, LED_S0_ON); + else + handle_led(hw, LED_S0_OFF); +} + +/* + * S0 NT state change event handler + */ +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_NT_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", + hw->name, __func__, + HFC_NT_LAYER1_STATES[dch->state]); + + else + printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case (1): + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + handle_led(hw, LED_S0_OFF); + break; + + case (2): + if (hw->nt_timer < 0) { + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); + } else { + hw->timers |= NT_ACTIVATION_TIMER; + hw->nt_timer = NT_T1_COUNT; + /* allow G2 -> G3 transition */ + write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); + } + break; + case (3): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + handle_led(hw, LED_S0_ON); + break; + case (4): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + break; + default: + break; + } + hfcsusb_ph_info(hw); +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (hw->protocol == ISDN_P_NT_S0) + ph_state_nt(dch); + else if (hw->protocol == ISDN_P_TE_S0) + ph_state_te(dch); +} + +/* + * disable/enable BChannel for desired protocoll + */ +static int +hfcsusb_setup_bch(struct bchannel *bch, int protocol) +{ + struct hfcsusb *hw = bch->hw; + __u8 conhdlc, sctrl, sctrl_r; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n", + hw->name, __func__, bch->state, protocol, + bch->nr); + + /* setup val for CON_HDLC */ + conhdlc = 0; + if (protocol > ISDN_P_NONE) + conhdlc = 8; /* enable FIFO */ + + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + /* fall trough */ + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; /* already in idle state */ + bch->state = ISDN_P_NONE; + clear_bit(FLG_HDLC, &bch->Flags); + clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_RAW): + conhdlc |= 2; + bch->state = protocol; + set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + set_bit(FLG_HDLC, &bch->Flags); + break; + default: + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: prot not known %x\n", + hw->name, __func__, protocol); + return -ENOPROTOOPT; + } + + if (protocol >= ISDN_P_NONE) { + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + + sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04); + sctrl_r = 0x0; + if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) { + sctrl |= 1; + sctrl_r |= 1; + } + if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) { + sctrl |= 2; + sctrl_r |= 2; + } + write_reg(hw, HFCUSB_SCTRL, sctrl); + write_reg(hw, HFCUSB_SCTRL_R, sctrl_r); + + if (protocol > ISDN_P_NONE) + handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON); + else + handle_led(hw, (bch->nr == 1) ? LED_B1_OFF : + LED_B2_OFF); + } + hfcsusb_ph_info(hw); + return 0; +} + +static void +hfcsusb_ph_command(struct hfcsusb *hw, u_char command) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: %x\n", + hw->name, __func__, command); + + switch (command) { + case HFC_L1_ACTIVATE_TE: + /* force sending sending INFO1 */ + write_reg(hw, HFCUSB_STATES, 0x14); + /* start l1 activation */ + write_reg(hw, HFCUSB_STATES, 0x04); + break; + + case HFC_L1_FORCE_DEACTIVATE_TE: + write_reg(hw, HFCUSB_STATES, 0x10); + write_reg(hw, HFCUSB_STATES, 0x03); + break; + + case HFC_L1_ACTIVATE_NT: + if (hw->dch.state == 3) + _queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + else + write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE | + HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); + break; + + case HFC_L1_DEACTIVATE_NT: + write_reg(hw, HFCUSB_STATES, + HFCUSB_DO_ACTION); + break; + } +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_FILL_EMPTY; + break; + case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */ + test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d " + "off=%d)\n", __func__, bch->nr, !!cq->p1); + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +/* collect data from incoming interrupt or isochron USB data */ +static void +hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, + int finish) +{ + struct hfcsusb *hw = fifo->hw; + struct sk_buff *rx_skb = NULL; + int maxlen = 0; + int fifon = fifo->fifonum; + int i; + int hdlc = 0; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) " + "dch(%p) bch(%p) ech(%p)\n", + hw->name, __func__, fifon, len, + fifo->dch, fifo->bch, fifo->ech); + + if (!len) + return; + + if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) { + printk(KERN_DEBUG "%s: %s: undefined channel\n", + hw->name, __func__); + return; + } + + spin_lock(&hw->lock); + if (fifo->dch) { + rx_skb = fifo->dch->rx_skb; + maxlen = fifo->dch->maxlen; + hdlc = 1; + } + if (fifo->bch) { + rx_skb = fifo->bch->rx_skb; + maxlen = fifo->bch->maxlen; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + } + if (fifo->ech) { + rx_skb = fifo->ech->rx_skb; + maxlen = fifo->ech->maxlen; + hdlc = 1; + } + + if (!rx_skb) { + rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); + if (rx_skb) { + if (fifo->dch) + fifo->dch->rx_skb = rx_skb; + if (fifo->bch) + fifo->bch->rx_skb = rx_skb; + if (fifo->ech) + fifo->ech->rx_skb = rx_skb; + skb_trim(rx_skb, 0); + } else { + printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n", + hw->name, __func__); + spin_unlock(&hw->lock); + return; + } + } + + if (fifo->dch || fifo->ech) { + /* D/E-Channel SKB range check */ + if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { + printk(KERN_DEBUG "%s: %s: sbk mem exceeded " + "for fifo(%d) HFCUSB_D_RX\n", + hw->name, __func__, fifon); + skb_trim(rx_skb, 0); + spin_unlock(&hw->lock); + return; + } + } else if (fifo->bch) { + /* B-Channel SKB range check */ + if ((rx_skb->len + len) >= (MAX_BCH_SIZE + 3)) { + printk(KERN_DEBUG "%s: %s: sbk mem exceeded " + "for fifo(%d) HFCUSB_B_RX\n", + hw->name, __func__, fifon); + skb_trim(rx_skb, 0); + spin_unlock(&hw->lock); + return; + } + } + + memcpy(skb_put(rx_skb, len), data, len); + + if (hdlc) { + /* we have a complete hdlc packet */ + if (finish) { + if ((rx_skb->len > 3) && + (!(rx_skb->data[rx_skb->len - 1]))) { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: fifon(%i)" + " new RX len(%i): ", + hw->name, __func__, fifon, + rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + + /* remove CRC & status */ + skb_trim(rx_skb, rx_skb->len - 3); + + if (fifo->dch) + recv_Dchannel(fifo->dch); + if (fifo->bch) + recv_Bchannel(fifo->bch); + if (fifo->ech) + recv_Echannel(fifo->ech, + &hw->dch); + } else { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG + "%s: CRC or minlen ERROR fifon(%i) " + "RX len(%i): ", + hw->name, fifon, rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + skb_trim(rx_skb, 0); + } + } + } else { + /* deliver transparent data to layer2 */ + if (rx_skb->len >= poll) + recv_Bchannel(fifo->bch); + } + spin_unlock(&hw->lock); +} + +void +fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, + void *buf, int num_packets, int packet_size, int interval, + usb_complete_t complete, void *context) +{ + int k; + + usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, + complete, context); + + urb->number_of_packets = num_packets; + urb->transfer_flags = URB_ISO_ASAP; + urb->actual_length = 0; + urb->interval = interval; + + for (k = 0; k < num_packets; k++) { + urb->iso_frame_desc[k].offset = packet_size * k; + urb->iso_frame_desc[k].length = packet_size; + urb->iso_frame_desc[k].actual_length = 0; + } +} + +/* receive completion routine for all ISO tx fifos */ +static void +rx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, + status, iso_status, i; + __u8 *buf; + static __u8 eof[8]; + __u8 s0_state; + + fifon = fifo->fifonum; + status = urb->status; + + spin_lock(&hw->lock); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock(&hw->lock); + return; + } + spin_unlock(&hw->lock); + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: with -EXDEV " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + s0_state = 0; + if (fifo->active && !status) { + num_isoc_packets = iso_packets[fifon]; + maxlen = fifo->usb_packet_maxlen; + + for (k = 0; k < num_isoc_packets; ++k) { + len = urb->iso_frame_desc[k].actual_length; + offset = urb->iso_frame_desc[k].offset; + buf = context_iso_urb->buffer + offset; + iso_status = urb->iso_frame_desc[k].status; + + if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, iso_status); + } + + /* USB data log for every D ISO in */ + if ((fifon == HFCUSB_D_RX) && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s: %d (%d/%d) len(%d) ", + hw->name, __func__, urb->start_frame, + k, num_isoc_packets-1, + len); + for (i = 0; i < len; i++) + printk("%x ", buf[i]); + printk("\n"); + } + + if (!iso_status) { + if (fifo->last_urblen != maxlen) { + /* + * save fifo fill-level threshold bits + * to use them later in TX ISO URB + * completions + */ + hw->threshold_mask = buf[1]; + + if (fifon == HFCUSB_D_RX) + s0_state = (buf[0] >> 4); + + eof[fifon] = buf[0] & 1; + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + len - 2, (len < maxlen) + ? eof[fifon] : 0); + } else + hfcsusb_rx_frame(fifo, buf, len, + (len < maxlen) ? + eof[fifon] : 0); + fifo->last_urblen = len; + } + } + + /* signal S0 layer1 state change */ + if ((s0_state) && (hw->initdone) && + (s0_state != hw->dch.state)) { + hw->dch.state = s0_state; + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)rx_iso_complete, urb->context); + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error submitting " + "ISO URB: %d\n", + hw->name, __func__, errcode); + } + } else { + if (status && (debug & DBG_HFC_URB_INFO)) + printk(KERN_DEBUG "%s: %s: rx_iso_complete : " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + } +} + +/* receive completion routine for all interrupt rx fifos */ +static void +rx_int_complete(struct urb *urb) +{ + int len, status, i; + __u8 *buf, maxlen, fifon; + struct usb_fifo *fifo = (struct usb_fifo *) urb->context; + struct hfcsusb *hw = fifo->hw; + static __u8 eof[8]; + + spin_lock(&hw->lock); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock(&hw->lock); + return; + } + spin_unlock(&hw->lock); + + fifon = fifo->fifonum; + if ((!fifo->active) || (urb->status)) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG + "%s: %s: RX-Fifo %i is going down (%i)\n", + hw->name, __func__, fifon, urb->status); + + fifo->urb->interval = 0; /* cancel automatic rescheduling */ + return; + } + len = urb->actual_length; + buf = fifo->buffer; + maxlen = fifo->usb_packet_maxlen; + + /* USB data log for every D INT in */ + if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ", + hw->name, __func__, len); + for (i = 0; i < len; i++) + printk("%02x ", buf[i]); + printk("\n"); + } + + if (fifo->last_urblen != fifo->usb_packet_maxlen) { + /* the threshold mask is in the 2nd status byte */ + hw->threshold_mask = buf[1]; + + /* signal S0 layer1 state change */ + if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) { + hw->dch.state = (buf[0] >> 4); + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + eof[fifon] = buf[0] & 1; + /* if we have more than the 2 status bytes -> collect data */ + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + urb->actual_length - 2, + (len < maxlen) ? eof[fifon] : 0); + } else { + hfcsusb_rx_frame(fifo, buf, urb->actual_length, + (len < maxlen) ? eof[fifon] : 0); + } + fifo->last_urblen = urb->actual_length; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error resubmitting USB\n", + hw->name, __func__); + } +} + +/* transmit completion routine for all ISO tx fifos */ +static void +tx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + struct sk_buff *tx_skb; + int k, tx_offset, num_isoc_packets, sink, remain, current_len, + errcode, hdlc, i; + int *tx_idx; + int frame_complete, fifon, status; + __u8 threshbit; + + spin_lock(&hw->lock); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock(&hw->lock); + return; + } + + if (fifo->dch) { + tx_skb = fifo->dch->tx_skb; + tx_idx = &fifo->dch->tx_idx; + hdlc = 1; + } else if (fifo->bch) { + tx_skb = fifo->bch->tx_skb; + tx_idx = &fifo->bch->tx_idx; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + } else { + printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n", + hw->name, __func__); + spin_unlock(&hw->lock); + return; + } + + fifon = fifo->fifonum; + status = urb->status; + + tx_offset = 0; + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG "%s: %s: " + "-EXDEV (%i) fifon (%d)\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + if (fifo->active && !status) { + /* is FifoFull-threshold set for our channel? */ + threshbit = (hw->threshold_mask & (1 << fifon)); + num_isoc_packets = iso_packets[fifon]; + + /* predict dataflow to avoid fifo overflow */ + if (fifon >= HFCUSB_D_TX) + sink = (threshbit) ? SINK_DMIN : SINK_DMAX; + else + sink = (threshbit) ? SINK_MIN : SINK_MAX; + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)tx_iso_complete, urb->context); + memset(context_iso_urb->buffer, 0, + sizeof(context_iso_urb->buffer)); + frame_complete = 0; + + for (k = 0; k < num_isoc_packets; ++k) { + /* analyze tx success of previous ISO packets */ + if (debug & DBG_HFC_URB_ERROR) { + errcode = urb->iso_frame_desc[k].status; + if (errcode) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, errcode); + } + } + + /* Generate next ISO Packets */ + if (tx_skb) + remain = tx_skb->len - *tx_idx; + else + remain = 0; + + if (remain > 0) { + fifo->bit_line -= sink; + current_len = (0 - fifo->bit_line) / 8; + if (current_len > 14) + current_len = 14; + if (current_len < 0) + current_len = 0; + if (remain < current_len) + current_len = remain; + + /* how much bit do we put on the line? */ + fifo->bit_line += current_len * 8; + + context_iso_urb->buffer[tx_offset] = 0; + if (current_len == remain) { + if (hdlc) { + /* signal frame completion */ + context_iso_urb-> + buffer[tx_offset] = 1; + /* add 2 byte flags and 16bit + * CRC at end of ISDN frame */ + fifo->bit_line += 32; + } + frame_complete = 1; + } + + /* copy tx data to iso-urb buffer */ + memcpy(context_iso_urb->buffer + tx_offset + 1, + (tx_skb->data + *tx_idx), current_len); + *tx_idx += current_len; + + urb->iso_frame_desc[k].offset = tx_offset; + urb->iso_frame_desc[k].length = current_len + 1; + + /* USB data log for every D ISO out */ + if ((fifon == HFCUSB_D_RX) && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s (%d/%d) offs(%d) len(%d) ", + hw->name, __func__, + k, num_isoc_packets-1, + urb->iso_frame_desc[k].offset, + urb->iso_frame_desc[k].length); + + for (i = urb->iso_frame_desc[k].offset; + i < (urb->iso_frame_desc[k].offset + + urb->iso_frame_desc[k].length); + i++) + printk("%x ", + context_iso_urb->buffer[i]); + + printk(" skb->len(%i) tx-idx(%d)\n", + tx_skb->len, *tx_idx); + } + + tx_offset += (current_len + 1); + } else { + urb->iso_frame_desc[k].offset = tx_offset++; + urb->iso_frame_desc[k].length = 1; + /* we lower data margin every msec */ + fifo->bit_line -= sink; + if (fifo->bit_line < BITLINE_INF) + fifo->bit_line = BITLINE_INF; + } + + if (frame_complete) { + frame_complete = 0; + + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: " + "fifon(%i) new TX len(%i): ", + hw->name, __func__, + fifon, tx_skb->len); + i = 0; + while (i < tx_skb->len) + printk("%02x ", + tx_skb->data[i++]); + printk("\n"); + } + + dev_kfree_skb(tx_skb); + tx_skb = NULL; + if (fifo->dch && get_next_dframe(fifo->dch)) + tx_skb = fifo->dch->tx_skb; + else if (fifo->bch && + get_next_bframe(fifo->bch)) { + if (test_bit(FLG_TRANSPARENT, + &fifo->bch->Flags)) + confirm_Bsend(fifo->bch); + tx_skb = fifo->bch->tx_skb; + } + } + } + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG + "%s: %s: error submitting ISO URB: %d \n", + hw->name, __func__, errcode); + } + + /* + * abuse DChannel tx iso completion to trigger NT mode state + * changes tx_iso_complete is assumed to be called every + * fifo->intervall (ms) + */ + if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0) + && (hw->timers & NT_ACTIVATION_TIMER)) { + if ((--hw->nt_timer) < 0) + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + } else { + if (status && (debug & DBG_HFC_URB_ERROR)) + printk(KERN_DEBUG "%s: %s: urb->status %s (%i)" + "fifonum=%d\n", + hw->name, __func__, + symbolic(urb_errlist, status), status, fifon); + } + spin_unlock(&hw->lock); +} + +/* + * allocs urbs and start isoc transfer with two pending urbs to avoid + * gaps in the transfer chain + */ +static int +start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, + usb_complete_t complete, int packet_size) +{ + struct hfcsusb *hw = fifo->hw; + int i, k, errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: fifo %i\n", + hw->name, __func__, fifo->fifonum); + + /* allocate Memory for Iso out Urbs */ + for (i = 0; i < 2; i++) { + if (!(fifo->iso[i].urb)) { + fifo->iso[i].urb = + usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); + if (!(fifo->iso[i].urb)) { + printk(KERN_DEBUG + "%s: %s: alloc urb for fifo %i failed", + hw->name, __func__, fifo->fifonum); + } + fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; + fifo->iso[i].indx = i; + + /* Init the first iso */ + if (ISO_BUFFER_SIZE >= + (fifo->usb_packet_maxlen * + num_packets_per_urb)) { + fill_isoc_urb(fifo->iso[i].urb, + fifo->hw->dev, fifo->pipe, + fifo->iso[i].buffer, + num_packets_per_urb, + fifo->usb_packet_maxlen, + fifo->intervall, complete, + &fifo->iso[i]); + memset(fifo->iso[i].buffer, 0, + sizeof(fifo->iso[i].buffer)); + + for (k = 0; k < num_packets_per_urb; k++) { + fifo->iso[i].urb-> + iso_frame_desc[k].offset = + k * packet_size; + fifo->iso[i].urb-> + iso_frame_desc[k].length = + packet_size; + } + } else { + printk(KERN_DEBUG + "%s: %s: ISO Buffer size to small!\n", + hw->name, __func__); + } + } + fifo->bit_line = BITLINE_INF; + + errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL); + fifo->active = (errcode >= 0) ? 1 : 0; + fifo->stop_gracefull = 0; + if (errcode < 0) { + printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n", + hw->name, __func__, + symbolic(urb_errlist, errcode), i); + } + } + return fifo->active; +} + +static void +stop_iso_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int i, timeout; + u_long flags; + + for (i = 0; i < 2; i++) { + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + } + + for (i = 0; i < 2; i++) { + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ/1000)*16); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + } +} + +static void +stop_int_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int timeout; + u_long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ/1000)*3); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); +} + +/* start the interrupt transfer for the given fifo */ +static void +start_int_fifo(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n", + hw->name, __func__, fifo->fifonum); + + if (!fifo->urb) { + fifo->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!fifo->urb) + return; + } + usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe, + fifo->buffer, fifo->usb_packet_maxlen, + (usb_complete_t)rx_int_complete, fifo, fifo->intervall); + fifo->active = 1; + fifo->stop_gracefull = 0; + errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); + if (errcode) { + printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n", + hw->name, __func__, errcode); + fifo->active = 0; + } +} + +static void +setPortMode(struct hfcsusb *hw) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__, + (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT"); + + if (hw->protocol == ISDN_P_TE_S0) { + write_reg(hw, HFCUSB_SCTRL, 0x40); + write_reg(hw, HFCUSB_SCTRL_E, 0x00); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE); + write_reg(hw, HFCUSB_STATES, 3 | 0x10); + write_reg(hw, HFCUSB_STATES, 3); + } else { + write_reg(hw, HFCUSB_SCTRL, 0x44); + write_reg(hw, HFCUSB_SCTRL_E, 0x09); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT); + write_reg(hw, HFCUSB_STATES, 1 | 0x10); + write_reg(hw, HFCUSB_STATES, 1); + } +} + +static void +reset_hfcsusb(struct hfcsusb *hw) +{ + struct usb_fifo *fifo; + int i; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* do Chip reset */ + write_reg(hw, HFCUSB_CIRM, 8); + + /* aux = output, reset off */ + write_reg(hw, HFCUSB_CIRM, 0x10); + + /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ + write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) | + ((hw->packet_size / 8) << 4)); + + /* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */ + write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size); + + /* enable PCM/GCI master mode */ + write_reg(hw, HFCUSB_MST_MODE1, 0); /* set default values */ + write_reg(hw, HFCUSB_MST_MODE0, 1); /* enable master mode */ + + /* init the fifos */ + write_reg(hw, HFCUSB_F_THRES, + (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); + + fifo = hw->fifos; + for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { + write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */ + fifo[i].max_size = + (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; + fifo[i].last_urblen = 0; + + /* set 2 bit for D- & E-channel */ + write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); + + /* enable all fifos */ + if (i == HFCUSB_D_TX) + write_reg(hw, HFCUSB_CON_HDLC, + (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); + else + write_reg(hw, HFCUSB_CON_HDLC, 0x08); + write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */ + } + + write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ + handle_led(hw, LED_POWER_ON); +} + +/* start USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_start_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint already running */ + if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* start rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + start_int_fifo(hw->fifos + channel*2 + 1); + + /* start rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_E: + start_isoc_chain(hw->fifos + HFCUSB_PCM_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + } + } + + /* start tx endpoints using USB ISO OUT method */ + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_TX, + ISOC_PACKETS_D, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + } +} + +/* stop USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint currently running */ + if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + stop_int_gracefull(hw->fifos + channel*2 + 1); + + /* rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) + stop_iso_gracefull(hw->fifos + channel*2 + 1); + + /* tx endpoints using USB ISO OUT method */ + if (channel != HFC_CHAN_E) + stop_iso_gracefull(hw->fifos + channel*2); +} + + +/* Hardware Initialization */ +int +setup_hfcsusb(struct hfcsusb *hw) +{ + int err; + u_char b; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* check the chip id */ + if (read_reg_atomic(hw, HFCUSB_CHIP_ID, &b) != 1) { + printk(KERN_DEBUG "%s: %s: cannot read chip id\n", + hw->name, __func__); + return 1; + } + if (b != HFCUSB_CHIPID) { + printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n", + hw->name, __func__, b); + return 1; + } + + /* first set the needed config, interface and alternate */ + err = usb_set_interface(hw->dev, hw->if_used, hw->alt_used); + + hw->led_state = 0; + + /* init the background machinery for control requests */ + hw->ctrl_read.bRequestType = 0xc0; + hw->ctrl_read.bRequest = 1; + hw->ctrl_read.wLength = cpu_to_le16(1); + hw->ctrl_write.bRequestType = 0x40; + hw->ctrl_write.bRequest = 0; + hw->ctrl_write.wLength = 0; + usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe, + (u_char *)&hw->ctrl_write, NULL, 0, + (usb_complete_t)ctrl_complete, hw); + + reset_hfcsusb(hw); + return 0; +} + +static void +release_hw(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* + * stop all endpoints gracefully + * TODO: mISDN_core should generate CLOSE_CHANNEL + * signals after calling mISDN_unregister_device() + */ + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B1); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B2); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + if (hw->protocol == ISDN_P_TE_S0) + l1_event(hw->dch.l1, CLOSE_CHANNEL); + + mISDN_unregister_device(&hw->dch.dev); + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + + if (hw->ctrl_urb) { + usb_kill_urb(hw->ctrl_urb); + usb_free_urb(hw->ctrl_urb); + hw->ctrl_urb = NULL; + } + + if (hw->intf) + usb_set_intfdata(hw->intf, NULL); + list_del(&hw->list); + kfree(hw); + hw = NULL; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfcsusb *hw = bch->hw; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n", + hw->name, __func__, bch->nr); + + spin_lock_irqsave(&hw->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + clear_bit(FLG_ACTIVE, &bch->Flags); + clear_bit(FLG_TX_BUSY, &bch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); + hfcsusb_setup_bch(bch, ISDN_P_NONE); + hfcsusb_stop_endpoint(hw, bch->nr); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + int ret = -EINVAL; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + + switch (cmd) { + case HW_TESTRX_RAW: + case HW_TESTRX_HDLC: + case HW_TESTRX_OFF: + ret = -EINVAL; + break; + + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + if (test_bit(FLG_ACTIVE, &bch->Flags)) + deactivate_bchannel(bch); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +static int +setup_instance(struct hfcsusb *hw, struct device *parent) +{ + u_long flags; + int err, i; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + spin_lock_init(&hw->ctrl_lock); + spin_lock_init(&hw->lock); + + mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state); + hw->dch.debug = debug & 0xFFFF; + hw->dch.hw = hw; + hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + hw->dch.dev.D.send = hfcusb_l2l1D; + hw->dch.dev.D.ctrl = hfc_dctrl; + + /* enable E-Channel logging */ + if (hw->fifos[HFCUSB_PCM_RX].pipe) + mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL); + + hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + hw->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + hw->bch[i].nr = i + 1; + set_channelmap(i + 1, hw->dch.dev.channelmap); + hw->bch[i].debug = debug; + mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM); + hw->bch[i].hw = hw; + hw->bch[i].ch.send = hfcusb_l2l1B; + hw->bch[i].ch.ctrl = hfc_bctrl; + hw->bch[i].ch.nr = i + 1; + list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels); + } + + hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_D_TX].dch = &hw->dch; + hw->fifos[HFCUSB_D_RX].dch = &hw->dch; + hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech; + hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech; + + err = setup_hfcsusb(hw); + if (err) + goto out; + + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME, + hfcsusb_cnt + 1); + printk(KERN_INFO "%s: registered as '%s'\n", + DRIVER_NAME, hw->name); + + err = mISDN_register_device(&hw->dch.dev, parent, hw->name); + if (err) + goto out; + + hfcsusb_cnt++; + write_lock_irqsave(&HFClock, flags); + list_add_tail(&hw->list, &HFClist); + write_unlock_irqrestore(&HFClock, flags); + return 0; + +out: + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + kfree(hw); + return err; +} + +static int +hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct hfcsusb *hw; + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *iface = intf->cur_altsetting; + struct usb_host_interface *iface_used = NULL; + struct usb_host_endpoint *ep; + struct hfcsusb_vdata *driver_info; + int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx, + probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, + ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size, + alt_used = 0; + + vend_idx = 0xffff; + for (i = 0; hfcsusb_idtab[i].idVendor; i++) { + if ((le16_to_cpu(dev->descriptor.idVendor) + == hfcsusb_idtab[i].idVendor) && + (le16_to_cpu(dev->descriptor.idProduct) + == hfcsusb_idtab[i].idProduct)) { + vend_idx = i; + continue; + } + } + + printk(KERN_DEBUG + "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", + __func__, ifnum, iface->desc.bAlternateSetting, + intf->minor, vend_idx); + + if (vend_idx == 0xffff) { + printk(KERN_WARNING + "%s: no valid vendor found in USB descriptor\n", + __func__); + return -EIO; + } + /* if vendor and product ID is OK, start probing alternate settings */ + alt_idx = 0; + small_match = -1; + + /* default settings */ + iso_packet_size = 16; + packet_size = 64; + + while (alt_idx < intf->num_altsetting) { + iface = intf->altsetting + alt_idx; + probe_alt_setting = iface->desc.bAlternateSetting; + cfg_used = 0; + + while (validconf[cfg_used][0]) { + cfg_found = 1; + vcf = validconf[cfg_used]; + ep = iface->endpoint; + memcpy(cmptbl, vcf, 16 * sizeof(int)); + + /* check for all endpoints in this alternate setting */ + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + ep_addr = ep->desc.bEndpointAddress; + + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (ep_addr & 0x80) + idx++; + attr = ep->desc.bmAttributes; + + if (cmptbl[idx] != EP_NOP) { + if (cmptbl[idx] == EP_NUL) + cfg_found = 0; + if (attr == USB_ENDPOINT_XFER_INT + && cmptbl[idx] == EP_INT) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_BULK + && cmptbl[idx] == EP_BLK) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_ISOC + && cmptbl[idx] == EP_ISO) + cmptbl[idx] = EP_NUL; + + if (attr == USB_ENDPOINT_XFER_INT && + ep->desc.bInterval < vcf[17]) { + cfg_found = 0; + } + } + ep++; + } + + for (i = 0; i < 16; i++) + if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) + cfg_found = 0; + + if (cfg_found) { + if (small_match < cfg_used) { + small_match = cfg_used; + alt_used = probe_alt_setting; + iface_used = iface; + } + } + cfg_used++; + } + alt_idx++; + } /* (alt_idx < intf->num_altsetting) */ + + /* not found a valid USB Ta Endpoint config */ + if (small_match == -1) + return -EIO; + + iface = iface_used; + hw = kzalloc(sizeof(struct hfcsusb), GFP_KERNEL); + if (!hw) + return -ENOMEM; /* got no mem */ + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME); + + ep = iface->endpoint; + vcf = validconf[small_match]; + + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + struct usb_fifo *f; + + ep_addr = ep->desc.bEndpointAddress; + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (ep_addr & 0x80) + idx++; + f = &hw->fifos[idx & 7]; + + /* init Endpoints */ + if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { + ep++; + continue; + } + switch (ep->desc.bmAttributes) { + case USB_ENDPOINT_XFER_INT: + f->pipe = usb_rcvintpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_INT; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_BULK: + if (ep_addr & 0x80) + f->pipe = usb_rcvbulkpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndbulkpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_BULK; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_ISOC: + if (ep_addr & 0x80) + f->pipe = usb_rcvisocpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndisocpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_ISOC; + iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + default: + f->pipe = 0; + } + + if (f->pipe) { + f->fifonum = idx & 7; + f->hw = hw; + f->usb_packet_maxlen = + le16_to_cpu(ep->desc.wMaxPacketSize); + f->intervall = ep->desc.bInterval; + } + ep++; + } + hw->dev = dev; /* save device */ + hw->if_used = ifnum; /* save used interface */ + hw->alt_used = alt_used; /* and alternate config */ + hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ + hw->cfg_used = vcf[16]; /* store used config */ + hw->vend_idx = vend_idx; /* store found vendor */ + hw->packet_size = packet_size; + hw->iso_packet_size = iso_packet_size; + + /* create the control pipes needed for register access */ + hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0); + hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0); + hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + + driver_info = + (struct hfcsusb_vdata *)hfcsusb_idtab[vend_idx].driver_info; + printk(KERN_DEBUG "%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n", + hw->name, __func__, driver_info->vend_name, + conf_str[small_match], ifnum, alt_used); + + if (setup_instance(hw, dev->dev.parent)) + return -EIO; + + hw->intf = intf; + usb_set_intfdata(hw->intf, hw); + return 0; +} + +/* function called when an active device is removed */ +static void +hfcsusb_disconnect(struct usb_interface *intf) +{ + struct hfcsusb *hw = usb_get_intfdata(intf); + struct hfcsusb *next; + int cnt = 0; + + printk(KERN_INFO "%s: device disconnected\n", hw->name); + + handle_led(hw, LED_POWER_OFF); + release_hw(hw); + + list_for_each_entry_safe(hw, next, &HFClist, list) + cnt++; + if (!cnt) + hfcsusb_cnt = 0; + + usb_set_intfdata(intf, NULL); +} + +static struct usb_driver hfcsusb_drv = { + .name = DRIVER_NAME, + .id_table = hfcsusb_idtab, + .probe = hfcsusb_probe, + .disconnect = hfcsusb_disconnect, +}; + +static int __init +hfcsusb_init(void) +{ + printk(KERN_INFO DRIVER_NAME " driver Rev. %s debug(0x%x) poll(%i)\n", + hfcsusb_rev, debug, poll); + + if (usb_register(&hfcsusb_drv)) { + printk(KERN_INFO DRIVER_NAME + ": Unable to register hfcsusb module at usb stack\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit +hfcsusb_cleanup(void) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_INFO DRIVER_NAME ": %s\n", __func__); + + /* unregister Hardware */ + usb_deregister(&hfcsusb_drv); /* release our driver */ +} + +module_init(hfcsusb_init); +module_exit(hfcsusb_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h new file mode 100644 index 000000000000..098486b8e8d2 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.h @@ -0,0 +1,418 @@ +/* + * hfcsusb.h, HFC-S USB mISDN driver + */ + +#ifndef __HFCSUSB_H__ +#define __HFCSUSB_H__ + + +#define DRIVER_NAME "HFC-S_USB" + +#define DBG_HFC_CALL_TRACE 0x00010000 +#define DBG_HFC_FIFO_VERBOSE 0x00020000 +#define DBG_HFC_USB_VERBOSE 0x00100000 +#define DBG_HFC_URB_INFO 0x00200000 +#define DBG_HFC_URB_ERROR 0x00400000 + +#define DEFAULT_TRANSP_BURST_SZ 128 + +#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* hfcsusb Layer1 commands */ +#define HFC_L1_ACTIVATE_TE 1 +#define HFC_L1_ACTIVATE_NT 2 +#define HFC_L1_DEACTIVATE_NT 3 +#define HFC_L1_FORCE_DEACTIVATE_TE 4 + +/* cmd FLAGS in HFCUSB_STATES register */ +#define HFCUSB_LOAD_STATE 0x10 +#define HFCUSB_ACTIVATE 0x20 +#define HFCUSB_DO_ACTION 0x40 +#define HFCUSB_NT_G2_G3 0x80 + +/* timers */ +#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */ +#define NT_T1_COUNT 10 + +#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ + +#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ +#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */ + +#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ +#define HFCUSB_CIRM 0x00 /* cirm register index */ +#define HFCUSB_USB_SIZE 0x07 /* int length register */ +#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ +#define HFCUSB_F_CROSS 0x0b /* bit order register */ +#define HFCUSB_CLKDEL 0x37 /* bit delay register */ +#define HFCUSB_CON_HDLC 0xfa /* channel connect register */ +#define HFCUSB_HDLC_PAR 0xfb +#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ +#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ +#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ +#define HFCUSB_F_THRES 0x0c /* threshold register */ +#define HFCUSB_FIFO 0x0f /* fifo select register */ +#define HFCUSB_F_USAGE 0x1a /* fifo usage register */ +#define HFCUSB_MST_MODE0 0x14 +#define HFCUSB_MST_MODE1 0x15 +#define HFCUSB_P_DATA 0x1f +#define HFCUSB_INC_RES_F 0x0e +#define HFCUSB_B1_SSL 0x20 +#define HFCUSB_B2_SSL 0x21 +#define HFCUSB_B1_RSL 0x24 +#define HFCUSB_B2_RSL 0x25 +#define HFCUSB_STATES 0x30 + + +#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ + +/* fifo registers */ +#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ +#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ +#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ +#define HFCUSB_B2_TX 2 +#define HFCUSB_B2_RX 3 +#define HFCUSB_D_TX 4 +#define HFCUSB_D_RX 5 +#define HFCUSB_PCM_TX 6 +#define HFCUSB_PCM_RX 7 + + +#define USB_INT 0 +#define USB_BULK 1 +#define USB_ISOC 2 + +#define ISOC_PACKETS_D 8 +#define ISOC_PACKETS_B 8 +#define ISO_BUFFER_SIZE 128 + +/* defines how much ISO packets are handled in one URB */ +static int iso_packets[8] = + { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, + ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D +}; + + +/* Fifo flow Control for TX ISO */ +#define SINK_MAX 68 +#define SINK_MIN 48 +#define SINK_DMIN 12 +#define SINK_DMAX 18 +#define BITLINE_INF (-96*8) + +/* HFC-S USB register access by Control-URSs */ +#define write_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \ + 0, 0, HFC_CTRL_TIMEOUT) +#define read_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \ + 1, HFC_CTRL_TIMEOUT) +#define HFC_CTRL_BUFSIZE 64 + +struct ctrl_buf { + __u8 hfcs_reg; /* register number */ + __u8 reg_val; /* value to be written (or read) */ +}; + +/* + * URB error codes + * Used to represent a list of values and their respective symbolic names + */ +struct hfcusb_symbolic_list { + const int num; + const char *name; +}; + +static struct hfcusb_symbolic_list urb_errlist[] = { + {-ENOMEM, "No memory for allocation of internal structures"}, + {-ENOSPC, "The host controller's bandwidth is already consumed"}, + {-ENOENT, "URB was canceled by unlink_urb"}, + {-EXDEV, "ISO transfer only partially completed"}, + {-EAGAIN, "Too match scheduled for the future"}, + {-ENXIO, "URB already queued"}, + {-EFBIG, "Too much ISO frames requested"}, + {-ENOSR, "Buffer error (overrun)"}, + {-EPIPE, "Specified endpoint is stalled (device not responding)"}, + {-EOVERFLOW, "Babble (bad cable?)"}, + {-EPROTO, "Bit-stuff error (bad cable?)"}, + {-EILSEQ, "CRC/Timeout"}, + {-ETIMEDOUT, "NAK (device does not respond)"}, + {-ESHUTDOWN, "Device unplugged"}, + {-1, NULL} +}; + +static inline const char * +symbolic(struct hfcusb_symbolic_list list[], const int num) +{ + int i; + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return list[i].name; + return "<unkown USB Error>"; +} + +/* USB descriptor need to contain one of the following EndPoint combination: */ +#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */ +#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */ +#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */ +#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */ + +#define EP_NUL 1 /* Endpoint at this position not allowed */ +#define EP_NOP 2 /* all type of endpoints allowed at this position */ +#define EP_ISO 3 /* Isochron endpoint mandatory at this position */ +#define EP_BLK 4 /* Bulk endpoint mandatory at this position */ +#define EP_INT 5 /* Interrupt endpoint mandatory at this position */ + +#define HFC_CHAN_B1 0 +#define HFC_CHAN_B2 1 +#define HFC_CHAN_D 2 +#define HFC_CHAN_E 3 + + +/* + * List of all supported enpoints configiration sets, used to find the + * best matching endpoint configuration within a devices' USB descriptor. + * We need at least 3 RX endpoints, and 3 TX endpoints, either + * INT-in and ISO-out, or ISO-in and ISO-out) + * with 4 RX endpoints even E-Channel logging is possible + */ +static int +validconf[][19] = { + /* INT in, ISO out config */ + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_4INT3ISO, 2, 1}, + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_3INT3ISO, 2, 0}, + /* ISO in, ISO out config */ + {EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, + CNF_4ISO3ISO, 2, 1}, + {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, + CNF_3ISO3ISO, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */ +}; + +/* string description of chosen config */ +char *conf_str[] = { + "4 Interrupt IN + 3 Isochron OUT", + "3 Interrupt IN + 3 Isochron OUT", + "4 Isochron IN + 3 Isochron OUT", + "3 Isochron IN + 3 Isochron OUT" +}; + + +#define LED_OFF 0 /* no LED support */ +#define LED_SCHEME1 1 /* LED standard scheme */ +#define LED_SCHEME2 2 /* not used yet... */ + +#define LED_POWER_ON 1 +#define LED_POWER_OFF 2 +#define LED_S0_ON 3 +#define LED_S0_OFF 4 +#define LED_B1_ON 5 +#define LED_B1_OFF 6 +#define LED_B1_DATA 7 +#define LED_B2_ON 8 +#define LED_B2_OFF 9 +#define LED_B2_DATA 10 + +#define LED_NORMAL 0 /* LEDs are normal */ +#define LED_INVERTED 1 /* LEDs are inverted */ + +/* time in ms to perform a Flashing LED when B-Channel has traffic */ +#define LED_TIME 250 + + + +struct hfcsusb; +struct usb_fifo; + +/* structure defining input+output fifos (interrupt/bulk mode) */ +struct iso_urb { + struct urb *urb; + __u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */ + struct usb_fifo *owner_fifo; /* pointer to owner fifo */ + __u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */ +#ifdef ISO_FRAME_START_DEBUG + int start_frames[ISO_FRAME_START_RING_COUNT]; + __u8 iso_frm_strt_pos; /* index in start_frame[] */ +#endif +}; + +struct usb_fifo { + int fifonum; /* fifo index attached to this structure */ + int active; /* fifo is currently active */ + struct hfcsusb *hw; /* pointer to main structure */ + int pipe; /* address of endpoint */ + __u8 usb_packet_maxlen; /* maximum length for usb transfer */ + unsigned int max_size; /* maximum size of receive/send packet */ + __u8 intervall; /* interrupt interval */ + struct urb *urb; /* transfer structure for usb routines */ + __u8 buffer[128]; /* buffer USB INT OUT URB data */ + int bit_line; /* how much bits are in the fifo? */ + + __u8 usb_transfer_mode; /* switched between ISO and INT */ + struct iso_urb iso[2]; /* two urbs to have one always + one pending */ + + struct dchannel *dch; /* link to hfcsusb_t->dch */ + struct bchannel *bch; /* link to hfcsusb_t->bch */ + struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */ + int last_urblen; /* remember length of last packet */ + __u8 stop_gracefull; /* stops URB retransmission */ +}; + +struct hfcsusb { + struct list_head list; + struct dchannel dch; + struct bchannel bch[2]; + struct dchannel ech; /* TODO : wait for struct echannel ;) */ + + struct usb_device *dev; /* our device */ + struct usb_interface *intf; /* used interface */ + int if_used; /* used interface number */ + int alt_used; /* used alternate config */ + int cfg_used; /* configuration index used */ + int vend_idx; /* index in hfcsusb_idtab */ + int packet_size; + int iso_packet_size; + struct usb_fifo fifos[HFCUSB_NUM_FIFOS]; + + /* control pipe background handling */ + struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE]; + int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; + struct urb *ctrl_urb; + struct usb_ctrlrequest ctrl_write; + struct usb_ctrlrequest ctrl_read; + int ctrl_paksize; + int ctrl_in_pipe, ctrl_out_pipe; + spinlock_t ctrl_lock; /* lock for ctrl */ + spinlock_t lock; + + __u8 threshold_mask; + __u8 led_state; + + __u8 protocol; + int nt_timer; + int open; + __u8 timers; + __u8 initdone; + char name[MISDN_MAX_IDLEN]; +}; + +/* private vendor specific data */ +struct hfcsusb_vdata { + __u8 led_scheme; /* led display scheme */ + signed short led_bits[8]; /* array of 8 possible LED bitmask */ + char *vend_name; /* device name */ +}; + + +#define HFC_MAX_TE_LAYER1_STATE 8 +#define HFC_MAX_NT_LAYER1_STATE 4 + +const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { + "TE F0 - Reset", + "TE F1 - Reset", + "TE F2 - Sensing", + "TE F3 - Deactivated", + "TE F4 - Awaiting signal", + "TE F5 - Identifying input", + "TE F6 - Synchronized", + "TE F7 - Activated", + "TE F8 - Lost framing", +}; + +const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { + "NT G0 - Reset", + "NT G1 - Deactive", + "NT G2 - Pending activation", + "NT G3 - Active", + "NT G4 - Pending deactivation", +}; + +/* supported devices */ +static struct usb_device_id hfcsusb_idtab[] = { + { + USB_DEVICE(0x0959, 0x2bd0), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_OFF, {4, 0, 2, 1}, + "ISDN USB TA (Cologne Chip HFC-S USB based)"}), + }, + { + USB_DEVICE(0x0675, 0x1688), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {1, 2, 0, 0}, + "DrayTek miniVigor 128 USB ISDN TA"}), + }, + { + USB_DEVICE(0x07b0, 0x0007), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Billion tiny USB ISDN TA 128"}), + }, + { + USB_DEVICE(0x0742, 0x2008), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Stollmann USB TA"}), + }, + { + USB_DEVICE(0x0742, 0x2009), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Aceex USB ISDN TA"}), + }, + { + USB_DEVICE(0x0742, 0x200A), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "OEM USB ISDN TA"}), + }, + { + USB_DEVICE(0x08e3, 0x0301), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {2, 0, 1, 4}, + "Olitec USB RNIS"}), + }, + { + USB_DEVICE(0x07fa, 0x0846), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Bewan Modem RNIS USB"}), + }, + { + USB_DEVICE(0x07fa, 0x0847), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Djinn Numeris USB"}), + }, + { + USB_DEVICE(0x07b0, 0x0006), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Twister ISDN TA"}), + }, + { + USB_DEVICE(0x071d, 0x1005), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x02, 0, 0x01, 0x04}, + "Eicon DIVA USB 4.0"}), + }, + { + USB_DEVICE(0x0586, 0x0102), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x88, -64, -32, -16}, + "ZyXEL OMNI.NET USB II"}), + }, + { } +}; + +MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); + +#endif /* __HFCSUSB_H__ */ diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c index 7ee5bd9f2bb4..579974cf4c9a 100644 --- a/drivers/isdn/hysdn/hysdn_net.c +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -38,16 +38,12 @@ char *hysdn_net_revision = "$Revision: 1.8.6.4 $"; /* inside the definition. */ /****************************************************************************/ struct net_local { - struct net_device netdev; /* the network device */ - struct net_device_stats stats; - /* additional vars may be added here */ - char dev_name[9]; /* our own device name */ - /* Tx control lock. This protects the transmit buffer ring * state along with the "tx full" state of the driver. This * means all netif_queue flow control actions are protected * by this lock as well. */ + struct net_device *dev; spinlock_t lock; struct sk_buff *skbs[MAX_SKB_BUFFERS]; /* pointers to tx-skbs */ int in_idx, out_idx; /* indexes to buffer ring */ @@ -55,15 +51,6 @@ struct net_local { }; /* net_local */ -/*****************************************************/ -/* Get the current statistics for this card. */ -/* This may be called with the card open or closed ! */ -/*****************************************************/ -static struct net_device_stats * -net_get_stats(struct net_device *dev) -{ - return (&((struct net_local *) dev)->stats); -} /* net_device_stats */ /*********************************************************************/ /* Open/initialize the board. This is called (in the current kernel) */ @@ -182,8 +169,8 @@ hysdn_tx_netack(hysdn_card * card) if (!lp->sk_count) return; /* error condition */ - lp->stats.tx_packets++; - lp->stats.tx_bytes += lp->skbs[lp->out_idx]->len; + lp->dev->stats.tx_packets++; + lp->dev->stats.tx_bytes += lp->skbs[lp->out_idx]->len; dev_kfree_skb(lp->skbs[lp->out_idx++]); /* free skb */ if (lp->out_idx >= MAX_SKB_BUFFERS) @@ -200,29 +187,30 @@ void hysdn_rx_netpkt(hysdn_card * card, unsigned char *buf, unsigned short len) { struct net_local *lp = card->netif; + struct net_device *dev = lp->dev; struct sk_buff *skb; if (!lp) return; /* non existing device */ - lp->stats.rx_bytes += len; + dev->stats.rx_bytes += len; skb = dev_alloc_skb(len); if (skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", - lp->netdev.name); - lp->stats.rx_dropped++; + dev->name); + dev->stats.rx_dropped++; return; } /* copy the data */ memcpy(skb_put(skb, len), buf, len); /* determine the used protocol */ - skb->protocol = eth_type_trans(skb, &lp->netdev); + skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - lp->stats.rx_packets++; /* adjust packet count */ + dev->stats.rx_packets++; /* adjust packet count */ + netif_rx(skb); } /* hysdn_rx_netpkt */ /*****************************************************/ @@ -242,24 +230,15 @@ hysdn_tx_netget(hysdn_card * card) return (lp->skbs[lp->out_idx]); /* next packet to send */ } /* hysdn_tx_netget */ +static const struct net_device_ops hysdn_netdev_ops = { + .ndo_open = net_open, + .ndo_stop = net_close, + .ndo_start_xmit = net_send_packet, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; -/*******************************************/ -/* init function called by register device */ -/*******************************************/ -static int -net_init(struct net_device *dev) -{ - /* setup the function table */ - dev->open = net_open; - dev->stop = net_close; - dev->hard_start_xmit = net_send_packet; - dev->get_stats = net_get_stats; - - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); - - return (0); /* success */ -} /* net_init */ /*****************************************************************************/ /* hysdn_net_create creates a new net device for the given card. If a device */ @@ -271,28 +250,34 @@ hysdn_net_create(hysdn_card * card) { struct net_device *dev; int i; + struct net_local *lp; + if(!card) { printk(KERN_WARNING "No card-pt in hysdn_net_create!\n"); return (-ENOMEM); } hysdn_net_release(card); /* release an existing net device */ - if ((dev = kzalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) { + + dev = alloc_etherdev(sizeof(struct net_local)); + if (!dev) { printk(KERN_WARNING "HYSDN: unable to allocate mem\n"); return (-ENOMEM); } + lp = netdev_priv(dev); + lp->dev = dev; + + dev->netdev_ops = &hysdn_netdev_ops; spin_lock_init(&((struct net_local *) dev)->lock); /* initialise necessary or informing fields */ dev->base_addr = card->iobase; /* IO address */ dev->irq = card->irq; /* irq */ - dev->init = net_init; /* the init function of the device */ - if(dev->name) { - strcpy(dev->name, ((struct net_local *) dev)->dev_name); - } + + dev->netdev_ops = &hysdn_netdev_ops; if ((i = register_netdev(dev))) { printk(KERN_WARNING "HYSDN: unable to create network device\n"); - kfree(dev); + free_netdev(dev); return (i); } dev->ml_priv = card; /* remember pointer to own data structure */ @@ -316,7 +301,7 @@ hysdn_net_release(hysdn_card * card) return (0); /* non existing */ card->netif = NULL; /* clear out pointer */ - dev->stop(dev); /* close the device */ + net_close(dev); flush_tx_buffers((struct net_local *) dev); /* empty buffers */ diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 023ea11d2f9e..7c5f97033b9f 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -1485,6 +1485,24 @@ isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return (rc); } + +static int isdn_net_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + isdn_net_local *lp = (isdn_net_local *) netdev_priv(dev); + + switch (lp->p_encap) { +#ifdef CONFIG_ISDN_PPP + case ISDN_NET_ENCAP_SYNCPPP: + return isdn_ppp_dev_ioctl(dev, ifr, cmd); +#endif + case ISDN_NET_ENCAP_CISCOHDLCK: + return isdn_ciscohdlck_dev_ioctl(dev, ifr, cmd); + default: + return -EINVAL; + } +} + /* called via cisco_timer.function */ static void isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data) @@ -1998,23 +2016,6 @@ isdn_net_init(struct net_device *ndev) ushort max_hlhdr_len = 0; int drvidx; - ether_setup(ndev); - ndev->header_ops = NULL; - - /* Setup the generic properties */ - ndev->mtu = 1500; - ndev->flags = IFF_NOARP|IFF_POINTOPOINT; - ndev->type = ARPHRD_ETHER; - ndev->addr_len = ETH_ALEN; - ndev->validate_addr = NULL; - - /* for clients with MPPP maybe higher values better */ - ndev->tx_queue_len = 30; - - /* The ISDN-specific entries in the device structure. */ - ndev->open = &isdn_net_open; - ndev->hard_start_xmit = &isdn_net_start_xmit; - /* * up till binding we ask the protocol layer to reserve as much * as we might need for HL layer @@ -2026,9 +2027,6 @@ isdn_net_init(struct net_device *ndev) max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen; ndev->hard_header_len = ETH_HLEN + max_hlhdr_len; - ndev->stop = &isdn_net_close; - ndev->get_stats = &isdn_net_get_stats; - ndev->do_ioctl = NULL; return 0; } @@ -2508,6 +2506,19 @@ isdn_net_force_dial(char *name) return (isdn_net_force_dial_lp(p->local)); } +/* The ISDN-specific entries in the device structure. */ +static const struct net_device_ops isdn_netdev_ops = { + .ndo_init = isdn_net_init, + .ndo_open = isdn_net_open, + .ndo_stop = isdn_net_close, + .ndo_do_ioctl = isdn_net_ioctl, + + .ndo_validate_addr = NULL, + .ndo_start_xmit = isdn_net_start_xmit, + .ndo_get_stats = isdn_net_get_stats, + .ndo_tx_timeout = isdn_net_tx_timeout, +}; + /* * Helper for alloc_netdev() */ @@ -2515,7 +2526,20 @@ static void _isdn_setup(struct net_device *dev) { isdn_net_local *lp = netdev_priv(dev); + ether_setup(dev); + dev->flags = IFF_NOARP | IFF_POINTOPOINT; + /* Setup the generic properties */ + dev->mtu = 1500; + dev->flags = IFF_NOARP|IFF_POINTOPOINT; + dev->type = ARPHRD_ETHER; + dev->addr_len = ETH_ALEN; + dev->header_ops = NULL; + dev->netdev_ops = &isdn_netdev_ops; + + /* for clients with MPPP maybe higher values better */ + dev->tx_queue_len = 30; + lp->p_encap = ISDN_NET_ENCAP_RAWIP; lp->magic = ISDN_NET_MAGIC; lp->last = lp; @@ -2570,7 +2594,7 @@ isdn_net_new(char *name, struct net_device *master) return NULL; } netdev->local = netdev_priv(netdev->dev); - netdev->dev->init = isdn_net_init; + if (master) { /* Device shall be a slave */ struct net_device *p = MASTER_TO_SLAVE(master); @@ -2588,7 +2612,6 @@ isdn_net_new(char *name, struct net_device *master) /* * Watchdog timer (currently) for master only. */ - netdev->dev->tx_timeout = isdn_net_tx_timeout; netdev->dev->watchdog_timeo = ISDN_NET_TX_TIMEOUT; if (register_netdev(netdev->dev) != 0) { printk(KERN_WARNING "isdn_net: Could not register net-device\n"); @@ -2704,7 +2727,6 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) #else p->dev->type = ARPHRD_PPP; /* change ARP type */ p->dev->addr_len = 0; - p->dev->do_ioctl = isdn_ppp_dev_ioctl; #endif break; case ISDN_NET_ENCAP_X25IFACE: @@ -2718,7 +2740,6 @@ isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) #endif break; case ISDN_NET_ENCAP_CISCOHDLCK: - p->dev->do_ioctl = isdn_ciscohdlck_dev_ioctl; break; default: if( cfg->p_encap >= 0 && diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile index 1cb5e633cf75..0a6bd2a9e730 100644 --- a/drivers/isdn/mISDN/Makefile +++ b/drivers/isdn/mISDN/Makefile @@ -8,6 +8,6 @@ obj-$(CONFIG_MISDN_L1OIP) += l1oip.o # multi objects -mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o +mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o l1oip-objs := l1oip_core.o l1oip_codec.o diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c new file mode 100644 index 000000000000..44d9c3d5d33d --- /dev/null +++ b/drivers/isdn/mISDN/clock.c @@ -0,0 +1,216 @@ +/* + * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu> + * + * 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 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. + * + * Quick API description: + * + * A clock source registers using mISDN_register_clock: + * name = text string to name clock source + * priority = value to priorize clock sources (0 = default) + * ctl = callback function to enable/disable clock source + * priv = private pointer of clock source + * return = pointer to clock source structure; + * + * Note: Callback 'ctl' can be called before mISDN_register_clock returns! + * Also it can be called during mISDN_unregister_clock. + * + * A clock source calls mISDN_clock_update with given samples elapsed, if + * enabled. If function call is delayed, tv must be set with the timestamp + * of the actual event. + * + * A clock source unregisters using mISDN_unregister_clock. + * + * To get current clock, call mISDN_clock_get. The signed short value + * counts the number of samples since. Time since last clock event is added. + * + */ + +#include <linux/types.h> +#include <linux/stddef.h> +#include <linux/spinlock.h> +#include <linux/mISDNif.h> +#include "core.h" + +static u_int *debug; +static LIST_HEAD(iclock_list); +DEFINE_RWLOCK(iclock_lock); +u16 iclock_count; /* counter of last clock */ +struct timeval iclock_tv; /* time stamp of last clock */ +int iclock_tv_valid; /* already received one timestamp */ +struct mISDNclock *iclock_current; + +void +mISDN_init_clock(u_int *dp) +{ + debug = dp; + do_gettimeofday(&iclock_tv); +} + +static void +select_iclock(void) +{ + struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; + int pri = -128; + + list_for_each_entry(iclock, &iclock_list, list) { + if (iclock->pri > pri) { + pri = iclock->pri; + bestclock = iclock; + } + if (iclock_current == iclock) + lastclock = iclock; + } + if (lastclock && bestclock != lastclock) { + /* last used clock source still exists but changes, disable */ + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG "Old clock source '%s' disable.\n", + lastclock->name); + lastclock->ctl(lastclock->priv, 0); + } + if (bestclock && bestclock != iclock_current) { + /* new clock source selected, enable */ + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG "New clock source '%s' enable.\n", + bestclock->name); + bestclock->ctl(bestclock->priv, 1); + } + if (bestclock != iclock_current) { + /* no clock received yet */ + iclock_tv_valid = 0; + } + iclock_current = bestclock; +} + +struct mISDNclock +*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) +{ + u_long flags; + struct mISDNclock *iclock; + + if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) + printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); + iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); + if (!iclock) { + printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); + return NULL; + } + strncpy(iclock->name, name, sizeof(iclock->name)-1); + iclock->pri = pri; + iclock->priv = priv; + iclock->ctl = ctl; + write_lock_irqsave(&iclock_lock, flags); + list_add_tail(&iclock->list, &iclock_list); + select_iclock(); + write_unlock_irqrestore(&iclock_lock, flags); + return iclock; +} +EXPORT_SYMBOL(mISDN_register_clock); + +void +mISDN_unregister_clock(struct mISDNclock *iclock) +{ + u_long flags; + + if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) + printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, + iclock->pri); + write_lock_irqsave(&iclock_lock, flags); + if (iclock_current == iclock) { + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG + "Current clock source '%s' unregisters.\n", + iclock->name); + iclock->ctl(iclock->priv, 0); + } + list_del(&iclock->list); + select_iclock(); + write_unlock_irqrestore(&iclock_lock, flags); +} +EXPORT_SYMBOL(mISDN_unregister_clock); + +void +mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) +{ + u_long flags; + struct timeval tv_now; + time_t elapsed_sec; + int elapsed_8000th; + + write_lock_irqsave(&iclock_lock, flags); + if (iclock_current != iclock) { + printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " + "listen to '%s'. This is a bug!\n", __func__, + iclock->name, + iclock_current ? iclock_current->name : "nothing"); + iclock->ctl(iclock->priv, 0); + write_unlock_irqrestore(&iclock_lock, flags); + return; + } + if (iclock_tv_valid) { + /* increment sample counter by given samples */ + iclock_count += samples; + if (tv) { /* tv must be set, if function call is delayed */ + iclock_tv.tv_sec = tv->tv_sec; + iclock_tv.tv_usec = tv->tv_usec; + } else + do_gettimeofday(&iclock_tv); + } else { + /* calc elapsed time by system clock */ + if (tv) { /* tv must be set, if function call is delayed */ + tv_now.tv_sec = tv->tv_sec; + tv_now.tv_usec = tv->tv_usec; + } else + do_gettimeofday(&tv_now); + elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; + elapsed_8000th = (tv_now.tv_usec / 125) + - (iclock_tv.tv_usec / 125); + if (elapsed_8000th < 0) { + elapsed_sec -= 1; + elapsed_8000th += 8000; + } + /* add elapsed time to counter and set new timestamp */ + iclock_count += elapsed_sec * 8000 + elapsed_8000th; + iclock_tv.tv_sec = tv_now.tv_sec; + iclock_tv.tv_usec = tv_now.tv_usec; + iclock_tv_valid = 1; + if (*debug & DEBUG_CLOCK) + printk("Received first clock from source '%s'.\n", + iclock_current ? iclock_current->name : "nothing"); + } + write_unlock_irqrestore(&iclock_lock, flags); +} +EXPORT_SYMBOL(mISDN_clock_update); + +unsigned short +mISDN_clock_get(void) +{ + u_long flags; + struct timeval tv_now; + time_t elapsed_sec; + int elapsed_8000th; + u16 count; + + read_lock_irqsave(&iclock_lock, flags); + /* calc elapsed time by system clock */ + do_gettimeofday(&tv_now); + elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; + elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125); + if (elapsed_8000th < 0) { + elapsed_sec -= 1; + elapsed_8000th += 8000; + } + /* add elapsed time to counter */ + count = iclock_count + elapsed_sec * 8000 + elapsed_8000th; + read_unlock_irqrestore(&iclock_lock, flags); + return count; +} +EXPORT_SYMBOL(mISDN_clock_get); + diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c index 751665c448d0..9426c9827e47 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -25,39 +25,183 @@ MODULE_AUTHOR("Karsten Keil"); MODULE_LICENSE("GPL"); module_param(debug, uint, S_IRUGO | S_IWUSR); -static LIST_HEAD(devices); -static DEFINE_RWLOCK(device_lock); static u64 device_ids; #define MAX_DEVICE_ID 63 static LIST_HEAD(Bprotocols); static DEFINE_RWLOCK(bp_lock); +static void mISDN_dev_release(struct device *dev) +{ + /* nothing to do: the device is part of its parent's data structure */ +} + +static ssize_t _show_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->id); +} + +static ssize_t _show_nrbchan(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->nrbchan); +} + +static ssize_t _show_d_protocols(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->Dprotocols); +} + +static ssize_t _show_b_protocols(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); +} + +static ssize_t _show_protocol(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->D.protocol); +} + +static ssize_t _show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + strcpy(buf, dev_name(dev)); + return strlen(buf); +} + +#if 0 /* hangs */ +static ssize_t _set_name(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + char *out = kmalloc(count + 1, GFP_KERNEL); + + if (!out) + return -ENOMEM; + + memcpy(out, buf, count); + if (count && out[count - 1] == '\n') + out[--count] = 0; + if (count) + err = device_rename(dev, out); + kfree(out); + + return (err < 0) ? err : count; +} +#endif + +static ssize_t _show_channelmap(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + char *bp = buf; + int i; + + for (i = 0; i <= mdev->nrbchan; i++) + *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0'; + + return bp - buf; +} + +static struct device_attribute mISDN_dev_attrs[] = { + __ATTR(id, S_IRUGO, _show_id, NULL), + __ATTR(d_protocols, S_IRUGO, _show_d_protocols, NULL), + __ATTR(b_protocols, S_IRUGO, _show_b_protocols, NULL), + __ATTR(protocol, S_IRUGO, _show_protocol, NULL), + __ATTR(channelmap, S_IRUGO, _show_channelmap, NULL), + __ATTR(nrbchan, S_IRUGO, _show_nrbchan, NULL), + __ATTR(name, S_IRUGO, _show_name, NULL), +/* __ATTR(name, S_IRUGO|S_IWUSR, _show_name, _set_name), */ + {} +}; + +#ifdef CONFIG_HOTPLUG +static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; + + if (add_uevent_var(env, "nchans=%d", mdev->nrbchan)) + return -ENOMEM; + + return 0; +} +#endif + +static void mISDN_class_release(struct class *cls) +{ + /* do nothing, it's static */ +} + +static struct class mISDN_class = { + .name = "mISDN", + .owner = THIS_MODULE, +#ifdef CONFIG_HOTPLUG + .dev_uevent = mISDN_uevent, +#endif + .dev_attrs = mISDN_dev_attrs, + .dev_release = mISDN_dev_release, + .class_release = mISDN_class_release, +}; + +static int +_get_mdevice(struct device *dev, void *id) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; + if (mdev->id != *(u_int *)id) + return 0; + return 1; +} + struct mISDNdevice *get_mdevice(u_int id) { - struct mISDNdevice *dev; + return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, + _get_mdevice)); +} - read_lock(&device_lock); - list_for_each_entry(dev, &devices, D.list) - if (dev->id == id) { - read_unlock(&device_lock); - return dev; - } - read_unlock(&device_lock); - return NULL; +static int +_get_mdevice_count(struct device *dev, void *cnt) +{ + *(int *)cnt += 1; + return 0; } int get_mdevice_count(void) { - struct mISDNdevice *dev; - int cnt = 0; + int cnt = 0; - read_lock(&device_lock); - list_for_each_entry(dev, &devices, D.list) - cnt++; - read_unlock(&device_lock); + class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); return cnt; } @@ -68,48 +212,66 @@ get_free_devid(void) for (i = 0; i <= MAX_DEVICE_ID; i++) if (!test_and_set_bit(i, (u_long *)&device_ids)) - return i; - return -1; + break; + if (i > MAX_DEVICE_ID) + return -1; + return i; } int -mISDN_register_device(struct mISDNdevice *dev, char *name) +mISDN_register_device(struct mISDNdevice *dev, + struct device *parent, char *name) { - u_long flags; int err; dev->id = get_free_devid(); + err = -EBUSY; if (dev->id < 0) - return -EBUSY; + goto error1; + + device_initialize(&dev->dev); if (name && name[0]) - strcpy(dev->name, name); + dev_set_name(&dev->dev, "%s", name); else - sprintf(dev->name, "mISDN%d", dev->id); + dev_set_name(&dev->dev, "mISDN%d", dev->id); if (debug & DEBUG_CORE) printk(KERN_DEBUG "mISDN_register %s %d\n", - dev->name, dev->id); + dev_name(&dev->dev), dev->id); err = create_stack(dev); if (err) - return err; - write_lock_irqsave(&device_lock, flags); - list_add_tail(&dev->D.list, &devices); - write_unlock_irqrestore(&device_lock, flags); + goto error1; + + dev->dev.class = &mISDN_class; + dev->dev.platform_data = dev; + dev->dev.parent = parent; + dev_set_drvdata(&dev->dev, dev); + + err = device_add(&dev->dev); + if (err) + goto error3; return 0; + +error3: + delete_stack(dev); + return err; +error1: + return err; + } EXPORT_SYMBOL(mISDN_register_device); void mISDN_unregister_device(struct mISDNdevice *dev) { - u_long flags; - if (debug & DEBUG_CORE) printk(KERN_DEBUG "mISDN_unregister %s %d\n", - dev->name, dev->id); - write_lock_irqsave(&device_lock, flags); - list_del(&dev->D.list); - write_unlock_irqrestore(&device_lock, flags); + dev_name(&dev->dev), dev->id); + /* sysfs_remove_link(&dev->dev.kobj, "device"); */ + device_del(&dev->dev); + dev_set_drvdata(&dev->dev, NULL); + test_and_clear_bit(dev->id, (u_long *)&device_ids); delete_stack(dev); + put_device(&dev->dev); } EXPORT_SYMBOL(mISDN_unregister_device); @@ -199,43 +361,45 @@ mISDNInit(void) printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); + mISDN_init_clock(&debug); mISDN_initstack(&debug); + err = class_register(&mISDN_class); + if (err) + goto error1; err = mISDN_inittimer(&debug); if (err) - goto error; + goto error2; err = l1_init(&debug); - if (err) { - mISDN_timer_cleanup(); - goto error; - } + if (err) + goto error3; err = Isdnl2_Init(&debug); - if (err) { - mISDN_timer_cleanup(); - l1_cleanup(); - goto error; - } + if (err) + goto error4; err = misdn_sock_init(&debug); - if (err) { - mISDN_timer_cleanup(); - l1_cleanup(); - Isdnl2_cleanup(); - } -error: + if (err) + goto error5; + return 0; + +error5: + Isdnl2_cleanup(); +error4: + l1_cleanup(); +error3: + mISDN_timer_cleanup(); +error2: + class_unregister(&mISDN_class); +error1: return err; } static void mISDN_cleanup(void) { misdn_sock_cleanup(); - mISDN_timer_cleanup(); - l1_cleanup(); Isdnl2_cleanup(); + l1_cleanup(); + mISDN_timer_cleanup(); + class_unregister(&mISDN_class); - if (!list_empty(&devices)) - printk(KERN_ERR "%s devices still registered\n", __func__); - - if (!list_empty(&Bprotocols)) - printk(KERN_ERR "%s Bprotocols still registered\n", __func__); printk(KERN_DEBUG "mISDNcore unloaded\n"); } diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h index 7da7233b4c1a..7ac2f81a812b 100644 --- a/drivers/isdn/mISDN/core.h +++ b/drivers/isdn/mISDN/core.h @@ -74,4 +74,6 @@ extern void l1_cleanup(void); extern int Isdnl2_Init(u_int *); extern void Isdnl2_cleanup(void); +extern void mISDN_init_clock(u_int *); + #endif diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h index 6c3fed6b8d4f..98a33c58f091 100644 --- a/drivers/isdn/mISDN/dsp.h +++ b/drivers/isdn/mISDN/dsp.h @@ -15,6 +15,7 @@ #define DEBUG_DSP_TONE 0x0020 #define DEBUG_DSP_BLOWFISH 0x0040 #define DEBUG_DSP_DELAY 0x0100 +#define DEBUG_DSP_CLOCK 0x0200 #define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ /* options may be: @@ -198,6 +199,7 @@ struct dsp { /* hardware stuff */ struct dsp_features features; int features_rx_off; /* set if rx_off is featured */ + int features_fill_empty; /* set if fill_empty is featured */ int pcm_slot_rx; /* current PCM slot (or -1) */ int pcm_bank_rx; int pcm_slot_tx; diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index c884511e2d49..0ac67bff303a 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -137,6 +137,7 @@ /* #define CMX_CONF_DEBUG */ /*#define CMX_DEBUG * massive read/write pointer output */ +/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */ /*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ static inline int @@ -744,11 +745,11 @@ conf_software: if (dsp->pcm_slot_rx >= 0 && dsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; + freeslots[dsp->pcm_slot_rx] = 0; if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; + freeslots[dsp->pcm_slot_tx] = 0; } } i = 0; @@ -836,11 +837,11 @@ conf_software: if (dsp->pcm_slot_rx >= 0 && dsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; + freeslots[dsp->pcm_slot_rx] = 0; if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; + freeslots[dsp->pcm_slot_tx] = 0; } } i1 = 0; @@ -926,10 +927,6 @@ conf_software: /* for more than two members.. */ - /* in case of hdlc, we change to software */ - if (dsp->hdlc) - goto conf_software; - /* if all members already have the same conference */ if (all_conf) return; @@ -940,6 +937,9 @@ conf_software: if (current_conf >= 0) { join_members: list_for_each_entry(member, &conf->mlist, list) { + /* in case of hdlc, change to software */ + if (member->dsp->hdlc) + goto conf_software; /* join to current conference */ if (member->dsp->hfc_conf == current_conf) continue; @@ -1135,6 +1135,25 @@ dsp_cmx_conf(struct dsp *dsp, u32 conf_id) return 0; } +#ifdef CMX_DELAY_DEBUG +int delaycount; +static void +showdelay(struct dsp *dsp, int samples, int delay) +{ + char bar[] = "--------------------------------------------------|"; + int sdelay; + + delaycount += samples; + if (delaycount < 8000) + return; + delaycount = 0; + + sdelay = delay * 50 / (dsp_poll << 2); + + printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay, + sdelay > 50 ? "..." : bar + 50 - sdelay); +} +#endif /* * audio data is received from card @@ -1168,11 +1187,18 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) dsp->rx_init = 0; if (dsp->features.unordered) { dsp->rx_R = (hh->id & CMX_BUFF_MASK); - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; + if (dsp->cmx_delay) + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + else + dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) + & CMX_BUFF_MASK; } else { dsp->rx_R = 0; - dsp->rx_W = dsp->cmx_delay; + if (dsp->cmx_delay) + dsp->rx_W = dsp->cmx_delay; + else + dsp->rx_W = dsp_poll >> 1; } } /* if frame contains time code, write directly */ @@ -1185,19 +1211,25 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) * we set our new read pointer, and write silence to buffer */ if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { - if (dsp_debug & DEBUG_DSP_CMX) + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " "maximum delay), adjusting read pointer! " "(inst %s)\n", (u_long)dsp, dsp->name); - /* flush buffer */ + /* flush rx buffer and set delay to dsp_poll / 2 */ if (dsp->features.unordered) { dsp->rx_R = (hh->id & CMX_BUFF_MASK); - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; + if (dsp->cmx_delay) + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) + & CMX_BUFF_MASK; } else { dsp->rx_R = 0; - dsp->rx_W = dsp->cmx_delay; + if (dsp->cmx_delay) + dsp->rx_W = dsp->cmx_delay; + else + dsp->rx_W = dsp_poll >> 1; } memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); } @@ -1205,7 +1237,7 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) if (dsp->cmx_delay) if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= (dsp->cmx_delay << 1)) { - if (dsp_debug & DEBUG_DSP_CMX) + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "cmx_receive(dsp=%lx): OVERRUN (because " "twice the delay is reached), adjusting " @@ -1243,6 +1275,9 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) /* increase write-pointer */ dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK); +#ifdef CMX_DELAY_DEBUG + showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK); +#endif } @@ -1360,8 +1395,12 @@ dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) t = (t+1) & CMX_BUFF_MASK; r = (r+1) & CMX_BUFF_MASK; } - if (r != rr) + if (r != rr) { + if (dsp_debug & DEBUG_DSP_CLOCK) + printk(KERN_DEBUG "%s: RX empty\n", + __func__); memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); + } /* -> if echo is enabled */ } else { /* @@ -1540,13 +1579,11 @@ send_packet: schedule_work(&dsp->workq); } -static u32 samplecount; +static u32 jittercount; /* counter for jitter check */; struct timer_list dsp_spl_tl; u32 dsp_spl_jiffies; /* calculate the next time to fire */ -#ifdef UNUSED -static u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ -#endif /* UNUSED */ -static struct timeval dsp_start_tv; /* time at start of calculation */ +static u16 dsp_count; /* last sample count */ +static int dsp_count_valid ; /* if we have last sample count */ void dsp_cmx_send(void *arg) @@ -1560,38 +1597,32 @@ dsp_cmx_send(void *arg) int r, rr; int jittercheck = 0, delay, i; u_long flags; - struct timeval tv; - u32 elapsed; - s16 length; + u16 length, count; /* lock */ spin_lock_irqsave(&dsp_lock, flags); - if (!dsp_start_tv.tv_sec) { - do_gettimeofday(&dsp_start_tv); + if (!dsp_count_valid) { + dsp_count = mISDN_clock_get(); length = dsp_poll; + dsp_count_valid = 1; } else { - do_gettimeofday(&tv); - elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) - + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); - dsp_start_tv.tv_sec = tv.tv_sec; - dsp_start_tv.tv_usec = tv.tv_usec; - length = elapsed; + count = mISDN_clock_get(); + length = count - dsp_count; + dsp_count = count; } if (length > MAX_POLL + 100) length = MAX_POLL + 100; -/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", - length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, - dsp_poll_diff & 0xffff); - */ + /* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */ /* - * check if jitter needs to be checked - * (this is about every second = 8192 samples) + * check if jitter needs to be checked (this is every second) */ - samplecount += length; - if ((samplecount & 8191) < length) + jittercount += length; + if (jittercount >= 8000) { + jittercount -= 8000; jittercheck = 1; + } /* loop all members that do not require conference mixing */ list_for_each_entry(dsp, &dsp_ilist, list) { @@ -1704,17 +1735,19 @@ dsp_cmx_send(void *arg) } /* * remove rx_delay only if we have delay AND we - * have not preset cmx_delay + * have not preset cmx_delay AND + * the delay is greater dsp_poll */ - if (delay && !dsp->cmx_delay) { - if (dsp_debug & DEBUG_DSP_CMX) + if (delay > dsp_poll && !dsp->cmx_delay) { + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "%s lowest rx_delay of %d bytes for" " dsp %s are now removed.\n", __func__, delay, dsp->name); r = dsp->rx_R; - rr = (r + delay) & CMX_BUFF_MASK; + rr = (r + delay - (dsp_poll >> 1)) + & CMX_BUFF_MASK; /* delete rx-data */ while (r != rr) { p[r] = dsp_silence; @@ -1736,15 +1769,16 @@ dsp_cmx_send(void *arg) * remove delay only if we have delay AND we * have enabled tx_dejitter */ - if (delay && dsp->tx_dejitter) { - if (dsp_debug & DEBUG_DSP_CMX) + if (delay > dsp_poll && dsp->tx_dejitter) { + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "%s lowest tx_delay of %d bytes for" " dsp %s are now removed.\n", __func__, delay, dsp->name); r = dsp->tx_R; - rr = (r + delay) & CMX_BUFF_MASK; + rr = (r + delay - (dsp_poll >> 1)) + & CMX_BUFF_MASK; /* delete tx-data */ while (r != rr) { q[r] = dsp_silence; @@ -1797,14 +1831,16 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) ww = dsp->tx_R; p = dsp->tx_buff; d = skb->data; - space = ww-w; - if (space <= 0) - space += CMX_BUFF_SIZE; + space = (ww - w - 1) & CMX_BUFF_MASK; /* write-pointer should not overrun nor reach read pointer */ - if (space-1 < skb->len) + if (space < skb->len) { /* write to the space we have left */ - ww = (ww - 1) & CMX_BUFF_MASK; - else + ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ + if (dsp_debug & DEBUG_DSP_CLOCK) + printk(KERN_DEBUG "%s: TX overflow space=%d skb->len=" + "%d, w=0x%04x, ww=0x%04x\n", __func__, space, + skb->len, w, ww); + } else /* write until all byte are copied */ ww = (w + skb->len) & CMX_BUFF_MASK; dsp->tx_W = ww; diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 1dc21d803410..3083338716b2 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -191,6 +191,8 @@ dsp_rx_off_member(struct dsp *dsp) struct mISDN_ctrl_req cq; int rx_off = 1; + memset(&cq, 0, sizeof(cq)); + if (!dsp->features_rx_off) return; @@ -249,6 +251,32 @@ dsp_rx_off(struct dsp *dsp) } } +/* enable "fill empty" feature */ +static void +dsp_fill_empty(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + + memset(&cq, 0, sizeof(cq)); + + if (!dsp->ch.peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no fill_empty\n", + __func__); + return; + } + cq.op = MISDN_CTRL_FILL_EMPTY; + cq.p1 = 1; + if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: %s set fill_empty = 1\n", + __func__, dsp->name); +} + static int dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) { @@ -273,8 +301,9 @@ dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: start dtmf\n", __func__); if (len == sizeof(int)) { - printk(KERN_NOTICE "changing DTMF Threshold " - "to %d\n", *((int *)data)); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_NOTICE "changing DTMF Threshold " + "to %d\n", *((int *)data)); dsp->dtmf.treshold = (*(int *)data) * 10000; } /* init goertzel */ @@ -593,8 +622,6 @@ get_features(struct mISDNchannel *ch) struct dsp *dsp = container_of(ch, struct dsp, ch); struct mISDN_ctrl_req cq; - if (dsp_options & DSP_OPT_NOHARDWARE) - return; if (!ch->peer) { if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: no peer, no features\n", @@ -610,6 +637,10 @@ get_features(struct mISDNchannel *ch) } if (cq.op & MISDN_CTRL_RX_OFF) dsp->features_rx_off = 1; + if (cq.op & MISDN_CTRL_FILL_EMPTY) + dsp->features_fill_empty = 1; + if (dsp_options & DSP_OPT_NOHARDWARE) + return; if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { cq.op = MISDN_CTRL_HW_FEATURES; *((u_long *)&cq.p1) = (u_long)&dsp->features; @@ -837,11 +868,14 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) } if (dsp->hdlc) { /* hdlc */ - spin_lock_irqsave(&dsp_lock, flags); - if (dsp->b_active) { - skb_queue_tail(&dsp->sendq, skb); - schedule_work(&dsp->workq); + if (!dsp->b_active) { + ret = -EIO; + break; } + hh->prim = PH_DATA_REQ; + spin_lock_irqsave(&dsp_lock, flags); + skb_queue_tail(&dsp->sendq, skb); + schedule_work(&dsp->workq); spin_unlock_irqrestore(&dsp_lock, flags); return 0; } @@ -865,6 +899,9 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) if (dsp->dtmf.hardware || dsp->dtmf.software) dsp_dtmf_goertzel_init(dsp); get_features(ch); + /* enable fill_empty feature */ + if (dsp->features_fill_empty) + dsp_fill_empty(dsp); /* send ph_activate */ hh->prim = PH_ACTIVATE_REQ; if (ch->peer) @@ -1105,7 +1142,7 @@ static int dsp_init(void) } else { poll = 8; while (poll <= MAX_POLL) { - tics = poll * HZ / 8000; + tics = (poll * HZ) / 8000; if (tics * 8000 == poll * HZ) { dsp_tics = tics; dsp_poll = poll; diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c index 83639be7f7ad..bf999bdc41c3 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -75,6 +75,15 @@ static struct device_attribute element_attributes[] = { __ATTR(args, 0444, attr_show_args, NULL), }; +static void +mISDN_dsp_dev_release(struct device *dev) +{ + struct dsp_element_entry *entry = + container_of(dev, struct dsp_element_entry, dev); + list_del(&entry->list); + kfree(entry); +} + int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) { struct dsp_element_entry *entry; @@ -83,13 +92,14 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) if (!elem) return -EINVAL; - entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); + entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC); if (!entry) return -ENOMEM; entry->elem = elem; entry->dev.class = elements_class; + entry->dev.release = mISDN_dsp_dev_release; dev_set_drvdata(&entry->dev, elem); dev_set_name(&entry->dev, elem->name); ret = device_register(&entry->dev); @@ -98,6 +108,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) __func__, elem->name); goto err1; } + list_add_tail(&entry->list, &dsp_elements); for (i = 0; i < (sizeof(element_attributes) / sizeof(struct device_attribute)); ++i) @@ -109,14 +120,15 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) goto err2; } - list_add_tail(&entry->list, &dsp_elements); - +#ifdef PIPELINE_DEBUG printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); +#endif return 0; err2: device_unregister(&entry->dev); + return ret; err1: kfree(entry); return ret; @@ -132,11 +144,11 @@ void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) list_for_each_entry_safe(entry, n, &dsp_elements, list) if (entry->elem == elem) { - list_del(&entry->list); device_unregister(&entry->dev); - kfree(entry); +#ifdef PIPELINE_DEBUG printk(KERN_DEBUG "%s: %s unregistered\n", __func__, elem->name); +#endif return; } printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); @@ -173,7 +185,9 @@ void dsp_pipeline_module_exit(void) kfree(entry); } +#ifdef PIPELINE_DEBUG printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); +#endif } int dsp_pipeline_init(struct dsp_pipeline *pipeline) @@ -239,7 +253,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) if (!len) return 0; - dup = kmalloc(len + 1, GFP_KERNEL); + dup = kmalloc(len + 1, GFP_ATOMIC); if (!dup) return 0; strcpy(dup, cfg); @@ -256,9 +270,9 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) elem = entry->elem; pipeline_entry = kmalloc(sizeof(struct - dsp_pipeline_entry), GFP_KERNEL); + dsp_pipeline_entry), GFP_ATOMIC); if (!pipeline_entry) { - printk(KERN_DEBUG "%s: failed to add " + printk(KERN_ERR "%s: failed to add " "entry to pipeline: %s (out of " "memory)\n", __func__, elem->name); incomplete = 1; @@ -286,7 +300,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) args : ""); #endif } else { - printk(KERN_DEBUG "%s: failed " + printk(KERN_ERR "%s: failed " "to add entry to pipeline: " "%s (new() returned NULL)\n", __func__, elem->name); @@ -301,7 +315,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) if (found) found = 0; else { - printk(KERN_DEBUG "%s: element not found, skipping: " + printk(KERN_ERR "%s: element not found, skipping: " "%s\n", __func__, name); incomplete = 1; } diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c index 2596fba4e614..ab1168a110ae 100644 --- a/drivers/isdn/mISDN/hwchannel.c +++ b/drivers/isdn/mISDN/hwchannel.c @@ -50,9 +50,6 @@ bchannel_bh(struct work_struct *ws) if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { while ((skb = skb_dequeue(&bch->rqueue))) { - if (bch->rcount >= 64) - printk(KERN_WARNING "B-channel %p receive " - "queue if full, but empties...\n", bch); bch->rcount--; if (likely(bch->ch.peer)) { err = bch->ch.recv(bch->ch.peer, skb); @@ -169,6 +166,25 @@ recv_Dchannel(struct dchannel *dch) EXPORT_SYMBOL(recv_Dchannel); void +recv_Echannel(struct dchannel *ech, struct dchannel *dch) +{ + struct mISDNhead *hh; + + if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */ + dev_kfree_skb(ech->rx_skb); + ech->rx_skb = NULL; + return; + } + hh = mISDN_HEAD_P(ech->rx_skb); + hh->prim = PH_DATA_E_IND; + hh->id = get_sapi_tei(ech->rx_skb->data); + skb_queue_tail(&dch->rqueue, ech->rx_skb); + ech->rx_skb = NULL; + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Echannel); + +void recv_Bchannel(struct bchannel *bch) { struct mISDNhead *hh; @@ -177,8 +193,10 @@ recv_Bchannel(struct bchannel *bch) hh->prim = PH_DATA_IND; hh->id = MISDN_ID_ANY; if (bch->rcount >= 64) { - dev_kfree_skb(bch->rx_skb); - bch->rx_skb = NULL; + printk(KERN_WARNING "B-channel %p receive queue overflow, " + "fushing!\n", bch); + skb_queue_purge(&bch->rqueue); + bch->rcount = 0; return; } bch->rcount++; @@ -200,8 +218,10 @@ void recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) { if (bch->rcount >= 64) { - dev_kfree_skb(skb); - return; + printk(KERN_WARNING "B-channel %p receive queue overflow, " + "fushing!\n", bch); + skb_queue_purge(&bch->rqueue); + bch->rcount = 0; } bch->rcount++; skb_queue_tail(&bch->rqueue, skb); @@ -245,8 +265,12 @@ confirm_Bsend(struct bchannel *bch) { struct sk_buff *skb; - if (bch->rcount >= 64) - return; + if (bch->rcount >= 64) { + printk(KERN_WARNING "B-channel %p receive queue overflow, " + "fushing!\n", bch); + skb_queue_purge(&bch->rqueue); + bch->rcount = 0; + } skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), 0, NULL, GFP_ATOMIC); if (!skb) { diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 0884dd6892f8..abe574989572 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -777,6 +777,8 @@ fail: static void l1oip_socket_close(struct l1oip *hc) { + struct dchannel *dch = hc->chan[hc->d_idx].dch; + /* kill thread */ if (hc->socket_thread) { if (debug & DEBUG_L1OIP_SOCKET) @@ -785,6 +787,16 @@ l1oip_socket_close(struct l1oip *hc) send_sig(SIGTERM, hc->socket_thread, 0); wait_for_completion(&hc->socket_complete); } + + /* if active, we send up a PH_DEACTIVATE and deactivate */ + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: interface become deactivated " + "due to timeout\n", __func__); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } } static int @@ -944,7 +956,8 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) switch (cq->op) { case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER; + cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER + | MISDN_CTRL_GETPEER; break; case MISDN_CTRL_SETPEER: hc->remoteip = (u32)cq->p1; @@ -964,6 +977,13 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) hc->remoteip = 0; l1oip_socket_open(hc); break; + case MISDN_CTRL_GETPEER: + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: getting ip address.\n", + __func__); + cq->p1 = hc->remoteip; + cq->p2 = hc->remoteport | (hc->localport << 16); + break; default: printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op); @@ -1413,7 +1433,8 @@ init_card(struct l1oip *hc, int pri, int bundle) hc->chan[i + ch].bch = bch; set_channelmap(bch->nr, dch->dev.channelmap); } - ret = mISDN_register_device(&dch->dev, hc->name); + /* TODO: create a parent device for this driver */ + ret = mISDN_register_device(&dch->dev, NULL, hc->name); if (ret) return ret; hc->registered = 1; diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c index b73e952d12cf..e826eeb1ecec 100644 --- a/drivers/isdn/mISDN/layer1.c +++ b/drivers/isdn/mISDN/layer1.c @@ -101,7 +101,7 @@ l1m_debug(struct FsmInst *fi, char *fmt, ...) va_list va; va_start(va, fmt); - printk(KERN_DEBUG "%s: ", l1->dch->dev.name); + printk(KERN_DEBUG "%s: ", dev_name(&l1->dch->dev.dev)); vprintk(fmt, va); printk("\n"); va_end(va); diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 37a2de18cfd0..508945d1b9c1 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -381,7 +381,7 @@ data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) memcpy(di.channelmap, dev->channelmap, sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; - strcpy(di.name, dev->name); + strcpy(di.name, dev_name(&dev->dev)); if (copy_to_user((void __user *)arg, &di, sizeof(di))) err = -EFAULT; } else @@ -460,6 +460,8 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; struct sock *sk = sock->sk; + struct hlist_node *node; + struct sock *csk; int err = 0; if (*debug & DEBUG_SOCKET) @@ -480,6 +482,26 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) err = -ENODEV; goto done; } + + if (sk->sk_protocol < ISDN_P_B_START) { + read_lock_bh(&data_sockets.lock); + sk_for_each(csk, node, &data_sockets.head) { + if (sk == csk) + continue; + if (_pms(csk)->dev != _pms(sk)->dev) + continue; + if (csk->sk_protocol >= ISDN_P_B_START) + continue; + if (IS_ISDN_P_TE(csk->sk_protocol) + == IS_ISDN_P_TE(sk->sk_protocol)) + continue; + read_unlock_bh(&data_sockets.lock); + err = -EBUSY; + goto done; + } + read_unlock_bh(&data_sockets.lock); + } + _pms(sk)->ch.send = mISDN_send; _pms(sk)->ch.ctrl = mISDN_ctrl; @@ -639,12 +661,27 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) memcpy(di.channelmap, dev->channelmap, sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; - strcpy(di.name, dev->name); + strcpy(di.name, dev_name(&dev->dev)); if (copy_to_user((void __user *)arg, &di, sizeof(di))) err = -EFAULT; } else err = -ENODEV; break; + case IMSETDEVNAME: + { + struct mISDN_devrename dn; + if (copy_from_user(&dn, (void __user *)arg, + sizeof(dn))) { + err = -EFAULT; + break; + } + dev = get_mdevice(dn.id); + if (dev) + err = device_rename(&dev->dev, dn.name); + else + err = -ENODEV; + } + break; default: err = -EINVAL; } diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index d55b14ae4e99..e2f45019ebf0 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -172,7 +172,8 @@ send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) else printk(KERN_WARNING "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, st->dev->name, hh->prim, hh->id); + __func__, dev_name(&st->dev->dev), hh->prim, + hh->id); } else if (lm == 0x8) { WARN_ON(lm == 0x8); ch = get_channel4id(st, hh->id); @@ -181,11 +182,12 @@ send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) else printk(KERN_WARNING "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, st->dev->name, hh->prim, hh->id); + __func__, dev_name(&st->dev->dev), hh->prim, + hh->id); } else { /* broadcast not handled yet */ printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", - __func__, st->dev->name, hh->prim); + __func__, dev_name(&st->dev->dev), hh->prim); } return -ESRCH; } @@ -209,7 +211,8 @@ mISDNStackd(void *data) unlock_kernel(); #endif if (*debug & DEBUG_MSG_THREAD) - printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name); + printk(KERN_DEBUG "mISDNStackd %s started\n", + dev_name(&st->dev->dev)); if (st->notify != NULL) { complete(st->notify); @@ -245,7 +248,7 @@ mISDNStackd(void *data) printk(KERN_DEBUG "%s: %s prim(%x) id(%x) " "send call(%d)\n", - __func__, st->dev->name, + __func__, dev_name(&st->dev->dev), mISDN_HEAD_PRIM(skb), mISDN_HEAD_ID(skb), err); dev_kfree_skb(skb); @@ -288,7 +291,7 @@ mISDNStackd(void *data) mISDN_STACK_ACTION_MASK)); if (*debug & DEBUG_MSG_THREAD) printk(KERN_DEBUG "%s: %s wake status %08lx\n", - __func__, st->dev->name, st->status); + __func__, dev_name(&st->dev->dev), st->status); test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); @@ -303,15 +306,16 @@ mISDNStackd(void *data) #ifdef MISDN_MSG_STATS printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " "msg %d sleep %d stopped\n", - st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt); + dev_name(&st->dev->dev), st->msg_cnt, st->sleep_cnt, + st->stopped_cnt); printk(KERN_DEBUG "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n", - st->dev->name, st->thread->utime, st->thread->stime); + dev_name(&st->dev->dev), st->thread->utime, st->thread->stime); printk(KERN_DEBUG "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", - st->dev->name, st->thread->nvcsw, st->thread->nivcsw); + dev_name(&st->dev->dev), st->thread->nvcsw, st->thread->nivcsw); printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", - st->dev->name); + dev_name(&st->dev->dev)); #endif test_and_set_bit(mISDN_STACK_KILLED, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); @@ -401,15 +405,16 @@ create_stack(struct mISDNdevice *dev) newst->own.send = mISDN_queue_message; newst->own.recv = mISDN_queue_message; if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name); + printk(KERN_DEBUG "%s: st(%s)\n", __func__, + dev_name(&newst->dev->dev)); newst->notify = &done; newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", - newst->dev->name); + dev_name(&newst->dev->dev)); if (IS_ERR(newst->thread)) { err = PTR_ERR(newst->thread); printk(KERN_ERR "mISDN:cannot create kernel thread for %s (%d)\n", - newst->dev->name, err); + dev_name(&newst->dev->dev), err); delete_teimanager(dev->teimgr); kfree(newst); } else @@ -428,29 +433,21 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev->name, protocol, adr->dev, adr->channel, - adr->sapi, adr->tei); + __func__, dev_name(&dev->dev), protocol, adr->dev, + adr->channel, adr->sapi, adr->tei); switch (protocol) { case ISDN_P_NT_S0: case ISDN_P_NT_E1: case ISDN_P_TE_S0: case ISDN_P_TE_E1: -#ifdef PROTOCOL_CHECK - /* this should be enhanced */ - if (!list_empty(&dev->D.st->layer2) - && dev->D.protocol != protocol) - return -EBUSY; - if (!hlist_empty(&dev->D.st->l1sock.head) - && dev->D.protocol != protocol) - return -EBUSY; -#endif ch->recv = mISDN_queue_message; ch->peer = &dev->D.st->own; ch->st = dev->D.st; rq.protocol = protocol; - rq.adr.channel = 0; + rq.adr.channel = adr->channel; err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err, + dev->id); if (err) return err; write_lock_bh(&dev->D.st->l1sock.lock); @@ -473,7 +470,7 @@ connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev->name, protocol, + __func__, dev_name(&dev->dev), protocol, adr->dev, adr->channel, adr->sapi, adr->tei); ch->st = dev->D.st; @@ -529,7 +526,7 @@ create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev->name, protocol, + __func__, dev_name(&dev->dev), protocol, adr->dev, adr->channel, adr->sapi, adr->tei); rq.protocol = ISDN_P_TE_S0; @@ -541,15 +538,6 @@ create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) rq.protocol = ISDN_P_NT_E1; case ISDN_P_LAPD_TE: -#ifdef PROTOCOL_CHECK - /* this should be enhanced */ - if (!list_empty(&dev->D.st->layer2) - && dev->D.protocol != protocol) - return -EBUSY; - if (!hlist_empty(&dev->D.st->l1sock.head) - && dev->D.protocol != protocol) - return -EBUSY; -#endif ch->recv = mISDN_queue_message; ch->peer = &dev->D.st->own; ch->st = dev->D.st; @@ -590,7 +578,7 @@ delete_channel(struct mISDNchannel *ch) } if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, - ch->st->dev->name, ch->protocol); + dev_name(&ch->st->dev->dev), ch->protocol); if (ch->protocol >= ISDN_P_B_START) { if (ch->peer) { ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); @@ -643,7 +631,7 @@ delete_stack(struct mISDNdevice *dev) if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%s)\n", __func__, - st->dev->name); + dev_name(&st->dev->dev)); if (dev->teimgr) delete_teimanager(dev->teimgr); if (st->thread) { diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c index 5c43d19e7c11..b452dead8fd0 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -968,9 +968,9 @@ create_teimgr(struct manager *mgr, struct channel_req *crq) if (*debug & DEBUG_L2_TEI) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, mgr->ch.st->dev->name, crq->protocol, - crq->adr.dev, crq->adr.channel, crq->adr.sapi, - crq->adr.tei); + __func__, dev_name(&mgr->ch.st->dev->dev), + crq->protocol, crq->adr.dev, crq->adr.channel, + crq->adr.sapi, crq->adr.tei); if (crq->adr.sapi != 0) /* not supported yet */ return -EINVAL; if (crq->adr.tei > GROUP_TEI) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e7fb7d2fcbfc..a4a1ae214630 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -63,6 +63,12 @@ config LEDS_WRAP help This option enables support for the PCEngines WRAP programmable LEDs. +config LEDS_ALIX2 + tristate "LED Support for ALIX.2 and ALIX.3 series" + depends on LEDS_CLASS && X86 && EXPERIMENTAL + help + This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. + config LEDS_H1940 tristate "LED Support for iPAQ H1940 device" depends on LEDS_CLASS && ARCH_H1940 @@ -77,7 +83,7 @@ config LEDS_COBALT_QUBE config LEDS_COBALT_RAQ bool "LED Support for the Cobalt Raq series" - depends on LEDS_CLASS && MIPS_COBALT + depends on LEDS_CLASS=y && MIPS_COBALT select LEDS_TRIGGERS help This option enables support for the Cobalt Raq series LEDs. @@ -158,6 +164,13 @@ config LEDS_PCA955X LED driver chips accessed via the I2C bus. Supported devices include PCA9550, PCA9551, PCA9552, and PCA9553. +config LEDS_WM8350 + tristate "LED Support for WM8350 AudioPlus PMIC" + depends on LEDS_CLASS && MFD_WM8350 + help + This option enables support for LEDs driven by the Wolfson + Microelectronics WM8350 AudioPlus PMIC. + config LEDS_DA903X tristate "LED Support for DA9030/DA9034 PMIC" depends on LEDS_CLASS && PMIC_DA903X diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e1967a29850e..bc247cb02e82 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o 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_ALIX2) += leds-alix2.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o @@ -23,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o +obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 6c4a326176d7..52f82e3ea13a 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -91,9 +91,29 @@ void led_classdev_resume(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_resume); +static int led_suspend(struct device *dev, pm_message_t state) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + if (led_cdev->flags & LED_CORE_SUSPENDRESUME) + led_classdev_suspend(led_cdev); + + return 0; +} + +static int led_resume(struct device *dev) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + if (led_cdev->flags & LED_CORE_SUSPENDRESUME) + led_classdev_resume(led_cdev); + + return 0; +} + /** * led_classdev_register - register a new object of led_classdev class. - * @dev: The device to register. + * @parent: The device to register. * @led_cdev: the led_classdev structure for this device. */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) @@ -174,6 +194,8 @@ static int __init leds_init(void) leds_class = class_create(THIS_MODULE, "leds"); if (IS_ERR(leds_class)) return PTR_ERR(leds_class); + leds_class->suspend = led_suspend; + leds_class->resume = led_resume; return 0; } diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c new file mode 100644 index 000000000000..ddbd7730dfc8 --- /dev/null +++ b/drivers/leds/leds-alix2.c @@ -0,0 +1,181 @@ +/* + * LEDs driver for PCEngines ALIX.2 and ALIX.3 + * + * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/string.h> + +static int force = 0; +module_param(force, bool, 0444); +MODULE_PARM_DESC(force, "Assume system has ALIX.2 style LEDs"); + +struct alix_led { + struct led_classdev cdev; + unsigned short port; + unsigned int on_value; + unsigned int off_value; +}; + +static void alix_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct alix_led *led_dev = + container_of(led_cdev, struct alix_led, cdev); + + if (brightness) + outl(led_dev->on_value, led_dev->port); + else + outl(led_dev->off_value, led_dev->port); +} + +static struct alix_led alix_leds[] = { + { + .cdev = { + .name = "alix:1", + .brightness_set = alix_led_set, + }, + .port = 0x6100, + .on_value = 1 << 22, + .off_value = 1 << 6, + }, + { + .cdev = { + .name = "alix:2", + .brightness_set = alix_led_set, + }, + .port = 0x6180, + .on_value = 1 << 25, + .off_value = 1 << 9, + }, + { + .cdev = { + .name = "alix:3", + .brightness_set = alix_led_set, + }, + .port = 0x6180, + .on_value = 1 << 27, + .off_value = 1 << 11, + }, +}; + +static int __init alix_led_probe(struct platform_device *pdev) +{ + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(alix_leds); i++) { + alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; + ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev); + if (ret < 0) + goto fail; + } + return 0; + +fail: + while (--i >= 0) + led_classdev_unregister(&alix_leds[i].cdev); + return ret; +} + +static int alix_led_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(alix_leds); i++) + led_classdev_unregister(&alix_leds[i].cdev); + return 0; +} + +static struct platform_driver alix_led_driver = { + .remove = alix_led_remove, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init alix_present(void) +{ + const unsigned long bios_phys = 0x000f0000; + const size_t bios_len = 0x00010000; + const char alix_sig[] = "PC Engines ALIX."; + const size_t alix_sig_len = sizeof(alix_sig) - 1; + + const char *bios_virt; + const char *scan_end; + const char *p; + int ret = 0; + + if (force) { + printk(KERN_NOTICE "%s: forced to skip BIOS test, " + "assume system has ALIX.2 style LEDs\n", + KBUILD_MODNAME); + ret = 1; + goto out; + } + + bios_virt = phys_to_virt(bios_phys); + scan_end = bios_virt + bios_len - (alix_sig_len + 2); + for (p = bios_virt; p < scan_end; p++) { + const char *tail; + + if (memcmp(p, alix_sig, alix_sig_len) != 0) { + continue; + } + + tail = p + alix_sig_len; + if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { + printk(KERN_INFO + "%s: system is recognized as \"%s\"\n", + KBUILD_MODNAME, p); + ret = 1; + break; + } + } + +out: + return ret; +} + +static struct platform_device *pdev; + +static int __init alix_led_init(void) +{ + int ret; + + if (!alix_present()) { + ret = -ENODEV; + goto out; + } + + pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); + if (!IS_ERR(pdev)) { + ret = platform_driver_probe(&alix_led_driver, alix_led_probe); + if (ret) + platform_device_unregister(pdev); + } else + ret = PTR_ERR(pdev); + +out: + return ret; +} + +static void __exit alix_led_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&alix_led_driver); +} + +module_init(alix_led_init); +module_exit(alix_led_exit); + +MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>"); +MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c index 1bd590bb3a6e..446050759b4d 100644 --- a/drivers/leds/leds-ams-delta.c +++ b/drivers/leds/leds-ams-delta.c @@ -79,37 +79,12 @@ static struct ams_delta_led ams_delta_leds[] = { }, }; -#ifdef CONFIG_PM -static int ams_delta_led_suspend(struct platform_device *dev, - pm_message_t state) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) - led_classdev_suspend(&ams_delta_leds[i].cdev); - - return 0; -} - -static int ams_delta_led_resume(struct platform_device *dev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) - led_classdev_resume(&ams_delta_leds[i].cdev); - - return 0; -} -#else -#define ams_delta_led_suspend NULL -#define ams_delta_led_resume NULL -#endif - static int ams_delta_led_probe(struct platform_device *pdev) { int i, ret; for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) { + ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; ret = led_classdev_register(&pdev->dev, &ams_delta_leds[i].cdev); if (ret < 0) @@ -127,7 +102,7 @@ static int ams_delta_led_remove(struct platform_device *pdev) { int i; - for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i--) + for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) led_classdev_unregister(&ams_delta_leds[i].cdev); return 0; @@ -136,8 +111,6 @@ static int ams_delta_led_remove(struct platform_device *pdev) static struct platform_driver ams_delta_led_driver = { .probe = ams_delta_led_probe, .remove = ams_delta_led_remove, - .suspend = ams_delta_led_suspend, - .resume = ams_delta_led_resume, .driver = { .name = "ams-delta-led", .owner = THIS_MODULE, @@ -151,7 +124,7 @@ static int __init ams_delta_led_init(void) static void __exit ams_delta_led_exit(void) { - return platform_driver_unregister(&ams_delta_led_driver); + platform_driver_unregister(&ams_delta_led_driver); } module_init(ams_delta_led_init); diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index eb3415e88f43..1813c84ea5fc 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -142,6 +142,7 @@ static struct led_classdev clevo_mail_led = { .name = "clevo::mail", .brightness_set = clevo_mail_led_set, .blink_set = clevo_mail_led_blink, + .flags = LED_CORE_SUSPENDRESUME, }; static int __init clevo_mail_led_probe(struct platform_device *pdev) @@ -155,29 +156,9 @@ static int clevo_mail_led_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int clevo_mail_led_suspend(struct platform_device *dev, - pm_message_t state) -{ - led_classdev_suspend(&clevo_mail_led); - return 0; -} - -static int clevo_mail_led_resume(struct platform_device *dev) -{ - led_classdev_resume(&clevo_mail_led); - return 0; -} -#else -#define clevo_mail_led_suspend NULL -#define clevo_mail_led_resume NULL -#endif - static struct platform_driver clevo_mail_led_driver = { .probe = clevo_mail_led_probe, .remove = clevo_mail_led_remove, - .suspend = clevo_mail_led_suspend, - .resume = clevo_mail_led_resume, .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c index 34935155c1c0..5f7c9c5c09b1 100644 --- a/drivers/leds/leds-fsg.c +++ b/drivers/leds/leds-fsg.c @@ -99,64 +99,43 @@ static void fsg_led_ring_set(struct led_classdev *led_cdev, } - static struct led_classdev fsg_wlan_led = { .name = "fsg:blue:wlan", .brightness_set = fsg_led_wlan_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev fsg_wan_led = { .name = "fsg:blue:wan", .brightness_set = fsg_led_wan_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev fsg_sata_led = { .name = "fsg:blue:sata", .brightness_set = fsg_led_sata_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev fsg_usb_led = { .name = "fsg:blue:usb", .brightness_set = fsg_led_usb_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev fsg_sync_led = { .name = "fsg:blue:sync", .brightness_set = fsg_led_sync_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev fsg_ring_led = { .name = "fsg:blue:ring", .brightness_set = fsg_led_ring_set, + .flags = LED_CORE_SUSPENDRESUME, }; - -#ifdef CONFIG_PM -static int fsg_led_suspend(struct platform_device *dev, pm_message_t state) -{ - led_classdev_suspend(&fsg_wlan_led); - led_classdev_suspend(&fsg_wan_led); - led_classdev_suspend(&fsg_sata_led); - led_classdev_suspend(&fsg_usb_led); - led_classdev_suspend(&fsg_sync_led); - led_classdev_suspend(&fsg_ring_led); - return 0; -} - -static int fsg_led_resume(struct platform_device *dev) -{ - led_classdev_resume(&fsg_wlan_led); - led_classdev_resume(&fsg_wan_led); - led_classdev_resume(&fsg_sata_led); - led_classdev_resume(&fsg_usb_led); - led_classdev_resume(&fsg_sync_led); - led_classdev_resume(&fsg_ring_led); - return 0; -} -#endif - - static int fsg_led_probe(struct platform_device *pdev) { int ret; @@ -232,10 +211,6 @@ static int fsg_led_remove(struct platform_device *pdev) static struct platform_driver fsg_led_driver = { .probe = fsg_led_probe, .remove = fsg_led_remove, -#ifdef CONFIG_PM - .suspend = fsg_led_suspend, - .resume = fsg_led_resume, -#endif .driver = { .name = "fsg-led", }, diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b13bd2950e95..2e3df08b649b 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -105,6 +105,7 @@ static int gpio_led_probe(struct platform_device *pdev) } led_dat->cdev.brightness_set = gpio_led_set; led_dat->cdev.brightness = LED_OFF; + led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; gpio_direction_output(led_dat->gpio, led_dat->active_low); @@ -154,44 +155,9 @@ static int __devexit gpio_led_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct gpio_led_platform_data *pdata = pdev->dev.platform_data; - struct gpio_led_data *leds_data; - int i; - - leds_data = platform_get_drvdata(pdev); - - for (i = 0; i < pdata->num_leds; i++) - led_classdev_suspend(&leds_data[i].cdev); - - return 0; -} - -static int gpio_led_resume(struct platform_device *pdev) -{ - struct gpio_led_platform_data *pdata = pdev->dev.platform_data; - struct gpio_led_data *leds_data; - int i; - - leds_data = platform_get_drvdata(pdev); - - for (i = 0; i < pdata->num_leds; i++) - led_classdev_resume(&leds_data[i].cdev); - - return 0; -} -#else -#define gpio_led_suspend NULL -#define gpio_led_resume NULL -#endif - static struct platform_driver gpio_led_driver = { .probe = gpio_led_probe, .remove = __devexit_p(gpio_led_remove), - .suspend = gpio_led_suspend, - .resume = gpio_led_resume, .driver = { .name = "leds-gpio", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-hp-disk.c b/drivers/leds/leds-hp-disk.c index 44fa757d8254..d786adc8c5e3 100644 --- a/drivers/leds/leds-hp-disk.c +++ b/drivers/leds/leds-hp-disk.c @@ -68,25 +68,9 @@ static struct led_classdev hpled_led = { .name = "hp:red:hddprotection", .default_trigger = "heartbeat", .brightness_set = hpled_set, + .flags = LED_CORE_SUSPENDRESUME, }; -#ifdef CONFIG_PM -static int hpled_suspend(struct acpi_device *dev, pm_message_t state) -{ - led_classdev_suspend(&hpled_led); - return 0; -} - -static int hpled_resume(struct acpi_device *dev) -{ - led_classdev_resume(&hpled_led); - return 0; -} -#else -#define hpled_suspend NULL -#define hpled_resume NULL -#endif - static int hpled_add(struct acpi_device *device) { int ret; @@ -121,8 +105,6 @@ static struct acpi_driver leds_hp_driver = { .ops = { .add = hpled_add, .remove = hpled_remove, - .suspend = hpled_suspend, - .resume = hpled_resume, } }; diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c index e8fb1baf8a50..e4ce1fd46338 100644 --- a/drivers/leds/leds-hp6xx.c +++ b/drivers/leds/leds-hp6xx.c @@ -45,30 +45,16 @@ static struct led_classdev hp6xx_red_led = { .name = "hp6xx:red", .default_trigger = "hp6xx-charge", .brightness_set = hp6xxled_red_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev hp6xx_green_led = { .name = "hp6xx:green", .default_trigger = "ide-disk", .brightness_set = hp6xxled_green_set, + .flags = LED_CORE_SUSPENDRESUME, }; -#ifdef CONFIG_PM -static int hp6xxled_suspend(struct platform_device *dev, pm_message_t state) -{ - led_classdev_suspend(&hp6xx_red_led); - led_classdev_suspend(&hp6xx_green_led); - return 0; -} - -static int hp6xxled_resume(struct platform_device *dev) -{ - led_classdev_resume(&hp6xx_red_led); - led_classdev_resume(&hp6xx_green_led); - return 0; -} -#endif - static int hp6xxled_probe(struct platform_device *pdev) { int ret; @@ -98,10 +84,6 @@ MODULE_ALIAS("platform:hp6xx-led"); static struct platform_driver hp6xxled_driver = { .probe = hp6xxled_probe, .remove = hp6xxled_remove, -#ifdef CONFIG_PM - .suspend = hp6xxled_suspend, - .resume = hp6xxled_resume, -#endif .driver = { .name = "hp6xx-led", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c index 054360473c94..93987a12da49 100644 --- a/drivers/leds/leds-net48xx.c +++ b/drivers/leds/leds-net48xx.c @@ -33,26 +33,9 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev, static struct led_classdev net48xx_error_led = { .name = "net48xx::error", .brightness_set = net48xx_error_led_set, + .flags = LED_CORE_SUSPENDRESUME, }; -#ifdef CONFIG_PM -static int net48xx_led_suspend(struct platform_device *dev, - pm_message_t state) -{ - led_classdev_suspend(&net48xx_error_led); - return 0; -} - -static int net48xx_led_resume(struct platform_device *dev) -{ - led_classdev_resume(&net48xx_error_led); - return 0; -} -#else -#define net48xx_led_suspend NULL -#define net48xx_led_resume NULL -#endif - static int net48xx_led_probe(struct platform_device *pdev) { return led_classdev_register(&pdev->dev, &net48xx_error_led); @@ -67,8 +50,6 @@ static int net48xx_led_remove(struct platform_device *pdev) static struct platform_driver net48xx_led_driver = { .probe = net48xx_led_probe, .remove = net48xx_led_remove, - .suspend = net48xx_led_suspend, - .resume = net48xx_led_resume, .driver = { .name = DRVNAME, .owner = THIS_MODULE, diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 4064d4f6b33b..76ec7498e2d5 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -16,6 +16,7 @@ #include <linux/leds.h> #include <linux/input.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <linux/leds-pca9532.h> static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; @@ -34,6 +35,7 @@ struct pca9532_data { struct pca9532_led leds[16]; struct mutex update_lock; struct input_dev *idev; + struct work_struct work; u8 pwm[2]; u8 psc[2]; }; @@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = { * as a compromise we average one pwm to the values requested by all * leds that are not ON/OFF. * */ -static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, +static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, enum led_brightness value) { int a = 0, b = 0, i = 0; @@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, b = b/a; if (b > 0xFF) return -EINVAL; - mutex_lock(&data->update_lock); data->pwm[pwm] = b; + data->psc[pwm] = blink; + return 0; +} + +static int pca9532_setpwm(struct i2c_client *client, int pwm) +{ + struct pca9532_data *data = i2c_get_clientdata(client); + mutex_lock(&data->update_lock); i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), data->pwm[pwm]); - data->psc[pwm] = blink; i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), data->psc[pwm]); mutex_unlock(&data->update_lock); @@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev, led->state = PCA9532_ON; else { led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ - err = pca9532_setpwm(led->client, 0, 0, value); + err = pca9532_calcpwm(led->client, 0, 0, value); if (err) return; /* XXX: led api doesn't allow error code? */ } - pca9532_setled(led); + schedule_work(&led->work); } static int pca9532_set_blink(struct led_classdev *led_cdev, @@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, struct pca9532_led *led = ldev_to_led(led_cdev); struct i2c_client *client = led->client; int psc; + int err = 0; if (*delay_on == 0 && *delay_off == 0) { /* led subsystem ask us for a blink rate */ @@ -148,11 +157,15 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, /* Thecus specific: only use PSC/PWM 0 */ psc = (*delay_on * 152-1)/1000; - return pca9532_setpwm(client, 0, psc, led_cdev->brightness); + err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); + if (err) + return err; + schedule_work(&led->work); + return 0; } -int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, - int value) +static int pca9532_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) { struct pca9532_data *data = input_get_drvdata(dev); @@ -165,13 +178,28 @@ int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, else data->pwm[1] = 0; - dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); + schedule_work(&data->work); + + return 0; +} + +static void pca9532_input_work(struct work_struct *work) +{ + struct pca9532_data *data; + data = container_of(work, struct pca9532_data, work); mutex_lock(&data->update_lock); i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), data->pwm[1]); mutex_unlock(&data->update_lock); +} - return 0; +static void pca9532_led_work(struct work_struct *work) +{ + struct pca9532_led *led; + led = container_of(work, struct pca9532_led, work); + if (led->state == PCA9532_PWM0) + pca9532_setpwm(led->client, 0); + pca9532_setled(led); } static int pca9532_configure(struct i2c_client *client, @@ -204,8 +232,9 @@ static int pca9532_configure(struct i2c_client *client, led->ldev.brightness = LED_OFF; led->ldev.brightness_set = pca9532_set_brightness; led->ldev.blink_set = pca9532_set_blink; - if (led_classdev_register(&client->dev, - &led->ldev) < 0) { + INIT_WORK(&led->work, pca9532_led_work); + err = led_classdev_register(&client->dev, &led->ldev); + if (err < 0) { dev_err(&client->dev, "couldn't register LED %s\n", led->name); @@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client, BIT_MASK(SND_TONE); data->idev->event = pca9532_event; input_set_drvdata(data->idev, data); + INIT_WORK(&data->work, pca9532_input_work); err = input_register_device(data->idev); if (err) { input_free_device(data->idev); + cancel_work_sync(&data->work); data->idev = NULL; goto exit; } @@ -252,18 +283,19 @@ exit: break; case PCA9532_TYPE_LED: led_classdev_unregister(&data->leds[i].ldev); + cancel_work_sync(&data->leds[i].work); break; case PCA9532_TYPE_N2100_BEEP: if (data->idev != NULL) { input_unregister_device(data->idev); input_free_device(data->idev); + cancel_work_sync(&data->work); data->idev = NULL; } break; } return err; - } static int pca9532_probe(struct i2c_client *client, @@ -271,12 +303,16 @@ static int pca9532_probe(struct i2c_client *client, { struct pca9532_data *data = i2c_get_clientdata(client); struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; + int err; + + if (!pca9532_pdata) + return -EIO; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -285,12 +321,13 @@ static int pca9532_probe(struct i2c_client *client, data->client = client; mutex_init(&data->update_lock); - if (pca9532_pdata == NULL) - return -EIO; - - pca9532_configure(client, data, pca9532_pdata); - return 0; + err = pca9532_configure(client, data, pca9532_pdata); + if (err) { + kfree(data); + i2c_set_clientdata(client, NULL); + } + return err; } static int pca9532_remove(struct i2c_client *client) @@ -303,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client) break; case PCA9532_TYPE_LED: led_classdev_unregister(&data->leds[i].ldev); + cancel_work_sync(&data->leds[i].work); break; case PCA9532_TYPE_N2100_BEEP: if (data->idev != NULL) { input_unregister_device(data->idev); input_free_device(data->idev); + cancel_work_sync(&data->work); data->idev = NULL; } break; diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 25a07f2643ad..4d81131542ae 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -82,6 +82,7 @@ static int s3c24xx_led_probe(struct platform_device *dev) led->cdev.brightness_set = s3c24xx_led_set; led->cdev.default_trigger = pdata->def_trigger; led->cdev.name = pdata->name; + led->cdev.flags |= LED_CORE_SUSPENDRESUME; led->pdata = pdata; @@ -111,33 +112,9 @@ static int s3c24xx_led_probe(struct platform_device *dev) return ret; } - -#ifdef CONFIG_PM -static int s3c24xx_led_suspend(struct platform_device *dev, pm_message_t state) -{ - struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); - - led_classdev_suspend(&led->cdev); - return 0; -} - -static int s3c24xx_led_resume(struct platform_device *dev) -{ - struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); - - led_classdev_resume(&led->cdev); - return 0; -} -#else -#define s3c24xx_led_suspend NULL -#define s3c24xx_led_resume NULL -#endif - static struct platform_driver s3c24xx_led_driver = { .probe = s3c24xx_led_probe, .remove = s3c24xx_led_remove, - .suspend = s3c24xx_led_suspend, - .resume = s3c24xx_led_resume, .driver = { .name = "s3c24xx_led", .owner = THIS_MODULE, diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c new file mode 100644 index 000000000000..38c6bcb07e6c --- /dev/null +++ b/drivers/leds/leds-wm8350.c @@ -0,0 +1,311 @@ +/* + * LED driver for WM8350 driven LEDS. + * + * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC. + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/mfd/wm8350/pmic.h> +#include <linux/regulator/consumer.h> + +/* Microamps */ +static const int isink_cur[] = { + 4, + 5, + 6, + 7, + 8, + 10, + 11, + 14, + 16, + 19, + 23, + 27, + 32, + 39, + 46, + 54, + 65, + 77, + 92, + 109, + 130, + 154, + 183, + 218, + 259, + 308, + 367, + 436, + 518, + 616, + 733, + 872, + 1037, + 1233, + 1466, + 1744, + 2073, + 2466, + 2933, + 3487, + 4147, + 4932, + 5865, + 6975, + 8294, + 9864, + 11730, + 13949, + 16589, + 19728, + 23460, + 27899, + 33178, + 39455, + 46920, + 55798, + 66355, + 78910, + 93840, + 111596, + 132710, + 157820, + 187681, + 223191 +}; + +#define to_wm8350_led(led_cdev) \ + container_of(led_cdev, struct wm8350_led, cdev) + +static void wm8350_led_enable(struct wm8350_led *led) +{ + int ret; + + if (led->enabled) + return; + + ret = regulator_enable(led->isink); + if (ret != 0) { + dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret); + return; + } + + ret = regulator_enable(led->dcdc); + if (ret != 0) { + dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret); + regulator_disable(led->isink); + return; + } + + led->enabled = 1; +} + +static void wm8350_led_disable(struct wm8350_led *led) +{ + int ret; + + if (!led->enabled) + return; + + ret = regulator_disable(led->dcdc); + if (ret != 0) { + dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret); + return; + } + + ret = regulator_disable(led->isink); + if (ret != 0) { + dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret); + regulator_enable(led->dcdc); + return; + } + + led->enabled = 0; +} + +static void led_work(struct work_struct *work) +{ + struct wm8350_led *led = container_of(work, struct wm8350_led, work); + int ret; + int uA; + unsigned long flags; + + mutex_lock(&led->mutex); + + spin_lock_irqsave(&led->value_lock, flags); + + if (led->value == LED_OFF) { + spin_unlock_irqrestore(&led->value_lock, flags); + wm8350_led_disable(led); + goto out; + } + + /* This scales linearly into the index of valid current + * settings which results in a linear scaling of perceived + * brightness due to the non-linear current settings provided + * by the hardware. + */ + uA = (led->max_uA_index * led->value) / LED_FULL; + spin_unlock_irqrestore(&led->value_lock, flags); + BUG_ON(uA >= ARRAY_SIZE(isink_cur)); + + ret = regulator_set_current_limit(led->isink, isink_cur[uA], + isink_cur[uA]); + if (ret != 0) + dev_err(led->cdev.dev, "Failed to set %duA: %d\n", + isink_cur[uA], ret); + + wm8350_led_enable(led); + +out: + mutex_unlock(&led->mutex); +} + +static void wm8350_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct wm8350_led *led = to_wm8350_led(led_cdev); + unsigned long flags; + + spin_lock_irqsave(&led->value_lock, flags); + led->value = value; + schedule_work(&led->work); + spin_unlock_irqrestore(&led->value_lock, flags); +} + +static void wm8350_led_shutdown(struct platform_device *pdev) +{ + struct wm8350_led *led = platform_get_drvdata(pdev); + + mutex_lock(&led->mutex); + led->value = LED_OFF; + wm8350_led_disable(led); + mutex_unlock(&led->mutex); +} + +static int wm8350_led_probe(struct platform_device *pdev) +{ + struct regulator *isink, *dcdc; + struct wm8350_led *led; + struct wm8350_led_platform_data *pdata = pdev->dev.platform_data; + int ret, i; + + if (pdata == NULL) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENODEV; + } + + if (pdata->max_uA < isink_cur[0]) { + dev_err(&pdev->dev, "Invalid maximum current %duA\n", + pdata->max_uA); + return -EINVAL; + } + + isink = regulator_get(&pdev->dev, "led_isink"); + if (IS_ERR(isink)) { + printk(KERN_ERR "%s: cant get ISINK\n", __func__); + return PTR_ERR(isink); + } + + dcdc = regulator_get(&pdev->dev, "led_vcc"); + if (IS_ERR(dcdc)) { + printk(KERN_ERR "%s: cant get DCDC\n", __func__); + ret = PTR_ERR(dcdc); + goto err_isink; + } + + led = kzalloc(sizeof(*led), GFP_KERNEL); + if (led == NULL) { + ret = -ENOMEM; + goto err_dcdc; + } + + led->cdev.brightness_set = wm8350_led_set; + led->cdev.default_trigger = pdata->default_trigger; + led->cdev.name = pdata->name; + led->cdev.flags |= LED_CORE_SUSPENDRESUME; + led->enabled = regulator_is_enabled(isink); + led->isink = isink; + led->dcdc = dcdc; + + for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++) + if (isink_cur[i] >= pdata->max_uA) + break; + led->max_uA_index = i; + if (pdata->max_uA != isink_cur[i]) + dev_warn(&pdev->dev, + "Maximum current %duA is not directly supported," + " check platform data\n", + pdata->max_uA); + + spin_lock_init(&led->value_lock); + mutex_init(&led->mutex); + INIT_WORK(&led->work, led_work); + led->value = LED_OFF; + platform_set_drvdata(pdev, led); + + ret = led_classdev_register(&pdev->dev, &led->cdev); + if (ret < 0) + goto err_led; + + return 0; + + err_led: + kfree(led); + err_dcdc: + regulator_put(dcdc); + err_isink: + regulator_put(isink); + return ret; +} + +static int wm8350_led_remove(struct platform_device *pdev) +{ + struct wm8350_led *led = platform_get_drvdata(pdev); + + led_classdev_unregister(&led->cdev); + flush_scheduled_work(); + wm8350_led_disable(led); + regulator_put(led->dcdc); + regulator_put(led->isink); + kfree(led); + return 0; +} + +static struct platform_driver wm8350_led_driver = { + .driver = { + .name = "wm8350-led", + .owner = THIS_MODULE, + }, + .probe = wm8350_led_probe, + .remove = wm8350_led_remove, + .shutdown = wm8350_led_shutdown, +}; + +static int __devinit wm8350_led_init(void) +{ + return platform_driver_register(&wm8350_led_driver); +} +module_init(wm8350_led_init); + +static void wm8350_led_exit(void) +{ + platform_driver_unregister(&wm8350_led_driver); +} +module_exit(wm8350_led_exit); + +MODULE_AUTHOR("Mark Brown"); +MODULE_DESCRIPTION("WM8350 LED driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-led"); diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c index 2f3aa87f2a1f..2982c86ac4cf 100644 --- a/drivers/leds/leds-wrap.c +++ b/drivers/leds/leds-wrap.c @@ -56,40 +56,21 @@ static struct led_classdev wrap_power_led = { .name = "wrap::power", .brightness_set = wrap_power_led_set, .default_trigger = "default-on", + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev wrap_error_led = { .name = "wrap::error", .brightness_set = wrap_error_led_set, + .flags = LED_CORE_SUSPENDRESUME, }; static struct led_classdev wrap_extra_led = { .name = "wrap::extra", .brightness_set = wrap_extra_led_set, + .flags = LED_CORE_SUSPENDRESUME, }; -#ifdef CONFIG_PM -static int wrap_led_suspend(struct platform_device *dev, - pm_message_t state) -{ - led_classdev_suspend(&wrap_power_led); - led_classdev_suspend(&wrap_error_led); - led_classdev_suspend(&wrap_extra_led); - return 0; -} - -static int wrap_led_resume(struct platform_device *dev) -{ - led_classdev_resume(&wrap_power_led); - led_classdev_resume(&wrap_error_led); - led_classdev_resume(&wrap_extra_led); - return 0; -} -#else -#define wrap_led_suspend NULL -#define wrap_led_resume NULL -#endif - static int wrap_led_probe(struct platform_device *pdev) { int ret; @@ -127,8 +108,6 @@ static int wrap_led_remove(struct platform_device *pdev) static struct platform_driver wrap_led_driver = { .probe = wrap_led_probe, .remove = wrap_led_remove, - .suspend = wrap_led_suspend, - .resume = wrap_led_resume, .driver = { .name = DRVNAME, .owner = THIS_MODULE, diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index db681962d7bb..3d6531396dda 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -199,6 +199,7 @@ err_out: static void timer_trig_deactivate(struct led_classdev *led_cdev) { struct timer_trig_data *timer_data = led_cdev->trigger_data; + unsigned long on = 0, off = 0; if (timer_data) { device_remove_file(led_cdev->dev, &dev_attr_delay_on); @@ -206,6 +207,10 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev) del_timer_sync(&timer_data->timer); kfree(timer_data); } + + /* If there is hardware support for blinking, stop it */ + if (led_cdev->blink_set) + led_cdev->blink_set(led_cdev, &on, &off); } static struct led_trigger timer_led_trigger = { diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index ab7c8e4a61f9..719943763391 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -215,7 +215,6 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, /* choose a good rdev and read the page from there */ mdk_rdev_t *rdev; - struct list_head *tmp; sector_t target; if (!page) @@ -223,7 +222,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, if (!page) return ERR_PTR(-ENOMEM); - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { if (! test_bit(In_sync, &rdev->flags) || test_bit(Faulty, &rdev->flags)) continue; @@ -964,9 +963,11 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) */ page = bitmap->sb_page; offset = sizeof(bitmap_super_t); - read_sb_page(bitmap->mddev, bitmap->offset, - page, - index, count); + if (!file) + read_sb_page(bitmap->mddev, + bitmap->offset, + page, + index, count); } else if (file) { page = read_page(file, index, bitmap, count); offset = 0; diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index f26c1f9a475b..86d9adf90e79 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -283,7 +283,6 @@ static int reconfig(mddev_t *mddev, int layout, int chunk_size) static int run(mddev_t *mddev) { mdk_rdev_t *rdev; - struct list_head *tmp; int i; conf_t *conf = kmalloc(sizeof(*conf), GFP_KERNEL); @@ -296,7 +295,7 @@ static int run(mddev_t *mddev) } conf->nfaults = 0; - rdev_for_each(rdev, tmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) conf->rdev = rdev; mddev->array_sectors = mddev->size * 2; diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 3b90c5c924ec..1e3aea9eecf1 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -105,7 +105,6 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) int i, nb_zone, cnt; sector_t min_sectors; sector_t curr_sector; - struct list_head *tmp; conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t), GFP_KERNEL); @@ -115,7 +114,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) cnt = 0; conf->array_sectors = 0; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { int j = rdev->raid_disk; dev_info_t *disk = conf->disks + j; diff --git a/drivers/md/md.c b/drivers/md/md.c index 1b1d32694f6f..41e2509bf896 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -214,20 +214,33 @@ static inline mddev_t *mddev_get(mddev_t *mddev) return mddev; } +static void mddev_delayed_delete(struct work_struct *ws) +{ + mddev_t *mddev = container_of(ws, mddev_t, del_work); + kobject_del(&mddev->kobj); + kobject_put(&mddev->kobj); +} + static void mddev_put(mddev_t *mddev) { if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock)) return; - if (!mddev->raid_disks && list_empty(&mddev->disks)) { + if (!mddev->raid_disks && list_empty(&mddev->disks) && + !mddev->hold_active) { list_del(&mddev->all_mddevs); - spin_unlock(&all_mddevs_lock); - blk_cleanup_queue(mddev->queue); - if (mddev->sysfs_state) - sysfs_put(mddev->sysfs_state); - mddev->sysfs_state = NULL; - kobject_put(&mddev->kobj); - } else - spin_unlock(&all_mddevs_lock); + if (mddev->gendisk) { + /* we did a probe so need to clean up. + * Call schedule_work inside the spinlock + * so that flush_scheduled_work() after + * mddev_find will succeed in waiting for the + * work to be done. + */ + INIT_WORK(&mddev->del_work, mddev_delayed_delete); + schedule_work(&mddev->del_work); + } else + kfree(mddev); + } + spin_unlock(&all_mddevs_lock); } static mddev_t * mddev_find(dev_t unit) @@ -236,15 +249,50 @@ static mddev_t * mddev_find(dev_t unit) retry: spin_lock(&all_mddevs_lock); - list_for_each_entry(mddev, &all_mddevs, all_mddevs) - if (mddev->unit == unit) { - mddev_get(mddev); + + if (unit) { + list_for_each_entry(mddev, &all_mddevs, all_mddevs) + if (mddev->unit == unit) { + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + kfree(new); + return mddev; + } + + if (new) { + list_add(&new->all_mddevs, &all_mddevs); spin_unlock(&all_mddevs_lock); - kfree(new); - return mddev; + new->hold_active = UNTIL_IOCTL; + return new; } - - if (new) { + } else if (new) { + /* find an unused unit number */ + static int next_minor = 512; + int start = next_minor; + int is_free = 0; + int dev = 0; + while (!is_free) { + dev = MKDEV(MD_MAJOR, next_minor); + next_minor++; + if (next_minor > MINORMASK) + next_minor = 0; + if (next_minor == start) { + /* Oh dear, all in use. */ + spin_unlock(&all_mddevs_lock); + kfree(new); + return NULL; + } + + is_free = 1; + list_for_each_entry(mddev, &all_mddevs, all_mddevs) + if (mddev->unit == dev) { + is_free = 0; + break; + } + } + new->unit = dev; + new->md_minor = MINOR(dev); + new->hold_active = UNTIL_STOP; list_add(&new->all_mddevs, &all_mddevs); spin_unlock(&all_mddevs_lock); return new; @@ -275,16 +323,6 @@ static mddev_t * mddev_find(dev_t unit) new->resync_max = MaxSector; new->level = LEVEL_NONE; - new->queue = blk_alloc_queue(GFP_KERNEL); - if (!new->queue) { - kfree(new); - return NULL; - } - /* Can be unlocked because the queue is new: no concurrency */ - queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, new->queue); - - blk_queue_make_request(new->queue, md_fail_request); - goto retry; } @@ -307,25 +345,23 @@ static inline void mddev_unlock(mddev_t * mddev) static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr) { - mdk_rdev_t * rdev; - struct list_head *tmp; + mdk_rdev_t *rdev; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->desc_nr == nr) return rdev; - } + return NULL; } static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev) { - struct list_head *tmp; mdk_rdev_t *rdev; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->bdev->bd_dev == dev) return rdev; - } + return NULL; } @@ -861,7 +897,6 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) { mdp_super_t *sb; - struct list_head *tmp; mdk_rdev_t *rdev2; int next_spare = mddev->raid_disks; @@ -933,7 +968,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->state |= (1<<MD_SB_BITMAP_PRESENT); sb->disks[0].state = (1<<MD_DISK_REMOVED); - rdev_for_each(rdev2, tmp, mddev) { + list_for_each_entry(rdev2, &mddev->disks, same_set) { mdp_disk_t *d; int desc_nr; if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags) @@ -1259,7 +1294,6 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) { struct mdp_superblock_1 *sb; - struct list_head *tmp; mdk_rdev_t *rdev2; int max_dev, i; /* make rdev->sb match mddev and rdev data. */ @@ -1307,7 +1341,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) } max_dev = 0; - rdev_for_each(rdev2, tmp, mddev) + list_for_each_entry(rdev2, &mddev->disks, same_set) if (rdev2->desc_nr+1 > max_dev) max_dev = rdev2->desc_nr+1; @@ -1316,7 +1350,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) for (i=0; i<max_dev;i++) sb->dev_roles[i] = cpu_to_le16(0xfffe); - rdev_for_each(rdev2, tmp, mddev) { + list_for_each_entry(rdev2, &mddev->disks, same_set) { i = rdev2->desc_nr; if (test_bit(Faulty, &rdev2->flags)) sb->dev_roles[i] = cpu_to_le16(0xfffe); @@ -1466,6 +1500,9 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); + + /* May as well allow recovery to be retried once */ + mddev->recovery_disabled = 0; return 0; fail: @@ -1571,8 +1608,7 @@ static void kick_rdev_from_array(mdk_rdev_t * rdev) static void export_array(mddev_t *mddev) { - struct list_head *tmp; - mdk_rdev_t *rdev; + mdk_rdev_t *rdev, *tmp; rdev_for_each(rdev, tmp, mddev) { if (!rdev->mddev) { @@ -1593,7 +1629,7 @@ static void print_desc(mdp_disk_t *desc) desc->major,desc->minor,desc->raid_disk,desc->state); } -static void print_sb(mdp_super_t *sb) +static void print_sb_90(mdp_super_t *sb) { int i; @@ -1624,10 +1660,57 @@ static void print_sb(mdp_super_t *sb) } printk(KERN_INFO "md: THIS: "); print_desc(&sb->this_disk); - } -static void print_rdev(mdk_rdev_t *rdev) +static void print_sb_1(struct mdp_superblock_1 *sb) +{ + __u8 *uuid; + + uuid = sb->set_uuid; + printk(KERN_INFO "md: SB: (V:%u) (F:0x%08x) Array-ID:<%02x%02x%02x%02x" + ":%02x%02x:%02x%02x:%02x%02x:%02x%02x%02x%02x%02x%02x>\n" + KERN_INFO "md: Name: \"%s\" CT:%llu\n", + le32_to_cpu(sb->major_version), + le32_to_cpu(sb->feature_map), + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15], + sb->set_name, + (unsigned long long)le64_to_cpu(sb->ctime) + & MD_SUPERBLOCK_1_TIME_SEC_MASK); + + uuid = sb->device_uuid; + printk(KERN_INFO "md: L%u SZ%llu RD:%u LO:%u CS:%u DO:%llu DS:%llu SO:%llu" + " RO:%llu\n" + KERN_INFO "md: Dev:%08x UUID: %02x%02x%02x%02x:%02x%02x:%02x%02x:%02x%02x" + ":%02x%02x%02x%02x%02x%02x\n" + KERN_INFO "md: (F:0x%08x) UT:%llu Events:%llu ResyncOffset:%llu CSUM:0x%08x\n" + KERN_INFO "md: (MaxDev:%u) \n", + le32_to_cpu(sb->level), + (unsigned long long)le64_to_cpu(sb->size), + le32_to_cpu(sb->raid_disks), + le32_to_cpu(sb->layout), + le32_to_cpu(sb->chunksize), + (unsigned long long)le64_to_cpu(sb->data_offset), + (unsigned long long)le64_to_cpu(sb->data_size), + (unsigned long long)le64_to_cpu(sb->super_offset), + (unsigned long long)le64_to_cpu(sb->recovery_offset), + le32_to_cpu(sb->dev_number), + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15], + sb->devflags, + (unsigned long long)le64_to_cpu(sb->utime) & MD_SUPERBLOCK_1_TIME_SEC_MASK, + (unsigned long long)le64_to_cpu(sb->events), + (unsigned long long)le64_to_cpu(sb->resync_offset), + le32_to_cpu(sb->sb_csum), + le32_to_cpu(sb->max_dev) + ); +} + +static void print_rdev(mdk_rdev_t *rdev, int major_version) { char b[BDEVNAME_SIZE]; printk(KERN_INFO "md: rdev %s, SZ:%08llu F:%d S:%d DN:%u\n", @@ -1635,15 +1718,22 @@ static void print_rdev(mdk_rdev_t *rdev) test_bit(Faulty, &rdev->flags), test_bit(In_sync, &rdev->flags), rdev->desc_nr); if (rdev->sb_loaded) { - printk(KERN_INFO "md: rdev superblock:\n"); - print_sb((mdp_super_t*)page_address(rdev->sb_page)); + printk(KERN_INFO "md: rdev superblock (MJ:%d):\n", major_version); + switch (major_version) { + case 0: + print_sb_90((mdp_super_t*)page_address(rdev->sb_page)); + break; + case 1: + print_sb_1((struct mdp_superblock_1 *)page_address(rdev->sb_page)); + break; + } } else printk(KERN_INFO "md: no rdev superblock!\n"); } static void md_print_devices(void) { - struct list_head *tmp, *tmp2; + struct list_head *tmp; mdk_rdev_t *rdev; mddev_t *mddev; char b[BDEVNAME_SIZE]; @@ -1658,12 +1748,12 @@ static void md_print_devices(void) bitmap_print_sb(mddev->bitmap); else printk("%s: ", mdname(mddev)); - rdev_for_each(rdev, tmp2, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) printk("<%s>", bdevname(rdev->bdev,b)); printk("\n"); - rdev_for_each(rdev, tmp2, mddev) - print_rdev(rdev); + list_for_each_entry(rdev, &mddev->disks, same_set) + print_rdev(rdev, mddev->major_version); } printk("md: **********************************\n"); printk("\n"); @@ -1679,9 +1769,8 @@ static void sync_sbs(mddev_t * mddev, int nospares) * with the rest of the array) */ mdk_rdev_t *rdev; - struct list_head *tmp; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { if (rdev->sb_events == mddev->events || (nospares && rdev->raid_disk < 0 && @@ -1699,7 +1788,6 @@ static void sync_sbs(mddev_t * mddev, int nospares) static void md_update_sb(mddev_t * mddev, int force_change) { - struct list_head *tmp; mdk_rdev_t *rdev; int sync_req; int nospares = 0; @@ -1790,7 +1878,7 @@ repeat: mdname(mddev),mddev->in_sync); bitmap_update_sb(mddev->bitmap); - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { char b[BDEVNAME_SIZE]; dprintk(KERN_INFO "md: "); if (rdev->sb_loaded != 1) @@ -1999,7 +2087,6 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) md_wakeup_thread(rdev->mddev->thread); } else if (rdev->mddev->pers) { mdk_rdev_t *rdev2; - struct list_head *tmp; /* Activating a spare .. or possibly reactivating * if we every get bitmaps working here. */ @@ -2010,7 +2097,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) if (rdev->mddev->pers->hot_add_disk == NULL) return -EINVAL; - rdev_for_each(rdev2, tmp, rdev->mddev) + list_for_each_entry(rdev2, &rdev->mddev->disks, same_set) if (rdev2->raid_disk == slot) return -EEXIST; @@ -2125,14 +2212,14 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) */ mddev_t *mddev; int overlap = 0; - struct list_head *tmp, *tmp2; + struct list_head *tmp; mddev_unlock(my_mddev); for_each_mddev(mddev, tmp) { mdk_rdev_t *rdev2; mddev_lock(mddev); - rdev_for_each(rdev2, tmp2, mddev) + list_for_each_entry(rdev2, &mddev->disks, same_set) if (test_bit(AllReserved, &rdev2->flags) || (rdev->bdev == rdev2->bdev && rdev != rdev2 && @@ -2328,8 +2415,7 @@ abort_free: static void analyze_sbs(mddev_t * mddev) { int i; - struct list_head *tmp; - mdk_rdev_t *rdev, *freshest; + mdk_rdev_t *rdev, *freshest, *tmp; char b[BDEVNAME_SIZE]; freshest = NULL; @@ -3046,7 +3132,7 @@ action_store(mddev_t *mddev, const char *page, size_t len) } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); + sysfs_notify_dirent(mddev->sysfs_action); return len; } @@ -3404,6 +3490,8 @@ md_attr_store(struct kobject *kobj, struct attribute *attr, if (!capable(CAP_SYS_ADMIN)) return -EACCES; rv = mddev_lock(mddev); + if (mddev->hold_active == UNTIL_IOCTL) + mddev->hold_active = 0; if (!rv) { rv = entry->store(mddev, page, length); mddev_unlock(mddev); @@ -3414,6 +3502,17 @@ md_attr_store(struct kobject *kobj, struct attribute *attr, static void md_free(struct kobject *ko) { mddev_t *mddev = container_of(ko, mddev_t, kobj); + + if (mddev->sysfs_state) + sysfs_put(mddev->sysfs_state); + + if (mddev->gendisk) { + del_gendisk(mddev->gendisk); + put_disk(mddev->gendisk); + } + if (mddev->queue) + blk_cleanup_queue(mddev->queue); + kfree(mddev); } @@ -3429,34 +3528,74 @@ static struct kobj_type md_ktype = { int mdp_major = 0; -static struct kobject *md_probe(dev_t dev, int *part, void *data) +static int md_alloc(dev_t dev, char *name) { static DEFINE_MUTEX(disks_mutex); mddev_t *mddev = mddev_find(dev); struct gendisk *disk; - int partitioned = (MAJOR(dev) != MD_MAJOR); - int shift = partitioned ? MdpMinorShift : 0; - int unit = MINOR(dev) >> shift; + int partitioned; + int shift; + int unit; int error; if (!mddev) - return NULL; + return -ENODEV; + + partitioned = (MAJOR(mddev->unit) != MD_MAJOR); + shift = partitioned ? MdpMinorShift : 0; + unit = MINOR(mddev->unit) >> shift; + + /* wait for any previous instance if this device + * to be completed removed (mddev_delayed_delete). + */ + flush_scheduled_work(); mutex_lock(&disks_mutex); if (mddev->gendisk) { mutex_unlock(&disks_mutex); mddev_put(mddev); - return NULL; + return -EEXIST; + } + + if (name) { + /* Need to ensure that 'name' is not a duplicate. + */ + mddev_t *mddev2; + spin_lock(&all_mddevs_lock); + + list_for_each_entry(mddev2, &all_mddevs, all_mddevs) + if (mddev2->gendisk && + strcmp(mddev2->gendisk->disk_name, name) == 0) { + spin_unlock(&all_mddevs_lock); + return -EEXIST; + } + spin_unlock(&all_mddevs_lock); + } + + mddev->queue = blk_alloc_queue(GFP_KERNEL); + if (!mddev->queue) { + mutex_unlock(&disks_mutex); + mddev_put(mddev); + return -ENOMEM; } + /* Can be unlocked because the queue is new: no concurrency */ + queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue); + + blk_queue_make_request(mddev->queue, md_fail_request); + disk = alloc_disk(1 << shift); if (!disk) { mutex_unlock(&disks_mutex); + blk_cleanup_queue(mddev->queue); + mddev->queue = NULL; mddev_put(mddev); - return NULL; + return -ENOMEM; } - disk->major = MAJOR(dev); + disk->major = MAJOR(mddev->unit); disk->first_minor = unit << shift; - if (partitioned) + if (name) + strcpy(disk->disk_name, name); + else if (partitioned) sprintf(disk->disk_name, "md_d%d", unit); else sprintf(disk->disk_name, "md%d", unit); @@ -3464,7 +3603,7 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) disk->private_data = mddev; disk->queue = mddev->queue; /* Allow extended partitions. This makes the - * 'mdp' device redundant, but we can really + * 'mdp' device redundant, but we can't really * remove it now. */ disk->flags |= GENHD_FL_EXT_DEVT; @@ -3480,9 +3619,35 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) kobject_uevent(&mddev->kobj, KOBJ_ADD); mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state"); } + mddev_put(mddev); + return 0; +} + +static struct kobject *md_probe(dev_t dev, int *part, void *data) +{ + md_alloc(dev, NULL); return NULL; } +static int add_named_array(const char *val, struct kernel_param *kp) +{ + /* val must be "md_*" where * is not all digits. + * We allocate an array with a large free minor number, and + * set the name to val. val must not already be an active name. + */ + int len = strlen(val); + char buf[DISK_NAME_LEN]; + + while (len && val[len-1] == '\n') + len--; + if (len >= DISK_NAME_LEN) + return -E2BIG; + strlcpy(buf, val, len+1); + if (strncmp(buf, "md_", 3) != 0) + return -EINVAL; + return md_alloc(0, buf); +} + static void md_safemode_timeout(unsigned long data) { mddev_t *mddev = (mddev_t *) data; @@ -3501,7 +3666,6 @@ static int do_md_run(mddev_t * mddev) { int err; int chunk_size; - struct list_head *tmp; mdk_rdev_t *rdev; struct gendisk *disk; struct mdk_personality *pers; @@ -3540,7 +3704,7 @@ static int do_md_run(mddev_t * mddev) } /* devices must have minimum size of one chunk */ - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { if (test_bit(Faulty, &rdev->flags)) continue; if (rdev->size < chunk_size / 1024) { @@ -3565,7 +3729,7 @@ static int do_md_run(mddev_t * mddev) * the only valid external interface is through the md * device. */ - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { if (test_bit(Faulty, &rdev->flags)) continue; sync_blockdev(rdev->bdev); @@ -3630,10 +3794,10 @@ static int do_md_run(mddev_t * mddev) */ char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; mdk_rdev_t *rdev2; - struct list_head *tmp2; int warned = 0; - rdev_for_each(rdev, tmp, mddev) { - rdev_for_each(rdev2, tmp2, mddev) { + + list_for_each_entry(rdev, &mddev->disks, same_set) + list_for_each_entry(rdev2, &mddev->disks, same_set) { if (rdev < rdev2 && rdev->bdev->bd_contains == rdev2->bdev->bd_contains) { @@ -3647,7 +3811,7 @@ static int do_md_run(mddev_t * mddev) warned = 1; } } - } + if (warned) printk(KERN_WARNING "True protection against single-disk" @@ -3684,6 +3848,7 @@ static int do_md_run(mddev_t * mddev) printk(KERN_WARNING "md: cannot register extra attributes for %s\n", mdname(mddev)); + mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action"); } else if (mddev->ro == 2) /* auto-readonly not meaningful */ mddev->ro = 0; @@ -3694,7 +3859,7 @@ static int do_md_run(mddev_t * mddev) mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */ mddev->in_sync = 1; - rdev_for_each(rdev, tmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk >= 0) { char nm[20]; sprintf(nm, "rd%d", rdev->raid_disk); @@ -3725,9 +3890,8 @@ static int do_md_run(mddev_t * mddev) * it will remove the drives and not do the right thing */ if (mddev->degraded && !mddev->sync_thread) { - struct list_head *rtmp; int spares = 0; - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk >= 0 && !test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) @@ -3754,7 +3918,8 @@ static int do_md_run(mddev_t * mddev) mddev->changed = 1; md_new_event(mddev); sysfs_notify_dirent(mddev->sysfs_state); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); + if (mddev->sysfs_action) + sysfs_notify_dirent(mddev->sysfs_action); sysfs_notify(&mddev->kobj, NULL, "degraded"); kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); return 0; @@ -3854,9 +4019,12 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) mddev->queue->merge_bvec_fn = NULL; mddev->queue->unplug_fn = NULL; mddev->queue->backing_dev_info.congested_fn = NULL; - if (mddev->pers->sync_request) + if (mddev->pers->sync_request) { sysfs_remove_group(&mddev->kobj, &md_redundancy_group); - + if (mddev->sysfs_action) + sysfs_put(mddev->sysfs_action); + mddev->sysfs_action = NULL; + } module_put(mddev->pers->owner); mddev->pers = NULL; /* tell userspace to handle 'inactive' */ @@ -3883,7 +4051,6 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) */ if (mode == 0) { mdk_rdev_t *rdev; - struct list_head *tmp; printk(KERN_INFO "md: %s stopped.\n", mdname(mddev)); @@ -3895,7 +4062,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) } mddev->bitmap_offset = 0; - rdev_for_each(rdev, tmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk >= 0) { char nm[20]; sprintf(nm, "rd%d", rdev->raid_disk); @@ -3941,6 +4108,8 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) mddev->barriers_work = 0; mddev->safemode = 0; kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); + if (mddev->hold_active == UNTIL_STOP) + mddev->hold_active = 0; } else if (mddev->pers) printk(KERN_INFO "md: %s switched to read-only mode.\n", @@ -3956,7 +4125,6 @@ out: static void autorun_array(mddev_t *mddev) { mdk_rdev_t *rdev; - struct list_head *tmp; int err; if (list_empty(&mddev->disks)) @@ -3964,7 +4132,7 @@ static void autorun_array(mddev_t *mddev) printk(KERN_INFO "md: running: "); - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { char b[BDEVNAME_SIZE]; printk("<%s>", bdevname(rdev->bdev,b)); } @@ -3991,8 +4159,7 @@ static void autorun_array(mddev_t *mddev) */ static void autorun_devices(int part) { - struct list_head *tmp; - mdk_rdev_t *rdev0, *rdev; + mdk_rdev_t *rdev0, *rdev, *tmp; mddev_t *mddev; char b[BDEVNAME_SIZE]; @@ -4007,7 +4174,7 @@ static void autorun_devices(int part) printk(KERN_INFO "md: considering %s ...\n", bdevname(rdev0->bdev,b)); INIT_LIST_HEAD(&candidates); - rdev_for_each_list(rdev, tmp, pending_raid_disks) + rdev_for_each_list(rdev, tmp, &pending_raid_disks) if (super_90_load(rdev, rdev0, 0) >= 0) { printk(KERN_INFO "md: adding %s ...\n", bdevname(rdev->bdev,b)); @@ -4053,7 +4220,7 @@ static void autorun_devices(int part) } else { printk(KERN_INFO "md: created %s\n", mdname(mddev)); mddev->persistent = 1; - rdev_for_each_list(rdev, tmp, candidates) { + rdev_for_each_list(rdev, tmp, &candidates) { list_del_init(&rdev->same_set); if (bind_rdev_to_array(rdev, mddev)) export_rdev(rdev); @@ -4064,7 +4231,7 @@ static void autorun_devices(int part) /* on success, candidates will be empty, on error * it won't... */ - rdev_for_each_list(rdev, tmp, candidates) { + rdev_for_each_list(rdev, tmp, &candidates) { list_del_init(&rdev->same_set); export_rdev(rdev); } @@ -4093,10 +4260,9 @@ static int get_array_info(mddev_t * mddev, void __user * arg) mdu_array_info_t info; int nr,working,active,failed,spare; mdk_rdev_t *rdev; - struct list_head *tmp; nr=working=active=failed=spare=0; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { nr++; if (test_bit(Faulty, &rdev->flags)) failed++; @@ -4614,9 +4780,8 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) static int update_size(mddev_t *mddev, sector_t num_sectors) { - mdk_rdev_t * rdev; + mdk_rdev_t *rdev; int rv; - struct list_head *tmp; int fit = (num_sectors == 0); if (mddev->pers->resize == NULL) @@ -4638,7 +4803,7 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) * grow, and re-add. */ return -EBUSY; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { sector_t avail; avail = rdev->size * 2; @@ -5000,6 +5165,9 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, done_unlock: abort_unlock: + if (mddev->hold_active == UNTIL_IOCTL && + err != -EINVAL) + mddev->hold_active = 0; mddev_unlock(mddev); return err; @@ -5016,14 +5184,25 @@ static int md_open(struct block_device *bdev, fmode_t mode) * Succeed if we can lock the mddev, which confirms that * it isn't being stopped right now. */ - mddev_t *mddev = bdev->bd_disk->private_data; + mddev_t *mddev = mddev_find(bdev->bd_dev); int err; + if (mddev->gendisk != bdev->bd_disk) { + /* we are racing with mddev_put which is discarding this + * bd_disk. + */ + mddev_put(mddev); + /* Wait until bdev->bd_disk is definitely gone */ + flush_scheduled_work(); + /* Then retry the open from the top */ + return -ERESTARTSYS; + } + BUG_ON(mddev != bdev->bd_disk->private_data); + if ((err = mutex_lock_interruptible_nested(&mddev->reconfig_mutex, 1))) goto out; err = 0; - mddev_get(mddev); atomic_inc(&mddev->openers); mddev_unlock(mddev); @@ -5187,11 +5366,10 @@ static void status_unused(struct seq_file *seq) { int i = 0; mdk_rdev_t *rdev; - struct list_head *tmp; seq_printf(seq, "unused devices: "); - rdev_for_each_list(rdev, tmp, pending_raid_disks) { + list_for_each_entry(rdev, &pending_raid_disks, same_set) { char b[BDEVNAME_SIZE]; i++; seq_printf(seq, "%s ", @@ -5350,7 +5528,6 @@ static int md_seq_show(struct seq_file *seq, void *v) { mddev_t *mddev = v; sector_t size; - struct list_head *tmp2; mdk_rdev_t *rdev; struct mdstat_info *mi = seq->private; struct bitmap *bitmap; @@ -5387,7 +5564,7 @@ static int md_seq_show(struct seq_file *seq, void *v) } size = 0; - rdev_for_each(rdev, tmp2, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { char b[BDEVNAME_SIZE]; seq_printf(seq, " %s[%d]", bdevname(rdev->bdev,b), rdev->desc_nr); @@ -5694,7 +5871,6 @@ void md_do_sync(mddev_t *mddev) struct list_head *tmp; sector_t last_check; int skipped = 0; - struct list_head *rtmp; mdk_rdev_t *rdev; char *desc; @@ -5799,7 +5975,7 @@ void md_do_sync(mddev_t *mddev) /* recovery follows the physical size of devices */ max_sectors = mddev->size << 1; j = MaxSector; - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk >= 0 && !test_bit(Faulty, &rdev->flags) && !test_bit(In_sync, &rdev->flags) && @@ -5949,7 +6125,7 @@ void md_do_sync(mddev_t *mddev) } else { if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) mddev->curr_resync = MaxSector; - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk >= 0 && !test_bit(Faulty, &rdev->flags) && !test_bit(In_sync, &rdev->flags) && @@ -5985,10 +6161,9 @@ EXPORT_SYMBOL_GPL(md_do_sync); static int remove_and_add_spares(mddev_t *mddev) { mdk_rdev_t *rdev; - struct list_head *rtmp; int spares = 0; - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk >= 0 && !test_bit(Blocked, &rdev->flags) && (test_bit(Faulty, &rdev->flags) || @@ -6003,8 +6178,8 @@ static int remove_and_add_spares(mddev_t *mddev) } } - if (mddev->degraded && ! mddev->ro) { - rdev_for_each(rdev, rtmp, mddev) { + if (mddev->degraded && ! mddev->ro && !mddev->recovery_disabled) { + list_for_each_entry(rdev, &mddev->disks, same_set) { if (rdev->raid_disk >= 0 && !test_bit(In_sync, &rdev->flags) && !test_bit(Blocked, &rdev->flags)) @@ -6056,7 +6231,6 @@ static int remove_and_add_spares(mddev_t *mddev) void md_check_recovery(mddev_t *mddev) { mdk_rdev_t *rdev; - struct list_head *rtmp; if (mddev->bitmap) @@ -6120,7 +6294,7 @@ void md_check_recovery(mddev_t *mddev) if (mddev->flags) md_update_sb(mddev, 0); - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (test_and_clear_bit(StateChanged, &rdev->flags)) sysfs_notify_dirent(rdev->sysfs_state); @@ -6149,13 +6323,13 @@ void md_check_recovery(mddev_t *mddev) * information must be scrapped */ if (!mddev->degraded) - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) rdev->saved_raid_disk = -1; mddev->recovery = 0; /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); + sysfs_notify_dirent(mddev->sysfs_action); md_new_event(mddev); goto unlock; } @@ -6216,7 +6390,7 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; } else md_wakeup_thread(mddev->sync_thread); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); + sysfs_notify_dirent(mddev->sysfs_action); md_new_event(mddev); } unlock: @@ -6224,7 +6398,8 @@ void md_check_recovery(mddev_t *mddev) clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery); if (test_and_clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery)) - sysfs_notify(&mddev->kobj, NULL, "sync_action"); + if (mddev->sysfs_action) + sysfs_notify_dirent(mddev->sysfs_action); } mddev_unlock(mddev); } @@ -6386,14 +6561,8 @@ static __exit void md_exit(void) unregister_sysctl_table(raid_table_header); remove_proc_entry("mdstat", NULL); for_each_mddev(mddev, tmp) { - struct gendisk *disk = mddev->gendisk; - if (!disk) - continue; export_array(mddev); - del_gendisk(disk); - put_disk(disk); - mddev->gendisk = NULL; - mddev_put(mddev); + mddev->hold_active = 0; } } @@ -6418,6 +6587,7 @@ static int set_ro(const char *val, struct kernel_param *kp) module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR); module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR); +module_param_call(new_array, add_named_array, NULL, NULL, S_IWUSR); EXPORT_SYMBOL(register_md_personality); EXPORT_SYMBOL(unregister_md_personality); diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index d4ac47d11279..f6d08f241671 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -408,7 +408,6 @@ static int multipath_run (mddev_t *mddev) int disk_idx; struct multipath_info *disk; mdk_rdev_t *rdev; - struct list_head *tmp; if (mddev->level != LEVEL_MULTIPATH) { printk("multipath: %s: raid level not set to multipath IO (%d)\n", @@ -441,7 +440,7 @@ static int multipath_run (mddev_t *mddev) } conf->working_disks = 0; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { disk_idx = rdev->raid_disk; if (disk_idx < 0 || disk_idx >= mddev->raid_disks) diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 8ac6488ad0dc..c605ba805586 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -53,11 +53,10 @@ static int raid0_congested(void *data, int bits) static int create_strip_zones (mddev_t *mddev) { int i, c, j; - sector_t current_offset, curr_zone_offset; + sector_t current_start, curr_zone_start; sector_t min_spacing; raid0_conf_t *conf = mddev_to_conf(mddev); mdk_rdev_t *smallest, *rdev1, *rdev2, *rdev; - struct list_head *tmp1, *tmp2; struct strip_zone *zone; int cnt; char b[BDEVNAME_SIZE]; @@ -67,19 +66,19 @@ static int create_strip_zones (mddev_t *mddev) */ conf->nr_strip_zones = 0; - rdev_for_each(rdev1, tmp1, mddev) { - printk("raid0: looking at %s\n", + list_for_each_entry(rdev1, &mddev->disks, same_set) { + printk(KERN_INFO "raid0: looking at %s\n", bdevname(rdev1->bdev,b)); c = 0; - rdev_for_each(rdev2, tmp2, mddev) { - printk("raid0: comparing %s(%llu)", + list_for_each_entry(rdev2, &mddev->disks, same_set) { + printk(KERN_INFO "raid0: comparing %s(%llu)", bdevname(rdev1->bdev,b), (unsigned long long)rdev1->size); - printk(" with %s(%llu)\n", + printk(KERN_INFO " with %s(%llu)\n", bdevname(rdev2->bdev,b), (unsigned long long)rdev2->size); if (rdev2 == rdev1) { - printk("raid0: END\n"); + printk(KERN_INFO "raid0: END\n"); break; } if (rdev2->size == rdev1->size) @@ -88,19 +87,20 @@ static int create_strip_zones (mddev_t *mddev) * Not unique, don't count it as a new * group */ - printk("raid0: EQUAL\n"); + printk(KERN_INFO "raid0: EQUAL\n"); c = 1; break; } - printk("raid0: NOT EQUAL\n"); + printk(KERN_INFO "raid0: NOT EQUAL\n"); } if (!c) { - printk("raid0: ==> UNIQUE\n"); + printk(KERN_INFO "raid0: ==> UNIQUE\n"); conf->nr_strip_zones++; - printk("raid0: %d zones\n", conf->nr_strip_zones); + printk(KERN_INFO "raid0: %d zones\n", + conf->nr_strip_zones); } } - printk("raid0: FINAL %d zones\n", conf->nr_strip_zones); + printk(KERN_INFO "raid0: FINAL %d zones\n", conf->nr_strip_zones); conf->strip_zone = kzalloc(sizeof(struct strip_zone)* conf->nr_strip_zones, GFP_KERNEL); @@ -119,16 +119,17 @@ static int create_strip_zones (mddev_t *mddev) cnt = 0; smallest = NULL; zone->dev = conf->devlist; - rdev_for_each(rdev1, tmp1, mddev) { + list_for_each_entry(rdev1, &mddev->disks, same_set) { int j = rdev1->raid_disk; if (j < 0 || j >= mddev->raid_disks) { - printk("raid0: bad disk number %d - aborting!\n", j); + printk(KERN_ERR "raid0: bad disk number %d - " + "aborting!\n", j); goto abort; } if (zone->dev[j]) { - printk("raid0: multiple devices for %d - aborting!\n", - j); + printk(KERN_ERR "raid0: multiple devices for %d - " + "aborting!\n", j); goto abort; } zone->dev[j] = rdev1; @@ -149,16 +150,16 @@ static int create_strip_zones (mddev_t *mddev) cnt++; } if (cnt != mddev->raid_disks) { - printk("raid0: too few disks (%d of %d) - aborting!\n", - cnt, mddev->raid_disks); + printk(KERN_ERR "raid0: too few disks (%d of %d) - " + "aborting!\n", cnt, mddev->raid_disks); goto abort; } zone->nb_dev = cnt; - zone->size = smallest->size * cnt; - zone->zone_offset = 0; + zone->sectors = smallest->size * cnt * 2; + zone->zone_start = 0; - current_offset = smallest->size; - curr_zone_offset = zone->size; + current_start = smallest->size * 2; + curr_zone_start = zone->sectors; /* now do the other zones */ for (i = 1; i < conf->nr_strip_zones; i++) @@ -166,40 +167,41 @@ static int create_strip_zones (mddev_t *mddev) zone = conf->strip_zone + i; zone->dev = conf->strip_zone[i-1].dev + mddev->raid_disks; - printk("raid0: zone %d\n", i); - zone->dev_offset = current_offset; + printk(KERN_INFO "raid0: zone %d\n", i); + zone->dev_start = current_start; smallest = NULL; c = 0; for (j=0; j<cnt; j++) { char b[BDEVNAME_SIZE]; rdev = conf->strip_zone[0].dev[j]; - printk("raid0: checking %s ...", bdevname(rdev->bdev,b)); - if (rdev->size > current_offset) - { - printk(" contained as device %d\n", c); + printk(KERN_INFO "raid0: checking %s ...", + bdevname(rdev->bdev, b)); + if (rdev->size > current_start / 2) { + printk(KERN_INFO " contained as device %d\n", + c); zone->dev[c] = rdev; c++; if (!smallest || (rdev->size <smallest->size)) { smallest = rdev; - printk(" (%llu) is smallest!.\n", + printk(KERN_INFO " (%llu) is smallest!.\n", (unsigned long long)rdev->size); } } else - printk(" nope.\n"); + printk(KERN_INFO " nope.\n"); } zone->nb_dev = c; - zone->size = (smallest->size - current_offset) * c; - printk("raid0: zone->nb_dev: %d, size: %llu\n", - zone->nb_dev, (unsigned long long)zone->size); + zone->sectors = (smallest->size * 2 - current_start) * c; + printk(KERN_INFO "raid0: zone->nb_dev: %d, sectors: %llu\n", + zone->nb_dev, (unsigned long long)zone->sectors); - zone->zone_offset = curr_zone_offset; - curr_zone_offset += zone->size; + zone->zone_start = curr_zone_start; + curr_zone_start += zone->sectors; - current_offset = smallest->size; - printk("raid0: current zone offset: %llu\n", - (unsigned long long)current_offset); + current_start = smallest->size * 2; + printk(KERN_INFO "raid0: current zone start: %llu\n", + (unsigned long long)current_start); } /* Now find appropriate hash spacing. @@ -210,16 +212,16 @@ static int create_strip_zones (mddev_t *mddev) * strip though as it's size has no bearing on the efficacy of the hash * table. */ - conf->hash_spacing = curr_zone_offset; - min_spacing = curr_zone_offset; + conf->spacing = curr_zone_start; + min_spacing = curr_zone_start; sector_div(min_spacing, PAGE_SIZE/sizeof(struct strip_zone*)); for (i=0; i < conf->nr_strip_zones-1; i++) { - sector_t sz = 0; - for (j=i; j<conf->nr_strip_zones-1 && - sz < min_spacing ; j++) - sz += conf->strip_zone[j].size; - if (sz >= min_spacing && sz < conf->hash_spacing) - conf->hash_spacing = sz; + sector_t s = 0; + for (j = i; j < conf->nr_strip_zones - 1 && + s < min_spacing; j++) + s += conf->strip_zone[j].sectors; + if (s >= min_spacing && s < conf->spacing) + conf->spacing = s; } mddev->queue->unplug_fn = raid0_unplug; @@ -227,7 +229,7 @@ static int create_strip_zones (mddev_t *mddev) mddev->queue->backing_dev_info.congested_fn = raid0_congested; mddev->queue->backing_dev_info.congested_data = mddev; - printk("raid0: done.\n"); + printk(KERN_INFO "raid0: done.\n"); return 0; abort: return 1; @@ -262,10 +264,9 @@ static int raid0_mergeable_bvec(struct request_queue *q, static int raid0_run (mddev_t *mddev) { unsigned cur=0, i=0, nb_zone; - s64 size; + s64 sectors; raid0_conf_t *conf; mdk_rdev_t *rdev; - struct list_head *tmp; if (mddev->chunk_size == 0) { printk(KERN_ERR "md/raid0: non-zero chunk size required.\n"); @@ -291,54 +292,54 @@ static int raid0_run (mddev_t *mddev) /* calculate array device size */ mddev->array_sectors = 0; - rdev_for_each(rdev, tmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) mddev->array_sectors += rdev->size * 2; - printk("raid0 : md_size is %llu blocks.\n", - (unsigned long long)mddev->array_sectors / 2); - printk("raid0 : conf->hash_spacing is %llu blocks.\n", - (unsigned long long)conf->hash_spacing); + printk(KERN_INFO "raid0 : md_size is %llu sectors.\n", + (unsigned long long)mddev->array_sectors); + printk(KERN_INFO "raid0 : conf->spacing is %llu sectors.\n", + (unsigned long long)conf->spacing); { - sector_t s = mddev->array_sectors / 2; - sector_t space = conf->hash_spacing; + sector_t s = mddev->array_sectors; + sector_t space = conf->spacing; int round; - conf->preshift = 0; + conf->sector_shift = 0; if (sizeof(sector_t) > sizeof(u32)) { /*shift down space and s so that sector_div will work */ while (space > (sector_t) (~(u32)0)) { s >>= 1; space >>= 1; s += 1; /* force round-up */ - conf->preshift++; + conf->sector_shift++; } } round = sector_div(s, (u32)space) ? 1 : 0; nb_zone = s + round; } - printk("raid0 : nb_zone is %d.\n", nb_zone); + printk(KERN_INFO "raid0 : nb_zone is %d.\n", nb_zone); - printk("raid0 : Allocating %Zd bytes for hash.\n", + printk(KERN_INFO "raid0 : Allocating %zu bytes for hash.\n", nb_zone*sizeof(struct strip_zone*)); conf->hash_table = kmalloc (sizeof (struct strip_zone *)*nb_zone, GFP_KERNEL); if (!conf->hash_table) goto out_free_conf; - size = conf->strip_zone[cur].size; + sectors = conf->strip_zone[cur].sectors; conf->hash_table[0] = conf->strip_zone + cur; for (i=1; i< nb_zone; i++) { - while (size <= conf->hash_spacing) { + while (sectors <= conf->spacing) { cur++; - size += conf->strip_zone[cur].size; + sectors += conf->strip_zone[cur].sectors; } - size -= conf->hash_spacing; + sectors -= conf->spacing; conf->hash_table[i] = conf->strip_zone + cur; } - if (conf->preshift) { - conf->hash_spacing >>= conf->preshift; - /* round hash_spacing up so when we divide by it, we + if (conf->sector_shift) { + conf->spacing >>= conf->sector_shift; + /* round spacing up so when we divide by it, we * err on the side of too-low, which is safest */ - conf->hash_spacing++; + conf->spacing++; } /* calculate the max read-ahead size. @@ -387,12 +388,12 @@ static int raid0_stop (mddev_t *mddev) static int raid0_make_request (struct request_queue *q, struct bio *bio) { mddev_t *mddev = q->queuedata; - unsigned int sect_in_chunk, chunksize_bits, chunk_size, chunk_sects; + unsigned int sect_in_chunk, chunksect_bits, chunk_sects; raid0_conf_t *conf = mddev_to_conf(mddev); struct strip_zone *zone; mdk_rdev_t *tmp_dev; sector_t chunk; - sector_t block, rsect; + sector_t sector, rsect; const int rw = bio_data_dir(bio); int cpu; @@ -407,11 +408,9 @@ static int raid0_make_request (struct request_queue *q, struct bio *bio) bio_sectors(bio)); part_stat_unlock(); - chunk_size = mddev->chunk_size >> 10; chunk_sects = mddev->chunk_size >> 9; - chunksize_bits = ffz(~chunk_size); - block = bio->bi_sector >> 1; - + chunksect_bits = ffz(~chunk_sects); + sector = bio->bi_sector; if (unlikely(chunk_sects < (bio->bi_sector & (chunk_sects - 1)) + (bio->bi_size >> 9))) { struct bio_pair *bp; @@ -434,28 +433,27 @@ static int raid0_make_request (struct request_queue *q, struct bio *bio) { - sector_t x = block >> conf->preshift; - sector_div(x, (u32)conf->hash_spacing); + sector_t x = sector >> conf->sector_shift; + sector_div(x, (u32)conf->spacing); zone = conf->hash_table[x]; } - - while (block >= (zone->zone_offset + zone->size)) + + while (sector >= zone->zone_start + zone->sectors) zone++; - - sect_in_chunk = bio->bi_sector & ((chunk_size<<1) -1); + + sect_in_chunk = bio->bi_sector & (chunk_sects - 1); { - sector_t x = (block - zone->zone_offset) >> chunksize_bits; + sector_t x = (sector - zone->zone_start) >> chunksect_bits; sector_div(x, zone->nb_dev); chunk = x; - x = block >> chunksize_bits; + x = sector >> chunksect_bits; tmp_dev = zone->dev[sector_div(x, zone->nb_dev)]; } - rsect = (((chunk << chunksize_bits) + zone->dev_offset)<<1) - + sect_in_chunk; + rsect = (chunk << chunksect_bits) + zone->dev_start + sect_in_chunk; bio->bi_bdev = tmp_dev->bdev; bio->bi_sector = rsect + tmp_dev->data_offset; @@ -467,7 +465,7 @@ static int raid0_make_request (struct request_queue *q, struct bio *bio) bad_map: printk("raid0_make_request bug: can't convert block across chunks" - " or bigger than %dk %llu %d\n", chunk_size, + " or bigger than %dk %llu %d\n", chunk_sects / 2, (unsigned long long)bio->bi_sector, bio->bi_size >> 10); bio_io_error(bio); @@ -492,10 +490,10 @@ static void raid0_status (struct seq_file *seq, mddev_t *mddev) seq_printf(seq, "%s/", bdevname( conf->strip_zone[j].dev[k]->bdev,b)); - seq_printf(seq, "] zo=%d do=%d s=%d\n", - conf->strip_zone[j].zone_offset, - conf->strip_zone[j].dev_offset, - conf->strip_zone[j].size); + seq_printf(seq, "] zs=%d ds=%d s=%d\n", + conf->strip_zone[j].zone_start, + conf->strip_zone[j].dev_start, + conf->strip_zone[j].sectors); } #endif seq_printf(seq, " %dk chunks", mddev->chunk_size/1024); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 9c788e2489b1..7b4f5f7155d8 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1016,12 +1016,16 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev) * else mark the drive as failed */ if (test_bit(In_sync, &rdev->flags) - && (conf->raid_disks - mddev->degraded) == 1) + && (conf->raid_disks - mddev->degraded) == 1) { /* * Don't fail the drive, act as though we were just a - * normal single drive + * normal single drive. + * However don't try a recovery from this drive as + * it is very likely to fail. */ + mddev->recovery_disabled = 1; return; + } if (test_and_clear_bit(In_sync, &rdev->flags)) { unsigned long flags; spin_lock_irqsave(&conf->device_lock, flags); @@ -1919,7 +1923,6 @@ static int run(mddev_t *mddev) int i, j, disk_idx; mirror_info_t *disk; mdk_rdev_t *rdev; - struct list_head *tmp; if (mddev->level != 1) { printk("raid1: %s: raid level not set to mirroring (%d)\n", @@ -1964,7 +1967,7 @@ static int run(mddev_t *mddev) spin_lock_init(&conf->device_lock); mddev->queue->queue_lock = &conf->device_lock; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { disk_idx = rdev->raid_disk; if (disk_idx >= mddev->raid_disks || disk_idx < 0) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 970a96ef9b18..6736d6dff981 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2025,7 +2025,6 @@ static int run(mddev_t *mddev) int i, disk_idx; mirror_info_t *disk; mdk_rdev_t *rdev; - struct list_head *tmp; int nc, fc, fo; sector_t stride, size; @@ -2108,7 +2107,7 @@ static int run(mddev_t *mddev) spin_lock_init(&conf->device_lock); mddev->queue->queue_lock = &conf->device_lock; - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { disk_idx = rdev->raid_disk; if (disk_idx >= mddev->raid_disks || disk_idx < 0) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index a36a7435edf5..a5ba080d303b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3998,7 +3998,6 @@ static int run(mddev_t *mddev) int raid_disk, memory; mdk_rdev_t *rdev; struct disk_info *disk; - struct list_head *tmp; int working_disks = 0; if (mddev->level != 5 && mddev->level != 4 && mddev->level != 6) { @@ -4108,7 +4107,7 @@ static int run(mddev_t *mddev) pr_debug("raid5: run(%s) called.\n", mdname(mddev)); - rdev_for_each(rdev, tmp, mddev) { + list_for_each_entry(rdev, &mddev->disks, same_set) { raid_disk = rdev->raid_disk; if (raid_disk >= conf->raid_disks || raid_disk < 0) @@ -4533,7 +4532,6 @@ static int raid5_start_reshape(mddev_t *mddev) { raid5_conf_t *conf = mddev_to_conf(mddev); mdk_rdev_t *rdev; - struct list_head *rtmp; int spares = 0; int added_devices = 0; unsigned long flags; @@ -4541,7 +4539,7 @@ static int raid5_start_reshape(mddev_t *mddev) if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) return -EBUSY; - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) spares++; @@ -4563,7 +4561,7 @@ static int raid5_start_reshape(mddev_t *mddev) /* Add some new drives, as many as will fit. * We know there are enough to make the newly sized array work. */ - rdev_for_each(rdev, rtmp, mddev) + list_for_each_entry(rdev, &mddev->disks, same_set) if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) { if (raid5_add_disk(mddev, rdev) == 0) { diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 0ee79fd7c7a9..4b8662edb7cb 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -150,7 +150,7 @@ static void set_audio(struct dvb_frontend *fe, } } -struct { +static struct { unsigned char seq[2]; } fm_mode[] = { { { 0x01, 0x81} }, /* Put device into expert mode */ @@ -207,7 +207,6 @@ static void tda8290_set_params(struct dvb_frontend *fe, msleep(1); if (params->mode == V4L2_TUNER_RADIO) { - int i; unsigned char deemphasis[] = { 0x13, 1 }; /* FIXME: allow using a different deemphasis */ @@ -767,7 +766,8 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, fe->ops.analog_ops.info.name = name; if (priv->ver & TDA8290) { - tda8290_init_tuner(fe); + if (priv->ver & (TDA8275 | TDA8275A)) + tda8290_init_tuner(fe); tda8290_init_if(fe); } else if (priv->ver & TDA8295) tda8295_init_if(fe); diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig index 1332301ef3ae..43f4d44edca6 100644 --- a/drivers/media/dvb/dm1105/Kconfig +++ b/drivers/media/dvb/dm1105/Kconfig @@ -1,6 +1,7 @@ config DVB_DM1105 tristate "SDMC DM1105 based PCI cards" depends on DVB_CORE && PCI && I2C + depends on INPUT select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 171f9ca124f7..843407785083 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -824,7 +824,7 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, return 0; } -struct dtv_cmds_h dtv_cmds[] = { +static struct dtv_cmds_h dtv_cmds[] = { [DTV_TUNE] = { .name = "DTV_TUNE", .cmd = DTV_TUNE, @@ -962,7 +962,7 @@ struct dtv_cmds_h dtv_cmds[] = { }, }; -void dtv_property_dump(struct dtv_property *tvp) +static void dtv_property_dump(struct dtv_property *tvp) { int i; @@ -993,7 +993,7 @@ void dtv_property_dump(struct dtv_property *tvp) dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data); } -int is_legacy_delivery_system(fe_delivery_system_t s) +static int is_legacy_delivery_system(fe_delivery_system_t s) { if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) || (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS) || @@ -1007,7 +1007,8 @@ int is_legacy_delivery_system(fe_delivery_system_t s) * drivers can use a single set_frontend tuning function, regardless of whether * it's being used for the legacy or new API, reducing code and complexity. */ -void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parameters *p) +static void dtv_property_cache_sync(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; @@ -1059,7 +1060,7 @@ void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parame /* Ensure the cached values are set correctly in the frontend * legacy tuning structures, for the advanced tuning API. */ -void dtv_property_legacy_params_sync(struct dvb_frontend *fe) +static void dtv_property_legacy_params_sync(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dvb_frontend_private *fepriv = fe->frontend_priv; @@ -1114,7 +1115,7 @@ void dtv_property_legacy_params_sync(struct dvb_frontend *fe) /* Ensure the cached values are set correctly in the frontend * legacy tuning structures, for the legacy tuning API. */ -void dtv_property_adv_params_sync(struct dvb_frontend *fe) +static void dtv_property_adv_params_sync(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct dvb_frontend_private *fepriv = fe->frontend_priv; @@ -1149,7 +1150,7 @@ void dtv_property_adv_params_sync(struct dvb_frontend *fe) } } -void dtv_property_cache_submit(struct dvb_frontend *fe) +static void dtv_property_cache_submit(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; @@ -1180,8 +1181,9 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, unsigned int cmd, void *parg); -int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, - struct inode *inode, struct file *file) +static int dtv_property_process_get(struct dvb_frontend *fe, + struct dtv_property *tvp, + struct inode *inode, struct file *file) { int r = 0; @@ -1253,8 +1255,10 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, return r; } -int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp, - struct inode *inode, struct file *file) +static int dtv_property_process_set(struct dvb_frontend *fe, + struct dtv_property *tvp, + struct inode *inode, + struct file *file) { int r = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 03fd9dd5c685..f6ba8468858e 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -125,7 +125,6 @@ static void hexdump( const unsigned char *buf, unsigned short len ) struct dvb_net_priv { int in_use; - struct net_device_stats stats; u16 pid; struct net_device *net; struct dvb_net *host; @@ -384,8 +383,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ - priv->stats.rx_errors++; - priv->stats.rx_frame_errors++; + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; } reset_ule(priv); priv->need_pusi = 1; @@ -438,8 +437,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) dev_kfree_skb( priv->ule_skb ); /* Prepare for next SNDU. */ // reset_ule(priv); moved to below. - priv->stats.rx_errors++; - priv->stats.rx_frame_errors++; + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; } reset_ule(priv); /* skip to next PUSI. */ @@ -460,8 +459,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) /* Drop partly decoded SNDU, reset state, resync on PUSI. */ if (priv->ule_skb) { dev_kfree_skb( priv->ule_skb ); - priv->stats.rx_errors++; - priv->stats.rx_frame_errors++; + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; } reset_ule(priv); priv->need_pusi = 1; @@ -477,8 +476,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_sndu_remain > 183) { /* Current SNDU lacks more data than there could be available in the * current TS cell. */ - priv->stats.rx_errors++; - priv->stats.rx_length_errors++; + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); @@ -520,8 +519,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_sndu_len < 5) { printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. " "Resyncing.\n", priv->ts_count, priv->ule_sndu_len); - priv->stats.rx_errors++; - priv->stats.rx_length_errors++; + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; priv->ule_sndu_len = 0; priv->need_pusi = 1; new_ts = 1; @@ -573,7 +572,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); - priv->stats.rx_dropped++; + dev->stats.rx_dropped++; return; } @@ -637,8 +636,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) ule_dump = 1; #endif - priv->stats.rx_errors++; - priv->stats.rx_crc_errors++; + dev->stats.rx_errors++; + dev->stats.rx_crc_errors++; dev_kfree_skb(priv->ule_skb); } else { /* CRC32 verified OK. */ @@ -744,8 +743,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) * receive the packet anyhow. */ /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) priv->ule_skb->pkt_type = PACKET_HOST; */ - priv->stats.rx_packets++; - priv->stats.rx_bytes += priv->ule_skb->len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += priv->ule_skb->len; netif_rx(priv->ule_skb); } sndu_done: @@ -800,8 +799,7 @@ static void dvb_net_sec(struct net_device *dev, { u8 *eth; struct sk_buff *skb; - struct net_device_stats *stats = - &((struct dvb_net_priv *) netdev_priv(dev))->stats; + struct net_device_stats *stats = &dev->stats; int snap = 0; /* note: pkt_len includes a 32bit checksum */ @@ -1216,28 +1214,29 @@ static int dvb_net_stop(struct net_device *dev) return dvb_net_feed_stop(dev); } -static struct net_device_stats * dvb_net_get_stats(struct net_device *dev) -{ - return &((struct dvb_net_priv *) netdev_priv(dev))->stats; -} - static const struct header_ops dvb_header_ops = { .create = eth_header, .parse = eth_header_parse, .rebuild = eth_rebuild_header, }; + +static const struct net_device_ops dvb_netdev_ops = { + .ndo_open = dvb_net_open, + .ndo_stop = dvb_net_stop, + .ndo_start_xmit = dvb_net_tx, + .ndo_set_multicast_list = dvb_net_set_multicast_list, + .ndo_set_mac_address = dvb_net_set_mac, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + static void dvb_net_setup(struct net_device *dev) { ether_setup(dev); dev->header_ops = &dvb_header_ops; - dev->open = dvb_net_open; - dev->stop = dvb_net_stop; - dev->hard_start_xmit = dvb_net_tx; - dev->get_stats = dvb_net_get_stats; - dev->set_multicast_list = dvb_net_set_multicast_list; - dev->set_mac_address = dvb_net_set_mac; + dev->netdev_ops = &dvb_netdev_ops; dev->mtu = 4096; dev->mc_count = 0; diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index 5017f08b14a6..c6e7b4215d6b 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -41,7 +41,7 @@ static int dvb_usb_anysee_debug; module_param_named(debug, dvb_usb_anysee_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); -int dvb_usb_anysee_delsys; +static int dvb_usb_anysee_delsys; module_param_named(delsys, dvb_usb_anysee_delsys, int, 0644); MODULE_PARM_DESC(delsys, "select delivery mode (0=DVB-C, 1=DVB-T)"); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c index 4f514d39b98f..28ad609e73f4 100644 --- a/drivers/media/dvb/frontends/cx24116.c +++ b/drivers/media/dvb/frontends/cx24116.c @@ -369,7 +369,7 @@ static int cx24116_set_inversion(struct cx24116_state *state, * Not all S2 mmodulation schemes are support and not all rates with * a scheme are support. Especially, no auto detect when in S2 mode. */ -struct cx24116_modfec { +static struct cx24116_modfec { fe_delivery_system_t delivery_system; fe_modulation_t modulation; fe_code_rate_t fec; diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c index 469ace5692c6..3bb0c4394f8a 100644 --- a/drivers/media/dvb/frontends/lgdt3304.c +++ b/drivers/media/dvb/frontends/lgdt3304.c @@ -42,7 +42,7 @@ static int i2c_write_demod_bytes (struct dvb_frontend *fe, __u8 *buf, int len) for (i=0; i<len-1; i+=3){ if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) { - printk("%s i2c_transfer error %d\n", __FUNCTION__, err); + printk("%s i2c_transfer error %d\n", __func__, err); if (err < 0) return err; else @@ -73,7 +73,7 @@ static int lgdt3304_i2c_read_reg(struct dvb_frontend *fe, unsigned int reg) i2cmsgs[1].buf = &buf; if((ret = i2c_transfer(state->i2c, i2cmsgs, 2))<0) { - printk("%s i2c_transfer error %d\n", __FUNCTION__, ret); + printk("%s i2c_transfer error %d\n", __func__, ret); return ret; } @@ -94,7 +94,7 @@ static int lgdt3304_i2c_write_reg(struct dvb_frontend *fe, int reg, int val) }; ret = i2c_transfer(state->i2c, &i2cmsgs, 1); if (ret != 1) { - printk("%s i2c_transfer error %d\n", __FUNCTION__, ret); + printk("%s i2c_transfer error %d\n", __func__, ret); return ret; } @@ -238,7 +238,7 @@ static int lgdt3304_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_ } if (err) { - printk("%s error setting modulation\n", __FUNCTION__); + printk("%s error setting modulation\n", __func__); } else { state->current_modulation = param->u.vsb.modulation; } @@ -305,7 +305,7 @@ static int lgdt3304_read_status(struct dvb_frontend *fe, fe_status_t *status) } break; default: - printk("%s unhandled modulation\n", __FUNCTION__); + printk("%s unhandled modulation\n", __func__); } diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c index 3cbb9cb2cf47..892af8c9ed57 100644 --- a/drivers/media/dvb/frontends/s921_module.c +++ b/drivers/media/dvb/frontends/s921_module.c @@ -136,7 +136,7 @@ static int s921_write(void *dev, u8 reg, u8 val) { }; if((err = i2c_transfer(state->i2c, &i2cmsgs, 1))<0) { - printk("%s i2c_transfer error %d\n", __FUNCTION__, err); + printk("%s i2c_transfer error %d\n", __func__, err); if (err < 0) return err; else diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c index ced9b7ae7d50..83dc7e12d5f0 100644 --- a/drivers/media/dvb/frontends/stb0899_algo.c +++ b/drivers/media/dvb/frontends/stb0899_algo.c @@ -54,7 +54,7 @@ static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr) * stb0899_get_srate * Get the current symbol rate */ -u32 stb0899_get_srate(struct stb0899_state *state) +static u32 stb0899_get_srate(struct stb0899_state *state) { struct stb0899_internal *internal = &state->internal; u8 sfr[3]; @@ -763,7 +763,7 @@ static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state) stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg); } -long Log2Int(int number) +static long Log2Int(int number) { int i; diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index bee28f77b93f..10613acf18f5 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -134,7 +134,7 @@ static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { }; /* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ -struct stb0899_tab stb0899_quant_tab[] = { +static struct stb0899_tab stb0899_quant_tab[] = { { 0, 0 }, { 0, 100 }, { 600, 200 }, @@ -177,7 +177,7 @@ struct stb0899_tab stb0899_quant_tab[] = { }; /* DVB-S2 Es/N0 estimate in dB/100 vs read value */ -struct stb0899_tab stb0899_est_tab[] = { +static struct stb0899_tab stb0899_est_tab[] = { { 0, 0 }, { 0, 1 }, { 301, 2 }, @@ -217,7 +217,7 @@ struct stb0899_tab stb0899_est_tab[] = { { 5721, 526017 }, }; -int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) { int ret; diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 3507463fdac9..bcbc5d41a0fe 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -1337,7 +1337,7 @@ static struct stb0899_config tt3200_config = { .tuner_set_rfsiggain = NULL }; -struct stb6100_config tt3200_stb6100_config = { +static struct stb6100_config tt3200_stb6100_config = { .tuner_address = 0x60, .refclock = 27000000, }; @@ -1450,7 +1450,7 @@ static void frontend_init(struct budget_ci *budget_ci) if (budget_ci->budget.dvb_frontend) { if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { - printk("%s: No LNBP21 found!\n", __FUNCTION__); + printk("%s: No LNBP21 found!\n", __func__); dvb_frontend_detach(budget_ci->budget.dvb_frontend); budget_ci->budget.dvb_frontend = NULL; } diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index b0f837588e01..2d250a2a7bc3 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -69,6 +69,11 @@ config VIDEO_CX88_DVB To compile this driver as a module, choose M here: the module will be called cx88-dvb. +config VIDEO_CX88_MPEG + tristate + depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD + default y + config VIDEO_CX88_VP3054 tristate "VP-3054 Secondary I2C Bus Support" default m diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 6ec30f242578..b06b1275a9ec 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -3,7 +3,8 @@ cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \ cx8800-objs := cx88-video.o cx88-vbi.o cx8802-objs := cx88-mpeg.o -obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o +obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o +obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index da4dd4913d9f..613dfea4ff3e 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -138,6 +138,28 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) return ret; } +static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open) +{ + struct videobuf_dvb_frontends *f; + struct videobuf_dvb_frontend *fe; + + if (!core->dvbdev) + return; + + f = &core->dvbdev->frontends; + + if (!f) + return; + + if (f->gate <= 1) /* undefined or fe0 */ + fe = videobuf_dvb_get_frontend(f, 1); + else + fe = videobuf_dvb_get_frontend(f, f->gate); + + if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) + fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); +} + /* ------------------------------------------------------------------ */ static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) @@ -597,12 +619,30 @@ static int dvb_register(struct cx8802_dev *dev) struct cx88_core *core = dev->core; struct videobuf_dvb_frontend *fe0, *fe1 = NULL; int mfe_shared = 0; /* bus not shared by default */ + int i; if (0 != core->i2c_rc) { printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name); goto frontend_detach; } + if (!core->board.num_frontends) + return -EINVAL; + + mutex_init(&dev->frontends.lock); + INIT_LIST_HEAD(&dev->frontends.felist); + + printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, + core->board.num_frontends); + for (i = 1; i <= core->board.num_frontends; i++) { + fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, i); + if (!fe0) { + printk(KERN_ERR "%s() failed to alloc\n", __func__); + videobuf_dvb_dealloc_frontends(&dev->frontends); + goto frontend_detach; + } + } + /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); if (!fe0) @@ -611,6 +651,9 @@ static int dvb_register(struct cx8802_dev *dev) /* multi-frontend gate control is undefined or defaults to fe0 */ dev->frontends.gate = 0; + /* Sets the gate control callback to be used by i2c command calls */ + core->gate_ctrl = cx88_dvb_gate_ctrl; + /* init frontend(s) */ switch (core->boardnr) { case CX88_BOARD_HAUPPAUGE_DVB_T1: @@ -1109,6 +1152,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->pci->dev, adapter_nr, mfe_shared); frontend_detach: + core->gate_ctrl = NULL; videobuf_dvb_dealloc_frontends(&dev->frontends); return -EINVAL; } @@ -1270,6 +1314,8 @@ static int cx8802_dvb_remove(struct cx8802_driver *drv) vp3054_i2c_remove(dev); + core->gate_ctrl = NULL; + return 0; } diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 1ab691d20692..c0ff2305d804 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -116,30 +116,16 @@ static int detach_inform(struct i2c_client *client) void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) { -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) - struct videobuf_dvb_frontends *f = &core->dvbdev->frontends; - struct videobuf_dvb_frontend *fe = NULL; -#endif if (0 != core->i2c_rc) return; -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) - if (core->dvbdev && f) { - if(f->gate <= 1) /* undefined or fe0 */ - fe = videobuf_dvb_get_frontend(f, 1); - else - fe = videobuf_dvb_get_frontend(f, f->gate); + if (core->gate_ctrl) + core->gate_ctrl(core, 1); - if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) - fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, 1); + i2c_clients_command(&core->i2c_adap, cmd, arg); - i2c_clients_command(&core->i2c_adap, cmd, arg); - - if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) - fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, 0); - } else -#endif - i2c_clients_command(&core->i2c_adap, cmd, arg); + if (core->gate_ctrl) + core->gate_ctrl(core, 0); } static const struct i2c_algo_bit_data cx8800_i2c_algo_template = { diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 59164fc94f5f..b295b76737e3 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -787,6 +787,9 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, dev->pci = pci_dev; dev->core = core; + /* Maintain a reference so cx88-video can query the 8802 device. */ + core->dvbdev = dev; + err = cx8802_init_common(dev); if (err != 0) goto fail_free; @@ -794,32 +797,6 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, INIT_LIST_HEAD(&dev->drvlist); list_add_tail(&dev->devlist,&cx8802_devlist); -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) - mutex_init(&dev->frontends.lock); - INIT_LIST_HEAD(&dev->frontends.felist); - - if (core->board.num_frontends) { - struct videobuf_dvb_frontend *fe; - int i; - - printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, - core->board.num_frontends); - for (i = 1; i <= core->board.num_frontends; i++) { - fe = videobuf_dvb_alloc_frontend(&dev->frontends, i); - if(fe == NULL) { - printk(KERN_ERR "%s() failed to alloc\n", - __func__); - videobuf_dvb_dealloc_frontends(&dev->frontends); - err = -ENOMEM; - goto fail_free; - } - } - } -#endif - - /* Maintain a reference so cx88-video can query the 8802 device. */ - core->dvbdev = dev; - /* now autoload cx88-dvb or cx88-blackbird */ request_modules(dev); return 0; @@ -827,6 +804,7 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, fail_free: kfree(dev); fail_core: + core->dvbdev = NULL; cx88_core_put(core,pci_dev); return err; } diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index eb9ce30dc5e6..60a8b3187f14 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -302,6 +302,7 @@ struct cx88_dmaqueue { struct btcx_riscmem stopper; u32 count; }; +struct cx88_core; struct cx88_core { struct list_head devlist; @@ -334,7 +335,8 @@ struct cx88_core { /* config info -- dvb */ #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) - int (*prev_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); + int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + void (*gate_ctrl)(struct cx88_core *core, int open); #endif /* state info */ diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index e776699b62f9..ef9bf008a924 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1842,7 +1842,7 @@ void em28xx_release_resources(struct em28xx *dev) * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device */ -int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, +static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { struct em28xx *dev = *devhandle; @@ -1990,8 +1990,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, int check_interface = 1; isoc_pipe = 1; endpoint = &interface->cur_altsetting->endpoint[1].desc; - if (usb_endpoint_type(endpoint) != - USB_ENDPOINT_XFER_ISOC) + if (!usb_endpoint_xfer_isoc(endpoint)) check_interface = 0; if (usb_endpoint_dir_out(endpoint)) diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 819cceaa6ef4..eb5fb05fab22 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -393,7 +393,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; } -struct em28xx_vol_table outputs[] = { +static const struct em28xx_vol_table outputs[] = { { EM28XX_AOUT_MASTER, AC97_MASTER_VOL }, { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL }, { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL }, diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 42bbaf64aceb..0443afe09ff8 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -307,7 +307,7 @@ static void em28xx_ir_work(struct work_struct *work) mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } -void em28xx_ir_start(struct em28xx_IR *ir) +static void em28xx_ir_start(struct em28xx_IR *ir) { setup_timer(&ir->timer, ir_timer, (unsigned long)ir); INIT_WORK(&ir->work, em28xx_ir_work); diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index af3f2dc2c702..ccea4a758464 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -113,7 +113,7 @@ int s5k83a_power_down(struct sd *sd) return 0; } -void s5k83a_dump_registers(struct sd *sd) +static void s5k83a_dump_registers(struct sd *sd) { int address; u8 page, old_page; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8fb92ac78c7b..fa304e5f252a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3655,7 +3655,7 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *hdw) int ret; pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset..."); ret = usb_lock_device_for_reset(hdw->usb_dev,NULL); - if (ret == 1) { + if (ret == 0) { ret = usb_reset_device(hdw->usb_dev); usb_unlock_device(hdw->usb_dev); } else { diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 9d33de22cc48..a1d6008efcbb 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -34,12 +34,10 @@ #include <linux/videodev2.h> -#include <asm/dma.h> +#include <mach/dma.h> #include <mach/pxa-regs.h> #include <mach/camera.h> -#include "pxa_camera.h" - #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) #define PXA_CAM_DRV_NAME "pxa27x-camera" diff --git a/drivers/media/video/pxa_camera.h b/drivers/media/video/pxa_camera.h deleted file mode 100644 index 89cbfc9a35c5..000000000000 --- a/drivers/media/video/pxa_camera.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Camera Interface */ -#define CICR0 __REG(0x50000000) -#define CICR1 __REG(0x50000004) -#define CICR2 __REG(0x50000008) -#define CICR3 __REG(0x5000000C) -#define CICR4 __REG(0x50000010) -#define CISR __REG(0x50000014) -#define CIFR __REG(0x50000018) -#define CITOR __REG(0x5000001C) -#define CIBR0 __REG(0x50000028) -#define CIBR1 __REG(0x50000030) -#define CIBR2 __REG(0x50000038) - -#define CICR0_DMAEN (1 << 31) /* DMA request enable */ -#define CICR0_PAR_EN (1 << 30) /* Parity enable */ -#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */ -#define CICR0_ENB (1 << 28) /* Camera interface enable */ -#define CICR0_DIS (1 << 27) /* Camera interface disable */ -#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */ -#define CICR0_TOM (1 << 9) /* Time-out mask */ -#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */ -#define CICR0_FEM (1 << 7) /* FIFO-empty mask */ -#define CICR0_EOLM (1 << 6) /* End-of-line mask */ -#define CICR0_PERRM (1 << 5) /* Parity-error mask */ -#define CICR0_QDM (1 << 4) /* Quick-disable mask */ -#define CICR0_CDM (1 << 3) /* Disable-done mask */ -#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */ -#define CICR0_EOFM (1 << 1) /* End-of-frame mask */ -#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */ - -#define CICR1_TBIT (1 << 31) /* Transparency bit */ -#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */ -#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */ -#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */ -#define CICR1_RGB_F (1 << 11) /* RGB format */ -#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */ -#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */ -#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */ -#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */ -#define CICR1_DW (0x7 << 0) /* Data width mask */ - -#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock - wait count mask */ -#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock - wait count mask */ -#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */ -#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock - wait count mask */ -#define CICR2_FSW (0x7 << 0) /* Frame stabilization - wait count mask */ - -#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock - wait count mask */ -#define CICR3_EFW (0xff << 16) /* End-of-frame line clock - wait count mask */ -#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */ -#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock - wait count mask */ -#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */ - -#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */ -#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */ -#define CICR4_PCP (1 << 22) /* Pixel clock polarity */ -#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */ -#define CICR4_VSP (1 << 20) /* Vertical sync polarity */ -#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */ -#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */ -#define CICR4_DIV (0xff << 0) /* Clock divisor mask */ - -#define CISR_FTO (1 << 15) /* FIFO time-out */ -#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */ -#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */ -#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */ -#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */ -#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */ -#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */ -#define CISR_EOL (1 << 8) /* End of line */ -#define CISR_PAR_ERR (1 << 7) /* Parity error */ -#define CISR_CQD (1 << 6) /* Camera interface quick disable */ -#define CISR_CDD (1 << 5) /* Camera interface disable done */ -#define CISR_SOF (1 << 4) /* Start of frame */ -#define CISR_EOF (1 << 3) /* End of frame */ -#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */ -#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */ -#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */ - -#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */ -#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */ -#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */ -#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */ -#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */ -#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */ -#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */ -#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */ - diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c index f8d85ddb4804..b08549661781 100644 --- a/drivers/media/video/usbvideo/ibmcam.c +++ b/drivers/media/video/usbvideo/ibmcam.c @@ -3779,7 +3779,7 @@ static int ibmcam_probe(struct usb_interface *intf, const struct usb_device_id * err("Alternate settings have different endpoint addresses!"); return -ENODEV; } - if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { + if (!usb_endpoint_xfer_isoc(endpoint)) { err("Interface %d. has non-ISO endpoint!", ifnum); return -ENODEV; } diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 90f0ce6a26bc..900ec2129ca1 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -823,7 +823,7 @@ static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id err("Alternate settings have different endpoint addresses!"); return -ENODEV; } - if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { + if (!usb_endpoint_xfer_isoc(endpoint)) { err("Interface %d. has non-ISO endpoint!", interface->desc.bInterfaceNumber); return -ENODEV; diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c index 839a08240c25..fbd1b6392290 100644 --- a/drivers/media/video/usbvideo/ultracam.c +++ b/drivers/media/video/usbvideo/ultracam.c @@ -556,7 +556,7 @@ static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id err("Alternate settings have different endpoint addresses!"); return -ENODEV; } - if (usb_endpoint_type(endpoint) != USB_ENDPOINT_XFER_ISOC) { + if (!usb_endpoint_xfer_isoc(endpoint)) { err("Interface %d. has non-ISO endpoint!", interface->desc.bInterfaceNumber); return -ENODEV; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 2be5e47ed081..2622de003a45 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1674,8 +1674,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, interface = &dev->actconfig->interface[ifnum]->altsetting[0]; } endpoint = &interface->endpoint[1].desc; - if (usb_endpoint_type(endpoint) != - USB_ENDPOINT_XFER_ISOC) { + if (!usb_endpoint_xfer_isoc(endpoint)) { err("%s: interface %d. has non-ISO endpoint!", __func__, ifnum); err("%s: Endpoint attributes %d", diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 9eefde031597..cf9d4c7f571a 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -29,7 +29,7 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) if (dev == NULL || v4l2_dev == NULL) return -EINVAL; /* Warn if we apparently re-register a device */ - WARN_ON(dev_get_drvdata(dev)); + WARN_ON(dev_get_drvdata(dev) != NULL); INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); v4l2_dev->dev = dev; @@ -61,7 +61,7 @@ int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) if (dev == NULL || sd == NULL || !sd->name[0]) return -EINVAL; /* Warn if we apparently re-register a subdev */ - WARN_ON(sd->dev); + WARN_ON(sd->dev != NULL); if (!try_module_get(sd->owner)) return -ENODEV; sd->dev = dev; diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index bc6d5aba0fe6..da1790e57a86 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -388,8 +388,7 @@ videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) page = alloc_page(GFP_USER | __GFP_DMA32); if (!page) return VM_FAULT_OOM; - clear_user_page(page_address(page), (unsigned long)vmf->virtual_address, - page); + clear_user_highpage(page, (unsigned long)vmf->virtual_address); vmf->page = page; return 0; } diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 1f1e3982b1aa..de143deb06f0 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -52,14 +52,14 @@ struct mspro_sys_attr { }; struct mspro_attr_entry { - unsigned int address; - unsigned int size; + __be32 address; + __be32 size; unsigned char id; unsigned char reserved[3]; } __attribute__((packed)); struct mspro_attribute { - unsigned short signature; + __be16 signature; unsigned short version; unsigned char count; unsigned char reserved[11]; @@ -69,28 +69,28 @@ struct mspro_attribute { struct mspro_sys_info { unsigned char class; unsigned char reserved0; - unsigned short block_size; - unsigned short block_count; - unsigned short user_block_count; - unsigned short page_size; + __be16 block_size; + __be16 block_count; + __be16 user_block_count; + __be16 page_size; unsigned char reserved1[2]; unsigned char assembly_date[8]; - unsigned int serial_number; + __be32 serial_number; unsigned char assembly_maker_code; unsigned char assembly_model_code[3]; - unsigned short memory_maker_code; - unsigned short memory_model_code; + __be16 memory_maker_code; + __be16 memory_model_code; unsigned char reserved2[4]; unsigned char vcc; unsigned char vpp; - unsigned short controller_number; - unsigned short controller_function; - unsigned short start_sector; - unsigned short unit_size; + __be16 controller_number; + __be16 controller_function; + __be16 start_sector; + __be16 unit_size; unsigned char ms_sub_class; unsigned char reserved3[4]; unsigned char interface_type; - unsigned short controller_code; + __be16 controller_code; unsigned char format_type; unsigned char reserved4; unsigned char device_type; @@ -124,11 +124,11 @@ struct mspro_specfile { } __attribute__((packed)); struct mspro_devinfo { - unsigned short cylinders; - unsigned short heads; - unsigned short bytes_per_track; - unsigned short bytes_per_sector; - unsigned short sectors_per_track; + __be16 cylinders; + __be16 heads; + __be16 bytes_per_track; + __be16 bytes_per_sector; + __be16 sectors_per_track; unsigned char reserved[6]; } __attribute__((packed)); @@ -338,8 +338,7 @@ static ssize_t mspro_block_attr_show_sysinfo(struct device *dev, rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly date: " "GMT%+d:%d %04u-%02u-%02u %02u:%02u:%02u\n", date_tz, date_tz_f, - be16_to_cpu(*(unsigned short *) - &x_sys->assembly_date[1]), + be16_to_cpup((__be16 *)&x_sys->assembly_date[1]), x_sys->assembly_date[3], x_sys->assembly_date[4], x_sys->assembly_date[5], x_sys->assembly_date[6], x_sys->assembly_date[7]); diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index b89f476cd0a9..c63817117c0a 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -308,10 +308,11 @@ static void mptctl_timeout_expired (MPT_IOCTL *ioctl) { int rc = 1; - dctlprintk(ioctl->ioc, printk(MYIOC_s_DEBUG_FMT ": Timeout Expired! Host %d\n", - ioctl->ioc->name, ioctl->ioc->id)); if (ioctl == NULL) return; + dctlprintk(ioctl->ioc, + printk(MYIOC_s_DEBUG_FMT ": Timeout Expired! Host %d\n", + ioctl->ioc->name, ioctl->ioc->id)); ioctl->wait_done = 0; if (ioctl->reset & MPTCTL_RESET_OK) diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index a13f6eecd25b..c2804f26cb44 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -106,7 +106,6 @@ struct mpt_lan_priv { u32 total_posted; u32 total_received; - struct net_device_stats stats; /* Per device statistics */ struct delayed_work post_buckets_task; struct net_device *dev; @@ -548,15 +547,6 @@ mpt_lan_close(struct net_device *dev) } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static struct net_device_stats * -mpt_lan_get_stats(struct net_device *dev) -{ - struct mpt_lan_priv *priv = netdev_priv(dev); - - return (struct net_device_stats *) &priv->stats; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static int mpt_lan_change_mtu(struct net_device *dev, int new_mtu) { @@ -594,8 +584,8 @@ mpt_lan_send_turbo(struct net_device *dev, u32 tmsg) ctx = GET_LAN_BUFFER_CONTEXT(tmsg); sent = priv->SendCtl[ctx].skb; - priv->stats.tx_packets++; - priv->stats.tx_bytes += sent->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += sent->len; dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", IOC_AND_NETDEV_NAMES_s_s(dev), @@ -636,7 +626,7 @@ mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep) switch (le16_to_cpu(pSendRep->IOCStatus) & MPI_IOCSTATUS_MASK) { case MPI_IOCSTATUS_SUCCESS: - priv->stats.tx_packets += count; + dev->stats.tx_packets += count; break; case MPI_IOCSTATUS_LAN_CANCELED: @@ -644,13 +634,13 @@ mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep) break; case MPI_IOCSTATUS_INVALID_SGL: - priv->stats.tx_errors += count; + dev->stats.tx_errors += count; printk (KERN_ERR MYNAM ": %s/%s: ERROR - Invalid SGL sent to IOC!\n", IOC_AND_NETDEV_NAMES_s_s(dev)); goto out; default: - priv->stats.tx_errors += count; + dev->stats.tx_errors += count; break; } @@ -661,7 +651,7 @@ mpt_lan_send_reply(struct net_device *dev, LANSendReply_t *pSendRep) ctx = GET_LAN_BUFFER_CONTEXT(le32_to_cpu(*pContext)); sent = priv->SendCtl[ctx].skb; - priv->stats.tx_bytes += sent->len; + dev->stats.tx_bytes += sent->len; dioprintk((KERN_INFO MYNAM ": %s/%s: @%s, skb %p sent.\n", IOC_AND_NETDEV_NAMES_s_s(dev), @@ -842,8 +832,8 @@ mpt_lan_receive_skb(struct net_device *dev, struct sk_buff *skb) "delivered to upper level.\n", IOC_AND_NETDEV_NAMES_s_s(dev), skb->len)); - priv->stats.rx_bytes += skb->len; - priv->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; skb->dev = dev; netif_rx(skb); @@ -1308,6 +1298,14 @@ mpt_lan_post_receive_buckets_work(struct work_struct *work) post_buckets_task.work)); } +static const struct net_device_ops mpt_netdev_ops = { + .ndo_open = mpt_lan_open, + .ndo_stop = mpt_lan_close, + .ndo_start_xmit = mpt_lan_sdu_send, + .ndo_change_mtu = mpt_lan_change_mtu, + .ndo_tx_timeout = mpt_lan_tx_timeout, +}; + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static struct net_device * mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum) @@ -1372,15 +1370,7 @@ mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum) priv->tx_max_out = (tx_max_out_p <= MPT_TX_MAX_OUT_LIM) ? tx_max_out_p : MPT_TX_MAX_OUT_LIM; - dev->open = mpt_lan_open; - dev->stop = mpt_lan_close; - dev->get_stats = mpt_lan_get_stats; - dev->set_multicast_list = NULL; - dev->change_mtu = mpt_lan_change_mtu; - dev->hard_start_xmit = mpt_lan_sdu_send; - -/* Not in 2.3.42. Need 2.3.45+ */ - dev->tx_timeout = mpt_lan_tx_timeout; + dev->netdev_ops = &mpt_netdev_ops; dev->watchdog_timeo = MPT_LAN_TX_TIMEOUT; dlprintk((KERN_INFO MYNAM ": Finished registering dev " diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c index 3a273ccef3f2..f92595c8f165 100644 --- a/drivers/mfd/wm8350-core.c +++ b/drivers/mfd/wm8350-core.c @@ -1453,6 +1453,9 @@ void wm8350_device_exit(struct wm8350 *wm8350) { int i; + for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++) + platform_device_unregister(wm8350->pmic.led[i].pdev); + for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) platform_device_unregister(wm8350->pmic.pdev[i]); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fee7304102af..419c378bd24b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -120,7 +120,7 @@ config TIFM_CORE cards are supported via 'MMC/SD Card support: TI Flash Media MMC/SD Interface support (MMC_TIFM_SD)'. - To compile this driver as a module, choose M here: the module will + To compile this driver as a module, choose M here: the module will be called tifm_core. config TIFM_7XX1 @@ -133,100 +133,9 @@ config TIFM_7XX1 To make actual use of the device, you will have to select some flash card format drivers, as outlined in the TIFM_CORE Help. - To compile this driver as a module, choose M here: the module will + To compile this driver as a module, choose M here: the module will be called tifm_7xx1. -config ACER_WMI - tristate "Acer WMI Laptop Extras (EXPERIMENTAL)" - depends on X86 - depends on EXPERIMENTAL - depends on ACPI - depends on LEDS_CLASS - depends on NEW_LEDS - depends on BACKLIGHT_CLASS_DEVICE - depends on SERIO_I8042 - depends on RFKILL - select ACPI_WMI - ---help--- - This is a driver for newer Acer (and Wistron) laptops. It adds - wireless radio and bluetooth control, and on some laptops, - exposes the mail LED and LCD backlight. - - For more information about this driver see - <file:Documentation/laptops/acer-wmi.txt> - - If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M - here. - -config ASUS_LAPTOP - tristate "Asus Laptop Extras (EXPERIMENTAL)" - depends on X86 - depends on ACPI - depends on EXPERIMENTAL && !ACPI_ASUS - depends on LEDS_CLASS - depends on NEW_LEDS - depends on BACKLIGHT_CLASS_DEVICE - ---help--- - This is the new Linux driver for Asus laptops. It may also support some - MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate - standard ACPI events that go through /proc/acpi/events. It also adds - support for video output switching, LCD backlight control, Bluetooth and - Wlan control, and most importantly, allows you to blink those fancy LEDs. - - For more information and a userspace daemon for handling the extra - buttons see <http://acpi4asus.sf.net/>. - - If you have an ACPI-compatible ASUS laptop, say Y or M here. - -config FUJITSU_LAPTOP - tristate "Fujitsu Laptop Extras" - depends on X86 - depends on ACPI - depends on INPUT - depends on BACKLIGHT_CLASS_DEVICE - ---help--- - This is a driver for laptops built by Fujitsu: - - * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks - * Possibly other Fujitsu laptop models - * Tested with S6410 and S7020 - - It adds support for LCD brightness control and some hotkeys. - - If you have a Fujitsu laptop, say Y or M here. - -config FUJITSU_LAPTOP_DEBUG - bool "Verbose debug mode for Fujitsu Laptop Extras" - depends on FUJITSU_LAPTOP - default n - ---help--- - Enables extra debug output from the fujitsu extras driver, at the - expense of a slight increase in driver size. - - If you are not sure, say N here. - -config TC1100_WMI - tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)" - depends on X86 && !X86_64 - depends on EXPERIMENTAL - depends on ACPI - select ACPI_WMI - ---help--- - This is a driver for the WMI extensions (wireless and bluetooth power - control) of the HP Compaq TC1100 tablet. - -config HP_WMI - tristate "HP WMI extras" - depends on ACPI_WMI - depends on INPUT - depends on RFKILL - help - Say Y here if you want to support WMI-based hotkeys on HP laptops and - to read data from WMI such as docking or ambient light sensor state. - - To compile this driver as a module, choose M here: the module will - be called hp-wmi. - config ICS932S401 tristate "Integrated Circuits ICS932S401" depends on I2C && EXPERIMENTAL @@ -237,170 +146,6 @@ config ICS932S401 This driver can also be built as a module. If so, the module will be called ics932s401. -config MSI_LAPTOP - tristate "MSI Laptop Extras" - depends on X86 - depends on ACPI - depends on BACKLIGHT_CLASS_DEVICE - ---help--- - This is a driver for laptops built by MSI (MICRO-STAR - INTERNATIONAL): - - MSI MegaBook S270 (MS-1013) - Cytron/TCM/Medion/Tchibo MD96100/SAM2000 - - It adds support for Bluetooth, WLAN and LCD brightness control. - - More information about this driver is available at - <http://0pointer.de/lennart/tchibo.html>. - - If you have an MSI S270 laptop, say Y or M here. - -config PANASONIC_LAPTOP - tristate "Panasonic Laptop Extras" - depends on X86 && INPUT && ACPI - depends on BACKLIGHT_CLASS_DEVICE - ---help--- - This driver adds support for access to backlight control and hotkeys - on Panasonic Let's Note laptops. - - If you have a Panasonic Let's note laptop (such as the R1(N variant), - R2, R3, R5, T2, W2 and Y2 series), say Y. - -config COMPAL_LAPTOP - tristate "Compal Laptop Extras" - depends on X86 - depends on ACPI - depends on BACKLIGHT_CLASS_DEVICE - ---help--- - This is a driver for laptops built by Compal: - - Compal FL90/IFL90 - Compal FL91/IFL91 - Compal FL92/JFL92 - Compal FT00/IFT00 - - It adds support for Bluetooth, WLAN and LCD brightness control. - - If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here. - -config SONY_LAPTOP - tristate "Sony Laptop Extras" - depends on X86 && ACPI - select BACKLIGHT_CLASS_DEVICE - depends on INPUT - ---help--- - This mini-driver drives the SNC and SPIC devices present in the ACPI - BIOS of the Sony Vaio laptops. - - It gives access to some extra laptop functionalities like Bluetooth, - screen brightness control, Fn keys and allows powering on/off some - devices. - - Read <file:Documentation/laptops/sony-laptop.txt> for more information. - -config SONYPI_COMPAT - bool "Sonypi compatibility" - depends on SONY_LAPTOP - ---help--- - Build the sonypi driver compatibility code into the sony-laptop driver. - -config THINKPAD_ACPI - tristate "ThinkPad ACPI Laptop Extras" - depends on X86 && ACPI - select BACKLIGHT_LCD_SUPPORT - select BACKLIGHT_CLASS_DEVICE - select HWMON - select NVRAM - select INPUT - select NEW_LEDS - select LEDS_CLASS - select NET - select RFKILL - ---help--- - This is a driver for the IBM and Lenovo ThinkPad laptops. It adds - support for Fn-Fx key combinations, Bluetooth control, video - output switching, ThinkLight control, UltraBay eject and more. - For more information about this driver see - <file:Documentation/laptops/thinkpad-acpi.txt> and - <http://ibm-acpi.sf.net/> . - - This driver was formerly known as ibm-acpi. - - If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. - -config THINKPAD_ACPI_DEBUG - bool "Verbose debug mode" - depends on THINKPAD_ACPI - default n - ---help--- - Enables extra debugging information, at the expense of a slightly - increase in driver size. - - If you are not sure, say N here. - -config THINKPAD_ACPI_DOCK - bool "Legacy Docking Station Support" - depends on THINKPAD_ACPI - depends on ACPI_DOCK=n - default n - ---help--- - Allows the thinkpad_acpi driver to handle docking station events. - This support was made obsolete by the generic ACPI docking station - support (CONFIG_ACPI_DOCK). It will allow locking and removing the - laptop from the docking station, but will not properly connect PCI - devices. - - If you are not sure, say N here. - -config THINKPAD_ACPI_BAY - bool "Legacy Removable Bay Support" - depends on THINKPAD_ACPI - default y - ---help--- - Allows the thinkpad_acpi driver to handle removable bays. It will - electrically disable the device in the bay, and also generate - notifications when the bay lever is ejected or inserted. - - If you are not sure, say Y here. - -config THINKPAD_ACPI_VIDEO - bool "Video output control support" - depends on THINKPAD_ACPI - default y - ---help--- - Allows the thinkpad_acpi driver to provide an interface to control - the various video output ports. - - This feature often won't work well, depending on ThinkPad model, - display state, video output devices in use, whether there is a X - server running, phase of the moon, and the current mood of - Schroedinger's cat. If you can use X.org's RandR to control - your ThinkPad's video output ports instead of this feature, - don't think twice: do it and say N here to save some memory. - - If you are not sure, say Y here. - -config THINKPAD_ACPI_HOTKEY_POLL - bool "Support NVRAM polling for hot keys" - depends on THINKPAD_ACPI - default y - ---help--- - Some thinkpad models benefit from NVRAM polling to detect a few of - the hot key press events. If you know your ThinkPad model does not - need to do NVRAM polling to support any of the hot keys you use, - unselecting this option will save about 1kB of memory. - - ThinkPads T40 and newer, R52 and newer, and X31 and newer are - unlikely to need NVRAM polling in their latest BIOS versions. - - NVRAM polling can detect at most the following keys: ThinkPad/Access - IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, - Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). - - If you are not sure, say Y here. The driver enables polling only if - it is strictly necessary to do so. - config ATMEL_SSC tristate "Device driver for Atmel SSC peripheral" depends on AVR32 || ARCH_AT91 @@ -413,31 +158,6 @@ config ATMEL_SSC If unsure, say N. -config INTEL_MENLOW - tristate "Thermal Management driver for Intel menlow platform" - depends on ACPI_THERMAL - select THERMAL - depends on X86 - ---help--- - ACPI thermal management enhancement driver on - Intel Menlow platform. - - If unsure, say N. - -config EEEPC_LAPTOP - tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" - depends on X86 - depends on ACPI - depends on BACKLIGHT_CLASS_DEVICE - depends on HWMON - depends on EXPERIMENTAL - depends on RFKILL - ---help--- - This driver supports the Fn-Fx keys on Eee PC laptops. - It also adds the ability to switch camera/wlan on/off. - - If you have an Eee PC laptop, say Y or M here. - config ENCLOSURE_SERVICES tristate "Enclosure Services" default n @@ -498,6 +218,18 @@ config SGI_GRU_DEBUG This option enables addition debugging code for the SGI GRU driver. If you are unsure, say N. +config DELL_LAPTOP + tristate "Dell Laptop Extras (EXPERIMENTAL)" + depends on X86 + depends on DCDBAS + depends on EXPERIMENTAL + depends on BACKLIGHT_CLASS_DEVICE + depends on RFKILL + default n + ---help--- + This driver adds support for rfkill and backlight control to Dell + laptops. + source "drivers/misc/c2port/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 817f7f5ab3bd..9cf8ae6e4b39 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -1,32 +1,20 @@ # # Makefile for misc devices that really don't fit anywhere else. # -obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ -obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o -obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o -obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o -obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o -obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o -obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_ICS932S401) += ics932s401.o -obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o +obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SGI_IOC4) += ioc4.o -obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o -obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o -obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o -obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ diff --git a/drivers/misc/dell-laptop.c b/drivers/misc/dell-laptop.c new file mode 100644 index 000000000000..4d33a2068b7a --- /dev/null +++ b/drivers/misc/dell-laptop.c @@ -0,0 +1,436 @@ +/* + * Driver for Dell laptop extras + * + * Copyright (c) Red Hat <mjg@redhat.com> + * + * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell + * Inc. + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/dmi.h> +#include <linux/io.h> +#include <linux/rfkill.h> +#include <linux/power_supply.h> +#include <linux/acpi.h> +#include "../firmware/dcdbas.h" + +#define BRIGHTNESS_TOKEN 0x7d + +/* This structure will be modified by the firmware when we enter + * system management mode, hence the volatiles */ + +struct calling_interface_buffer { + u16 class; + u16 select; + volatile u32 input[4]; + volatile u32 output[4]; +} __packed; + +struct calling_interface_token { + u16 tokenID; + u16 location; + union { + u16 value; + u16 stringlength; + }; +}; + +struct calling_interface_structure { + struct dmi_header header; + u16 cmdIOAddress; + u8 cmdIOCode; + u32 supportedCmds; + struct calling_interface_token tokens[]; +} __packed; + +static int da_command_address; +static int da_command_code; +static int da_num_tokens; +static struct calling_interface_token *da_tokens; + +static struct backlight_device *dell_backlight_device; +static struct rfkill *wifi_rfkill; +static struct rfkill *bluetooth_rfkill; +static struct rfkill *wwan_rfkill; + +static const struct dmi_system_id __initdata dell_device_table[] = { + { + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, + { } +}; + +static void parse_da_table(const struct dmi_header *dm) +{ + /* Final token is a terminator, so we don't want to copy it */ + int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; + struct calling_interface_structure *table = + container_of(dm, struct calling_interface_structure, header); + + /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least + 6 bytes of entry */ + + if (dm->length < 17) + return; + + da_command_address = table->cmdIOAddress; + da_command_code = table->cmdIOCode; + + da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * + sizeof(struct calling_interface_token), + GFP_KERNEL); + + if (!da_tokens) + return; + + memcpy(da_tokens+da_num_tokens, table->tokens, + sizeof(struct calling_interface_token) * tokens); + + da_num_tokens += tokens; +} + +static void find_tokens(const struct dmi_header *dm) +{ + switch (dm->type) { + case 0xd4: /* Indexed IO */ + break; + case 0xd5: /* Protected Area Type 1 */ + break; + case 0xd6: /* Protected Area Type 2 */ + break; + case 0xda: /* Calling interface */ + parse_da_table(dm); + break; + } +} + +static int find_token_location(int tokenid) +{ + int i; + for (i = 0; i < da_num_tokens; i++) { + if (da_tokens[i].tokenID == tokenid) + return da_tokens[i].location; + } + + return -1; +} + +static struct calling_interface_buffer * +dell_send_request(struct calling_interface_buffer *buffer, int class, + int select) +{ + struct smi_cmd command; + + command.magic = SMI_CMD_MAGIC; + command.command_address = da_command_address; + command.command_code = da_command_code; + command.ebx = virt_to_phys(buffer); + command.ecx = 0x42534931; + + buffer->class = class; + buffer->select = select; + + dcdbas_smi_request(&command); + + return buffer; +} + +/* Derived from information in DellWirelessCtl.cpp: + Class 17, select 11 is radio control. It returns an array of 32-bit values. + + result[0]: return code + result[1]: + Bit 0: Hardware switch supported + Bit 1: Wifi locator supported + Bit 2: Wifi is supported + Bit 3: Bluetooth is supported + Bit 4: WWAN is supported + Bit 5: Wireless keyboard supported + Bits 6-7: Reserved + Bit 8: Wifi is installed + Bit 9: Bluetooth is installed + Bit 10: WWAN is installed + Bits 11-15: Reserved + Bit 16: Hardware switch is on + Bit 17: Wifi is blocked + Bit 18: Bluetooth is blocked + Bit 19: WWAN is blocked + Bits 20-31: Reserved + result[2]: NVRAM size in bytes + result[3]: NVRAM format version number +*/ + +static int dell_rfkill_set(int radio, enum rfkill_state state) +{ + struct calling_interface_buffer buffer; + int disable = (state == RFKILL_STATE_UNBLOCKED) ? 0 : 1; + + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + buffer.input[0] = (1 | (radio<<8) | (disable << 16)); + dell_send_request(&buffer, 17, 11); + + return 0; +} + +static int dell_wifi_set(void *data, enum rfkill_state state) +{ + return dell_rfkill_set(1, state); +} + +static int dell_bluetooth_set(void *data, enum rfkill_state state) +{ + return dell_rfkill_set(2, state); +} + +static int dell_wwan_set(void *data, enum rfkill_state state) +{ + return dell_rfkill_set(3, state); +} + +static int dell_rfkill_get(int bit, enum rfkill_state *state) +{ + struct calling_interface_buffer buffer; + int status; + int new_state = RFKILL_STATE_HARD_BLOCKED; + + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + dell_send_request(&buffer, 17, 11); + status = buffer.output[1]; + + if (status & (1<<16)) + new_state = RFKILL_STATE_SOFT_BLOCKED; + + if (status & (1<<bit)) + *state = new_state; + else + *state = RFKILL_STATE_UNBLOCKED; + + return 0; +} + +static int dell_wifi_get(void *data, enum rfkill_state *state) +{ + return dell_rfkill_get(17, state); +} + +static int dell_bluetooth_get(void *data, enum rfkill_state *state) +{ + return dell_rfkill_get(18, state); +} + +static int dell_wwan_get(void *data, enum rfkill_state *state) +{ + return dell_rfkill_get(19, state); +} + +static int dell_setup_rfkill(void) +{ + struct calling_interface_buffer buffer; + int status; + int ret; + + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + dell_send_request(&buffer, 17, 11); + status = buffer.output[1]; + + if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { + wifi_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WLAN); + if (!wifi_rfkill) + goto err_wifi; + wifi_rfkill->name = "dell-wifi"; + wifi_rfkill->toggle_radio = dell_wifi_set; + wifi_rfkill->get_state = dell_wifi_get; + ret = rfkill_register(wifi_rfkill); + if (ret) + goto err_wifi; + } + + if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { + bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH); + if (!bluetooth_rfkill) + goto err_bluetooth; + bluetooth_rfkill->name = "dell-bluetooth"; + bluetooth_rfkill->toggle_radio = dell_bluetooth_set; + bluetooth_rfkill->get_state = dell_bluetooth_get; + ret = rfkill_register(bluetooth_rfkill); + if (ret) + goto err_bluetooth; + } + + if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { + wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN); + if (!wwan_rfkill) + goto err_wwan; + wwan_rfkill->name = "dell-wwan"; + wwan_rfkill->toggle_radio = dell_wwan_set; + wwan_rfkill->get_state = dell_wwan_get; + ret = rfkill_register(wwan_rfkill); + if (ret) + goto err_wwan; + } + + return 0; +err_wwan: + if (wwan_rfkill) + rfkill_free(wwan_rfkill); + if (bluetooth_rfkill) { + rfkill_unregister(bluetooth_rfkill); + bluetooth_rfkill = NULL; + } +err_bluetooth: + if (bluetooth_rfkill) + rfkill_free(bluetooth_rfkill); + if (wifi_rfkill) { + rfkill_unregister(wifi_rfkill); + wifi_rfkill = NULL; + } +err_wifi: + if (wifi_rfkill) + rfkill_free(wifi_rfkill); + + return ret; +} + +static int dell_send_intensity(struct backlight_device *bd) +{ + struct calling_interface_buffer buffer; + + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); + buffer.input[1] = bd->props.brightness; + + if (buffer.input[0] == -1) + return -ENODEV; + + if (power_supply_is_system_supplied() > 0) + dell_send_request(&buffer, 1, 2); + else + dell_send_request(&buffer, 1, 1); + + return 0; +} + +static int dell_get_intensity(struct backlight_device *bd) +{ + struct calling_interface_buffer buffer; + + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); + + if (buffer.input[0] == -1) + return -ENODEV; + + if (power_supply_is_system_supplied() > 0) + dell_send_request(&buffer, 0, 2); + else + dell_send_request(&buffer, 0, 1); + + return buffer.output[1]; +} + +static struct backlight_ops dell_ops = { + .get_brightness = dell_get_intensity, + .update_status = dell_send_intensity, +}; + +static int __init dell_init(void) +{ + struct calling_interface_buffer buffer; + int max_intensity = 0; + int ret; + + if (!dmi_check_system(dell_device_table)) + return -ENODEV; + + dmi_walk(find_tokens); + + if (!da_tokens) { + printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n"); + return -ENODEV; + } + + ret = dell_setup_rfkill(); + + if (ret) { + printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); + goto out; + } + +#ifdef CONFIG_ACPI + /* In the event of an ACPI backlight being available, don't + * register the platform controller. + */ + if (acpi_video_backlight_support()) + return 0; +#endif + + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); + + if (buffer.input[0] != -1) { + dell_send_request(&buffer, 0, 2); + max_intensity = buffer.output[3]; + } + + if (max_intensity) { + dell_backlight_device = backlight_device_register( + "dell_backlight", + NULL, NULL, + &dell_ops); + + if (IS_ERR(dell_backlight_device)) { + ret = PTR_ERR(dell_backlight_device); + dell_backlight_device = NULL; + goto out; + } + + dell_backlight_device->props.max_brightness = max_intensity; + dell_backlight_device->props.brightness = + dell_get_intensity(dell_backlight_device); + backlight_update_status(dell_backlight_device); + } + + return 0; +out: + if (wifi_rfkill) + rfkill_unregister(wifi_rfkill); + if (bluetooth_rfkill) + rfkill_unregister(bluetooth_rfkill); + if (wwan_rfkill) + rfkill_unregister(wwan_rfkill); + kfree(da_tokens); + return ret; +} + +static void __exit dell_exit(void) +{ + backlight_device_unregister(dell_backlight_device); + if (wifi_rfkill) + rfkill_unregister(wifi_rfkill); + if (bluetooth_rfkill) + rfkill_unregister(bluetooth_rfkill); + if (wwan_rfkill) + rfkill_unregister(wwan_rfkill); +} + +module_init(dell_init); +module_exit(dell_exit); + +MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); +MODULE_DESCRIPTION("Dell laptop driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 0736cff9d97a..3cf61ece71d7 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -119,7 +119,7 @@ enclosure_register(struct device *dev, const char *name, int components, edev->edev.class = &enclosure_class; edev->edev.parent = get_device(dev); edev->cb = cb; - snprintf(edev->edev.bus_id, BUS_ID_SIZE, "%s", name); + dev_set_name(&edev->edev, name); err = device_register(&edev->edev); if (err) goto err; @@ -170,7 +170,7 @@ EXPORT_SYMBOL_GPL(enclosure_unregister); static void enclosure_link_name(struct enclosure_component *cdev, char *name) { strcpy(name, "enclosure_device:"); - strcat(name, cdev->cdev.bus_id); + strcat(name, dev_name(&cdev->cdev)); } static void enclosure_remove_links(struct enclosure_component *cdev) @@ -256,9 +256,9 @@ enclosure_component_register(struct enclosure_device *edev, cdev = &ecomp->cdev; cdev->parent = get_device(&edev->edev); if (name) - snprintf(cdev->bus_id, BUS_ID_SIZE, "%s", name); + dev_set_name(cdev, name); else - snprintf(cdev->bus_id, BUS_ID_SIZE, "%u", number); + dev_set_name(cdev, "%u", number); cdev->release = enclosure_component_release; cdev->groups = enclosure_groups; diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 81152b3e360c..7957f525b2f4 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -95,11 +95,6 @@ struct xpnet_pending_msg { atomic_t use_count; }; -/* driver specific structure pointed to by the device structure */ -struct xpnet_dev_private { - struct net_device_stats stats; -}; - struct net_device *xpnet_device; /* @@ -153,7 +148,6 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) struct sk_buff *skb; void *dst; enum xp_retval ret; - struct xpnet_dev_private *priv = netdev_priv(xpnet_device); if (!XPNET_VALID_MSG(msg)) { /* @@ -161,7 +155,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) */ xpc_received(partid, channel, (void *)msg); - priv->stats.rx_errors++; + xpnet_device->stats.rx_errors++; return; } @@ -176,7 +170,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) xpc_received(partid, channel, (void *)msg); - priv->stats.rx_errors++; + xpnet_device->stats.rx_errors++; return; } @@ -226,7 +220,7 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) xpc_received(partid, channel, (void *)msg); - priv->stats.rx_errors++; + xpnet_device->stats.rx_errors++; return; } @@ -247,8 +241,8 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) skb_end_pointer(skb), skb->len); xpnet_device->last_rx = jiffies; - priv->stats.rx_packets++; - priv->stats.rx_bytes += skb->len + ETH_HLEN; + xpnet_device->stats.rx_packets++; + xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; netif_rx_ni(skb); xpc_received(partid, channel, (void *)msg); @@ -353,26 +347,6 @@ xpnet_dev_change_mtu(struct net_device *dev, int new_mtu) } /* - * Required for the net_device structure. - */ -static int -xpnet_dev_set_config(struct net_device *dev, struct ifmap *new_map) -{ - return 0; -} - -/* - * Return statistics to the caller. - */ -static struct net_device_stats * -xpnet_dev_get_stats(struct net_device *dev) -{ - struct xpnet_dev_private *priv = netdev_priv(dev); - - return &priv->stats; -} - -/* * Notification that the other end has received the message and * DMA'd the skb information. At this point, they are done with * our side. When all recipients are done processing, we @@ -453,7 +427,6 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct xpnet_pending_msg *queued_msg; u64 start_addr, end_addr; short dest_partid; - struct xpnet_dev_private *priv = netdev_priv(dev); u16 embedded_bytes = 0; dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p " @@ -476,7 +449,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping " "packet\n", sizeof(struct xpnet_pending_msg)); - priv->stats.tx_errors++; + dev->stats.tx_errors++; return -ENOMEM; } @@ -526,8 +499,8 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) kfree(queued_msg); } - priv->stats.tx_packets++; - priv->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; return 0; } @@ -538,12 +511,19 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) static void xpnet_dev_tx_timeout(struct net_device *dev) { - struct xpnet_dev_private *priv = netdev_priv(dev); - - priv->stats.tx_errors++; - return; + dev->stats.tx_errors++; } +static const struct net_device_ops xpnet_netdev_ops = { + .ndo_open = xpnet_dev_open, + .ndo_stop = xpnet_dev_stop, + .ndo_start_xmit = xpnet_dev_hard_start_xmit, + .ndo_change_mtu = xpnet_dev_change_mtu, + .ndo_tx_timeout = xpnet_dev_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __init xpnet_init(void) { @@ -563,8 +543,7 @@ xpnet_init(void) * use ether_setup() to init the majority of our device * structure and then override the necessary pieces. */ - xpnet_device = alloc_netdev(sizeof(struct xpnet_dev_private), - XPNET_DEVICE_NAME, ether_setup); + xpnet_device = alloc_netdev(0, XPNET_DEVICE_NAME, ether_setup); if (xpnet_device == NULL) { kfree(xpnet_broadcast_partitions); return -ENOMEM; @@ -573,13 +552,6 @@ xpnet_init(void) netif_carrier_off(xpnet_device); xpnet_device->mtu = XPNET_DEF_MTU; - xpnet_device->change_mtu = xpnet_dev_change_mtu; - xpnet_device->open = xpnet_dev_open; - xpnet_device->get_stats = xpnet_dev_get_stats; - xpnet_device->stop = xpnet_dev_stop; - xpnet_device->hard_start_xmit = xpnet_dev_hard_start_xmit; - xpnet_device->tx_timeout = xpnet_dev_tx_timeout; - xpnet_device->set_config = xpnet_dev_set_config; /* * Multicast assumes the LSB of the first octet is set for multicast diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 1e97916914ad..76bfe16c09b1 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -55,7 +55,6 @@ enum atmel_mci_state { struct atmel_mci_dma { #ifdef CONFIG_MMC_ATMELMCI_DMA - struct dma_client client; struct dma_chan *chan; struct dma_async_tx_descriptor *data_desc; #endif @@ -593,10 +592,8 @@ atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) /* If we don't have a channel, we can't do DMA */ chan = host->dma.chan; - if (chan) { - dma_chan_get(chan); + if (chan) host->data_chan = chan; - } if (!chan) return -ENODEV; @@ -1443,60 +1440,6 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_MMC_ATMELMCI_DMA - -static inline struct atmel_mci * -dma_client_to_atmel_mci(struct dma_client *client) -{ - return container_of(client, struct atmel_mci, dma.client); -} - -static enum dma_state_client atmci_dma_event(struct dma_client *client, - struct dma_chan *chan, enum dma_state state) -{ - struct atmel_mci *host; - enum dma_state_client ret = DMA_NAK; - - host = dma_client_to_atmel_mci(client); - - switch (state) { - case DMA_RESOURCE_AVAILABLE: - spin_lock_bh(&host->lock); - if (!host->dma.chan) { - host->dma.chan = chan; - ret = DMA_ACK; - } - spin_unlock_bh(&host->lock); - - if (ret == DMA_ACK) - dev_info(&host->pdev->dev, - "Using %s for DMA transfers\n", - chan->dev.bus_id); - break; - - case DMA_RESOURCE_REMOVED: - spin_lock_bh(&host->lock); - if (host->dma.chan == chan) { - host->dma.chan = NULL; - ret = DMA_ACK; - } - spin_unlock_bh(&host->lock); - - if (ret == DMA_ACK) - dev_info(&host->pdev->dev, - "Lost %s, falling back to PIO\n", - chan->dev.bus_id); - break; - - default: - break; - } - - - return ret; -} -#endif /* CONFIG_MMC_ATMELMCI_DMA */ - static int __init atmci_init_slot(struct atmel_mci *host, struct mci_slot_pdata *slot_data, unsigned int id, u32 sdc_reg) @@ -1600,6 +1543,18 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, mmc_free_host(slot->mmc); } +#ifdef CONFIG_MMC_ATMELMCI_DMA +static bool filter(struct dma_chan *chan, void *slave) +{ + struct dw_dma_slave *dws = slave; + + if (dws->dma_dev == chan->device->dev) + return true; + else + return false; +} +#endif + static int __init atmci_probe(struct platform_device *pdev) { struct mci_platform_data *pdata; @@ -1652,22 +1607,20 @@ static int __init atmci_probe(struct platform_device *pdev) goto err_request_irq; #ifdef CONFIG_MMC_ATMELMCI_DMA - if (pdata->dma_slave) { - struct dma_slave *slave = pdata->dma_slave; + if (pdata->dma_slave.dma_dev) { + struct dw_dma_slave *dws = &pdata->dma_slave; + dma_cap_mask_t mask; - slave->tx_reg = regs->start + MCI_TDR; - slave->rx_reg = regs->start + MCI_RDR; + dws->tx_reg = regs->start + MCI_TDR; + dws->rx_reg = regs->start + MCI_RDR; /* Try to grab a DMA channel */ - host->dma.client.event_callback = atmci_dma_event; - dma_cap_set(DMA_SLAVE, host->dma.client.cap_mask); - host->dma.client.slave = slave; - - dma_async_client_register(&host->dma.client); - dma_async_client_chan_request(&host->dma.client); - } else { - dev_notice(&pdev->dev, "DMA not available, using PIO\n"); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma.chan = dma_request_channel(mask, filter, dws); } + if (!host->dma.chan) + dev_notice(&pdev->dev, "DMA not available, using PIO\n"); #endif /* CONFIG_MMC_ATMELMCI_DMA */ platform_set_drvdata(pdev, host); @@ -1699,8 +1652,8 @@ static int __init atmci_probe(struct platform_device *pdev) err_init_slot: #ifdef CONFIG_MMC_ATMELMCI_DMA - if (pdata->dma_slave) - dma_async_client_unregister(&host->dma.client); + if (host->dma.chan) + dma_release_channel(host->dma.chan); #endif free_irq(irq, host); err_request_irq: @@ -1731,8 +1684,8 @@ static int __exit atmci_remove(struct platform_device *pdev) clk_disable(host->mck); #ifdef CONFIG_MMC_ATMELMCI_DMA - if (host->dma.client.slave) - dma_async_client_unregister(&host->dma.client); + if (host->dma.chan) + dma_release_channel(host->dma.chan); #endif free_irq(platform_get_irq(pdev, 0), host); @@ -1761,7 +1714,7 @@ static void __exit atmci_exit(void) platform_driver_unregister(&atmci_driver); } -module_init(atmci_init); +late_initcall(atmci_init); /* try to load after dma driver when built-in */ module_exit(atmci_exit); MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver"); diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index a90d50c2c3e5..7d04fb9ddcaa 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -45,6 +45,14 @@ config MTD_PARTITIONS devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. +config MTD_TESTS + tristate "MTD tests support" + depends on m + help + This option includes various MTD tests into compilation. The tests + should normally be compiled as kernel modules. The modules perform + various checks and verifications when loaded. + config MTD_REDBOOT_PARTS tristate "RedBoot partition table parsing" depends on MTD_PARTITIONS @@ -316,6 +324,8 @@ source "drivers/mtd/nand/Kconfig" source "drivers/mtd/onenand/Kconfig" +source "drivers/mtd/lpddr/Kconfig" + source "drivers/mtd/ubi/Kconfig" endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4b77335715f0..4521b1ecce45 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -29,6 +29,6 @@ obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o -obj-y += chips/ maps/ devices/ nand/ onenand/ +obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c93a8be5d5f1..f5ab6fa1057b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -58,8 +58,8 @@ static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t static int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_intelext_sync (struct mtd_info *); -static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); -static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); #ifdef CONFIG_MTD_OTP static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); @@ -558,8 +558,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) } for (i=0; i<mtd->numeraseregions;i++){ - printk(KERN_DEBUG "erase region %d: offset=0x%x,size=0x%x,blocks=%d\n", - i,mtd->eraseregions[i].offset, + printk(KERN_DEBUG "erase region %d: offset=0x%llx,size=0x%x,blocks=%d\n", + i,(unsigned long long)mtd->eraseregions[i].offset, mtd->eraseregions[i].erasesize, mtd->eraseregions[i].numblocks); } @@ -2058,7 +2058,7 @@ out: put_chip(map, chip, adr); return ret; } -static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { int ret; @@ -2082,7 +2082,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) return ret; } -static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { int ret; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d74ec46aa032..94bb61e19047 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -71,8 +71,8 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); #include "fwh_lock.h" -static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len); -static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); static struct mtd_chip_driver cfi_amdstd_chipdrv = { .probe = NULL, /* Not usable directly */ @@ -322,6 +322,14 @@ static struct cfi_fixup fixup_table[] = { }; +static void cfi_fixup_major_minor(struct cfi_private *cfi, + struct cfi_pri_amdstd *extp) +{ + if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e && + extp->MajorVersion == '0') + extp->MajorVersion = '1'; +} + struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; @@ -363,6 +371,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) return NULL; } + cfi_fixup_major_minor(cfi, extp); + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " @@ -1774,12 +1784,12 @@ out_unlock: return ret; } -static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL); } -static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); } diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d4714dd9f7ab..6c740f346f91 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -42,8 +42,8 @@ static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_staa_sync (struct mtd_info *); -static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len); -static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); static int cfi_staa_suspend (struct mtd_info *); static void cfi_staa_resume (struct mtd_info *); @@ -221,8 +221,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) } for (i=0; i<mtd->numeraseregions;i++){ - printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", - i,mtd->eraseregions[i].offset, + printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n", + i, (unsigned long long)mtd->eraseregions[i].offset, mtd->eraseregions[i].erasesize, mtd->eraseregions[i].numblocks); } @@ -964,7 +964,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd, adr += regions[i].erasesize; len -= regions[i].erasesize; - if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + if (adr % (1<< cfi->chipshift) == (((unsigned long)regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) i++; if (adr >> cfi->chipshift) { @@ -1135,7 +1135,7 @@ retry: spin_unlock_bh(chip->mutex); return 0; } -static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; @@ -1284,7 +1284,7 @@ retry: spin_unlock_bh(chip->mutex); return 0; } -static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index ab44f2b996f8..57e0e4e921f9 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h @@ -77,7 +77,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, } -static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) { int ret; @@ -88,7 +88,7 @@ static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) } -static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) { int ret; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 6fde0a2e3567..bc33200535fc 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -120,6 +120,13 @@ config MTD_PHRAM doesn't have access to, memory beyond the mem=xxx limit, nvram, memory on the video card, etc... +config MTD_PS3VRAM + tristate "PS3 video RAM" + depends on FB_PS3 + help + This driver allows you to use excess PS3 video RAM as volatile + storage or system swap. + config MTD_LART tristate "28F160xx flash driver for LART" depends on SA1100_LART diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf3923..e51521df4e40 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o +obj-$(CONFIG_MTD_PS3VRAM) += ps3vram.o diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index f4bda4cee495..578de1c67bfe 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -619,7 +619,7 @@ static struct mtd_partition lart_partitions[] = { }; #endif -int __init lart_flash_init (void) +static int __init lart_flash_init (void) { int result; memset (&mtd,0,sizeof (mtd)); @@ -690,7 +690,7 @@ int __init lart_flash_init (void) return (result); } -void __exit lart_flash_exit (void) +static void __exit lart_flash_exit (void) { #ifndef HAVE_PARTITIONS del_mtd_device (&mtd); @@ -705,5 +705,3 @@ module_exit (lart_flash_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>"); MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board"); - - diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 5733f0643843..7c3fc766dcf1 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -20,6 +20,7 @@ #include <linux/device.h> #include <linux/interrupt.h> #include <linux/mutex.h> +#include <linux/math64.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -169,9 +170,9 @@ static int wait_till_ready(struct m25p *flash) */ static int erase_chip(struct m25p *flash) { - DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB\n", - dev_name(&flash->spi->dev), __func__, - flash->mtd.size / 1024); + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n", + dev_name(&flash->spi->dev), __func__, + (long long)(flash->mtd.size >> 10)); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -232,18 +233,18 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) { struct m25p *flash = mtd_to_m25p(mtd); u32 addr,len; + uint32_t rem; - DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", - dev_name(&flash->spi->dev), __func__, "at", - (u32)instr->addr, instr->len); + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n", + dev_name(&flash->spi->dev), __func__, "at", + (long long)instr->addr, (long long)instr->len); /* sanity checks */ if (instr->addr + instr->len > flash->mtd.size) return -EINVAL; - if ((instr->addr % mtd->erasesize) != 0 - || (instr->len % mtd->erasesize) != 0) { + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) return -EINVAL; - } addr = instr->addr; len = instr->len; @@ -677,24 +678,24 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.erasesize = info->sector_size; } - dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, - flash->mtd.size / 1024); + dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, + (long long)flash->mtd.size >> 10); DEBUG(MTD_DEBUG_LEVEL2, - "mtd .name = %s, .size = 0x%.8x (%uMiB) " + "mtd .name = %s, .size = 0x%llx (%lldMiB) " ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", flash->mtd.name, - flash->mtd.size, flash->mtd.size / (1024*1024), + (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), flash->mtd.erasesize, flash->mtd.erasesize / 1024, flash->mtd.numeraseregions); if (flash->mtd.numeraseregions) for (i = 0; i < flash->mtd.numeraseregions; i++) DEBUG(MTD_DEBUG_LEVEL2, - "mtd.eraseregions[%d] = { .offset = 0x%.8x, " + "mtd.eraseregions[%d] = { .offset = 0x%llx, " ".erasesize = 0x%.8x (%uKiB), " ".numblocks = %d }\n", - i, flash->mtd.eraseregions[i].offset, + i, (long long)flash->mtd.eraseregions[i].offset, flash->mtd.eraseregions[i].erasesize, flash->mtd.eraseregions[i].erasesize / 1024, flash->mtd.eraseregions[i].numblocks); @@ -722,12 +723,12 @@ static int __devinit m25p_probe(struct spi_device *spi) if (nr_parts > 0) { for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " - "{.name = %s, .offset = 0x%.8x, " - ".size = 0x%.8x (%uKiB) }\n", + "{.name = %s, .offset = 0x%llx, " + ".size = 0x%llx (%lldKiB) }\n", i, parts[i].name, - parts[i].offset, - parts[i].size, - parts[i].size / 1024); + (long long)parts[i].offset, + (long long)parts[i].size, + (long long)(parts[i].size >> 10)); } flash->partitioned = 1; return add_mtd_partitions(&flash->mtd, parts, nr_parts); diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 65126cd668ff..d44f741ae229 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -16,6 +16,7 @@ #include <linux/device.h> #include <linux/mutex.h> #include <linux/err.h> +#include <linux/math64.h> #include <linux/spi/spi.h> #include <linux/spi/flash.h> @@ -152,15 +153,20 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) struct spi_message msg; unsigned blocksize = priv->page_size << 3; uint8_t *command; + uint32_t rem; - DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n", - dev_name(&spi->dev), - instr->addr, instr->len); + DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%llx len 0x%llx\n", + dev_name(&spi->dev), (long long)instr->addr, + (long long)instr->len); /* Sanity checks */ - if ((instr->addr + instr->len) > mtd->size - || (instr->len % priv->page_size) != 0 - || (instr->addr % priv->page_size) != 0) + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + div_u64_rem(instr->len, priv->page_size, &rem); + if (rem) + return -EINVAL; + div_u64_rem(instr->addr, priv->page_size, &rem); + if (rem) return -EINVAL; spi_message_init(&msg); @@ -178,7 +184,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) /* Calculate flash page address; use block erase (for speed) if * we're at a block boundary and need to erase the whole block. */ - pageaddr = instr->addr / priv->page_size; + pageaddr = div_u64(instr->len, priv->page_size); do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize; pageaddr = pageaddr << priv->page_offset; @@ -667,8 +673,8 @@ add_dataflash_otp(struct spi_device *spi, char *name, if (revision >= 'c') otp_tag = otp_setup(device, revision); - dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes%s\n", - name, DIV_ROUND_UP(device->size, 1024), + dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n", + name, (long long)((device->size + 1023) >> 10), pagesize, otp_tag); dev_set_drvdata(&spi->dev, priv); diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c new file mode 100644 index 000000000000..d21e9beb7ed2 --- /dev/null +++ b/drivers/mtd/devices/ps3vram.c @@ -0,0 +1,768 @@ +/** + * ps3vram - Use extra PS3 video ram as MTD block device. + * + * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com> + * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr> + */ + +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <linux/gfp.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> + +#include <asm/lv1call.h> +#include <asm/ps3.h> + +#define DEVICE_NAME "ps3vram" + +#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */ +#define XDR_IOIF 0x0c000000 + +#define FIFO_BASE XDR_IOIF +#define FIFO_SIZE (64 * 1024) + +#define DMA_PAGE_SIZE (4 * 1024) + +#define CACHE_PAGE_SIZE (256 * 1024) +#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE) + +#define CACHE_OFFSET CACHE_PAGE_SIZE +#define FIFO_OFFSET 0 + +#define CTRL_PUT 0x10 +#define CTRL_GET 0x11 +#define CTRL_TOP 0x15 + +#define UPLOAD_SUBCH 1 +#define DOWNLOAD_SUBCH 2 + +#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 + +#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 + +struct mtd_info ps3vram_mtd; + +#define CACHE_PAGE_PRESENT 1 +#define CACHE_PAGE_DIRTY 2 + +struct ps3vram_tag { + unsigned int address; + unsigned int flags; +}; + +struct ps3vram_cache { + unsigned int page_count; + unsigned int page_size; + struct ps3vram_tag *tags; +}; + +struct ps3vram_priv { + u64 memory_handle; + u64 context_handle; + u32 *ctrl; + u32 *reports; + u8 __iomem *ddr_base; + u8 *xdr_buf; + + u32 *fifo_base; + u32 *fifo_ptr; + + struct device *dev; + struct ps3vram_cache cache; + + /* Used to serialize cache/DMA operations */ + struct mutex lock; +}; + +#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */ +#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */ +#define DMA_NOTIFIER_SIZE 0x40 +#define NOTIFIER 7 /* notifier used for completion report */ + +/* A trailing '-' means to subtract off ps3fb_videomemory.size */ +char *size = "256M-"; +module_param(size, charp, 0); +MODULE_PARM_DESC(size, "memory size"); + +static u32 *ps3vram_get_notifier(u32 *reports, int notifier) +{ + return (void *) reports + + DMA_NOTIFIER_OFFSET_BASE + + DMA_NOTIFIER_SIZE * notifier; +} + +static void ps3vram_notifier_reset(struct mtd_info *mtd) +{ + int i; + + struct ps3vram_priv *priv = mtd->priv; + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + for (i = 0; i < 4; i++) + notify[i] = 0xffffffff; +} + +static int ps3vram_notifier_wait(struct mtd_info *mtd, unsigned int timeout_ms) +{ + struct ps3vram_priv *priv = mtd->priv; + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); + + do { + if (!notify[3]) + return 0; + msleep(1); + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static void ps3vram_init_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; + priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; +} + +static int ps3vram_wait_ring(struct mtd_info *mtd, unsigned int timeout_ms) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); + + do { + if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET]) + return 0; + msleep(1); + } while (time_before(jiffies, timeout)); + + dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", __func__, + __LINE__, priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET], + priv->ctrl[CTRL_TOP]); + + return -ETIMEDOUT; +} + +static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data) +{ + *(priv->fifo_ptr)++ = data; +} + +static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, + u32 tag, u32 size) +{ + ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag); +} + +static void ps3vram_rewind_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + u64 status; + + ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET)); + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; + + /* asking the HV for a blit will kick the fifo */ + status = lv1_gpu_context_attribute(priv->context_handle, + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, + 0, 0, 0, 0); + if (status) + dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", + __func__, __LINE__); + + priv->fifo_ptr = priv->fifo_base; +} + +static void ps3vram_fire_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + u64 status; + + mutex_lock(&ps3_gpu_mutex); + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET + + (priv->fifo_ptr - priv->fifo_base) * sizeof(u32); + + /* asking the HV for a blit will kick the fifo */ + status = lv1_gpu_context_attribute(priv->context_handle, + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, + 0, 0, 0, 0); + if (status) + dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", + __func__, __LINE__); + + if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) > + FIFO_SIZE - 1024) { + dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__, + __LINE__); + ps3vram_wait_ring(mtd, 200); + ps3vram_rewind_ring(mtd); + } + + mutex_unlock(&ps3_gpu_mutex); +} + +static void ps3vram_bind(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1); + ps3vram_out_ring(priv, 0x31337303); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3); + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ + + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1); + ps3vram_out_ring(priv, 0x3137c0de); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3); + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ + + ps3vram_fire_ring(mtd); +} + +static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, + unsigned int dst_offset, int len, int count) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, UPLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + ps3vram_out_ring(priv, XDR_IOIF + src_offset); + ps3vram_out_ring(priv, dst_offset); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, count); + ps3vram_out_ring(priv, (1 << 8) | 1); + ps3vram_out_ring(priv, 0); + + ps3vram_notifier_reset(mtd); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); + ps3vram_out_ring(priv, 0); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1); + ps3vram_out_ring(priv, 0); + ps3vram_fire_ring(mtd); + if (ps3vram_notifier_wait(mtd, 200) < 0) { + dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, + __LINE__); + return -1; + } + + return 0; +} + +static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, + unsigned int dst_offset, int len, int count) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + ps3vram_out_ring(priv, src_offset); + ps3vram_out_ring(priv, XDR_IOIF + dst_offset); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, count); + ps3vram_out_ring(priv, (1 << 8) | 1); + ps3vram_out_ring(priv, 0); + + ps3vram_notifier_reset(mtd); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); + ps3vram_out_ring(priv, 0); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1); + ps3vram_out_ring(priv, 0); + ps3vram_fire_ring(mtd); + if (ps3vram_notifier_wait(mtd, 200) < 0) { + dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, + __LINE__); + return -1; + } + + return 0; +} + +static void ps3vram_cache_evict(struct mtd_info *mtd, int entry) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + + if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) { + dev_dbg(priv->dev, "%s:%d: flushing %d : 0x%08x\n", __func__, + __LINE__, entry, cache->tags[entry].address); + if (ps3vram_upload(mtd, + CACHE_OFFSET + entry * cache->page_size, + cache->tags[entry].address, + DMA_PAGE_SIZE, + cache->page_size / DMA_PAGE_SIZE) < 0) { + dev_dbg(priv->dev, "%s:%d: failed to upload from " + "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, + entry * cache->page_size, + cache->tags[entry].address, cache->page_size); + } + cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY; + } +} + +static void ps3vram_cache_load(struct mtd_info *mtd, int entry, + unsigned int address) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + + dev_dbg(priv->dev, "%s:%d: fetching %d : 0x%08x\n", __func__, __LINE__, + entry, address); + if (ps3vram_download(mtd, + address, + CACHE_OFFSET + entry * cache->page_size, + DMA_PAGE_SIZE, + cache->page_size / DMA_PAGE_SIZE) < 0) { + dev_err(priv->dev, "%s:%d: failed to download from " + "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, address, + entry * cache->page_size, cache->page_size); + } + + cache->tags[entry].address = address; + cache->tags[entry].flags |= CACHE_PAGE_PRESENT; +} + + +static void ps3vram_cache_flush(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + int i; + + dev_dbg(priv->dev, "%s:%d: FLUSH\n", __func__, __LINE__); + for (i = 0; i < cache->page_count; i++) { + ps3vram_cache_evict(mtd, i); + cache->tags[i].flags = 0; + } +} + +static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + unsigned int base; + unsigned int offset; + int i; + static int counter; + + offset = (unsigned int) (address & (cache->page_size - 1)); + base = (unsigned int) (address - offset); + + /* fully associative check */ + for (i = 0; i < cache->page_count; i++) { + if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) && + cache->tags[i].address == base) { + dev_dbg(priv->dev, "%s:%d: found entry %d : 0x%08x\n", + __func__, __LINE__, i, cache->tags[i].address); + return i; + } + } + + /* choose a random entry */ + i = (jiffies + (counter++)) % cache->page_count; + dev_dbg(priv->dev, "%s:%d: using entry %d\n", __func__, __LINE__, i); + + ps3vram_cache_evict(mtd, i); + ps3vram_cache_load(mtd, i, base); + + return i; +} + +static int ps3vram_cache_init(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + priv->cache.page_count = CACHE_PAGE_COUNT; + priv->cache.page_size = CACHE_PAGE_SIZE; + priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * + CACHE_PAGE_COUNT, GFP_KERNEL); + if (priv->cache.tags == NULL) { + dev_err(priv->dev, "%s:%d: could not allocate cache tags\n", + __func__, __LINE__); + return -ENOMEM; + } + + dev_info(priv->dev, "created ram cache: %d entries, %d KiB each\n", + CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024); + + return 0; +} + +static void ps3vram_cache_cleanup(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_cache_flush(mtd); + kfree(priv->cache.tags); +} + +static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct ps3vram_priv *priv = mtd->priv; + + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + mutex_lock(&priv->lock); + + ps3vram_cache_flush(mtd); + + /* Set bytes to 0xFF */ + memset_io(priv->ddr_base + instr->addr, 0xFF, instr->len); + + mutex_unlock(&priv->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned int cached, count; + + dev_dbg(priv->dev, "%s:%d: from=0x%08x len=0x%zx\n", __func__, __LINE__, + (unsigned int)from, len); + + if (from >= mtd->size) + return -EINVAL; + + if (len > mtd->size - from) + len = mtd->size - from; + + /* Copy from vram to buf */ + count = len; + while (count) { + unsigned int offset, avail; + unsigned int entry; + + offset = (unsigned int) (from & (priv->cache.page_size - 1)); + avail = priv->cache.page_size - offset; + + mutex_lock(&priv->lock); + + entry = ps3vram_cache_match(mtd, from); + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; + + dev_dbg(priv->dev, "%s:%d: from=%08x cached=%08x offset=%08x " + "avail=%08x count=%08x\n", __func__, __LINE__, + (unsigned int)from, cached, offset, avail, count); + + if (avail > count) + avail = count; + memcpy(buf, priv->xdr_buf + cached, avail); + + mutex_unlock(&priv->lock); + + buf += avail; + count -= avail; + from += avail; + } + + *retlen = len; + return 0; +} + +static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned int cached, count; + + if (to >= mtd->size) + return -EINVAL; + + if (len > mtd->size - to) + len = mtd->size - to; + + /* Copy from buf to vram */ + count = len; + while (count) { + unsigned int offset, avail; + unsigned int entry; + + offset = (unsigned int) (to & (priv->cache.page_size - 1)); + avail = priv->cache.page_size - offset; + + mutex_lock(&priv->lock); + + entry = ps3vram_cache_match(mtd, to); + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; + + dev_dbg(priv->dev, "%s:%d: to=%08x cached=%08x offset=%08x " + "avail=%08x count=%08x\n", __func__, __LINE__, + (unsigned int)to, cached, offset, avail, count); + + if (avail > count) + avail = count; + memcpy(priv->xdr_buf + cached, buf, avail); + + priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY; + + mutex_unlock(&priv->lock); + + buf += avail; + count -= avail; + to += avail; + } + + *retlen = len; + return 0; +} + +static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) +{ + struct ps3vram_priv *priv; + int status; + u64 ddr_lpar; + u64 ctrl_lpar; + u64 info_lpar; + u64 reports_lpar; + u64 ddr_size; + u64 reports_size; + int ret = -ENOMEM; + char *rest; + + ret = -EIO; + ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL); + if (!ps3vram_mtd.priv) + goto out; + priv = ps3vram_mtd.priv; + + mutex_init(&priv->lock); + priv->dev = &dev->core; + + /* Allocate XDR buffer (1MiB aligned) */ + priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL, + get_order(XDR_BUF_SIZE)); + if (priv->xdr_buf == NULL) { + dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n", + __func__, __LINE__); + ret = -ENOMEM; + goto out_free_priv; + } + + /* Put FIFO at begginning of XDR buffer */ + priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET); + priv->fifo_ptr = priv->fifo_base; + + /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ + if (ps3_open_hv_device(dev)) { + dev_err(&dev->core, "%s:%d: ps3_open_hv_device failed\n", + __func__, __LINE__); + ret = -EAGAIN; + goto out_close_gpu; + } + + /* Request memory */ + status = -1; + ddr_size = memparse(size, &rest); + if (*rest == '-') + ddr_size -= ps3fb_videomemory.size; + ddr_size = ALIGN(ddr_size, 1024*1024); + if (ddr_size <= 0) { + dev_err(&dev->core, "%s:%d: specified size is too small\n", + __func__, __LINE__); + ret = -EINVAL; + goto out_close_gpu; + } + + while (ddr_size > 0) { + status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, + &priv->memory_handle, + &ddr_lpar); + if (!status) + break; + ddr_size -= 1024*1024; + } + if (status || ddr_size <= 0) { + dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n", + __func__, __LINE__); + ret = -ENOMEM; + goto out_free_xdr_buf; + } + + /* Request context */ + status = lv1_gpu_context_allocate(priv->memory_handle, + 0, + &priv->context_handle, + &ctrl_lpar, + &info_lpar, + &reports_lpar, + &reports_size); + if (status) { + dev_err(&dev->core, "%s:%d: lv1_gpu_context_allocate failed\n", + __func__, __LINE__); + ret = -ENOMEM; + goto out_free_memory; + } + + /* Map XDR buffer to RSX */ + status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, + ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), + XDR_BUF_SIZE, 0); + if (status) { + dev_err(&dev->core, "%s:%d: lv1_gpu_context_iomap failed\n", + __func__, __LINE__); + ret = -ENOMEM; + goto out_free_context; + } + + priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE); + + if (!priv->ddr_base) { + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); + ret = -ENOMEM; + goto out_free_context; + } + + priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); + if (!priv->ctrl) { + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); + ret = -ENOMEM; + goto out_unmap_vram; + } + + priv->reports = ioremap(reports_lpar, reports_size); + if (!priv->reports) { + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); + ret = -ENOMEM; + goto out_unmap_ctrl; + } + + mutex_lock(&ps3_gpu_mutex); + ps3vram_init_ring(&ps3vram_mtd); + mutex_unlock(&ps3_gpu_mutex); + + ps3vram_mtd.name = "ps3vram"; + ps3vram_mtd.size = ddr_size; + ps3vram_mtd.flags = MTD_CAP_RAM; + ps3vram_mtd.erase = ps3vram_erase; + ps3vram_mtd.point = NULL; + ps3vram_mtd.unpoint = NULL; + ps3vram_mtd.read = ps3vram_read; + ps3vram_mtd.write = ps3vram_write; + ps3vram_mtd.owner = THIS_MODULE; + ps3vram_mtd.type = MTD_RAM; + ps3vram_mtd.erasesize = CACHE_PAGE_SIZE; + ps3vram_mtd.writesize = 1; + + ps3vram_bind(&ps3vram_mtd); + + mutex_lock(&ps3_gpu_mutex); + ret = ps3vram_wait_ring(&ps3vram_mtd, 100); + mutex_unlock(&ps3_gpu_mutex); + if (ret < 0) { + dev_err(&dev->core, "%s:%d: failed to initialize channels\n", + __func__, __LINE__); + ret = -ETIMEDOUT; + goto out_unmap_reports; + } + + ps3vram_cache_init(&ps3vram_mtd); + + if (add_mtd_device(&ps3vram_mtd)) { + dev_err(&dev->core, "%s:%d: add_mtd_device failed\n", + __func__, __LINE__); + ret = -EAGAIN; + goto out_cache_cleanup; + } + + dev_info(&dev->core, "reserved %u MiB of gpu memory\n", + (unsigned int)(ddr_size / 1024 / 1024)); + + return 0; + +out_cache_cleanup: + ps3vram_cache_cleanup(&ps3vram_mtd); +out_unmap_reports: + iounmap(priv->reports); +out_unmap_ctrl: + iounmap(priv->ctrl); +out_unmap_vram: + iounmap(priv->ddr_base); +out_free_context: + lv1_gpu_context_free(priv->context_handle); +out_free_memory: + lv1_gpu_memory_free(priv->memory_handle); +out_close_gpu: + ps3_close_hv_device(dev); +out_free_xdr_buf: + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); +out_free_priv: + kfree(ps3vram_mtd.priv); + ps3vram_mtd.priv = NULL; +out: + return ret; +} + +static int ps3vram_shutdown(struct ps3_system_bus_device *dev) +{ + struct ps3vram_priv *priv; + + priv = ps3vram_mtd.priv; + + del_mtd_device(&ps3vram_mtd); + ps3vram_cache_cleanup(&ps3vram_mtd); + iounmap(priv->reports); + iounmap(priv->ctrl); + iounmap(priv->ddr_base); + lv1_gpu_context_free(priv->context_handle); + lv1_gpu_memory_free(priv->memory_handle); + ps3_close_hv_device(dev); + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); + kfree(priv); + return 0; +} + +static struct ps3_system_bus_driver ps3vram_driver = { + .match_id = PS3_MATCH_ID_GPU, + .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3vram_probe, + .remove = ps3vram_shutdown, + .shutdown = ps3vram_shutdown, +}; + +static int __init ps3vram_init(void) +{ + return ps3_system_bus_driver_register(&ps3vram_driver); +} + +static void __exit ps3vram_exit(void) +{ + ps3_system_bus_driver_unregister(&ps3vram_driver); +} + +module_init(ps3vram_init); +module_exit(ps3vram_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jim Paris <jim@jtan.com>"); +MODULE_DESCRIPTION("MTD driver for PS3 video RAM"); +MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK); diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 9bf581c4f740..a790c062af1f 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -109,25 +109,25 @@ module_param(shuffle_freq, int, 0); /* Each memory region corresponds to a minor device */ typedef struct partition_t { struct mtd_blktrans_dev mbd; - u_int32_t state; - u_int32_t *VirtualBlockMap; - u_int32_t *VirtualPageMap; - u_int32_t FreeTotal; + uint32_t state; + uint32_t *VirtualBlockMap; + uint32_t *VirtualPageMap; + uint32_t FreeTotal; struct eun_info_t { - u_int32_t Offset; - u_int32_t EraseCount; - u_int32_t Free; - u_int32_t Deleted; + uint32_t Offset; + uint32_t EraseCount; + uint32_t Free; + uint32_t Deleted; } *EUNInfo; struct xfer_info_t { - u_int32_t Offset; - u_int32_t EraseCount; - u_int16_t state; + uint32_t Offset; + uint32_t EraseCount; + uint16_t state; } *XferInfo; - u_int16_t bam_index; - u_int32_t *bam_cache; - u_int16_t DataUnits; - u_int32_t BlocksPerUnit; + uint16_t bam_index; + uint32_t *bam_cache; + uint16_t DataUnits; + uint32_t BlocksPerUnit; erase_unit_header_t header; } partition_t; @@ -199,8 +199,8 @@ static int scan_header(partition_t *part) static int build_maps(partition_t *part) { erase_unit_header_t header; - u_int16_t xvalid, xtrans, i; - u_int blocks, j; + uint16_t xvalid, xtrans, i; + unsigned blocks, j; int hdr_ok, ret = -1; ssize_t retval; loff_t offset; @@ -269,14 +269,14 @@ static int build_maps(partition_t *part) /* Set up virtual page map */ blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize; - part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t)); + part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t)); if (!part->VirtualBlockMap) goto out_XferInfo; - memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t)); + memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t)); part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; - part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t), + part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t), GFP_KERNEL); if (!part->bam_cache) goto out_VirtualBlockMap; @@ -290,7 +290,7 @@ static int build_maps(partition_t *part) offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); ret = part->mbd.mtd->read(part->mbd.mtd, offset, - part->BlocksPerUnit * sizeof(u_int32_t), &retval, + part->BlocksPerUnit * sizeof(uint32_t), &retval, (unsigned char *)part->bam_cache); if (ret) @@ -332,7 +332,7 @@ out: ======================================================================*/ static int erase_xfer(partition_t *part, - u_int16_t xfernum) + uint16_t xfernum) { int ret; struct xfer_info_t *xfer; @@ -408,7 +408,7 @@ static int prepare_xfer(partition_t *part, int i) erase_unit_header_t header; struct xfer_info_t *xfer; int nbam, ret; - u_int32_t ctl; + uint32_t ctl; ssize_t retlen; loff_t offset; @@ -430,15 +430,15 @@ static int prepare_xfer(partition_t *part, int i) } /* Write the BAM stub */ - nbam = (part->BlocksPerUnit * sizeof(u_int32_t) + + nbam = (part->BlocksPerUnit * sizeof(uint32_t) + le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE; offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset); ctl = cpu_to_le32(BLOCK_CONTROL); - for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { + for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) { - ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, (u_char *)&ctl); if (ret) @@ -461,18 +461,18 @@ static int prepare_xfer(partition_t *part, int i) ======================================================================*/ -static int copy_erase_unit(partition_t *part, u_int16_t srcunit, - u_int16_t xferunit) +static int copy_erase_unit(partition_t *part, uint16_t srcunit, + uint16_t xferunit) { u_char buf[SECTOR_SIZE]; struct eun_info_t *eun; struct xfer_info_t *xfer; - u_int32_t src, dest, free, i; - u_int16_t unit; + uint32_t src, dest, free, i; + uint16_t unit; int ret; ssize_t retlen; loff_t offset; - u_int16_t srcunitswap = cpu_to_le16(srcunit); + uint16_t srcunitswap = cpu_to_le16(srcunit); eun = &part->EUNInfo[srcunit]; xfer = &part->XferInfo[xferunit]; @@ -486,7 +486,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); ret = part->mbd.mtd->read(part->mbd.mtd, offset, - part->BlocksPerUnit * sizeof(u_int32_t), + part->BlocksPerUnit * sizeof(uint32_t), &retlen, (u_char *) (part->bam_cache)); /* mark the cache bad, in case we get an error later */ @@ -503,7 +503,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, offset = xfer->Offset + 20; /* Bad! */ unit = cpu_to_le16(0x7fff); - ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t), + ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen, (u_char *) &unit); if (ret) { @@ -560,7 +560,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, /* All clear? Then update the LogicalEUN again */ - ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t), + ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t), &retlen, (u_char *)&srcunitswap); if (ret) { @@ -605,8 +605,8 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, static int reclaim_block(partition_t *part) { - u_int16_t i, eun, xfer; - u_int32_t best; + uint16_t i, eun, xfer; + uint32_t best; int queued, ret; DEBUG(0, "ftl_cs: reclaiming space...\n"); @@ -723,10 +723,10 @@ static void dump_lists(partition_t *part) } #endif -static u_int32_t find_free(partition_t *part) +static uint32_t find_free(partition_t *part) { - u_int16_t stop, eun; - u_int32_t blk; + uint16_t stop, eun; + uint32_t blk; size_t retlen; int ret; @@ -749,7 +749,7 @@ static u_int32_t find_free(partition_t *part) ret = part->mbd.mtd->read(part->mbd.mtd, part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), - part->BlocksPerUnit * sizeof(u_int32_t), + part->BlocksPerUnit * sizeof(uint32_t), &retlen, (u_char *) (part->bam_cache)); if (ret) { @@ -786,7 +786,7 @@ static u_int32_t find_free(partition_t *part) static int ftl_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks) { - u_int32_t log_addr, bsize; + uint32_t log_addr, bsize; u_long i; int ret; size_t offset, retlen; @@ -829,14 +829,14 @@ static int ftl_read(partition_t *part, caddr_t buffer, ======================================================================*/ -static int set_bam_entry(partition_t *part, u_int32_t log_addr, - u_int32_t virt_addr) +static int set_bam_entry(partition_t *part, uint32_t log_addr, + uint32_t virt_addr) { - u_int32_t bsize, blk, le_virt_addr; + uint32_t bsize, blk, le_virt_addr; #ifdef PSYCHO_DEBUG - u_int32_t old_addr; + uint32_t old_addr; #endif - u_int16_t eun; + uint16_t eun; int ret; size_t retlen, offset; @@ -845,11 +845,11 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, bsize = 1 << part->header.EraseUnitSize; eun = log_addr / bsize; blk = (log_addr % bsize) / SECTOR_SIZE; - offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) + + offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) + le32_to_cpu(part->header.BAMOffset)); #ifdef PSYCHO_DEBUG - ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), + ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, (u_char *)&old_addr); if (ret) { printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); @@ -886,7 +886,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, #endif part->bam_cache[blk] = le_virt_addr; } - ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen, (u_char *)&le_virt_addr); if (ret) { @@ -900,7 +900,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, static int ftl_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks) { - u_int32_t bsize, log_addr, virt_addr, old_addr, blk; + uint32_t bsize, log_addr, virt_addr, old_addr, blk; u_long i; int ret; size_t retlen, offset; diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 50ce13887f63..73f05227dc8c 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -50,7 +50,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct INFTLrecord *inftl; unsigned long temp; - if (mtd->type != MTD_NANDFLASH) + if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) return; /* OK, this is moderately ugly. But probably safe. Alternatives? */ if (memcmp(mtd->name, "DiskOnChip", 10)) diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 9113628ed1ef..f751dd97c549 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -63,7 +63,7 @@ static int find_boot_record(struct INFTLrecord *inftl) * otherwise. */ inftl->EraseSize = inftl->mbd.mtd->erasesize; - inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize; inftl->MediaUnit = BLOCK_NIL; @@ -187,7 +187,7 @@ static int find_boot_record(struct INFTLrecord *inftl) mh->BlockMultiplierBits); inftl->EraseSize = inftl->mbd.mtd->erasesize << mh->BlockMultiplierBits; - inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; + inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize; block >>= mh->BlockMultiplierBits; } diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig new file mode 100644 index 000000000000..acd4ea9b2278 --- /dev/null +++ b/drivers/mtd/lpddr/Kconfig @@ -0,0 +1,22 @@ +# drivers/mtd/chips/Kconfig + +menu "LPDDR flash memory drivers" + depends on MTD!=n + +config MTD_LPDDR + tristate "Support for LPDDR flash chips" + select MTD_QINFO_PROBE + help + This option enables support of LPDDR (Low power double data rate) + flash chips. Synonymous with Mobile-DDR. It is a new standard for + DDR memories, intended for battery-operated systems. + +config MTD_QINFO_PROBE + tristate "Detect flash chips by QINFO probe" + help + Device Information for LPDDR chips is offered through the Overlay + Window QINFO interface, permits software to be used for entire + families of devices. This serves similar purpose of CFI on legacy + Flash products +endmenu + diff --git a/drivers/mtd/lpddr/Makefile b/drivers/mtd/lpddr/Makefile new file mode 100644 index 000000000000..da48e46b5812 --- /dev/null +++ b/drivers/mtd/lpddr/Makefile @@ -0,0 +1,6 @@ +# +# linux/drivers/mtd/lpddr/Makefile +# + +obj-$(CONFIG_MTD_QINFO_PROBE) += qinfo_probe.o +obj-$(CONFIG_MTD_LPDDR) += lpddr_cmds.o diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c new file mode 100644 index 000000000000..e22ca49583e7 --- /dev/null +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -0,0 +1,796 @@ +/* + * LPDDR flash memory device operations. This module provides read, write, + * erase, lock/unlock support for LPDDR flash memories + * (C) 2008 Korolev Alexey <akorolev@infradead.org> + * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com> + * Many thanks to Roman Borisov for intial enabling + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * TODO: + * Implement VPP management + * Implement XIP support + * Implement OTP support + */ +#include <linux/mtd/pfow.h> +#include <linux/mtd/qinfo.h> + +static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, + size_t *retlen, u_char *buf); +static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, const u_char *buf); +static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen); +static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr); +static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); +static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, + size_t *retlen, void **mtdbuf, resource_size_t *phys); +static void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len); +static int get_chip(struct map_info *map, struct flchip *chip, int mode); +static int chip_ready(struct map_info *map, struct flchip *chip, int mode); +static void put_chip(struct map_info *map, struct flchip *chip); + +struct mtd_info *lpddr_cmdset(struct map_info *map) +{ + struct lpddr_private *lpddr = map->fldrv_priv; + struct flchip_shared *shared; + struct flchip *chip; + struct mtd_info *mtd; + int numchips; + int i, j; + + mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + return NULL; + } + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->read = lpddr_read; + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; + mtd->flags &= ~MTD_BIT_WRITEABLE; + mtd->erase = lpddr_erase; + mtd->write = lpddr_write_buffers; + mtd->writev = lpddr_writev; + mtd->read_oob = NULL; + mtd->write_oob = NULL; + mtd->sync = NULL; + mtd->lock = lpddr_lock; + mtd->unlock = lpddr_unlock; + mtd->suspend = NULL; + mtd->resume = NULL; + if (map_is_linear(map)) { + mtd->point = lpddr_point; + mtd->unpoint = lpddr_unpoint; + } + mtd->block_isbad = NULL; + mtd->block_markbad = NULL; + mtd->size = 1 << lpddr->qinfo->DevSizeShift; + mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift; + mtd->writesize = 1 << lpddr->qinfo->BufSizeShift; + + shared = kmalloc(sizeof(struct flchip_shared) * lpddr->numchips, + GFP_KERNEL); + if (!shared) { + kfree(lpddr); + kfree(mtd); + return NULL; + } + + chip = &lpddr->chips[0]; + numchips = lpddr->numchips / lpddr->qinfo->HWPartsNum; + for (i = 0; i < numchips; i++) { + shared[i].writing = shared[i].erasing = NULL; + spin_lock_init(&shared[i].lock); + for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) { + *chip = lpddr->chips[i]; + chip->start += j << lpddr->chipshift; + chip->oldstate = chip->state = FL_READY; + chip->priv = &shared[i]; + /* those should be reset too since + they create memory references. */ + init_waitqueue_head(&chip->wq); + spin_lock_init(&chip->_spinlock); + chip->mutex = &chip->_spinlock; + chip++; + } + } + + return mtd; +} +EXPORT_SYMBOL(lpddr_cmdset); + +static int wait_for_ready(struct map_info *map, struct flchip *chip, + unsigned int chip_op_time) +{ + unsigned int timeo, reset_timeo, sleep_time; + unsigned int dsr; + flstate_t chip_state = chip->state; + int ret = 0; + + /* set our timeout to 8 times the expected delay */ + timeo = chip_op_time * 8; + if (!timeo) + timeo = 500000; + reset_timeo = timeo; + sleep_time = chip_op_time / 2; + + for (;;) { + dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR)); + if (dsr & DSR_READY_STATUS) + break; + if (!timeo) { + printk(KERN_ERR "%s: Flash timeout error state %d \n", + map->name, chip_state); + ret = -ETIME; + break; + } + + /* OK Still waiting. Drop the lock, wait a while and retry. */ + 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--; + } + 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 (chip->erase_suspended || chip->write_suspended) { + /* Suspend has occured while sleep: reset timeout */ + timeo = reset_timeo; + chip->erase_suspended = chip->write_suspended = 0; + } + } + /* check status for errors */ + if (dsr & DSR_ERR) { + /* Clear DSR*/ + map_write(map, CMD(~(DSR_ERR)), map->pfow_base + PFOW_DSR); + printk(KERN_WARNING"%s: Bad status on wait: 0x%x \n", + map->name, dsr); + print_drs_error(dsr); + ret = -EIO; + } + chip->state = FL_READY; + return ret; +} + +static int get_chip(struct map_info *map, struct flchip *chip, int mode) +{ + int ret; + DECLARE_WAITQUEUE(wait, current); + + retry: + if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING) + && chip->state != FL_SYNCING) { + /* + * OK. We have possibility for contension on the write/erase + * operations which are global to the real chip and not per + * partition. So let's fight it over in the partition which + * currently has authority on the operation. + * + * The rules are as follows: + * + * - any write operation must own shared->writing. + * + * - any erase operation must own _both_ shared->writing and + * shared->erasing. + * + * - contension arbitration is handled in the owner's context. + * + * The 'shared' struct can be read and/or written only when + * its lock is taken. + */ + struct flchip_shared *shared = chip->priv; + struct flchip *contender; + spin_lock(&shared->lock); + contender = shared->writing; + if (contender && contender != chip) { + /* + * The engine to perform desired operation on this + * partition is already in use by someone else. + * Let's fight over it in the context of the chip + * currently using it. If it is possible to suspend, + * that other partition will do just that, otherwise + * it'll happily send us to sleep. In any case, when + * get_chip returns success we're clear to go ahead. + */ + ret = spin_trylock(contender->mutex); + spin_unlock(&shared->lock); + if (!ret) + goto retry; + spin_unlock(chip->mutex); + ret = chip_ready(map, contender, mode); + spin_lock(chip->mutex); + + if (ret == -EAGAIN) { + spin_unlock(contender->mutex); + goto retry; + } + if (ret) { + spin_unlock(contender->mutex); + return ret; + } + spin_lock(&shared->lock); + + /* We should not own chip if it is already in FL_SYNCING + * state. Put contender and retry. */ + if (chip->state == FL_SYNCING) { + put_chip(map, contender); + spin_unlock(contender->mutex); + goto retry; + } + spin_unlock(contender->mutex); + } + + /* Check if we have suspended erase on this chip. + Must sleep in such a case. */ + if (mode == FL_ERASING && shared->erasing + && shared->erasing->oldstate == FL_ERASING) { + spin_unlock(&shared->lock); + 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); + goto retry; + } + + /* We now own it */ + shared->writing = chip; + if (mode == FL_ERASING) + shared->erasing = chip; + spin_unlock(&shared->lock); + } + + ret = chip_ready(map, chip, mode); + if (ret == -EAGAIN) + goto retry; + + return ret; +} + +static int chip_ready(struct map_info *map, struct flchip *chip, int mode) +{ + struct lpddr_private *lpddr = map->fldrv_priv; + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + /* Prevent setting state FL_SYNCING for chip in suspended state. */ + if (FL_SYNCING == mode && FL_READY != chip->oldstate) + goto sleep; + + switch (chip->state) { + case FL_READY: + case FL_JEDEC_QUERY: + return 0; + + case FL_ERASING: + if (!lpddr->qinfo->SuspEraseSupp || + !(mode == FL_READY || mode == FL_POINT)) + goto sleep; + + map_write(map, CMD(LPDDR_SUSPEND), + map->pfow_base + PFOW_PROGRAM_ERASE_SUSPEND); + chip->oldstate = FL_ERASING; + chip->state = FL_ERASE_SUSPENDING; + ret = wait_for_ready(map, chip, 0); + if (ret) { + /* Oops. something got wrong. */ + /* Resume and pretend we weren't here. */ + map_write(map, CMD(LPDDR_RESUME), + map->pfow_base + PFOW_COMMAND_CODE); + map_write(map, CMD(LPDDR_START_EXECUTION), + map->pfow_base + PFOW_COMMAND_EXECUTE); + chip->state = FL_ERASING; + chip->oldstate = FL_READY; + printk(KERN_ERR "%s: suspend operation failed." + "State may be wrong \n", map->name); + return -EIO; + } + chip->erase_suspended = 1; + chip->state = FL_READY; + return 0; + /* Erase suspend */ + case FL_POINT: + /* Only if there's no operation suspended... */ + if (mode == FL_READY && chip->oldstate == FL_READY) + return 0; + + default: +sleep: + 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); + return -EAGAIN; + } +} + +static void put_chip(struct map_info *map, struct flchip *chip) +{ + if (chip->priv) { + struct flchip_shared *shared = chip->priv; + spin_lock(&shared->lock); + if (shared->writing == chip && chip->oldstate == FL_READY) { + /* We own the ability to write, but we're done */ + shared->writing = shared->erasing; + if (shared->writing && shared->writing != chip) { + /* give back the ownership */ + struct flchip *loaner = shared->writing; + spin_lock(loaner->mutex); + spin_unlock(&shared->lock); + spin_unlock(chip->mutex); + put_chip(map, loaner); + spin_lock(chip->mutex); + spin_unlock(loaner->mutex); + wake_up(&chip->wq); + return; + } + shared->erasing = NULL; + shared->writing = NULL; + } else if (shared->erasing == chip && shared->writing != chip) { + /* + * We own the ability to erase without the ability + * to write, which means the erase was suspended + * and some other partition is currently writing. + * Don't let the switch below mess things up since + * we don't have ownership to resume anything. + */ + spin_unlock(&shared->lock); + wake_up(&chip->wq); + return; + } + spin_unlock(&shared->lock); + } + + switch (chip->oldstate) { + case FL_ERASING: + chip->state = chip->oldstate; + map_write(map, CMD(LPDDR_RESUME), + map->pfow_base + PFOW_COMMAND_CODE); + map_write(map, CMD(LPDDR_START_EXECUTION), + map->pfow_base + PFOW_COMMAND_EXECUTE); + chip->oldstate = FL_READY; + chip->state = FL_ERASING; + break; + case FL_READY: + break; + default: + printk(KERN_ERR "%s: put_chip() called with oldstate %d!\n", + map->name, chip->oldstate); + } + wake_up(&chip->wq); +} + +int do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const struct kvec **pvec, + unsigned long *pvec_seek, int len) +{ + struct lpddr_private *lpddr = map->fldrv_priv; + map_word datum; + int ret, wbufsize, word_gap, words; + const struct kvec *vec; + unsigned long vec_seek; + unsigned long prog_buf_ofs; + + wbufsize = 1 << lpddr->qinfo->BufSizeShift; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, FL_WRITING); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + /* Figure out the number of words to write */ + word_gap = (-adr & (map_bankwidth(map)-1)); + words = (len - word_gap + map_bankwidth(map) - 1) / map_bankwidth(map); + if (!word_gap) { + words--; + } else { + word_gap = map_bankwidth(map) - word_gap; + adr -= word_gap; + datum = map_word_ff(map); + } + /* Write data */ + /* Get the program buffer offset from PFOW register data first*/ + prog_buf_ofs = map->pfow_base + CMDVAL(map_read(map, + map->pfow_base + PFOW_PROGRAM_BUFFER_OFFSET)); + vec = *pvec; + vec_seek = *pvec_seek; + do { + int n = map_bankwidth(map) - word_gap; + + if (n > vec->iov_len - vec_seek) + n = vec->iov_len - vec_seek; + if (n > len) + n = len; + + if (!word_gap && (len < map_bankwidth(map))) + datum = map_word_ff(map); + + datum = map_word_load_partial(map, datum, + vec->iov_base + vec_seek, word_gap, n); + + len -= n; + word_gap += n; + if (!len || word_gap == map_bankwidth(map)) { + map_write(map, datum, prog_buf_ofs); + prog_buf_ofs += map_bankwidth(map); + word_gap = 0; + } + + vec_seek += n; + if (vec_seek == vec->iov_len) { + vec++; + vec_seek = 0; + } + } while (len); + *pvec = vec; + *pvec_seek = vec_seek; + + /* GO GO GO */ + send_pfow_command(map, LPDDR_BUFF_PROGRAM, adr, wbufsize, NULL); + chip->state = FL_WRITING; + ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->ProgBufferTime)); + if (ret) { + printk(KERN_WARNING"%s Buffer program error: %d at %lx; \n", + map->name, ret, adr); + goto out; + } + + out: put_chip(map, chip); + spin_unlock(chip->mutex); + return ret; +} + +int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) +{ + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int chipnum = adr >> lpddr->chipshift; + struct flchip *chip = &lpddr->chips[chipnum]; + int ret; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, FL_ERASING); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL); + chip->state = FL_ERASING; + ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->BlockEraseTime)*1000); + if (ret) { + printk(KERN_WARNING"%s Erase block error %d at : %llx\n", + map->name, ret, adr); + goto out; + } + out: put_chip(map, chip); + spin_unlock(chip->mutex); + return ret; +} + +static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, + size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int chipnum = adr >> lpddr->chipshift; + struct flchip *chip = &lpddr->chips[chipnum]; + int ret = 0; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, FL_READY); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + map_copy_from(map, buf, adr, len); + *retlen = len; + + put_chip(map, chip); + spin_unlock(chip->mutex); + return ret; +} + +static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, + size_t *retlen, void **mtdbuf, resource_size_t *phys) +{ + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int chipnum = adr >> lpddr->chipshift; + unsigned long ofs, last_end = 0; + struct flchip *chip = &lpddr->chips[chipnum]; + int ret = 0; + + if (!map->virt || (adr + len > mtd->size)) + return -EINVAL; + + /* ofs: offset within the first chip that the first read should start */ + ofs = adr - (chipnum << lpddr->chipshift); + + *mtdbuf = (void *)map->virt + chip->start + ofs; + *retlen = 0; + + while (len) { + unsigned long thislen; + + if (chipnum >= lpddr->numchips) + break; + + /* We cannot point across chips that are virtually disjoint */ + if (!last_end) + last_end = chip->start; + else if (chip->start != last_end) + break; + + if ((len + ofs - 1) >> lpddr->chipshift) + thislen = (1<<lpddr->chipshift) - ofs; + else + thislen = len; + /* get the chip */ + spin_lock(chip->mutex); + ret = get_chip(map, chip, FL_POINT); + spin_unlock(chip->mutex); + if (ret) + break; + + chip->state = FL_POINT; + chip->ref_point_counter++; + *retlen += thislen; + len -= thislen; + + ofs = 0; + last_end += 1 << lpddr->chipshift; + chipnum++; + chip = &lpddr->chips[chipnum]; + } + return 0; +} + +static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) +{ + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int chipnum = adr >> lpddr->chipshift; + unsigned long ofs; + + /* ofs: offset within the first chip that the first read should start */ + ofs = adr - (chipnum << lpddr->chipshift); + + while (len) { + unsigned long thislen; + struct flchip *chip; + + chip = &lpddr->chips[chipnum]; + if (chipnum >= lpddr->numchips) + break; + + if ((len + ofs - 1) >> lpddr->chipshift) + thislen = (1<<lpddr->chipshift) - ofs; + else + thislen = len; + + spin_lock(chip->mutex); + if (chip->state == FL_POINT) { + chip->ref_point_counter--; + if (chip->ref_point_counter == 0) + chip->state = FL_READY; + } else + printk(KERN_WARNING "%s: Warning: unpoint called on non" + "pointed region\n", map->name); + + put_chip(map, chip); + spin_unlock(chip->mutex); + + len -= thislen; + ofs = 0; + chipnum++; + } +} + +static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct kvec vec; + + vec.iov_base = (void *) buf; + vec.iov_len = len; + + return lpddr_writev(mtd, &vec, 1, to, retlen); +} + + +static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs, + unsigned long count, loff_t to, size_t *retlen) +{ + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs, vec_seek, i; + int wbufsize = 1 << lpddr->qinfo->BufSizeShift; + + size_t len = 0; + + for (i = 0; i < count; i++) + len += vecs[i].iov_len; + + *retlen = 0; + if (!len) + return 0; + + chipnum = to >> lpddr->chipshift; + + ofs = to; + vec_seek = 0; + + do { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) + size = len; + + ret = do_write_buffer(map, &lpddr->chips[chipnum], + ofs, &vecs, &vec_seek, size); + if (ret) + return ret; + + ofs += size; + (*retlen) += size; + len -= size; + + /* Be nice and reschedule with the chip in a usable + * state for other processes */ + cond_resched(); + + } while (len); + + return 0; +} + +static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + unsigned long ofs, len; + int ret; + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int size = 1 << lpddr->qinfo->UniformBlockSizeShift; + + ofs = instr->addr; + len = instr->len; + + if (ofs > mtd->size || (len + ofs) > mtd->size) + return -EINVAL; + + while (len > 0) { + ret = do_erase_oneblock(mtd, ofs); + if (ret) + return ret; + ofs += size; + len -= size; + } + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +#define DO_XXLOCK_LOCK 1 +#define DO_XXLOCK_UNLOCK 2 +int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) +{ + int ret = 0; + struct map_info *map = mtd->priv; + struct lpddr_private *lpddr = map->fldrv_priv; + int chipnum = adr >> lpddr->chipshift; + struct flchip *chip = &lpddr->chips[chipnum]; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, FL_LOCKING); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + if (thunk == DO_XXLOCK_LOCK) { + send_pfow_command(map, LPDDR_LOCK_BLOCK, adr, adr + len, NULL); + chip->state = FL_LOCKING; + } else if (thunk == DO_XXLOCK_UNLOCK) { + send_pfow_command(map, LPDDR_UNLOCK_BLOCK, adr, adr + len, NULL); + chip->state = FL_UNLOCKING; + } else + BUG(); + + ret = wait_for_ready(map, chip, 1); + if (ret) { + printk(KERN_ERR "%s: block unlock error status %d \n", + map->name, ret); + goto out; + } +out: put_chip(map, chip); + spin_unlock(chip->mutex); + return ret; +} + +static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + return do_xxlock(mtd, ofs, len, DO_XXLOCK_LOCK); +} + +static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + return do_xxlock(mtd, ofs, len, DO_XXLOCK_UNLOCK); +} + +int word_program(struct map_info *map, loff_t adr, uint32_t curval) +{ + int ret; + struct lpddr_private *lpddr = map->fldrv_priv; + int chipnum = adr >> lpddr->chipshift; + struct flchip *chip = &lpddr->chips[chipnum]; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, FL_WRITING); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + send_pfow_command(map, LPDDR_WORD_PROGRAM, adr, 0x00, (map_word *)&curval); + + ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->SingleWordProgTime)); + if (ret) { + printk(KERN_WARNING"%s word_program error at: %llx; val: %x\n", + map->name, adr, curval); + goto out; + } + +out: put_chip(map, chip); + spin_unlock(chip->mutex); + return ret; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexey Korolev <akorolev@infradead.org>"); +MODULE_DESCRIPTION("MTD driver for LPDDR flash chips"); diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c new file mode 100644 index 000000000000..79bf40f48b75 --- /dev/null +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -0,0 +1,255 @@ +/* + * Probing flash chips with QINFO records. + * (C) 2008 Korolev Alexey <akorolev@infradead.org> + * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/interrupt.h> + +#include <linux/mtd/xip.h> +#include <linux/mtd/map.h> +#include <linux/mtd/pfow.h> +#include <linux/mtd/qinfo.h> + +static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr); +struct mtd_info *lpddr_probe(struct map_info *map); +static struct lpddr_private *lpddr_probe_chip(struct map_info *map); +static int lpddr_pfow_present(struct map_info *map, + struct lpddr_private *lpddr); + +static struct qinfo_query_info qinfo_array[] = { + /* General device info */ + {0, 0, "DevSizeShift", "Device size 2^n bytes"}, + {0, 3, "BufSizeShift", "Program buffer size 2^n bytes"}, + /* Erase block information */ + {1, 1, "TotalBlocksNum", "Total number of blocks"}, + {1, 2, "UniformBlockSizeShift", "Uniform block size 2^n bytes"}, + /* Partition information */ + {2, 1, "HWPartsNum", "Number of hardware partitions"}, + /* Optional features */ + {5, 1, "SuspEraseSupp", "Suspend erase supported"}, + /* Operation typical time */ + {10, 0, "SingleWordProgTime", "Single word program 2^n u-sec"}, + {10, 1, "ProgBufferTime", "Program buffer write 2^n u-sec"}, + {10, 2, "BlockEraseTime", "Block erase 2^n m-sec"}, + {10, 3, "FullChipEraseTime", "Full chip erase 2^n m-sec"}, +}; + +static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str) +{ + int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info); + int i; + int bankwidth = map_bankwidth(map) * 8; + int major, minor; + + for (i = 0; i < qinfo_lines; i++) { + if (strcmp(id_str, qinfo_array[i].id_str) == 0) { + major = qinfo_array[i].major & ((1 << bankwidth) - 1); + minor = qinfo_array[i].minor & ((1 << bankwidth) - 1); + return minor | (major << bankwidth); + } + } + printk(KERN_ERR"%s qinfo id string is wrong! \n", map->name); + BUG(); + return -1; +} + +static uint16_t lpddr_info_query(struct map_info *map, char *id_str) +{ + unsigned int dsr, val; + int bits_per_chip = map_bankwidth(map) * 8; + unsigned long adr = lpddr_get_qinforec_pos(map, id_str); + int attempts = 20; + + /* Write a request for the PFOW record */ + map_write(map, CMD(LPDDR_INFO_QUERY), + map->pfow_base + PFOW_COMMAND_CODE); + map_write(map, CMD(adr & ((1 << bits_per_chip) - 1)), + map->pfow_base + PFOW_COMMAND_ADDRESS_L); + map_write(map, CMD(adr >> bits_per_chip), + map->pfow_base + PFOW_COMMAND_ADDRESS_H); + map_write(map, CMD(LPDDR_START_EXECUTION), + map->pfow_base + PFOW_COMMAND_EXECUTE); + + while ((attempts--) > 0) { + dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR)); + if (dsr & DSR_READY_STATUS) + break; + udelay(10); + } + + val = CMDVAL(map_read(map, map->pfow_base + PFOW_COMMAND_DATA)); + return val; +} + +static int lpddr_pfow_present(struct map_info *map, struct lpddr_private *lpddr) +{ + map_word pfow_val[4]; + + /* Check identification string */ + pfow_val[0] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_P); + pfow_val[1] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_F); + pfow_val[2] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_O); + pfow_val[3] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_W); + + if (!map_word_equal(map, CMD('P'), pfow_val[0])) + goto out; + + if (!map_word_equal(map, CMD('F'), pfow_val[1])) + goto out; + + if (!map_word_equal(map, CMD('O'), pfow_val[2])) + goto out; + + if (!map_word_equal(map, CMD('W'), pfow_val[3])) + goto out; + + return 1; /* "PFOW" is found */ +out: + printk(KERN_WARNING"%s: PFOW string at 0x%lx is not found \n", + map->name, map->pfow_base); + return 0; +} + +static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr) +{ + + lpddr->qinfo = kmalloc(sizeof(struct qinfo_chip), GFP_KERNEL); + if (!lpddr->qinfo) { + printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n", + map->name); + return 0; + } + memset(lpddr->qinfo, 0, sizeof(struct qinfo_chip)); + + /* Get the ManuID */ + lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID)); + /* Get the DeviceID */ + lpddr->DevId = CMDVAL(map_read(map, map->pfow_base + PFOW_DEVICE_ID)); + /* read parameters from chip qinfo table */ + lpddr->qinfo->DevSizeShift = lpddr_info_query(map, "DevSizeShift"); + lpddr->qinfo->TotalBlocksNum = lpddr_info_query(map, "TotalBlocksNum"); + lpddr->qinfo->BufSizeShift = lpddr_info_query(map, "BufSizeShift"); + lpddr->qinfo->HWPartsNum = lpddr_info_query(map, "HWPartsNum"); + lpddr->qinfo->UniformBlockSizeShift = + lpddr_info_query(map, "UniformBlockSizeShift"); + lpddr->qinfo->SuspEraseSupp = lpddr_info_query(map, "SuspEraseSupp"); + lpddr->qinfo->SingleWordProgTime = + lpddr_info_query(map, "SingleWordProgTime"); + lpddr->qinfo->ProgBufferTime = lpddr_info_query(map, "ProgBufferTime"); + lpddr->qinfo->BlockEraseTime = lpddr_info_query(map, "BlockEraseTime"); + return 1; +} +static struct lpddr_private *lpddr_probe_chip(struct map_info *map) +{ + struct lpddr_private lpddr; + struct lpddr_private *retlpddr; + int numvirtchips; + + + if ((map->pfow_base + 0x1000) >= map->size) { + printk(KERN_NOTICE"%s Probe at base (0x%08lx) past the end of" + "the map(0x%08lx)\n", map->name, + (unsigned long)map->pfow_base, map->size - 1); + return NULL; + } + memset(&lpddr, 0, sizeof(struct lpddr_private)); + if (!lpddr_pfow_present(map, &lpddr)) + return NULL; + + if (!lpddr_chip_setup(map, &lpddr)) + return NULL; + + /* Ok so we found a chip */ + lpddr.chipshift = lpddr.qinfo->DevSizeShift; + lpddr.numchips = 1; + + numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum; + retlpddr = kmalloc(sizeof(struct lpddr_private) + + numvirtchips * sizeof(struct flchip), GFP_KERNEL); + if (!retlpddr) + return NULL; + + memset(retlpddr, 0, sizeof(struct lpddr_private) + + numvirtchips * sizeof(struct flchip)); + memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private)); + + retlpddr->numchips = numvirtchips; + retlpddr->chipshift = retlpddr->qinfo->DevSizeShift - + __ffs(retlpddr->qinfo->HWPartsNum); + + return retlpddr; +} + +struct mtd_info *lpddr_probe(struct map_info *map) +{ + struct mtd_info *mtd = NULL; + struct lpddr_private *lpddr; + + /* First probe the map to see if we havecan open PFOW here */ + lpddr = lpddr_probe_chip(map); + if (!lpddr) + return NULL; + + map->fldrv_priv = lpddr; + mtd = lpddr_cmdset(map); + if (mtd) { + if (mtd->size > map->size) { + printk(KERN_WARNING "Reducing visibility of %ldKiB chip" + "to %ldKiB\n", (unsigned long)mtd->size >> 10, + (unsigned long)map->size >> 10); + mtd->size = map->size; + } + return mtd; + } + + kfree(lpddr->qinfo); + kfree(lpddr); + map->fldrv_priv = NULL; + return NULL; +} + +static struct mtd_chip_driver lpddr_chipdrv = { + .probe = lpddr_probe, + .name = "qinfo_probe", + .module = THIS_MODULE +}; + +static int __init lpddr_probe_init(void) +{ + register_mtd_chip_driver(&lpddr_chipdrv); + return 0; +} + +static void __exit lpddr_probe_exit(void) +{ + unregister_mtd_chip_driver(&lpddr_chipdrv); +} + +module_init(lpddr_probe_init); +module_exit(lpddr_probe_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vasiliy Leonenko <vasiliy.leonenko@gmail.com>"); +MODULE_DESCRIPTION("Driver to probe qinfo flash chips"); + diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 5ea169362164..0225cbbf22de 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -10,8 +10,8 @@ config MTD_COMPLEX_MAPPINGS paged mappings of flash chips. config MTD_PHYSMAP - tristate "CFI Flash device in physical memory map" - depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM + tristate "Flash device in physical memory map" + depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_LPDDR help This provides a 'mapping' driver which allows the NOR Flash and ROM driver code to communicate with chips which are mapped @@ -23,9 +23,20 @@ config MTD_PHYSMAP To compile this driver as a module, choose M here: the module will be called physmap. +config MTD_PHYSMAP_COMPAT + bool "Physmap compat support" + depends on MTD_PHYSMAP + default n + help + Setup a simple mapping via the Kconfig options. Normally the + physmap configuration options are done via your board's + resource file. + + If unsure, say N here. + config MTD_PHYSMAP_START hex "Physical start address of flash mapping" - depends on MTD_PHYSMAP + depends on MTD_PHYSMAP_COMPAT default "0x8000000" help This is the physical memory location at which the flash chips @@ -37,7 +48,7 @@ config MTD_PHYSMAP_START config MTD_PHYSMAP_LEN hex "Physical length of flash mapping" - depends on MTD_PHYSMAP + depends on MTD_PHYSMAP_COMPAT default "0" help This is the total length of the mapping of the flash chips on @@ -51,7 +62,7 @@ config MTD_PHYSMAP_LEN config MTD_PHYSMAP_BANKWIDTH int "Bank width in octets" - depends on MTD_PHYSMAP + depends on MTD_PHYSMAP_COMPAT default "2" help This is the total width of the data bus of the flash devices diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 82811bcb0436..845ad4f2a542 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -111,7 +111,7 @@ static struct mtd_partition alchemy_partitions[] = { static struct mtd_info *mymtd; -int __init alchemy_mtd_init(void) +static int __init alchemy_mtd_init(void) { struct mtd_partition *parts; int nb_parts = 0; diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index d1eec7d3243f..237733d094c4 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -232,8 +232,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, /* Trim the size if we are larger than the map */ if (map->mtd->size > map->map.size) { printk(KERN_WARNING MOD_NAME - " rom(%u) larger than window(%lu). fixing...\n", - map->mtd->size, map->map.size); + " rom(%llu) larger than window(%lu). fixing...\n", + (unsigned long long)map->mtd->size, map->map.size); map->mtd->size = map->map.size; } if (window->rsrc.parent) { diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 0ecc3f6d735b..b4ed81611918 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -88,7 +88,7 @@ struct mtd_partition flagadm_parts[] = { static struct mtd_info *mymtd; -int __init init_flagadm(void) +static int __init init_flagadm(void) { printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", FLASH_SIZE, FLASH_PHYS_ADDR); diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 1a6feb4474de..5f7a245ed132 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -263,8 +263,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, /* Trim the size if we are larger than the map */ if (map->mtd->size > map->map.size) { printk(KERN_WARNING MOD_NAME - " rom(%u) larger than window(%lu). fixing...\n", - map->mtd->size, map->map.size); + " rom(%llu) larger than window(%lu). fixing...\n", + (unsigned long long)map->mtd->size, map->map.size); map->mtd->size = map->map.size; } if (window->rsrc.parent) { diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index e115667bf1d0..cfacfa6f45dd 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -69,7 +69,7 @@ struct map_info dbox2_flash_map = { .phys = WINDOW_ADDR, }; -int __init init_dbox2_flash(void) +static int __init init_dbox2_flash(void) { printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 9433738c1664..be9e90b44587 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -71,7 +71,7 @@ static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; static int mtd_parts_nb = 0; static struct mtd_partition *mtd_parts = 0; -int __init init_edb7312nor(void) +static int __init init_edb7312nor(void) { static const char *rom_probe_types[] = PROBETYPES; const char **type; diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index bbbcdd4c8d13..11a2f57df9cf 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -324,8 +324,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, /* Trim the size if we are larger than the map */ if (map->mtd->size > map->map.size) { printk(KERN_WARNING MOD_NAME - " rom(%u) larger than window(%lu). fixing...\n", - map->mtd->size, map->map.size); + " rom(%llu) larger than window(%lu). fixing...\n", + (unsigned long long)map->mtd->size, map->map.size); map->mtd->size = map->map.size; } if (window->rsrc.parent) { diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index a8e3fde4cbd5..1e43124d498b 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -181,7 +181,7 @@ __setup("MTD_Partition=", MTD_New_Partition); /* Backwards-spelling-compatibility */ __setup("MTD_Partion=", MTD_New_Partition); -int __init init_fortunet(void) +static int __init init_fortunet(void) { int ix,iy; for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++) diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 3b959fad1c4e..72c724fa8c27 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -65,7 +65,7 @@ static const char *probes[] = { "cmdlinepart", NULL }; /* * Initialize FLASH support */ -int __init h720x_mtd_init(void) +static int __init h720x_mtd_init(void) { char *part_type = NULL; diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index aeb6c916e23f..c32bc28920b3 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -258,8 +258,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, /* Trim the size if we are larger than the map */ if (map->mtd->size > map->map.size) { printk(KERN_WARNING MOD_NAME - " rom(%u) larger than window(%lu). fixing...\n", - map->mtd->size, map->map.size); + " rom(%llu) larger than window(%lu). fixing...\n", + (unsigned long long)map->mtd->size, map->map.size); map->mtd->size = map->map.size; } if (window->rsrc.parent) { diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 2682ab51a367..998a27da97f3 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -70,7 +70,7 @@ static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; static const char *probes[] = { "cmdlinepart", NULL }; -int __init init_impa7(void) +static int __init init_impa7(void) { static const char *rom_probe_types[] = PROBETYPES; const char **type; diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c index ed58f6a77bd9..748c85f635f1 100644 --- a/drivers/mtd/maps/ipaq-flash.c +++ b/drivers/mtd/maps/ipaq-flash.c @@ -202,7 +202,7 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; static int __init h1900_special_case(void); -int __init ipaq_mtd_init(void) +static int __init ipaq_mtd_init(void) { struct mtd_partition *parts = NULL; int nb_parts = 0; diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 706f67394b07..0eb5a7c85380 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c @@ -55,7 +55,7 @@ struct map_info mbx_map = { .bankwidth = 4, }; -int __init init_mbx(void) +static int __init init_mbx(void) { printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 965e6c6d6ab0..a97133eb9d70 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -226,7 +226,7 @@ static int __init nettel_init(void) if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", - amd_mtd->size>>10); + (int)(amd_mtd->size>>10)); amd_mtd->owner = THIS_MODULE; @@ -357,13 +357,12 @@ static int __init nettel_init(void) *intel1par = 0; } - printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", - (intel_mtd->size >> 10)); + printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %lldKiB\n", + (unsigned long long)(intel_mtd->size >> 10)); intel_mtd->owner = THIS_MODULE; - num_intel_partitions = sizeof(nettel_intel_partitions) / - sizeof(nettel_intel_partitions[0]); + num_intel_partitions = ARRAY_SIZE(nettel_intel_partitions); if (intelboot) { /* diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index 43e04c1d22a9..2b2e45093218 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -184,7 +184,7 @@ void cleanup_oct5066(void) release_region(PAGE_IO, 1); } -int __init init_oct5066(void) +static int __init init_oct5066(void) { int i; int ret = 0; diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 1db16e549e38..87743661d48e 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -29,7 +29,6 @@ struct physmap_flash_info { struct map_info map[MAX_RESOURCES]; #ifdef CONFIG_MTD_PARTITIONS int nr_parts; - struct mtd_partition *parts; #endif }; @@ -56,14 +55,10 @@ static int physmap_flash_remove(struct platform_device *dev) for (i = 0; i < MAX_RESOURCES; i++) { if (info->mtd[i] != NULL) { #ifdef CONFIG_MTD_PARTITIONS - if (info->nr_parts) { + if (info->nr_parts || physmap_data->nr_parts) del_mtd_partitions(info->mtd[i]); - kfree(info->parts); - } else if (physmap_data->nr_parts) { - del_mtd_partitions(info->mtd[i]); - } else { + else del_mtd_device(info->mtd[i]); - } #else del_mtd_device(info->mtd[i]); #endif @@ -73,7 +68,12 @@ static int physmap_flash_remove(struct platform_device *dev) return 0; } -static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; +static const char *rom_probe_types[] = { + "cfi_probe", + "jedec_probe", + "qinfo_probe", + "map_rom", + NULL }; #ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; #endif @@ -86,6 +86,9 @@ static int physmap_flash_probe(struct platform_device *dev) int err = 0; int i; int devices_found = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif physmap_data = dev->dev.platform_data; if (physmap_data == NULL) @@ -119,6 +122,7 @@ static int physmap_flash_probe(struct platform_device *dev) info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; info->map[i].bankwidth = physmap_data->width; info->map[i].set_vpp = physmap_data->set_vpp; + info->map[i].pfow_base = physmap_data->pfow_base; info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys, info->map[i].size); @@ -163,9 +167,10 @@ static int physmap_flash_probe(struct platform_device *dev) goto err_out; #ifdef CONFIG_MTD_PARTITIONS - err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); + err = parse_mtd_partitions(info->cmtd, part_probe_types, &parts, 0); if (err > 0) { - add_mtd_partitions(info->cmtd, info->parts, err); + add_mtd_partitions(info->cmtd, parts, err); + kfree(parts); return 0; } @@ -251,14 +256,7 @@ static struct platform_driver physmap_flash_driver = { }; -#ifdef CONFIG_MTD_PHYSMAP_LEN -#if CONFIG_MTD_PHYSMAP_LEN != 0 -#warning using PHYSMAP compat code -#define PHYSMAP_COMPAT -#endif -#endif - -#ifdef PHYSMAP_COMPAT +#ifdef CONFIG_MTD_PHYSMAP_COMPAT static struct physmap_flash_data physmap_flash_data = { .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, }; @@ -302,7 +300,7 @@ static int __init physmap_init(void) int err; err = platform_driver_register(&physmap_flash_driver); -#ifdef PHYSMAP_COMPAT +#ifdef CONFIG_MTD_PHYSMAP_COMPAT if (err == 0) platform_device_register(&physmap_flash); #endif @@ -312,7 +310,7 @@ static int __init physmap_init(void) static void __exit physmap_exit(void) { -#ifdef PHYSMAP_COMPAT +#ifdef CONFIG_MTD_PHYSMAP_COMPAT platform_device_unregister(&physmap_flash); #endif platform_driver_unregister(&physmap_flash_driver); @@ -326,8 +324,7 @@ MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_DESCRIPTION("Generic configurable MTD map driver"); /* legacy platform drivers can't hotplug or coldplg */ -#ifndef PHYSMAP_COMPAT +#ifndef CONFIG_MTD_PHYSMAP_COMPAT /* work with hotplug and coldplug */ MODULE_ALIAS("platform:physmap-flash"); #endif - diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index f43ba2815cbb..4768bd5459d6 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -48,7 +48,7 @@ static int fcnt; #define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__) -int __init init_msp_flash(void) +static int __init init_msp_flash(void) { int i, j; int offset, coff; diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index de002eb1a7fe..933c0b63b016 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -122,7 +122,7 @@ struct map_info redwood_flash_map = { static struct mtd_info *redwood_mtd; -int __init init_redwood_flash(void) +static int __init init_redwood_flash(void) { int err; diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 14d90edb4430..3e3ef53d4fd4 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -23,7 +23,7 @@ static struct map_info rpxlite_map = { .phys = WINDOW_ADDR, }; -int __init init_rpxlite(void) +static int __init init_rpxlite(void) { printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index 6e1e99cd2b59..d5374cdcb163 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c @@ -136,7 +136,7 @@ static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS]; #endif /* CONFIG_MTD_PARTITIONS */ -int __init init_sbc8240_mtd (void) +static int __init init_sbc8240_mtd (void) { static struct _cjs { u_long addr; diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 21169e6d646c..7e329f09a548 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -118,7 +118,8 @@ scb2_fixup_mtd(struct mtd_info *mtd) struct mtd_erase_region_info *region = &mtd->eraseregions[i]; if (region->numblocks * region->erasesize > mtd->size) { - region->numblocks = (mtd->size / region->erasesize); + region->numblocks = ((unsigned long)mtd->size / + region->erasesize); done = 1; } else { region->numblocks = 0; @@ -187,8 +188,9 @@ scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) return -ENODEV; } - printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n", - scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size); + printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n", + (unsigned long long)scb2_mtd->size, + (unsigned long long)(SCB2_WINDOW - scb2_mtd->size)); add_mtd_device(scb2_mtd); diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 026eab028189..b392f096c706 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -47,7 +47,7 @@ static struct mtd_partition sharpsl_partitions[1] = { } }; -int __init init_sharpsl(void) +static int __init init_sharpsl(void) { struct mtd_partition *parts; int nb_parts = 0; diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index a5d3d8531faa..60146984f4be 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -109,7 +109,7 @@ static struct mtd_partition tqm8xxl_fs_partitions[] = { }; #endif -int __init init_tqm_mtd(void) +static int __init init_tqm_mtd(void) { int idx = 0, ret = 0; unsigned long flash_addr, flash_size, mtd_size = 0; diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 0dc645f8152f..81756e397711 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -51,7 +51,7 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, /****************************************************************************/ -int __init uclinux_mtd_init(void) +static int __init uclinux_mtd_init(void) { struct mtd_info *mtd; struct map_info *mapp; @@ -94,7 +94,7 @@ int __init uclinux_mtd_init(void) /****************************************************************************/ -void __exit uclinux_mtd_cleanup(void) +static void __exit uclinux_mtd_cleanup(void) { if (uclinux_ram_mtdinfo) { del_mtd_partitions(uclinux_ram_mtdinfo); diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index 5a0c9a353b0f..6d452dcdfe34 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -146,7 +146,7 @@ static void __exit cleanup_vmax301(void) iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START); } -int __init init_vmax301(void) +static int __init init_vmax301(void) { int i; unsigned long iomapadr; diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 413b0cf9bbd2..933a2b6598b4 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -74,7 +74,7 @@ do { \ } \ } while (0); -int __init init_sbc82xx_flash(void) +static int __init init_sbc82xx_flash(void) { volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; int bigflash; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index bcffeda2df3d..e9ec59e9a566 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -450,16 +450,20 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (!erase) ret = -ENOMEM; else { + struct erase_info_user einfo; + wait_queue_head_t waitq; DECLARE_WAITQUEUE(wait, current); init_waitqueue_head(&waitq); - if (copy_from_user(&erase->addr, argp, + if (copy_from_user(&einfo, argp, sizeof(struct erase_info_user))) { kfree(erase); return -EFAULT; } + erase->addr = einfo.start; + erase->len = einfo.length; erase->mtd = mtd; erase->callback = mtdchar_erase_callback; erase->priv = (unsigned long)&waitq; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 1a05cf37851e..3dbb1b38db66 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -197,7 +197,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, continue; } - size = min(total_len, (size_t)(subdev->size - to)); + size = min_t(uint64_t, total_len, subdev->size - to); wsize = size; /* store for future use */ entry_high = entry_low; @@ -385,7 +385,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) struct mtd_concat *concat = CONCAT(mtd); struct mtd_info *subdev; int i, err; - u_int32_t length, offset = 0; + uint64_t length, offset = 0; struct erase_info *erase; if (!(mtd->flags & MTD_WRITEABLE)) @@ -518,7 +518,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; } -static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_concat *concat = CONCAT(mtd); int i, err = -EINVAL; @@ -528,7 +528,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - size_t size; + uint64_t size; if (ofs >= subdev->size) { size = 0; @@ -556,7 +556,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) return err; } -static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_concat *concat = CONCAT(mtd); int i, err = 0; @@ -566,7 +566,7 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - size_t size; + uint64_t size; if (ofs >= subdev->size) { size = 0; @@ -696,7 +696,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c int i; size_t size; struct mtd_concat *concat; - u_int32_t max_erasesize, curr_erasesize; + uint32_t max_erasesize, curr_erasesize; int num_erase_region; printk(KERN_NOTICE "Concatenating MTD devices:\n"); @@ -842,12 +842,14 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.erasesize = curr_erasesize; concat->mtd.numeraseregions = 0; } else { + uint64_t tmp64; + /* * erase block size varies across the subdevices: allocate * space to store the data describing the variable erase regions */ struct mtd_erase_region_info *erase_region_p; - u_int32_t begin, position; + uint64_t begin, position; concat->mtd.erasesize = max_erasesize; concat->mtd.numeraseregions = num_erase_region; @@ -879,8 +881,9 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c erase_region_p->offset = begin; erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = - (position - begin) / curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; begin = position; curr_erasesize = subdev[i]->erasesize; @@ -897,9 +900,9 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c erase_region_p->offset = begin; erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = - (position - - begin) / curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; begin = position; curr_erasesize = @@ -909,14 +912,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } position += subdev[i]->eraseregions[j]. - numblocks * curr_erasesize; + numblocks * (uint64_t)curr_erasesize; } } } /* Now write the final entry */ erase_region_p->offset = begin; erase_region_p->erasesize = curr_erasesize; - erase_region_p->numblocks = (position - begin) / curr_erasesize; + tmp64 = position - begin; + do_div(tmp64, curr_erasesize); + erase_region_p->numblocks = tmp64; } return &concat->mtd; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index a9d246949820..76fe0a1e7a5e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -57,6 +57,19 @@ int add_mtd_device(struct mtd_info *mtd) mtd->index = i; mtd->usecount = 0; + if (is_power_of_2(mtd->erasesize)) + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + else + mtd->erasesize_shift = 0; + + if (is_power_of_2(mtd->writesize)) + mtd->writesize_shift = ffs(mtd->writesize) - 1; + else + mtd->writesize_shift = 0; + + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + /* Some chips always power up locked. Unlock them now */ if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { @@ -344,7 +357,8 @@ static inline int mtd_proc_info (char *buf, int i) if (!this) return 0; - return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size, + return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i, + (unsigned long long)this->size, this->erasesize, this->name); } diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index aebb3b27edbd..1a6b3beabe8d 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -80,9 +80,9 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); - printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " + printk (KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] " "on \"%s\" failed\n", - erase.addr, erase.len, mtd->name); + (unsigned long long)erase.addr, (unsigned long long)erase.len, mtd->name); return ret; } @@ -289,7 +289,10 @@ static void mtdoops_notify_add(struct mtd_info *mtd) } cxt->mtd = mtd; - cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; + if (mtd->size > INT_MAX) + cxt->oops_pages = INT_MAX / OOPS_PAGE_SIZE; + else + cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE; find_next_position(cxt); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 3728913fa5fa..144e6b613a77 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -26,7 +26,7 @@ static LIST_HEAD(mtd_partitions); struct mtd_part { struct mtd_info mtd; struct mtd_info *master; - u_int32_t offset; + uint64_t offset; int index; struct list_head list; int registered; @@ -235,7 +235,7 @@ void mtd_erase_callback(struct erase_info *instr) } EXPORT_SYMBOL_GPL(mtd_erase_callback); -static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -243,7 +243,7 @@ static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len) return part->master->lock(part->master, ofs + part->offset, len); } -static int part_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = PART(mtd); if ((len + ofs) > mtd->size) @@ -317,7 +317,7 @@ EXPORT_SYMBOL(del_mtd_partitions); static struct mtd_part *add_one_partition(struct mtd_info *master, const struct mtd_partition *part, int partno, - u_int32_t cur_offset) + uint64_t cur_offset) { struct mtd_part *slave; @@ -395,19 +395,19 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->offset = cur_offset; if (slave->offset == MTDPART_OFS_NXTBLK) { slave->offset = cur_offset; - if ((cur_offset % master->erasesize) != 0) { + if (mtd_mod_by_eb(cur_offset, master) != 0) { /* Round up to next erasesize */ - slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; + slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; printk(KERN_NOTICE "Moving partition %d: " - "0x%08x -> 0x%08x\n", partno, - cur_offset, slave->offset); + "0x%012llx -> 0x%012llx\n", partno, + (unsigned long long)cur_offset, (unsigned long long)slave->offset); } } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; - printk(KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, - slave->offset + slave->mtd.size, slave->mtd.name); + printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, + (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); /* let's do some sanity checks */ if (slave->offset >= master->size) { @@ -420,13 +420,13 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, } if (slave->offset + slave->mtd.size > master->size) { slave->mtd.size = master->size - slave->offset; - printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", - part->name, master->name, slave->mtd.size); + printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", + part->name, master->name, (unsigned long long)slave->mtd.size); } if (master->numeraseregions > 1) { /* Deal with variable erase size stuff */ int i, max = master->numeraseregions; - u32 end = slave->offset + slave->mtd.size; + u64 end = slave->offset + slave->mtd.size; struct mtd_erase_region_info *regions = master->eraseregions; /* Find the first erase regions which is part of this @@ -449,7 +449,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, } if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->offset % slave->mtd.erasesize)) { + mtd_mod_by_eb(slave->offset, &slave->mtd)) { /* 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 */ @@ -458,7 +458,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, part->name); } if ((slave->mtd.flags & MTD_WRITEABLE) && - (slave->mtd.size % slave->mtd.erasesize)) { + mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { slave->mtd.flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", part->name); @@ -466,7 +466,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.ecclayout = master->ecclayout; if (master->block_isbad) { - uint32_t offs = 0; + uint64_t offs = 0; while (offs < slave->mtd.size) { if (master->block_isbad(master, @@ -501,7 +501,7 @@ int add_mtd_partitions(struct mtd_info *master, int nbparts) { struct mtd_part *slave; - u_int32_t cur_offset = 0; + uint64_t cur_offset = 0; int i; printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f8ae0400c49c..8b12e6e109d3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -163,6 +163,13 @@ config MTD_NAND_S3C2410_HWECC incorrect ECC generation, and if using these, the default of software ECC is preferable. +config MTD_NAND_NDFC + tristate "NDFC NanD Flash Controller" + depends on 4xx + select MTD_NAND_ECC_SMC + help + NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs + config MTD_NAND_S3C2410_CLKSTOP bool "S3C2410 NAND IDLE clock stop" depends on MTD_NAND_S3C2410 diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 962380394855..6d9649159a18 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -676,11 +676,11 @@ static int alauda_probe(struct usb_interface *interface, goto error; al->write_out = usb_sndbulkpipe(al->dev, - ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + usb_endpoint_num(ep_wr)); al->bulk_in = usb_rcvbulkpipe(al->dev, - ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + usb_endpoint_num(ep_in)); al->bulk_out = usb_sndbulkpipe(al->dev, - ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + usb_endpoint_num(ep_out)); /* second device is identical up to now */ memcpy(al+1, al, sizeof(*al)); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index b8064bf3aee4..22a6b2e50e91 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -90,7 +90,7 @@ static int timing[3]; module_param_array(timing, int, &numtimings, 0644); #ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "RedBoot", NULL }; +static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; #endif /* Hrm. Why isn't this already conditional on something in the struct device? */ @@ -805,10 +805,13 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, add_mtd_device(mtd); #ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd->name = "cafe_nand"; +#endif nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); if (nr_parts > 0) { cafe->parts = parts; - dev_info(&cafe->pdev->dev, "%d RedBoot partitions found\n", nr_parts); + dev_info(&cafe->pdev->dev, "%d partitions found\n", nr_parts); add_mtd_partitions(mtd, parts, nr_parts); } #endif diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4aa5bd6158da..65929db29446 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -777,7 +777,9 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) /* Fill in fsl_elbc_mtd structure */ priv->mtd.priv = chip; priv->mtd.owner = THIS_MODULE; - priv->fmr = 0; /* rest filled in later */ + + /* Set the ECCM according to the settings in bootloader.*/ + priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM; /* fill in nand_chip structure */ /* set up function call table */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0a9c9cd33f96..0c3afccde8a2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2014,13 +2014,14 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int allowbbt) { - int page, len, status, pages_per_block, ret, chipnr; + int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; - int rewrite_bbt[NAND_MAX_CHIPS]={0}; + loff_t rewrite_bbt[NAND_MAX_CHIPS]={0}; unsigned int bbt_masked_page = 0xffffffff; + loff_t len; - DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); + DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", + (unsigned long long)instr->addr, (unsigned long long)instr->len); /* Start address must align on block boundary */ if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { @@ -2116,7 +2117,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; - instr->fail_addr = (page << chip->page_shift); + instr->fail_addr = + ((loff_t)page << chip->page_shift); goto erase_exit; } @@ -2126,7 +2128,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, */ if (bbt_masked_page != 0xffffffff && (page & BBT_PAGE_MASK) == bbt_masked_page) - rewrite_bbt[chipnr] = (page << chip->page_shift); + rewrite_bbt[chipnr] = + ((loff_t)page << chip->page_shift); /* Increment page address and decrement length */ len -= (1 << chip->phys_erase_shift); @@ -2173,7 +2176,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, continue; /* update the BBT for chip */ DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); nand_update_bbt(mtd, rewrite_bbt[chipnr]); } @@ -2365,7 +2368,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!mtd->name) mtd->name = type->name; - chip->chipsize = type->chipsize << 20; + chip->chipsize = (uint64_t)type->chipsize << 20; /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { @@ -2423,7 +2426,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; - chip->chip_shift = ffs(chip->chipsize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1; /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? @@ -2517,7 +2523,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure - * @maxchips: Number of chips to scan for * * This is the second phase of the normal nand_scan() function. It * fills out all the uninitialized function pointers with the defaults diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 0b1c48595f12..55c23e5cd210 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -171,16 +171,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { - printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); mtd->ecc_stats.bbtblocks++; continue; } /* Leave it for now, if its matured we can move this * message to MTD_DEBUG_LEVEL0 */ - printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", - ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n", + (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); /* Factory marked bad or worn out ? */ if (tmp == 0) this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); @@ -284,7 +284,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { - scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, + scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, mtd->writesize); td->version[0] = buf[mtd->writesize + td->veroffs]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", @@ -293,7 +293,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { - scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, + scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, mtd->writesize); md->version[0] = buf[mtd->writesize + md->veroffs]; printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", @@ -411,7 +411,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; - from = startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << (this->bbt_erase_shift - 1); } for (i = startblock; i < numblocks;) { @@ -428,8 +428,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); + printk(KERN_WARNING "Bad eraseblock %d at 0x%012llx\n", + i >> 1, (unsigned long long)from); mtd->ecc_stats.badblocks++; } @@ -495,7 +495,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr for (block = 0; block < td->maxblocks; block++) { int actblock = startblock + dir * block; - loff_t offs = actblock << this->bbt_erase_shift; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; /* Read first page */ scan_read_raw(mtd, buf, offs, mtd->writesize); @@ -719,7 +719,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; - einfo.addr = (unsigned long)to; + einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); if (res < 0) @@ -729,8 +729,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, if (res < 0) goto outerr; - printk(KERN_DEBUG "Bad block table written to 0x%08x, version " - "0x%02X\n", (unsigned int)to, td->version[chip]); + printk(KERN_DEBUG "Bad block table written to 0x%012llx, version " + "0x%02X\n", (unsigned long long)to, td->version[chip]); /* Mark it as used */ td->pages[chip] = page; @@ -910,7 +910,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) newval = oldval | (0x2 << (block & 0x06)); this->bbt[(block >> 3)] = newval; if ((oldval != newval) && td->reserved_block_code) - nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); continue; } update = 0; @@ -931,7 +931,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) new ones have been marked, then we need to update the stored bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); } } @@ -1027,7 +1027,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) if (!this->bbt || !td) return -EINVAL; - len = mtd->size >> (this->bbt_erase_shift + 2); /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ae7c57781a68..cd0711b83ac4 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -38,6 +38,9 @@ #include <linux/delay.h> #include <linux/list.h> #include <linux/random.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/pagemap.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -100,6 +103,7 @@ static unsigned int bitflips = 0; static char *gravepages = NULL; static unsigned int rptwear = 0; static unsigned int overridesize = 0; +static char *cache_file = NULL; module_param(first_id_byte, uint, 0400); module_param(second_id_byte, uint, 0400); @@ -122,12 +126,13 @@ module_param(bitflips, uint, 0400); module_param(gravepages, charp, 0400); module_param(rptwear, uint, 0400); module_param(overridesize, uint, 0400); +module_param(cache_file, charp, 0400); MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); -MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); +MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); @@ -153,6 +158,7 @@ MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " "The size is specified in erase blocks and as the exponent of a power of two" " e.g. 5 means a size of 32 erase blocks"); +MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); /* The largest possible page size */ #define NS_LARGEST_PAGE_SIZE 2048 @@ -266,6 +272,9 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I */ #define NS_MAX_PREVSTATES 1 +/* Maximum page cache pages needed to read or write a NAND page to the cache_file */ +#define NS_MAX_HELD_PAGES 16 + /* * A union to represent flash memory contents and flash buffer. */ @@ -295,6 +304,9 @@ struct nandsim { /* The simulated NAND flash pages array */ union ns_mem *pages; + /* Slab allocator for nand pages */ + struct kmem_cache *nand_pages_slab; + /* Internal buffer of page + OOB size bytes */ union ns_mem buf; @@ -335,6 +347,13 @@ struct nandsim { int ale; /* address Latch Enable */ int wp; /* write Protect */ } lines; + + /* Fields needed when using a cache file */ + struct file *cfile; /* Open file */ + unsigned char *pages_written; /* Which pages have been written */ + void *file_buf; + struct page *held_pages[NS_MAX_HELD_PAGES]; + int held_cnt; }; /* @@ -420,25 +439,69 @@ static struct mtd_info *nsmtd; static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; /* - * Allocate array of page pointers and initialize the array to NULL - * pointers. + * Allocate array of page pointers, create slab allocation for an array + * and initialize the array by NULL pointers. * * RETURNS: 0 if success, -ENOMEM if memory alloc fails. */ static int alloc_device(struct nandsim *ns) { - int i; + struct file *cfile; + int i, err; + + if (cache_file) { + cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); + if (IS_ERR(cfile)) + return PTR_ERR(cfile); + if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) { + NS_ERR("alloc_device: cache file not readable\n"); + err = -EINVAL; + goto err_close; + } + if (!cfile->f_op->write && !cfile->f_op->aio_write) { + NS_ERR("alloc_device: cache file not writeable\n"); + err = -EINVAL; + goto err_close; + } + ns->pages_written = vmalloc(ns->geom.pgnum); + if (!ns->pages_written) { + NS_ERR("alloc_device: unable to allocate pages written array\n"); + err = -ENOMEM; + goto err_close; + } + ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->file_buf) { + NS_ERR("alloc_device: unable to allocate file buf\n"); + err = -ENOMEM; + goto err_free; + } + ns->cfile = cfile; + memset(ns->pages_written, 0, ns->geom.pgnum); + return 0; + } ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); if (!ns->pages) { - NS_ERR("alloc_map: unable to allocate page array\n"); + NS_ERR("alloc_device: unable to allocate page array\n"); return -ENOMEM; } for (i = 0; i < ns->geom.pgnum; i++) { ns->pages[i].byte = NULL; } + ns->nand_pages_slab = kmem_cache_create("nandsim", + ns->geom.pgszoob, 0, 0, NULL); + if (!ns->nand_pages_slab) { + NS_ERR("cache_create: unable to create kmem_cache\n"); + return -ENOMEM; + } return 0; + +err_free: + vfree(ns->pages_written); +err_close: + filp_close(cfile, NULL); + return err; } /* @@ -448,11 +511,20 @@ static void free_device(struct nandsim *ns) { int i; + if (ns->cfile) { + kfree(ns->file_buf); + vfree(ns->pages_written); + filp_close(ns->cfile, NULL); + return; + } + if (ns->pages) { for (i = 0; i < ns->geom.pgnum; i++) { if (ns->pages[i].byte) - kfree(ns->pages[i].byte); + kmem_cache_free(ns->nand_pages_slab, + ns->pages[i].byte); } + kmem_cache_destroy(ns->nand_pages_slab); vfree(ns->pages); } } @@ -464,7 +536,7 @@ static char *get_partition_name(int i) return kstrdup(buf, GFP_KERNEL); } -static u_int64_t divide(u_int64_t n, u_int32_t d) +static uint64_t divide(uint64_t n, uint32_t d) { do_div(n, d); return n; @@ -480,8 +552,8 @@ static int init_nandsim(struct mtd_info *mtd) struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); int i, ret = 0; - u_int64_t remains; - u_int64_t next_offset; + uint64_t remains; + uint64_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); @@ -548,7 +620,7 @@ static int init_nandsim(struct mtd_info *mtd) remains = ns->geom.totsz; next_offset = 0; for (i = 0; i < parts_num; ++i) { - u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz; + uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz; if (!part_sz || part_sz > remains) { NS_ERR("bad partition size.\n"); @@ -1211,6 +1283,97 @@ static int find_operation(struct nandsim *ns, uint32_t flag) return -1; } +static void put_pages(struct nandsim *ns) +{ + int i; + + for (i = 0; i < ns->held_cnt; i++) + page_cache_release(ns->held_pages[i]); +} + +/* Get page cache pages in advance to provide NOFS memory allocation */ +static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos) +{ + pgoff_t index, start_index, end_index; + struct page *page; + struct address_space *mapping = file->f_mapping; + + start_index = pos >> PAGE_CACHE_SHIFT; + end_index = (pos + count - 1) >> PAGE_CACHE_SHIFT; + if (end_index - start_index + 1 > NS_MAX_HELD_PAGES) + return -EINVAL; + ns->held_cnt = 0; + for (index = start_index; index <= end_index; index++) { + page = find_get_page(mapping, index); + if (page == NULL) { + page = find_or_create_page(mapping, index, GFP_NOFS); + if (page == NULL) { + write_inode_now(mapping->host, 1); + page = find_or_create_page(mapping, index, GFP_NOFS); + } + if (page == NULL) { + put_pages(ns); + return -ENOMEM; + } + unlock_page(page); + } + ns->held_pages[ns->held_cnt++] = page; + } + return 0; +} + +static int set_memalloc(void) +{ + if (current->flags & PF_MEMALLOC) + return 0; + current->flags |= PF_MEMALLOC; + return 1; +} + +static void clear_memalloc(int memalloc) +{ + if (memalloc) + current->flags &= ~PF_MEMALLOC; +} + +static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) +{ + mm_segment_t old_fs; + ssize_t tx; + int err, memalloc; + + err = get_pages(ns, file, count, *pos); + if (err) + return err; + old_fs = get_fs(); + set_fs(get_ds()); + memalloc = set_memalloc(); + tx = vfs_read(file, (char __user *)buf, count, pos); + clear_memalloc(memalloc); + set_fs(old_fs); + put_pages(ns); + return tx; +} + +static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) +{ + mm_segment_t old_fs; + ssize_t tx; + int err, memalloc; + + err = get_pages(ns, file, count, *pos); + if (err) + return err; + old_fs = get_fs(); + set_fs(get_ds()); + memalloc = set_memalloc(); + tx = vfs_write(file, (char __user *)buf, count, pos); + clear_memalloc(memalloc); + set_fs(old_fs); + put_pages(ns); + return tx; +} + /* * Returns a pointer to the current page. */ @@ -1227,6 +1390,38 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; } +int do_read_error(struct nandsim *ns, int num) +{ + unsigned int page_no = ns->regs.row; + + if (read_error(page_no)) { + int i; + memset(ns->buf.byte, 0xFF, num); + for (i = 0; i < num; ++i) + ns->buf.byte[i] = random32(); + NS_WARN("simulating read error in page %u\n", page_no); + return 1; + } + return 0; +} + +void do_bit_flips(struct nandsim *ns, int num) +{ + if (bitflips && random32() < (1 << 22)) { + int flips = 1; + if (bitflips > 1) + flips = (random32() % (int) bitflips) + 1; + while (flips--) { + int pos = random32() % (num * 8); + ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); + NS_WARN("read_page: flipping bit %d in page %d " + "reading from %d ecc: corrected=%u failed=%u\n", + pos, ns->regs.row, ns->regs.column + ns->regs.off, + nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); + } + } +} + /* * Fill the NAND buffer with data read from the specified page. */ @@ -1234,36 +1429,40 @@ static void read_page(struct nandsim *ns, int num) { union ns_mem *mypage; + if (ns->cfile) { + if (!ns->pages_written[ns->regs.row]) { + NS_DBG("read_page: page %d not written\n", ns->regs.row); + memset(ns->buf.byte, 0xFF, num); + } else { + loff_t pos; + ssize_t tx; + + NS_DBG("read_page: page %d written, reading from %d\n", + ns->regs.row, ns->regs.column + ns->regs.off); + if (do_read_error(ns, num)) + return; + pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + tx = read_file(ns, ns->cfile, ns->buf.byte, num, &pos); + if (tx != num) { + NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); + return; + } + do_bit_flips(ns, num); + } + return; + } + mypage = NS_GET_PAGE(ns); if (mypage->byte == NULL) { NS_DBG("read_page: page %d not allocated\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { - unsigned int page_no = ns->regs.row; NS_DBG("read_page: page %d allocated, reading from %d\n", ns->regs.row, ns->regs.column + ns->regs.off); - if (read_error(page_no)) { - int i; - memset(ns->buf.byte, 0xFF, num); - for (i = 0; i < num; ++i) - ns->buf.byte[i] = random32(); - NS_WARN("simulating read error in page %u\n", page_no); + if (do_read_error(ns, num)) return; - } memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); - if (bitflips && random32() < (1 << 22)) { - int flips = 1; - if (bitflips > 1) - flips = (random32() % (int) bitflips) + 1; - while (flips--) { - int pos = random32() % (num * 8); - ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); - NS_WARN("read_page: flipping bit %d in page %d " - "reading from %d ecc: corrected=%u failed=%u\n", - pos, ns->regs.row, ns->regs.column + ns->regs.off, - nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); - } - } + do_bit_flips(ns, num); } } @@ -1275,11 +1474,20 @@ static void erase_sector(struct nandsim *ns) union ns_mem *mypage; int i; + if (ns->cfile) { + for (i = 0; i < ns->geom.pgsec; i++) + if (ns->pages_written[ns->regs.row + i]) { + NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); + ns->pages_written[ns->regs.row + i] = 0; + } + return; + } + mypage = NS_GET_PAGE(ns); for (i = 0; i < ns->geom.pgsec; i++) { if (mypage->byte != NULL) { NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); - kfree(mypage->byte); + kmem_cache_free(ns->nand_pages_slab, mypage->byte); mypage->byte = NULL; } mypage++; @@ -1295,16 +1503,57 @@ static int prog_page(struct nandsim *ns, int num) union ns_mem *mypage; u_char *pg_off; + if (ns->cfile) { + loff_t off, pos; + ssize_t tx; + int all; + + NS_DBG("prog_page: writing page %d\n", ns->regs.row); + pg_off = ns->file_buf + ns->regs.column + ns->regs.off; + off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + if (!ns->pages_written[ns->regs.row]) { + all = 1; + memset(ns->file_buf, 0xff, ns->geom.pgszoob); + } else { + all = 0; + pos = off; + tx = read_file(ns, ns->cfile, pg_off, num, &pos); + if (tx != num) { + NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + } + for (i = 0; i < num; i++) + pg_off[i] &= ns->buf.byte[i]; + if (all) { + pos = (loff_t)ns->regs.row * ns->geom.pgszoob; + tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, &pos); + if (tx != ns->geom.pgszoob) { + NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + ns->pages_written[ns->regs.row] = 1; + } else { + pos = off; + tx = write_file(ns, ns->cfile, pg_off, num, &pos); + if (tx != num) { + NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); + return -1; + } + } + return 0; + } + mypage = NS_GET_PAGE(ns); if (mypage->byte == NULL) { NS_DBG("prog_page: allocating page %d\n", ns->regs.row); /* * We allocate memory with GFP_NOFS because a flash FS may * utilize this. If it is holding an FS lock, then gets here, - * then kmalloc runs writeback which goes to the FS again - * and deadlocks. This was seen in practice. + * then kernel memory alloc runs writeback which goes to the FS + * again and deadlocks. This was seen in practice. */ - mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS); + mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS); if (mypage->byte == NULL) { NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); return -1; @@ -1736,13 +1985,17 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) /* Check if chip is expecting command */ if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { - /* - * We are in situation when something else (not command) - * was expected but command was input. In this case ignore - * previous command(s)/state(s) and accept the last one. - */ - NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " - "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + /* Do not warn if only 2 id bytes are read */ + if (!(ns->regs.command == NAND_CMD_READID && + NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) { + /* + * We are in situation when something else (not command) + * was expected but command was input. In this case ignore + * previous command(s)/state(s) and accept the last one. + */ + NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " + "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + } switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); } @@ -2044,7 +2297,7 @@ static int __init ns_init_module(void) } if (overridesize) { - u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize; + uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; if (new_size >> overridesize != nsmtd->erasesize) { NS_ERR("overridesize is too big\n"); goto err_exit; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 955959eb02d4..582cf80f555a 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -2,12 +2,20 @@ * drivers/mtd/ndfc.c * * Overview: - * Platform independend driver for NDFC (NanD Flash Controller) + * Platform independent driver for NDFC (NanD Flash Controller) * integrated into EP440 cores * + * Ported to an OF platform driver by Sean MacLennan + * + * The NDFC supports multiple chips, but this driver only supports a + * single chip since I do not have access to any boards with + * multiple chips. + * * Author: Thomas Gleixner * * Copyright 2006 IBM + * Copyright 2008 PIKA Technologies + * Sean MacLennan <smaclennan@pikatech.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 @@ -21,27 +29,20 @@ #include <linux/mtd/partitions.h> #include <linux/mtd/ndfc.h> #include <linux/mtd/mtd.h> -#include <linux/platform_device.h> - +#include <linux/of_platform.h> #include <asm/io.h> -#ifdef CONFIG_40x -#include <asm/ibm405.h> -#else -#include <asm/ibm44x.h> -#endif - -struct ndfc_nand_mtd { - struct mtd_info mtd; - struct nand_chip chip; - struct platform_nand_chip *pl_chip; -}; -static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS]; struct ndfc_controller { - void __iomem *ndfcbase; - struct nand_hw_control ndfc_control; - atomic_t childs_active; + struct of_device *ofdev; + void __iomem *ndfcbase; + struct mtd_info mtd; + struct nand_chip chip; + int chip_select; + struct nand_hw_control ndfc_control; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif }; static struct ndfc_controller ndfc_ctrl; @@ -50,17 +51,14 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) { uint32_t ccr; struct ndfc_controller *ndfc = &ndfc_ctrl; - struct nand_chip *nandchip = mtd->priv; - struct ndfc_nand_mtd *nandmtd = nandchip->priv; - struct platform_nand_chip *pchip = nandmtd->pl_chip; - ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); + ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); if (chip >= 0) { ccr &= ~NDFC_CCR_BS_MASK; - ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); + ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); } else ccr |= NDFC_CCR_RESET_CE; - __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); + out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); } static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) @@ -80,7 +78,7 @@ static int ndfc_ready(struct mtd_info *mtd) { struct ndfc_controller *ndfc = &ndfc_ctrl; - return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; + return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; } static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) @@ -88,9 +86,9 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) uint32_t ccr; struct ndfc_controller *ndfc = &ndfc_ctrl; - ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); + ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); ccr |= NDFC_CCR_RESET_ECC; - __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); + out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); wmb(); } @@ -102,9 +100,10 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, uint8_t *p = (uint8_t *)&ecc; wmb(); - ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC); - ecc_code[0] = p[1]; - ecc_code[1] = p[2]; + ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); + /* The NDFC uses Smart Media (SMC) bytes order */ + ecc_code[0] = p[2]; + ecc_code[1] = p[1]; ecc_code[2] = p[3]; return 0; @@ -123,7 +122,7 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) - *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA); + *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); } static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) @@ -132,7 +131,7 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) - __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA); + out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); } static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) @@ -141,7 +140,7 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) uint32_t *p = (uint32_t *) buf; for(;len > 0; len -= 4) - if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA)) + if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA)) return -EFAULT; return 0; } @@ -149,10 +148,19 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) /* * Initialize chip structure */ -static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) +static int ndfc_chip_init(struct ndfc_controller *ndfc, + struct device_node *node) { - struct ndfc_controller *ndfc = &ndfc_ctrl; - struct nand_chip *chip = &mtd->chip; +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + static const char *part_types[] = { "cmdlinepart", NULL }; +#else + static const char *part_types[] = { NULL }; +#endif +#endif + struct device_node *flash_np; + struct nand_chip *chip = &ndfc->chip; + int ret; chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; @@ -160,8 +168,6 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->dev_ready = ndfc_ready; chip->select_chip = ndfc_select_chip; chip->chip_delay = 50; - chip->priv = mtd; - chip->options = mtd->pl_chip->options; chip->controller = &ndfc->ndfc_control; chip->read_buf = ndfc_read_buf; chip->write_buf = ndfc_write_buf; @@ -172,143 +178,136 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 256; chip->ecc.bytes = 3; - chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout; - mtd->mtd.priv = chip; - mtd->mtd.owner = THIS_MODULE; -} - -static int ndfc_chip_probe(struct platform_device *pdev) -{ - struct platform_nand_chip *nc = pdev->dev.platform_data; - struct ndfc_chip_settings *settings = nc->priv; - struct ndfc_controller *ndfc = &ndfc_ctrl; - struct ndfc_nand_mtd *nandmtd; - - if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS) - return -EINVAL; - - /* Set the bank settings */ - __raw_writel(settings->bank_settings, - ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2)); - nandmtd = &ndfc_mtd[pdev->id]; - if (nandmtd->pl_chip) - return -EBUSY; + ndfc->mtd.priv = chip; + ndfc->mtd.owner = THIS_MODULE; - nandmtd->pl_chip = nc; - ndfc_chip_init(nandmtd); - - /* Scan for chips */ - if (nand_scan(&nandmtd->mtd, nc->nr_chips)) { - nandmtd->pl_chip = NULL; + flash_np = of_get_next_child(node, NULL); + if (!flash_np) return -ENODEV; + + ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", + ndfc->ofdev->dev.bus_id, flash_np->name); + if (!ndfc->mtd.name) { + ret = -ENOMEM; + goto err; } -#ifdef CONFIG_MTD_PARTITIONS - printk("Number of partitions %d\n", nc->nr_partitions); - if (nc->nr_partitions) { - /* Add the full device, so complete dumps can be made */ - add_mtd_device(&nandmtd->mtd); - add_mtd_partitions(&nandmtd->mtd, nc->partitions, - nc->nr_partitions); + ret = nand_scan(&ndfc->mtd, 1); + if (ret) + goto err; - } else -#else - add_mtd_device(&nandmtd->mtd); +#ifdef CONFIG_MTD_PARTITIONS + ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0); + if (ret < 0) + goto err; + +#ifdef CONFIG_MTD_OF_PARTS + if (ret == 0) { + ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np, + &ndfc->parts); + if (ret < 0) + goto err; + } #endif - atomic_inc(&ndfc->childs_active); - return 0; -} + if (ret > 0) + ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret); + else +#endif + ret = add_mtd_device(&ndfc->mtd); -static int ndfc_chip_remove(struct platform_device *pdev) -{ - return 0; +err: + of_node_put(flash_np); + if (ret) + kfree(ndfc->mtd.name); + return ret; } -static int ndfc_nand_probe(struct platform_device *pdev) +static int __devinit ndfc_probe(struct of_device *ofdev, + const struct of_device_id *match) { - struct platform_nand_ctrl *nc = pdev->dev.platform_data; - struct ndfc_controller_settings *settings = nc->priv; - struct resource *res = pdev->resource; struct ndfc_controller *ndfc = &ndfc_ctrl; - unsigned long long phys = settings->ndfc_erpn | res->start; + const u32 *reg; + u32 ccr; + int err, len; -#ifndef CONFIG_PHYS_64BIT - ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); -#else - ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); -#endif + spin_lock_init(&ndfc->ndfc_control.lock); + init_waitqueue_head(&ndfc->ndfc_control.wq); + ndfc->ofdev = ofdev; + dev_set_drvdata(&ofdev->dev, ndfc); + + /* Read the reg property to get the chip select */ + reg = of_get_property(ofdev->node, "reg", &len); + if (reg == NULL || len != 12) { + dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); + return -ENOENT; + } + ndfc->chip_select = reg[0]; + + ndfc->ndfcbase = of_iomap(ofdev->node, 0); if (!ndfc->ndfcbase) { - printk(KERN_ERR "NDFC: ioremap failed\n"); + dev_err(&ofdev->dev, "failed to get memory\n"); return -EIO; } - __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR); + ccr = NDFC_CCR_BS(ndfc->chip_select); - spin_lock_init(&ndfc->ndfc_control.lock); - init_waitqueue_head(&ndfc->ndfc_control.wq); + /* It is ok if ccr does not exist - just default to 0 */ + reg = of_get_property(ofdev->node, "ccr", NULL); + if (reg) + ccr |= *reg; - platform_set_drvdata(pdev, ndfc); + out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); - printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n", - __raw_readl(ndfc->ndfcbase + NDFC_REVID)); + /* Set the bank settings if given */ + reg = of_get_property(ofdev->node, "bank-settings", NULL); + if (reg) { + int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); + out_be32(ndfc->ndfcbase + offset, *reg); + } + + err = ndfc_chip_init(ndfc, ofdev->node); + if (err) { + iounmap(ndfc->ndfcbase); + return err; + } return 0; } -static int ndfc_nand_remove(struct platform_device *pdev) +static int __devexit ndfc_remove(struct of_device *ofdev) { - struct ndfc_controller *ndfc = platform_get_drvdata(pdev); + struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); - if (atomic_read(&ndfc->childs_active)) - return -EBUSY; + nand_release(&ndfc->mtd); - if (ndfc) { - platform_set_drvdata(pdev, NULL); - iounmap(ndfc_ctrl.ndfcbase); - ndfc_ctrl.ndfcbase = NULL; - } return 0; } -/* driver device registration */ - -static struct platform_driver ndfc_chip_driver = { - .probe = ndfc_chip_probe, - .remove = ndfc_chip_remove, - .driver = { - .name = "ndfc-chip", - .owner = THIS_MODULE, - }, +static const struct of_device_id ndfc_match[] = { + { .compatible = "ibm,ndfc", }, + {} }; +MODULE_DEVICE_TABLE(of, ndfc_match); -static struct platform_driver ndfc_nand_driver = { - .probe = ndfc_nand_probe, - .remove = ndfc_nand_remove, - .driver = { - .name = "ndfc-nand", - .owner = THIS_MODULE, +static struct of_platform_driver ndfc_driver = { + .driver = { + .name = "ndfc", }, + .match_table = ndfc_match, + .probe = ndfc_probe, + .remove = __devexit_p(ndfc_remove), }; static int __init ndfc_nand_init(void) { - int ret; - - spin_lock_init(&ndfc_ctrl.ndfc_control.lock); - init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq); - - ret = platform_driver_register(&ndfc_nand_driver); - if (!ret) - ret = platform_driver_register(&ndfc_chip_driver); - return ret; + return of_register_platform_driver(&ndfc_driver); } static void __exit ndfc_nand_exit(void) { - platform_driver_unregister(&ndfc_chip_driver); - platform_driver_unregister(&ndfc_nand_driver); + of_unregister_platform_driver(&ndfc_driver); } module_init(ndfc_nand_init); @@ -316,6 +315,4 @@ module_exit(ndfc_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); -MODULE_DESCRIPTION("Platform driver for NDFC"); -MODULE_ALIAS("platform:ndfc-chip"); -MODULE_ALIAS("platform:ndfc-nand"); +MODULE_DESCRIPTION("OF Platform driver for NDFC"); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index fc4144495610..cc55cbc2b308 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -298,7 +298,7 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = { #define NDTR1_tAR(c) (min((c), 15) << 0) /* convert nano-seconds to nand flash controller clock cycles */ -#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) +#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_timing *t) @@ -368,14 +368,14 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, /* large block, 2 cycles for column address * row address starts from 3rd cycle */ - info->ndcb1 |= (page_addr << 16) | (column & 0xffff); + info->ndcb1 |= page_addr << 16; if (info->row_addr_cycles == 3) info->ndcb2 = (page_addr >> 16) & 0xff; } else /* small block, 1 cycles for column address * row address starts from 2nd cycle */ - info->ndcb1 = (page_addr << 8) | (column & 0xff); + info->ndcb1 = page_addr << 8; if (cmd == cmdset->program) info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 30a518e211bd..54ec7542a7b7 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -2,6 +2,7 @@ * drivers/mtd/nand/sharpsl.c * * Copyright (C) 2004 Richard Purdie + * Copyright (C) 2008 Dmitry Baryshkov * * Based on Sharp's NAND driver sharp_sl.c * @@ -19,22 +20,31 @@ #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/sharpsl.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> + #include <asm/io.h> #include <mach/hardware.h> #include <asm/mach-types.h> -static void __iomem *sharpsl_io_base; -static int sharpsl_phys_base = 0x0C000000; +struct sharpsl_nand { + struct mtd_info mtd; + struct nand_chip chip; + + void __iomem *io; +}; + +#define mtd_to_sharpsl(_mtd) container_of(_mtd, struct sharpsl_nand, mtd) /* register offset */ -#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ -#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ -#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ -#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ -#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ -#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ -#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ +#define ECCLPLB 0x00 /* line parity 7 - 0 bit */ +#define ECCLPUB 0x04 /* line parity 15 - 8 bit */ +#define ECCCP 0x08 /* column parity 5 - 0 bit */ +#define ECCCNTR 0x0C /* ECC byte counter */ +#define ECCCLRR 0x10 /* cleare ECC */ +#define FLASHIO 0x14 /* Flash I/O */ +#define FLASHCTL 0x18 /* Flash Control */ /* Flash control bit */ #define FLRYBY (1 << 5) @@ -45,35 +55,6 @@ static int sharpsl_phys_base = 0x0C000000; #define FLCE0 (1 << 0) /* - * MTD structure for SharpSL - */ -static struct mtd_info *sharpsl_mtd = NULL; - -/* - * Define partitions for flash device - */ -#define DEFAULT_NUM_PARTITIONS 3 - -static int nr_partitions; -static struct mtd_partition sharpsl_nand_default_partition_info[] = { - { - .name = "System Area", - .offset = 0, - .size = 7 * 1024 * 1024, - }, - { - .name = "Root Filesystem", - .offset = 7 * 1024 * 1024, - .size = 30 * 1024 * 1024, - }, - { - .name = "Home Filesystem", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL, - }, -}; - -/* * hardware specific access to control-lines * ctrl: * NAND_CNE: bit 0 -> ! bit 0 & 4 @@ -84,6 +65,7 @@ static struct mtd_partition sharpsl_nand_default_partition_info[] = { static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); struct nand_chip *chip = mtd->priv; if (ctrl & NAND_CTRL_CHANGE) { @@ -93,103 +75,97 @@ static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, bits ^= 0x11; - writeb((readb(FLASHCTL) & ~0x17) | bits, FLASHCTL); + writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); } if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W); } -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -static struct nand_bbt_descr sharpsl_bbt = { - .options = 0, - .offs = 4, - .len = 2, - .pattern = scan_ff_pattern -}; - -static struct nand_bbt_descr sharpsl_akita_bbt = { - .options = 0, - .offs = 4, - .len = 1, - .pattern = scan_ff_pattern -}; - -static struct nand_ecclayout akita_oobinfo = { - .eccbytes = 24, - .eccpos = { - 0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11, - 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23, - 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37}, - .oobfree = {{0x08, 0x09}} -}; - static int sharpsl_nand_dev_ready(struct mtd_info *mtd) { - return !((readb(FLASHCTL) & FLRYBY) == 0); + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); } static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) { - writeb(0, ECCCLRR); + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + writeb(0, sharpsl->io + ECCCLRR); } static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) { - ecc_code[0] = ~readb(ECCLPUB); - ecc_code[1] = ~readb(ECCLPLB); - ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; - return readb(ECCCNTR) != 0; + struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); + ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); + ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); + ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; + return readb(sharpsl->io + ECCCNTR) != 0; } #ifdef CONFIG_MTD_PARTITIONS -const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *part_probes[] = { "cmdlinepart", NULL }; #endif /* * Main initialization routine */ -static int __init sharpsl_nand_init(void) +static int __devinit sharpsl_nand_probe(struct platform_device *pdev) { struct nand_chip *this; +#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *sharpsl_partition_info; + int nr_partitions; +#endif + struct resource *r; int err = 0; + struct sharpsl_nand *sharpsl; + struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; + + if (!data) { + dev_err(&pdev->dev, "no platform data!\n"); + return -EINVAL; + } /* Allocate memory for MTD device structure and private data */ - sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!sharpsl_mtd) { + sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); + if (!sharpsl) { printk("Unable to allocate SharpSL NAND MTD device structure.\n"); return -ENOMEM; } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no io memory resource defined!\n"); + err = -ENODEV; + goto err_get_res; + } + /* map physical address */ - sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); - if (!sharpsl_io_base) { + sharpsl->io = ioremap(r->start, resource_size(r)); + if (!sharpsl->io) { printk("ioremap to access Sharp SL NAND chip failed\n"); - kfree(sharpsl_mtd); - return -EIO; + err = -EIO; + goto err_ioremap; } /* Get pointer to private data */ - this = (struct nand_chip *)(&sharpsl_mtd[1]); - - /* Initialize structures */ - memset(sharpsl_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); + this = (struct nand_chip *)(&sharpsl->chip); /* Link the private data with the MTD structure */ - sharpsl_mtd->priv = this; - sharpsl_mtd->owner = THIS_MODULE; + sharpsl->mtd.priv = this; + sharpsl->mtd.owner = THIS_MODULE; + + platform_set_drvdata(pdev, sharpsl); /* * PXA initialize */ - writeb(readb(FLASHCTL) | FLWP, FLASHCTL); + writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); /* Set address of NAND IO lines */ - this->IO_ADDR_R = FLASHIO; - this->IO_ADDR_W = FLASHIO; + this->IO_ADDR_R = sharpsl->io + FLASHIO; + this->IO_ADDR_W = sharpsl->io + FLASHIO; /* Set address of hardware control function */ this->cmd_ctrl = sharpsl_nand_hwcontrol; this->dev_ready = sharpsl_nand_dev_ready; @@ -199,68 +175,89 @@ static int __init sharpsl_nand_init(void) this->ecc.mode = NAND_ECC_HW; this->ecc.size = 256; this->ecc.bytes = 3; - this->badblock_pattern = &sharpsl_bbt; - if (machine_is_akita() || machine_is_borzoi()) { - this->badblock_pattern = &sharpsl_akita_bbt; - this->ecc.layout = &akita_oobinfo; - } + this->badblock_pattern = data->badblock_pattern; + this->ecc.layout = data->ecc_layout; this->ecc.hwctl = sharpsl_nand_enable_hwecc; this->ecc.calculate = sharpsl_nand_calculate_ecc; this->ecc.correct = nand_correct_data; /* Scan to find existence of the device */ - err = nand_scan(sharpsl_mtd, 1); - if (err) { - iounmap(sharpsl_io_base); - kfree(sharpsl_mtd); - return err; - } + err = nand_scan(&sharpsl->mtd, 1); + if (err) + goto err_scan; /* Register the partitions */ - sharpsl_mtd->name = "sharpsl-nand"; - nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, &sharpsl_partition_info, 0); - + sharpsl->mtd.name = "sharpsl-nand"; +#ifdef CONFIG_MTD_PARTITIONS + nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0); if (nr_partitions <= 0) { - nr_partitions = DEFAULT_NUM_PARTITIONS; - sharpsl_partition_info = sharpsl_nand_default_partition_info; - if (machine_is_poodle()) { - sharpsl_partition_info[1].size = 22 * 1024 * 1024; - } else if (machine_is_corgi() || machine_is_shepherd()) { - sharpsl_partition_info[1].size = 25 * 1024 * 1024; - } else if (machine_is_husky()) { - sharpsl_partition_info[1].size = 53 * 1024 * 1024; - } else if (machine_is_spitz()) { - sharpsl_partition_info[1].size = 5 * 1024 * 1024; - } else if (machine_is_akita()) { - sharpsl_partition_info[1].size = 58 * 1024 * 1024; - } else if (machine_is_borzoi()) { - sharpsl_partition_info[1].size = 32 * 1024 * 1024; - } + nr_partitions = data->nr_partitions; + sharpsl_partition_info = data->partitions; } - add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); + if (nr_partitions > 0) + err = add_mtd_partitions(&sharpsl->mtd, sharpsl_partition_info, nr_partitions); + else +#endif + err = add_mtd_device(&sharpsl->mtd); + if (err) + goto err_add; /* Return happy */ return 0; -} -module_init(sharpsl_nand_init); +err_add: + nand_release(&sharpsl->mtd); + +err_scan: + platform_set_drvdata(pdev, NULL); + iounmap(sharpsl->io); +err_ioremap: +err_get_res: + kfree(sharpsl); + return err; +} /* * Clean up routine */ -static void __exit sharpsl_nand_cleanup(void) +static int __devexit sharpsl_nand_remove(struct platform_device *pdev) { + struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); + /* Release resources, unregister device */ - nand_release(sharpsl_mtd); + nand_release(&sharpsl->mtd); - iounmap(sharpsl_io_base); + platform_set_drvdata(pdev, NULL); + + iounmap(sharpsl->io); /* Free the MTD device structure */ - kfree(sharpsl_mtd); + kfree(sharpsl); + + return 0; +} + +static struct platform_driver sharpsl_nand_driver = { + .driver = { + .name = "sharpsl-nand", + .owner = THIS_MODULE, + }, + .probe = sharpsl_nand_probe, + .remove = __devexit_p(sharpsl_nand_remove), +}; + +static int __init sharpsl_nand_init(void) +{ + return platform_driver_register(&sharpsl_nand_driver); } +module_init(sharpsl_nand_init); -module_exit(sharpsl_nand_cleanup); +static void __exit sharpsl_nand_exit(void) +{ + platform_driver_unregister(&sharpsl_nand_driver); +} +module_exit(sharpsl_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 320b929abe79..d1c4546513f7 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -39,7 +39,7 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct NFTLrecord *nftl; unsigned long temp; - if (mtd->type != MTD_NANDFLASH) + if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) return; /* OK, this is moderately ugly. But probably safe. Alternatives? */ if (memcmp(mtd->name, "DiskOnChip", 10)) diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index ccc4f209fbb5..8b22b1836e9f 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -51,7 +51,7 @@ static int find_boot_record(struct NFTLrecord *nftl) the mtd device accordingly. We could even get rid of nftl->EraseSize if there were any point in doing so. */ nftl->EraseSize = nftl->mbd.mtd->erasesize; - nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize; nftl->MediaUnit = BLOCK_NIL; nftl->SpareMediaUnit = BLOCK_NIL; @@ -168,7 +168,7 @@ device is already correct. printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", mh->UnitSizeFactor); nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); - nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize; } #endif nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 90ed319f26e6..529af271db17 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1772,7 +1772,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) int len; int ret = 0; - DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); + DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); block_size = (1 << this->erase_shift); @@ -1810,7 +1810,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Check if we have a bad block, we do not erase bad blocks */ if (onenand_block_isbad_nolock(mtd, addr, 0)) { - printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); + printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%012llx\n", (unsigned long long) addr); instr->state = MTD_ERASE_FAILED; goto erase_exit; } @@ -2029,7 +2029,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int * * Lock one or more blocks */ -static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { int ret; @@ -2047,7 +2047,7 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) * * Unlock one or more blocks */ -static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { int ret; diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index e538c0a72abb..d2aa9c46530f 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -21,8 +21,6 @@ #include <asm/types.h> -#define const_cpu_to_le16 __constant_cpu_to_le16 - static int block_size = 0; module_param(block_size, int, 0); MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size"); @@ -156,7 +154,7 @@ static int scan_header(struct partition *part) size_t retlen; sectors_per_block = part->block_size / SECTOR_SIZE; - part->total_blocks = part->mbd.mtd->size / part->block_size; + part->total_blocks = (u32)part->mbd.mtd->size / part->block_size; if (part->total_blocks < 2) return -ENOENT; @@ -276,16 +274,17 @@ static void erase_callback(struct erase_info *erase) part = (struct partition*)erase->priv; - i = erase->addr / part->block_size; - if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) { - printk(KERN_ERR PREFIX "erase callback for unknown offset %x " - "on '%s'\n", erase->addr, part->mbd.mtd->name); + i = (u32)erase->addr / part->block_size; + if (i >= part->total_blocks || part->blocks[i].offset != erase->addr || + erase->addr > UINT_MAX) { + printk(KERN_ERR PREFIX "erase callback for unknown offset %llx " + "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name); return; } if (erase->state != MTD_ERASE_DONE) { - printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', " - "state %d\n", erase->addr, + printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', " + "state %d\n", (unsigned long long)erase->addr, part->mbd.mtd->name, erase->state); part->blocks[i].state = BLOCK_FAILED; @@ -297,7 +296,7 @@ static void erase_callback(struct erase_info *erase) return; } - magic = const_cpu_to_le16(RFD_MAGIC); + magic = cpu_to_le16(RFD_MAGIC); part->blocks[i].state = BLOCK_ERASED; part->blocks[i].free_sectors = part->data_sectors_per_block; @@ -345,9 +344,9 @@ static int erase_block(struct partition *part, int block) rc = part->mbd.mtd->erase(part->mbd.mtd, erase); if (rc) { - printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' " - "failed\n", erase->addr, erase->len, - part->mbd.mtd->name); + printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' " + "failed\n", (unsigned long long)erase->addr, + (unsigned long long)erase->len, part->mbd.mtd->name); kfree(erase); } @@ -587,7 +586,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr) int block, offset, rc; u_long addr; size_t retlen; - u16 del = const_cpu_to_le16(SECTOR_DELETED); + u16 del = cpu_to_le16(SECTOR_DELETED); block = old_addr / part->block_size; offset = (old_addr % part->block_size) / SECTOR_SIZE - @@ -763,7 +762,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { struct partition *part; - if (mtd->type != MTD_NORFLASH) + if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX) return; part = kzalloc(sizeof(struct partition), GFP_KERNEL); diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 33a5d6ed6f18..3f67e00d98e0 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -294,7 +294,8 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) int cis_sector; /* Check for small page NAND flash */ - if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE) + if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE || + mtd->size > UINT_MAX) return; /* Check for SSDFC format by reading CIS/IDI sector */ @@ -316,7 +317,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT); ssfdc->erase_size = mtd->erasesize; - ssfdc->map_len = mtd->size / mtd->erasesize; + ssfdc->map_len = (u32)mtd->size / mtd->erasesize; DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n", @@ -327,7 +328,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ssfdc->heads = 16; ssfdc->sectors = 32; get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors); - ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) / + ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) / ((long)ssfdc->sectors * (long)ssfdc->heads)); DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n", diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile new file mode 100644 index 000000000000..c1d501335006 --- /dev/null +++ b/drivers/mtd/tests/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_MTD_TESTS) += mtd_oobtest.o +obj-$(CONFIG_MTD_TESTS) += mtd_pagetest.o +obj-$(CONFIG_MTD_TESTS) += mtd_readtest.o +obj-$(CONFIG_MTD_TESTS) += mtd_speedtest.o +obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o +obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o +obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c new file mode 100644 index 000000000000..afbc3f8126db --- /dev/null +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -0,0 +1,742 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test OOB read and write on MTD device. + * + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> + */ + +#include <asm/div64.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> + +#define PRINT_PREF KERN_INFO "mtd_oobtest: " + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *readbuf; +static unsigned char *writebuf; +static unsigned char *bbt; + +static int ebcnt; +static int pgcnt; +static int errcnt; +static int use_offset; +static int use_len; +static int use_len_max; +static int vary_offset; +static unsigned long next = 1; + +static inline unsigned int simple_rand(void) +{ + next = next * 1103515245 + 12345; + return (unsigned int)((next / 65536) % 32768); +} + +static inline void simple_srand(unsigned long seed) +{ + next = seed; +} + +static void set_random_data(unsigned char *buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + buf[i] = simple_rand(); +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd->erase(mtd, &ei); + if (err) { + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + printk(PRINT_PREF "some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int erase_whole_device(void) +{ + int err; + unsigned int i; + + printk(PRINT_PREF "erasing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + return err; + cond_resched(); + } + printk(PRINT_PREF "erased %u eraseblocks\n", i); + return 0; +} + +static void do_vary_offset(void) +{ + use_len -= 1; + if (use_len < 1) { + use_offset += 1; + if (use_offset >= use_len_max) + use_offset = 0; + use_len = use_len_max - use_offset; + } +} + +static int write_eraseblock(int ebnum) +{ + int i; + struct mtd_oob_ops ops; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { + set_random_data(writebuf, use_len); + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = use_len; + ops.oobretlen = 0; + ops.ooboffs = use_offset; + ops.datbuf = 0; + ops.oobbuf = writebuf; + err = mtd->write_oob(mtd, addr, &ops); + if (err || ops.oobretlen != use_len) { + printk(PRINT_PREF "error: writeoob failed at %#llx\n", + (long long)addr); + printk(PRINT_PREF "error: use_len %d, use_offset %d\n", + use_len, use_offset); + errcnt += 1; + return err ? err : -1; + } + if (vary_offset) + do_vary_offset(); + } + + return err; +} + +static int write_whole_device(void) +{ + int err; + unsigned int i; + + printk(PRINT_PREF "writing OOBs of whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + return err; + if (i % 256 == 0) + printk(PRINT_PREF "written up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "written %u eraseblocks\n", i); + return 0; +} + +static int verify_eraseblock(int ebnum) +{ + int i; + struct mtd_oob_ops ops; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { + set_random_data(writebuf, use_len); + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = use_len; + ops.oobretlen = 0; + ops.ooboffs = use_offset; + ops.datbuf = 0; + ops.oobbuf = readbuf; + err = mtd->read_oob(mtd, addr, &ops); + if (err || ops.oobretlen != use_len) { + printk(PRINT_PREF "error: readoob failed at %#llx\n", + (long long)addr); + errcnt += 1; + return err ? err : -1; + } + if (memcmp(readbuf, writebuf, use_len)) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + printk(PRINT_PREF "error: too many errors\n"); + return -1; + } + } + if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) { + int k; + + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = readbuf; + err = mtd->read_oob(mtd, addr, &ops); + if (err || ops.oobretlen != mtd->ecclayout->oobavail) { + printk(PRINT_PREF "error: readoob failed at " + "%#llx\n", (long long)addr); + errcnt += 1; + return err ? err : -1; + } + if (memcmp(readbuf + use_offset, writebuf, use_len)) { + printk(PRINT_PREF "error: verify failed at " + "%#llx\n", (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + printk(PRINT_PREF "error: too many " + "errors\n"); + return -1; + } + } + for (k = 0; k < use_offset; ++k) + if (readbuf[k] != 0xff) { + printk(PRINT_PREF "error: verify 0xff " + "failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + printk(PRINT_PREF "error: too " + "many errors\n"); + return -1; + } + } + for (k = use_offset + use_len; + k < mtd->ecclayout->oobavail; ++k) + if (readbuf[k] != 0xff) { + printk(PRINT_PREF "error: verify 0xff " + "failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + printk(PRINT_PREF "error: too " + "many errors\n"); + return -1; + } + } + } + if (vary_offset) + do_vary_offset(); + } + return err; +} + +static int verify_eraseblock_in_one_go(int ebnum) +{ + struct mtd_oob_ops ops; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + size_t len = mtd->ecclayout->oobavail * pgcnt; + + set_random_data(writebuf, len); + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = len; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = readbuf; + err = mtd->read_oob(mtd, addr, &ops); + if (err || ops.oobretlen != len) { + printk(PRINT_PREF "error: readoob failed at %#llx\n", + (long long)addr); + errcnt += 1; + return err ? err : -1; + } + if (memcmp(readbuf, writebuf, len)) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + printk(PRINT_PREF "error: too many errors\n"); + return -1; + } + } + + return err; +} + +static int verify_all_eraseblocks(void) +{ + int err; + unsigned int i; + + printk(PRINT_PREF "verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock(i); + if (err) + return err; + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + return 0; +} + +static int is_block_bad(int ebnum) +{ + int ret; + loff_t addr = ebnum * mtd->erasesize; + + ret = mtd->block_isbad(mtd, addr); + if (ret) + printk(PRINT_PREF "block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + memset(bbt, 0 , ebcnt); + + printk(PRINT_PREF "scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_oobtest_init(void) +{ + int err = 0; + unsigned int i; + uint64_t tmp; + struct mtd_oob_ops ops; + loff_t addr = 0, addr0; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: cannot get MTD device\n"); + return err; + } + + if (mtd->type != MTD_NANDFLASH) { + printk(PRINT_PREF "this test requires NAND flash\n"); + goto out; + } + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + mtd->writesize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + mtd->erasesize = mtd->erasesize; + readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!readbuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!writebuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 0; + + /* First test: write all OOB, read it back and verify */ + printk(PRINT_PREF "test 1 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + simple_srand(1); + err = write_whole_device(); + if (err) + goto out; + + simple_srand(1); + err = verify_all_eraseblocks(); + if (err) + goto out; + + /* + * Second test: write all OOB, a block at a time, read it back and + * verify. + */ + printk(PRINT_PREF "test 2 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + simple_srand(3); + err = write_whole_device(); + if (err) + goto out; + + /* Check all eraseblocks */ + simple_srand(3); + printk(PRINT_PREF "verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock_in_one_go(i); + if (err) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + + /* + * Third test: write OOB at varying offsets and lengths, read it back + * and verify. + */ + printk(PRINT_PREF "test 3 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks */ + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 1; + simple_srand(5); + printk(PRINT_PREF "writing OOBs of whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "written up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 1; + simple_srand(5); + err = verify_all_eraseblocks(); + if (err) + goto out; + + use_offset = 0; + use_len = mtd->ecclayout->oobavail; + use_len_max = mtd->ecclayout->oobavail; + vary_offset = 0; + + /* Fourth test: try to write off end of device */ + printk(PRINT_PREF "test 4 of 5\n"); + + err = erase_whole_device(); + if (err) + goto out; + + addr0 = 0; + for (i = 0; bbt[i] && i < ebcnt; ++i) + addr0 += mtd->erasesize; + + /* Attempt to write off end of OOB */ + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = 1; + ops.oobretlen = 0; + ops.ooboffs = mtd->ecclayout->oobavail; + ops.datbuf = 0; + ops.oobbuf = writebuf; + printk(PRINT_PREF "attempting to start write past end of OOB\n"); + printk(PRINT_PREF "an error is expected...\n"); + err = mtd->write_oob(mtd, addr0, &ops); + if (err) { + printk(PRINT_PREF "error occurred as expected\n"); + err = 0; + } else { + printk(PRINT_PREF "error: can write past end of OOB\n"); + errcnt += 1; + } + + /* Attempt to read off end of OOB */ + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = 1; + ops.oobretlen = 0; + ops.ooboffs = mtd->ecclayout->oobavail; + ops.datbuf = 0; + ops.oobbuf = readbuf; + printk(PRINT_PREF "attempting to start read past end of OOB\n"); + printk(PRINT_PREF "an error is expected...\n"); + err = mtd->read_oob(mtd, addr0, &ops); + if (err) { + printk(PRINT_PREF "error occurred as expected\n"); + err = 0; + } else { + printk(PRINT_PREF "error: can read past end of OOB\n"); + errcnt += 1; + } + + if (bbt[ebcnt - 1]) + printk(PRINT_PREF "skipping end of device tests because last " + "block is bad\n"); + else { + /* Attempt to write off end of device */ + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail + 1; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = writebuf; + printk(PRINT_PREF "attempting to write past end of device\n"); + printk(PRINT_PREF "an error is expected...\n"); + err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + printk(PRINT_PREF "error occurred as expected\n"); + err = 0; + } else { + printk(PRINT_PREF "error: wrote past end of device\n"); + errcnt += 1; + } + + /* Attempt to read off end of device */ + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail + 1; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = readbuf; + printk(PRINT_PREF "attempting to read past end of device\n"); + printk(PRINT_PREF "an error is expected...\n"); + err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + printk(PRINT_PREF "error occurred as expected\n"); + err = 0; + } else { + printk(PRINT_PREF "error: read past end of device\n"); + errcnt += 1; + } + + err = erase_eraseblock(ebcnt - 1); + if (err) + goto out; + + /* Attempt to write off end of device */ + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail; + ops.oobretlen = 0; + ops.ooboffs = 1; + ops.datbuf = 0; + ops.oobbuf = writebuf; + printk(PRINT_PREF "attempting to write past end of device\n"); + printk(PRINT_PREF "an error is expected...\n"); + err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + printk(PRINT_PREF "error occurred as expected\n"); + err = 0; + } else { + printk(PRINT_PREF "error: wrote past end of device\n"); + errcnt += 1; + } + + /* Attempt to read off end of device */ + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail; + ops.oobretlen = 0; + ops.ooboffs = 1; + ops.datbuf = 0; + ops.oobbuf = readbuf; + printk(PRINT_PREF "attempting to read past end of device\n"); + printk(PRINT_PREF "an error is expected...\n"); + err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops); + if (err) { + printk(PRINT_PREF "error occurred as expected\n"); + err = 0; + } else { + printk(PRINT_PREF "error: read past end of device\n"); + errcnt += 1; + } + } + + /* Fifth test: write / read across block boundaries */ + printk(PRINT_PREF "test 5 of 5\n"); + + /* Erase all eraseblocks */ + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks */ + simple_srand(11); + printk(PRINT_PREF "writing OOBs of whole device\n"); + for (i = 0; i < ebcnt - 1; ++i) { + int cnt = 2; + int pg; + size_t sz = mtd->ecclayout->oobavail; + if (bbt[i] || bbt[i + 1]) + continue; + addr = (i + 1) * mtd->erasesize - mtd->writesize; + for (pg = 0; pg < cnt; ++pg) { + set_random_data(writebuf, sz); + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = sz; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = writebuf; + err = mtd->write_oob(mtd, addr, &ops); + if (err) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "written up to eraseblock " + "%u\n", i); + cond_resched(); + addr += mtd->writesize; + } + } + printk(PRINT_PREF "written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + simple_srand(11); + printk(PRINT_PREF "verifying all eraseblocks\n"); + for (i = 0; i < ebcnt - 1; ++i) { + if (bbt[i] || bbt[i + 1]) + continue; + set_random_data(writebuf, mtd->ecclayout->oobavail * 2); + addr = (i + 1) * mtd->erasesize - mtd->writesize; + ops.mode = MTD_OOB_AUTO; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->ecclayout->oobavail * 2; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = readbuf; + err = mtd->read_oob(mtd, addr, &ops); + if (err) + goto out; + if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + if (errcnt > 1000) { + printk(PRINT_PREF "error: too many errors\n"); + goto out; + } + } + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + + printk(PRINT_PREF "finished with %d errors\n", errcnt); +out: + kfree(bbt); + kfree(writebuf); + kfree(readbuf); + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_oobtest_init); + +static void __exit mtd_oobtest_exit(void) +{ + return; +} +module_exit(mtd_oobtest_exit); + +MODULE_DESCRIPTION("Out-of-band test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c new file mode 100644 index 000000000000..9648818b9e2c --- /dev/null +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -0,0 +1,632 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test page read and write on MTD device. + * + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> + */ + +#include <asm/div64.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> + +#define PRINT_PREF KERN_INFO "mtd_pagetest: " + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *twopages; +static unsigned char *writebuf; +static unsigned char *boundary; +static unsigned char *bbt; + +static int pgsize; +static int bufsize; +static int ebcnt; +static int pgcnt; +static int errcnt; +static unsigned long next = 1; + +static inline unsigned int simple_rand(void) +{ + next = next * 1103515245 + 12345; + return (unsigned int)((next / 65536) % 32768); +} + +static inline void simple_srand(unsigned long seed) +{ + next = seed; +} + +static void set_random_data(unsigned char *buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + buf[i] = simple_rand(); +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd->erase(mtd, &ei); + if (err) { + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + printk(PRINT_PREF "some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int write_eraseblock(int ebnum) +{ + int err = 0; + size_t written = 0; + loff_t addr = ebnum * mtd->erasesize; + + set_random_data(writebuf, mtd->erasesize); + cond_resched(); + err = mtd->write(mtd, addr, mtd->erasesize, &written, writebuf); + if (err || written != mtd->erasesize) + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr); + + return err; +} + +static int verify_eraseblock(int ebnum) +{ + uint32_t j; + size_t read = 0; + int err = 0, i; + loff_t addr0, addrn; + loff_t addr = ebnum * mtd->erasesize; + + addr0 = 0; + for (i = 0; bbt[i] && i < ebcnt; ++i) + addr0 += mtd->erasesize; + + addrn = mtd->size; + for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) + addrn -= mtd->erasesize; + + set_random_data(writebuf, mtd->erasesize); + for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { + /* Do a read to set the internal dataRAMs to different data */ + err = mtd->read(mtd, addr0, bufsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != bufsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr0); + return err; + } + err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != bufsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)(addrn - bufsize)); + return err; + } + memset(twopages, 0, bufsize); + read = 0; + err = mtd->read(mtd, addr, bufsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != bufsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + break; + } + if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + } + } + /* Check boundary between eraseblocks */ + if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { + unsigned long oldnext = next; + /* Do a read to set the internal dataRAMs to different data */ + err = mtd->read(mtd, addr0, bufsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != bufsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr0); + return err; + } + err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != bufsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)(addrn - bufsize)); + return err; + } + memset(twopages, 0, bufsize); + read = 0; + err = mtd->read(mtd, addr, bufsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != bufsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + return err; + } + memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); + set_random_data(boundary + pgsize, pgsize); + if (memcmp(twopages, boundary, bufsize)) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + } + next = oldnext; + } + return err; +} + +static int crosstest(void) +{ + size_t read = 0; + int err = 0, i; + loff_t addr, addr0, addrn; + unsigned char *pp1, *pp2, *pp3, *pp4; + + printk(PRINT_PREF "crosstest\n"); + pp1 = kmalloc(pgsize * 4, GFP_KERNEL); + if (!pp1) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + pp2 = pp1 + pgsize; + pp3 = pp2 + pgsize; + pp4 = pp3 + pgsize; + memset(pp1, 0, pgsize * 4); + + addr0 = 0; + for (i = 0; bbt[i] && i < ebcnt; ++i) + addr0 += mtd->erasesize; + + addrn = mtd->size; + for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) + addrn -= mtd->erasesize; + + /* Read 2nd-to-last page to pp1 */ + read = 0; + addr = addrn - pgsize - pgsize; + err = mtd->read(mtd, addr, pgsize, &read, pp1); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read 3rd-to-last page to pp1 */ + read = 0; + addr = addrn - pgsize - pgsize - pgsize; + err = mtd->read(mtd, addr, pgsize, &read, pp1); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read first page to pp2 */ + read = 0; + addr = addr0; + printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); + err = mtd->read(mtd, addr, pgsize, &read, pp2); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read last page to pp3 */ + read = 0; + addr = addrn - pgsize; + printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); + err = mtd->read(mtd, addr, pgsize, &read, pp3); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* Read first page again to pp4 */ + read = 0; + addr = addr0; + printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); + err = mtd->read(mtd, addr, pgsize, &read, pp4); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + kfree(pp1); + return err; + } + + /* pp2 and pp4 should be the same */ + printk(PRINT_PREF "verifying pages read at %#llx match\n", + (long long)addr0); + if (memcmp(pp2, pp4, pgsize)) { + printk(PRINT_PREF "verify failed!\n"); + errcnt += 1; + } else if (!err) + printk(PRINT_PREF "crosstest ok\n"); + kfree(pp1); + return err; +} + +static int erasecrosstest(void) +{ + size_t read = 0, written = 0; + int err = 0, i, ebnum, ok = 1, ebnum2; + loff_t addr0; + char *readbuf = twopages; + + printk(PRINT_PREF "erasecrosstest\n"); + + ebnum = 0; + addr0 = 0; + for (i = 0; bbt[i] && i < ebcnt; ++i) { + addr0 += mtd->erasesize; + ebnum += 1; + } + + ebnum2 = ebcnt - 1; + while (ebnum2 && bbt[ebnum2]) + ebnum2 -= 1; + + printk(PRINT_PREF "erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); + set_random_data(writebuf, pgsize); + strcpy(writebuf, "There is no data like this!"); + err = mtd->write(mtd, addr0, pgsize, &written, writebuf); + if (err || written != pgsize) { + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); + memset(readbuf, 0, pgsize); + err = mtd->read(mtd, addr0, pgsize, &read, readbuf); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); + if (memcmp(writebuf, readbuf, pgsize)) { + printk(PRINT_PREF "verify failed!\n"); + errcnt += 1; + ok = 0; + return err; + } + + printk(PRINT_PREF "erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); + set_random_data(writebuf, pgsize); + strcpy(writebuf, "There is no data like this!"); + err = mtd->write(mtd, addr0, pgsize, &written, writebuf); + if (err || written != pgsize) { + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + printk(PRINT_PREF "erasing block %d\n", ebnum2); + err = erase_eraseblock(ebnum2); + if (err) + return err; + + printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); + memset(readbuf, 0, pgsize); + err = mtd->read(mtd, addr0, pgsize, &read, readbuf); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); + if (memcmp(writebuf, readbuf, pgsize)) { + printk(PRINT_PREF "verify failed!\n"); + errcnt += 1; + ok = 0; + } + + if (ok && !err) + printk(PRINT_PREF "erasecrosstest ok\n"); + return err; +} + +static int erasetest(void) +{ + size_t read = 0, written = 0; + int err = 0, i, ebnum, ok = 1; + loff_t addr0; + + printk(PRINT_PREF "erasetest\n"); + + ebnum = 0; + addr0 = 0; + for (i = 0; bbt[i] && i < ebcnt; ++i) { + addr0 += mtd->erasesize; + ebnum += 1; + } + + printk(PRINT_PREF "erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); + set_random_data(writebuf, pgsize); + err = mtd->write(mtd, addr0, pgsize, &written, writebuf); + if (err || written != pgsize) { + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + printk(PRINT_PREF "erasing block %d\n", ebnum); + err = erase_eraseblock(ebnum); + if (err) + return err; + + printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); + err = mtd->read(mtd, addr0, pgsize, &read, twopages); + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr0); + return err ? err : -1; + } + + printk(PRINT_PREF "verifying 1st page of block %d is all 0xff\n", + ebnum); + for (i = 0; i < pgsize; ++i) + if (twopages[i] != 0xff) { + printk(PRINT_PREF "verifying all 0xff failed at %d\n", + i); + errcnt += 1; + ok = 0; + break; + } + + if (ok && !err) + printk(PRINT_PREF "erasetest ok\n"); + + return err; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd->block_isbad(mtd, addr); + if (ret) + printk(PRINT_PREF "block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + memset(bbt, 0 , ebcnt); + + printk(PRINT_PREF "scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_pagetest_init(void) +{ + int err = 0; + uint64_t tmp; + uint32_t i; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: cannot get MTD device\n"); + return err; + } + + if (mtd->type != MTD_NANDFLASH) { + printk(PRINT_PREF "this test requires NAND flash\n"); + goto out; + } + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + bufsize = pgsize * 2; + writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!writebuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + twopages = kmalloc(bufsize, GFP_KERNEL); + if (!twopages) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + boundary = kmalloc(bufsize, GFP_KERNEL); + if (!boundary) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + /* Erase all eraseblocks */ + printk(PRINT_PREF "erasing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + printk(PRINT_PREF "erased %u eraseblocks\n", i); + + /* Write all eraseblocks */ + simple_srand(1); + printk(PRINT_PREF "writing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "written up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + simple_srand(1); + printk(PRINT_PREF "verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock(i); + if (err) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + + err = crosstest(); + if (err) + goto out; + + err = erasecrosstest(); + if (err) + goto out; + + err = erasetest(); + if (err) + goto out; + + printk(PRINT_PREF "finished with %d errors\n", errcnt); +out: + + kfree(bbt); + kfree(boundary); + kfree(twopages); + kfree(writebuf); + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_pagetest_init); + +static void __exit mtd_pagetest_exit(void) +{ + return; +} +module_exit(mtd_pagetest_exit); + +MODULE_DESCRIPTION("NAND page test"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c new file mode 100644 index 000000000000..645e77fdc63d --- /dev/null +++ b/drivers/mtd/tests/mtd_readtest.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Check MTD device read. + * + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> + +#define PRINT_PREF KERN_INFO "mtd_readtest: " + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *iobuf; +static unsigned char *iobuf1; +static unsigned char *bbt; + +static int pgsize; +static int ebcnt; +static int pgcnt; + +static int read_eraseblock_by_page(int ebnum) +{ + size_t read = 0; + int i, ret, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + void *oobbuf = iobuf1; + + for (i = 0; i < pgcnt; i++) { + memset(buf, 0 , pgcnt); + ret = mtd->read(mtd, addr, pgsize, &read, buf); + if (ret == -EUCLEAN) + ret = 0; + if (ret || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + if (!err) + err = ret; + if (!err) + err = -EINVAL; + } + if (mtd->oobsize) { + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_PLACE; + ops.len = 0; + ops.retlen = 0; + ops.ooblen = mtd->oobsize; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = 0; + ops.oobbuf = oobbuf; + ret = mtd->read_oob(mtd, addr, &ops); + if (ret || ops.oobretlen != mtd->oobsize) { + printk(PRINT_PREF "error: read oob failed at " + "%#llx\n", (long long)addr); + if (!err) + err = ret; + if (!err) + err = -EINVAL; + } + oobbuf += mtd->oobsize; + } + addr += pgsize; + buf += pgsize; + } + + return err; +} + +static void dump_eraseblock(int ebnum) +{ + int i, j, n; + char line[128]; + int pg, oob; + + printk(PRINT_PREF "dumping eraseblock %d\n", ebnum); + n = mtd->erasesize; + for (i = 0; i < n;) { + char *p = line; + + p += sprintf(p, "%05x: ", i); + for (j = 0; j < 32 && i < n; j++, i++) + p += sprintf(p, "%02x", (unsigned int)iobuf[i]); + printk(KERN_CRIT "%s\n", line); + cond_resched(); + } + if (!mtd->oobsize) + return; + printk(PRINT_PREF "dumping oob from eraseblock %d\n", ebnum); + n = mtd->oobsize; + for (pg = 0, i = 0; pg < pgcnt; pg++) + for (oob = 0; oob < n;) { + char *p = line; + + p += sprintf(p, "%05x: ", i); + for (j = 0; j < 32 && oob < n; j++, oob++, i++) + p += sprintf(p, "%02x", + (unsigned int)iobuf1[i]); + printk(KERN_CRIT "%s\n", line); + cond_resched(); + } +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd->block_isbad(mtd, addr); + if (ret) + printk(PRINT_PREF "block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + memset(bbt, 0 , ebcnt); + + printk(PRINT_PREF "scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_readtest_init(void) +{ + uint64_t tmp; + int err, i; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: Cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + printk(PRINT_PREF "not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!iobuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!iobuf1) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + /* Read all eraseblocks 1 page at a time */ + printk(PRINT_PREF "testing page read\n"); + for (i = 0; i < ebcnt; ++i) { + int ret; + + if (bbt[i]) + continue; + ret = read_eraseblock_by_page(i); + if (ret) { + dump_eraseblock(i); + if (!err) + err = ret; + } + cond_resched(); + } + + if (err) + printk(PRINT_PREF "finished with errors\n"); + else + printk(PRINT_PREF "finished\n"); + +out: + + kfree(iobuf); + kfree(iobuf1); + kfree(bbt); + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_readtest_init); + +static void __exit mtd_readtest_exit(void) +{ + return; +} +module_exit(mtd_readtest_exit); + +MODULE_DESCRIPTION("Read test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c new file mode 100644 index 000000000000..141363a7e805 --- /dev/null +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2007 Nokia Corporation + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test read and write speed of a MTD device. + * + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> + +#define PRINT_PREF KERN_INFO "mtd_speedtest: " + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *iobuf; +static unsigned char *bbt; + +static int pgsize; +static int ebcnt; +static int pgcnt; +static int goodebcnt; +static struct timeval start, finish; +static unsigned long next = 1; + +static inline unsigned int simple_rand(void) +{ + next = next * 1103515245 + 12345; + return (unsigned int)((next / 65536) % 32768); +} + +static inline void simple_srand(unsigned long seed) +{ + next = seed; +} + +static void set_random_data(unsigned char *buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + buf[i] = simple_rand(); +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd->erase(mtd, &ei); + if (err) { + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + printk(PRINT_PREF "some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int erase_whole_device(void) +{ + int err; + unsigned int i; + + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + return err; + cond_resched(); + } + return 0; +} + +static int write_eraseblock(int ebnum) +{ + size_t written = 0; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + err = mtd->write(mtd, addr, mtd->erasesize, &written, iobuf); + if (err || written != mtd->erasesize) { + printk(PRINT_PREF "error: write failed at %#llx\n", addr); + if (!err) + err = -EINVAL; + } + + return err; +} + +static int write_eraseblock_by_page(int ebnum) +{ + size_t written = 0; + int i, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < pgcnt; i++) { + err = mtd->write(mtd, addr, pgsize, &written, buf); + if (err || written != pgsize) { + printk(PRINT_PREF "error: write failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + break; + } + addr += pgsize; + buf += pgsize; + } + + return err; +} + +static int write_eraseblock_by_2pages(int ebnum) +{ + size_t written = 0, sz = pgsize * 2; + int i, n = pgcnt / 2, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < n; i++) { + err = mtd->write(mtd, addr, sz, &written, buf); + if (err || written != sz) { + printk(PRINT_PREF "error: write failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + return err; + } + addr += sz; + buf += sz; + } + if (pgcnt % 2) { + err = mtd->write(mtd, addr, pgsize, &written, buf); + if (err || written != pgsize) { + printk(PRINT_PREF "error: write failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + } + } + + return err; +} + +static int read_eraseblock(int ebnum) +{ + size_t read = 0; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + err = mtd->read(mtd, addr, mtd->erasesize, &read, iobuf); + /* Ignore corrected ECC errors */ + if (err == -EUCLEAN) + err = 0; + if (err || read != mtd->erasesize) { + printk(PRINT_PREF "error: read failed at %#llx\n", addr); + if (!err) + err = -EINVAL; + } + + return err; +} + +static int read_eraseblock_by_page(int ebnum) +{ + size_t read = 0; + int i, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < pgcnt; i++) { + err = mtd->read(mtd, addr, pgsize, &read, buf); + /* Ignore corrected ECC errors */ + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + break; + } + addr += pgsize; + buf += pgsize; + } + + return err; +} + +static int read_eraseblock_by_2pages(int ebnum) +{ + size_t read = 0, sz = pgsize * 2; + int i, n = pgcnt / 2, err = 0; + loff_t addr = ebnum * mtd->erasesize; + void *buf = iobuf; + + for (i = 0; i < n; i++) { + err = mtd->read(mtd, addr, sz, &read, buf); + /* Ignore corrected ECC errors */ + if (err == -EUCLEAN) + err = 0; + if (err || read != sz) { + printk(PRINT_PREF "error: read failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + return err; + } + addr += sz; + buf += sz; + } + if (pgcnt % 2) { + err = mtd->read(mtd, addr, pgsize, &read, buf); + /* Ignore corrected ECC errors */ + if (err == -EUCLEAN) + err = 0; + if (err || read != pgsize) { + printk(PRINT_PREF "error: read failed at %#llx\n", + addr); + if (!err) + err = -EINVAL; + } + } + + return err; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd->block_isbad(mtd, addr); + if (ret) + printk(PRINT_PREF "block %d is bad\n", ebnum); + return ret; +} + +static inline void start_timing(void) +{ + do_gettimeofday(&start); +} + +static inline void stop_timing(void) +{ + do_gettimeofday(&finish); +} + +static long calc_speed(void) +{ + long ms, k, speed; + + ms = (finish.tv_sec - start.tv_sec) * 1000 + + (finish.tv_usec - start.tv_usec) / 1000; + k = goodebcnt * mtd->erasesize / 1024; + speed = (k * 1000) / ms; + return speed; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + memset(bbt, 0 , ebcnt); + + printk(PRINT_PREF "scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + goodebcnt = ebcnt - bad; + return 0; +} + +static int __init mtd_speedtest_init(void) +{ + int err, i; + long speed; + uint64_t tmp; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + printk(PRINT_PREF "not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!iobuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + + simple_srand(1); + set_random_data(iobuf, mtd->erasesize); + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks, 1 eraseblock at a time */ + printk(PRINT_PREF "testing eraseblock write speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed); + + /* Read all eraseblocks, 1 eraseblock at a time */ + printk(PRINT_PREF "testing eraseblock read speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = read_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "eraseblock read speed is %ld KiB/s\n", speed); + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks, 1 page at a time */ + printk(PRINT_PREF "testing page write speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock_by_page(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "page write speed is %ld KiB/s\n", speed); + + /* Read all eraseblocks, 1 page at a time */ + printk(PRINT_PREF "testing page read speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = read_eraseblock_by_page(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "page read speed is %ld KiB/s\n", speed); + + err = erase_whole_device(); + if (err) + goto out; + + /* Write all eraseblocks, 2 pages at a time */ + printk(PRINT_PREF "testing 2 page write speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock_by_2pages(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "2 page write speed is %ld KiB/s\n", speed); + + /* Read all eraseblocks, 2 pages at a time */ + printk(PRINT_PREF "testing 2 page read speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = read_eraseblock_by_2pages(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "2 page read speed is %ld KiB/s\n", speed); + + /* Erase all eraseblocks */ + printk(PRINT_PREF "Testing erase speed\n"); + start_timing(); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + stop_timing(); + speed = calc_speed(); + printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed); + + printk(PRINT_PREF "finished\n"); +out: + kfree(iobuf); + kfree(bbt); + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_speedtest_init); + +static void __exit mtd_speedtest_exit(void) +{ + return; +} +module_exit(mtd_speedtest_exit); + +MODULE_DESCRIPTION("Speed test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c new file mode 100644 index 000000000000..63920476b57a --- /dev/null +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2006-2008 Nokia Corporation + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test random reads, writes and erases on MTD device. + * + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> + +#define PRINT_PREF KERN_INFO "mtd_stresstest: " + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static int count = 10000; +module_param(count, int, S_IRUGO); +MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); + +static struct mtd_info *mtd; +static unsigned char *writebuf; +static unsigned char *readbuf; +static unsigned char *bbt; +static int *offsets; + +static int pgsize; +static int bufsize; +static int ebcnt; +static int pgcnt; +static unsigned long next = 1; + +static inline unsigned int simple_rand(void) +{ + next = next * 1103515245 + 12345; + return (unsigned int)((next / 65536) % 32768); +} + +static inline void simple_srand(unsigned long seed) +{ + next = seed; +} + +static int rand_eb(void) +{ + int eb; + +again: + if (ebcnt < 32768) + eb = simple_rand(); + else + eb = (simple_rand() << 15) | simple_rand(); + /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ + eb %= (ebcnt - 1); + if (bbt[eb]) + goto again; + return eb; +} + +static int rand_offs(void) +{ + int offs; + + if (bufsize < 32768) + offs = simple_rand(); + else + offs = (simple_rand() << 15) | simple_rand(); + offs %= bufsize; + return offs; +} + +static int rand_len(int offs) +{ + int len; + + if (bufsize < 32768) + len = simple_rand(); + else + len = (simple_rand() << 15) | simple_rand(); + len %= (bufsize - offs); + return len; +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd->erase(mtd, &ei); + if (unlikely(err)) { + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (unlikely(ei.state == MTD_ERASE_FAILED)) { + printk(PRINT_PREF "some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd->block_isbad(mtd, addr); + if (ret) + printk(PRINT_PREF "block %d is bad\n", ebnum); + return ret; +} + +static int do_read(void) +{ + size_t read = 0; + int eb = rand_eb(); + int offs = rand_offs(); + int len = rand_len(offs), err; + loff_t addr; + + if (bbt[eb + 1]) { + if (offs >= mtd->erasesize) + offs -= mtd->erasesize; + if (offs + len > mtd->erasesize) + len = mtd->erasesize - offs; + } + addr = eb * mtd->erasesize + offs; + err = mtd->read(mtd, addr, len, &read, readbuf); + if (err == -EUCLEAN) + err = 0; + if (unlikely(err || read != len)) { + printk(PRINT_PREF "error: read failed at 0x%llx\n", + (long long)addr); + if (!err) + err = -EINVAL; + return err; + } + return 0; +} + +static int do_write(void) +{ + int eb = rand_eb(), offs, err, len; + size_t written = 0; + loff_t addr; + + offs = offsets[eb]; + if (offs >= mtd->erasesize) { + err = erase_eraseblock(eb); + if (err) + return err; + offs = offsets[eb] = 0; + } + len = rand_len(offs); + len = ((len + pgsize - 1) / pgsize) * pgsize; + if (offs + len > mtd->erasesize) { + if (bbt[eb + 1]) + len = mtd->erasesize - offs; + else { + err = erase_eraseblock(eb + 1); + if (err) + return err; + offsets[eb + 1] = 0; + } + } + addr = eb * mtd->erasesize + offs; + err = mtd->write(mtd, addr, len, &written, writebuf); + if (unlikely(err || written != len)) { + printk(PRINT_PREF "error: write failed at 0x%llx\n", + (long long)addr); + if (!err) + err = -EINVAL; + return err; + } + offs += len; + while (offs > mtd->erasesize) { + offsets[eb++] = mtd->erasesize; + offs -= mtd->erasesize; + } + offsets[eb] = offs; + return 0; +} + +static int do_operation(void) +{ + if (simple_rand() & 1) + return do_read(); + else + return do_write(); +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + memset(bbt, 0 , ebcnt); + + printk(PRINT_PREF "scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_stresstest_init(void) +{ + int err; + int i, op; + uint64_t tmp; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + printk(PRINT_PREF "not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + "page size %u, count of eraseblocks %u, pages per " + "eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + pgsize, ebcnt, pgcnt, mtd->oobsize); + + /* Read or write up 2 eraseblocks at a time */ + bufsize = mtd->erasesize * 2; + + err = -ENOMEM; + readbuf = vmalloc(bufsize); + writebuf = vmalloc(bufsize); + offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); + if (!readbuf || !writebuf || !offsets) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + for (i = 0; i < ebcnt; i++) + offsets[i] = mtd->erasesize; + simple_srand(current->pid); + for (i = 0; i < bufsize; i++) + writebuf[i] = simple_rand(); + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + /* Do operations */ + printk(PRINT_PREF "doing operations\n"); + for (op = 0; op < count; op++) { + if ((op & 1023) == 0) + printk(PRINT_PREF "%d operations done\n", op); + err = do_operation(); + if (err) + goto out; + cond_resched(); + } + printk(PRINT_PREF "finished, %d operations done\n", op); + +out: + kfree(offsets); + kfree(bbt); + vfree(writebuf); + vfree(readbuf); + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_stresstest_init); + +static void __exit mtd_stresstest_exit(void) +{ + return; +} +module_exit(mtd_stresstest_exit); + +MODULE_DESCRIPTION("Stress test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c new file mode 100644 index 000000000000..5b889724268e --- /dev/null +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2006-2007 Nokia Corporation + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Test sub-page read and write on MTD device. + * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> + +#define PRINT_PREF KERN_INFO "mtd_subpagetest: " + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static struct mtd_info *mtd; +static unsigned char *writebuf; +static unsigned char *readbuf; +static unsigned char *bbt; + +static int subpgsize; +static int bufsize; +static int ebcnt; +static int pgcnt; +static int errcnt; +static unsigned long next = 1; + +static inline unsigned int simple_rand(void) +{ + next = next * 1103515245 + 12345; + return (unsigned int)((next / 65536) % 32768); +} + +static inline void simple_srand(unsigned long seed) +{ + next = seed; +} + +static void set_random_data(unsigned char *buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + buf[i] = simple_rand(); +} + +static inline void clear_data(unsigned char *buf, size_t len) +{ + memset(buf, 0, len); +} + +static int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd->erase(mtd, &ei); + if (err) { + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + printk(PRINT_PREF "some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +static int erase_whole_device(void) +{ + int err; + unsigned int i; + + printk(PRINT_PREF "erasing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = erase_eraseblock(i); + if (err) + return err; + cond_resched(); + } + printk(PRINT_PREF "erased %u eraseblocks\n", i); + return 0; +} + +static int write_eraseblock(int ebnum) +{ + size_t written = 0; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + set_random_data(writebuf, subpgsize); + err = mtd->write(mtd, addr, subpgsize, &written, writebuf); + if (unlikely(err || written != subpgsize)) { + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr); + if (written != subpgsize) { + printk(PRINT_PREF " write size: %#x\n", subpgsize); + printk(PRINT_PREF " written: %#zx\n", written); + } + return err ? err : -1; + } + + addr += subpgsize; + + set_random_data(writebuf, subpgsize); + err = mtd->write(mtd, addr, subpgsize, &written, writebuf); + if (unlikely(err || written != subpgsize)) { + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr); + if (written != subpgsize) { + printk(PRINT_PREF " write size: %#x\n", subpgsize); + printk(PRINT_PREF " written: %#zx\n", written); + } + return err ? err : -1; + } + + return err; +} + +static int write_eraseblock2(int ebnum) +{ + size_t written = 0; + int err = 0, k; + loff_t addr = ebnum * mtd->erasesize; + + for (k = 1; k < 33; ++k) { + if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) + break; + set_random_data(writebuf, subpgsize * k); + err = mtd->write(mtd, addr, subpgsize * k, &written, writebuf); + if (unlikely(err || written != subpgsize * k)) { + printk(PRINT_PREF "error: write failed at %#llx\n", + (long long)addr); + if (written != subpgsize) { + printk(PRINT_PREF " write size: %#x\n", + subpgsize * k); + printk(PRINT_PREF " written: %#08zx\n", + written); + } + return err ? err : -1; + } + addr += subpgsize * k; + } + + return err; +} + +static void print_subpage(unsigned char *p) +{ + int i, j; + + for (i = 0; i < subpgsize; ) { + for (j = 0; i < subpgsize && j < 32; ++i, ++j) + printk("%02x", *p++); + printk("\n"); + } +} + +static int verify_eraseblock(int ebnum) +{ + size_t read = 0; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + set_random_data(writebuf, subpgsize); + clear_data(readbuf, subpgsize); + read = 0; + err = mtd->read(mtd, addr, subpgsize, &read, readbuf); + if (unlikely(err || read != subpgsize)) { + if (err == -EUCLEAN && read == subpgsize) { + printk(PRINT_PREF "ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + printk(PRINT_PREF "------------- written----------------\n"); + print_subpage(writebuf); + printk(PRINT_PREF "------------- read ------------------\n"); + print_subpage(readbuf); + printk(PRINT_PREF "-------------------------------------\n"); + errcnt += 1; + } + + addr += subpgsize; + + set_random_data(writebuf, subpgsize); + clear_data(readbuf, subpgsize); + read = 0; + err = mtd->read(mtd, addr, subpgsize, &read, readbuf); + if (unlikely(err || read != subpgsize)) { + if (err == -EUCLEAN && read == subpgsize) { + printk(PRINT_PREF "ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + printk(PRINT_PREF "error: read failed at %#llx\n", + (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + printk(PRINT_PREF "------------- written----------------\n"); + print_subpage(writebuf); + printk(PRINT_PREF "------------- read ------------------\n"); + print_subpage(readbuf); + printk(PRINT_PREF "-------------------------------------\n"); + errcnt += 1; + } + + return err; +} + +static int verify_eraseblock2(int ebnum) +{ + size_t read = 0; + int err = 0, k; + loff_t addr = ebnum * mtd->erasesize; + + for (k = 1; k < 33; ++k) { + if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) + break; + set_random_data(writebuf, subpgsize * k); + clear_data(readbuf, subpgsize * k); + read = 0; + err = mtd->read(mtd, addr, subpgsize * k, &read, readbuf); + if (unlikely(err || read != subpgsize * k)) { + if (err == -EUCLEAN && read == subpgsize * k) { + printk(PRINT_PREF "ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + printk(PRINT_PREF "error: read failed at " + "%#llx\n", (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { + printk(PRINT_PREF "error: verify failed at %#llx\n", + (long long)addr); + errcnt += 1; + } + addr += subpgsize * k; + } + + return err; +} + +static int verify_eraseblock_ff(int ebnum) +{ + uint32_t j; + size_t read = 0; + int err = 0; + loff_t addr = ebnum * mtd->erasesize; + + memset(writebuf, 0xff, subpgsize); + for (j = 0; j < mtd->erasesize / subpgsize; ++j) { + clear_data(readbuf, subpgsize); + read = 0; + err = mtd->read(mtd, addr, subpgsize, &read, readbuf); + if (unlikely(err || read != subpgsize)) { + if (err == -EUCLEAN && read == subpgsize) { + printk(PRINT_PREF "ECC correction at %#llx\n", + (long long)addr); + err = 0; + } else { + printk(PRINT_PREF "error: read failed at " + "%#llx\n", (long long)addr); + return err ? err : -1; + } + } + if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { + printk(PRINT_PREF "error: verify 0xff failed at " + "%#llx\n", (long long)addr); + errcnt += 1; + } + addr += subpgsize; + } + + return err; +} + +static int verify_all_eraseblocks_ff(void) +{ + int err; + unsigned int i; + + printk(PRINT_PREF "verifying all eraseblocks for 0xff\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock_ff(i); + if (err) + return err; + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + return 0; +} + +static int is_block_bad(int ebnum) +{ + loff_t addr = ebnum * mtd->erasesize; + int ret; + + ret = mtd->block_isbad(mtd, addr); + if (ret) + printk(PRINT_PREF "block %d is bad\n", ebnum); + return ret; +} + +static int scan_for_bad_eraseblocks(void) +{ + int i, bad = 0; + + bbt = kmalloc(ebcnt, GFP_KERNEL); + if (!bbt) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + return -ENOMEM; + } + memset(bbt, 0 , ebcnt); + + printk(PRINT_PREF "scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); + return 0; +} + +static int __init mtd_subpagetest_init(void) +{ + int err = 0; + uint32_t i; + uint64_t tmp; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: cannot get MTD device\n"); + return err; + } + + if (mtd->type != MTD_NANDFLASH) { + printk(PRINT_PREF "this test requires NAND flash\n"); + goto out; + } + + subpgsize = mtd->writesize >> mtd->subpage_sft; + printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " + "page size %u, subpage size %u, count of eraseblocks %u, " + "pages per eraseblock %u, OOB size %u\n", + (unsigned long long)mtd->size, mtd->erasesize, + mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize); + + err = -ENOMEM; + bufsize = subpgsize * 32; + writebuf = kmalloc(bufsize, GFP_KERNEL); + if (!writebuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + readbuf = kmalloc(bufsize, GFP_KERNEL); + if (!readbuf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out; + } + + tmp = mtd->size; + do_div(tmp, mtd->erasesize); + ebcnt = tmp; + pgcnt = mtd->erasesize / mtd->writesize; + + err = scan_for_bad_eraseblocks(); + if (err) + goto out; + + err = erase_whole_device(); + if (err) + goto out; + + printk(PRINT_PREF "writing whole device\n"); + simple_srand(1); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "written up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "written %u eraseblocks\n", i); + + simple_srand(1); + printk(PRINT_PREF "verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + + err = erase_whole_device(); + if (err) + goto out; + + err = verify_all_eraseblocks_ff(); + if (err) + goto out; + + /* Write all eraseblocks */ + simple_srand(3); + printk(PRINT_PREF "writing whole device\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = write_eraseblock2(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "written up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "written %u eraseblocks\n", i); + + /* Check all eraseblocks */ + simple_srand(3); + printk(PRINT_PREF "verifying all eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = verify_eraseblock2(i); + if (unlikely(err)) + goto out; + if (i % 256 == 0) + printk(PRINT_PREF "verified up to eraseblock %u\n", i); + cond_resched(); + } + printk(PRINT_PREF "verified %u eraseblocks\n", i); + + err = erase_whole_device(); + if (err) + goto out; + + err = verify_all_eraseblocks_ff(); + if (err) + goto out; + + printk(PRINT_PREF "finished with %d errors\n", errcnt); + +out: + kfree(bbt); + kfree(readbuf); + kfree(writebuf); + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(mtd_subpagetest_init); + +static void __exit mtd_subpagetest_exit(void) +{ + return; +} +module_exit(mtd_subpagetest_exit); + +MODULE_DESCRIPTION("Subpage test module"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c new file mode 100644 index 000000000000..631a0ab3a33c --- /dev/null +++ b/drivers/mtd/tests/mtd_torturetest.c @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2006-2008 Artem Bityutskiy + * Copyright (C) 2006-2008 Jarkko Lavinen + * Copyright (C) 2006-2008 Adrian Hunter + * + * 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 program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING. If not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter + * + * WARNING: this test program may kill your flash and your device. Do not + * use it unless you know what you do. Authors are not responsible for any + * damage caused by this program. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> +#include <linux/sched.h> + +#define PRINT_PREF KERN_INFO "mtd_torturetest: " +#define RETRIES 3 + +static int eb = 8; +module_param(eb, int, S_IRUGO); +MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device"); + +static int ebcnt = 32; +module_param(ebcnt, int, S_IRUGO); +MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture"); + +static int pgcnt; +module_param(pgcnt, int, S_IRUGO); +MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)"); + +static int dev; +module_param(dev, int, S_IRUGO); +MODULE_PARM_DESC(dev, "MTD device number to use"); + +static int gran = 512; +module_param(gran, int, S_IRUGO); +MODULE_PARM_DESC(gran, "how often the status information should be printed"); + +static int check = 1; +module_param(check, int, S_IRUGO); +MODULE_PARM_DESC(check, "if the written data should be checked"); + +static unsigned int cycles_count; +module_param(cycles_count, uint, S_IRUGO); +MODULE_PARM_DESC(cycles_count, "how many erase cycles to do " + "(infinite by default)"); + +static struct mtd_info *mtd; + +/* This buffer contains 0x555555...0xAAAAAA... pattern */ +static unsigned char *patt_5A5; +/* This buffer contains 0xAAAAAA...0x555555... pattern */ +static unsigned char *patt_A5A; +/* This buffer contains all 0xFF bytes */ +static unsigned char *patt_FF; +/* This a temporary buffer is use when checking data */ +static unsigned char *check_buf; +/* How many erase cycles were done */ +static unsigned int erase_cycles; + +static int pgsize; +static struct timeval start, finish; + +static void report_corrupt(unsigned char *read, unsigned char *written); + +static inline void start_timing(void) +{ + do_gettimeofday(&start); +} + +static inline void stop_timing(void) +{ + do_gettimeofday(&finish); +} + +/* + * Erase eraseblock number @ebnum. + */ +static inline int erase_eraseblock(int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd->erase(mtd, &ei); + if (err) { + printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + printk(PRINT_PREF "some erase error occurred at EB %d\n", + ebnum); + return -EIO; + } + + return 0; +} + +/* + * Check that the contents of eraseblock number @enbum is equivalent to the + * @buf buffer. + */ +static inline int check_eraseblock(int ebnum, unsigned char *buf) +{ + int err, retries = 0; + size_t read = 0; + loff_t addr = ebnum * mtd->erasesize; + size_t len = mtd->erasesize; + + if (pgcnt) { + addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; + len = pgcnt * pgsize; + } + +retry: + err = mtd->read(mtd, addr, len, &read, check_buf); + if (err == -EUCLEAN) + printk(PRINT_PREF "single bit flip occurred at EB %d " + "MTD reported that it was fixed.\n", ebnum); + else if (err) { + printk(PRINT_PREF "error %d while reading EB %d, " + "read %zd\n", err, ebnum, read); + return err; + } + + if (read != len) { + printk(PRINT_PREF "failed to read %zd bytes from EB %d, " + "read only %zd, but no error reported\n", + len, ebnum, read); + return -EIO; + } + + if (memcmp(buf, check_buf, len)) { + printk(PRINT_PREF "read wrong data from EB %d\n", ebnum); + report_corrupt(check_buf, buf); + + if (retries++ < RETRIES) { + /* Try read again */ + yield(); + printk(PRINT_PREF "re-try reading data from EB %d\n", + ebnum); + goto retry; + } else { + printk(PRINT_PREF "retried %d times, still errors, " + "give-up\n", RETRIES); + return -EINVAL; + } + } + + if (retries != 0) + printk(PRINT_PREF "only attempt number %d was OK (!!!)\n", + retries); + + return 0; +} + +static inline int write_pattern(int ebnum, void *buf) +{ + int err; + size_t written = 0; + loff_t addr = ebnum * mtd->erasesize; + size_t len = mtd->erasesize; + + if (pgcnt) { + addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; + len = pgcnt * pgsize; + } + err = mtd->write(mtd, addr, len, &written, buf); + if (err) { + printk(PRINT_PREF "error %d while writing EB %d, written %zd" + " bytes\n", err, ebnum, written); + return err; + } + if (written != len) { + printk(PRINT_PREF "written only %zd bytes of %zd, but no error" + " reported\n", written, len); + return -EIO; + } + + return 0; +} + +static int __init tort_init(void) +{ + int err = 0, i, infinite = !cycles_count; + int bad_ebs[ebcnt]; + + printk(KERN_INFO "\n"); + printk(KERN_INFO "=================================================\n"); + printk(PRINT_PREF "Warning: this program is trying to wear out your " + "flash, stop it if this is not wanted.\n"); + printk(PRINT_PREF "MTD device: %d\n", dev); + printk(PRINT_PREF "torture %d eraseblocks (%d-%d) of mtd%d\n", + ebcnt, eb, eb + ebcnt - 1, dev); + if (pgcnt) + printk(PRINT_PREF "torturing just %d pages per eraseblock\n", + pgcnt); + printk(PRINT_PREF "write verify %s\n", check ? "enabled" : "disabled"); + + mtd = get_mtd_device(NULL, dev); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + printk(PRINT_PREF "error: cannot get MTD device\n"); + return err; + } + + if (mtd->writesize == 1) { + printk(PRINT_PREF "not NAND flash, assume page size is 512 " + "bytes.\n"); + pgsize = 512; + } else + pgsize = mtd->writesize; + + if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { + printk(PRINT_PREF "error: invalid pgcnt value %d\n", pgcnt); + goto out_mtd; + } + + err = -ENOMEM; + patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!patt_5A5) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out_mtd; + } + + patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!patt_A5A) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out_patt_5A5; + } + + patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!patt_FF) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out_patt_A5A; + } + + check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!check_buf) { + printk(PRINT_PREF "error: cannot allocate memory\n"); + goto out_patt_FF; + } + + err = 0; + + /* Initialize patterns */ + memset(patt_FF, 0xFF, mtd->erasesize); + for (i = 0; i < mtd->erasesize / pgsize; i++) { + if (!(i & 1)) { + memset(patt_5A5 + i * pgsize, 0x55, pgsize); + memset(patt_A5A + i * pgsize, 0xAA, pgsize); + } else { + memset(patt_5A5 + i * pgsize, 0xAA, pgsize); + memset(patt_A5A + i * pgsize, 0x55, pgsize); + } + } + + /* + * Check if there is a bad eraseblock among those we are going to test. + */ + memset(&bad_ebs[0], 0, sizeof(int) * ebcnt); + if (mtd->block_isbad) { + for (i = eb; i < eb + ebcnt; i++) { + err = mtd->block_isbad(mtd, + (loff_t)i * mtd->erasesize); + + if (err < 0) { + printk(PRINT_PREF "block_isbad() returned %d " + "for EB %d\n", err, i); + goto out; + } + + if (err) { + printk("EB %d is bad. Skip it.\n", i); + bad_ebs[i - eb] = 1; + } + } + } + + start_timing(); + while (1) { + int i; + void *patt; + + /* Erase all eraseblocks */ + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + err = erase_eraseblock(i); + if (err) + goto out; + cond_resched(); + } + + /* Check if the eraseblocks contain only 0xFF bytes */ + if (check) { + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + err = check_eraseblock(i, patt_FF); + if (err) { + printk(PRINT_PREF "verify failed" + " for 0xFF... pattern\n"); + goto out; + } + cond_resched(); + } + } + + /* Write the pattern */ + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + if ((eb + erase_cycles) & 1) + patt = patt_5A5; + else + patt = patt_A5A; + err = write_pattern(i, patt); + if (err) + goto out; + cond_resched(); + } + + /* Verify what we wrote */ + if (check) { + for (i = eb; i < eb + ebcnt; i++) { + if (bad_ebs[i - eb]) + continue; + if ((eb + erase_cycles) & 1) + patt = patt_5A5; + else + patt = patt_A5A; + err = check_eraseblock(i, patt); + if (err) { + printk(PRINT_PREF "verify failed for %s" + " pattern\n", + ((eb + erase_cycles) & 1) ? + "0x55AA55..." : "0xAA55AA..."); + goto out; + } + cond_resched(); + } + } + + erase_cycles += 1; + + if (erase_cycles % gran == 0) { + long ms; + + stop_timing(); + ms = (finish.tv_sec - start.tv_sec) * 1000 + + (finish.tv_usec - start.tv_usec) / 1000; + printk(PRINT_PREF "%08u erase cycles done, took %lu " + "milliseconds (%lu seconds)\n", + erase_cycles, ms, ms / 1000); + start_timing(); + } + + if (!infinite && --cycles_count == 0) + break; + } +out: + + printk(PRINT_PREF "finished after %u erase cycles\n", + erase_cycles); + kfree(check_buf); +out_patt_FF: + kfree(patt_FF); +out_patt_A5A: + kfree(patt_A5A); +out_patt_5A5: + kfree(patt_5A5); +out_mtd: + put_mtd_device(mtd); + if (err) + printk(PRINT_PREF "error %d occurred during torturing\n", err); + printk(KERN_INFO "=================================================\n"); + return err; +} +module_init(tort_init); + +static void __exit tort_exit(void) +{ + return; +} +module_exit(tort_exit); + +static int countdiffs(unsigned char *buf, unsigned char *check_buf, + unsigned offset, unsigned len, unsigned *bytesp, + unsigned *bitsp); +static void print_bufs(unsigned char *read, unsigned char *written, int start, + int len); + +/* + * Report the detailed information about how the read EB differs from what was + * written. + */ +static void report_corrupt(unsigned char *read, unsigned char *written) +{ + int i; + int bytes, bits, pages, first; + int offset, len; + size_t check_len = mtd->erasesize; + + if (pgcnt) + check_len = pgcnt * pgsize; + + bytes = bits = pages = 0; + for (i = 0; i < check_len; i += pgsize) + if (countdiffs(written, read, i, pgsize, &bytes, + &bits) >= 0) + pages++; + + printk(PRINT_PREF "verify fails on %d pages, %d bytes/%d bits\n", + pages, bytes, bits); + printk(PRINT_PREF "The following is a list of all differences between" + " what was read from flash and what was expected\n"); + + for (i = 0; i < check_len; i += pgsize) { + cond_resched(); + bytes = bits = 0; + first = countdiffs(written, read, i, pgsize, &bytes, + &bits); + if (first < 0) + continue; + + printk("-------------------------------------------------------" + "----------------------------------\n"); + + printk(PRINT_PREF "Page %zd has %d bytes/%d bits failing verify," + " starting at offset 0x%x\n", + (mtd->erasesize - check_len + i) / pgsize, + bytes, bits, first); + + offset = first & ~0x7; + len = ((first + bytes) | 0x7) + 1 - offset; + + print_bufs(read, written, offset, len); + } +} + +static void print_bufs(unsigned char *read, unsigned char *written, int start, + int len) +{ + int i = 0, j1, j2; + char *diff; + + printk("Offset Read Written\n"); + while (i < len) { + printk("0x%08x: ", start + i); + diff = " "; + for (j1 = 0; j1 < 8 && i + j1 < len; j1++) { + printk(" %02x", read[start + i + j1]); + if (read[start + i + j1] != written[start + i + j1]) + diff = "***"; + } + + while (j1 < 8) { + printk(" "); + j1 += 1; + } + + printk(" %s ", diff); + + for (j2 = 0; j2 < 8 && i + j2 < len; j2++) + printk(" %02x", written[start + i + j2]); + printk("\n"); + i += 8; + } +} + +/* + * Count the number of differing bytes and bits and return the first differing + * offset. + */ +static int countdiffs(unsigned char *buf, unsigned char *check_buf, + unsigned offset, unsigned len, unsigned *bytesp, + unsigned *bitsp) +{ + unsigned i, bit; + int first = -1; + + for (i = offset; i < offset + len; i++) + if (buf[i] != check_buf[i]) { + first = i; + break; + } + + while (i < offset + len) { + if (buf[i] != check_buf[i]) { + (*bytesp)++; + bit = 1; + while (bit < 256) { + if ((buf[i] & bit) != (check_buf[i] & bit)) + (*bitsp)++; + bit <<= 1; + } + } + i++; + } + + return first; +} + +MODULE_DESCRIPTION("Eraseblock torturing module"); +MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 7caf22cd5ad0..9082768cc6c3 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -561,7 +561,7 @@ static int io_init(struct ubi_device *ubi) */ ubi->peb_size = ubi->mtd->erasesize; - ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; + ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd); ubi->flash_size = ubi->mtd->size; if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 605812bb0b1a..6dd4f5e77f82 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -215,7 +215,8 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) struct ubi_volume *vol; struct ubi_device *ubi; - dbg_gen("erase %u bytes at offset %u", instr->len, instr->addr); + dbg_gen("erase %llu bytes at offset %llu", (unsigned long long)instr->len, + (unsigned long long)instr->addr); if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) return -EINVAL; @@ -223,11 +224,11 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) if (instr->len < 0 || instr->addr + instr->len > mtd->size) return -EINVAL; - if (instr->addr % mtd->writesize || instr->len % mtd->writesize) + if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) return -EINVAL; - lnum = instr->addr / mtd->erasesize; - count = instr->len / mtd->erasesize; + lnum = mtd_div_by_eb(instr->addr, mtd); + count = mtd_div_by_eb(instr->len, mtd); vol = container_of(mtd, struct ubi_volume, gluebi_mtd); ubi = vol->ubi; @@ -255,7 +256,7 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) out_err: instr->state = MTD_ERASE_FAILED; - instr->fail_addr = lnum * mtd->erasesize; + instr->fail_addr = (long long)lnum * mtd->erasesize; return err; } @@ -294,7 +295,7 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) * bytes. */ if (vol->vol_type == UBI_DYNAMIC_VOLUME) - mtd->size = vol->usable_leb_size * vol->reserved_pebs; + mtd->size = (long long)vol->usable_leb_size * vol->reserved_pebs; else mtd->size = vol->used_bytes; @@ -304,8 +305,8 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) return -ENFILE; } - dbg_gen("added mtd%d (\"%s\"), size %u, EB size %u", - mtd->index, mtd->name, mtd->size, mtd->erasesize); + dbg_gen("added mtd%d (\"%s\"), size %llu, EB size %u", + mtd->index, mtd->name, (unsigned long long)mtd->size, mtd->erasesize); return 0; } diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 5d9bcf109c13..4abbe573fa40 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -564,7 +564,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_unmap); * @dtype: expected data type * * This function maps an un-mapped logical eraseblock @lnum to a physical - * eraseblock. This means, that after a successfull invocation of this + * eraseblock. This means, that after a successful invocation of this * function the logical eraseblock @lnum will be empty (contain only %0xFF * bytes) and be mapped to a physical eraseblock, even if an unclean reboot * happens. diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9a18270c1081..65afda4a62d9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -830,7 +830,7 @@ config ULTRA32 config BFIN_MAC tristate "Blackfin on-chip MAC support" - depends on NET_ETHERNET && (BF526 || BF527 || BF536 || BF537) + depends on NET_ETHERNET && (BF516 || BF518 || BF526 || BF527 || BF536 || BF537) select CRC32 select MII select PHYLIB @@ -2614,6 +2614,8 @@ source "drivers/net/tokenring/Kconfig" source "drivers/net/wireless/Kconfig" +source "drivers/net/wimax/Kconfig" + source "drivers/net/usb/Kconfig" source "drivers/net/pcmcia/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index e5c34b464211..a3c5c002f224 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -263,3 +263,4 @@ obj-$(CONFIG_NIU) += niu.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_SFC) += sfc/ +obj-$(CONFIG_WIMAX) += wimax/ diff --git a/drivers/net/acenic_firmware.h b/drivers/net/acenic_firmware.h deleted file mode 100644 index fd41f7887e27..000000000000 --- a/drivers/net/acenic_firmware.h +++ /dev/null @@ -1,9456 +0,0 @@ -/* - * Declare these here even if Tigon I support is disabled to avoid - * the compiler complaining about undefined symbols. - */ -#define tigonFwReleaseMajor 0xc -#define tigonFwReleaseMinor 0x4 -#define tigonFwReleaseFix 0xb -#define tigonFwStartAddr 0x00004000 -#define tigonFwTextAddr 0x00004000 -#define tigonFwTextLen 0x11140 -#define tigonFwRodataAddr 0x00015140 -#define tigonFwRodataLen 0xac0 -#define tigonFwDataAddr 0x00015c20 -#define tigonFwDataLen 0x170 -#define tigonFwSbssAddr 0x00015d90 -#define tigonFwSbssLen 0x38 -#define tigonFwBssAddr 0x00015dd0 -#define tigonFwBssLen 0x2080 -#ifdef CONFIG_ACENIC_OMIT_TIGON_I -#define tigonFwText NULL -#define tigonFwData NULL -#define tigonFwRodata NULL -#else -/* Generated by genfw.c */ -static u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __devinitdata = { -0x10000003, -0x0, 0xd, 0xd, 0x3c1d0001, -0x8fbd5c54, 0x3a0f021, 0x3c100000, 0x26104000, -0xc00100c, 0x0, 0xd, 0x27bdffd8, -0x3c1cc000, 0x3c1b0013, 0x377bd800, 0xd021, -0x3c170013, 0x36f75418, 0x2e02021, 0x340583e8, -0xafbf0024, 0xc002488, 0xafb00020, 0xc0023e8, -0x0, 0x3c040001, 0x248451a4, 0x24050001, -0x2e03021, 0x3821, 0x3c100001, 0x26107e50, -0xafb00010, 0xc002403, 0xafbb0014, 0x3c02000f, -0x3442ffff, 0x2021024, 0x362102b, 0x10400009, -0x24050003, 0x3c040001, 0x248451b0, 0x2003021, -0x3603821, 0x3c020010, 0xafa20010, 0xc002403, -0xafa00014, 0x2021, 0x3405c000, 0x3c010001, -0x370821, 0xa02083b0, 0x3c010001, 0x370821, -0xa02083b2, 0x3c010001, 0x370821, 0xa02083b3, -0x3c010001, 0x370821, 0xac2083b4, 0xa2e004d8, -0x418c0, 0x24840001, 0x771021, 0xac40727c, -0x771021, 0xac407280, 0x2e31021, 0xa445727c, -0x2c820020, 0x1440fff7, 0x418c0, 0x2021, -0x3405c000, 0x418c0, 0x24840001, 0x771021, -0xac40737c, 0x771021, 0xac407380, 0x2e31021, -0xa445737c, 0x2c820080, 0x5440fff7, 0x418c0, -0xaf800054, 0xaf80011c, 0x8f820044, 0x34420040, -0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, -0x8f420218, 0x30420002, 0x10400009, 0x0, -0x8f420220, 0x3c030002, 0x34630004, 0x431025, -0xaee204c4, 0x8f42021c, 0x8001074, 0x34420004, -0x8f420220, 0x3c030002, 0x34630006, 0x431025, -0xaee204c4, 0x8f42021c, 0x34420006, 0xaee204cc, -0x8f420218, 0x30420010, 0x1040000a, 0x0, -0x8f42021c, 0x34420004, 0xaee204c8, 0x8f420220, -0x3c03000a, 0x34630004, 0x431025, 0x800108a, -0xaee204c0, 0x8f420220, 0x3c03000a, 0x34630006, -0x431025, 0xaee204c0, 0x8f42021c, 0x34420006, -0xaee204c8, 0x8f420218, 0x30420200, 0x10400003, -0x24020001, 0x8001091, 0xa2e27248, 0xa2e07248, -0x24020001, 0xaf8200a0, 0xaf8200b0, 0x8f830054, -0x8f820054, 0x8001099, 0x24630064, 0x8f820054, -0x621023, 0x2c420065, 0x1440fffc, 0x0, -0xaf800044, 0x8f420208, 0x8f43020c, 0xaee20010, -0xaee30014, 0x8ee40010, 0x8ee50014, 0x26e20030, -0xaee20028, 0x24020490, 0xaee20018, 0xaf840090, -0xaf850094, 0x8ee20028, 0xaf8200b4, 0x96e2001a, -0xaf82009c, 0x8f8200b0, 0x8ee304cc, 0x431025, -0xaf8200b0, 0x8f8200b0, 0x30420004, 0x1440fffd, -0x0, 0x8ee20450, 0x8ee30454, 0xaee304fc, -0x8ee204fc, 0x2442e000, 0x2c422001, 0x1440000d, -0x26e40030, 0x8ee20450, 0x8ee30454, 0x3c040001, -0x248451bc, 0x3c050001, 0xafa00010, 0xafa00014, -0x8ee704fc, 0x34a5f000, 0xc002403, 0x603021, -0x26e40030, 0xc002488, 0x24050400, 0x27440080, -0xc002488, 0x24050080, 0x26e4777c, 0xc002488, -0x24050400, 0x8f42025c, 0x26e40094, 0xaee20060, -0x8f420260, 0x27450200, 0x24060008, 0xaee20068, -0x24020006, 0xc00249a, 0xaee20064, 0x3c023b9a, -0x3442ca00, 0x2021, 0x24030002, 0xaee30074, -0xaee30070, 0xaee2006c, 0x240203e8, 0xaee20104, -0x24020001, 0xaee30100, 0xaee2010c, 0x3c030001, -0x641821, 0x90635c20, 0x2e41021, 0x24840001, -0xa043009c, 0x2c82000f, 0x1440fff8, 0x0, -0x8f820040, 0x2e41821, 0x24840001, 0x21702, -0x24420030, 0xa062009c, 0x2e41021, 0xa040009c, -0x96e2046a, 0x30420003, 0x14400009, 0x0, -0x96e2047a, 0x30420003, 0x50400131, 0x3c030800, -0x96e2046a, 0x30420003, 0x1040002a, 0x3c020700, -0x96e2047a, 0x30420003, 0x10400026, 0x3c020700, -0x96e3047a, 0x96e2046a, 0x14620022, 0x3c020700, -0x8ee204c0, 0x24030001, 0xa2e34e20, 0x34420e00, -0xaee204c0, 0x8f420218, 0x30420100, 0x10400005, -0x0, 0x3c020001, 0x2442e168, 0x800111d, -0x21100, 0x3c020001, 0x2442d35c, 0x21100, -0x21182, 0x3c030800, 0x431025, 0x3c010001, -0xac221238, 0x3c020001, 0x2442f680, 0x21100, -0x21182, 0x3c030800, 0x431025, 0x3c010001, -0xac221278, 0x8ee20000, 0x34424000, 0x8001238, -0xaee20000, 0x34423000, 0xafa20018, 0x8ee20608, -0x8f430228, 0x24420001, 0x304900ff, 0x512300e2, -0xafa00010, 0x8ee20608, 0x210c0, 0x571021, -0x8fa30018, 0x8fa4001c, 0xac43060c, 0xac440610, -0x8f870120, 0x27623800, 0x24e80020, 0x102102b, -0x50400001, 0x27683000, 0x8f820128, 0x11020004, -0x0, 0x8f820124, 0x15020007, 0x1021, -0x8ee201a4, 0x3021, 0x24420001, 0xaee201a4, -0x80011a0, 0x8ee201a4, 0x8ee40608, 0x420c0, -0x801821, 0x8ee40430, 0x8ee50434, 0xa32821, -0xa3302b, 0x822021, 0x862021, 0xace40000, -0xace50004, 0x8ee30608, 0x24020008, 0xa4e2000e, -0x2402000d, 0xace20018, 0xace9001c, 0x318c0, -0x2463060c, 0x2e31021, 0xace20008, 0x8ee204c4, -0xace20010, 0xaf880120, 0x92e24e20, 0x14400037, -0x24060001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c830000, 0x24020007, 0x1462001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x24030040, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee54e30, 0x24420001, 0x10430007, -0x0, 0x8ee24e34, 0x24420001, 0x10a20005, -0x0, 0x800118a, 0x0, 0x14a00005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400013, -0xac800000, 0x80011a0, 0x0, 0x8ee24e30, -0x24030040, 0x24420001, 0x50430003, 0x1021, -0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x24020007, -0xac820000, 0x24020001, 0xac820004, 0x54c0000c, -0xaee90608, 0x3c040001, 0x248451c8, 0xafa00010, -0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, -0xc002403, 0x34a5f000, 0x8001223, 0x0, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x3021, 0x24420001, 0xaee201a4, -0x8001207, 0x8ee201a4, 0x8ee20608, 0xac62001c, -0x8ee404a0, 0x8ee504a4, 0x2462001c, 0xac620008, -0x24020008, 0xa462000e, 0x24020011, 0xac620018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400037, 0x24060001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c830000, 0x24020012, 0x1462001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x24030040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee54e30, 0x24420001, 0x10430007, 0x0, -0x8ee24e34, 0x24420001, 0x10a20005, 0x0, -0x80011f1, 0x0, 0x14a00005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x8001207, 0x0, 0x8ee24e30, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020012, 0xac820000, -0x24020001, 0xac820004, 0x14c0001b, 0x0, -0x3c040001, 0x248451d0, 0xafa00010, 0xafa00014, -0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, -0x34a5f001, 0x8ee201b0, 0x24420001, 0xaee201b0, -0x8001223, 0x8ee201b0, 0x3c040001, 0x248451dc, -0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, -0xc002403, 0x34a5f005, 0x8ee201ac, 0x24420001, -0xaee201ac, 0x8ee201ac, 0x8ee20160, 0x3c040001, -0x248451e8, 0x3405f001, 0x24420001, 0xaee20160, -0x8ee20160, 0x3021, 0x3821, 0xafa00010, -0xc002403, 0xafa00014, 0x8001238, 0x0, -0x3c020001, 0x2442f5a8, 0x21100, 0x21182, -0x431025, 0x3c010001, 0xac221278, 0x96e2045a, -0x30420003, 0x10400025, 0x3c050fff, 0x8ee204c8, -0x34a5ffff, 0x34420a00, 0xaee204c8, 0x8ee304c8, -0x3c040001, 0x248451f4, 0x24020001, 0xa2e204ec, -0xa2e204ed, 0x3c020002, 0x621825, 0x3c020001, -0x2442a390, 0x451024, 0x21082, 0xaee304c8, -0x3c030800, 0x431025, 0x3c010001, 0xac221220, -0x3c020001, 0x2442add4, 0x451024, 0x21082, -0x431025, 0x3c010001, 0xac221280, 0x96e6045a, -0x3821, 0x24050011, 0xafa00010, 0xc002403, -0xafa00014, 0x8001268, 0x0, 0x3c020001, -0x2442a9d4, 0x21100, 0x21182, 0x3c030800, -0x431025, 0x3c010001, 0xac221280, 0x96e2046a, -0x30420010, 0x14400009, 0x0, 0x96e2047a, -0x30420010, 0x10400112, 0x0, 0x96e2046a, -0x30420010, 0x10400005, 0x3c020700, 0x96e2047a, -0x30420010, 0x14400102, 0x3c020700, 0x34423000, -0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, -0x304900ff, 0x512300e2, 0xafa00010, 0x8ee20608, -0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, -0xac43060c, 0xac440610, 0x8f870120, 0x27623800, -0x24e80020, 0x102102b, 0x50400001, 0x27683000, -0x8f820128, 0x11020004, 0x0, 0x8f820124, -0x15020007, 0x1021, 0x8ee201a4, 0x3021, -0x24420001, 0xaee201a4, 0x80012ea, 0x8ee201a4, -0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, -0x8ee50434, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xace40000, 0xace50004, 0x8ee30608, -0x24020008, 0xa4e2000e, 0x2402000d, 0xace20018, -0xace9001c, 0x318c0, 0x2463060c, 0x2e31021, -0xace20008, 0x8ee204c4, 0xace20010, 0xaf880120, -0x92e24e20, 0x14400037, 0x24060001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020007, 0x1462001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, -0x24420001, 0x10430007, 0x0, 0x8ee24e34, -0x24420001, 0x10a20005, 0x0, 0x80012d4, -0x0, 0x14a00005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x80012ea, -0x0, 0x8ee24e30, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x24020007, 0xac820000, 0x24020001, -0xac820004, 0x54c0000c, 0xaee90608, 0x3c040001, -0x248451c8, 0xafa00010, 0xafa00014, 0x8ee60608, -0x8f470228, 0x3c050009, 0xc002403, 0x34a5f000, -0x800136d, 0x0, 0x8f830120, 0x27623800, -0x24660020, 0xc2102b, 0x50400001, 0x27663000, -0x8f820128, 0x10c20004, 0x0, 0x8f820124, -0x14c20007, 0x0, 0x8ee201a4, 0x3021, -0x24420001, 0xaee201a4, 0x8001351, 0x8ee201a4, -0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, -0x2462001c, 0xac620008, 0x24020008, 0xa462000e, -0x24020011, 0xac620018, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400037, 0x24060001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c830000, 0x24020012, -0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x24030040, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, -0x10430007, 0x0, 0x8ee24e34, 0x24420001, -0x10a20005, 0x0, 0x800133b, 0x0, -0x14a00005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400013, 0xac800000, 0x8001351, 0x0, -0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020012, 0xac820000, 0x24020001, 0xac820004, -0x14c0001b, 0x0, 0x3c040001, 0x248451d0, -0xafa00010, 0xafa00014, 0x8ee60608, 0x8f470228, -0x3c050009, 0xc002403, 0x34a5f001, 0x8ee201b0, -0x24420001, 0xaee201b0, 0x800136d, 0x8ee201b0, -0x3c040001, 0x248451dc, 0xafa00014, 0x8ee60608, -0x8f470228, 0x3c050009, 0xc002403, 0x34a5f005, -0x8ee201ac, 0x24420001, 0xaee201ac, 0x8ee201ac, -0x8ee20160, 0x3c040001, 0x248451e8, 0x3405f002, -0x24420001, 0xaee20160, 0x8ee20160, 0x3021, -0x3821, 0xafa00010, 0xc002403, 0xafa00014, -0x96e6047a, 0x96e7046a, 0x3c040001, 0x24845200, -0x24050012, 0xafa00010, 0xc002403, 0xafa00014, -0xc004500, 0x0, 0xc002318, 0x0, -0x3c060001, 0x34c63800, 0xaee00608, 0xaf400228, -0xaf40022c, 0x96e30458, 0x8ee40000, 0x3c0512d8, -0x34a5c358, 0x27623800, 0xaee27258, 0x27623800, -0xaee27260, 0x27623800, 0xaee27264, 0x3661021, -0xaee27270, 0x2402ffff, 0xaee004d4, 0xaee004e0, -0xaee004e4, 0xaee004f0, 0xa2e004f4, 0xaee00e0c, -0xaee00e18, 0xaee00e10, 0xaee00e14, 0xaee00e1c, -0xaee0724c, 0xaee05244, 0xaee05240, 0xaee0523c, -0xaee07250, 0xaee07254, 0xaee0725c, 0xaee07268, -0xaee004d0, 0x2463ffff, 0x852025, 0xaee304f8, -0xaee40000, 0xaf800060, 0xaf820064, 0x3c020100, -0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, -0x304900ff, 0x512300e2, 0xafa00010, 0x8ee20608, -0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, -0xac43060c, 0xac440610, 0x8f870120, 0x27623800, -0x24e80020, 0x102102b, 0x50400001, 0x27683000, -0x8f820128, 0x11020004, 0x0, 0x8f820124, -0x15020007, 0x1021, 0x8ee201a4, 0x3021, -0x24420001, 0xaee201a4, 0x8001422, 0x8ee201a4, -0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, -0x8ee50434, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xace40000, 0xace50004, 0x8ee30608, -0x24020008, 0xa4e2000e, 0x2402000d, 0xace20018, -0xace9001c, 0x318c0, 0x2463060c, 0x2e31021, -0xace20008, 0x8ee204c4, 0xace20010, 0xaf880120, -0x92e24e20, 0x14400037, 0x24060001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020007, 0x1462001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, -0x24420001, 0x10430007, 0x0, 0x8ee24e34, -0x24420001, 0x10a20005, 0x0, 0x800140c, -0x0, 0x14a00005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x8001422, -0x0, 0x8ee24e30, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x24020007, 0xac820000, 0x24020001, -0xac820004, 0x54c0000c, 0xaee90608, 0x3c040001, -0x248451c8, 0xafa00010, 0xafa00014, 0x8ee60608, -0x8f470228, 0x3c050009, 0xc002403, 0x34a5f000, -0x80014a5, 0x0, 0x8f830120, 0x27623800, -0x24660020, 0xc2102b, 0x50400001, 0x27663000, -0x8f820128, 0x10c20004, 0x0, 0x8f820124, -0x14c20007, 0x0, 0x8ee201a4, 0x3021, -0x24420001, 0xaee201a4, 0x8001489, 0x8ee201a4, -0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, -0x2462001c, 0xac620008, 0x24020008, 0xa462000e, -0x24020011, 0xac620018, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400037, 0x24060001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c830000, 0x24020012, -0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x24030040, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, -0x10430007, 0x0, 0x8ee24e34, 0x24420001, -0x10a20005, 0x0, 0x8001473, 0x0, -0x14a00005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400013, 0xac800000, 0x8001489, 0x0, -0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020012, 0xac820000, 0x24020001, 0xac820004, -0x14c0001b, 0x0, 0x3c040001, 0x248451d0, -0xafa00010, 0xafa00014, 0x8ee60608, 0x8f470228, -0x3c050009, 0xc002403, 0x34a5f001, 0x8ee201b0, -0x24420001, 0xaee201b0, 0x80014a5, 0x8ee201b0, -0x3c040001, 0x248451dc, 0xafa00014, 0x8ee60608, -0x8f470228, 0x3c050009, 0xc002403, 0x34a5f005, -0x8ee201ac, 0x24420001, 0xaee201ac, 0x8ee201ac, -0x8ee20154, 0x24420001, 0xaee20154, 0xc0014dc, -0x8ee20154, 0x8f8200a0, 0x30420004, 0x1440fffd, -0x0, 0x8f820040, 0x30420001, 0x14400008, -0x0, 0x8f430104, 0x24020001, 0x10620004, -0x0, 0x8f420264, 0x10400006, 0x0, -0x8ee2017c, 0x24420001, 0xaee2017c, 0x80014c5, -0x8ee2017c, 0x8f820044, 0x34420004, 0xaf820044, -0x8ee20178, 0x24420001, 0xaee20178, 0x8ee20178, -0x8f8200d8, 0x8f8300d4, 0x431023, 0xaee2726c, -0x8ee2726c, 0x1c400003, 0x3c030001, 0x431021, -0xaee2726c, 0xc004064, 0x0, 0xc004440, -0xaf800228, 0x8fbf0024, 0x8fb00020, 0x3e00008, -0x27bd0028, 0x3e00008, 0x0, 0x3e00008, -0x0, 0x0, 0x0, 0x2402002c, -0xaf820050, 0xaee07274, 0x8f420238, 0xaee27278, -0x8f820054, 0x24420067, 0xaf820058, 0xaee07b88, -0xaee07b8c, 0xaee07b84, 0x3c010001, 0x370821, -0xac2083bc, 0x3c010001, 0x370821, 0x3e00008, -0xa02083b9, 0x27bdffd8, 0xafbf0024, 0xafb00020, -0x8f820054, 0x3c030001, 0x8c635cd8, 0x24420067, -0x1060000d, 0xaf820058, 0x3c020001, 0x571021, -0x904283b8, 0x10400005, 0x3c030200, 0x3c010001, -0x370821, 0x8001503, 0xa02083b8, 0x8ee20000, -0x431025, 0xaee20000, 0x8f420218, 0x30420100, -0x104000c6, 0x0, 0x8f8200b0, 0x30420004, -0x104000c2, 0x0, 0x3c030001, 0x771821, -0x8c6383d0, 0x8f820104, 0x146200b4, 0x0, -0x3c030001, 0x771821, 0x8c6383d4, 0x8f8200b4, -0x146200ae, 0x0, 0x8f8200b0, 0x3c030080, -0x431024, 0x1040000d, 0x0, 0x8f82011c, -0x34420002, 0xaf82011c, 0x8f8200b0, 0x2403fffb, -0x431024, 0xaf8200b0, 0x8f82011c, 0x2403fffd, -0x431024, 0x80015cc, 0xaf82011c, 0x3c030001, -0x771821, 0x8c6383d0, 0x8f820104, 0x14620082, -0x0, 0x3c030001, 0x771821, 0x8c6383d4, -0x8f8200b4, 0x1462007c, 0x0, 0x3c070001, -0xf73821, 0x8ce783d0, 0x8f8200b0, 0x3c040001, -0x24845270, 0xafa00014, 0xafa20010, 0x8f8600b0, -0x3c050005, 0xc002403, 0x34a50900, 0x8f82011c, -0x34420002, 0xaf82011c, 0x8f830104, 0x8f8200b0, -0x34420001, 0xaf8200b0, 0xaf830104, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20006, 0x0, 0x8ee201a4, -0x24420001, 0xaee201a4, 0x80015a0, 0x8ee201a4, -0x8f440208, 0x8f45020c, 0x26e20030, 0xac620008, -0x24020400, 0xa462000e, 0x2402000f, 0xac620018, -0xac60001c, 0xac640000, 0xac650004, 0x8ee204c4, -0xac620010, 0xaf860120, 0x92e24e20, 0x14400037, -0x0, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c830000, 0x24020007, 0x1462001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x24030040, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee54e30, 0x24420001, 0x10430007, -0x0, 0x8ee24e34, 0x24420001, 0x10a20005, -0x0, 0x800158a, 0x0, 0x14a00005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400013, -0xac800000, 0x80015a0, 0x0, 0x8ee24e30, -0x24030040, 0x24420001, 0x50430003, 0x1021, -0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x24020007, -0xac820000, 0x24020001, 0xac820004, 0x8f82011c, -0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201e4, -0x3c070001, 0xf73821, 0x8ce783d0, 0x24420001, -0xaee201e4, 0x8ee201e4, 0x3c040001, 0x2484527c, -0x80015bd, 0xafa00010, 0x8f820104, 0x3c010001, -0x370821, 0xac2283d0, 0x8f8200b4, 0x3c070001, -0xf73821, 0x8ce783d0, 0x3c040001, 0x24845284, -0x3c010001, 0x370821, 0xac2283d4, 0xafa00010, -0xafa00014, 0x8f8600b0, 0x3c050005, 0xc002403, -0x34a50900, 0x80015cc, 0x0, 0x8f820104, -0x3c010001, 0x370821, 0xac2283d0, 0x8f8200b4, -0x3c010001, 0x370821, 0xac2283d4, 0x8ee27274, -0x92e304f4, 0x24420067, 0x14600006, 0xaee27274, -0x8ee27274, 0x8f430234, 0x43102b, 0x1440007b, -0x0, 0x8ee304e4, 0x8ee204f8, 0x14620004, -0x0, 0x92e204f4, 0x50400074, 0xa2e004f4, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, -0x8001637, 0x8ee201a4, 0x8ee204e4, 0xac62001c, -0x8ee404b0, 0x8ee504b4, 0x2462001c, 0xac620008, -0x24020008, 0xa462000e, 0x24020011, 0xac620018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400037, 0x24100001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c830000, 0x24020012, 0x1462001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x24030040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee54e30, 0x24420001, 0x10430007, 0x0, -0x8ee24e34, 0x24420001, 0x10a20005, 0x0, -0x8001621, 0x0, 0x14a00005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x8001637, 0x0, 0x8ee24e30, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020012, 0xac820000, -0x24020001, 0xac820004, 0x5600000b, 0x24100001, -0x8ee204e4, 0x3c040001, 0x2484528c, 0xafa00014, -0xafa20010, 0x8ee60608, 0x8f470228, 0x3c050009, -0xc002403, 0x34a5f006, 0x16000003, 0x24020001, -0x8001650, 0xa2e204f4, 0x8ee20170, 0x24420001, -0xaee20170, 0x8ee20170, 0x8ee204e4, 0xa2e004f4, -0xaee004f0, 0xaee07274, 0xaee204f8, 0x8ee20e1c, -0x1040006d, 0x0, 0x8f830120, 0x27623800, -0x24660020, 0xc2102b, 0x50400001, 0x27663000, -0x8f820128, 0x10c20004, 0x0, 0x8f820124, -0x14c20007, 0x0, 0x8ee201a4, 0x8021, -0x24420001, 0xaee201a4, 0x80016ad, 0x8ee201a4, -0x8ee2724c, 0xac62001c, 0x8ee404a8, 0x8ee504ac, -0x2462001c, 0xac620008, 0x24020008, 0xa462000e, -0x24020011, 0xac620018, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400037, 0x24100001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c830000, 0x24020012, -0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x24030040, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, -0x10430007, 0x0, 0x8ee24e34, 0x24420001, -0x10a20005, 0x0, 0x8001697, 0x0, -0x14a00005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400013, 0xac800000, 0x80016ad, 0x0, -0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020012, 0xac820000, 0x24020001, 0xac820004, -0x5600000b, 0x24100001, 0x8ee2724c, 0x3c040001, -0x24845298, 0xafa00014, 0xafa20010, 0x8ee6724c, -0x8f470280, 0x3c050009, 0xc002403, 0x34a5f008, -0x56000001, 0xaee00e1c, 0x8ee20174, 0x24420001, -0xaee20174, 0x8ee20174, 0x8ee24e24, 0x10400019, -0x0, 0xaee04e24, 0x8f820040, 0x30420001, -0x14400008, 0x0, 0x8f430104, 0x24020001, -0x10620004, 0x0, 0x8f420264, 0x10400006, -0x0, 0x8ee2017c, 0x24420001, 0xaee2017c, -0x80016da, 0x8ee2017c, 0x8f820044, 0x34420004, -0xaf820044, 0x8ee20178, 0x24420001, 0xaee20178, -0x8ee20178, 0x8ee27278, 0x2442ff99, 0xaee27278, -0x8ee27278, 0x1c4002ad, 0x0, 0x8f420238, -0x104002aa, 0x0, 0x3c020001, 0x571021, -0x904283e0, 0x144002a5, 0x0, 0x8f420080, -0xaee2004c, 0x8f4200c0, 0xaee20048, 0x8f420084, -0xaee20038, 0x8f420084, 0xaee20244, 0x8f420088, -0xaee20248, 0x8f42008c, 0xaee2024c, 0x8f420090, -0xaee20250, 0x8f420094, 0xaee20254, 0x8f420098, -0xaee20258, 0x8f42009c, 0xaee2025c, 0x8f4200a0, -0xaee20260, 0x8f4200a4, 0xaee20264, 0x8f4200a8, -0xaee20268, 0x8f4200ac, 0xaee2026c, 0x8f4200b0, -0xaee20270, 0x8f4200b4, 0xaee20274, 0x8f4200b8, -0xaee20278, 0x8f4200bc, 0x24040001, 0xaee2027c, -0xaee0003c, 0x41080, 0x571021, 0x8ee3003c, -0x8c420244, 0x24840001, 0x621821, 0x2c82000f, -0xaee3003c, 0x1440fff8, 0x41080, 0x8f4200cc, -0xaee20050, 0x8f4200d0, 0xaee20054, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x8021, 0x24420001, 0xaee201a4, 0x8001775, -0x8ee201a4, 0x8f440208, 0x8f45020c, 0x26e20030, -0xac620008, 0x24020400, 0xa462000e, 0x2402000f, -0xac620018, 0xac60001c, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400037, 0x24100001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c830000, 0x24020007, -0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x24030040, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, -0x10430007, 0x0, 0x8ee24e34, 0x24420001, -0x10a20005, 0x0, 0x800175f, 0x0, -0x14a00005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400013, 0xac800000, 0x8001775, 0x0, -0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020007, 0xac820000, 0x24020001, 0xac820004, -0x12000212, 0x3c020400, 0xafa20018, 0x3c020001, -0x571021, 0x904283b0, 0x1040010b, 0x0, -0x8ee20608, 0x8f430228, 0x24420001, 0x304a00ff, -0x514300fd, 0xafa00010, 0x8ee20608, 0x210c0, -0x571021, 0x8fa30018, 0x8fa4001c, 0xac43060c, -0xac440610, 0x8f830054, 0x8f820054, 0x24690032, -0x1221023, 0x2c420033, 0x1040006a, 0x5821, -0x24180008, 0x240f000d, 0x240d0007, 0x240c0040, -0x240e0001, 0x8f870120, 0x27623800, 0x24e80020, -0x102102b, 0x50400001, 0x27683000, 0x8f820128, -0x11020004, 0x0, 0x8f820124, 0x15020007, -0x1021, 0x8ee201a4, 0x8021, 0x24420001, -0xaee201a4, 0x80017f3, 0x8ee201a4, 0x8ee40608, -0x420c0, 0x801821, 0x8ee40430, 0x8ee50434, -0xa32821, 0xa3302b, 0x822021, 0x862021, -0xace40000, 0xace50004, 0x8ee20608, 0xa4f8000e, -0xacef0018, 0xacea001c, 0x210c0, 0x2442060c, -0x2e21021, 0xace20008, 0x8ee204c4, 0xace20010, -0xaf880120, 0x92e24e20, 0x14400033, 0x24100001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x144d001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x104c0007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x80017e0, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400010, 0xac800000, 0x80017f3, -0x0, 0x8ee24e30, 0x24420001, 0x504c0003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0xac8d0000, 0xac8e0004, 0x56000006, 0x240b0001, -0x8f820054, 0x1221023, 0x2c420033, 0x1440ff9d, -0x0, 0x316300ff, 0x24020001, 0x14620077, -0x3c050009, 0xaeea0608, 0x8f830054, 0x8f820054, -0x24690032, 0x1221023, 0x2c420033, 0x10400061, -0x5821, 0x240d0008, 0x240c0011, 0x24080012, -0x24070040, 0x240a0001, 0x8f830120, 0x27623800, -0x24660020, 0xc2102b, 0x50400001, 0x27663000, -0x8f820128, 0x10c20004, 0x0, 0x8f820124, -0x14c20007, 0x0, 0x8ee201a4, 0x8021, -0x24420001, 0xaee201a4, 0x800185f, 0x8ee201a4, -0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, -0x2462001c, 0xac620008, 0xa46d000e, 0xac6c0018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400033, 0x24100001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x1448001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x10470007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x800184c, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400010, 0xac800000, 0x800185f, -0x0, 0x8ee24e30, 0x24420001, 0x50470003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0xac880000, 0xac8a0004, 0x56000006, 0x240b0001, -0x8f820054, 0x1221023, 0x2c420033, 0x1440ffa6, -0x0, 0x316300ff, 0x24020001, 0x14620003, -0x3c050009, 0x800197c, 0x24100001, 0x3c040001, -0x248452a4, 0xafa00010, 0xafa00014, 0x8f860120, -0x8f870124, 0x800187b, 0x34a5f011, 0x3c040001, -0x248452b0, 0xafa00010, 0xafa00014, 0x8f860120, -0x8f870124, 0x34a5f010, 0xc002403, 0x8021, -0x800197c, 0x0, 0x3c040001, 0x248452bc, -0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, -0x8001975, 0x34a5f00f, 0x8ee20608, 0x8f430228, -0x24420001, 0x304900ff, 0x512300e2, 0xafa00010, -0x8ee20608, 0x210c0, 0x571021, 0x8fa30018, -0x8fa4001c, 0xac43060c, 0xac440610, 0x8f870120, -0x27623800, 0x24e80020, 0x102102b, 0x50400001, -0x27683000, 0x8f820128, 0x11020004, 0x0, -0x8f820124, 0x15020007, 0x1021, 0x8ee201a4, -0x8021, 0x24420001, 0xaee201a4, 0x80018f7, -0x8ee201a4, 0x8ee40608, 0x420c0, 0x801821, -0x8ee40430, 0x8ee50434, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0xace40000, 0xace50004, -0x8ee30608, 0x24020008, 0xa4e2000e, 0x2402000d, -0xace20018, 0xace9001c, 0x318c0, 0x2463060c, -0x2e31021, 0xace20008, 0x8ee204c4, 0xace20010, -0xaf880120, 0x92e24e20, 0x14400037, 0x24100001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c830000, 0x24020007, 0x1462001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x24030040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee54e30, 0x24420001, 0x10430007, 0x0, -0x8ee24e34, 0x24420001, 0x10a20005, 0x0, -0x80018e1, 0x0, 0x14a00005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x80018f7, 0x0, 0x8ee24e30, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020007, 0xac820000, -0x24020001, 0xac820004, 0x5600000c, 0xaee90608, -0x3c040001, 0x248452c8, 0xafa00010, 0xafa00014, -0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, -0x34a5f000, 0x800197c, 0x0, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x8021, 0x24420001, 0xaee201a4, 0x800195e, -0x8ee201a4, 0x8ee20608, 0xac62001c, 0x8ee404a0, -0x8ee504a4, 0x2462001c, 0xac620008, 0x24020008, -0xa462000e, 0x24020011, 0xac620018, 0xac640000, -0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, -0x92e24e20, 0x14400037, 0x24100001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020012, 0x1462001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, -0x24420001, 0x10430007, 0x0, 0x8ee24e34, -0x24420001, 0x10a20005, 0x0, 0x8001948, -0x0, 0x14a00005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x800195e, -0x0, 0x8ee24e30, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x24020012, 0xac820000, 0x24020001, -0xac820004, 0x5600001d, 0x24100001, 0x3c040001, -0x248452d0, 0xafa00010, 0xafa00014, 0x8ee60608, -0x8f470228, 0x3c050009, 0xc002403, 0x34a5f001, -0x8ee201b0, 0x24420001, 0xaee201b0, 0x800197c, -0x8ee201b0, 0x3c040001, 0x248452dc, 0xafa00014, -0x8ee60608, 0x8f470228, 0x3c050009, 0x34a5f005, -0xc002403, 0x0, 0x8ee201ac, 0x8021, -0x24420001, 0xaee201ac, 0x8ee201ac, 0x1200000c, -0x24020001, 0x3c010001, 0x370821, 0xa02083b0, -0x8f420238, 0x8ee30158, 0x24630001, 0xaee30158, -0x8ee30158, 0x800198c, 0xaee27278, 0x24020001, -0x3c010001, 0x370821, 0xa02283b0, 0x3c020001, -0x8c425cd8, 0x10400187, 0x0, 0x8ee27b84, -0x24430001, 0x284200c9, 0x144001a4, 0xaee37b84, -0x8ee204d4, 0x30420002, 0x14400119, 0xaee07b84, -0x8ee204d4, 0x3c030600, 0x34631000, 0x34420002, -0xaee204d4, 0xafa30018, 0x8ee20608, 0x8f430228, -0x24420001, 0x304a00ff, 0x514300fd, 0xafa00010, -0x8ee20608, 0x210c0, 0x571021, 0x8fa30018, -0x8fa4001c, 0xac43060c, 0xac440610, 0x8f830054, -0x8f820054, 0x24690032, 0x1221023, 0x2c420033, -0x1040006a, 0x5821, 0x24180008, 0x240f000d, -0x240d0007, 0x240c0040, 0x240e0001, 0x8f870120, -0x27623800, 0x24e80020, 0x102102b, 0x50400001, -0x27683000, 0x8f820128, 0x11020004, 0x0, -0x8f820124, 0x15020007, 0x1021, 0x8ee201a4, -0x8021, 0x24420001, 0xaee201a4, 0x8001a15, -0x8ee201a4, 0x8ee40608, 0x420c0, 0x801821, -0x8ee40430, 0x8ee50434, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0xace40000, 0xace50004, -0x8ee20608, 0xa4f8000e, 0xacef0018, 0xacea001c, -0x210c0, 0x2442060c, 0x2e21021, 0xace20008, -0x8ee204c4, 0xace20010, 0xaf880120, 0x92e24e20, -0x14400033, 0x24100001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c820000, 0x144d001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x104c0007, -0x0, 0x8ee24e34, 0x24420001, 0x10620005, -0x0, 0x8001a02, 0x0, 0x14600005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, -0xac800000, 0x8001a15, 0x0, 0x8ee24e30, -0x24420001, 0x504c0003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0xac8d0000, 0xac8e0004, -0x56000006, 0x240b0001, 0x8f820054, 0x1221023, -0x2c420033, 0x1440ff9d, 0x0, 0x316300ff, -0x24020001, 0x54620078, 0xafa00010, 0xaeea0608, -0x8f830054, 0x8f820054, 0x24690032, 0x1221023, -0x2c420033, 0x10400061, 0x5821, 0x240d0008, -0x240c0011, 0x24080012, 0x24070040, 0x240a0001, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, -0x8001a81, 0x8ee201a4, 0x8ee20608, 0xac62001c, -0x8ee404a0, 0x8ee504a4, 0x2462001c, 0xac620008, -0xa46d000e, 0xac6c0018, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400033, 0x24100001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c820000, 0x1448001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10470007, -0x0, 0x8ee24e34, 0x24420001, 0x10620005, -0x0, 0x8001a6e, 0x0, 0x14600005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, -0xac800000, 0x8001a81, 0x0, 0x8ee24e30, -0x24420001, 0x50470003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0xac880000, 0xac8a0004, -0x56000006, 0x240b0001, 0x8f820054, 0x1221023, -0x2c420033, 0x1440ffa6, 0x0, 0x316300ff, -0x24020001, 0x10620022, 0x0, 0x3c040001, -0x248452a4, 0xafa00010, 0xafa00014, 0x8f860120, -0x8f870124, 0x3c050009, 0xc002403, 0x34a5f011, -0x8001aad, 0x0, 0x3c040001, 0x248452b0, -0xafa00014, 0x8f860120, 0x8f870124, 0x3c050009, -0xc002403, 0x34a5f010, 0x8001aad, 0x0, -0x3c040001, 0x248452bc, 0xafa00014, 0x8ee60608, -0x8f470228, 0x3c050009, 0xc002403, 0x34a5f00f, -0x8ee201ac, 0x24420001, 0xaee201ac, 0x8ee201ac, -0x8ee2015c, 0x24420001, 0xaee2015c, 0x8ee2015c, -0x8ee204d4, 0x30420001, 0x10400055, 0x0, -0x8f420218, 0x30420080, 0x10400029, 0x0, -0x8f820044, 0x34420040, 0xaf820044, 0x8ee27b7c, -0x402821, 0x8ee200c0, 0x8ee300c4, 0x24060000, -0x2407ffff, 0x2021, 0x461024, 0x1444000d, -0x671824, 0x1465000b, 0x0, 0x8ee27b80, -0x402821, 0x8ee200e0, 0x8ee300e4, 0x2021, -0x461024, 0x14440003, 0x671824, 0x1065000b, -0x0, 0x8ee200c0, 0x8ee300c4, 0x8ee400e0, -0x8ee500e4, 0xaee37b7c, 0xaee57b80, 0x8f820044, -0x38420020, 0x8001b38, 0xaf820044, 0x8f820044, -0x2403ffdf, 0x431024, 0x8001b38, 0xaf820044, -0x8f820044, 0x2403ffdf, 0x431024, 0xaf820044, -0x8ee27b7c, 0x402821, 0x8ee200c0, 0x8ee300c4, -0x24060000, 0x2407ffff, 0x2021, 0x461024, -0x1444000d, 0x671824, 0x1465000b, 0x0, -0x8ee27b80, 0x402821, 0x8ee200e0, 0x8ee300e4, -0x2021, 0x461024, 0x14440003, 0x671824, -0x1065000b, 0x0, 0x8ee200c0, 0x8ee300c4, -0x8ee400e0, 0x8ee500e4, 0xaee37b7c, 0xaee57b80, -0x8f820044, 0x38420040, 0x8001b38, 0xaf820044, -0x8f820044, 0x34420040, 0x8001b38, 0xaf820044, -0x8f820044, 0x34420040, 0xaf820044, 0x8ee27b8c, -0x24430001, 0x28420015, 0x14400028, 0xaee37b8c, -0x8f820044, 0x38420020, 0xaf820044, 0x8001b38, -0xaee07b8c, 0x8ee204d4, 0x30420001, 0x10400011, -0x0, 0x8f420218, 0x30420080, 0x10400009, -0x0, 0x8f820044, 0x34420020, 0xaf820044, -0x8f820044, 0x2403ffbf, 0x431024, 0x8001b36, -0xaf820044, 0x8f820044, 0x34420060, 0x8001b36, -0xaf820044, 0x8f820044, 0x34420040, 0xaf820044, -0x8ee27b88, 0x24430001, 0x28421389, 0x14400005, -0xaee37b88, 0x8f820044, 0x38420020, 0xaf820044, -0xaee07b88, 0xc004603, 0x0, 0x8fbf0024, -0x8fb00020, 0x3e00008, 0x27bd0028, 0x27bdffb8, -0xafbf0044, 0xafb60040, 0xafb5003c, 0xafb40038, -0xafb30034, 0xafb20030, 0xafb1002c, 0xafb00028, -0x8f960064, 0x32c20004, 0x1040000c, 0x24020004, -0xaf820064, 0x8f420114, 0xaee204e0, 0x8f820060, -0x34420008, 0xaf820060, 0x8ee2016c, 0x24420001, -0xaee2016c, 0x80022f4, 0x8ee2016c, 0x32c20001, -0x10400004, 0x24020001, 0xaf820064, 0x80022f4, -0x0, 0x32c20002, 0x1440000c, 0x3c050003, -0x3c040001, 0x24845354, 0x34a50001, 0x2c03021, -0x3821, 0xafa00010, 0xc002403, 0xafa00014, -0x2402fff8, 0x80022f4, 0xaf820064, 0x8f43022c, -0x8f42010c, 0x5062000c, 0xafa00010, 0x8f42022c, -0x21080, 0x5a1021, 0x8c420300, 0xafa20020, -0x8f42022c, 0x24070001, 0x24420001, 0x3042003f, -0x8001b80, 0xaf42022c, 0x3c040001, 0x24845360, -0xafa00014, 0x8f46022c, 0x8f47010c, 0x3c050003, -0xc002403, 0x34a5f01f, 0x3821, 0x14e00003, -0x0, 0x80022ed, 0xaf960064, 0x93a20020, -0x2443ffff, 0x2c620011, 0x10400658, 0x31080, -0x3c010001, 0x220821, 0x8c225418, 0x400008, -0x0, 0x8fa20020, 0x30420fff, 0xaee20e0c, -0x8f820060, 0x34420200, 0xaf820060, 0x8ee20118, -0x24420001, 0xaee20118, 0x80022e8, 0x8ee20118, -0x8fa20020, 0x24030001, 0x3c010001, 0x370821, -0xa02383b1, 0x30420fff, 0xaee25238, 0x8f820060, -0x34420100, 0xaf820060, 0x8ee20144, 0x24420001, -0xaee20144, 0x80022e8, 0x8ee20144, 0x8fa20020, -0x21200, 0x22502, 0x24020001, 0x10820005, -0x24020002, 0x10820009, 0x2402fffe, 0x8001bc9, -0xafa00010, 0x8ee204d4, 0xaee40070, 0xaee40074, -0x34420001, 0x8001bbd, 0xaee204d4, 0x8ee304d4, -0xaee40070, 0xaee40074, 0x621824, 0xaee304d4, -0x8f840054, 0x41442, 0x41c82, 0x431021, -0x41cc2, 0x431023, 0x41d02, 0x431021, -0x41d42, 0x431023, 0x8001bd0, 0xaee20078, -0x3c040001, 0x2484536c, 0xafa00014, 0x8fa60020, -0x3c050003, 0xc002403, 0x34a50004, 0x8ee20110, -0x24420001, 0xaee20110, 0x80022e8, 0x8ee20110, -0x27440212, 0xc0022fe, 0x24050006, 0x3049001f, -0x920c0, 0x2e41021, 0x9442727c, 0x30424000, -0x1040000a, 0x971021, 0x97430212, 0xa443727e, -0x8f430214, 0x971021, 0xac437280, 0x2e41821, -0x34028000, 0x8001c79, 0xa462727c, 0x9443727e, -0x97420212, 0x14620006, 0x2e41021, 0x971021, -0x8c437280, 0x8f420214, 0x1062009f, 0x2e41021, -0x9442727c, 0x30428000, 0x1040002a, 0x2406ffff, -0x2021, 0x410c0, 0x2e21021, 0x9442737c, -0x30424000, 0x54400005, 0x803021, 0x24840001, -0x2c820080, 0x1440fff8, 0x410c0, 0x4c10010, -0x618c0, 0x610c0, 0x571821, 0x8c63737c, -0x571021, 0xafa30010, 0x8c427380, 0x3c040001, -0x24845378, 0xafa20014, 0x8f470214, 0x3c050003, -0xc002403, 0x34a50013, 0x8001c90, 0x3c020800, -0x97440212, 0x771021, 0xa444737e, 0x8f440214, -0x771021, 0x2e31821, 0xac447380, 0x34028000, -0xa462737c, 0x910c0, 0x2e21021, 0x8001c79, -0xa446727c, 0x2e41021, 0x9445727c, 0x8001c2e, -0x510c0, 0x9443737e, 0x97420212, 0x14620006, -0x510c0, 0x971021, 0x8c437380, 0x8f420214, -0x10620065, 0x510c0, 0x2e21021, 0x9445737c, -0x510c0, 0x2e21021, 0x9442737c, 0x30428000, -0x1040fff0, 0x971021, 0x520c0, 0x971021, -0x9443737e, 0x97420212, 0x14620006, 0x2406ffff, -0x971021, 0x8c437380, 0x8f420214, 0x10620053, -0x3c020800, 0x2021, 0x410c0, 0x2e21021, -0x9442737c, 0x30424000, 0x54400005, 0x803021, -0x24840001, 0x2c820080, 0x1440fff8, 0x410c0, -0x4c10023, 0x618c0, 0x910c0, 0x571821, -0x8c63727c, 0x571021, 0xafa30010, 0x8c427280, -0x3c040001, 0x24845384, 0xafa20014, 0x8f470214, -0x3c050003, 0xc002403, 0x34a5f017, 0x8001c90, -0x3c020800, 0x8f430210, 0xb71021, 0xac43777c, -0x8f430214, 0xb71021, 0xac437780, 0x3c020001, -0x571021, 0x8c4283b4, 0x24420001, 0x3c010001, -0x370821, 0xac2283b4, 0x3c030001, 0x771821, -0x8c6383b4, 0x2e51021, 0x8001c82, 0xa443777c, -0x97440212, 0x771021, 0xa444737e, 0x8f440214, -0x771021, 0x2e31821, 0xac447380, 0x34028000, -0xa462737c, 0x510c0, 0x2e21021, 0xa446737c, -0x2021, 0x428c0, 0x2e51021, 0x9442777c, -0x1040ffdc, 0x24840001, 0x2c820080, 0x5440fffa, -0x428c0, 0x92e204d8, 0x10400006, 0x24020001, -0x8ee304dc, 0x1221004, 0x621825, 0x8001c8f, -0xaee304dc, 0x8f830228, 0x24020001, 0x1221004, -0x621825, 0xaf830228, 0x3c020800, 0x34421000, -0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, -0x304a00ff, 0x514300fd, 0xafa00010, 0x8ee20608, -0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, -0xac43060c, 0xac440610, 0x8f830054, 0x8f820054, -0x24690032, 0x1221023, 0x2c420033, 0x1040006a, -0x5821, 0x24100008, 0x240f000d, 0x240d0007, -0x240c0040, 0x240e0001, 0x8f870120, 0x27623800, -0x24e80020, 0x102102b, 0x50400001, 0x27683000, -0x8f820128, 0x11020004, 0x0, 0x8f820124, -0x15020007, 0x1021, 0x8ee201a4, 0x3821, -0x24420001, 0xaee201a4, 0x8001d08, 0x8ee201a4, -0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, -0x8ee50434, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xace40000, 0xace50004, 0x8ee20608, -0xa4f0000e, 0xacef0018, 0xacea001c, 0x210c0, -0x2442060c, 0x2e21021, 0xace20008, 0x8ee204c4, -0xace20010, 0xaf880120, 0x92e24e20, 0x14400033, -0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c820000, 0x144d001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x104c0007, 0x0, -0x8ee24e34, 0x24420001, 0x10620005, 0x0, -0x8001cf5, 0x0, 0x14600005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400010, 0xac800000, -0x8001d08, 0x0, 0x8ee24e30, 0x24420001, -0x504c0003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0xac8d0000, 0xac8e0004, 0x54e00006, -0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, -0x1440ff9d, 0x0, 0x316300ff, 0x24020001, -0x54620078, 0xafa00010, 0xaeea0608, 0x8f830054, -0x8f820054, 0x24690032, 0x1221023, 0x2c420033, -0x10400061, 0x5821, 0x240e0008, 0x240d0011, -0x240a0012, 0x24080040, 0x240c0001, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x3821, 0x24420001, 0xaee201a4, 0x8001d74, -0x8ee201a4, 0x8ee20608, 0xac62001c, 0x8ee404a0, -0x8ee504a4, 0x2462001c, 0xac620008, 0xa46e000e, -0xac6d0018, 0xac640000, 0xac650004, 0x8ee204c4, -0xac620010, 0xaf860120, 0x92e24e20, 0x14400033, -0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c820000, 0x144a001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x10480007, 0x0, -0x8ee24e34, 0x24420001, 0x10620005, 0x0, -0x8001d61, 0x0, 0x14600005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400010, 0xac800000, -0x8001d74, 0x0, 0x8ee24e30, 0x24420001, -0x50480003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0xac8a0000, 0xac8c0004, 0x54e00006, -0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, -0x1440ffa6, 0x0, 0x316300ff, 0x24020001, -0x10620022, 0x0, 0x3c040001, 0x24845390, -0xafa00010, 0xafa00014, 0x8f860120, 0x8f870124, -0x3c050009, 0xc002403, 0x34a5f011, 0x8001da0, -0x0, 0x3c040001, 0x2484539c, 0xafa00014, -0x8f860120, 0x8f870124, 0x3c050009, 0xc002403, -0x34a5f010, 0x8001da0, 0x0, 0x3c040001, -0x248453a8, 0xafa00014, 0x8ee60608, 0x8f470228, -0x3c050009, 0xc002403, 0x34a5f00f, 0x8ee201ac, -0x24420001, 0xaee201ac, 0x8ee201ac, 0x8ee20124, -0x24420001, 0xaee20124, 0x8001f97, 0x8ee20124, -0x27440212, 0xc0022fe, 0x24050006, 0x3049001f, -0x928c0, 0x2e51021, 0x9442727c, 0x30428000, -0x1040002f, 0x2e51021, 0x9442727c, 0x30424000, -0x1440001c, 0xb71021, 0x9443727e, 0x97420212, -0x14620018, 0xb71021, 0x8c437280, 0x8f420214, -0x54620016, 0xafa20010, 0x92e204d8, 0x10400007, -0x24020001, 0x8ee304dc, 0x1221004, 0x21027, -0x621824, 0x8001dc9, 0xaee304dc, 0x8f830228, -0x1221004, 0x21027, 0x621824, 0xaf830228, -0x910c0, 0x2e21821, 0x3402c000, 0x8001e4e, -0xa462727c, 0x8f420214, 0xafa20010, 0x910c0, -0x571021, 0x8c42727c, 0x3c040001, 0x248453b4, -0x3c050003, 0xafa20014, 0x8f470210, 0x34a5f01c, -0xc002403, 0x1203021, 0x8001e83, 0x3c020800, -0xb71021, 0x9443727e, 0x97420212, 0x14620019, -0x918c0, 0xb71021, 0x8c437280, 0x8f420214, -0x14620014, 0x918c0, 0x2e51021, 0x9447727c, -0x720c0, 0x971021, 0x9443737e, 0xb71021, -0xa443727e, 0x971021, 0x8c437380, 0xb71021, -0xac437280, 0x2e41021, 0x9443737c, 0x2e51021, -0xa443727c, 0x2e41821, 0x3402c000, 0x8001e4e, -0xa462737c, 0x2e31021, 0x9447727c, 0x3021, -0x720c0, 0x2e41021, 0x9442737c, 0x4021, -0x30428000, 0x14400025, 0xe02821, 0x605021, -0x340bc000, 0x971021, 0x9443737e, 0x97420212, -0x54620015, 0xe02821, 0x971021, 0x8c437380, -0x8f420214, 0x54620010, 0xe02821, 0x11000006, -0x2e41021, 0x9443737c, 0x510c0, 0x2e21021, -0x8001e1a, 0xa443737c, 0x9443737c, 0x2ea1021, -0xa443727c, 0x710c0, 0x2e21021, 0xa44b737c, -0x8001e28, 0x24060001, 0x510c0, 0x2e21021, -0x9447737c, 0x720c0, 0x2e41021, 0x9442737c, -0x30428000, 0x1040ffdf, 0x25080001, 0x30c200ff, -0x14400025, 0x2021, 0x720c0, 0x971021, -0x9443737e, 0x97420212, 0x1462000f, 0x910c0, -0x971021, 0x8c437380, 0x8f420214, 0x1462000a, -0x910c0, 0x2e41821, 0x3402c000, 0x15000015, -0xa462737c, 0x910c0, 0x2e21821, 0x34028000, -0x8001e4e, 0xa462727c, 0x571021, 0x8c42727c, -0x3c040001, 0x248453c0, 0x3c050003, 0xafa20010, -0x710c0, 0x571021, 0x8c42737c, 0x34a5001e, -0x1203021, 0xc002403, 0xafa20014, 0x8001e83, -0x3c020800, 0x2021, 0x428c0, 0xb71021, -0x9443777e, 0x97420212, 0x5462002b, 0x24840001, -0xb71021, 0x8c437780, 0x8f420214, 0x54620026, -0x24840001, 0x3c020001, 0x571021, 0x8c4283b4, -0x2442ffff, 0x3c010001, 0x370821, 0xac2283b4, -0x3c020001, 0x571021, 0x8c4283b4, 0x809021, -0x242102b, 0x1040000e, 0x24b1777c, 0x24b07784, -0x2f02021, 0x2f12821, 0xc002490, 0x24060008, -0x26310008, 0x3c020001, 0x571021, 0x8c4283b4, -0x26520001, 0x242102b, 0x1440fff5, 0x26100008, -0x3c040001, 0x972021, 0x8c8483b4, 0x24050008, -0x420c0, 0x2484777c, 0xc002488, 0x2e42021, -0x8001e83, 0x3c020800, 0x2c820080, 0x1440ffcf, -0x428c0, 0x3c020800, 0x34422000, 0xafa20018, -0x8ee20608, 0x8f430228, 0x24420001, 0x304a00ff, -0x514300fd, 0xafa00010, 0x8ee20608, 0x210c0, -0x571021, 0x8fa30018, 0x8fa4001c, 0xac43060c, -0xac440610, 0x8f830054, 0x8f820054, 0x24690032, -0x1221023, 0x2c420033, 0x1040006a, 0x5821, -0x24100008, 0x240f000d, 0x240d0007, 0x240c0040, -0x240e0001, 0x8f870120, 0x27623800, 0x24e80020, -0x102102b, 0x50400001, 0x27683000, 0x8f820128, -0x11020004, 0x0, 0x8f820124, 0x15020007, -0x1021, 0x8ee201a4, 0x3821, 0x24420001, -0xaee201a4, 0x8001efb, 0x8ee201a4, 0x8ee40608, -0x420c0, 0x801821, 0x8ee40430, 0x8ee50434, -0xa32821, 0xa3302b, 0x822021, 0x862021, -0xace40000, 0xace50004, 0x8ee20608, 0xa4f0000e, -0xacef0018, 0xacea001c, 0x210c0, 0x2442060c, -0x2e21021, 0xace20008, 0x8ee204c4, 0xace20010, -0xaf880120, 0x92e24e20, 0x14400033, 0x24070001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x144d001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x104c0007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x8001ee8, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400010, 0xac800000, 0x8001efb, -0x0, 0x8ee24e30, 0x24420001, 0x504c0003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0xac8d0000, 0xac8e0004, 0x54e00006, 0x240b0001, -0x8f820054, 0x1221023, 0x2c420033, 0x1440ff9d, -0x0, 0x316300ff, 0x24020001, 0x54620078, -0xafa00010, 0xaeea0608, 0x8f830054, 0x8f820054, -0x24690032, 0x1221023, 0x2c420033, 0x10400061, -0x5821, 0x240e0008, 0x240d0011, 0x240a0012, -0x24080040, 0x240c0001, 0x8f830120, 0x27623800, -0x24660020, 0xc2102b, 0x50400001, 0x27663000, -0x8f820128, 0x10c20004, 0x0, 0x8f820124, -0x14c20007, 0x0, 0x8ee201a4, 0x3821, -0x24420001, 0xaee201a4, 0x8001f67, 0x8ee201a4, -0x8ee20608, 0xac62001c, 0x8ee404a0, 0x8ee504a4, -0x2462001c, 0xac620008, 0xa46e000e, 0xac6d0018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400033, 0x24070001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x144a001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x10480007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x8001f54, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400010, 0xac800000, 0x8001f67, -0x0, 0x8ee24e30, 0x24420001, 0x50480003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0xac8a0000, 0xac8c0004, 0x54e00006, 0x240b0001, -0x8f820054, 0x1221023, 0x2c420033, 0x1440ffa6, -0x0, 0x316300ff, 0x24020001, 0x10620022, -0x0, 0x3c040001, 0x24845390, 0xafa00010, -0xafa00014, 0x8f860120, 0x8f870124, 0x3c050009, -0xc002403, 0x34a5f011, 0x8001f93, 0x0, -0x3c040001, 0x2484539c, 0xafa00014, 0x8f860120, -0x8f870124, 0x3c050009, 0xc002403, 0x34a5f010, -0x8001f93, 0x0, 0x3c040001, 0x248453a8, -0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, -0xc002403, 0x34a5f00f, 0x8ee201ac, 0x24420001, -0xaee201ac, 0x8ee201ac, 0x8ee20128, 0x24420001, -0xaee20128, 0x8ee20128, 0x8ee20164, 0x24420001, -0xaee20164, 0x80022e8, 0x8ee20164, 0x8fa20020, -0x21200, 0x21d02, 0x24020001, 0x10620005, -0x24020002, 0x1062000d, 0x0, 0x8001fb7, -0xafa00010, 0x92e204d8, 0x14400006, 0x24020001, -0x8f820228, 0xaee204dc, 0x2402ffff, 0xaf820228, -0x24020001, 0x8001fbe, 0xa2e204d8, 0x92e204d8, -0x5040000c, 0xa2e004d8, 0x8ee204dc, 0xaf820228, -0x8001fbe, 0xa2e004d8, 0x3c040001, 0x248453c8, -0xafa00014, 0x8fa60020, 0x3c050003, 0xc002403, -0x34a5f009, 0x8ee2013c, 0x24420001, 0xaee2013c, -0x80022e8, 0x8ee2013c, 0x8fa20020, 0x21200, -0x22502, 0x24020001, 0x10820005, 0x24020002, -0x1082000f, 0x0, 0x8001fe3, 0xafa00010, -0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, -0x34420008, 0xaf820220, 0x24020001, 0x3c010001, -0x370821, 0xa02283b2, 0x8001fea, 0xaee40108, -0x8f820220, 0x3c0308ff, 0x3463fff7, 0x431024, -0xaf820220, 0x3c010001, 0x370821, 0xa02083b2, -0x8001fea, 0xaee40108, 0x3c040001, 0x248453d4, -0xafa00014, 0x8fa60020, 0x3c050003, 0xc002403, -0x34a5f00a, 0x8ee2012c, 0x24420001, 0xaee2012c, -0x80022e8, 0x8ee2012c, 0x8fa20020, 0x21200, -0x21d02, 0x24020001, 0x10620005, 0x24020002, -0x1062000e, 0x0, 0x8002011, 0xafa00010, -0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, -0x34420008, 0xaf820220, 0x24020001, 0x3c010001, -0x370821, 0x8002018, 0xa02283b3, 0x3c020001, -0x571021, 0x904283b2, 0x3c010001, 0x370821, -0x1440000e, 0xa02083b3, 0x8f820220, 0x3c0308ff, -0x3463fff7, 0x431024, 0x8002018, 0xaf820220, -0x3c040001, 0x248453e0, 0xafa00014, 0x8fa60020, -0x3c050003, 0xc002403, 0x34a5f00b, 0x8ee20114, -0x24420001, 0xaee20114, 0x80022e8, 0x8ee20114, -0x27840208, 0x27450200, 0xc00249a, 0x24060008, -0x26e40094, 0x27450200, 0xc00249a, 0x24060008, -0x8ee20134, 0x24420001, 0xaee20134, 0x80022e8, -0x8ee20134, 0x8f460248, 0x2021, 0xc005108, -0x24050004, 0x8ee20130, 0x24420001, 0xaee20130, -0x80022e8, 0x8ee20130, 0x8ef301cc, 0x8ef401d0, -0x8ef501d8, 0x8ee20140, 0x26e40030, 0x24420001, -0xaee20140, 0x8ef00140, 0x8ef10074, 0x8ef20070, -0xc002488, 0x24050400, 0xaef301cc, 0xaef401d0, -0xaef501d8, 0xaef00140, 0xaef10074, 0xaef20070, -0x8f42025c, 0x26e40094, 0xaee20060, 0x8f420260, -0x27450200, 0x24060008, 0xaee20068, 0x24020006, -0xc00249a, 0xaee20064, 0x3c023b9a, 0x3442ca00, -0xaee2006c, 0x240203e8, 0x24040002, 0x24030001, -0xaee20104, 0xaee40100, 0xaee3010c, 0x8f820220, -0x30420008, 0x10400004, 0x0, 0xaee30108, -0x8002061, 0x2021, 0xaee40108, 0x2021, -0x3c030001, 0x641821, 0x90635c30, 0x2e41021, -0x24840001, 0xa043009c, 0x2c82000f, 0x1440fff8, -0x0, 0x8f820040, 0x2e41821, 0x24840001, -0x21702, 0x24420030, 0xa062009c, 0x2e41021, -0x80022e8, 0xa040009c, 0x24020001, 0x3c010001, -0x370821, 0xa02283e0, 0x240b0400, 0x24080014, -0x240a0040, 0x24090001, 0x8f830100, 0x27623000, -0x24660020, 0xc2102b, 0x50400001, 0x27662800, -0x8f820108, 0x10c20004, 0x0, 0x8f820104, -0x14c20007, 0x26e20030, 0x8ee201a8, 0x3821, -0x24420001, 0xaee201a8, 0x80020a8, 0x8ee201a8, -0x8ee404b8, 0x8ee504bc, 0xac620008, 0xa46b000e, -0xac680018, 0xac60001c, 0xac640000, 0xac650004, -0x8ee204cc, 0xac620010, 0xaf860100, 0x92e204ec, -0x1440000e, 0x24070001, 0x8ee24e28, 0x24420001, -0x504a0003, 0x1021, 0x8ee24e28, 0x24420001, -0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e21021, 0xac480000, 0xac490004, 0x10e0ffd2, -0x0, 0x80022e8, 0x0, 0x3c020900, -0xaee05238, 0xaee0523c, 0xaee05240, 0xaee05244, -0xaee001d0, 0x3c010001, 0x370821, 0xa02083b1, -0xafa20018, 0x8ee20608, 0x8f430228, 0x24420001, -0x304a00ff, 0x514300fd, 0xafa00010, 0x8ee20608, -0x210c0, 0x571021, 0x8fa30018, 0x8fa4001c, -0xac43060c, 0xac440610, 0x8f830054, 0x8f820054, -0x24690032, 0x1221023, 0x2c420033, 0x1040006a, -0x5821, 0x24100008, 0x240f000d, 0x240d0007, -0x240c0040, 0x240e0001, 0x8f870120, 0x27623800, -0x24e80020, 0x102102b, 0x50400001, 0x27683000, -0x8f820128, 0x11020004, 0x0, 0x8f820124, -0x15020007, 0x1021, 0x8ee201a4, 0x3821, -0x24420001, 0xaee201a4, 0x800212c, 0x8ee201a4, -0x8ee40608, 0x420c0, 0x801821, 0x8ee40430, -0x8ee50434, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xace40000, 0xace50004, 0x8ee20608, -0xa4f0000e, 0xacef0018, 0xacea001c, 0x210c0, -0x2442060c, 0x2e21021, 0xace20008, 0x8ee204c4, -0xace20010, 0xaf880120, 0x92e24e20, 0x14400033, -0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c820000, 0x144d001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x104c0007, 0x0, -0x8ee24e34, 0x24420001, 0x10620005, 0x0, -0x8002119, 0x0, 0x14600005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400010, 0xac800000, -0x800212c, 0x0, 0x8ee24e30, 0x24420001, -0x504c0003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0xac8d0000, 0xac8e0004, 0x54e00006, -0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, -0x1440ff9d, 0x0, 0x316300ff, 0x24020001, -0x54620078, 0xafa00010, 0xaeea0608, 0x8f830054, -0x8f820054, 0x24690032, 0x1221023, 0x2c420033, -0x10400061, 0x5821, 0x240e0008, 0x240d0011, -0x240a0012, 0x24080040, 0x240c0001, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x3821, 0x24420001, 0xaee201a4, 0x8002198, -0x8ee201a4, 0x8ee20608, 0xac62001c, 0x8ee404a0, -0x8ee504a4, 0x2462001c, 0xac620008, 0xa46e000e, -0xac6d0018, 0xac640000, 0xac650004, 0x8ee204c4, -0xac620010, 0xaf860120, 0x92e24e20, 0x14400033, -0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c820000, 0x144a001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x10480007, 0x0, -0x8ee24e34, 0x24420001, 0x10620005, 0x0, -0x8002185, 0x0, 0x14600005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400010, 0xac800000, -0x8002198, 0x0, 0x8ee24e30, 0x24420001, -0x50480003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0xac8a0000, 0xac8c0004, 0x54e00006, -0x240b0001, 0x8f820054, 0x1221023, 0x2c420033, -0x1440ffa6, 0x0, 0x316300ff, 0x24020001, -0x10620022, 0x0, 0x3c040001, 0x24845390, -0xafa00010, 0xafa00014, 0x8f860120, 0x8f870124, -0x3c050009, 0xc002403, 0x34a5f011, 0x80021c4, -0x0, 0x3c040001, 0x2484539c, 0xafa00014, -0x8f860120, 0x8f870124, 0x3c050009, 0xc002403, -0x34a5f010, 0x80021c4, 0x0, 0x3c040001, -0x248453a8, 0xafa00014, 0x8ee60608, 0x8f470228, -0x3c050009, 0xc002403, 0x34a5f00f, 0x8ee201ac, -0x24420001, 0xaee201ac, 0x8ee201ac, 0x8ee20120, -0x24420001, 0xaee20120, 0x8ee20120, 0x8ee20168, -0x24420001, 0xaee20168, 0x80022e8, 0x8ee20168, -0x8f42025c, 0x26e40094, 0xaee20060, 0x8f420260, -0x27450200, 0x24060008, 0xc00249a, 0xaee20068, -0x8f820220, 0x30420008, 0x14400002, 0x24020001, -0x24020002, 0xaee20108, 0x8ee2011c, 0x24420001, -0xaee2011c, 0x80022e8, 0x8ee2011c, 0x3c040001, -0x248453ec, 0xafa00010, 0xafa00014, 0x8fa60020, -0x3c050003, 0xc002403, 0x34a5f00f, 0x93a20020, -0x3c030700, 0x34631000, 0x431025, 0xafa20018, -0x8ee20608, 0x8f430228, 0x24420001, 0x304900ff, -0x512300e2, 0xafa00010, 0x8ee20608, 0x210c0, -0x571021, 0x8fa30018, 0x8fa4001c, 0xac43060c, -0xac440610, 0x8f870120, 0x27623800, 0x24e80020, -0x102102b, 0x50400001, 0x27683000, 0x8f820128, -0x11020004, 0x0, 0x8f820124, 0x15020007, -0x1021, 0x8ee201a4, 0x3821, 0x24420001, -0xaee201a4, 0x800225d, 0x8ee201a4, 0x8ee40608, -0x420c0, 0x801821, 0x8ee40430, 0x8ee50434, -0xa32821, 0xa3302b, 0x822021, 0x862021, -0xace40000, 0xace50004, 0x8ee30608, 0x24020008, -0xa4e2000e, 0x2402000d, 0xace20018, 0xace9001c, -0x318c0, 0x2463060c, 0x2e31021, 0xace20008, -0x8ee204c4, 0xace20010, 0xaf880120, 0x92e24e20, -0x14400037, 0x24070001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c830000, 0x24020007, -0x1462001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x24030040, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee54e30, 0x24420001, -0x10430007, 0x0, 0x8ee24e34, 0x24420001, -0x10a20005, 0x0, 0x8002247, 0x0, -0x14a00005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400013, 0xac800000, 0x800225d, 0x0, -0x8ee24e30, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020007, 0xac820000, 0x24020001, 0xac820004, -0x54e0000c, 0xaee90608, 0x3c040001, 0x248453f4, -0xafa00010, 0xafa00014, 0x8ee60608, 0x8f470228, -0x3c050009, 0xc002403, 0x34a5f000, 0x80022e0, -0x0, 0x8f830120, 0x27623800, 0x24660020, -0xc2102b, 0x50400001, 0x27663000, 0x8f820128, -0x10c20004, 0x0, 0x8f820124, 0x14c20007, -0x0, 0x8ee201a4, 0x3821, 0x24420001, -0xaee201a4, 0x80022c4, 0x8ee201a4, 0x8ee20608, -0xac62001c, 0x8ee404a0, 0x8ee504a4, 0x2462001c, -0xac620008, 0x24020008, 0xa462000e, 0x24020011, -0xac620018, 0xac640000, 0xac650004, 0x8ee204c4, -0xac620010, 0xaf860120, 0x92e24e20, 0x14400037, -0x24070001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c830000, 0x24020012, 0x1462001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x24030040, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee54e30, 0x24420001, 0x10430007, -0x0, 0x8ee24e34, 0x24420001, 0x10a20005, -0x0, 0x80022ae, 0x0, 0x14a00005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400013, -0xac800000, 0x80022c4, 0x0, 0x8ee24e30, -0x24030040, 0x24420001, 0x50430003, 0x1021, -0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x24020012, -0xac820000, 0x24020001, 0xac820004, 0x14e0001b, -0x0, 0x3c040001, 0x248453fc, 0xafa00010, -0xafa00014, 0x8ee60608, 0x8f470228, 0x3c050009, -0xc002403, 0x34a5f001, 0x8ee201b0, 0x24420001, -0xaee201b0, 0x80022e0, 0x8ee201b0, 0x3c040001, -0x24845408, 0xafa00014, 0x8ee60608, 0x8f470228, -0x3c050009, 0xc002403, 0x34a5f005, 0x8ee201ac, -0x24420001, 0xaee201ac, 0x8ee201ac, 0x8ee20150, -0x24420001, 0xaee20150, 0x8ee20150, 0x8ee20160, -0x24420001, 0xaee20160, 0x8ee20160, 0x8f43022c, -0x8f42010c, 0x14620009, 0x24020002, 0xaf820064, -0x8f820064, 0x14400005, 0x0, 0x8f43022c, -0x8f42010c, 0x1462f875, 0x0, 0x8fbf0044, -0x8fb60040, 0x8fb5003c, 0x8fb40038, 0x8fb30034, -0x8fb20030, 0x8fb1002c, 0x8fb00028, 0x3e00008, -0x27bd0048, 0x27bdfff8, 0x2408ffff, 0x10a00014, -0x4821, 0x3c0aedb8, 0x354a8320, 0x90870000, -0x24840001, 0x3021, 0x1071026, 0x30420001, -0x10400002, 0x81842, 0x6a1826, 0x604021, -0x24c60001, 0x2cc20008, 0x1440fff7, 0x73842, -0x25290001, 0x125102b, 0x1440fff0, 0x0, -0x1001021, 0x3e00008, 0x27bd0008, 0x27bdffe8, -0x27642800, 0xafbf0010, 0xc002488, 0x24051000, -0x24020021, 0xaf800100, 0xaf800104, 0xaf800108, -0xaf800110, 0xaf800114, 0xaf800118, 0xaf800120, -0xaf800124, 0xaf800128, 0xaf800130, 0xaf800134, -0xaf800138, 0xaee04e28, 0xaee04e2c, 0xaee04e30, -0xaee04e34, 0xaf82011c, 0x8f420218, 0x30420040, -0x10400004, 0x0, 0x8f82011c, 0x34420004, -0xaf82011c, 0x8fbf0010, 0x3e00008, 0x27bd0018, -0x27bdffe0, 0xafbf0018, 0x8f820104, 0xafa20010, -0x8f820100, 0x3c050002, 0xafa20014, 0x8f8600b0, -0x8f87011c, 0x3c040001, 0x248454c0, 0xc002403, -0x34a5f000, 0x8f8300b0, 0x3c027f00, 0x621824, -0x3c020400, 0x10620029, 0x43102b, 0x14400008, -0x3c022000, 0x3c020100, 0x10620024, 0x3c020200, -0x10620011, 0x0, 0x8002374, 0x0, -0x10620008, 0x3c024000, 0x1462001c, 0x0, -0x8ee20190, 0x24420001, 0xaee20190, 0x8002374, -0x8ee20190, 0x8ee2018c, 0x24420001, 0xaee2018c, -0x8002374, 0x8ee2018c, 0x8f82011c, 0x34420002, -0xaf82011c, 0x8f830104, 0x8f8200b0, 0x34420001, -0xaf8200b0, 0xaf830104, 0x8f82011c, 0x2403fffd, -0x431024, 0xaf82011c, 0x8ee201a0, 0x24420001, -0xaee201a0, 0x8002377, 0x8ee201a0, 0x8f8200b0, -0x34420001, 0xaf8200b0, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x27bdffe0, 0xafbf001c, 0xafb00018, -0x8f820120, 0xafa20010, 0x8f820124, 0x3c050001, -0xafa20014, 0x8f8600a0, 0x8f87011c, 0x3c040001, -0x248454cc, 0xc002403, 0x34a5f000, 0x8f8300a0, -0x3c027f00, 0x621824, 0x3c020400, 0x10620053, -0x8021, 0x43102b, 0x14400008, 0x3c042000, -0x3c020100, 0x1062004d, 0x3c020200, 0x1062003a, -0x0, 0x80023e0, 0x0, 0x10640003, -0x3c024000, 0x14620045, 0x0, 0x8f8200a0, -0x441024, 0x10400006, 0x0, 0x8ee20194, -0x24420001, 0xaee20194, 0x80023a9, 0x8ee20194, -0x8ee20198, 0x24420001, 0xaee20198, 0x8ee20198, -0x8f82011c, 0x34420002, 0xaf82011c, 0x8f82011c, -0x30420200, 0x1040001b, 0x0, 0x8f8300a0, -0x8f840124, 0x8f8200ac, 0x14400007, 0x24020001, -0x3c020001, 0x3442f000, 0x621024, 0x50400001, -0x24100001, 0x24020001, 0x1200000d, 0xaf8200a0, -0x8f820124, 0x2442ffe0, 0xaf820124, 0x8f820124, -0x8f820124, 0x27633000, 0x43102b, 0x10400005, -0x276237e0, 0xaf820124, 0x80023ca, 0x0, -0xaf840124, 0x8f82011c, 0x2403fffd, 0x431024, -0x80023e3, 0xaf82011c, 0x8f82011c, 0x34420002, -0xaf82011c, 0x8f830124, 0x8f8200a0, 0x34420001, -0xaf8200a0, 0xaf830124, 0x8f82011c, 0x2403fffd, -0x431024, 0xaf82011c, 0x8ee2019c, 0x24420001, -0xaee2019c, 0x80023e3, 0x8ee2019c, 0x8f8200a0, -0x34420001, 0xaf8200a0, 0x8fbf001c, 0x8fb00018, -0x3e00008, 0x27bd0020, 0x0, 0x3c020001, -0x8c425c58, 0x27bdffe8, 0xafbf0014, 0x14400012, -0xafb00010, 0x3c100001, 0x26105dd0, 0x2002021, -0xc002488, 0x24052000, 0x26021fe0, 0x3c010001, -0xac225d94, 0x3c010001, 0xac225d90, 0xaf420250, -0x24022000, 0xaf500254, 0xaf420258, 0x24020001, -0x3c010001, 0xac225c58, 0x8fbf0014, 0x8fb00010, -0x3e00008, 0x27bd0018, 0x3c030001, 0x8c635d94, -0x8c820000, 0x8fa80010, 0x8fa90014, 0xac620000, -0x3c020001, 0x8c425d94, 0x8c830004, 0xac430004, -0xac450008, 0x8f840054, 0x2443ffe0, 0xac460010, -0xac470014, 0xac480018, 0xac49001c, 0x3c010001, -0xac235d94, 0xac44000c, 0x3c020001, 0x24425dd0, -0x62182b, 0x10600005, 0x0, 0x3c020001, -0x8c425d90, 0x3c010001, 0xac225d94, 0x3c030001, -0x8c635d94, 0x3c020001, 0x8c425c40, 0xac620000, -0x3c030001, 0x8c635d94, 0x3c020001, 0x8c425c40, -0xac620004, 0x3e00008, 0xaf430250, 0x3c030001, -0x8c635d94, 0x3c020001, 0x8c425c40, 0x27bdffd0, -0xafb40020, 0x8fb40040, 0xafb00010, 0x808021, -0xafb50024, 0x8fb50044, 0x8fa40048, 0xafb10014, -0xa08821, 0xafbf0028, 0xafb3001c, 0xafb20018, -0xac620000, 0x3c050001, 0x8ca55d94, 0x3c020001, -0x8c425c40, 0xc09021, 0xe09821, 0x10800006, -0xaca20004, 0x24a50008, 0xc002490, 0x24060018, -0x800244e, 0x0, 0x24a40008, 0xc002488, -0x24050018, 0x3c020001, 0x8c425d94, 0x3c050001, -0x24a55dd0, 0x2442ffe0, 0x3c010001, 0xac225d94, -0x45102b, 0x10400005, 0x0, 0x3c020001, -0x8c425d90, 0x3c010001, 0xac225d94, 0x3c030001, -0x8c635d94, 0x8e020000, 0xac620000, 0x3c030001, -0x8c635d94, 0x8e020004, 0xac620004, 0xac710008, -0x8f840054, 0x2462ffe0, 0x3c010001, 0xac225d94, -0x45102b, 0xac720010, 0xac730014, 0xac740018, -0xac75001c, 0x10400005, 0xac64000c, 0x3c020001, -0x8c425d90, 0x3c010001, 0xac225d94, 0x3c030001, -0x8c635d94, 0x3c020001, 0x8c425c40, 0xac620000, -0x3c030001, 0x8c635d94, 0x3c020001, 0x8c425c40, -0xac620004, 0xaf430250, 0x8fbf0028, 0x8fb50024, -0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, -0x8fb00010, 0x3e00008, 0x27bd0030, 0x10a00005, -0x0, 0xac800000, 0x24a5fffc, 0x14a0fffd, -0x24840004, 0x3e00008, 0x0, 0x10c00007, -0x0, 0x8c820000, 0x24840004, 0x24c6fffc, -0xaca20000, 0x14c0fffb, 0x24a50004, 0x3e00008, -0x0, 0x10c00007, 0x0, 0x8ca20000, -0x24a50004, 0x24c6fffc, 0xac820000, 0x14c0fffb, -0x24840004, 0x3e00008, 0x0, 0x3e00008, -0x0, 0x27bdffd8, 0xafbf0020, 0x8ee304e4, -0x8ee204e0, 0x10620436, 0x0, 0x8ee204e4, -0x8ee304fc, 0x21100, 0x626021, 0x95870008, -0x8d8a0000, 0x8d8b0004, 0x958d000a, 0x8ee2725c, -0x8ee3726c, 0x30e4ffff, 0x441021, 0x62182b, -0x10600015, 0x31a20004, 0x8f8200d8, 0x8ee37258, -0x431023, 0xaee2726c, 0x8ee2726c, 0x1c400003, -0x3c030001, 0x431021, 0xaee2726c, 0x8ee2725c, -0x8ee3726c, 0x441021, 0x62182b, 0x10600006, -0x31a20004, 0x8ee201b8, 0x24420001, 0xaee201b8, -0x80028e1, 0x8ee201b8, 0x10400240, 0x31a20200, -0x1040014d, 0x4821, 0x96e2045a, 0x30420010, -0x10400149, 0x0, 0x8f840100, 0x27623000, -0x24850020, 0xa2102b, 0x50400001, 0x27652800, -0x8f820108, 0x10a20004, 0x0, 0x8f820104, -0x14a20006, 0x2402000c, 0x8ee201a8, 0x24420001, -0xaee201a8, 0x800252c, 0x8ee201a8, 0xac8a0000, -0xac8b0004, 0x8ee37264, 0x24060005, 0xa482000e, -0xac860018, 0xac830008, 0x8ee204e4, 0xac82001c, -0x8ee204c8, 0xac820010, 0xaf850100, 0x92e204ec, -0x14400036, 0x24090001, 0x8ee24e28, 0x210c0, -0x24424e38, 0x2e22021, 0x8c820000, 0x1446001f, -0x0, 0x8ee34e28, 0x8ee24e2c, 0x1062001b, -0x24030040, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e2c, 0x8ee54e28, 0x24420001, 0x10430007, -0x0, 0x8ee24e2c, 0x24420001, 0x10a20005, -0x0, 0x8002516, 0x0, 0x14a00005, -0x0, 0x8f820108, 0x24420020, 0xaf820108, -0x8f820108, 0x8c820004, 0x2c420011, 0x50400013, -0xac800000, 0x800252c, 0x0, 0x8ee24e28, -0x24030040, 0x24420001, 0x50430003, 0x1021, -0x8ee24e28, 0x24420001, 0xaee24e28, 0x8ee24e28, -0x210c0, 0x24424e38, 0x2e22021, 0x24020005, -0xac820000, 0x24020001, 0xac820004, 0x1520000a, -0x3c040001, 0xafab0010, 0x8ee27264, 0x3c040001, -0x24845730, 0x3c050004, 0xafa20014, 0x8ee604e4, -0x80028be, 0x34a5f114, 0x8ee27264, 0x34843800, -0x3641821, 0x24420010, 0x43102b, 0x14400073, -0x0, 0x8ee27264, 0x24480010, 0x3641021, -0x102102b, 0x14400002, 0x3c02ffff, 0x1024021, -0x8f850100, 0x27623000, 0x24a60020, 0xc2102b, -0x50400001, 0x27662800, 0x8f820108, 0x10c20004, -0x0, 0x8f820104, 0x14c20007, 0x2563000c, -0x8ee201a8, 0x4821, 0x24420001, 0xaee201a8, -0x80025a0, 0x8ee201a8, 0x2c64000c, 0x1441021, -0xaca20000, 0xaca30004, 0x24e2fff4, 0xa4a2000e, -0x24020006, 0xaca80008, 0xaca20018, 0x8ee204e4, -0xaca2001c, 0x8ee204c8, 0x3c030002, 0x431025, -0xaca20010, 0xaf860100, 0x92e204ec, 0x14400037, -0x24090001, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e22021, 0x8c830000, 0x24020005, 0x1462001f, -0x0, 0x8ee34e28, 0x8ee24e2c, 0x1062001b, -0x24030040, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e2c, 0x8ee54e28, 0x24420001, 0x10430007, -0x0, 0x8ee24e2c, 0x24420001, 0x10a20005, -0x0, 0x800258a, 0x0, 0x14a00005, -0x0, 0x8f820108, 0x24420020, 0xaf820108, -0x8f820108, 0x8c820004, 0x2c420011, 0x50400013, -0xac800000, 0x80025a0, 0x0, 0x8ee24e28, -0x24030040, 0x24420001, 0x50430003, 0x1021, -0x8ee24e28, 0x24420001, 0xaee24e28, 0x8ee24e28, -0x210c0, 0x24424e38, 0x2e22021, 0x24020005, -0xac820000, 0x24020001, 0xac820004, 0x1520000a, -0x2508fffc, 0xafab0010, 0x8ee27264, 0x3c040001, -0x24845730, 0x3c050004, 0xafa20014, 0x8ee604e4, -0x80028be, 0x34a5f125, 0x34028100, 0xa5020000, -0x9582000e, 0x800261d, 0xa5020002, 0x8f850100, -0x27623000, 0x24a60020, 0xc2102b, 0x50400001, -0x27662800, 0x8f820108, 0x10c20004, 0x0, -0x8f820104, 0x14c20007, 0x2563000c, 0x8ee201a8, -0x4821, 0x24420001, 0xaee201a8, 0x800260d, -0x8ee201a8, 0x2c64000c, 0x1441021, 0xaca20000, -0xaca30004, 0x8ee37264, 0x24e2fff4, 0xa4a2000e, -0x24020006, 0xaca20018, 0x24630010, 0xaca30008, -0x8ee204e4, 0xaca2001c, 0x8ee204c8, 0x3c030002, -0x431025, 0xaca20010, 0xaf860100, 0x92e204ec, -0x14400037, 0x24090001, 0x8ee24e28, 0x210c0, -0x24424e38, 0x2e22021, 0x8c830000, 0x24020005, -0x1462001f, 0x0, 0x8ee34e28, 0x8ee24e2c, -0x1062001b, 0x24030040, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e2c, 0x8ee54e28, 0x24420001, -0x10430007, 0x0, 0x8ee24e2c, 0x24420001, -0x10a20005, 0x0, 0x80025f7, 0x0, -0x14a00005, 0x0, 0x8f820108, 0x24420020, -0xaf820108, 0x8f820108, 0x8c820004, 0x2c420011, -0x50400013, 0xac800000, 0x800260d, 0x0, -0x8ee24e28, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e28, 0x24420001, 0xaee24e28, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x24020005, 0xac820000, 0x24020001, 0xac820004, -0x1520000a, 0x34028100, 0xafab0010, 0x8ee27264, -0x3c040001, 0x24845730, 0x3c050004, 0xafa20014, -0x8ee604e4, 0x80028be, 0x34a5f015, 0x8ee37264, -0xa462000c, 0x8ee37264, 0x9582000e, 0xa462000e, -0x8002681, 0x24e70004, 0x8f840100, 0x27623000, -0x24850020, 0xa2102b, 0x50400001, 0x27652800, -0x8f820108, 0x10a20004, 0x0, 0x8f820104, -0x14a20007, 0x24020006, 0x8ee201a8, 0x4821, -0x24420001, 0xaee201a8, 0x8002677, 0x8ee201a8, -0xac8a0000, 0xac8b0004, 0x8ee37264, 0xa487000e, -0xac820018, 0xac830008, 0x8ee204e4, 0xac82001c, -0x8ee204c8, 0x3c030002, 0x431025, 0xac820010, -0xaf850100, 0x92e204ec, 0x14400037, 0x24090001, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x8c830000, 0x24020005, 0x1462001f, 0x0, -0x8ee34e28, 0x8ee24e2c, 0x1062001b, 0x24030040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e2c, -0x8ee54e28, 0x24420001, 0x10430007, 0x0, -0x8ee24e2c, 0x24420001, 0x10a20005, 0x0, -0x8002661, 0x0, 0x14a00005, 0x0, -0x8f820108, 0x24420020, 0xaf820108, 0x8f820108, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x8002677, 0x0, 0x8ee24e28, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e28, -0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, -0x24424e38, 0x2e22021, 0x24020005, 0xac820000, -0x24020001, 0xac820004, 0x15200009, 0x3c050004, -0xafab0010, 0x8ee27264, 0x3c040001, 0x24845730, -0xafa20014, 0x8ee604e4, 0x80028be, 0x34a5f004, -0x8ee2725c, 0x30e7ffff, 0x471021, 0xaee2725c, -0x8ee204e4, 0x8ee304fc, 0x8ee47258, 0x21100, -0x431021, 0xac44000c, 0x8ee27258, 0xafa20018, -0x8ee3725c, 0xafa3001c, 0x8ee2725c, 0x2c42003c, -0x10400004, 0x24620001, 0x2403fffe, 0x431024, -0xafa2001c, 0x8ee27264, 0x3c060001, 0x34c63800, -0x8ee3725c, 0x2405fff8, 0x471021, 0x24420007, -0x451024, 0x24630007, 0xaee27258, 0x8ee2726c, -0x8ee47258, 0x651824, 0x431023, 0xaee2726c, -0x3661021, 0x82202b, 0x14800004, 0x3c03ffff, -0x8ee27258, 0x431021, 0xaee27258, 0x8ee27258, -0xaee27264, 0x8f8200f0, 0x24470008, 0x27621800, -0xe2102b, 0x50400001, 0x27671000, 0x8f8200f4, -0x14e20007, 0x0, 0x8ee201b4, 0x4821, -0x24420001, 0xaee201b4, 0x80026c4, 0x8ee201b4, -0x8f8200f0, 0x24090001, 0x8fa30018, 0x8fa4001c, -0xac430000, 0xac440004, 0xaf8700f0, 0x15200012, -0xd1142, 0x8f8200f0, 0xafa20010, 0x8f8200f4, -0x3c040001, 0x2484573c, 0xafa20014, 0x8fa60018, -0x8fa7001c, 0x3c050004, 0xc002403, 0x34a5f005, -0x8ee20088, 0x24420001, 0xaee20088, 0x8ee20088, -0x80028d3, 0xaee0725c, 0x30430003, 0x24020002, -0x10620016, 0x28620003, 0x10400005, 0x24020001, -0x10620008, 0x0, 0x8002703, 0x0, -0x24020003, 0x10620017, 0x0, 0x8002703, -0x0, 0x8ee200e8, 0x8ee300ec, 0x24630001, -0x2c640001, 0x441021, 0xaee200e8, 0xaee300ec, -0x8ee200e8, 0x8002703, 0x8ee300ec, 0x8ee200f0, -0x8ee300f4, 0x24630001, 0x2c640001, 0x441021, -0xaee200f0, 0xaee300f4, 0x8ee200f0, 0x8002703, -0x8ee300f4, 0x8ee200f8, 0x8ee300fc, 0x24630001, -0x2c640001, 0x441021, 0xaee200f8, 0xaee300fc, -0x8ee200f8, 0x8ee300fc, 0x8ee2725c, 0x8ee400e0, -0x8ee500e4, 0x401821, 0x1021, 0xa32821, -0xa3302b, 0x822021, 0x862021, 0xaee400e0, -0xaee500e4, 0x80028d3, 0xaee0725c, 0x30e2ffff, -0x104001c1, 0x31a20200, 0x1040014d, 0x4821, -0x96e2045a, 0x30420010, 0x10400149, 0x0, -0x8f840100, 0x27623000, 0x24850020, 0xa2102b, -0x50400001, 0x27652800, 0x8f820108, 0x10a20004, -0x0, 0x8f820104, 0x14a20006, 0x2402000c, -0x8ee201a8, 0x24420001, 0xaee201a8, 0x800276e, -0x8ee201a8, 0xac8a0000, 0xac8b0004, 0x8ee37264, -0x24060005, 0xa482000e, 0xac860018, 0xac830008, -0x8ee204e4, 0xac82001c, 0x8ee204c8, 0xac820010, -0xaf850100, 0x92e204ec, 0x14400036, 0x24090001, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x8c820000, 0x1446001f, 0x0, 0x8ee34e28, -0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, -0x24420001, 0x10430007, 0x0, 0x8ee24e2c, -0x24420001, 0x10a20005, 0x0, 0x8002758, -0x0, 0x14a00005, 0x0, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x800276e, -0x0, 0x8ee24e28, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e28, 0x24420001, -0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e22021, 0x24020005, 0xac820000, 0x24020001, -0xac820004, 0x1520000a, 0x3c040001, 0xafab0010, -0x8ee27264, 0x3c040001, 0x24845730, 0x3c050004, -0xafa20014, 0x8ee604e4, 0x80028be, 0x34a5f014, -0x8ee27264, 0x34843800, 0x3641821, 0x24420010, -0x43102b, 0x14400073, 0x0, 0x8ee27264, -0x24480010, 0x3641021, 0x102102b, 0x14400002, -0x3c02ffff, 0x1024021, 0x8f850100, 0x27623000, -0x24a60020, 0xc2102b, 0x50400001, 0x27662800, -0x8f820108, 0x10c20004, 0x0, 0x8f820104, -0x14c20007, 0x2563000c, 0x8ee201a8, 0x4821, -0x24420001, 0xaee201a8, 0x80027e2, 0x8ee201a8, -0x2c64000c, 0x1441021, 0xaca20000, 0xaca30004, -0x24e2fff4, 0xa4a2000e, 0x24020006, 0xaca80008, -0xaca20018, 0x8ee204e4, 0xaca2001c, 0x8ee204c8, -0x3c030002, 0x431025, 0xaca20010, 0xaf860100, -0x92e204ec, 0x14400037, 0x24090001, 0x8ee24e28, -0x210c0, 0x24424e38, 0x2e22021, 0x8c830000, -0x24020005, 0x1462001f, 0x0, 0x8ee34e28, -0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, -0x24420001, 0x10430007, 0x0, 0x8ee24e2c, -0x24420001, 0x10a20005, 0x0, 0x80027cc, -0x0, 0x14a00005, 0x0, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x80027e2, -0x0, 0x8ee24e28, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e28, 0x24420001, -0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e22021, 0x24020005, 0xac820000, 0x24020001, -0xac820004, 0x1520000a, 0x2508fffc, 0xafab0010, -0x8ee27264, 0x3c040001, 0x24845730, 0x3c050004, -0xafa20014, 0x8ee604e4, 0x80028be, 0x34a5f015, -0x34028100, 0xa5020000, 0x9582000e, 0x800285f, -0xa5020002, 0x8f850100, 0x27623000, 0x24a60020, -0xc2102b, 0x50400001, 0x27662800, 0x8f820108, -0x10c20004, 0x0, 0x8f820104, 0x14c20007, -0x2563000c, 0x8ee201a8, 0x4821, 0x24420001, -0xaee201a8, 0x800284f, 0x8ee201a8, 0x2c64000c, -0x1441021, 0xaca20000, 0xaca30004, 0x8ee37264, -0x24e2fff4, 0xa4a2000e, 0x24020006, 0xaca20018, -0x24630010, 0xaca30008, 0x8ee204e4, 0xaca2001c, -0x8ee204c8, 0x3c030002, 0x431025, 0xaca20010, -0xaf860100, 0x92e204ec, 0x14400037, 0x24090001, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x8c830000, 0x24020005, 0x1462001f, 0x0, -0x8ee34e28, 0x8ee24e2c, 0x1062001b, 0x24030040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e2c, -0x8ee54e28, 0x24420001, 0x10430007, 0x0, -0x8ee24e2c, 0x24420001, 0x10a20005, 0x0, -0x8002839, 0x0, 0x14a00005, 0x0, -0x8f820108, 0x24420020, 0xaf820108, 0x8f820108, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x800284f, 0x0, 0x8ee24e28, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e28, -0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, -0x24424e38, 0x2e22021, 0x24020005, 0xac820000, -0x24020001, 0xac820004, 0x1520000a, 0x34028100, -0xafab0010, 0x8ee27264, 0x3c040001, 0x24845730, -0x3c050004, 0xafa20014, 0x8ee604e4, 0x80028be, -0x34a5f016, 0x8ee37264, 0xa462000c, 0x8ee37264, -0x9582000e, 0xa462000e, 0x80028c2, 0x24e70004, -0x8f830100, 0x27623000, 0x24640020, 0x82102b, -0x50400001, 0x27642800, 0x8f820108, 0x10820004, -0x0, 0x8f820104, 0x14820007, 0x24050005, -0x8ee201a8, 0x4821, 0x24420001, 0xaee201a8, -0x80028b6, 0x8ee201a8, 0xac6a0000, 0xac6b0004, -0x8ee27264, 0xa467000e, 0xac650018, 0xac620008, -0x8ee204e4, 0xac62001c, 0x8ee204c8, 0xac620010, -0xaf840100, 0x92e204ec, 0x14400036, 0x24090001, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x8c820000, 0x1445001f, 0x0, 0x8ee34e28, -0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, -0x24420001, 0x10430007, 0x0, 0x8ee24e2c, -0x24420001, 0x10a20005, 0x0, 0x80028a0, -0x0, 0x14a00005, 0x0, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x80028b6, -0x0, 0x8ee24e28, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e28, 0x24420001, -0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e22021, 0x24020005, 0xac820000, 0x24020001, -0xac820004, 0x1520000b, 0x3c050004, 0x3c040001, -0x24845748, 0xafab0010, 0xafa00014, 0x8ee604e4, -0x34a5f017, 0xc002403, 0x30e7ffff, 0x80028e1, -0x0, 0x8ee27264, 0x3c050001, 0x30e4ffff, -0x441021, 0xaee27264, 0x8ee2725c, 0x8ee37264, -0x34a53800, 0x441021, 0xaee2725c, 0x3651021, -0x62182b, 0x14600004, 0x3c03ffff, 0x8ee27264, -0x431021, 0xaee27264, 0x8ee304e4, 0x96e20458, -0x24630001, 0x2442ffff, 0x621824, 0xaee304e4, -0x8ee304e4, 0x8ee204e0, 0x14620005, 0x0, -0x8f820060, 0x2403fff7, 0x431024, 0xaf820060, -0x8fbf0020, 0x3e00008, 0x27bd0028, 0x27bdffe0, -0xafbf0018, 0x8ee304e8, 0x8ee204e0, 0x10620189, -0x0, 0x8ee204e8, 0x8ee304fc, 0x21100, -0x621821, 0x94670008, 0x92e204ed, 0x8c680000, -0x8c690004, 0x10400023, 0x946a000a, 0x8ee204c8, -0x34460400, 0x31420200, 0x1040001f, 0x0, -0x96e2045a, 0x30420010, 0x1040001b, 0x3c028000, -0x3c010001, 0x370821, 0xac2283d8, 0x8ee27264, -0x9464000e, 0x3c050001, 0x34a53800, 0x24420004, -0xaee27264, 0x8ee37264, 0x42400, 0x3651021, -0x3c010001, 0x370821, 0xac2483dc, 0x62182b, -0x14600005, 0x24e70004, 0x8ee27264, 0x3c03ffff, -0x431021, 0xaee27264, 0x8ee27264, 0x8002917, -0xaee27258, 0x8ee604c8, 0x8ee2726c, 0x30e4ffff, -0x44102a, 0x10400015, 0x0, 0x8f8200d8, -0x8ee37258, 0x431023, 0xaee2726c, 0x8ee2726c, -0x1c400007, 0x44102a, 0x8ee2726c, 0x3c030001, -0x431021, 0xaee2726c, 0x8ee2726c, 0x44102a, -0x10400006, 0x0, 0x8ee201b8, 0x24420001, -0xaee201b8, 0x8002a72, 0x8ee201b8, 0x3c020001, -0x571021, 0x8c4283d8, 0x54400001, 0x24e7fffc, -0x31420004, 0x104000b9, 0x30e2ffff, 0x3c020001, -0x571021, 0x8c4283d8, 0x1040002f, 0x5021, -0x8f840100, 0x27623000, 0x24850020, 0xa2102b, -0x50400001, 0x27652800, 0x8f820108, 0x10a20032, -0x0, 0x8f820104, 0x10a2002f, 0x24020015, -0xac880000, 0xac890004, 0x8ee37264, 0xa487000e, -0xac820018, 0xac830008, 0x8ee204e8, 0x3c030001, -0x771821, 0x8c6383dc, 0xac860010, 0x431025, -0xac82001c, 0xaf850100, 0x92e204ec, 0x14400066, -0x240a0001, 0x8ee24e28, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e28, 0x24420001, -0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e21821, 0x24020015, 0xac620000, 0x24020001, -0x80029bf, 0xac620004, 0x8f840100, 0x27623000, -0x24850020, 0xa2102b, 0x50400001, 0x27652800, -0x8f820108, 0x10a20004, 0x0, 0x8f820104, -0x14a20006, 0x24020006, 0x8ee201a8, 0x24420001, -0xaee201a8, 0x80029bf, 0x8ee201a8, 0xac880000, -0xac890004, 0x8ee37264, 0xa487000e, 0xac820018, -0xac830008, 0x8ee204e8, 0xac860010, 0xac82001c, -0xaf850100, 0x92e204ec, 0x14400037, 0x240a0001, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x8c830000, 0x24020005, 0x1462001f, 0x0, -0x8ee34e28, 0x8ee24e2c, 0x1062001b, 0x24030040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e2c, -0x8ee54e28, 0x24420001, 0x10430007, 0x0, -0x8ee24e2c, 0x24420001, 0x10a20005, 0x0, -0x80029a9, 0x0, 0x14a00005, 0x0, -0x8f820108, 0x24420020, 0xaf820108, 0x8f820108, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x80029bf, 0x0, 0x8ee24e28, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e28, -0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, -0x24424e38, 0x2e22021, 0x24020005, 0xac820000, -0x24020001, 0xac820004, 0x1540000a, 0x24020001, -0xafa90010, 0x8ee27264, 0x3c040001, 0x24845730, -0x3c050004, 0xafa20014, 0x8ee604e4, 0x8002a4f, -0x34a5f204, 0xa2e204ed, 0x8ee204e8, 0x8ee304fc, -0x8ee47258, 0x3c060001, 0x34c63800, 0x3c010001, -0x370821, 0xac2083d8, 0x3c010001, 0x370821, -0xac2083dc, 0x21100, 0x431021, 0xac44000c, -0x8ee27264, 0x2405fff8, 0x30e3ffff, 0x431021, -0x24420007, 0x451024, 0x24630007, 0xaee27258, -0x8ee2726c, 0x8ee47258, 0x651824, 0x431023, -0xaee2726c, 0x3661021, 0x82202b, 0x14800004, -0x3c03ffff, 0x8ee27258, 0x431021, 0xaee27258, -0x8ee27258, 0x8002a64, 0xaee27264, 0x10400073, -0x0, 0x8f830100, 0x27623000, 0x24640020, -0x82102b, 0x14400002, 0x5021, 0x27642800, -0x8f820108, 0x10820004, 0x0, 0x8f820104, -0x14820006, 0x24050005, 0x8ee201a8, 0x24420001, -0xaee201a8, 0x8002a46, 0x8ee201a8, 0xac680000, -0xac690004, 0x8ee27264, 0xa467000e, 0xac650018, -0xac620008, 0x8ee204e8, 0xac660010, 0xac62001c, -0xaf840100, 0x92e204ec, 0x14400036, 0x240a0001, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e22021, -0x8c820000, 0x1445001f, 0x0, 0x8ee34e28, -0x8ee24e2c, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e2c, 0x8ee54e28, -0x24420001, 0x10430007, 0x0, 0x8ee24e2c, -0x24420001, 0x10a20005, 0x0, 0x8002a30, -0x0, 0x14a00005, 0x0, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x8002a46, -0x0, 0x8ee24e28, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e28, 0x24420001, -0xaee24e28, 0x8ee24e28, 0x210c0, 0x24424e38, -0x2e22021, 0x24020005, 0xac820000, 0x24020001, -0xac820004, 0x1540000c, 0x30e5ffff, 0x3c040001, -0x24845748, 0x3c050004, 0xafa90010, 0xafa00014, -0x8ee604e4, 0x34a5f237, 0xc002403, 0x30e7ffff, -0x8002a72, 0x0, 0x8ee27264, 0x451021, -0xaee27264, 0x8ee2726c, 0x8ee37264, 0x3c040001, -0x34843800, 0xa2e004ed, 0x451023, 0xaee2726c, -0x3641021, 0x62182b, 0x14600004, 0x3c03ffff, -0x8ee27264, 0x431021, 0xaee27264, 0x8ee304e8, -0x96e20458, 0x24630001, 0x2442ffff, 0x621824, -0xaee304e8, 0x8ee304e8, 0x8ee204e0, 0x14620005, -0x0, 0x8f820060, 0x2403fff7, 0x431024, -0xaf820060, 0x8fbf0018, 0x3e00008, 0x27bd0020, -0x27bdffe0, 0xafbf001c, 0xafb00018, 0x8f820100, -0x8ee34e2c, 0x8f820104, 0x8f850108, 0x24020040, -0x24630001, 0x50620003, 0x1021, 0x8ee24e2c, -0x24420001, 0xaee24e2c, 0x8ee24e2c, 0x8ee34e2c, -0x210c0, 0x24424e38, 0x2e22021, 0x8ee24e28, -0x8c870004, 0x14620007, 0xa03021, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x8002aa2, -0xac800000, 0x8ee24e2c, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e2c, 0x24420001, -0x210c0, 0x24424e38, 0x2e22021, 0x8c820004, -0x8f830108, 0x21140, 0x621821, 0xaf830108, -0xac800000, 0x8cc20018, 0x2443fffe, 0x2c620013, -0x104000c1, 0x31080, 0x3c010001, 0x220821, -0x8c225770, 0x400008, 0x0, 0x8ee204f0, -0x471021, 0xaee204f0, 0x8ee204f0, 0x8f43023c, -0x43102b, 0x144000be, 0x0, 0x8ee304e4, -0x8ee204f8, 0x506200ba, 0xa2e004f4, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x8021, 0x24420001, 0xaee201a4, 0x8002b12, -0x8ee201a4, 0x8ee204e4, 0xac62001c, 0x8ee404b0, -0x8ee504b4, 0x2462001c, 0xac620008, 0x24020008, -0xa462000e, 0x24020011, 0xac620018, 0xac640000, -0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, -0x92e24e20, 0x14400037, 0x24100001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020012, 0x1462001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x24030040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee54e30, -0x24420001, 0x10430007, 0x0, 0x8ee24e34, -0x24420001, 0x10a20005, 0x0, 0x8002afc, -0x0, 0x14a00005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x8002b12, -0x0, 0x8ee24e30, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x24020012, 0xac820000, 0x24020001, -0xac820004, 0x5600000b, 0x24100001, 0x8ee204e4, -0x3c040001, 0x24845754, 0xafa00014, 0xafa20010, -0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, -0x34a5f006, 0x16000003, 0x24020001, 0x8002b71, -0xa2e204f4, 0x8ee20170, 0x24420001, 0xaee20170, -0x8ee20170, 0x8ee204e4, 0xa2e004f4, 0xaee004f0, -0xaee204f8, 0x8f42023c, 0x50400045, 0xaee07274, -0x8ee20184, 0x24420001, 0xaee20184, 0x8ee20184, -0x8002b71, 0xaee07274, 0x8ee20504, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee20504, -0x24420001, 0xaee20504, 0x8ee20504, 0x8cc30018, -0x21080, 0x571021, 0x8c440508, 0x24020003, -0x1462000f, 0x0, 0x3c020001, 0x571021, -0x904283b1, 0x10400014, 0x0, 0x8ee201d0, -0x8ee35240, 0x441021, 0xaee201d0, 0x8ee201d8, -0x641821, 0x306300ff, 0x8002b59, 0xaee35240, -0x8ee201cc, 0x8ee30e10, 0x441021, 0xaee201cc, -0x8ee201d8, 0x641821, 0x306301ff, 0xaee30e10, -0x441021, 0xaee201d8, 0x8ee20000, 0x34420040, -0x8002b71, 0xaee20000, 0x8ee2014c, 0x3c010001, -0x370821, 0xa02083e0, 0x24420001, 0xaee2014c, -0x8002b71, 0x8ee2014c, 0x94c7000e, 0x8cc2001c, -0x3c040001, 0x24845760, 0xafa60014, 0xafa20010, -0x8cc60018, 0x3c050008, 0xc002403, 0x34a50910, -0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, -0x27bdff98, 0xafbf0060, 0xafbe005c, 0xafb60058, -0xafb50054, 0xafb40050, 0xafb3004c, 0xafb20048, -0xafb10044, 0xafb00040, 0x8f830108, 0x8f820104, -0xafa00024, 0x106203e7, 0xafa0002c, 0x3c1e0001, -0x37de3800, 0x3c0bffff, 0x8f930108, 0x8e620018, -0x8f830104, 0x2443fffe, 0x2c620014, 0x104003cf, -0x31080, 0x3c010001, 0x220821, 0x8c2257c0, -0x400008, 0x0, 0x9663000e, 0x8ee2725c, -0x8ee404f0, 0x431021, 0xaee2725c, 0x8e63001c, -0x96e20458, 0x24840001, 0xaee404f0, 0x24630001, -0x2442ffff, 0x621824, 0xaee304e4, 0x8f42023c, -0x82202b, 0x148003b9, 0x0, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x8021, 0x24420001, 0xaee201a4, 0x8002bfe, -0x8ee201a4, 0x8ee204e4, 0xac62001c, 0x8ee404b0, -0x8ee504b4, 0x2462001c, 0xac620008, 0x24020008, -0xa462000e, 0x24020011, 0xac620018, 0xac640000, -0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, -0x92e24e20, 0x14400037, 0x24100001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020012, 0x1462001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x240c0040, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x104c0007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x8002be8, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400013, 0xac800000, 0x8002bfe, -0x0, 0x8ee24e30, 0x240c0040, 0x24420001, -0x504c0003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x24020012, 0x240c0001, 0xac820000, -0xac8c0004, 0x5600000d, 0x24100001, 0x8ee204e4, -0x3c040001, 0x24845754, 0xafa00014, 0xafa20010, -0x8ee60608, 0x8f470228, 0x3c050009, 0x34a5f006, -0xc002403, 0xafab0038, 0x8fab0038, 0x1200030a, -0x240c0001, 0x8002f19, 0x0, 0x966c001c, -0xafac002c, 0x9662001e, 0x3c0c8000, 0xafac0024, -0xae62001c, 0x8e75001c, 0x8ee204fc, 0x8ee404fc, -0x151900, 0x621021, 0x8c52000c, 0x92e27b98, -0x641821, 0x9476000a, 0x14400003, 0x32c20002, -0xaef27ba4, 0xaef57b9c, 0x1040004b, 0x8021, -0x96e2045a, 0x30420002, 0x10400047, 0x0, -0x8e63001c, 0x8ee204fc, 0x32100, 0x821021, -0x8c42000c, 0x37e1821, 0x24420022, 0x43102b, -0x1440000a, 0x24050014, 0x8ee204fc, 0x821021, -0x8c44000c, 0xafab0038, 0xc002f75, 0x2484000e, -0x8fab0038, 0x8002c52, 0x3050ffff, 0x8ee204fc, -0x821021, 0x8c42000c, 0x9450000e, 0x94430010, -0x94440012, 0x94450014, 0x2038021, 0x2048021, -0x2058021, 0x94430016, 0x94440018, 0x9445001a, -0x2038021, 0x2048021, 0x2058021, 0x9443001c, -0x9444001e, 0x94420020, 0x2038021, 0x2048021, -0x2028021, 0x101c02, 0x3202ffff, 0x628021, -0x8e63001c, 0x8ee204fc, 0x102402, 0x32900, -0xa21021, 0x8c43000c, 0x3202ffff, 0x828021, -0x37e1021, 0x24630018, 0x62182b, 0x14600009, -0x0, 0x8ee204fc, 0xa21021, 0x8c43000c, -0x101027, 0x3c01ffff, 0x230821, 0x8002c6f, -0xa4220018, 0x8ee204fc, 0xa21021, 0x8c43000c, -0x101027, 0xa4620018, 0x96e2045a, 0x8821, -0x30420008, 0x14400063, 0xa021, 0x8e63001c, -0x8ee204fc, 0x33100, 0xc21021, 0x8c42000c, -0x37e1821, 0x24420022, 0x43102b, 0x14400035, -0x0, 0x8ee204fc, 0xc21021, 0x8c42000c, -0x24470010, 0x37e1021, 0xe2102b, 0x50400001, -0xeb3821, 0x8ee204fc, 0x94f10000, 0xc21021, -0x8c42000c, 0x24470016, 0x37e1021, 0xe2102b, -0x14400002, 0x2634ffec, 0xeb3821, 0x8ee204fc, -0x90e30001, 0xc21021, 0x8c42000c, 0x2447001a, -0x37e1021, 0xe2102b, 0x14400002, 0x2838821, -0xeb3821, 0x94e20000, 0x24e70002, 0x2228821, -0x37e1021, 0xe2102b, 0x50400001, 0xeb3821, -0x94e20000, 0x24e70002, 0x2228821, 0x37e1021, -0xe2102b, 0x50400001, 0xeb3821, 0x94e20000, -0x24e70002, 0x2228821, 0x37e1021, 0xe2102b, -0x50400001, 0xeb3821, 0x94e20000, 0x8002cd0, -0x2228821, 0x8ee204fc, 0xc21021, 0x8c43000c, -0x8ee204fc, 0x94710010, 0x8ee304fc, 0xc21021, -0x8c44000c, 0xc31821, 0x8c62000c, 0x2634ffec, -0x90840017, 0x8ee304fc, 0x9442001a, 0x2848821, -0xc31821, 0x8c65000c, 0x8ee304fc, 0x2228821, -0x8ee204fc, 0xc31821, 0xc21021, 0x8c44000c, -0x8c62000c, 0x94a3001c, 0x9484001e, 0x94420020, -0x2238821, 0x2248821, 0x2228821, 0x111c02, -0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, -0x628821, 0x32c20001, 0x104000b2, 0x0, -0x96e2045a, 0x30420001, 0x104000ae, 0x32c20080, -0x10400008, 0x0, 0x92e27b98, 0x14400005, -0x0, 0x240c0001, 0xa2ec7b98, 0xaef57b9c, -0xaef27ba4, 0x8ee304fc, 0x151100, 0x431021, -0x8c47000c, 0x37e1821, 0x24e2000e, 0x43102b, -0x14400008, 0xe02021, 0x2405000e, 0xc002f75, -0xafab0038, 0x3042ffff, 0x8fab0038, 0x8002d09, -0x2028021, 0x94e60000, 0x24e70002, 0x94e50000, -0x24e70002, 0x94e30000, 0x24e70002, 0x94e20000, -0x24e70002, 0x94e40000, 0x24e70002, 0x2068021, -0x2058021, 0x2038021, 0x2028021, 0x94e20000, -0x94e30002, 0x2048021, 0x2028021, 0x2038021, -0x101c02, 0x3202ffff, 0x628021, 0x101c02, -0x3202ffff, 0x8ee47b9c, 0x628021, 0x14950004, -0x3205ffff, 0x96620016, 0x8002d17, 0x512021, -0x96620016, 0x542021, 0x41402, 0x3083ffff, -0x432021, 0x852023, 0x41402, 0x822021, -0x3084ffff, 0x50800001, 0x3404ffff, 0x8ee27ba4, -0x24430017, 0x37e1021, 0x62102b, 0x50400001, -0x6b1821, 0x90630000, 0x24020011, 0x14620031, -0x24020006, 0x8ee27ba4, 0x37e1821, 0x24420028, -0x43102b, 0x14400018, 0x0, 0x8ee27b9c, -0x12a2000a, 0x32c20100, 0x8ee27ba4, 0x3c01ffff, -0x220821, 0x94220028, 0x822021, 0x41c02, -0x3082ffff, 0x622021, 0x32c20100, 0x14400004, -0x41027, 0x92e27b98, 0x14400002, 0x41027, -0x3044ffff, 0x8ee27ba4, 0x3c01ffff, 0x220821, -0x8002d8a, 0xa4240028, 0x8ee27b9c, 0x12a20008, -0x32c20100, 0x8ee27ba4, 0x94420028, 0x822021, -0x41c02, 0x3082ffff, 0x622021, 0x32c20100, -0x14400004, 0x41027, 0x92e27b98, 0x14400002, -0x41027, 0x3044ffff, 0x8ee27ba4, 0x8002d8a, -0xa4440028, 0x1462002f, 0x37e1821, 0x8ee27ba4, -0x24420032, 0x43102b, 0x14400018, 0x0, -0x8ee27b9c, 0x12a2000a, 0x32c20100, 0x8ee27ba4, -0x3c01ffff, 0x220821, 0x94220032, 0x822021, -0x41c02, 0x3082ffff, 0x622021, 0x32c20100, -0x14400004, 0x41027, 0x92e27b98, 0x14400002, -0x41027, 0x3044ffff, 0x8ee27ba4, 0x3c01ffff, -0x220821, 0x8002d8a, 0xa4240032, 0x8ee27b9c, -0x12a20008, 0x32c20100, 0x8ee27ba4, 0x94420032, -0x822021, 0x41c02, 0x3082ffff, 0x622021, -0x32c20100, 0x14400004, 0x41027, 0x92e27b98, -0x14400002, 0x41027, 0x3044ffff, 0x8ee27ba4, -0xa4440032, 0x8fac0024, 0x1180002c, 0x37e1821, -0x8e420000, 0xae42fffc, 0x2642000a, 0x43102b, -0x1440001b, 0x34038100, 0x26430004, 0x37e1021, -0x62102b, 0x14400003, 0x602021, 0x6b1821, -0x602021, 0x8c620000, 0x24630004, 0xae420000, -0x37e1021, 0x62102b, 0x50400001, 0x6b1821, -0x8c620000, 0xac820000, 0x34028100, 0xa4620000, -0x24630002, 0x37e1021, 0x62102b, 0x50400001, -0x6b1821, 0x97ac002e, 0x8002db4, 0xa46c0000, -0x8e420004, 0x8e440008, 0xa6430008, 0x97ac002e, -0xa64c000a, 0xae420000, 0xae440004, 0x9662000e, -0x2652fffc, 0x24420004, 0xa662000e, 0x9662000e, -0x8ee3725c, 0x621821, 0xaee3725c, 0xafb20018, -0x8ee3725c, 0xafa3001c, 0x8ee2725c, 0x2c42003c, -0x10400004, 0x24620001, 0x2403fffe, 0x431024, -0xafa2001c, 0x32c20080, 0x1040000c, 0x32c20100, -0x8ee27ba8, 0x24430001, 0x210c0, 0x571021, -0xaee37ba8, 0x8fa30018, 0x8fa4001c, 0xac437bac, -0xac447bb0, 0x8002ea0, 0xaee0725c, 0x10400072, -0x0, 0x8ee27ba8, 0x24430001, 0x210c0, -0x571021, 0xaee37ba8, 0x8fa30018, 0x8fa4001c, -0xac437bac, 0xac447bb0, 0x8ee27ba8, 0x10400063, -0x4821, 0x5021, 0x8f8200f0, 0x24480008, -0x27621800, 0x102102b, 0x50400001, 0x27681000, -0x8f8200f4, 0x15020007, 0x0, 0x8ee201b4, -0x8021, 0x24420001, 0xaee201b4, 0x8002dfa, -0x8ee201b4, 0x8f8300f0, 0x24100001, 0x1571021, -0x8c447bac, 0x8c457bb0, 0xac640000, 0xac650004, -0xaf8800f0, 0x16000006, 0x2ea1021, 0x8ee20088, -0x24420001, 0xaee20088, 0x8002e3f, 0x8ee20088, -0x8c427bb0, 0x8ee400e0, 0x8ee500e4, 0x8ee67b9c, -0x401821, 0x1021, 0xa32821, 0xa3382b, -0x822021, 0x872021, 0x8ee204fc, 0xc93021, -0x63100, 0xaee400e0, 0xaee500e4, 0xc23021, -0x94c2000a, 0x240c0002, 0x21142, 0x30430003, -0x106c0016, 0x28620003, 0x10400005, 0x240c0001, -0x106c0008, 0x0, 0x8002e3f, 0x0, -0x240c0003, 0x106c0017, 0x0, 0x8002e3f, -0x0, 0x8ee200e8, 0x8ee300ec, 0x24630001, -0x2c640001, 0x441021, 0xaee200e8, 0xaee300ec, -0x8ee200e8, 0x8002e3f, 0x8ee300ec, 0x8ee200f0, -0x8ee300f4, 0x24630001, 0x2c640001, 0x441021, -0xaee200f0, 0xaee300f4, 0x8ee200f0, 0x8002e3f, -0x8ee300f4, 0x8ee200f8, 0x8ee300fc, 0x24630001, -0x2c640001, 0x441021, 0xaee200f8, 0xaee300fc, -0x8ee200f8, 0x8ee300fc, 0x8ee27ba8, 0x25290001, -0x122102b, 0x1440ffa0, 0x254a0008, 0xa2e07b98, -0x8002e9f, 0xaee07ba8, 0x8f8200f0, 0x24470008, -0x27621800, 0xe2102b, 0x50400001, 0x27671000, -0x8f8200f4, 0x14e20007, 0x0, 0x8ee201b4, -0x8021, 0x24420001, 0xaee201b4, 0x8002e5d, -0x8ee201b4, 0x8f8200f0, 0x24100001, 0x8fa30018, -0x8fa4001c, 0xac430000, 0xac440004, 0xaf8700f0, -0x16000007, 0x0, 0x8ee20088, 0x24420001, -0xaee20088, 0x8ee20088, 0x8002ea0, 0xaee0725c, -0x8ee2725c, 0x8ee400e0, 0x8ee500e4, 0x240c0002, -0x401821, 0x1021, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0x161142, 0x30430003, -0xaee400e0, 0xaee500e4, 0x106c0017, 0x2c620003, -0x10400005, 0x240c0001, 0x106c0008, 0x0, -0x8002ea0, 0xaee0725c, 0x240c0003, 0x106c0019, -0x0, 0x8002ea0, 0xaee0725c, 0x8ee200e8, -0x8ee300ec, 0x24630001, 0x2c640001, 0x441021, -0xaee200e8, 0xaee300ec, 0x8ee200e8, 0x8ee300ec, -0x8002ea0, 0xaee0725c, 0x8ee200f0, 0x8ee300f4, -0x24630001, 0x2c640001, 0x441021, 0xaee200f0, -0xaee300f4, 0x8ee200f0, 0x8ee300f4, 0x8002ea0, -0xaee0725c, 0x8ee200f8, 0x8ee300fc, 0x24630001, -0x2c640001, 0x441021, 0xaee200f8, 0xaee300fc, -0x8ee200f8, 0x8ee300fc, 0xaee0725c, 0x8e62001c, -0x96e30458, 0x8ee404f0, 0x24420001, 0x2463ffff, -0x431024, 0x24840001, 0xaee204e4, 0xaee404f0, -0x8f42023c, 0x82202b, 0x148000b0, 0x0, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, -0x8002f07, 0x8ee201a4, 0x8ee204e4, 0xac62001c, -0x8ee404b0, 0x8ee504b4, 0x2462001c, 0xac620008, -0x24020008, 0xa462000e, 0x24020011, 0xac620018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400037, 0x24100001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c830000, 0x24020012, 0x1462001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x240c0040, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x104c0007, 0x0, -0x8ee24e34, 0x24420001, 0x10620005, 0x0, -0x8002ef1, 0x0, 0x14600005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x8002f07, 0x0, 0x8ee24e30, 0x240c0040, -0x24420001, 0x504c0003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020012, 0x240c0001, -0xac820000, 0xac8c0004, 0x5600000d, 0x24100001, -0x8ee204e4, 0x3c040001, 0x24845754, 0xafa00014, -0xafa20010, 0x8ee60608, 0x8f470228, 0x3c050009, -0x34a5f006, 0xc002403, 0xafab0038, 0x8fab0038, -0x16000003, 0x240c0001, 0x8002f5c, 0xa2ec04f4, -0x8ee20170, 0x24420001, 0xaee20170, 0x8ee20170, -0x8ee204e4, 0xa2e004f4, 0xaee004f0, 0xaee07274, -0xaee204f8, 0x8f42023c, 0x10400038, 0x0, -0x8ee20184, 0x24420001, 0xaee20184, 0x8002f5c, -0x8ee20184, 0x8ee20504, 0x240c0040, 0x24420001, -0x504c0003, 0x1021, 0x8ee20504, 0x24420001, -0xaee20504, 0x8ee20504, 0x8e630018, 0x240c0003, -0x21080, 0x571021, 0x146c000f, 0x8c440508, -0x3c020001, 0x571021, 0x904283b1, 0x10400014, -0x0, 0x8ee201d0, 0x8ee35240, 0x441021, -0xaee201d0, 0x8ee201d8, 0x641821, 0x306300ff, -0x8002f4f, 0xaee35240, 0x8ee201cc, 0x8ee30e10, -0x441021, 0xaee201cc, 0x8ee201d8, 0x641821, -0x306301ff, 0xaee30e10, 0x441021, 0xaee201d8, -0x8ee20000, 0x34420040, 0x8002f5c, 0xaee20000, -0x8ee2014c, 0x3c010001, 0x370821, 0xa02083e0, -0x24420001, 0xaee2014c, 0x8ee2014c, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x8f820108, -0x27633000, 0x43102b, 0x14400002, 0x27622800, -0xaf820108, 0x8f830108, 0x8f820104, 0x1462fc1e, -0x0, 0x8fbf0060, 0x8fbe005c, 0x8fb60058, -0x8fb50054, 0x8fb40050, 0x8fb3004c, 0x8fb20048, -0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0068, -0x52843, 0x10a0000d, 0x3021, 0x3c030001, -0x34633800, 0x3c07ffff, 0x3631021, 0x82102b, -0x50400001, 0x872021, 0x94820000, 0x24840002, -0x24a5ffff, 0x14a0fff8, 0xc23021, 0x61c02, -0x30c2ffff, 0x623021, 0x61c02, 0x30c2ffff, -0x623021, 0x3e00008, 0x30c2ffff, 0x27bdff88, -0x240f0001, 0xafbf0070, 0xafbe006c, 0xafb60068, -0xafb50064, 0xafb40060, 0xafb3005c, 0xafb20058, -0xafb10054, 0xafb00050, 0xa3a00027, 0xafaf002c, -0x8ee204d4, 0x8021, 0x30420001, 0x1440002a, -0xa3a00037, 0x8f8700e0, 0x8f8800c4, 0x8f8200e8, -0xe22023, 0x2c821000, 0x50400001, 0x24841000, -0x420c2, 0x801821, 0x8ee400c8, 0x8ee500cc, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaee400c8, 0xaee500cc, 0x8f8300c8, -0x3c02000a, 0x3442efff, 0x1032023, 0x44102b, -0x10400003, 0x3c02000a, 0x3442f000, 0x822021, -0x801821, 0x8ee400c0, 0x8ee500c4, 0x1021, -0xa32821, 0xa3302b, 0x822021, 0x862021, -0xaee400c0, 0xaee500c4, 0xaf8800c8, 0xaf8700e4, -0x80034cc, 0xaf8700e8, 0x3c020001, 0x571021, -0x904283c0, 0x1040000b, 0x0, 0x3c140001, -0x297a021, 0x8e9483c4, 0x3c130001, 0x2779821, -0x8e7383c8, 0x3c120001, 0x2579021, 0x8003193, -0x8e5283cc, 0x8f8300e0, 0x8f8200e4, 0x10430007, -0x8821, 0x8f8200e4, 0x24110001, 0x8c430000, -0x8c440004, 0xafa30018, 0xafa4001c, 0x1620000e, -0x3c02ffff, 0x8f8200c4, 0xafa20010, 0x8f8200c8, -0x3c040001, 0x24845870, 0xafa20014, 0x8f8600e0, -0x8f8700e4, 0x3c050006, 0xc002403, 0x34a5f000, -0x80034cc, 0x0, 0x8fa3001c, 0x8fb20018, -0x3074ffff, 0x2694fffc, 0x621024, 0x10400058, -0x2409821, 0x3c020080, 0x621024, 0x1040000a, -0x3c040040, 0x8ee2007c, 0x24420001, 0xaee2007c, -0x8ee2007c, 0x8ee201fc, 0x24420001, 0xaee201fc, -0x80034c6, 0x8ee201fc, 0x3c060004, 0x3c0b0001, -0x3c0a0002, 0x3c050010, 0x3c090008, 0x8ee20080, -0x3c080020, 0x34078000, 0x24420001, 0xaee20080, -0x8ee20080, 0x8fa2001c, 0x441824, 0x10660021, -0xc3102b, 0x14400007, 0x0, 0x106b0011, -0x0, 0x106a0015, 0x0, 0x8003049, -0x42042, 0x10650023, 0xa3102b, 0x14400005, -0x0, 0x10690019, 0x0, 0x8003049, -0x42042, 0x10680021, 0x0, 0x8003049, -0x42042, 0x8ee20034, 0x24420001, 0xaee20034, -0x8ee20034, 0x8003049, 0x42042, 0x8ee201ec, -0x24420001, 0xaee201ec, 0x8ee201ec, 0x8003049, -0x42042, 0x8ee201f0, 0x24420001, 0xaee201f0, -0x8ee201f0, 0x8003049, 0x42042, 0x8ee201f4, -0x24420001, 0xaee201f4, 0x8ee201f4, 0x8003049, -0x42042, 0x8ee20030, 0x24420001, 0xaee20030, -0x8ee20030, 0x8003049, 0x42042, 0x8ee201f8, -0x24420001, 0xaee201f8, 0x8ee201f8, 0x42042, -0x1087047c, 0x0, 0x800300e, 0x0, -0x3c020001, 0x571021, 0x904283b2, 0x14400084, -0x24020001, 0x3c030001, 0x771821, 0x906383b3, -0x1462007f, 0x3c020100, 0x8e430000, 0x621024, -0x1040006f, 0x2402ffff, 0x14620005, 0x24100001, -0x96430004, 0x3402ffff, 0x10620075, 0x0, -0x92e204d8, 0x14400072, 0x0, 0x3c020001, -0x571021, 0x8c4283b4, 0x28420005, 0x10400020, -0x3821, 0x3c020001, 0x571021, 0x8c4283b4, -0x18400016, 0x2821, 0x96660000, 0x520c0, -0x971021, 0x9442777e, 0x14460009, 0x971021, -0x94437780, 0x96620002, 0x14620005, 0x971021, -0x94437782, 0x96620004, 0x50620008, 0x24070001, -0x3c020001, 0x571021, 0x8c4283b4, 0x24a50001, -0xa2102a, 0x5440ffee, 0x520c0, 0x30e200ff, -0x10400440, 0x0, 0x80030d5, 0x0, -0x2402021, 0xc0022fe, 0x24050006, 0x3044001f, -0x428c0, 0x2e51021, 0x9442727c, 0x30424000, -0x14400434, 0xb71021, 0x9443727e, 0x96620000, -0x1462000b, 0x418c0, 0xb71021, 0x94437280, -0x96620002, 0x14620006, 0x418c0, 0xb71021, -0x94437282, 0x96620004, 0x10620035, 0x418c0, -0x2e31021, 0x9442727c, 0x30428000, 0x14400421, -0x2e31021, 0x944b727c, 0x96670000, 0xb28c0, -0xb71021, 0x9442737e, 0x80030b7, 0x3021, -0x420c0, 0x2e41021, 0x9443737c, 0x2e41021, -0x944b737c, 0x30638000, 0x14600010, 0xb28c0, -0xb71021, 0x9442737e, 0x1447fff5, 0x1602021, -0xb71021, 0x94437380, 0x96620002, 0x5462fff1, -0x420c0, 0xb71021, 0x94437382, 0x96620004, -0x5462ffec, 0x420c0, 0x24060001, 0x30c200ff, -0x10400400, 0x0, 0x80030d5, 0x0, -0x97430202, 0x96420000, 0x146203fa, 0x0, -0x97430204, 0x96420002, 0x146203f6, 0x0, -0x97430206, 0x96420004, 0x146203f2, 0x0, -0x92420000, 0x3a030001, 0x30420001, 0x431024, -0x10400074, 0x2402ffff, 0x8e630000, 0x14620004, -0x3402ffff, 0x96630004, 0x1062006f, 0x240f0002, -0x3c020001, 0x571021, 0x904283b2, 0x1440006a, -0x240f0003, 0x92e204d8, 0x54400068, 0xafaf002c, -0x3c020001, 0x571021, 0x8c4283b4, 0x28420005, -0x10400020, 0x3821, 0x3c020001, 0x571021, -0x8c4283b4, 0x18400016, 0x2821, 0x96660000, -0x520c0, 0x971021, 0x9442777e, 0x14460009, -0x971021, 0x94437780, 0x96620002, 0x14620005, -0x971021, 0x94437782, 0x96620004, 0x50620008, -0x24070001, 0x3c020001, 0x571021, 0x8c4283b4, -0x24a50001, 0xa2102a, 0x5440ffee, 0x520c0, -0x30e200ff, 0x14400044, 0x240f0003, 0x80034c6, -0x0, 0x2402021, 0xc0022fe, 0x24050006, -0x3044001f, 0x428c0, 0x2e51021, 0x9442727c, -0x30424000, 0x144003af, 0xb71021, 0x9443727e, -0x96620000, 0x1462000b, 0x418c0, 0xb71021, -0x94437280, 0x96620002, 0x14620006, 0x418c0, -0xb71021, 0x94437282, 0x96620004, 0x10620027, -0x418c0, 0x2e31021, 0x9442727c, 0x30428000, -0x1440039c, 0x2e31021, 0x944b727c, 0x96670000, -0xb28c0, 0xb71021, 0x9442737e, 0x800313c, -0x3021, 0x420c0, 0x2e41021, 0x9443737c, -0x2e41021, 0x944b737c, 0x30638000, 0x14600010, -0xb28c0, 0xb71021, 0x9442737e, 0x1447fff5, -0x1602021, 0xb71021, 0x94437380, 0x96620002, -0x5462fff1, 0x420c0, 0xb71021, 0x94437382, -0x96620004, 0x5462ffec, 0x420c0, 0x24060001, -0x30c200ff, 0x1040037b, 0x0, 0x800314f, -0x240f0003, 0x240f0001, 0xafaf002c, 0x8f420260, -0x54102b, 0x1040003a, 0x0, 0x8f8300e4, -0x8f8200e0, 0x10620003, 0x24630008, 0xaf8300e4, -0xaf8300e8, 0x8ee400c0, 0x8ee500c4, 0x2801821, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaee400c0, 0xaee500c4, 0x8ee20058, -0x24420001, 0xaee20058, 0x8ee20058, 0x8ee2007c, -0x24420001, 0xaee2007c, 0x8ee2007c, 0x8f8200e0, -0xafa20010, 0x8f8200e4, 0x3c040001, 0x24845878, -0xafa20014, 0x8fa60018, 0x8fa7001c, 0x3c050006, -0xc002403, 0x34a5f003, 0x80034cc, 0x0, -0x8ee25240, 0xafa20010, 0x8ee25244, 0x3c040001, -0x24845884, 0xafa20014, 0x8ee60e10, 0x8ee70e18, -0x3c050006, 0xc002403, 0x34a5f002, 0x8ee201c0, -0x24420001, 0xaee201c0, 0x8ee20000, 0x8ee301c0, -0x2403ffbf, 0x431024, 0x8003470, 0xaee20000, -0x96e20468, 0x54102b, 0x10400003, 0x0, -0x240f0001, 0xa3af0027, 0x12800301, 0x24160007, -0x24150040, 0x241e0001, 0x240e0012, 0x8ee2724c, -0x8f430280, 0x24420001, 0x304207ff, 0x106202d3, -0x0, 0x93a20027, 0x10400014, 0x0, -0x8ee35240, 0x8ee25244, 0x10620009, 0x26ed5244, -0x8ee65244, 0x8ee35244, 0x21140, 0x24425248, -0x2e28021, 0x24630001, 0x80031bf, 0x306b00ff, -0x92e27248, 0x1440ffca, 0x0, 0x8ee201e0, -0x24420001, 0xaee201e0, 0x8ee201e0, 0x8ee30e10, -0x8ee20e18, 0x1062ffc2, 0x26ed0e18, 0x8ee60e18, -0x8ee30e18, 0x21140, 0x24420e20, 0x2e28021, -0x24630001, 0x306b01ff, 0x96e2046a, 0x30420010, -0x10400019, 0x0, 0x9642000c, 0x340f8100, -0x144f0015, 0x0, 0x3c020001, 0x571021, -0x904283c0, 0x14400010, 0x0, 0x9642000e, -0xa6020016, 0x8e420008, 0x8e430004, 0x8e440000, -0x2694fffc, 0xae42000c, 0xae430008, 0xae440004, -0x9602000e, 0x26730004, 0x240f0001, 0xa3af0037, -0x34420200, 0xa602000e, 0x8e020000, 0x8e030004, -0x3c040001, 0x34843800, 0x306a0007, 0x26a9823, -0x3641021, 0x262102b, 0x10400005, 0x28aa021, -0x2641023, 0x3621823, 0x3c020020, 0x439823, -0x26820007, 0x2404fff8, 0x9603000a, 0x446024, -0x6a1821, 0x6c102b, 0x10400002, 0x1803821, -0x603821, 0xae130018, 0x8f880120, 0x24e20007, -0x443824, 0x27623800, 0x25090020, 0x122102b, -0x50400001, 0x27693000, 0x8f820128, 0x11220004, -0x0, 0x8f820124, 0x15220007, 0x1401821, -0x8ee201a4, 0x8821, 0x24420001, 0xaee201a4, -0x800324c, 0x8ee201a4, 0x8e040000, 0x8e050004, -0x1021, 0xad130008, 0xa507000e, 0xad160018, -0xad06001c, 0xa3302b, 0xa32823, 0x822023, -0x862023, 0xad040000, 0xad050004, 0x8ee204c0, -0xad020010, 0xaf890120, 0x92e24e20, 0x14400033, -0x24110001, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c820000, 0x1456001f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062001b, 0x0, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x10550007, 0x0, -0x8ee24e34, 0x24420001, 0x10620005, 0x0, -0x8003239, 0x0, 0x14600005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400010, 0xac800000, -0x800324c, 0x0, 0x8ee24e30, 0x24420001, -0x50550003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0xac960000, 0xac9e0004, 0x16200018, -0x3c050006, 0x8e020018, 0x3c040001, 0x24845890, -0xafa20010, 0x8e020000, 0x8e030004, 0x34a5f009, -0x2003021, 0xc002403, 0xafa30014, 0x93a20037, -0x10400216, 0x340f8100, 0x8e420004, 0x8e430008, -0x8e44000c, 0xa64f000c, 0xae420000, 0xae430004, -0xae440008, 0x96020016, 0x8003470, 0xa642000e, -0x14ec0168, 0x28a1823, 0x960c000a, 0x9603000e, -0x28a1023, 0xa602000a, 0x34620004, 0xa602000e, -0x8f880120, 0x27623800, 0x25090020, 0x122102b, -0x14400002, 0x306affff, 0x27693000, 0x8f820128, -0x11220004, 0x0, 0x8f820124, 0x15220007, -0x24040020, 0x8ee201a4, 0x8821, 0x24420001, -0xaee201a4, 0x80032ca, 0x8ee201a4, 0x8ee5724c, -0x8ee60490, 0x8ee70494, 0xa504000e, 0x24040004, -0xad100008, 0xad040018, 0x52940, 0xa01821, -0x1021, 0xe33821, 0xe3202b, 0xc23021, -0xc43021, 0xad060000, 0xad070004, 0x8ee2724c, -0xad02001c, 0x8ee204c4, 0xad020010, 0xaf890120, -0x92e24e20, 0x14400033, 0x24110001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c820000, -0x1456001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x0, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee34e30, 0x24420001, -0x10550007, 0x0, 0x8ee24e34, 0x24420001, -0x10620005, 0x0, 0x80032b7, 0x0, -0x14600005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400010, 0xac800000, 0x80032ca, 0x0, -0x8ee24e30, 0x24420001, 0x50550003, 0x1021, -0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0xac960000, -0xac9e0004, 0x1620000d, 0x0, 0xa60c000a, -0xa60a000e, 0x8f820100, 0xafa20010, 0x8f820104, -0x3c040001, 0x2484589c, 0x3c050006, 0xafa20014, -0x8ee6724c, 0x800343b, 0x34a5f00b, 0x3c010001, -0x370821, 0xa02083c0, 0xadab0000, 0x8ee201d8, -0x8ee3724c, 0x2442ffff, 0xaee201d8, 0x8ee201d8, -0x24630001, 0x306307ff, 0x26e25244, 0x15a20006, -0xaee3724c, 0x8ee201d0, 0x2442ffff, 0xaee201d0, -0x80032ef, 0x8ee201d0, 0x8ee201cc, 0x2442ffff, -0xaee201cc, 0x8ee201cc, 0x8f420240, 0x10400073, -0x0, 0x8ee20e1c, 0x24420001, 0xaee20e1c, -0x8f430240, 0x43102b, 0x14400176, 0xa021, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x8821, 0x24420001, 0xaee201a4, -0x800334f, 0x8ee201a4, 0x8ee2724c, 0xac62001c, -0x8ee404a8, 0x8ee504ac, 0x2462001c, 0xac620008, -0x24020008, 0xa462000e, 0x24020011, 0xac620018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400033, 0x24110001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x144e001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x10550007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x800333c, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400010, 0xac800000, 0x800334f, -0x0, 0x8ee24e30, 0x24420001, 0x50550003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0xac8e0000, 0xac9e0004, 0x5620000d, 0x24110001, -0x8ee2724c, 0x3c040001, 0x248458a8, 0xafa00014, -0xafa20010, 0x8ee6724c, 0x8f470280, 0x3c050009, -0x34a5f008, 0xc002403, 0xafae0048, 0x8fae0048, -0x56200001, 0xaee00e1c, 0x8ee20188, 0x24420001, -0xaee20188, 0x80033c8, 0x8ee20188, 0x8f830120, -0x27623800, 0x24660020, 0xc2102b, 0x50400001, -0x27663000, 0x8f820128, 0x10c20004, 0x0, -0x8f820124, 0x14c20007, 0x0, 0x8ee201a4, -0x8821, 0x24420001, 0xaee201a4, 0x80033ba, -0x8ee201a4, 0x8ee2724c, 0xac62001c, 0x8ee404a8, -0x8ee504ac, 0x2462001c, 0xac620008, 0x24020008, -0xa462000e, 0x24020011, 0xac620018, 0xac640000, -0xac650004, 0x8ee204c4, 0xac620010, 0xaf860120, -0x92e24e20, 0x14400033, 0x24110001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c820000, -0x144e001f, 0x0, 0x8ee34e30, 0x8ee24e34, -0x1062001b, 0x0, 0x8c820004, 0x24420001, -0xac820004, 0x8ee24e34, 0x8ee34e30, 0x24420001, -0x10550007, 0x0, 0x8ee24e34, 0x24420001, -0x10620005, 0x0, 0x80033a7, 0x0, -0x14600005, 0x0, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x8c820004, 0x2c420011, -0x50400010, 0xac800000, 0x80033ba, 0x0, -0x8ee24e30, 0x24420001, 0x50550003, 0x1021, -0x8ee24e30, 0x24420001, 0xaee24e30, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0xac8e0000, -0xac9e0004, 0x1620000d, 0x0, 0x8ee2724c, -0x3c040001, 0x248458a8, 0xafa00014, 0xafa20010, -0x8ee6724c, 0x8f470280, 0x3c050009, 0x34a5f008, -0xc002403, 0xafae0048, 0x8fae0048, 0x8ee20174, -0x24420001, 0xaee20174, 0x8ee20174, 0x800346e, -0xa021, 0x960c000a, 0x183102b, 0x54400001, -0x1801821, 0xa603000a, 0x8f880120, 0x27623800, -0x25090020, 0x122102b, 0x50400001, 0x27693000, -0x8f820128, 0x11220004, 0x0, 0x8f820124, -0x15220007, 0x24040020, 0x8ee201a4, 0x8821, -0x24420001, 0xaee201a4, 0x800342f, 0x8ee201a4, -0x8ee5724c, 0x8ee60490, 0x8ee70494, 0xa504000e, -0x24040004, 0xad100008, 0xad040018, 0x52940, -0xa01821, 0x1021, 0xe33821, 0xe3202b, -0xc23021, 0xc43021, 0xad060000, 0xad070004, -0x8ee2724c, 0xad02001c, 0x8ee204c4, 0xad020010, -0xaf890120, 0x92e24e20, 0x14400033, 0x24110001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x1456001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x10550007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x800341c, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400010, 0xac800000, 0x800342f, -0x0, 0x8ee24e30, 0x24420001, 0x50550003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0xac960000, 0xac9e0004, 0x1620001d, 0x0, -0xa60c000a, 0x8f820100, 0xafa20010, 0x8f820104, -0x3c040001, 0x2484589c, 0x3c050006, 0xafa20014, -0x8ee6724c, 0x34a5f00d, 0xc002403, 0x2003821, -0x93a20037, 0x10400031, 0x340f8100, 0x8e420004, -0x8e430008, 0x8e44000c, 0xa64f000c, 0xae420000, -0xae430004, 0xae440008, 0x96020016, 0xa642000e, -0x9602000e, 0x3042fdff, 0x8003470, 0xa602000e, -0x8ee201d8, 0x2442ffff, 0xaee201d8, 0x8ee201d8, -0x8ee201cc, 0x3c04001f, 0x3c010001, 0x370821, -0xa03e83c0, 0x2442ffff, 0xaee201cc, 0x9603000a, -0x3484ffff, 0x8ee201cc, 0x6a1821, 0x2639821, -0x93202b, 0x10800003, 0x3c02fff5, 0x34421000, -0x2629821, 0xadab0000, 0x8ee2724c, 0x24420001, -0x304207ff, 0xaee2724c, 0x8f420240, 0x10400004, -0x283a023, 0x8ee20e1c, 0x24420001, 0xaee20e1c, -0xa3a00027, 0x1680fd29, 0x0, 0x12800024, -0x0, 0x3c010001, 0x370821, 0xac3483c4, -0x3c010001, 0x370821, 0xac3383c8, 0x3c010001, -0x370821, 0xac3283cc, 0x93a20037, 0x10400008, -0x0, 0x3c020001, 0x571021, 0x8c4283cc, -0x24420004, 0x3c010001, 0x370821, 0xac2283cc, -0x8ee2724c, 0x8f430280, 0x24420001, 0x304207ff, -0x14620006, 0x0, 0x8ee201c4, 0x24420001, -0xaee201c4, 0x80034cc, 0x8ee201c4, 0x8ee201bc, -0x24420001, 0xaee201bc, 0x80034cc, 0x8ee201bc, -0x97a4001e, 0x2484fffc, 0x801821, 0x8ee400c0, -0x8ee500c4, 0x1021, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0xaee400c0, 0xaee500c4, -0x8faf002c, 0x24020002, 0x11e2000f, 0x29e20003, -0x14400017, 0x24020003, 0x15e20015, 0x0, -0x8ee200d0, 0x8ee300d4, 0x24630001, 0x2c640001, -0x441021, 0xaee200d0, 0xaee300d4, 0x8ee200d0, -0x80034c6, 0x8ee300d4, 0x8ee200d8, 0x8ee300dc, -0x24630001, 0x2c640001, 0x441021, 0xaee200d8, -0xaee300dc, 0x8ee200d8, 0x80034c6, 0x8ee300dc, -0x8ee200c8, 0x8ee300cc, 0x24630001, 0x2c640001, -0x441021, 0xaee200c8, 0xaee300cc, 0x8ee200c8, -0x8ee300cc, 0x8f8300e4, 0x8f8200e0, 0x10620003, -0x24630008, 0xaf8300e4, 0xaf8300e8, 0x8fbf0070, -0x8fbe006c, 0x8fb60068, 0x8fb50064, 0x8fb40060, -0x8fb3005c, 0x8fb20058, 0x8fb10054, 0x8fb00050, -0x3e00008, 0x27bd0078, 0x27bdffb0, 0xafb50044, -0xa821, 0xafb00030, 0x8021, 0xafbf004c, -0xafb60048, 0xafb40040, 0xafb3003c, 0xafb20038, -0xafb10034, 0x8ee204d4, 0x24140001, 0x30420001, -0x1440002a, 0xb021, 0x8f8700e0, 0x8f8800c4, -0x8f8200e8, 0xe22023, 0x2c821000, 0x50400001, -0x24841000, 0x420c2, 0x801821, 0x8ee400c8, -0x8ee500cc, 0x1021, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0xaee400c8, 0xaee500cc, -0x8f8300c8, 0x3c02000a, 0x3442efff, 0x1032023, -0x44102b, 0x10400003, 0x3c02000a, 0x3442f000, -0x822021, 0x801821, 0x8ee400c0, 0x8ee500c4, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaee400c0, 0xaee500c4, 0xaf8800c8, -0xaf8700e4, 0x8003850, 0xaf8700e8, 0x3c020001, -0x571021, 0x904283c0, 0x1040000b, 0x0, -0x3c130001, 0x2779821, 0x8e7383c4, 0x3c110001, -0x2378821, 0x8e3183c8, 0x3c120001, 0x2579021, -0x80036e8, 0x8e5283cc, 0x8f8300e0, 0x8f8200e4, -0x10430007, 0x4821, 0x8f8200e4, 0x24090001, -0x8c430000, 0x8c440004, 0xafa30018, 0xafa4001c, -0x1520000e, 0x3c02ffff, 0x8f8200c4, 0xafa20010, -0x8f8200c8, 0x3c040001, 0x24845870, 0xafa20014, -0x8f8600e0, 0x8f8700e4, 0x3c050006, 0xc002403, -0x34a5f000, 0x8003850, 0x0, 0x8fa3001c, -0x8fb20018, 0x3073ffff, 0x2673fffc, 0x621024, -0x10400058, 0x2408821, 0x3c020080, 0x621024, -0x1040000a, 0x3c040040, 0x8ee2007c, 0x24420001, -0xaee2007c, 0x8ee2007c, 0x8ee201fc, 0x24420001, -0xaee201fc, 0x800384a, 0x8ee201fc, 0x3c060004, -0x3c0b0001, 0x3c0a0002, 0x3c050010, 0x3c090008, -0x8ee20080, 0x3c080020, 0x34078000, 0x24420001, -0xaee20080, 0x8ee20080, 0x8fa2001c, 0x441824, -0x10660021, 0xc3102b, 0x14400007, 0x0, -0x106b0011, 0x0, 0x106a0015, 0x0, -0x8003592, 0x42042, 0x10650023, 0xa3102b, -0x14400005, 0x0, 0x10690019, 0x0, -0x8003592, 0x42042, 0x10680021, 0x0, -0x8003592, 0x42042, 0x8ee20034, 0x24420001, -0xaee20034, 0x8ee20034, 0x8003592, 0x42042, -0x8ee201ec, 0x24420001, 0xaee201ec, 0x8ee201ec, -0x8003592, 0x42042, 0x8ee201f0, 0x24420001, -0xaee201f0, 0x8ee201f0, 0x8003592, 0x42042, -0x8ee201f4, 0x24420001, 0xaee201f4, 0x8ee201f4, -0x8003592, 0x42042, 0x8ee20030, 0x24420001, -0xaee20030, 0x8ee20030, 0x8003592, 0x42042, -0x8ee201f8, 0x24420001, 0xaee201f8, 0x8ee201f8, -0x42042, 0x108702b7, 0x0, 0x8003557, -0x0, 0x3c020001, 0x571021, 0x904283b2, -0x14400084, 0x24020001, 0x3c030001, 0x771821, -0x906383b3, 0x1462007f, 0x3c020100, 0x8e430000, -0x621024, 0x1040006f, 0x2402ffff, 0x14620005, -0x24100001, 0x96430004, 0x3402ffff, 0x10620075, -0x0, 0x92e204d8, 0x14400072, 0x0, -0x3c020001, 0x571021, 0x8c4283b4, 0x28420005, -0x10400020, 0x3821, 0x3c020001, 0x571021, -0x8c4283b4, 0x18400016, 0x2821, 0x96260000, -0x520c0, 0x971021, 0x9442777e, 0x14460009, -0x971021, 0x94437780, 0x96220002, 0x14620005, -0x971021, 0x94437782, 0x96220004, 0x50620008, -0x24070001, 0x3c020001, 0x571021, 0x8c4283b4, -0x24a50001, 0xa2102a, 0x5440ffee, 0x520c0, -0x30e200ff, 0x1040027b, 0x0, 0x800361e, -0x0, 0x2402021, 0xc0022fe, 0x24050006, -0x3044001f, 0x428c0, 0x2e51021, 0x9442727c, -0x30424000, 0x1440026f, 0xb71021, 0x9443727e, -0x96220000, 0x1462000b, 0x418c0, 0xb71021, -0x94437280, 0x96220002, 0x14620006, 0x418c0, -0xb71021, 0x94437282, 0x96220004, 0x10620035, -0x418c0, 0x2e31021, 0x9442727c, 0x30428000, -0x1440025c, 0x2e31021, 0x9448727c, 0x96270000, -0x828c0, 0xb71021, 0x9442737e, 0x8003600, -0x3021, 0x420c0, 0x2e41021, 0x9443737c, -0x2e41021, 0x9448737c, 0x30638000, 0x14600010, -0x828c0, 0xb71021, 0x9442737e, 0x1447fff5, -0x1002021, 0xb71021, 0x94437380, 0x96220002, -0x5462fff1, 0x420c0, 0xb71021, 0x94437382, -0x96220004, 0x5462ffec, 0x420c0, 0x24060001, -0x30c200ff, 0x1040023b, 0x0, 0x800361e, -0x0, 0x97430202, 0x96420000, 0x14620235, -0x0, 0x97430204, 0x96420002, 0x14620231, -0x0, 0x97430206, 0x96420004, 0x1462022d, -0x0, 0x92420000, 0x3a030001, 0x30420001, -0x431024, 0x10400074, 0x2402ffff, 0x8e230000, -0x14620004, 0x3402ffff, 0x96230004, 0x1062006f, -0x24140002, 0x3c020001, 0x571021, 0x904283b2, -0x1440006a, 0x24140003, 0x92e204d8, 0x14400067, -0x0, 0x3c020001, 0x571021, 0x8c4283b4, -0x28420005, 0x10400020, 0x3821, 0x3c020001, -0x571021, 0x8c4283b4, 0x18400016, 0x2821, -0x96260000, 0x520c0, 0x971021, 0x9442777e, -0x14460009, 0x971021, 0x94437780, 0x96220002, -0x14620005, 0x971021, 0x94437782, 0x96220004, -0x50620008, 0x24070001, 0x3c020001, 0x571021, -0x8c4283b4, 0x24a50001, 0xa2102a, 0x5440ffee, -0x520c0, 0x30e200ff, 0x14400044, 0x24140003, -0x800384a, 0x0, 0x2402021, 0xc0022fe, -0x24050006, 0x3044001f, 0x428c0, 0x2e51021, -0x9442727c, 0x30424000, 0x144001ea, 0xb71021, -0x9443727e, 0x96220000, 0x1462000b, 0x418c0, -0xb71021, 0x94437280, 0x96220002, 0x14620006, -0x418c0, 0xb71021, 0x94437282, 0x96220004, -0x10620027, 0x418c0, 0x2e31021, 0x9442727c, -0x30428000, 0x144001d7, 0x2e31021, 0x9448727c, -0x96270000, 0x828c0, 0xb71021, 0x9442737e, -0x8003685, 0x3021, 0x420c0, 0x2e41021, -0x9443737c, 0x2e41021, 0x9448737c, 0x30638000, -0x14600010, 0x828c0, 0xb71021, 0x9442737e, -0x1447fff5, 0x1002021, 0xb71021, 0x94437380, -0x96220002, 0x5462fff1, 0x420c0, 0xb71021, -0x94437382, 0x96220004, 0x5462ffec, 0x420c0, -0x24060001, 0x30c200ff, 0x104001b6, 0x0, -0x8003698, 0x24140003, 0x24140001, 0x8f420260, -0x53102b, 0x10400049, 0x0, 0x8f8300e4, -0x8f8200e0, 0x10620003, 0x24630008, 0xaf8300e4, -0xaf8300e8, 0x8ee400c0, 0x8ee500c4, 0x2601821, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaee400c0, 0xaee500c4, 0x8ee20058, -0x24420001, 0xaee20058, 0x8ee20058, 0x8ee2007c, -0x24420001, 0xaee2007c, 0x8ee2007c, 0x8f8200e0, -0xafa20010, 0x8f8200e4, 0x3c040001, 0x24845878, -0xafa20014, 0x8fa60018, 0x8fa7001c, 0x3c050006, -0xc002403, 0x34a5f003, 0x8003850, 0x0, -0x8ee25240, 0xafa20010, 0x8ee25244, 0x3c040001, -0x24845884, 0xafa20014, 0x8ee60e10, 0x8ee70e18, -0xc002403, 0x34a5f002, 0x8ee201c0, 0x24420001, -0xaee201c0, 0x8ee20000, 0x8ee301c0, 0x2403ffbf, -0x431024, 0x80037f8, 0xaee20000, 0x8ee25240, -0xafa20010, 0x8ee25244, 0x3c040001, 0x24845884, -0xafa20014, 0x8ee60e10, 0x8ee70e18, 0x3c050006, -0xc002403, 0x34a5f002, 0x8ee201c0, 0x24420001, -0xaee201c0, 0x80037f8, 0x8ee201c0, 0x96e20468, -0x53102b, 0x54400001, 0x3c158000, 0x12600131, -0x3c0c001f, 0x358cffff, 0x8ee2724c, 0x8f430280, -0x24420001, 0x304207ff, 0x10620108, 0x0, -0x12a00014, 0x0, 0x8ee35240, 0x8ee25244, -0x10620009, 0x26ee5244, 0x8eeb5244, 0x8ee35244, -0x21140, 0x24425248, 0x2e28021, 0x24630001, -0x8003712, 0x306800ff, 0x92e27248, 0x1440ffc0, -0x3c050006, 0x8ee201e0, 0x24420001, 0xaee201e0, -0x8ee201e0, 0x8ee30e10, 0x8ee20e18, 0x1062ffcb, -0x26ee0e18, 0x8eeb0e18, 0xa821, 0x8ee30e18, -0x21140, 0x24420e20, 0x2e28021, 0x24630001, -0x306801ff, 0x96e2046a, 0x30420010, 0x10400017, -0x34028100, 0x9643000c, 0x14620014, 0x0, -0x3c020001, 0x571021, 0x904283c0, 0x1440000f, -0x0, 0x9642000e, 0xa6020016, 0x8e420008, -0x8e430004, 0x8e440000, 0x2673fffc, 0xae42000c, -0xae430008, 0xae440004, 0x9602000e, 0x26310004, -0x24160001, 0x34420200, 0xa602000e, 0x9603000a, -0x2605021, 0x73102b, 0x10400002, 0x2606821, -0x605021, 0x2d42003d, 0x1040002a, 0x3821, -0x9623000c, 0x24020800, 0x54620027, 0xae110018, -0x3c020001, 0x571021, 0x904283c0, 0x54400022, -0xae110018, 0x26220017, 0x182102b, 0x10400013, -0x0, 0x3c02fff5, 0x511021, 0x90421017, -0x38430006, 0x2c630001, 0x38420011, 0x2c420001, -0x621825, 0x10600013, 0x26220010, 0x182102b, -0x1040000e, 0x0, 0x3c07fff5, 0xf13821, -0x94e71010, 0x800375e, 0x24e7000e, 0x92220017, -0x38430006, 0x2c630001, 0x38420011, 0x2c420001, -0x621825, 0x50600004, 0xae110018, 0x96270010, -0x24e7000e, 0xae110018, 0x3c020001, 0x571021, -0x904283c0, 0x2102b, 0x14e00002, 0x24ec0, -0x1403821, 0x8f830120, 0x27623800, 0x24660020, -0xc2102b, 0x50400001, 0x27663000, 0x8f820128, -0x10c20004, 0x0, 0x8f820124, 0x14c20007, -0x2402000b, 0x8ee201a4, 0x4821, 0x24420001, -0xaee201a4, 0x80037bf, 0x8ee201a4, 0x8e040000, -0x8e050004, 0xac620018, 0x1751025, 0x491025, -0xac710008, 0xa467000e, 0xac62001c, 0xac640000, -0xac650004, 0x8ee204c0, 0xac620010, 0xaf860120, -0x92e24e20, 0x14400038, 0x24090001, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020007, 0x14620020, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001c, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee34e34, 0x8ee54e30, -0x24020040, 0x24630001, 0x10620007, 0x0, -0x8ee24e34, 0x24420001, 0x10a20005, 0x0, -0x80037a9, 0x0, 0x14a00005, 0x0, -0x8f820128, 0x24420020, 0xaf820128, 0x8f820128, -0x8c820004, 0x2c420011, 0x50400013, 0xac800000, -0x80037bf, 0x0, 0x8ee24e30, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020007, 0xac820000, -0x24020001, 0xac820004, 0x15200018, 0x3c050006, -0x8e020018, 0x3c040001, 0x24845890, 0xafa20010, -0x8e020000, 0x8e030004, 0x34a5f009, 0x2003021, -0xc002403, 0xafa30014, 0x32c200ff, 0x1040002b, -0x34028100, 0x8e430004, 0x8e440008, 0x8e45000c, -0xa642000c, 0xae430000, 0xae440004, 0xae450008, -0x96020016, 0x80037f8, 0xa642000e, 0x154d000a, -0x0, 0x9602000e, 0xa613000a, 0x34420004, -0xa602000e, 0x3c010001, 0x370821, 0xa02083c0, -0x80037f6, 0x9821, 0x9604000a, 0x93102b, -0x10400002, 0x2601821, 0x801821, 0x24020001, -0xa603000a, 0x3c010001, 0x370821, 0xa02283c0, -0x9604000a, 0x2248821, 0x191102b, 0x10400003, -0x3c02fff5, 0x34421000, 0x2228821, 0x2649823, -0xa821, 0x1660fef4, 0xadc80000, 0x12600021, -0x32c200ff, 0x3c010001, 0x370821, 0xac3383c4, -0x3c010001, 0x370821, 0xac3183c8, 0x3c010001, -0x370821, 0x10400008, 0xac3283cc, 0x3c020001, -0x571021, 0x8c4283cc, 0x24420004, 0x3c010001, -0x370821, 0xac2283cc, 0x8ee2724c, 0x8f430280, -0x24420001, 0x14620006, 0x0, 0x8ee201c4, -0x24420001, 0xaee201c4, 0x8003850, 0x8ee201c4, -0x8ee201bc, 0x24420001, 0xaee201bc, 0x8003850, -0x8ee201bc, 0x97a4001e, 0x2484fffc, 0x801821, -0x8ee400c0, 0x8ee500c4, 0x1021, 0xa32821, -0xa3302b, 0x822021, 0x862021, 0x24020002, -0xaee400c0, 0xaee500c4, 0x1282000f, 0x2a820003, -0x14400017, 0x24020003, 0x16820015, 0x0, -0x8ee200d0, 0x8ee300d4, 0x24630001, 0x2c640001, -0x441021, 0xaee200d0, 0xaee300d4, 0x8ee200d0, -0x800384a, 0x8ee300d4, 0x8ee200d8, 0x8ee300dc, -0x24630001, 0x2c640001, 0x441021, 0xaee200d8, -0xaee300dc, 0x8ee200d8, 0x800384a, 0x8ee300dc, -0x8ee200c8, 0x8ee300cc, 0x24630001, 0x2c640001, -0x441021, 0xaee200c8, 0xaee300cc, 0x8ee200c8, -0x8ee300cc, 0x8f8300e4, 0x8f8200e0, 0x10620003, -0x24630008, 0xaf8300e4, 0xaf8300e8, 0x8fbf004c, -0x8fb60048, 0x8fb50044, 0x8fb40040, 0x8fb3003c, -0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, -0x27bd0050, 0x27bdff90, 0xafb60060, 0xb021, -0xafbf0068, 0xafbe0064, 0xafb5005c, 0xafb40058, -0xafb30054, 0xafb20050, 0xafb1004c, 0xafb00048, -0x8ee204d4, 0x8821, 0x24150001, 0x30420001, -0x1440002a, 0xa3a0002f, 0x8f8700e0, 0x8f8800c4, -0x8f8200e8, 0xe22023, 0x2c821000, 0x50400001, -0x24841000, 0x420c2, 0x801821, 0x8ee400c8, -0x8ee500cc, 0x1021, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0xaee400c8, 0xaee500cc, -0x8f8300c8, 0x3c02000a, 0x3442efff, 0x1032023, -0x44102b, 0x10400003, 0x3c02000a, 0x3442f000, -0x822021, 0x801821, 0x8ee400c0, 0x8ee500c4, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaee400c0, 0xaee500c4, 0xaf8800c8, -0xaf8700e4, 0x8003c5b, 0xaf8700e8, 0x3c020001, -0x571021, 0x904283c0, 0x1040000b, 0x0, -0x3c130001, 0x2779821, 0x8e7383c4, 0x3c100001, -0x2178021, 0x8e1083c8, 0x3c120001, 0x2579021, -0x8003a59, 0x8e5283cc, 0x8f8300e0, 0x8f8200e4, -0x10430007, 0x3821, 0x8f8200e4, 0x24070001, -0x8c430000, 0x8c440004, 0xafa30018, 0xafa4001c, -0x14e0000e, 0x3c02ffff, 0x8f8200c4, 0xafa20010, -0x8f8200c8, 0x3c040001, 0x248458b4, 0xafa20014, -0x8f8600e0, 0x8f8700e4, 0x3c050006, 0xc002403, -0x34a5f200, 0x8003c5b, 0x0, 0x8fa3001c, -0x8fb20018, 0x3073ffff, 0x2673fffc, 0x621024, -0x10400058, 0x2408021, 0x3c020080, 0x621024, -0x1040000a, 0x3c040040, 0x8ee2007c, 0x24420001, -0xaee2007c, 0x8ee2007c, 0x8ee201fc, 0x24420001, -0xaee201fc, 0x8003c55, 0x8ee201fc, 0x3c060004, -0x3c0b0001, 0x3c0a0002, 0x3c050010, 0x3c090008, -0x8ee20080, 0x3c080020, 0x34078000, 0x24420001, -0xaee20080, 0x8ee20080, 0x8fa2001c, 0x441824, -0x10660021, 0xc3102b, 0x14400007, 0x0, -0x106b0011, 0x0, 0x106a0015, 0x0, -0x8003916, 0x42042, 0x10650023, 0xa3102b, -0x14400005, 0x0, 0x10690019, 0x0, -0x8003916, 0x42042, 0x10680021, 0x0, -0x8003916, 0x42042, 0x8ee20034, 0x24420001, -0xaee20034, 0x8ee20034, 0x8003916, 0x42042, -0x8ee201ec, 0x24420001, 0xaee201ec, 0x8ee201ec, -0x8003916, 0x42042, 0x8ee201f0, 0x24420001, -0xaee201f0, 0x8ee201f0, 0x8003916, 0x42042, -0x8ee201f4, 0x24420001, 0xaee201f4, 0x8ee201f4, -0x8003916, 0x42042, 0x8ee20030, 0x24420001, -0xaee20030, 0x8ee20030, 0x8003916, 0x42042, -0x8ee201f8, 0x24420001, 0xaee201f8, 0x8ee201f8, -0x42042, 0x1087033e, 0x0, 0x80038db, -0x0, 0x3c020001, 0x571021, 0x904283b2, -0x14400084, 0x24020001, 0x3c030001, 0x771821, -0x906383b3, 0x1462007f, 0x3c020100, 0x8e430000, -0x621024, 0x1040006f, 0x2402ffff, 0x14620005, -0x24110001, 0x96430004, 0x3402ffff, 0x10620075, -0x0, 0x92e204d8, 0x14400072, 0x0, -0x3c020001, 0x571021, 0x8c4283b4, 0x28420005, -0x10400020, 0x3821, 0x3c020001, 0x571021, -0x8c4283b4, 0x18400016, 0x2821, 0x96060000, -0x520c0, 0x971021, 0x9442777e, 0x14460009, -0x971021, 0x94437780, 0x96020002, 0x14620005, -0x971021, 0x94437782, 0x96020004, 0x50620008, -0x24070001, 0x3c020001, 0x571021, 0x8c4283b4, -0x24a50001, 0xa2102a, 0x5440ffee, 0x520c0, -0x30e200ff, 0x10400302, 0x0, 0x80039a2, -0x0, 0x2402021, 0xc0022fe, 0x24050006, -0x3044001f, 0x428c0, 0x2e51021, 0x9442727c, -0x30424000, 0x144002f6, 0xb71021, 0x9443727e, -0x96020000, 0x1462000b, 0x418c0, 0xb71021, -0x94437280, 0x96020002, 0x14620006, 0x418c0, -0xb71021, 0x94437282, 0x96020004, 0x10620035, -0x418c0, 0x2e31021, 0x9442727c, 0x30428000, -0x144002e3, 0x2e31021, 0x944d727c, 0x96070000, -0xd28c0, 0xb71021, 0x9442737e, 0x8003984, -0x3021, 0x420c0, 0x2e41021, 0x9443737c, -0x2e41021, 0x944d737c, 0x30638000, 0x14600010, -0xd28c0, 0xb71021, 0x9442737e, 0x1447fff5, -0x1a02021, 0xb71021, 0x94437380, 0x96020002, -0x5462fff1, 0x420c0, 0xb71021, 0x94437382, -0x96020004, 0x5462ffec, 0x420c0, 0x24060001, -0x30c200ff, 0x104002c2, 0x0, 0x80039a2, -0x0, 0x97430202, 0x96420000, 0x146202bc, -0x0, 0x97430204, 0x96420002, 0x146202b8, -0x0, 0x97430206, 0x96420004, 0x146202b4, -0x0, 0x92420000, 0x3a230001, 0x30420001, -0x431024, 0x10400074, 0x2402ffff, 0x8e030000, -0x14620004, 0x3402ffff, 0x96030004, 0x1062006f, -0x24150002, 0x3c020001, 0x571021, 0x904283b2, -0x1440006a, 0x24150003, 0x92e204d8, 0x14400067, -0x0, 0x3c020001, 0x571021, 0x8c4283b4, -0x28420005, 0x10400020, 0x3821, 0x3c020001, -0x571021, 0x8c4283b4, 0x18400016, 0x2821, -0x96060000, 0x520c0, 0x971021, 0x9442777e, -0x14460009, 0x971021, 0x94437780, 0x96020002, -0x14620005, 0x971021, 0x94437782, 0x96020004, -0x50620008, 0x24070001, 0x3c020001, 0x571021, -0x8c4283b4, 0x24a50001, 0xa2102a, 0x5440ffee, -0x520c0, 0x30e200ff, 0x14400044, 0x24150003, -0x8003c55, 0x0, 0x2402021, 0xc0022fe, -0x24050006, 0x3044001f, 0x428c0, 0x2e51021, -0x9442727c, 0x30424000, 0x14400271, 0xb71021, -0x9443727e, 0x96020000, 0x1462000b, 0x418c0, -0xb71021, 0x94437280, 0x96020002, 0x14620006, -0x418c0, 0xb71021, 0x94437282, 0x96020004, -0x10620027, 0x418c0, 0x2e31021, 0x9442727c, -0x30428000, 0x1440025e, 0x2e31021, 0x944d727c, -0x96070000, 0xd28c0, 0xb71021, 0x9442737e, -0x8003a09, 0x3021, 0x420c0, 0x2e41021, -0x9443737c, 0x2e41021, 0x944d737c, 0x30638000, -0x14600010, 0xd28c0, 0xb71021, 0x9442737e, -0x1447fff5, 0x1a02021, 0xb71021, 0x94437380, -0x96020002, 0x5462fff1, 0x420c0, 0xb71021, -0x94437382, 0x96020004, 0x5462ffec, 0x420c0, -0x24060001, 0x30c200ff, 0x1040023d, 0x0, -0x8003a1c, 0x24150003, 0x24150001, 0x8f420260, -0x53102b, 0x10400036, 0x0, 0x8f8300e4, -0x8f8200e0, 0x10620003, 0x24630008, 0xaf8300e4, -0xaf8300e8, 0x8ee400c0, 0x8ee500c4, 0x2601821, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaee400c0, 0xaee500c4, 0x8ee20058, -0x24420001, 0xaee20058, 0x8ee20058, 0x8ee2007c, -0x24420001, 0xaee2007c, 0x8ee2007c, 0x8f8200e0, -0xafa20010, 0x8f8200e4, 0x3c040001, 0x248458c0, -0xafa20014, 0x8fa60018, 0x8fa7001c, 0x3c050006, -0xc002403, 0x34a5f203, 0x8003c5b, 0x0, -0x8ee25240, 0xafa20010, 0x8ee25244, 0x3c040001, -0x248458cc, 0xafa20014, 0x8ee60e10, 0x8ee70e18, -0x3c050006, 0xc002403, 0x34a5f202, 0x8ee201c0, -0x24420001, 0xaee201c0, 0x8003c02, 0x8ee201c0, -0x96e20468, 0x53102b, 0x54400001, 0x3c168000, -0x126001cb, 0x3c0e001f, 0x35ceffff, 0x3c0ffff5, -0x35ef1000, 0x241e0040, 0x8ee2724c, 0x8f430280, -0x24420001, 0x304207ff, 0x1062019e, 0x0, -0x12c00012, 0x0, 0x8ee35240, 0x8ee25244, -0x1062000a, 0x26f85244, 0x8ef45244, 0xafb80024, -0x8ee35244, 0x21140, 0x24425248, 0x2e28821, -0x24630001, 0x8003a85, 0x306d00ff, 0x8ee201e0, -0x24420001, 0xaee201e0, 0x8ee201e0, 0x8ee30e10, -0x8ee20e18, 0x1062ffca, 0x26f80e18, 0x8ef40e18, -0xb021, 0xafb80024, 0x8ee30e18, 0x21140, -0x24420e20, 0x2e28821, 0x24630001, 0x306d01ff, -0x96e2046a, 0x30420010, 0x10400018, 0x34028100, -0x9643000c, 0x14620015, 0x0, 0x3c020001, -0x571021, 0x904283c0, 0x14400010, 0x0, -0x9642000e, 0xa6220016, 0x8e420008, 0x8e430004, -0x8e440000, 0x2673fffc, 0xae42000c, 0xae430008, -0xae440004, 0x9622000e, 0x26100004, 0x24180001, -0xa3b8002f, 0x34420200, 0xa622000e, 0x8e220000, -0x8e230004, 0x3c040001, 0x34843800, 0x2003021, -0x306a0007, 0x20a8023, 0x3641021, 0x202102b, -0x10400005, 0x26a9821, 0x2041023, 0x3621823, -0x3c020020, 0x438023, 0x26620007, 0x9623000a, -0x2418fff8, 0x58c824, 0x6a1821, 0x79102b, -0x10400002, 0x3206021, 0x606021, 0x1801821, -0x24620007, 0x2418fff8, 0x586024, 0x26c102b, -0x14400004, 0x1932823, 0x1832823, 0x8003ac3, -0xc31021, 0xd31021, 0x4a2023, 0x1c4102b, -0x54400001, 0x8f2021, 0x25420040, 0x4c102b, -0x14400035, 0x5821, 0x94c3000c, 0x24020800, -0x54620032, 0xae260018, 0x3c020001, 0x571021, -0x904283c0, 0x5440002d, 0xae260018, 0x24c20017, -0x1c2102b, 0x10400013, 0x0, 0x3c02fff5, -0x461021, 0x90421017, 0x38430006, 0x2c630001, -0x38420011, 0x2c420001, 0x621825, 0x10600014, -0x24c20010, 0x1c2102b, 0x1040000e, 0x0, -0x3c0bfff5, 0x1665821, 0x956b1010, 0x8003af4, -0x2562000e, 0x90c20017, 0x38430006, 0x2c630001, -0x38420011, 0x2c420001, 0x621825, 0x10600005, -0x1601821, 0x94cb0010, 0x2562000e, 0x4a5821, -0x1601821, 0x24620007, 0x2418fff8, 0x585824, -0xc31021, 0x4a2023, 0x1c4102b, 0x10400002, -0x1632823, 0x8f2021, 0xae260018, 0x3c020001, -0x571021, 0x904283c0, 0x2102b, 0x216c0, -0x15600002, 0xafa20044, 0x1805821, 0x30820001, -0x10400007, 0x4021, 0x90880000, 0x24840001, -0x1c4102b, 0x10400002, 0x24a5ffff, 0x8f2021, -0x50a00012, 0x81c02, 0x2ca20002, 0x54400009, -0x24a5ffff, 0x94820000, 0x24840002, 0x1024021, -0x1c4102b, 0x10400006, 0x24a5fffe, 0x8003b21, -0x8f2021, 0x90820000, 0x21200, 0x1024021, -0x14a0fff2, 0x2ca20002, 0x81c02, 0x3102ffff, -0x624021, 0x3108ffff, 0x1402821, 0x11400011, -0x2002021, 0x2ca20002, 0x54400009, 0x24a5ffff, -0x94820000, 0x24840002, 0x1024021, 0x1c4102b, -0x10400006, 0x24a5fffe, 0x8003b38, 0x8f2021, -0x90820000, 0x21200, 0x1024021, 0x14a0fff2, -0x2ca20002, 0x81c02, 0x3102ffff, 0x624021, -0x81c02, 0x3102ffff, 0x8f890120, 0x624021, -0x27623800, 0x25230020, 0x62102b, 0x14400002, -0x3108ffff, 0x27633000, 0x8f820128, 0x10620004, -0x0, 0x8f820124, 0x14620007, 0x1402821, -0x8ee201a4, 0x3821, 0x24420001, 0xaee201a4, -0x8003bc9, 0x8ee201a4, 0x8e260000, 0x8e270004, -0x81400, 0x3448000b, 0xad300008, 0xa52b000e, -0xad280018, 0x8fb80044, 0x2021, 0x2961025, -0x581025, 0xad22001c, 0xe5102b, 0xe53823, -0xc43023, 0xc23023, 0xad260000, 0xad270004, -0x8ee204c0, 0xad220010, 0xaf830120, 0x92e24e20, -0x1440005f, 0x24070001, 0x2502ffee, 0x2c420002, -0x14400003, 0x24020011, 0x15020024, 0x0, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c830000, 0x24020012, 0x1462000f, 0x0, -0x8ee34e30, 0x8ee24e34, 0x1062000b, 0x0, -0x8c820004, 0x24420001, 0xac820004, 0x8ee24e34, -0x8ee34e30, 0x24420001, 0x105e002a, 0x0, -0x8003ba8, 0x0, 0x8ee24e30, 0x24420001, -0x505e0003, 0x1021, 0x8ee24e30, 0x24420001, -0xaee24e30, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8003bc6, 0x24020012, 0x8ee24e30, -0x210c0, 0x24425038, 0x2e22021, 0x8c830000, -0x24020007, 0x1462001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x105e0007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x8003bb4, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400012, 0xac800000, 0x8003bc9, -0x0, 0x8ee24e30, 0x24420001, 0x505e0003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020007, 0xac820000, 0x24020001, 0xac820004, -0x14e00019, 0x3c050006, 0x3c040001, 0x24845890, -0x8e220018, 0x34a5f209, 0xafa20010, 0x8e220000, -0x8e230004, 0x2203021, 0x1603821, 0xc002403, -0xafa30014, 0x93a2002f, 0x1040002a, 0x34028100, -0x8e430004, 0x8e440008, 0x8e45000c, 0xa642000c, -0xae430000, 0xae440004, 0xae450008, 0x96220016, -0x8003c02, 0xa642000e, 0x1599000a, 0x26a1823, -0x9622000e, 0xa623000a, 0x34420004, 0xa622000e, -0x3c010001, 0x370821, 0xa02083c0, 0x8003bff, -0x9821, 0x9624000a, 0x83102b, 0x54400001, -0x801821, 0x24020001, 0xa623000a, 0x3c010001, -0x370821, 0xa02283c0, 0x9622000a, 0x4a1821, -0x2038021, 0x1d0102b, 0x54400001, 0x20f8021, -0x2639823, 0xb021, 0x8fb80024, 0x1660fe5e, -0xaf0d0000, 0x12600022, 0x0, 0x3c010001, -0x370821, 0xac3383c4, 0x3c010001, 0x370821, -0xac3083c8, 0x3c010001, 0x370821, 0xac3283cc, -0x93a2002f, 0x10400008, 0x0, 0x3c020001, -0x571021, 0x8c4283cc, 0x24420004, 0x3c010001, -0x370821, 0xac2283cc, 0x8f430280, 0x8ee2724c, -0x14620006, 0x0, 0x8ee201c4, 0x24420001, -0xaee201c4, 0x8003c5b, 0x8ee201c4, 0x8ee201bc, -0x24420001, 0xaee201bc, 0x8003c5b, 0x8ee201bc, -0x97a4001e, 0x2484fffc, 0x801821, 0x8ee400c0, -0x8ee500c4, 0x1021, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0x24020002, 0xaee400c0, -0xaee500c4, 0x12a2000f, 0x2aa20003, 0x14400017, -0x24020003, 0x16a20015, 0x0, 0x8ee200d0, -0x8ee300d4, 0x24630001, 0x2c640001, 0x441021, -0xaee200d0, 0xaee300d4, 0x8ee200d0, 0x8003c55, -0x8ee300d4, 0x8ee200d8, 0x8ee300dc, 0x24630001, -0x2c640001, 0x441021, 0xaee200d8, 0xaee300dc, -0x8ee200d8, 0x8003c55, 0x8ee300dc, 0x8ee200c8, -0x8ee300cc, 0x24630001, 0x2c640001, 0x441021, -0xaee200c8, 0xaee300cc, 0x8ee200c8, 0x8ee300cc, -0x8f8300e4, 0x8f8200e0, 0x10620003, 0x24630008, -0xaf8300e4, 0xaf8300e8, 0x8fbf0068, 0x8fbe0064, -0x8fb60060, 0x8fb5005c, 0x8fb40058, 0x8fb30054, -0x8fb20050, 0x8fb1004c, 0x8fb00048, 0x3e00008, -0x27bd0070, 0x27bdffe0, 0xafbf0018, 0x8ee30e14, -0x8ee20e0c, 0x10620074, 0x0, 0x8ee30e0c, -0x8ee20e14, 0x622023, 0x4820001, 0x24840200, -0x8ee30e18, 0x8ee20e14, 0x43102b, 0x14400004, -0x24020200, 0x8ee30e14, 0x8003c7d, 0x431823, -0x8ee20e18, 0x8ee30e14, 0x431023, 0x2443ffff, -0x804821, 0x69102a, 0x54400001, 0x604821, -0x8f870100, 0x27623000, 0x24e80020, 0x102102b, -0x50400001, 0x27682800, 0x8f820108, 0x11020004, -0x0, 0x8f820104, 0x15020007, 0x1021, -0x8ee201a8, 0x2021, 0x24420001, 0xaee201a8, -0x8003cbf, 0x8ee201a8, 0x8ee40e14, 0x42140, -0x801821, 0x8ee40460, 0x8ee50464, 0xa32821, -0xa3302b, 0x822021, 0x862021, 0xace40000, -0xace50004, 0x8ee30e14, 0x91140, 0xa4e2000e, -0x24020002, 0xace20018, 0x31940, 0x24630e20, -0x2e31021, 0xace20008, 0x8ee20e14, 0xace2001c, -0x8ee204cc, 0xace20010, 0xaf880100, 0x92e204ec, -0x14400011, 0x24040001, 0x8ee24e28, 0x24030040, -0x24420001, 0x50430003, 0x1021, 0x8ee24e28, -0x24420001, 0xaee24e28, 0x8ee24e28, 0x210c0, -0x24424e38, 0x2e21821, 0x24020002, 0xac620000, -0x24020001, 0xac620004, 0x1480000e, 0x24030040, -0x8ee20e14, 0xafa20010, 0x8ee20e18, 0x3c050007, -0xafa20014, 0x8ee60e0c, 0x8ee70e10, 0x3c040001, -0x248458d4, 0xc002403, 0x34a5f001, 0x8003cdd, -0x0, 0x8ee20500, 0x24420001, 0x50430003, -0x1021, 0x8ee20500, 0x24420001, 0xaee20500, -0x8ee20500, 0x21080, 0x571021, 0xac490508, -0x8ee20e14, 0x491021, 0x304201ff, 0xaee20e14, -0x8ee30e14, 0x8ee20e0c, 0x14620005, 0x0, -0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, -0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, -0xafbf0018, 0x8ee3523c, 0x8ee25238, 0x10620074, -0x0, 0x8ee35238, 0x8ee2523c, 0x622023, -0x4820001, 0x24840100, 0x8ee35244, 0x8ee2523c, -0x43102b, 0x14400004, 0x24020100, 0x8ee3523c, -0x8003cff, 0x431823, 0x8ee25244, 0x8ee3523c, -0x431023, 0x2443ffff, 0x804821, 0x69102a, -0x54400001, 0x604821, 0x8f870100, 0x27623000, -0x24e80020, 0x102102b, 0x50400001, 0x27682800, -0x8f820108, 0x11020004, 0x0, 0x8f820104, -0x15020007, 0x1021, 0x8ee201a8, 0x2021, -0x24420001, 0xaee201a8, 0x8003d41, 0x8ee201a8, -0x8ee4523c, 0x42140, 0x801821, 0x8ee40470, -0x8ee50474, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xace40000, 0xace50004, 0x8ee3523c, -0x91140, 0xa4e2000e, 0x24020003, 0xace20018, -0x31940, 0x24635248, 0x2e31021, 0xace20008, -0x8ee2523c, 0xace2001c, 0x8ee204cc, 0xace20010, -0xaf880100, 0x92e204ec, 0x14400011, 0x24040001, -0x8ee24e28, 0x24030040, 0x24420001, 0x50430003, -0x1021, 0x8ee24e28, 0x24420001, 0xaee24e28, -0x8ee24e28, 0x210c0, 0x24424e38, 0x2e21821, -0x24020003, 0xac620000, 0x24020001, 0xac620004, -0x1480000e, 0x24030040, 0x8ee2523c, 0xafa20010, -0x8ee25244, 0x3c050007, 0xafa20014, 0x8ee65238, -0x8ee75240, 0x3c040001, 0x248458e0, 0xc002403, -0x34a5f010, 0x8003d5f, 0x0, 0x8ee20500, -0x24420001, 0x50430003, 0x1021, 0x8ee20500, -0x24420001, 0xaee20500, 0x8ee20500, 0x21080, -0x571021, 0xac490508, 0x8ee2523c, 0x491021, -0x304200ff, 0xaee2523c, 0x8ee3523c, 0x8ee25238, -0x14620005, 0x0, 0x8f820060, 0x2403feff, -0x431024, 0xaf820060, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x8f820120, 0x8ee34e34, 0x8f820124, -0x8f860128, 0x24020040, 0x24630001, 0x50620003, -0x1021, 0x8ee24e34, 0x24420001, 0xaee24e34, -0x8ee24e34, 0x8ee44e34, 0x8ee34e30, 0x210c0, -0x24425038, 0x14830007, 0x2e22821, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8003d92, -0xaca00000, 0x8ee24e34, 0x24030040, 0x24420001, -0x50430003, 0x1021, 0x8ee24e34, 0x24420001, -0x210c0, 0x24425038, 0x2e22821, 0x8ca20004, -0x8f830128, 0x21140, 0x621821, 0xaf830128, -0xaca00000, 0x8cc20018, 0x2443fffe, 0x2c620012, -0x10400008, 0x31080, 0x3c010001, 0x220821, -0x8c2258f0, 0x400008, 0x0, 0x24020001, -0xaee24e24, 0x3e00008, 0x0, 0x27bdffc8, -0xafbf0030, 0xafb5002c, 0xafb40028, 0xafb30024, -0xafb20020, 0xafb1001c, 0xafb00018, 0x8f830128, -0x8f820124, 0x106202b0, 0x9821, 0x3c11001f, -0x3631ffff, 0x3c12fff5, 0x36521000, 0x24150012, -0x24140040, 0x8f8c0128, 0x8f820128, 0x24420020, -0xaf820128, 0x9182001b, 0x8f830128, 0x2443fffe, -0x2c620012, 0x1040029c, 0x31080, 0x3c010001, -0x220821, 0x8c225948, 0x400008, 0x0, -0x8f420218, 0x30420100, 0x10400007, 0x0, -0x95830016, 0x95820018, 0x621823, 0x31402, -0x431021, 0xa5820016, 0x8d82001c, 0x3c038000, -0x3044ffff, 0x436824, 0x3c030800, 0x431824, -0x11a00004, 0xad84001c, 0x41140, 0x8003dd8, -0x24425248, 0x41140, 0x24420e20, 0x2e25821, -0x9562000e, 0x3042fffc, 0x10600004, 0xa562000e, -0x95840016, 0x8003ec0, 0x0, 0x8d690018, -0x4021, 0x952a0000, 0x25290002, 0x95270000, -0x25290002, 0x95260000, 0x25290002, 0x95250000, -0x25290002, 0x95240000, 0x25290002, 0x95230000, -0x25290002, 0x95220000, 0x25290002, 0x1475021, -0x1465021, 0x1455021, 0x1445021, 0x1435021, -0x1425021, 0xa1c02, 0x3142ffff, 0x625021, -0xa1c02, 0x3142ffff, 0x625021, 0x96e2046a, -0x314effff, 0x30420002, 0x10400044, 0x5021, -0x25220014, 0x222102b, 0x10400014, 0x1201821, -0x2405000a, 0x2021, 0x223102b, 0x54400001, -0x721821, 0x94620000, 0x24630002, 0x24a5ffff, -0x14a0fff9, 0x822021, 0x41c02, 0x3082ffff, -0x622021, 0x41402, 0x3083ffff, 0x431021, -0x3042ffff, 0x8003e33, 0x1425021, 0x952a0000, -0x25290002, 0x95280000, 0x25290002, 0x95270000, -0x25290002, 0x95260000, 0x25290002, 0x95250000, -0x25290002, 0x95230000, 0x25290002, 0x95220000, -0x25290002, 0x95240000, 0x25290002, 0x1485021, -0x1475021, 0x1465021, 0x1455021, 0x1435021, -0x1425021, 0x95220000, 0x95230002, 0x1445021, -0x1425021, 0x1435021, 0xa1c02, 0x3142ffff, -0x625021, 0xa1c02, 0x3142ffff, 0x625021, -0x3148ffff, 0x51000001, 0x3408ffff, 0x8d620018, -0x9443000c, 0x24020800, 0x54620005, 0xa5680010, -0x9562000e, 0x34420002, 0xa562000e, 0xa5680010, -0x96e2046a, 0x2821, 0x30420008, 0x14400056, -0x3021, 0x8d630018, 0x24620024, 0x222102b, -0x10400034, 0x24690010, 0x229102b, 0x54400001, -0x1324821, 0x95250000, 0x24690014, 0x229102b, -0x10400002, 0x24a5ffec, 0x1324821, 0x95220000, -0x30420fff, 0x14400003, 0x25290002, 0x8003e60, -0x24130001, 0x9821, 0xa03021, 0x229102b, -0x54400001, 0x1324821, 0x91220001, 0x25290002, -0xa22821, 0x229102b, 0x54400001, 0x1324821, -0x25290002, 0x229102b, 0x54400001, 0x1324821, -0x95220000, 0x25290002, 0xa22821, 0x229102b, -0x54400001, 0x1324821, 0x95220000, 0x25290002, -0xa22821, 0x229102b, 0x54400001, 0x1324821, -0x95220000, 0x25290002, 0xa22821, 0x229102b, -0x54400001, 0x1324821, 0x95220000, 0x8003e99, -0xa22821, 0x94650010, 0x94620014, 0x24690016, -0x30420fff, 0x14400003, 0x24a5ffec, 0x8003e8c, -0x24130001, 0x9821, 0xa03021, 0x91230001, -0x25290004, 0x95220000, 0x25290002, 0x95240000, -0x25290002, 0xa32821, 0xa22821, 0x95220000, -0x95230002, 0xa42821, 0xa22821, 0xa32821, -0x51c02, 0x30a2ffff, 0x622821, 0x51c02, -0x30a2ffff, 0x622821, 0x96e2046a, 0x30420001, -0x1040001e, 0x2021, 0x95820016, 0x4e2023, -0x41402, 0x822021, 0x326200ff, 0x50400002, -0x862021, 0x852021, 0x41402, 0x822021, -0x3084ffff, 0x50800001, 0x3404ffff, 0x8d620018, -0x24430017, 0x223102b, 0x54400001, 0x721821, -0x90620000, 0x38430011, 0x2c630001, 0x38420006, -0x2c420001, 0x621825, 0x10600004, 0x0, -0x9562000e, 0x34420001, 0xa562000e, 0x9562000e, -0x240a0002, 0x30420004, 0x10400002, 0xa5640012, -0x240a0004, 0x8f880120, 0x27623800, 0x25090020, -0x122102b, 0x50400001, 0x27693000, 0x8f820128, -0x11220004, 0x0, 0x8f820124, 0x15220007, -0x24040020, 0x8ee201a4, 0x8021, 0x24420001, -0xaee201a4, 0x8003f4f, 0x8ee201a4, 0x8ee5724c, -0x8ee60490, 0x8ee70494, 0xad0b0008, 0xa504000e, -0xad0a0018, 0x52940, 0xa01821, 0x1021, -0xe33821, 0xe3202b, 0xc23021, 0xc43021, -0xad060000, 0xad070004, 0x8ee2724c, 0x4d1025, -0xad02001c, 0x8ee204c4, 0xad020010, 0xaf890120, -0x92e24e20, 0x14400060, 0x24100001, 0x2543ffee, -0x2c630002, 0x39420011, 0x2c420001, 0x621825, -0x10600024, 0x0, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c820000, 0x1455000f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062000b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x1054002b, -0x0, 0x8003f2e, 0x0, 0x8ee24e30, -0x24420001, 0x50540003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020001, 0x8003f4e, -0xac950000, 0x8ee24e30, 0x210c0, 0x24425038, -0x2e22021, 0x8c830000, 0x24020007, 0x1462001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10540007, -0x0, 0x8ee24e34, 0x24420001, 0x10620005, -0x0, 0x8003f3a, 0x0, 0x14600005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400012, -0xac800000, 0x8003f4f, 0x0, 0x8ee24e30, -0x24420001, 0x50540003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020007, 0xac820000, -0x24020001, 0xac820004, 0x1600000d, 0x0, -0x8f820120, 0x3c040001, 0x24845938, 0xafa00014, -0xafa20010, 0x8d86001c, 0x8f870124, 0x3c050008, -0xc002403, 0x34a50001, 0x8004057, 0x0, -0x8ee2724c, 0x24420001, 0x304207ff, 0x11a00006, -0xaee2724c, 0x8ee201d0, 0x2442ffff, 0xaee201d0, -0x8003f6b, 0x8ee201d0, 0x8ee201cc, 0x2442ffff, -0xaee201cc, 0x8ee201cc, 0x8ee201d8, 0x2442ffff, -0xaee201d8, 0x8004057, 0x8ee201d8, 0x8f420240, -0x104000e5, 0x0, 0x8ee20e1c, 0x24420001, -0x8004057, 0xaee20e1c, 0x9582001e, 0xad82001c, -0x8f420240, 0x10400072, 0x0, 0x8ee20e1c, -0x24420001, 0xaee20e1c, 0x8f430240, 0x43102b, -0x144000d5, 0x0, 0x8f830120, 0x27623800, -0x24660020, 0xc2102b, 0x50400001, 0x27663000, -0x8f820128, 0x10c20004, 0x0, 0x8f820124, -0x14c20007, 0x0, 0x8ee201a4, 0x8021, -0x24420001, 0xaee201a4, 0x8003fda, 0x8ee201a4, -0x8ee2724c, 0xac62001c, 0x8ee404a8, 0x8ee504ac, -0x2462001c, 0xac620008, 0x24020008, 0xa462000e, -0x24020011, 0xac620018, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400034, 0x24100001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c820000, 0x1455001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10540007, -0x0, 0x8ee24e34, 0x24420001, 0x10620005, -0x0, 0x8003fc6, 0x0, 0x14600005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400011, -0xac800000, 0x8003fda, 0x0, 0x8ee24e30, -0x24420001, 0x50540003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x24020001, 0xac950000, -0xac820004, 0x5600000b, 0x24100001, 0x8ee2724c, -0x3c040001, 0x248458a8, 0xafa00014, 0xafa20010, -0x8ee6724c, 0x8f470280, 0x3c050009, 0xc002403, -0x34a5f008, 0x56000001, 0xaee00e1c, 0x8ee20188, -0x24420001, 0xaee20188, 0x8004050, 0x8ee20188, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x8021, 0x24420001, 0xaee201a4, -0x8004044, 0x8ee201a4, 0x8ee2724c, 0xac62001c, -0x8ee404a8, 0x8ee504ac, 0x2462001c, 0xac620008, -0x24020008, 0xa462000e, 0x24020011, 0xac620018, -0xac640000, 0xac650004, 0x8ee204c4, 0xac620010, -0xaf860120, 0x92e24e20, 0x14400034, 0x24100001, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x8c820000, 0x1455001f, 0x0, 0x8ee34e30, -0x8ee24e34, 0x1062001b, 0x0, 0x8c820004, -0x24420001, 0xac820004, 0x8ee24e34, 0x8ee34e30, -0x24420001, 0x10540007, 0x0, 0x8ee24e34, -0x24420001, 0x10620005, 0x0, 0x8004030, -0x0, 0x14600005, 0x0, 0x8f820128, -0x24420020, 0xaf820128, 0x8f820128, 0x8c820004, -0x2c420011, 0x50400011, 0xac800000, 0x8004044, -0x0, 0x8ee24e30, 0x24420001, 0x50540003, -0x1021, 0x8ee24e30, 0x24420001, 0xaee24e30, -0x8ee24e30, 0x210c0, 0x24425038, 0x2e22021, -0x24020001, 0xac950000, 0xac820004, 0x1600000b, -0x0, 0x8ee2724c, 0x3c040001, 0x248458a8, -0xafa00014, 0xafa20010, 0x8ee6724c, 0x8f470280, -0x3c050009, 0xc002403, 0x34a5f008, 0x8ee20174, -0x24420001, 0xaee20174, 0x8004057, 0x8ee20174, -0x24020001, 0xaee24e24, 0x8f830128, 0x8f820124, -0x1462fd58, 0x0, 0x8fbf0030, 0x8fb5002c, -0x8fb40028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, -0x8fb00018, 0x3e00008, 0x27bd0038, 0x27bdffe8, -0x27840208, 0x27450200, 0x24060008, 0xafbf0014, -0xc00249a, 0xafb00010, 0x2021, 0x24100001, -0x2402241f, 0xaf900210, 0xaf900200, 0xaf800204, -0xaf820214, 0x8f460248, 0x24030004, 0x3c020040, -0x3c010001, 0xac235cc4, 0x3c010001, 0xac235cc8, -0x3c010001, 0xac205d9c, 0x3c010001, 0xac225cc0, -0x3c010001, 0xac235cc8, 0xc005108, 0x24050004, -0xc004822, 0x0, 0x8ee20000, 0x3c03feff, -0x3463fffd, 0x431024, 0xaee20000, 0x3c023c00, -0xaf82021c, 0x3c010001, 0x370821, 0xac3083ac, -0x8fbf0014, 0x8fb00010, 0x3e00008, 0x27bd0018, -0x27bdffe0, 0x3c050008, 0x34a50400, 0xafbf0018, -0xafa00010, 0xafa00014, 0x8f860200, 0x3c040001, -0x248459f0, 0xc002403, 0x3821, 0x8ee20280, -0x24420001, 0xaee20280, 0x8ee20280, 0x8f830200, -0x3c023f00, 0x621824, 0x8fbf0018, 0x3c020400, -0x3e00008, 0x27bd0020, 0x27bdffd8, 0xafbf0020, -0xafb1001c, 0xafb00018, 0x8f900220, 0x8ee20214, -0x3821, 0x24420001, 0xaee20214, 0x8ee20214, -0x3c020300, 0x2021024, 0x10400027, 0x3c110400, -0xc00429b, 0x0, 0x3c020100, 0x2021024, -0x10400007, 0x0, 0x8ee20218, 0x24420001, -0xaee20218, 0x8ee20218, 0x80040c6, 0x3c03fdff, -0x8ee2021c, 0x24420001, 0xaee2021c, 0x8ee2021c, -0x3c03fdff, 0x3463ffff, 0x3c0808ff, 0x3508ffff, -0x8ee20000, 0x3c040001, 0x248459fc, 0x3c050008, -0x2003021, 0x431024, 0xaee20000, 0x8f820220, -0x3821, 0x3c030300, 0x481024, 0x431025, -0xaf820220, 0xafa00010, 0xc002403, 0xafa00014, -0x8004296, 0x0, 0x2111024, 0x1040001f, -0x3c024000, 0x8f830224, 0x24021402, 0x1462000b, -0x3c03fdff, 0x3c040001, 0x24845a08, 0x3c050008, -0xafa00010, 0xafa00014, 0x8f860224, 0x34a5ffff, -0xc002403, 0x3821, 0x3c03fdff, 0x8ee20000, -0x3463ffff, 0x2002021, 0x431024, 0xc004e54, -0xaee20000, 0x8ee20220, 0x24420001, 0xaee20220, -0x8ee20220, 0x8f820220, 0x3c0308ff, 0x3463ffff, -0x431024, 0x8004295, 0x511025, 0x2021024, -0x10400142, 0x0, 0x8ee2022c, 0x24420001, -0xaee2022c, 0x8ee2022c, 0x8f820220, 0x3c0308ff, -0x3463ffff, 0x431024, 0x34420004, 0xaf820220, -0x8f830054, 0x8f820054, 0x800410e, 0x24630002, -0x8f820054, 0x621023, 0x2c420003, 0x1440fffc, -0x0, 0x8f8600e0, 0x8f8400e4, 0x30c20007, -0x10400012, 0x0, 0x8f8300e4, 0x2402fff8, -0xc21024, 0x1043000d, 0x0, 0x8f820054, -0x8f8300e0, 0x14c30009, 0x24440050, 0x8f820054, -0x821023, 0x2c420051, 0x10400004, 0x0, -0x8f8200e0, 0x10c2fff9, 0x0, 0x8f820220, -0x3c0308ff, 0x3463fffd, 0x431024, 0xaf820220, -0x8f8600e0, 0x30c20007, 0x10400003, 0x2402fff8, -0xc23024, 0xaf8600e0, 0x8f8300c4, 0x3c02001f, -0x3442ffff, 0x24680008, 0x48102b, 0x10400003, -0x3c02fff5, 0x34421000, 0x1024021, 0x8f8b00c8, -0x8f850120, 0x8f840124, 0x8004145, 0x6021, -0x27623800, 0x82102b, 0x50400001, 0x27643000, -0x10a40010, 0x318200ff, 0x8c820018, 0x38430007, -0x2c630001, 0x3842000b, 0x2c420001, 0x621825, -0x5060fff3, 0x24840020, 0x8ee20240, 0x240c0001, -0x24420001, 0xaee20240, 0x8ee20240, 0x8c8b0008, -0x318200ff, 0x14400065, 0x0, 0x3c020001, -0x571021, 0x904283c0, 0x14400060, 0x0, -0x8f8400e4, 0xc41023, 0x218c3, 0x4620001, -0x24630200, 0x8f8900c4, 0x10600005, 0x24020001, -0x10620009, 0x0, 0x8004187, 0x0, -0x8ee20230, 0x1205821, 0x24420001, 0xaee20230, -0x80041bc, 0x8ee20230, 0x8ee20234, 0x3c05000a, -0x24420001, 0xaee20234, 0x8c8b0000, 0x34a5f000, -0x8ee20234, 0x12b1823, 0xa3102b, 0x54400001, -0x651821, 0x2c62233f, 0x14400040, 0x0, -0x8f8200e8, 0x24420008, 0xaf8200e8, 0x8f8200e8, -0x8f8200e4, 0x1205821, 0x24420008, 0xaf8200e4, -0x80041bc, 0x8f8200e4, 0x8ee20238, 0x3c03000a, -0x24420001, 0xaee20238, 0x8c840000, 0x3463f000, -0x8ee20238, 0x883823, 0x67102b, 0x54400001, -0xe33821, 0x3c020003, 0x34420d40, 0x47102b, -0x10400003, 0x0, 0x80041bc, 0x805821, -0x8f8200e4, 0x24440008, 0xaf8400e4, 0x8f8400e4, -0x10860018, 0x3c05000a, 0x34a5f000, 0x3c0a0003, -0x354a0d40, 0x8ee2007c, 0x24420001, 0xaee2007c, -0x8c830000, 0x8ee2007c, 0x683823, 0xa7102b, -0x54400001, 0xe53821, 0x147102b, 0x54400007, -0x605821, 0x8f8200e4, 0x24440008, 0xaf8400e4, -0x8f8400e4, 0x1486ffef, 0x0, 0x14860005, -0x0, 0x1205821, 0xaf8600e4, 0x80041bc, -0xaf8600e8, 0xaf8400e4, 0xaf8400e8, 0x8f8200c8, -0x3c03000a, 0x3463f000, 0x483823, 0x67102b, -0x54400001, 0xe33821, 0x3c020003, 0x34420d3f, -0x47102b, 0x54400007, 0x6021, 0x1683823, -0x67102b, 0x54400003, 0xe33821, 0x80041cf, -0x3c020003, 0x3c020003, 0x34420d3f, 0x47102b, -0x14400016, 0x318200ff, 0x14400006, 0x0, -0x3c020001, 0x571021, 0x904283c0, 0x1040000f, -0x0, 0x8ee2023c, 0x3c04fdff, 0x8ee30000, -0x3484ffff, 0x24420001, 0xaee2023c, 0x8ee2023c, -0x24020001, 0x641824, 0x3c010001, 0x370821, -0xa02283b8, 0x800422c, 0xaee30000, 0xaf8b00c8, -0x8f8300c8, 0x8f8200c4, 0x3c04000a, 0x3484f000, -0x623823, 0x87102b, 0x54400001, 0xe43821, -0x3c020003, 0x34420d40, 0x47102b, 0x2ce30001, -0x431025, 0x10400008, 0x0, 0x8f820220, -0x3c0308ff, 0x3463ffff, 0x431024, 0x3c034000, -0x431025, 0xaf820220, 0x8f8600e0, 0x8f8400e4, -0x10c4002a, 0x0, 0x8ee2007c, 0x24420001, -0xaee2007c, 0x8ee2007c, 0x24c2fff8, 0xaf8200e0, -0x3c020001, 0x8c427e30, 0x3c030008, 0x8f8600e0, -0x431024, 0x1040001d, 0x0, 0x10c4001b, -0x240dfff8, 0x3c0a000a, 0x354af000, 0x3c0c0080, -0x24850008, 0x27622800, 0x50a20001, 0x27651800, -0x8c880004, 0x8c820000, 0x8ca90000, 0x3103ffff, -0x431021, 0x4d1024, 0x24430010, 0x6b102b, -0x54400001, 0x6a1821, 0x12b102b, 0x54400001, -0x12a4821, 0x10690002, 0x10c1025, 0xac820004, -0xa02021, 0x14c4ffeb, 0x24850008, 0x8f820220, -0x3c0308ff, 0x3463ffff, 0x431024, 0x34420002, -0xaf820220, 0x8f830054, 0x8f820054, 0x8004237, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x8f820220, 0x3c0308ff, -0x3463fffb, 0x431024, 0xaf820220, 0x6010055, -0x0, 0x8ee20228, 0x24420001, 0xaee20228, -0x8ee20228, 0x8f820220, 0x3c0308ff, 0x3463ffff, -0x431024, 0x34420004, 0xaf820220, 0x8f830054, -0x8f820054, 0x8004251, 0x24630002, 0x8f820054, -0x621023, 0x2c420003, 0x1440fffc, 0x0, -0x8f8600e0, 0x30c20007, 0x10400012, 0x0, -0x8f8300e4, 0x2402fff8, 0xc21024, 0x1043000d, -0x0, 0x8f820054, 0x8f8300e0, 0x14c30009, -0x24440032, 0x8f820054, 0x821023, 0x2c420033, -0x10400004, 0x0, 0x8f8200e0, 0x10c2fff9, -0x0, 0x8f820220, 0x3c0308ff, 0x3463fffd, -0x431024, 0xaf820220, 0x8f8600e0, 0x30c20007, -0x10400003, 0x2402fff8, 0xc23024, 0xaf8600e0, -0x240301f5, 0x8f8200e8, 0x673823, 0x718c0, -0x431021, 0xaf8200e8, 0x8f8200e8, 0xaf8200e4, -0x8ee2007c, 0x3c0408ff, 0x3484ffff, 0x471021, -0xaee2007c, 0x8f820220, 0x3c038000, 0x34630002, -0x441024, 0x431025, 0xaf820220, 0x8f830054, -0x8f820054, 0x800428d, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x8f820220, 0x3c0308ff, 0x3463fffb, 0x431024, -0xaf820220, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, -0x3e00008, 0x27bd0028, 0x3c020001, 0x8c425cd8, -0x27bdffd8, 0x10400012, 0xafbf0020, 0x3c040001, -0x24845a14, 0x3c050008, 0x24020001, 0x3c010001, -0x370821, 0xac2283ac, 0xafa00010, 0xafa00014, -0x8f860220, 0x34a50498, 0x3c010001, 0xac205cd8, -0x3c010001, 0xac225ccc, 0xc002403, 0x3821, -0x8f420268, 0x3c037fff, 0x3463ffff, 0x431024, -0xaf420268, 0x8ee204d0, 0x8ee404d4, 0x2403fffe, -0x431024, 0x30840002, 0x1080011e, 0xaee204d0, -0x8ee204d4, 0x2403fffd, 0x431024, 0xaee204d4, -0x8f820044, 0x3c030600, 0x34632000, 0x34420020, -0xaf820044, 0xafa30018, 0x8ee20608, 0x8f430228, -0x24420001, 0x304a00ff, 0x514300fe, 0xafa00010, -0x8ee20608, 0x210c0, 0x571021, 0x8fa30018, -0x8fa4001c, 0xac43060c, 0xac440610, 0x8f830054, -0x8f820054, 0x24690032, 0x1221023, 0x2c420033, -0x1040006a, 0x5821, 0x24180008, 0x240f000d, -0x240d0007, 0x240c0040, 0x240e0001, 0x8f870120, -0x27623800, 0x24e80020, 0x102102b, 0x50400001, -0x27683000, 0x8f820128, 0x11020004, 0x0, -0x8f820124, 0x15020007, 0x1021, 0x8ee201a4, -0x2821, 0x24420001, 0xaee201a4, 0x800433d, -0x8ee201a4, 0x8ee40608, 0x420c0, 0x801821, -0x8ee40430, 0x8ee50434, 0xa32821, 0xa3302b, -0x822021, 0x862021, 0xace40000, 0xace50004, -0x8ee20608, 0xa4f8000e, 0xacef0018, 0xacea001c, -0x210c0, 0x2442060c, 0x2e21021, 0xace20008, -0x8ee204c4, 0xace20010, 0xaf880120, 0x92e24e20, -0x14400033, 0x24050001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c820000, 0x144d001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x104c0007, -0x0, 0x8ee24e34, 0x24420001, 0x10620005, -0x0, 0x800432a, 0x0, 0x14600005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, -0xac800000, 0x800433d, 0x0, 0x8ee24e30, -0x24420001, 0x504c0003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0xac8d0000, 0xac8e0004, -0x54a00006, 0x240b0001, 0x8f820054, 0x1221023, -0x2c420033, 0x1440ff9d, 0x0, 0x316300ff, -0x24020001, 0x54620079, 0xafa00010, 0xaeea0608, -0x8f830054, 0x8f820054, 0x24690032, 0x1221023, -0x2c420033, 0x10400061, 0x5821, 0x240d0008, -0x240c0011, 0x24080012, 0x24070040, 0x240a0001, -0x8f830120, 0x27623800, 0x24660020, 0xc2102b, -0x50400001, 0x27663000, 0x8f820128, 0x10c20004, -0x0, 0x8f820124, 0x14c20007, 0x0, -0x8ee201a4, 0x2821, 0x24420001, 0xaee201a4, -0x80043a9, 0x8ee201a4, 0x8ee20608, 0xac62001c, -0x8ee404a0, 0x8ee504a4, 0x2462001c, 0xac620008, -0xa46d000e, 0xac6c0018, 0xac640000, 0xac650004, -0x8ee204c4, 0xac620010, 0xaf860120, 0x92e24e20, -0x14400033, 0x24050001, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0x8c820000, 0x1448001f, -0x0, 0x8ee34e30, 0x8ee24e34, 0x1062001b, -0x0, 0x8c820004, 0x24420001, 0xac820004, -0x8ee24e34, 0x8ee34e30, 0x24420001, 0x10470007, -0x0, 0x8ee24e34, 0x24420001, 0x10620005, -0x0, 0x8004396, 0x0, 0x14600005, -0x0, 0x8f820128, 0x24420020, 0xaf820128, -0x8f820128, 0x8c820004, 0x2c420011, 0x50400010, -0xac800000, 0x80043a9, 0x0, 0x8ee24e30, -0x24420001, 0x50470003, 0x1021, 0x8ee24e30, -0x24420001, 0xaee24e30, 0x8ee24e30, 0x210c0, -0x24425038, 0x2e22021, 0xac880000, 0xac8a0004, -0x54a00006, 0x240b0001, 0x8f820054, 0x1221023, -0x2c420033, 0x1440ffa6, 0x0, 0x316300ff, -0x24020001, 0x54620003, 0xafa00010, 0x80043d6, -0x0, 0x3c040001, 0x24845a20, 0xafa00014, -0x8f860120, 0x8f870124, 0x3c050009, 0xc002403, -0x34a5f011, 0x80043d6, 0x0, 0x3c040001, -0x24845a2c, 0xafa00014, 0x8f860120, 0x8f870124, -0x3c050009, 0xc002403, 0x34a5f010, 0x80043d6, -0x0, 0x3c040001, 0x24845a38, 0xafa00014, -0x8ee60608, 0x8f470228, 0x3c050009, 0xc002403, -0x34a5f00f, 0x8ee201ac, 0x24420001, 0xaee201ac, -0x8ee201ac, 0x8ee2015c, 0x24420001, 0xaee2015c, -0x8ee2015c, 0x8fbf0020, 0x3e00008, 0x27bd0028, -0x3c020001, 0x8c425cd8, 0x27bdffe0, 0x1440000d, -0xafbf0018, 0x3c040001, 0x24845a44, 0x3c050008, -0xafa00010, 0xafa00014, 0x8f860220, 0x34a50499, -0x24020001, 0x3c010001, 0xac225cd8, 0xc002403, -0x3821, 0x8ee204d0, 0x3c030001, 0x771821, -0x946383b2, 0x34420001, 0x10600007, 0xaee204d0, -0x8f820220, 0x3c0308ff, 0x3463ffff, 0x431024, -0x34420008, 0xaf820220, 0x2021, 0xc0052a2, -0x24050004, 0xaf420268, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x3c120001, -0x26521200, 0x3c140001, 0x8e945c50, 0x3c100001, -0x26101120, 0x3c15c000, 0x36b50060, 0x8e8a0000, -0x8eb30000, 0x26a400b, 0x248000a, 0x200f821, -0x0, 0xd, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x80014d6, -0x0, 0x80014d8, 0x3c0a0001, 0x80014d8, -0x3c0a0002, 0x80014d8, 0x0, 0x80024a6, -0x0, 0x80014d8, 0x3c0a0003, 0x80014d8, -0x3c0a0004, 0x8002f8c, 0x0, 0x80014d8, -0x3c0a0005, 0x8003ce8, 0x0, 0x8003c66, -0x0, 0x80014d8, 0x3c0a0006, 0x80014d8, -0x3c0a0007, 0x80014d8, 0x0, 0x80014d8, -0x0, 0x80014d8, 0x0, 0x8002a75, -0x0, 0x80014d8, 0x3c0a000b, 0x80014d8, -0x3c0a000c, 0x80014d8, 0x3c0a000d, 0x800237a, -0x0, 0x8002339, 0x0, 0x80014d8, -0x3c0a000e, 0x8001b3c, 0x0, 0x80024a4, -0x0, 0x80014d8, 0x3c0a000f, 0x80040a7, -0x0, 0x8004091, 0x0, 0x80014d8, -0x3c0a0010, 0x80014ee, 0x0, 0x80014d8, -0x3c0a0011, 0x80014d8, 0x3c0a0012, 0x80014d8, -0x3c0a0013, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x3c030001, -0x34633800, 0x24050080, 0x2404001f, 0x2406ffff, -0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, -0x3631021, 0xaf8200c0, 0x3631021, 0xaf8200c4, -0x3631021, 0xaf8200c8, 0x27623800, 0xaf8200d0, -0x27623800, 0xaf8200d4, 0x27623800, 0xaf8200d8, -0x27621800, 0xaf8200e0, 0x27621800, 0xaf8200e4, -0x27621800, 0xaf8200e8, 0x27621000, 0xaf8200f0, -0x27621000, 0xaf8200f4, 0x27621000, 0xaf8200f8, -0xaca00000, 0x2484ffff, 0x1486fffd, 0x24a50004, -0x8f830040, 0x3c02f000, 0x621824, 0x3c025000, -0x1062000c, 0x43102b, 0x14400006, 0x3c026000, -0x3c024000, 0x10620008, 0x24020800, 0x8004539, -0x0, 0x10620004, 0x24020800, 0x8004539, -0x0, 0x24020700, 0x3c010001, 0xac225cdc, -0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, -0xafb00020, 0x8f830054, 0x8f820054, 0x3c010001, -0xac205cc4, 0x8004545, 0x24630064, 0x8f820054, -0x621023, 0x2c420065, 0x1440fffc, 0x0, -0xc004d71, 0x0, 0x24040001, 0x2821, -0x27a60018, 0x34028000, 0xc00498e, 0xa7a20018, -0x8f830054, 0x8f820054, 0x8004556, 0x24630064, -0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, -0x24040001, 0x24050001, 0xc00494c, 0x27a60018, -0x8f830054, 0x8f820054, 0x8004562, 0x24630064, -0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, -0x24040001, 0x24050001, 0xc00494c, 0x27a60018, -0x8f830054, 0x8f820054, 0x800456e, 0x24630064, -0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, -0x24040001, 0x3c060001, 0x24c65da0, 0xc00494c, -0x24050002, 0x8f830054, 0x8f820054, 0x800457b, -0x24630064, 0x8f820054, 0x621023, 0x2c420065, -0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, -0x26105da2, 0xc00494c, 0x2003021, 0x97a60018, -0x3c070001, 0x94e75da0, 0x3c040001, 0x24845ab0, -0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, -0xc002403, 0xafa20010, 0x97a20018, 0x1040004c, -0x24036040, 0x96020000, 0x3042fff0, 0x1443000a, -0x24020020, 0x3c030001, 0x94635da0, 0x54620009, -0x24027830, 0x24020003, 0x3c010001, 0xac225cc4, -0x80045ac, 0x24020005, 0x3c030001, 0x94635da0, -0x24027830, 0x1462000f, 0x24030010, 0x3c020001, -0x94425da2, 0x3042fff0, 0x1443000a, 0x24020003, -0x3c010001, 0xac225cc4, 0x24020006, 0x3c010001, -0xac225db0, 0x3c010001, 0xac225dbc, 0x80045e6, -0x3c09fff0, 0x3c020001, 0x8c425cc4, 0x3c030001, -0x94635da0, 0x34420001, 0x3c010001, 0xac225cc4, -0x24020015, 0x1462000f, 0x0, 0x3c020001, -0x94425da2, 0x3042fff0, 0x3843f420, 0x2c630001, -0x3842f430, 0x2c420001, 0x621825, 0x10600005, -0x24020003, 0x3c010001, 0xac225dbc, 0x80045e6, -0x3c09fff0, 0x3c030001, 0x94635da0, 0x24027810, -0x1462000b, 0x24020002, 0x3c020001, 0x94425da2, -0x3042fff0, 0x14400006, 0x24020002, 0x24020004, -0x3c010001, 0xac225dbc, 0x80045e6, 0x3c09fff0, -0x3c010001, 0xac225dbc, 0x80045e6, 0x3c09fff0, -0x3c020001, 0x8c425cc4, 0x24030001, 0x3c010001, -0xac235dbc, 0x34420004, 0x3c010001, 0xac225cc4, -0x3c09fff0, 0x3529bdc0, 0x3c060001, 0x8cc65cc4, -0x3c040001, 0x24845ab0, 0x24020001, 0x3c010001, -0xac225ccc, 0x8f820054, 0x3c070001, 0x8ce75dbc, -0x3c030001, 0x94635da0, 0x3c080001, 0x95085da2, -0x3c05000d, 0x34a50100, 0x3c010001, 0xac205cc8, -0x491021, 0x3c010001, 0xac225dac, 0xafa30010, -0xc002403, 0xafa80014, 0x8fbf0024, 0x8fb00020, -0x3e00008, 0x27bd0028, 0x27bdffe8, 0x3c050001, -0x8ca55cc8, 0x24060004, 0x24020001, 0x14a20014, -0xafbf0010, 0x3c020001, 0x8c427e3c, 0x30428000, -0x10400005, 0x3c04000f, 0x3c030001, 0x8c635dbc, -0x8004617, 0x34844240, 0x3c040004, 0x3c030001, -0x8c635dbc, 0x348493e0, 0x24020005, 0x14620016, -0x0, 0x3c04003d, 0x800462f, 0x34840900, -0x3c020001, 0x8c427e38, 0x30428000, 0x10400005, -0x3c04001e, 0x3c030001, 0x8c635dbc, 0x800462a, -0x34848480, 0x3c04000f, 0x3c030001, 0x8c635dbc, -0x34844240, 0x24020005, 0x14620003, 0x0, -0x3c04007a, 0x34841200, 0x3c020001, 0x8c425dac, -0x8f830054, 0x441021, 0x431023, 0x44102b, -0x14400037, 0x0, 0x3c020001, 0x8c425cd0, -0x14400033, 0x0, 0x3c010001, 0x10c00025, -0xac205ce0, 0x3c090001, 0x8d295cc4, 0x24070001, -0x3c044000, 0x3c080001, 0x25087e3c, 0x250afffc, -0x52842, 0x14a00002, 0x24c6ffff, 0x24050008, -0xa91024, 0x10400010, 0x0, 0x14a70008, -0x0, 0x8d020000, 0x441024, 0x1040000a, -0x0, 0x3c010001, 0x800465b, 0xac255ce0, -0x8d420000, 0x441024, 0x10400003, 0x0, -0x3c010001, 0xac275ce0, 0x3c020001, 0x8c425ce0, -0x6182b, 0x2c420001, 0x431024, 0x5440ffe5, -0x52842, 0x8f820054, 0x3c030001, 0x8c635ce0, -0x3c010001, 0xac225dac, 0x1060002a, 0x24020001, -0x3c010001, 0xac255cc8, 0x3c010001, 0xac225ccc, -0x3c020001, 0x8c425ce0, 0x10400022, 0x0, -0x3c020001, 0x8c425ccc, 0x1040000a, 0x24020001, -0x3c010001, 0xac205ccc, 0x3c010001, 0x370821, -0xac2283ac, 0x3c010001, 0xac205d4c, 0x3c010001, -0xac225d04, 0x3c030001, 0x771821, 0x8c6383ac, -0x24020008, 0x10620005, 0x24020001, 0xc004695, -0x0, 0x8004692, 0x0, 0x3c030001, -0x8c635cc8, 0x10620007, 0x2402000e, 0x3c030001, -0x8c637dd0, 0x10620003, 0x0, 0xc004e54, -0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, -0x27bdffe0, 0x3c02fdff, 0xafbf0018, 0x8ee30000, -0x3c050001, 0x8ca55cc8, 0x3c040001, 0x8c845cf0, -0x3442ffff, 0x621824, 0x14a40008, 0xaee30000, -0x3c030001, 0x771821, 0x8c6383ac, 0x3c020001, -0x8c425cf4, 0x10620008, 0x0, 0x3c020001, -0x571021, 0x8c4283ac, 0x3c010001, 0xac255cf0, -0x3c010001, 0xac225cf4, 0x3c030001, 0x8c635cc8, -0x24020002, 0x10620169, 0x2c620003, 0x10400005, -0x24020001, 0x10620008, 0x0, 0x800481c, -0x0, 0x24020004, 0x106200b1, 0x24020001, -0x800481d, 0x0, 0x3c020001, 0x571021, -0x8c4283ac, 0x2443ffff, 0x2c620008, 0x1040015a, -0x31080, 0x3c010001, 0x220821, 0x8c225ac8, -0x400008, 0x0, 0x3c030001, 0x8c635dbc, -0x24020005, 0x14620014, 0x0, 0x3c020001, -0x8c425cd4, 0x1040000a, 0x24020003, 0xc004822, -0x0, 0x24020002, 0x3c010001, 0x370821, -0xac2283ac, 0x3c010001, 0x80046e0, 0xac205cd4, -0x3c010001, 0x370821, 0xac2283ac, 0x3c010001, -0x800481f, 0xac205c60, 0xc004822, 0x0, -0x3c020001, 0x8c425cd4, 0x3c010001, 0xac205c60, -0x104000dd, 0x24020002, 0x3c010001, 0x370821, -0xac2283ac, 0x3c010001, 0x800481f, 0xac205cd4, -0x3c030001, 0x8c635dbc, 0x24020005, 0x14620003, -0x24020001, 0x3c010001, 0xac225d00, 0xc0049cf, -0x0, 0x3c030001, 0x8c635d00, 0x800478e, -0x24020011, 0x3c050001, 0x8ca55cc8, 0x3c060001, -0x8cc67e3c, 0xc005108, 0x2021, 0x24020005, -0x3c010001, 0xac205cd4, 0x3c010001, 0x370821, -0x800481f, 0xac2283ac, 0x3c040001, 0x24845abc, -0x3c05000f, 0x34a50100, 0x3021, 0x3821, -0xafa00010, 0xc002403, 0xafa00014, 0x800481f, -0x0, 0x8f820220, 0x3c03f700, 0x431025, -0x80047b7, 0xaf820220, 0x8f820220, 0x3c030004, -0x431024, 0x144000a9, 0x24020007, 0x8f830054, -0x3c020001, 0x8c425da4, 0x2463d8f0, 0x431023, -0x2c422710, 0x144000f8, 0x24020001, 0x800481d, -0x0, 0x3c050001, 0x8ca55cc8, 0xc0052a2, -0x2021, 0xc005386, 0x2021, 0x3c030001, -0x8c637e34, 0x46100ea, 0x24020001, 0x3c020008, -0x621024, 0x10400006, 0x0, 0x8f820214, -0x3c03ffff, 0x431024, 0x8004741, 0x3442251f, -0x8f820214, 0x3c03ffff, 0x431024, 0x3442241f, -0xaf820214, 0x8ee20000, 0x3c030200, 0x431025, -0xaee20000, 0x8f820220, 0x2403fffb, 0x431024, -0xaf820220, 0x8f820220, 0x34420002, 0xaf820220, -0x24020008, 0x3c010001, 0x370821, 0xac2283ac, -0x8f820220, 0x3c030004, 0x431024, 0x14400005, -0x0, 0x8f820220, 0x3c03f700, 0x431025, -0xaf820220, 0x3c030001, 0x8c635dbc, 0x24020005, -0x1462000a, 0x0, 0x3c020001, 0x94425da2, -0x24429fbc, 0x2c420004, 0x10400004, 0x24040018, -0x24050002, 0xc004d93, 0x24060020, 0xc0043dd, -0x0, 0x3c010001, 0x800481f, 0xac205d50, -0x3c020001, 0x571021, 0x8c4283ac, 0x2443ffff, -0x2c620008, 0x104000ac, 0x31080, 0x3c010001, -0x220821, 0x8c225ae8, 0x400008, 0x0, -0xc00429b, 0x0, 0x3c010001, 0xac205ccc, -0xaf800204, 0x3c010001, 0xc004822, 0xac207e20, -0x24020001, 0x3c010001, 0xac225ce4, 0x24020002, -0x3c010001, 0x370821, 0x800481f, 0xac2283ac, -0xc00489f, 0x0, 0x3c030001, 0x8c635ce4, -0x24020009, 0x14620090, 0x24020003, 0x3c010001, -0x370821, 0x800481f, 0xac2283ac, 0x3c020001, -0x8c427e38, 0x30424000, 0x10400005, 0x0, -0x8f820044, 0x3c03ffff, 0x800479f, 0x34637fff, -0x8f820044, 0x2403ff7f, 0x431024, 0xaf820044, -0x8f830054, 0x80047b9, 0x24020004, 0x8f830054, -0x3c020001, 0x8c425da4, 0x2463d8f0, 0x431023, -0x2c422710, 0x14400074, 0x24020005, 0x3c010001, -0x370821, 0x800481f, 0xac2283ac, 0x8f820220, -0x3c03f700, 0x431025, 0xaf820220, 0xaf800204, -0x3c010001, 0xac207e20, 0x8f830054, 0x24020006, -0x3c010001, 0x370821, 0xac2283ac, 0x3c010001, -0x800481f, 0xac235da4, 0x8f830054, 0x3c020001, -0x8c425da4, 0x2463fff6, 0x431023, 0x2c42000a, -0x14400059, 0x0, 0x24020007, 0x3c010001, -0x370821, 0x800481f, 0xac2283ac, 0x8f820220, -0x3c04f700, 0x441025, 0xaf820220, 0x8f820220, -0x3c030300, 0x431024, 0x14400005, 0x1821, -0x8f820220, 0x24030001, 0x441025, 0xaf820220, -0x10600043, 0x24020001, 0x8f820214, 0x3c03ffff, -0x3c040001, 0x8c845d98, 0x431024, 0x3442251f, -0xaf820214, 0x24020008, 0x3c010001, 0x370821, -0x1080000b, 0xac2283ac, 0x3c020001, 0x8c425d74, -0x14400007, 0x24020001, 0x3c010001, 0xac227dd0, -0xc004e54, 0x8f840220, 0x800480c, 0x0, -0x8f820220, 0x3c030008, 0x431024, 0x14400017, -0x2402000e, 0x3c010001, 0xac227dd0, 0x8ee20000, -0x2021, 0x3c030200, 0x431025, 0xc005386, -0xaee20000, 0x8f820220, 0x2403fffb, 0x431024, -0xaf820220, 0x8f820220, 0x34420002, 0xc0043dd, -0xaf820220, 0x3c050001, 0x8ca55cc8, 0xc0052a2, -0x2021, 0x800481f, 0x0, 0x3c020001, -0x8c425d74, 0x10400010, 0x0, 0x3c020001, -0x8c425d70, 0x2442ffff, 0x3c010001, 0xac225d70, -0x14400009, 0x24020002, 0x3c010001, 0xac205d74, -0x3c010001, 0x800481f, 0xac225d70, 0x24020001, -0x3c010001, 0xac225ccc, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, -0x34420004, 0xaf820220, 0x8f820200, 0x3c060001, -0x8cc65cc8, 0x34420004, 0xaf820200, 0x24020002, -0x10c2003a, 0x2cc20003, 0x10400005, 0x24020001, -0x10c20008, 0x0, 0x8004868, 0x0, -0x24020004, 0x10c20013, 0x24020001, 0x8004868, -0x0, 0x3c030001, 0x8c635cb8, 0x3c020001, -0x8c425cc0, 0x3c040001, 0x8c845cdc, 0x3c050001, -0x8ca55cbc, 0xaf860200, 0xaf860220, 0x34630022, -0x441025, 0x451025, 0x34420002, 0x8004867, -0xaf830200, 0x3c030001, 0x8c635d98, 0xaf820200, -0x10600009, 0xaf820220, 0x3c020001, 0x8c425d74, -0x14400005, 0x3c033f00, 0x3c020001, 0x8c425cb0, -0x800485b, 0x346300e0, 0x3c020001, 0x8c425cb0, -0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, -0x3c030001, 0x8c635cb4, 0x3c04f700, 0x3c020001, -0x8c425cc0, 0x3c050001, 0x8ca55cdc, 0x641825, -0x431025, 0x451025, 0xaf820220, 0x3e00008, -0x0, 0x8f820220, 0x3c030001, 0x8c635cc8, -0x34420004, 0xaf820220, 0x24020001, 0x1062000f, -0x0, 0x8f830054, 0x8f820054, 0x24630002, -0x621023, 0x2c420003, 0x10400011, 0x0, -0x8f820054, 0x621023, 0x2c420003, 0x1040000c, -0x0, 0x8004879, 0x0, 0x8f830054, -0x8f820054, 0x8004885, 0x24630007, 0x8f820054, -0x621023, 0x2c420008, 0x1440fffc, 0x0, -0x8f8400e0, 0x30820007, 0x1040000d, 0x0, -0x8f820054, 0x8f8300e0, 0x14830009, 0x24450032, -0x8f820054, 0xa21023, 0x2c420033, 0x10400004, -0x0, 0x8f8200e0, 0x1082fff9, 0x0, -0x8f820220, 0x2403fffd, 0x431024, 0xaf820220, -0x3e00008, 0x0, 0x3c030001, 0x8c635ce4, -0x3c020001, 0x8c425ce8, 0x50620004, 0x2463ffff, -0x3c010001, 0xac235ce8, 0x2463ffff, 0x2c620009, -0x1040009d, 0x31080, 0x3c010001, 0x220821, -0x8c225b08, 0x400008, 0x0, 0x8f820044, -0x34428080, 0xaf820044, 0x8f830054, 0x8004938, -0x24020002, 0x8f830054, 0x3c020001, 0x8c425da8, -0x2463d8f0, 0x431023, 0x2c422710, 0x1440008a, -0x24020003, 0x8004945, 0x0, 0x8f820044, -0x3c03ffff, 0x34637fff, 0x431024, 0xaf820044, -0x8f830054, 0x8004938, 0x24020004, 0x8f830054, -0x3c020001, 0x8c425da8, 0x2463fff6, 0x431023, -0x2c42000a, 0x14400078, 0x24020005, 0x8004945, -0x0, 0x8f820220, 0x3c03f700, 0x431025, -0xaf820220, 0x8f820220, 0x2403fffb, 0x431024, -0xaf820220, 0x8f820220, 0x34420002, 0xaf820220, -0x3c023f00, 0x344200e0, 0xaf820200, 0x8f820200, -0x2403fffd, 0x431024, 0xaf820200, 0x24040001, -0x3405ffff, 0xaf840204, 0x8f830054, 0x8f820054, -0x80048ec, 0x24630001, 0x8f820054, 0x621023, -0x2c420002, 0x1440fffc, 0x0, 0x8f820224, -0x42040, 0xa4102b, 0x1040fff2, 0x0, -0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, -0x8f820214, 0x3c03ffff, 0x431024, 0x3442251f, -0xaf820214, 0x8f820220, 0x2403fffb, 0x431024, -0xaf820220, 0x8f820220, 0x3c04f700, 0x34840008, -0x34420002, 0xaf820220, 0x8f820220, 0x3c033f00, -0x346300e2, 0x441025, 0xaf820220, 0xaf830200, -0x8f8400f0, 0x276217f8, 0x14820002, 0x24850008, -0x27651000, 0x8f8200f4, 0x10a20007, 0x3c038000, -0x34630040, 0x3c020001, 0x24425c70, 0xac820000, -0xac830004, 0xaf8500f0, 0x8f830054, 0x8004938, -0x24020006, 0x8f830054, 0x3c020001, 0x8c425da8, -0x2463fff6, 0x431023, 0x2c42000a, 0x14400022, -0x24020007, 0x8004945, 0x0, 0x8f8200e0, -0xaf8200e4, 0x8f8200e0, 0xaf8200e8, 0x8f820220, -0x34420004, 0xaf820220, 0x8f820220, 0x2403fff7, -0x431024, 0xaf820220, 0x8f820044, 0x34428080, -0xaf820044, 0x8f830054, 0x24020008, 0x3c010001, -0xac225ce4, 0x3c010001, 0x8004947, 0xac235da8, -0x8f830054, 0x3c020001, 0x8c425da8, 0x2463d8f0, -0x431023, 0x2c422710, 0x14400003, 0x24020009, -0x3c010001, 0xac225ce4, 0x3e00008, 0x0, -0x0, 0x0, 0x0, 0x27bdffd8, -0xafb20018, 0x809021, 0xafb3001c, 0xa09821, -0xafb10014, 0xc08821, 0xafb00010, 0x8021, -0xafbf0020, 0xa6200000, 0xc004d4b, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d4b, 0x2021, 0xc004d4b, 0x24040001, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x24100010, 0x2501024, 0x10400002, 0x2021, -0x24040001, 0xc004d4b, 0x108042, 0x1600fffa, -0x2501024, 0x24100010, 0x2701024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x2701024, 0xc004d71, 0x34108000, -0xc004d71, 0x0, 0xc004d2b, 0x0, -0x50400005, 0x108042, 0x96220000, 0x501025, -0xa6220000, 0x108042, 0x1600fff7, 0x0, -0xc004d71, 0x0, 0x8fbf0020, 0x8fb3001c, -0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, -0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, -0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, -0xafb00010, 0x8021, 0xafbf0020, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0x24100010, 0x2301024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x2301024, 0x24100010, 0x2501024, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x2501024, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0x34108000, -0x96620000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, -0x0, 0xc004d71, 0x0, 0x8fbf0020, -0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, -0x3e00008, 0x27bd0028, 0x3c030001, 0x8c635d00, -0x3c020001, 0x8c425d48, 0x27bdffd8, 0xafbf0020, -0xafb1001c, 0x10620003, 0xafb00018, 0x3c010001, -0xac235d48, 0x2463ffff, 0x2c620013, 0x10400349, -0x31080, 0x3c010001, 0x220821, 0x8c225b30, -0x400008, 0x0, 0xc004d71, 0x8021, -0x34028000, 0xa7a20010, 0x27b10010, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0xc004d4b, -0x2021, 0x108042, 0x1600fffc, 0x0, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fff8, 0x0, 0xc004d71, 0x0, -0x8004d24, 0x24020002, 0x27b10010, 0xa7a00010, -0x8021, 0xc004d4b, 0x24040001, 0x26100001, -0x2e020020, 0x1440fffb, 0x0, 0xc004d4b, -0x2021, 0xc004d4b, 0x24040001, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0x24100010, -0x32020001, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020001, -0x24100010, 0xc004d4b, 0x2021, 0x108042, -0x1600fffc, 0x0, 0xc004d71, 0x34108000, -0xc004d71, 0x0, 0xc004d2b, 0x0, -0x50400005, 0x108042, 0x96220000, 0x501025, -0xa6220000, 0x108042, 0x1600fff7, 0x0, -0xc004d71, 0x0, 0x97a20010, 0x30428000, -0x144002dc, 0x24020003, 0x8004d24, 0x0, -0x24021200, 0xa7a20010, 0x27b10010, 0x8021, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0xc004d4b, 0x2021, 0x108042, 0x1600fffc, -0x0, 0xc004d4b, 0x24040001, 0xc004d4b, -0x2021, 0x34108000, 0x96220000, 0x501024, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fff8, 0x0, 0xc004d71, -0x0, 0x8f830054, 0x8004d16, 0x24020004, -0x8f830054, 0x3c020001, 0x8c425db8, 0x2463ff9c, -0x431023, 0x2c420064, 0x1440029e, 0x24020002, -0x3c030001, 0x8c635dbc, 0x10620297, 0x2c620003, -0x14400296, 0x24020011, 0x24020003, 0x10620005, -0x24020004, 0x10620291, 0x2402000f, 0x8004d24, -0x24020011, 0x8004d24, 0x24020005, 0x24020014, -0xa7a20010, 0x27b10010, 0x8021, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x32020012, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020012, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0x34108000, -0x96220000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, -0x0, 0xc004d71, 0x0, 0x8f830054, -0x8004d16, 0x24020006, 0x8f830054, 0x3c020001, -0x8c425db8, 0x2463ff9c, 0x431023, 0x2c420064, -0x14400250, 0x24020007, 0x8004d24, 0x0, -0x24020006, 0xa7a20010, 0x27b10010, 0x8021, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020013, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020013, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fff8, 0x0, 0xc004d71, 0x0, -0x8f830054, 0x8004d16, 0x24020008, 0x8f830054, -0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, -0x2c420064, 0x1440020f, 0x24020009, 0x8004d24, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, -0xc004d4b, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, -0xc004d71, 0x34108000, 0xc004d71, 0x0, -0xc004d2b, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004d71, 0x8021, -0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fff8, 0x0, 0xc004d71, 0x0, -0x8f830054, 0x8004d16, 0x2402000a, 0x8f830054, -0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, -0x2c420064, 0x1440019b, 0x2402000b, 0x8004d24, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, -0xc004d4b, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020017, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020017, -0xc004d71, 0x34108000, 0xc004d71, 0x0, -0xc004d2b, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004d71, 0x8021, -0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020017, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020017, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fff8, 0x0, 0xc004d71, 0x0, -0x8f830054, 0x8004d16, 0x2402000c, 0x8f830054, -0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, -0x2c420064, 0x14400127, 0x24020012, 0x8004d24, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, -0xc004d4b, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020014, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020014, -0xc004d71, 0x34108000, 0xc004d71, 0x0, -0xc004d2b, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004d71, 0x8021, -0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020014, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020014, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fff8, 0x0, 0xc004d71, 0x0, -0x8f830054, 0x8004d16, 0x24020013, 0x8f830054, -0x3c020001, 0x8c425db8, 0x2463ff9c, 0x431023, -0x2c420064, 0x144000b3, 0x2402000d, 0x8004d24, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x24040001, -0xc004d4b, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, -0xc004d71, 0x34108000, 0xc004d71, 0x0, -0xc004d2b, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004d71, 0x8021, -0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, -0xc004d4b, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0xc004d4b, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d4b, 0x108042, 0x1600fffa, 0x32020018, -0xc004d4b, 0x24040001, 0xc004d4b, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fff8, 0x0, 0xc004d71, 0x0, -0x8f830054, 0x8004d16, 0x2402000e, 0x24020840, -0xa7a20010, 0x27b10010, 0x8021, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x32020013, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x32020013, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0x34108000, -0x96220000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, -0x0, 0xc004d71, 0x0, 0x8f830054, -0x24020010, 0x3c010001, 0xac225d00, 0x3c010001, -0x8004d26, 0xac235db8, 0x8f830054, 0x3c020001, -0x8c425db8, 0x2463ff9c, 0x431023, 0x2c420064, -0x14400004, 0x0, 0x24020011, 0x3c010001, -0xac225d00, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, -0x3e00008, 0x27bd0028, 0x8f850044, 0x8f820044, -0x3c030001, 0x431025, 0x3c030008, 0xaf820044, -0x8f840054, 0x8f820054, 0xa32824, 0x8004d37, -0x24840001, 0x8f820054, 0x821023, 0x2c420002, -0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, -0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, -0x8f820054, 0x8004d45, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x3e00008, 0xa01021, 0x8f830044, 0x3c02fff0, -0x3442ffff, 0x42480, 0x621824, 0x3c020002, -0x822025, 0x641825, 0xaf830044, 0x8f820044, -0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, -0x8f830054, 0x8f820054, 0x8004d5e, 0x24630001, -0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, -0x0, 0x8f820044, 0x3c030001, 0x431025, -0xaf820044, 0x8f830054, 0x8f820054, 0x8004d6b, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x3e00008, 0x0, -0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, -0xaf820044, 0x8f820044, 0x3c030001, 0x431025, -0xaf820044, 0x8f830054, 0x8f820054, 0x8004d7f, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, -0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, -0x8f820054, 0x8004d8d, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, -0x809821, 0xafb5002c, 0xa0a821, 0xafb20020, -0xc09021, 0x32a2ffff, 0xafbf0030, 0xafb40028, -0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, -0x3271ffff, 0x27b20010, 0x8021, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x2301024, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x2301024, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0x34108000, -0x96420000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d4b, 0x108042, 0x12000075, -0x0, 0x8004dc9, 0x0, 0x3274ffff, -0x27b10010, 0xa7a00010, 0x8021, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x24040001, 0xc004d4b, -0x2021, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x2901024, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x2901024, 0xc004d71, -0x34108000, 0xc004d71, 0x0, 0xc004d2b, -0x0, 0x50400005, 0x108042, 0x96220000, -0x501025, 0xa6220000, 0x108042, 0x1600fff7, -0x0, 0xc004d71, 0x0, 0x32a5ffff, -0x24020001, 0x54a20004, 0x24020002, 0x97a20010, -0x8004e14, 0x521025, 0x14a20006, 0x3271ffff, -0x97a20010, 0x121827, 0x431024, 0xa7a20010, -0x3271ffff, 0x27b20010, 0x8021, 0xc004d4b, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0xc004d4b, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d4b, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x2301024, -0x10400002, 0x2021, 0x24040001, 0xc004d4b, -0x108042, 0x1600fffa, 0x2301024, 0xc004d4b, -0x24040001, 0xc004d4b, 0x2021, 0x34108000, -0x96420000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d4b, 0x108042, 0x1600fff8, -0x0, 0xc004d71, 0x0, 0x8fbf0030, -0x8fb5002c, 0x8fb40028, 0x8fb30024, 0x8fb20020, -0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, -0x0, 0x0, 0x0, 0x27bdffe8, -0xafbf0010, 0x3c030001, 0x771821, 0x8c6383ac, -0x24020008, 0x1462022c, 0x803021, 0x3c020001, -0x8c425d98, 0x14400033, 0x0, 0x8f850224, -0x38a30020, 0x2c630001, 0x38a20010, 0x2c420001, -0x621825, 0x1460000d, 0x38a30030, 0x2c630001, -0x38a20400, 0x2c420001, 0x621825, 0x14600007, -0x38a30402, 0x2c630001, 0x38a20404, 0x2c420001, -0x621825, 0x10600005, 0x0, 0xc00429b, -0x0, 0x8004e8d, 0x2402000e, 0xc0043dd, -0x0, 0x3c050001, 0x8ca55cc8, 0xc0052a2, -0x2021, 0x3c030001, 0x8c635cc8, 0x24020004, -0x14620005, 0x2403fffb, 0x3c020001, 0x8c425cc4, -0x8004e89, 0x2403fff7, 0x3c020001, 0x8c425cc4, -0x431024, 0x3c010001, 0xac225cc4, 0x2402000e, -0x3c010001, 0xc00429b, 0xac227dd0, 0x8005087, -0x0, 0x8f820220, 0x3c030400, 0x431024, -0x10400027, 0x2403ffbf, 0x8f850224, 0x3c020001, -0x8c427ddc, 0xa32024, 0x431024, 0x1482000c, -0x0, 0x3c020001, 0x8c427de0, 0x24420001, -0x3c010001, 0xac227de0, 0x2c420002, 0x14400008, -0x24020001, 0x3c010001, 0x8004ead, 0xac227e00, -0x3c010001, 0xac207de0, 0x3c010001, 0xac207e00, -0x3c020001, 0x8c427e00, 0x10400006, 0x30a20040, -0x10400004, 0x24020001, 0x3c010001, 0x8004eb8, -0xac227e04, 0x3c010001, 0xac207e04, 0x3c010001, -0xac257ddc, 0x3c010001, 0x8004ec8, 0xac207e10, -0x24020001, 0x3c010001, 0xac227e10, 0x3c010001, -0xac207e00, 0x3c010001, 0xac207de0, 0x3c010001, -0xac207e04, 0x3c010001, 0xac207ddc, 0x3c030001, -0x8c637dd0, 0x3c020001, 0x8c427dd4, 0x10620003, -0x3c020200, 0x3c010001, 0xac237dd4, 0xc21024, -0x10400007, 0x2463ffff, 0x8f820220, 0x24030001, -0x3c010001, 0xac235ccc, 0x8005085, 0x3c03f700, -0x2c62000e, 0x104001a8, 0x31080, 0x3c010001, -0x220821, 0x8c225b80, 0x400008, 0x0, -0x3c010001, 0xac207e00, 0x3c010001, 0xac207de0, -0x3c010001, 0xac207ddc, 0x3c010001, 0xac207e04, -0x3c010001, 0xac207df8, 0x3c010001, 0xac207df0, -0xc00486a, 0xaf800224, 0x24020002, 0x3c010001, -0xac227dd0, 0x3c020001, 0x8c427e10, 0x14400056, -0x3c03fdff, 0x8ee20000, 0x3463ffff, 0x431024, -0xc00429b, 0xaee20000, 0xaf800204, 0x8f820200, -0x2403fffd, 0x431024, 0xaf820200, 0x3c010001, -0xac207e20, 0x8f830054, 0x3c020001, 0x8c427df8, -0x24040001, 0x3c010001, 0xac247e0c, 0x24420001, -0x3c010001, 0xac227df8, 0x2c420004, 0x3c010001, -0xac237df4, 0x14400006, 0x24020003, 0x3c010001, -0xac245ccc, 0x3c010001, 0x8005083, 0xac207df8, -0x3c010001, 0x8005083, 0xac227dd0, 0x8f830054, -0x3c020001, 0x8c427df4, 0x2463d8f0, 0x431023, -0x2c422710, 0x14400003, 0x24020004, 0x3c010001, -0xac227dd0, 0x3c020001, 0x8c427e10, 0x14400026, -0x3c03fdff, 0x8ee20000, 0x3463ffff, 0x431024, -0x8005083, 0xaee20000, 0x3c040001, 0x8c845d9c, -0x3c010001, 0xc00508a, 0xac207de8, 0x3c020001, -0x8c427e1c, 0xaf820204, 0x3c020001, 0x8c427e10, -0x14400015, 0x3c03fdff, 0x8ee20000, 0x3463ffff, -0x431024, 0xaee20000, 0x8f820204, 0x30420030, -0x1440013c, 0x24020002, 0x3c030001, 0x8c637e1c, -0x24020005, 0x3c010001, 0xac227dd0, 0x3c010001, -0x8005083, 0xac237e20, 0x3c020001, 0x8c427e10, -0x10400010, 0x3c03fdff, 0x3c020001, 0x8c425d6c, -0x24420001, 0x3c010001, 0xac225d6c, 0x2c420002, -0x14400131, 0x24020001, 0x3c010001, 0xac225d74, -0x3c010001, 0xac205d6c, 0x3c010001, 0x8005083, -0xac225ccc, 0x8ee20000, 0x3463ffff, 0x431024, -0xaee20000, 0x3c020001, 0x8c427e00, 0x10400122, -0x0, 0x3c020001, 0x8c427ddc, 0x1040011e, -0x0, 0x3c010001, 0xac227e08, 0x24020003, -0x3c010001, 0xac227de0, 0x8005024, 0x24020006, -0x3c010001, 0xac207de8, 0x8f820204, 0x34420040, -0xaf820204, 0x3c020001, 0x8c427e20, 0x24030007, -0x3c010001, 0xac237dd0, 0x34420040, 0x3c010001, -0xac227e20, 0x3c020001, 0x8c427e00, 0x10400005, -0x0, 0x3c020001, 0x8c427ddc, 0x104000f9, -0x24020002, 0x3c050001, 0x24a57de0, 0x8ca20000, -0x2c424e21, 0x104000f3, 0x24020002, 0x3c020001, -0x8c427e04, 0x104000f8, 0x2404ffbf, 0x3c020001, -0x8c427ddc, 0x3c030001, 0x8c637e08, 0x441024, -0x641824, 0x10430004, 0x24020001, 0x3c010001, -0x8005083, 0xac227dd0, 0x24020003, 0xaca20000, -0x24020008, 0x3c010001, 0xac227dd0, 0x3c020001, -0x8c427e0c, 0x1040000c, 0x24020001, 0x3c040001, -0xc005097, 0x8c847ddc, 0x3c020001, 0x8c427e28, -0x14400005, 0x24020001, 0x3c020001, 0x8c427e24, -0x10400006, 0x24020001, 0x3c010001, 0xac225ccc, -0x3c010001, 0x8005083, 0xac207df8, 0x3c020001, -0x8c427df0, 0x3c030001, 0x8c637ddc, 0x2c420001, -0x210c0, 0x30630008, 0x3c010001, 0xac227df0, -0x3c010001, 0xac237dec, 0x8f830054, 0x24020009, -0x3c010001, 0xac227dd0, 0x3c010001, 0x8005083, -0xac237df4, 0x8f830054, 0x3c020001, 0x8c427df4, -0x2463d8f0, 0x431023, 0x2c422710, 0x144000a8, -0x0, 0x3c020001, 0x8c427e00, 0x10400005, -0x0, 0x3c020001, 0x8c427ddc, 0x104000a9, -0x24020002, 0x3c030001, 0x24637de0, 0x8c620000, -0x2c424e21, 0x104000a3, 0x24020002, 0x3c020001, -0x8c427e0c, 0x1040000e, 0x0, 0x3c020001, -0x8c427ddc, 0x3c010001, 0xac207e0c, 0x30420080, -0x1040002f, 0x2402000c, 0x8f820204, 0x30420080, -0x1440000c, 0x24020003, 0x8005011, 0x2402000c, -0x3c020001, 0x8c427ddc, 0x30420080, 0x14400005, -0x24020003, 0x8f820204, 0x30420080, 0x1040001f, -0x24020003, 0xac620000, 0x2402000a, 0x3c010001, -0xac227dd0, 0x3c040001, 0x24847e18, 0x8c820000, -0x3c030001, 0x8c637df0, 0x431025, 0xaf820204, -0x8c830000, 0x3c040001, 0x8c847df0, 0x2402000b, -0x3c010001, 0xac227dd0, 0x641825, 0x3c010001, -0xac237e20, 0x3c050001, 0x24a57de0, 0x8ca20000, -0x2c424e21, 0x1040006f, 0x24020002, 0x3c020001, -0x8c427e10, 0x10400005, 0x0, 0x2402000c, -0x3c010001, 0x8005083, 0xac227dd0, 0x3c020001, -0x8c427e00, 0x1040006c, 0x0, 0x3c040001, -0x8c847ddc, 0x1080005e, 0x30820008, 0x3c030001, -0x8c637dec, 0x10620064, 0x24020003, 0x3c010001, -0xac247e08, 0xaca20000, 0x24020006, 0x3c010001, -0x8005083, 0xac227dd0, 0x8f820200, 0x34420002, -0xaf820200, 0x8f830054, 0x2402000d, 0x3c010001, -0xac227dd0, 0x3c010001, 0xac237df4, 0x8f830054, -0x3c020001, 0x8c427df4, 0x2463d8f0, 0x431023, -0x2c422710, 0x1440003a, 0x0, 0x3c020001, -0x8c427e10, 0x10400029, 0x2402000e, 0x3c030001, -0x8c637e24, 0x3c010001, 0x14600015, 0xac227dd0, -0xc0043dd, 0x0, 0x3c050001, 0x8ca55cc8, -0xc0052a2, 0x2021, 0x3c030001, 0x8c635cc8, -0x24020004, 0x14620005, 0x2403fffb, 0x3c020001, -0x8c425cc4, 0x8005052, 0x2403fff7, 0x3c020001, -0x8c425cc4, 0x431024, 0x3c010001, 0xac225cc4, -0x8ee20000, 0x3c030200, 0x431025, 0xaee20000, -0x8f820224, 0x3c010001, 0xac227e2c, 0x8f820220, -0x2403fffb, 0x431024, 0xaf820220, 0x8f820220, -0x34420002, 0x8005083, 0xaf820220, 0x3c020001, -0x8c427e00, 0x10400005, 0x0, 0x3c020001, -0x8c427ddc, 0x1040000f, 0x24020002, 0x3c020001, -0x8c427de0, 0x2c424e21, 0x1040000a, 0x24020002, -0x3c020001, 0x8c427e00, 0x1040000f, 0x0, -0x3c020001, 0x8c427ddc, 0x1440000b, 0x0, -0x24020002, 0x3c010001, 0x8005083, 0xac227dd0, -0x3c020001, 0x8c427e00, 0x10400003, 0x0, -0xc00429b, 0x0, 0x8f820220, 0x3c03f700, -0x431025, 0xaf820220, 0x8fbf0010, 0x3e00008, -0x27bd0018, 0x3c030001, 0x24637e28, 0x8c620000, -0x10400005, 0x34422000, 0x3c010001, 0xac227e1c, -0x8005095, 0xac600000, 0x3c010001, 0xac247e1c, -0x3e00008, 0x0, 0x27bdffe0, 0x30820030, -0xafbf0018, 0x3c010001, 0xac227e24, 0x14400067, -0x3c02ffff, 0x34421f0e, 0x821024, 0x14400061, -0x24020030, 0x30822000, 0x1040005d, 0x30838000, -0x31a02, 0x30820001, 0x21200, 0x3c040001, -0x8c845d9c, 0x621825, 0x331c2, 0x3c030001, -0x24635d78, 0x30828000, 0x21202, 0x30840001, -0x42200, 0x441025, 0x239c2, 0x61080, -0x431021, 0x471021, 0x90430000, 0x24020001, -0x10620025, 0x0, 0x10600007, 0x24020002, -0x10620013, 0x24020003, 0x1062002c, 0x3c05000f, -0x80050f9, 0x0, 0x8f820200, 0x2403feff, -0x431024, 0xaf820200, 0x8f820220, 0x3c03fffe, -0x3463ffff, 0x431024, 0xaf820220, 0x3c010001, -0xac207e44, 0x3c010001, 0x8005104, 0xac207e4c, -0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, -0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, -0x24020100, 0x3c010001, 0xac227e44, 0x3c010001, -0x8005104, 0xac207e4c, 0x8f820200, 0x2403feff, -0x431024, 0xaf820200, 0x8f820220, 0x3c030001, -0x431025, 0xaf820220, 0x3c010001, 0xac207e44, -0x3c010001, 0x8005104, 0xac237e4c, 0x8f820200, -0x34420100, 0xaf820200, 0x8f820220, 0x3c030001, -0x431025, 0xaf820220, 0x24020100, 0x3c010001, -0xac227e44, 0x3c010001, 0x8005104, 0xac237e4c, -0x34a5ffff, 0x3c040001, 0x24845bb8, 0xafa30010, -0xc002403, 0xafa00014, 0x8005104, 0x0, -0x24020030, 0x3c010001, 0xac227e28, 0x8fbf0018, -0x3e00008, 0x27bd0020, 0x0, 0x27bdffc8, -0xafb20028, 0x809021, 0xafb3002c, 0xa09821, -0xafb00020, 0xc08021, 0x3c040001, 0x24845bd0, -0x3c050009, 0x3c020001, 0x8c425cc8, 0x34a59001, -0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, -0xa7a0001a, 0xafb00014, 0xc002403, 0xafa20010, -0x24020002, 0x12620083, 0x2e620003, 0x10400005, -0x24020001, 0x1262000a, 0x0, 0x800529b, -0x0, 0x24020004, 0x126200fa, 0x24020008, -0x126200f9, 0x3c02ffec, 0x800529b, 0x0, -0x3c020001, 0x8c425cc4, 0x30420002, 0x14400004, -0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, -0x3c010001, 0x310821, 0xac307e3c, 0x3c024000, -0x2021024, 0x1040004e, 0x1023c2, 0x30840030, -0x101382, 0x3042001c, 0x3c030001, 0x24635d08, -0x431021, 0x823821, 0x3c020020, 0x2021024, -0x10400006, 0x24020100, 0x3c010001, 0x310821, -0xac227e40, 0x8005150, 0x3c020080, 0x3c010001, -0x310821, 0xac207e40, 0x3c020080, 0x2021024, -0x10400006, 0x121940, 0x3c020001, 0x3c010001, -0x230821, 0x800515c, 0xac227e48, 0x121140, -0x3c010001, 0x220821, 0xac207e48, 0x94e40000, -0x3c030001, 0x8c635dbc, 0x24020005, 0x10620010, -0xa7a40018, 0x32024000, 0x10400002, 0x34824000, -0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, -0x24e60002, 0x34420001, 0xc00498e, 0xa4e20002, -0x24040001, 0x2821, 0xc00498e, 0x27a60018, -0x3c020001, 0x8c425cc8, 0x24110001, 0x3c010001, -0xac315cd4, 0x14530004, 0x32028000, 0xc00429b, -0x0, 0x32028000, 0x1040011f, 0x0, -0xc00429b, 0x0, 0x3c030001, 0x8c635dbc, -0x24020005, 0x10620118, 0x24020002, 0x3c010001, -0xac315ccc, 0x3c010001, 0x800529b, 0xac225cc8, -0x24040001, 0x24050004, 0x27b0001a, 0xc00498e, -0x2003021, 0x24040001, 0x2821, 0xc00498e, -0x2003021, 0x3c020001, 0x511021, 0x8c427e34, -0x3c040001, 0x8c845cc8, 0x3c03bfff, 0x3463ffff, -0x3c010001, 0xac335cd4, 0x431024, 0x3c010001, -0x310821, 0x109300fa, 0xac227e34, 0x800529b, -0x0, 0x3c022000, 0x2021024, 0x10400005, -0x24020001, 0x3c010001, 0xac225d98, 0x80051ad, -0x128940, 0x3c010001, 0xac205d98, 0x128940, -0x3c010001, 0x310821, 0xac307e38, 0x3c024000, -0x2021024, 0x14400016, 0x0, 0x3c020001, -0x8c425d98, 0x10400008, 0x24040004, 0x24050001, -0xc004d93, 0x24062000, 0x24020001, 0x3c010001, -0x370821, 0xac2283ac, 0x3c020001, 0x511021, -0x8c427e30, 0x3c03bfff, 0x3463ffff, 0x431024, -0x3c010001, 0x310821, 0x8005299, 0xac227e30, -0x3c020001, 0x8c425d98, 0x10400028, 0x3c0300a0, -0x2031024, 0x5443000d, 0x3c020020, 0x3c020001, -0x8c425d9c, 0x24030100, 0x3c010001, 0x310821, -0xac237e44, 0x3c030001, 0x3c010001, 0x310821, -0xac237e4c, 0x80051f0, 0x34420400, 0x2021024, -0x10400008, 0x24030100, 0x3c020001, 0x8c425d9c, -0x3c010001, 0x310821, 0xac237e44, 0x80051f0, -0x34420800, 0x3c020080, 0x2021024, 0x1040002e, -0x3c030001, 0x3c020001, 0x8c425d9c, 0x3c010001, -0x310821, 0xac237e4c, 0x34420c00, 0x3c010001, -0xac225d9c, 0x8005218, 0x24040001, 0x3c020020, -0x2021024, 0x10400006, 0x24020100, 0x3c010001, -0x310821, 0xac227e44, 0x8005201, 0x3c020080, -0x3c010001, 0x310821, 0xac207e44, 0x3c020080, -0x2021024, 0x10400007, 0x121940, 0x3c020001, -0x3c010001, 0x230821, 0xac227e4c, 0x800520f, -0x24040001, 0x121140, 0x3c010001, 0x220821, -0xac207e4c, 0x24040001, 0x2821, 0x27b0001e, -0xc00494c, 0x2003021, 0x24040001, 0x2821, -0xc00494c, 0x2003021, 0x24040001, 0x24050001, -0x27b0001c, 0xc00494c, 0x2003021, 0x24040001, -0x24050001, 0xc00494c, 0x2003021, 0x8005299, -0x0, 0x3c02ffec, 0x3442ffff, 0x2028024, -0x3c020008, 0x2028025, 0x121140, 0x3c010001, -0x220821, 0xac307e38, 0x3c022000, 0x2021024, -0x10400009, 0x0, 0x3c020001, 0x8c425d74, -0x14400005, 0x24020001, 0x3c010001, 0xac225d98, -0x800523a, 0x3c024000, 0x3c010001, 0xac205d98, -0x3c024000, 0x2021024, 0x1440001e, 0x0, -0x3c020001, 0x8c425d98, 0x3c010001, 0xac205ce0, -0x10400007, 0x24022020, 0x3c010001, 0xac225d9c, -0x24020001, 0x3c010001, 0x370821, 0xac2283ac, -0x3c04bfff, 0x121940, 0x3c020001, 0x431021, -0x8c427e30, 0x3c050001, 0x8ca55cc8, 0x3484ffff, -0x441024, 0x3c010001, 0x230821, 0xac227e30, -0x24020001, 0x10a20044, 0x0, 0x8005299, -0x0, 0x3c020001, 0x8c425d98, 0x1040001c, -0x24022000, 0x3c010001, 0xac225d9c, 0x3c0300a0, -0x2031024, 0x14430005, 0x121140, 0x3402a000, -0x3c010001, 0x8005294, 0xac225d9c, 0x3c030001, -0x621821, 0x8c637e38, 0x3c020020, 0x621024, -0x10400004, 0x24022001, 0x3c010001, 0x8005294, -0xac225d9c, 0x3c020080, 0x621024, 0x1040001f, -0x3402a001, 0x3c010001, 0x8005294, 0xac225d9c, -0x3c020020, 0x2021024, 0x10400007, 0x121940, -0x24020100, 0x3c010001, 0x230821, 0xac227e44, -0x8005288, 0x3c020080, 0x121140, 0x3c010001, -0x220821, 0xac207e44, 0x3c020080, 0x2021024, -0x10400006, 0x121940, 0x3c020001, 0x3c010001, -0x230821, 0x8005294, 0xac227e4c, 0x121140, -0x3c010001, 0x220821, 0xac207e4c, 0x3c030001, -0x8c635cc8, 0x24020001, 0x10620003, 0x0, -0xc00429b, 0x0, 0x8fbf0030, 0x8fb3002c, -0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, -0x27bd0038, 0x27bdffd8, 0xafb20020, 0x809021, -0xafb1001c, 0x8821, 0x24020002, 0xafbf0024, -0xafb00018, 0xa7a00012, 0x10a200d3, 0xa7a00010, -0x2ca20003, 0x10400005, 0x24020001, 0x10a2000a, -0x128140, 0x8005380, 0x2201021, 0x24020004, -0x10a2007d, 0x24020008, 0x10a2007c, 0x122940, -0x8005380, 0x2201021, 0x3c030001, 0x701821, -0x8c637e3c, 0x3c024000, 0x621024, 0x14400009, -0x24040001, 0x3c027fff, 0x3442ffff, 0x628824, -0x3c010001, 0x300821, 0xac317e34, 0x8005380, -0x2201021, 0x24050001, 0xc00494c, 0x27a60010, -0x24040001, 0x24050001, 0xc00494c, 0x27a60010, -0x97a20010, 0x30420004, 0x10400034, 0x3c114000, -0x3c020001, 0x8c425dbc, 0x2443ffff, 0x2c620006, -0x10400034, 0x31080, 0x3c010001, 0x220821, -0x8c225be0, 0x400008, 0x0, 0x24040001, -0x24050011, 0x27b00012, 0xc00494c, 0x2003021, -0x24040001, 0x24050011, 0xc00494c, 0x2003021, -0x97a50012, 0x30a24000, 0x10400002, 0x3c040010, -0x3c040008, 0x3c030001, 0x8005301, 0x30a28000, -0x24040001, 0x24050014, 0x27b00012, 0xc00494c, -0x2003021, 0x24040001, 0x24050014, 0xc00494c, -0x2003021, 0x97a50012, 0x30a21000, 0x10400002, -0x3c040010, 0x3c040008, 0x3c030001, 0x30a20800, -0x54400001, 0x3c030002, 0x3c028000, 0x2221025, -0x641825, 0x800530e, 0x438825, 0x3c110001, -0x2308821, 0x8e317e3c, 0x3c027fff, 0x3442ffff, -0x2228824, 0x3c020001, 0x8c425cd8, 0x1040001d, -0x121140, 0x3c020001, 0x8c425d98, 0x10400002, -0x3c022000, 0x2228825, 0x121140, 0x3c010001, -0x220821, 0x8c227e40, 0x10400003, 0x3c020020, -0x8005322, 0x2228825, 0x3c02ffdf, 0x3442ffff, -0x2228824, 0x121140, 0x3c010001, 0x220821, -0x8c227e48, 0x10400003, 0x3c020080, 0x800532d, -0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, -0x121140, 0x3c010001, 0x220821, 0xac317e34, -0x8005380, 0x2201021, 0x122940, 0x3c030001, -0x651821, 0x8c637e38, 0x3c024000, 0x621024, -0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, -0x3c010001, 0x250821, 0xac317e30, 0x8005380, -0x2201021, 0x3c020001, 0x8c425cd8, 0x10400033, -0x3c11c00c, 0x3c020001, 0x8c425d74, 0x3c04c00c, -0x34842000, 0x3c030001, 0x8c635d98, 0x2102b, -0x21023, 0x441024, 0x10600003, 0x518825, -0x3c022000, 0x2228825, 0x3c020001, 0x451021, -0x8c427e44, 0x10400003, 0x3c020020, 0x800535d, -0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, -0x121140, 0x3c010001, 0x220821, 0x8c227e4c, -0x10400003, 0x3c020080, 0x8005368, 0x2228825, -0x3c02ff7f, 0x3442ffff, 0x2228824, 0x3c020001, -0x8c425d60, 0x10400002, 0x3c020800, 0x2228825, -0x3c020001, 0x8c425d64, 0x10400002, 0x3c020400, -0x2228825, 0x3c020001, 0x8c425d68, 0x10400006, -0x3c020100, 0x800537b, 0x2228825, 0x3c027fff, -0x3442ffff, 0x628824, 0x121140, 0x3c010001, -0x220821, 0xac317e30, 0x2201021, 0x8fbf0024, -0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, -0x27bd0028, 0x27bdffd8, 0xafb40020, 0x80a021, -0xafbf0024, 0xafb3001c, 0xafb20018, 0xafb10014, -0xafb00010, 0x8f900200, 0x3c030001, 0x8c635cc8, -0x8f930220, 0x24020002, 0x10620063, 0x2c620003, -0x10400005, 0x24020001, 0x1062000a, 0x141940, -0x8005448, 0x0, 0x24020004, 0x1062005a, -0x24020008, 0x10620059, 0x149140, 0x8005448, -0x0, 0x3c040001, 0x832021, 0x8c847e3c, -0x3c110001, 0x2238821, 0x8e317e34, 0x3c024000, -0x821024, 0x1040003e, 0x3c020008, 0x2221024, -0x10400020, 0x36100002, 0x3c020001, 0x431021, -0x8c427e40, 0x10400005, 0x36100020, 0x36100100, -0x3c020020, 0x80053bd, 0x2228825, 0x2402feff, -0x2028024, 0x3c02ffdf, 0x3442ffff, 0x2228824, -0x141140, 0x3c010001, 0x220821, 0x8c227e48, -0x10400005, 0x3c020001, 0x2629825, 0x3c020080, -0x80053dc, 0x2228825, 0x3c02fffe, 0x3442ffff, -0x2629824, 0x3c02ff7f, 0x3442ffff, 0x80053dc, -0x2228824, 0x2402fedf, 0x2028024, 0x3c02fffe, -0x3442ffff, 0x2629824, 0x3c02ff5f, 0x3442ffff, -0x2228824, 0x3c010001, 0x230821, 0xac207e40, -0x3c010001, 0x230821, 0xac207e48, 0xc00486a, -0x0, 0xaf900200, 0xaf930220, 0x8f820220, -0x2403fffb, 0x431024, 0xaf820220, 0x8f820220, -0x34420002, 0xaf820220, 0x80053f3, 0x141140, -0x8f820200, 0x2403fffd, 0x431024, 0xc00486a, -0xaf820200, 0x3c02bfff, 0x3442ffff, 0xc00429b, -0x2228824, 0x141140, 0x3c010001, 0x220821, -0x8005448, 0xac317e34, 0x149140, 0x3c040001, -0x922021, 0x8c847e38, 0x3c110001, 0x2328821, -0x8e317e30, 0x3c024000, 0x821024, 0x14400011, -0x0, 0x3c020001, 0x8c425d98, 0x14400006, -0x3c02bfff, 0x8f820200, 0x34420002, 0xc00486a, -0xaf820200, 0x3c02bfff, 0x3442ffff, 0xc00429b, -0x2228824, 0x3c010001, 0x320821, 0x8005448, -0xac317e30, 0x3c020001, 0x8c425d98, 0x10400005, -0x3c020020, 0x3c020001, 0x8c425d74, 0x1040002b, -0x3c020020, 0x821024, 0x10400007, 0x36100020, -0x24020100, 0x3c010001, 0x320821, 0xac227e44, -0x8005428, 0x36100100, 0x3c010001, 0x320821, -0xac207e44, 0x2402feff, 0x2028024, 0x3c020080, -0x821024, 0x10400007, 0x141940, 0x3c020001, -0x3c010001, 0x230821, 0xac227e4c, 0x8005439, -0x2629825, 0x141140, 0x3c010001, 0x220821, -0xac207e4c, 0x3c02fffe, 0x3442ffff, 0x2629824, -0xc00486a, 0x0, 0xaf900200, 0xaf930220, -0x8f820220, 0x2403fffb, 0x431024, 0xaf820220, -0x8f820220, 0x34420002, 0xaf820220, 0x141140, -0x3c010001, 0x220821, 0xac317e30, 0x8fbf0024, -0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, -0x8fb00010, 0x3e00008, 0x27bd0028, 0x0 }; -static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __devinitdata = { -0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f66776d, 0x61696e2e, 0x632c7620, 0x312e312e, -0x322e3131, 0x20313939, 0x382f3034, 0x2f323720, -0x32323a31, 0x333a3432, 0x20736875, 0x616e6720, -0x45787020, 0x24000000, 0x7468655f, 0x4441574e, -0x0, 0x53544143, 0x4b5f3120, 0x0, -0x42616453, 0x6e64526e, 0x67000000, 0x3f456e71, -0x45767400, 0x3f6e6f51, 0x64457650, 0x0, -0x6576526e, 0x6746756c, 0x6c000000, 0x496c6c43, -0x6f6e6652, 0x78000000, 0x53656e64, 0x436b5375, -0x6d000000, 0x52656376, 0x566c616e, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f74696d, 0x65722e63, 0x2c762031, 0x2e312e32, -0x2e382031, 0x3939382f, 0x30372f33, 0x31203137, -0x3a35383a, 0x34352073, 0x6875616e, 0x67204578, -0x70202400, 0x542d446d, 0x61526431, 0x0, -0x542d446d, 0x61424200, 0x542d446d, 0x61320000, -0x3f6e6f51, 0x64547845, 0x0, 0x3f6e6f51, -0x64527845, 0x0, 0x656e714d, 0x45765046, -0x61696c00, 0x656e714d, 0x45764661, 0x696c0000, -0x6661696c, 0x456e454d, 0x0, 0x3f456e71, -0x45767400, 0x3f6e6f51, 0x64457650, 0x0, -0x6576526e, 0x6746756c, 0x6c000000, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f636f6d, 0x6d616e64, 0x2e632c76, 0x20312e31, -0x2e322e31, 0x30203139, 0x39382f31, 0x312f3138, -0x2031373a, 0x31313a31, 0x38207368, 0x75616e67, -0x20457870, 0x20240000, 0x3f4d626f, 0x78457674, -0x0, 0x4e4f636f, 0x6d616e64, 0x0, -0x68737465, 0x5f455252, 0x0, 0x412d4572, -0x72427563, 0x0, 0x4552524f, 0x522d4164, -0x64000000, 0x656e714d, 0x45765046, 0x61696c00, -0x656e714d, 0x45764661, 0x696c0000, 0x6661696c, -0x456e454d, 0x0, 0x442d4572, 0x724c6173, -0x74000000, 0x442d4572, 0x72320000, 0x6d437374, -0x4d644552, 0x52000000, 0x70726f6d, 0x4d644552, -0x52000000, 0x46696c74, 0x4d644552, 0x52000000, -0x636d645f, 0x45525200, 0x3f456e71, 0x45767400, -0x3f6e6f51, 0x64457650, 0x0, 0x6576526e, -0x6746756c, 0x6c000000, 0x0, 0x6ea0, -0x7fbc, 0x6e38, 0x8734, 0x82b0, -0x8780, 0x8780, 0x6f54, 0x7694, -0x7f0c, 0x80a8, 0x8074, 0x8780, -0x7e70, 0x80cc, 0x6e64, 0x81cc, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f646d61, 0x2e632c76, 0x20312e31, 0x2e322e33, -0x20313939, 0x382f3034, 0x2f323720, 0x32323a31, -0x333a3431, 0x20736875, 0x616e6720, 0x45787020, -0x24000000, 0x646d6172, 0x6441544e, 0x0, -0x646d6177, 0x7241544e, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f747261, 0x63652e63, 0x2c762031, 0x2e312e32, -0x2e322031, 0x3939382f, 0x30342f32, 0x37203232, -0x3a31333a, 0x35302073, 0x6875616e, 0x67204578, -0x70202400, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f646174, 0x612e632c, 0x7620312e, 0x312e322e, -0x32203139, 0x39382f30, 0x342f3237, 0x2032323a, -0x31333a34, 0x30207368, 0x75616e67, 0x20457870, -0x20240000, 0x46575f56, 0x45525349, 0x4f4e3a20, -0x23312046, 0x72692041, 0x70722037, 0x2031373a, -0x35353a34, 0x38205044, 0x54203230, 0x30300000, -0x46575f43, 0x4f4d5049, 0x4c455f54, 0x494d453a, -0x2031373a, 0x35353a34, 0x38000000, 0x46575f43, -0x4f4d5049, 0x4c455f42, 0x593a2064, 0x65767263, -0x73000000, 0x46575f43, 0x4f4d5049, 0x4c455f48, -0x4f53543a, 0x20636f6d, 0x70757465, 0x0, -0x46575f43, 0x4f4d5049, 0x4c455f44, 0x4f4d4149, -0x4e3a2065, 0x6e672e61, 0x6374656f, 0x6e2e636f, -0x6d000000, 0x46575f43, 0x4f4d5049, 0x4c45523a, -0x20676363, 0x20766572, 0x73696f6e, 0x20322e37, -0x2e320000, 0x0, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f6d656d, 0x2e632c76, 0x20312e31, 0x2e322e32, -0x20313939, 0x382f3034, 0x2f323720, 0x32323a31, -0x333a3434, 0x20736875, 0x616e6720, 0x45787020, -0x24000000, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f73656e, 0x642e632c, 0x7620312e, 0x312e322e, -0x31312031, 0x3939382f, 0x31322f32, 0x32203137, -0x3a31373a, 0x35352073, 0x6875616e, 0x67204578, -0x70202400, 0x736e6464, 0x654e6f51, 0x20000000, -0x6e6f454e, 0x515f5458, 0x0, 0x736e6464, -0x744e6f51, 0x20000000, 0x3f6e6f51, 0x64547845, -0x0, 0x756e6b72, 0x64747970, 0x65000000, -0x0, 0xaccc, 0xaccc, 0xad9c, -0xaab0, 0xaab0, 0xad9c, 0xad9c, -0xad9c, 0xad9c, 0xad9c, 0xad9c, -0xad9c, 0xad9c, 0xad9c, 0xad9c, -0xad9c, 0xad9c, 0xad9c, 0xad7c, -0x0, 0xbca8, 0xbca8, 0xbd70, -0xae4c, 0xb058, 0xbd70, 0xbd70, -0xbd70, 0xbd70, 0xbd70, 0xbd70, -0xbd70, 0xbd70, 0xbd70, 0xbd70, -0xbd70, 0xbd70, 0xbd70, 0xbd54, -0xb040, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f726563, 0x762e632c, 0x7620312e, 0x312e322e, -0x31392031, 0x3939382f, 0x30372f32, 0x34203231, -0x3a33303a, 0x30352073, 0x6875616e, 0x67204578, -0x70202400, 0x706b5278, 0x45525200, 0x66726d32, -0x4c617267, 0x65000000, 0x72784e6f, 0x52784264, -0x0, 0x72785144, 0x6d614446, 0x0, -0x72785144, 0x6d614246, 0x0, 0x3f6e6f51, -0x64527845, 0x0, 0x706b5278, 0x45525273, -0x0, 0x66726d32, 0x4c726753, 0x0, -0x72784e6f, 0x42645300, 0x3f724264, 0x446d6146, -0x0, 0x3f724a42, 0x64446d46, 0x0, -0x0, 0xf678, 0xf678, 0xf678, -0xf678, 0xf678, 0xf678, 0xf678, -0xf678, 0xf678, 0xf678, 0xf678, -0xf678, 0xf678, 0xf678, 0xf678, -0xf670, 0xf670, 0xf670, 0x572d444d, -0x41456e46, 0x0, 0x0, 0xfdc0, -0x1015c, 0xfddc, 0x1015c, 0x1015c, -0x1015c, 0x1015c, 0x1015c, 0x1015c, -0xf704, 0x1015c, 0x1015c, 0x1015c, -0x1015c, 0x1015c, 0x10154, 0x10154, -0x10154, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f6d6163, 0x2e632c76, 0x20312e31, 0x2e322e31, -0x32203139, 0x39382f30, 0x342f3237, 0x2032323a, -0x31333a34, 0x32207368, 0x75616e67, 0x20457870, -0x20240000, 0x6d616374, 0x7841544e, 0x0, -0x4e745379, 0x6e264c6b, 0x0, 0x72656d61, -0x73737274, 0x0, 0x6c696e6b, 0x444f574e, -0x0, 0x656e714d, 0x45765046, 0x61696c00, -0x656e714d, 0x45764661, 0x696c0000, 0x6661696c, -0x456e454d, 0x0, 0x6c696e6b, 0x55500000, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, -0x2f636b73, 0x756d2e63, 0x2c762031, 0x2e312e32, -0x2e322031, 0x3939382f, 0x30342f32, 0x37203232, -0x3a31333a, 0x33392073, 0x6875616e, 0x67204578, -0x70202400, 0x50726f62, 0x65506879, 0x0, -0x6c6e6b41, 0x53535254, 0x0, 0x11b2c, -0x11bc4, 0x11bf8, 0x11c2c, 0x11c58, -0x11c6c, 0x11ca8, 0x1207c, 0x11de4, -0x11e24, 0x11e50, 0x11e90, 0x11ec0, -0x11efc, 0x11f30, 0x1207c, 0x122c0, -0x122d8, 0x12300, 0x12320, 0x12348, -0x12478, 0x124a0, 0x124f4, 0x1251c, -0x0, 0x1278c, 0x1285c, 0x12934, -0x12a04, 0x12a60, 0x12b3c, 0x12b64, -0x12c40, 0x12c68, 0x12e10, 0x12e38, -0x12fe0, 0x131d8, 0x1346c, 0x13380, -0x1346c, 0x13498, 0x13008, 0x131b0, -0x0, 0x13b84, 0x13bc8, 0x13c60, -0x13cac, 0x13d1c, 0x13db4, 0x13de8, -0x13e70, 0x13f08, 0x13fd8, 0x14018, -0x1409c, 0x140c0, 0x141f4, 0x646f4261, -0x73655067, 0x0, 0x0, 0x0, -0x0, 0x73746d61, 0x634c4e4b, 0x0, -0x0, 0x14c38, 0x14c38, 0x14b80, -0x14bc4, 0x14c38, 0x14c38, 0x0, -0x0, 0x0 }; -static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __devinitdata = { -0x416c7465, -0x6f6e2041, 0x63654e49, 0x43205600, 0x416c7465, -0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, -0x0, 0x0, 0x0, 0x135418, -0x13e7fc, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x60cf00, -0x60, 0xcf000000, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x3, 0x0, -0x1, 0x0, 0x0, 0x0, -0x1, 0x0, 0x1, 0x0, -0x0, 0x0, 0x0, 0x1, -0x1, 0x0, 0x0, 0x0, -0x0, 0x0, 0x1000000, 0x21000000, -0x12000140, 0x0, 0x0, 0x20000000, -0x120000a0, 0x0, 0x12000060, 0x12000180, -0x120001e0, 0x0, 0x0, 0x0, -0x1, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x2, -0x0, 0x0, 0x30001, 0x1, -0x30201, 0x0, 0x0, 0x0 }; -#endif -/* Generated by genfw.c */ -#define tigon2FwReleaseMajor 0xc -#define tigon2FwReleaseMinor 0x4 -#define tigon2FwReleaseFix 0xb -#define tigon2FwStartAddr 0x00004000 -#define tigon2FwTextAddr 0x00004000 -#define tigon2FwTextLen 0x11bc0 -#define tigon2FwRodataAddr 0x00015bc0 -#define tigon2FwRodataLen 0x10d0 -#define tigon2FwDataAddr 0x00016cc0 -#define tigon2FwDataLen 0x1c0 -#define tigon2FwSbssAddr 0x00016e80 -#define tigon2FwSbssLen 0xcc -#define tigon2FwBssAddr 0x00016f50 -#define tigon2FwBssLen 0x20c0 -static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __devinitdata = { -0x0, -0x10000003, 0x0, 0xd, 0xd, -0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, -0x26104000, 0xc0010c0, 0x0, 0xd, -0x3c1d0001, 0x8fbd6d24, 0x3a0f021, 0x3c100000, -0x26104000, 0xc0017e0, 0x0, 0xd, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x2000008, -0x0, 0x800172f, 0x3c0a0001, 0x800172f, -0x3c0a0002, 0x800172f, 0x0, 0x8002cac, -0x0, 0x8002c4f, 0x0, 0x800172f, -0x3c0a0004, 0x800328a, 0x0, 0x8001a52, -0x0, 0x800394d, 0x0, 0x80038f4, -0x0, 0x800172f, 0x3c0a0006, 0x80039bb, -0x3c0a0007, 0x800172f, 0x3c0a0008, 0x800172f, -0x3c0a0009, 0x8003a13, 0x0, 0x8002ea6, -0x0, 0x800172f, 0x3c0a000b, 0x800172f, -0x3c0a000c, 0x800172f, 0x3c0a000d, 0x80028fb, -0x0, 0x8002890, 0x0, 0x800172f, -0x3c0a000e, 0x800208c, 0x0, 0x8001964, -0x0, 0x8001a04, 0x0, 0x8003ca6, -0x0, 0x8003c94, 0x0, 0x800172f, -0x0, 0x800191a, 0x0, 0x800172f, -0x0, 0x800172f, 0x3c0a0013, 0x800172f, -0x3c0a0014, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x27bdffe0, -0x3c1cc000, 0xafbf001c, 0xafb00018, 0x8f820140, -0x24030003, 0xaf8300ec, 0x34420004, 0xc002b20, -0xaf820140, 0x3c0100c0, 0xc001763, 0xac203ffc, -0x401821, 0x3c020010, 0x3c010001, 0xac236e9c, -0x10620011, 0x43102b, 0x14400002, 0x3c020020, -0x3c020008, 0x1062000c, 0x24050100, 0x3c060001, -0x8cc66e9c, 0x3c040001, 0x24845c74, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020020, -0x3c010001, 0xac226e9c, 0x24020008, 0x3c010001, -0xac226eb4, 0x2402001f, 0x3c010001, 0xac226ec4, -0x24020016, 0x3c010001, 0xac226e98, 0x3c05fffe, -0x34a56f08, 0x3c020001, 0x8c426e9c, 0x3c030002, -0x24639010, 0x3c040001, 0x8c846cc4, 0x431023, -0x14800002, 0x458021, 0x2610fa38, 0x2402f000, -0x2028024, 0xc001785, 0x2002021, 0x2022823, -0x3c040020, 0x821823, 0x651823, 0x247bb000, -0x3c03fffe, 0x3463bf08, 0x363b821, 0x3c0600bf, -0x34c6f000, 0x3c070001, 0x8ce76cc0, 0x3c0300bf, -0x3463e000, 0x852023, 0x3c010001, 0xac246ea8, -0x822023, 0x3c010001, 0xac256e90, 0x52842, -0x3c010001, 0xac226e84, 0x27620ffc, 0x3c010001, -0xac226d20, 0x27621ffc, 0xdb3023, 0x7b1823, -0x3c010001, 0xac246e88, 0x3c010001, 0xac256eac, -0x3c010001, 0xac226d24, 0xaf860150, 0x10e00011, -0xaf830250, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, -0xc001749, 0x0, 0x3c020001, 0x8c426cd0, -0x3c030001, 0x8c636cd4, 0x2442fe00, 0x24630200, -0x3c010001, 0xac226cd0, 0x3c010001, 0x10000004, -0xac236cd4, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, -0x3c020001, 0x8c426cc4, 0x1040000d, 0x26fafa38, -0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, -0x3c1a0001, 0x8f5a6cd4, 0x2442fa38, 0x246305c8, -0x3c010001, 0xac226cd0, 0x3c010001, 0xac236cd4, -0x3c020001, 0x8c426cc8, 0x14400003, 0x0, -0x3c010001, 0xac206cd0, 0xc001151, 0x0, -0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, -0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, -0x27bdff98, 0xafb00048, 0x3c100001, 0x8e1066b8, -0xafb20050, 0x3c120000, 0x26524100, 0xafbf0060, -0xafbe005c, 0xafb50058, 0xafb30054, 0xafb1004c, -0xafa20034, 0xafa30030, 0xafa00010, 0xafa00014, -0x8f860040, 0x3c040001, 0x24845c80, 0x24050200, -0x3c010001, 0xac326e80, 0xc002b3b, 0x2003821, -0x8f830040, 0x3c02f000, 0x621824, 0x3c026000, -0x1062000b, 0xa3a0003f, 0x240e0001, 0x3c040001, -0x24845c88, 0xa3ae003f, 0xafa00010, 0xafa00014, -0x8f860040, 0x24050300, 0xc002b3b, 0x2003821, -0x8f820240, 0x3c030001, 0x431025, 0xaf820240, -0xaf800048, 0x8f820048, 0x14400005, 0x0, -0xaf800048, 0x8f820048, 0x10400004, 0x0, -0xaf800048, 0x10000003, 0x2e02021, 0xaf80004c, -0x2e02021, 0x3c050001, 0xc002ba8, 0x34a540f8, -0x3402021, 0xc002ba8, 0x240505c8, 0x3c020001, -0x8c426ea8, 0x3c0d0001, 0x8dad6e88, 0x3c030001, -0x8c636e84, 0x3c080001, 0x8d086e90, 0x3c090001, -0x8d296eac, 0x3c0a0001, 0x8d4a6eb4, 0x3c0b0001, -0x8d6b6ec4, 0x3c0c0001, 0x8d8c6e98, 0x3c040001, -0x24845c94, 0x24050400, 0xaf42013c, 0x8f42013c, -0x24060001, 0x24070001, 0xaf400000, 0xaf4d0138, -0xaf430144, 0xaf480148, 0xaf49014c, 0xaf4a0150, -0xaf4b0154, 0xaf4c0158, 0x2442ff80, 0xaf420140, -0x24020001, 0xafa20010, 0xc002b3b, 0xafa00014, -0x8f420138, 0xafa20010, 0x8f42013c, 0xafa20014, -0x8f460144, 0x8f470148, 0x3c040001, 0x24845ca0, -0xc002b3b, 0x24050500, 0xafb70010, 0xafba0014, -0x8f46014c, 0x8f470150, 0x3c040001, 0x24845cac, -0xc002b3b, 0x24050600, 0x3c020001, 0x8c426e9c, -0x3603821, 0x3c060002, 0x24c69010, 0x2448ffff, -0x1061824, 0xe81024, 0x43102b, 0x10400006, -0x24050900, 0x3c040001, 0x24845cb8, 0xafa80010, -0xc002b3b, 0xafa00014, 0x8f82000c, 0xafa20010, -0x8f82003c, 0xafa20014, 0x8f860000, 0x8f870004, -0x3c040001, 0x24845cc4, 0xc002b3b, 0x24051000, -0x8c020220, 0x8c030224, 0x8c060218, 0x8c07021c, -0x3c040001, 0x24845ccc, 0x24051100, 0xafa20010, -0xc002b3b, 0xafa30014, 0xaf800054, 0xaf80011c, -0x8c020218, 0x30420002, 0x10400009, 0x0, -0x8c020220, 0x3c030002, 0x34630004, 0x431025, -0xaf42000c, 0x8c02021c, 0x10000008, 0x34420004, -0x8c020220, 0x3c030002, 0x34630006, 0x431025, -0xaf42000c, 0x8c02021c, 0x34420006, 0xaf420014, -0x8c020218, 0x30420010, 0x1040000a, 0x0, -0x8c02021c, 0x34420004, 0xaf420010, 0x8c020220, -0x3c03000a, 0x34630004, 0x431025, 0x10000009, -0xaf420008, 0x8c020220, 0x3c03000a, 0x34630006, -0x431025, 0xaf420008, 0x8c02021c, 0x34420006, -0xaf420010, 0x24020001, 0xaf8200a0, 0xaf8200b0, -0x8f830054, 0x8f820054, 0xaf8000d0, 0xaf8000c0, -0x10000002, 0x24630064, 0x8f820054, 0x621023, -0x2c420065, 0x1440fffc, 0x0, 0x8c040208, -0x8c05020c, 0x26e20028, 0xaee20020, 0x24020490, -0xaee20010, 0xaee40008, 0xaee5000c, 0x26e40008, -0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, -0x8c820018, 0xaf8200b4, 0x9482000a, 0xaf82009c, -0x8f420014, 0xaf8200b0, 0x8f8200b0, 0x30420004, -0x1440fffd, 0x0, 0x8f8200b0, 0x3c03ef00, -0x431024, 0x10400021, 0x0, 0x8f8200b4, -0xafa20010, 0x8f820090, 0x8f830094, 0x3c040001, -0x24845cd4, 0xafa30014, 0x8f8600b0, 0x8f87009c, -0x3c050001, 0xc002b3b, 0x34a5200d, 0x3c040001, -0x24845ce0, 0x240203c0, 0xafa20010, 0xafa00014, -0x8f860144, 0x3c070001, 0x24e75ce8, 0xc002b3b, -0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, -0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, -0x3c030001, 0x431025, 0xaf820140, 0x96e20472, -0x96e60452, 0x96e70462, 0xafa20010, 0x96e20482, -0x3c040001, 0x24845d14, 0x24051200, 0xc002b3b, -0xafa20014, 0x96f00452, 0x32020001, 0x10400002, -0xb021, 0x24160001, 0x32020002, 0x54400001, -0x36d60002, 0x32020008, 0x54400001, 0x36d60004, -0x32020010, 0x54400001, 0x36d60008, 0x32020020, -0x54400001, 0x36d60010, 0x32020040, 0x54400001, -0x36d60020, 0x32020080, 0x54400001, 0x36d60040, -0x96e60482, 0x30c20200, 0x54400001, 0x36d64000, -0x96e30472, 0x30620200, 0x10400003, 0x30620100, -0x10000003, 0x36d62000, 0x54400001, 0x36d61000, -0x96f00462, 0x32c24000, 0x14400004, 0x3207009b, -0x30c2009b, 0x14e20007, 0x240e0001, 0x32c22000, -0x1440000d, 0x32020001, 0x3062009b, 0x10e20009, -0x240e0001, 0x3c040001, 0x24845d20, 0x24051300, -0x2003821, 0xa3ae003f, 0xafa30010, 0xc002b3b, -0xafa00014, 0x32020001, 0x54400001, 0x36d60080, -0x32020002, 0x54400001, 0x36d60100, 0x32020008, -0x54400001, 0x36d60200, 0x32020010, 0x54400001, -0x36d60400, 0x32020080, 0x54400001, 0x36d60800, -0x8c020218, 0x30420200, 0x10400002, 0x3c020008, -0x2c2b025, 0x8c020218, 0x30420800, 0x10400002, -0x3c020080, 0x2c2b025, 0x8c020218, 0x30420400, -0x10400002, 0x3c020100, 0x2c2b025, 0x8c020218, -0x30420100, 0x10400002, 0x3c020200, 0x2c2b025, -0x8c020218, 0x30420080, 0x10400002, 0x3c020400, -0x2c2b025, 0x8c020218, 0x30422000, 0x10400002, -0x3c020010, 0x2c2b025, 0x8c020218, 0x30424000, -0x10400002, 0x3c020020, 0x2c2b025, 0x8c020218, -0x30421000, 0x10400002, 0x3c020040, 0x2c2b025, -0x8ee20498, 0x8ee3049c, 0xaf420160, 0xaf430164, -0x8ee204a0, 0x8ee304a4, 0xaf420168, 0xaf43016c, -0x8ee204a8, 0x8ee304ac, 0xaf420170, 0xaf430174, -0x8ee20428, 0x8ee3042c, 0xaf420178, 0xaf43017c, -0x8ee20448, 0x8ee3044c, 0xaf420180, 0xaf430184, -0x8ee20458, 0x8ee3045c, 0xaf420188, 0xaf43018c, -0x8ee20468, 0x8ee3046c, 0xaf420190, 0xaf430194, -0x8ee20478, 0x8ee3047c, 0xaf420198, 0xaf43019c, -0x8ee20488, 0x8ee3048c, 0xaf4201a0, 0xaf4301a4, -0x8ee204b0, 0x8ee304b4, 0x24040080, 0xaf4201a8, -0xaf4301ac, 0xc002ba8, 0x24050080, 0x8c02025c, -0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, -0x24060008, 0xc002bbf, 0xaf4201f8, 0x3c043b9a, -0x3484ca00, 0x3821, 0x24020006, 0x24030002, -0xaf4201f4, 0x240203e8, 0xaf430204, 0xaf430200, -0xaf4401fc, 0xaf420294, 0x24020001, 0xaf430290, -0xaf42029c, 0x3c030001, 0x671821, 0x90636cd8, -0x3471021, 0x24e70001, 0xa043022c, 0x2ce2000f, -0x1440fff8, 0x3471821, 0x24e70001, 0x3c080001, -0x350840f8, 0x8f820040, 0x3c040001, 0x24845d2c, -0x24051400, 0x21702, 0x24420030, 0xa062022c, -0x3471021, 0xa040022c, 0x8c070218, 0x2c03021, -0x240205c8, 0xafa20010, 0xc002b3b, 0xafa80014, -0x3c040001, 0x24845d38, 0x3c050000, 0x24a55c80, -0x24060010, 0x27b10030, 0x2203821, 0x27b30034, -0xc0017a3, 0xafb30010, 0x3c030001, 0x8c636cc8, -0x1060000a, 0x408021, 0x8fa30030, 0x2405ff00, -0x8fa20034, 0x246400ff, 0x852024, 0x831823, -0x431023, 0xafa20034, 0xafa40030, 0x3c040001, -0x24845d44, 0x3c050000, 0x24a54100, 0x24060108, -0x2203821, 0xc0017a3, 0xafb30010, 0x409021, -0x32c20003, 0x3c010001, 0xac326e80, 0x10400045, -0x2203821, 0x8f820050, 0x3c030010, 0x431024, -0x10400016, 0x0, 0x8c020218, 0x30420040, -0x1040000f, 0x24020001, 0x8f820050, 0x8c030218, -0x240e0001, 0x3c040001, 0x24845d50, 0xa3ae003f, -0xafa20010, 0xafa30014, 0x8f870040, 0x24051500, -0xc002b3b, 0x2c03021, 0x10000004, 0x0, -0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, -0x24845d5c, 0x3c050001, 0x24a55b40, 0x3c060001, -0x24c65bac, 0xc53023, 0x8f420010, 0x27b30030, -0x2603821, 0x27b10034, 0x34420a00, 0xaf420010, -0xc0017a3, 0xafb10010, 0x3c040001, 0x24845d70, -0x3c050001, 0x24a5b714, 0x3c060001, 0x24c6ba90, -0xc53023, 0x2603821, 0xaf420108, 0xc0017a3, -0xafb10010, 0x3c040001, 0x24845d8c, 0x3c050001, -0x24a5be58, 0x3c060001, 0x24c6c900, 0xc53023, -0x2603821, 0x3c010001, 0xac226ef4, 0xc0017a3, -0xafb10010, 0x3c040001, 0x24845da4, 0x10000024, -0x24051600, 0x3c040001, 0x24845dac, 0x3c050001, -0x24a5a10c, 0x3c060001, 0x24c6a238, 0xc53023, -0xc0017a3, 0xafb30010, 0x3c040001, 0x24845dbc, -0x3c050001, 0x24a5b2b0, 0x3c060001, 0x24c6b70c, -0xc53023, 0x2203821, 0xaf420108, 0xc0017a3, -0xafb30010, 0x3c040001, 0x24845dd0, 0x3c050001, -0x24a5ba98, 0x3c060001, 0x24c6be50, 0xc53023, -0x2203821, 0x3c010001, 0xac226ef4, 0xc0017a3, -0xafb30010, 0x3c040001, 0x24845de4, 0x24051650, -0x2c03021, 0x3821, 0x3c010001, 0xac226ef8, -0xafa00010, 0xc002b3b, 0xafa00014, 0x32c20020, -0x10400021, 0x27a70030, 0x3c040001, 0x24845df0, -0x3c050001, 0x24a5b13c, 0x3c060001, 0x24c6b2a8, -0xc53023, 0x24022000, 0xaf42001c, 0x27a20034, -0xc0017a3, 0xafa20010, 0x21900, 0x31982, -0x3c040800, 0x641825, 0xae430028, 0x24030010, -0xaf43003c, 0x96e30450, 0xaf430040, 0x8f430040, -0x3c040001, 0x24845e04, 0xafa00014, 0xafa30010, -0x8f47001c, 0x24051660, 0x3c010001, 0xac226ef0, -0x10000025, 0x32c60020, 0x8ee20448, 0x8ee3044c, -0xaf43001c, 0x8f42001c, 0x2442e000, 0x2c422001, -0x1440000a, 0x240e0001, 0x3c040001, 0x24845e10, -0xa3ae003f, 0xafa00010, 0xafa00014, 0x8f46001c, -0x24051700, 0xc002b3b, 0x3821, 0x3c020000, -0x24425cbc, 0x21100, 0x21182, 0x3c030800, -0x431025, 0xae420028, 0x24020008, 0xaf42003c, -0x96e20450, 0xaf420040, 0x8f420040, 0x3c040001, -0x24845e1c, 0xafa00014, 0xafa20010, 0x8f47001c, -0x24051800, 0x32c60020, 0xc002b3b, 0x0, -0x3c050fff, 0x3c030001, 0x8c636ef4, 0x34a5ffff, -0x2403021, 0x3c020001, 0x8c426ef8, 0x3c040800, -0x651824, 0x31882, 0x641825, 0x451024, -0x21082, 0x441025, 0xacc20080, 0x32c20180, -0x10400056, 0xacc30020, 0x8f82005c, 0x3c030080, -0x431024, 0x1040000d, 0x0, 0x8f820050, -0xafa20010, 0x8f82005c, 0x240e0001, 0x3c040001, -0x24845e28, 0xa3ae003f, 0xafa20014, 0x8f870040, -0x24051900, 0xc002b3b, 0x2c03021, 0x8f820050, -0x3c030010, 0x431024, 0x10400016, 0x0, -0x8c020218, 0x30420040, 0x1040000f, 0x24020001, -0x8f820050, 0x8c030218, 0x240e0001, 0x3c040001, -0x24845d50, 0xa3ae003f, 0xafa20010, 0xafa30014, -0x8f870040, 0x24052000, 0xc002b3b, 0x2c03021, -0x10000004, 0x0, 0x3c010001, 0x370821, -0xa02240f4, 0x3c040001, 0x24845e34, 0x3c050001, -0x24a55ac0, 0x3c060001, 0x24c65b38, 0xc53023, -0x8f420008, 0x27b30030, 0x2603821, 0x27b10034, -0x34420e00, 0xaf420008, 0xc0017a3, 0xafb10010, -0x3c040001, 0x24845e4c, 0x3c050001, 0x24a5d8b4, -0x3c060001, 0x24c6e3c8, 0xc53023, 0x2603821, -0xaf42010c, 0xc0017a3, 0xafb10010, 0x3c040001, -0x24845e64, 0x3c050001, 0x24a5e9ac, 0x3c060001, -0x24c6f0f0, 0xc53023, 0x2603821, 0x3c010001, -0xac226f04, 0xc0017a3, 0xafb10010, 0x3c040001, -0x24845e7c, 0x10000027, 0x24052100, 0x3c040001, -0x24845e84, 0x3c050001, 0x24a59fc8, 0x3c060001, -0x24c6a104, 0xc53023, 0x27b10030, 0x2203821, -0x27b30034, 0xc0017a3, 0xafb30010, 0x3c040001, -0x24845e94, 0x3c050001, 0x24a5cad4, 0x3c060001, -0x24c6d8ac, 0xc53023, 0x2203821, 0xaf42010c, -0xc0017a3, 0xafb30010, 0x3c040001, 0x24845ea4, -0x3c050001, 0x24a5e84c, 0x3c060001, 0x24c6e9a4, -0xc53023, 0x2203821, 0x3c010001, 0xac226f04, -0xc0017a3, 0xafb30010, 0x3c040001, 0x24845eb8, -0x24052150, 0x2c03021, 0x3821, 0x3c010001, -0xac226f10, 0xafa00010, 0xc002b3b, 0xafa00014, -0x3c110fff, 0x3c030001, 0x8c636f04, 0x3631ffff, -0x2409821, 0x3c020001, 0x8c426f10, 0x3c0e0800, -0x711824, 0x31882, 0x6e1825, 0x511024, -0x21082, 0x4e1025, 0xae630038, 0xae620078, -0x8c020218, 0x30420040, 0x14400004, 0x24020001, -0x3c010001, 0x370821, 0xa02240f4, 0x3c040001, -0x24845ec4, 0x3c050001, 0x24a5e3d0, 0x3c060001, -0x24c6e52c, 0xc53023, 0x27be0030, 0x3c03821, -0x27b50034, 0xc0017a3, 0xafb50010, 0x3c010001, -0xac226efc, 0x511024, 0x21082, 0x3c0e0800, -0x4e1025, 0xae620050, 0x32c22000, 0x10400006, -0x3c03821, 0x3c020000, 0x24425cbc, 0x2221024, -0x1000000f, 0x21082, 0x3c040001, 0x24845ed8, -0x3c050001, 0x24a5e534, 0x3c060001, 0x24c6e6e4, -0xc53023, 0xc0017a3, 0xafb50010, 0x3c010001, -0xac226f14, 0x511024, 0x21082, 0x3c0e0800, -0x4e1025, 0xae620048, 0x32c24000, 0x10400005, -0x27a70030, 0x3c020000, 0x24425cbc, 0x1000000e, -0x21100, 0x3c040001, 0x24845ef0, 0x3c050001, -0x24a5e6ec, 0x3c060001, 0x24c6e844, 0xc53023, -0x27a20034, 0xc0017a3, 0xafa20010, 0x3c010001, -0xac226f08, 0x21100, 0x21182, 0x3c030800, -0x431025, 0xae420060, 0x3c040001, 0x24845f08, -0x3c050001, 0x24a58230, 0x3c060001, 0x24c68650, -0xc53023, 0x27b10030, 0x2203821, 0x27b30034, -0xc0017a3, 0xafb30010, 0x3c0e0fff, 0x35ceffff, -0x3c040001, 0x24845f14, 0x3c050000, 0x24a56468, -0x3c060000, 0x24c66588, 0xc53023, 0x2203821, -0x240f021, 0x3c010001, 0xac226edc, 0x4e1024, -0x21082, 0x3c150800, 0x551025, 0xafae0044, -0xafc200b8, 0xc0017a3, 0xafb30010, 0x3c040001, -0x24845f20, 0x3c050000, 0x24a56590, 0x3c060000, -0x24c66808, 0x8fae0044, 0xc53023, 0x2203821, -0x3c010001, 0xac226ed0, 0x4e1024, 0x21082, -0x551025, 0xafc200e8, 0xc0017a3, 0xafb30010, -0x3c040001, 0x24845f38, 0x3c050000, 0x24a56810, -0x3c060000, 0x24c66940, 0x8fae0044, 0xc53023, -0x2203821, 0x3c010001, 0xac226ec8, 0x4e1024, -0x21082, 0x551025, 0xafc200c0, 0xc0017a3, -0xafb30010, 0x3c040001, 0x24845f50, 0x3c050001, -0x24a5fad0, 0x3c060001, 0x24c6fba8, 0x8fae0044, -0xc53023, 0x2203821, 0x3c010001, 0xac226ed4, -0x4e1024, 0x21082, 0x551025, 0xafc200c8, -0xc0017a3, 0xafb30010, 0x3c040001, 0x24845f5c, -0x3c050001, 0x24a5c93c, 0x3c060001, 0x24c6ca20, -0xc53023, 0x2203821, 0xaf420110, 0xc0017a3, -0xafb30010, 0x3c040001, 0x24845f6c, 0x3c050001, -0x24a5c910, 0x3c060001, 0x24c6c934, 0xc53023, -0x2203821, 0xaf420124, 0xc0017a3, 0xafb30010, -0x3c040001, 0x24845f7c, 0x3c050001, 0x24a55a80, -0x3c060001, 0x24c65aac, 0xc53023, 0x2203821, -0xaf420120, 0xaf420114, 0xc0017a3, 0xafb30010, -0x3c040001, 0x24845f88, 0x3c050001, 0x24a5f298, -0x3c060001, 0x24c6f6b4, 0xc53023, 0x2203821, -0xaf420118, 0xc0017a3, 0xafb30010, 0x8fae0044, -0x3c010001, 0xac226f18, 0x4e1024, 0x21082, -0x551025, 0xc003fc3, 0xafc200d0, 0xc003c40, -0x0, 0xc0027a8, 0x0, 0xac000228, -0xac00022c, 0x96e20450, 0x2442ffff, 0xaf420038, -0x96e20460, 0xaf420080, 0x32c24000, 0x14400003, -0x0, 0x96e20480, 0xaf420084, 0x96e70490, -0x50e00001, 0x24070800, 0x24e2ffff, 0xaf420088, -0xaf42007c, 0x24020800, 0x10e2000f, 0x32c24000, -0x10400003, 0x24020400, 0x10e2000b, 0x0, -0x240e0001, 0x3c040001, 0x24845f98, 0xa3ae003f, -0x96e60490, 0x24052170, 0x2c03821, 0xafa00010, -0xc002b3b, 0xafa00014, 0x8f430138, 0x8f440138, -0x24020001, 0xa34205c2, 0xaf430094, 0xaf440098, -0xafa00010, 0xafa00014, 0x8f460080, 0x8f470084, -0x3c040001, 0x24845fa4, 0xc002b3b, 0x24052200, -0xc0024a4, 0x3c110800, 0x3c1433d8, 0x3694cb58, -0x3c020800, 0x34420080, 0x3c040001, 0x24845fb0, -0x3c050000, 0x24a55d00, 0x3c060000, 0x24c65d1c, -0xc53023, 0x27a70030, 0xaf820060, 0x2402ffff, -0xaf820064, 0x27a20034, 0xc0017a3, 0xafa20010, -0x3c010001, 0xac226eb8, 0x21100, 0x21182, -0x511025, 0xc0018fc, 0xae420000, 0x8f820240, -0x3c030001, 0x431025, 0xaf820240, 0x3c020000, -0x24424034, 0xaf820244, 0xaf800240, 0x8f820060, -0x511024, 0x14400005, 0x3c030800, 0x8f820060, -0x431024, 0x1040fffd, 0x0, 0xc003c4d, -0x8821, 0x3c020100, 0xafa20020, 0x8f530018, -0x240200ff, 0x56620001, 0x26710001, 0x8c020228, -0x1622000e, 0x1330c0, 0x8f42033c, 0x24420001, -0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, -0x24845c24, 0x3c050009, 0xafa00014, 0xafa20010, -0x8fa60020, 0x1000003f, 0x34a50100, 0xd71021, -0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, -0xc01821, 0x8f440178, 0x8f45017c, 0x1021, -0x24070004, 0xafa70010, 0xafb10014, 0x8f48000c, -0x24c604c0, 0x2e63021, 0xafa80018, 0x8f48010c, -0x24070008, 0xa32821, 0xa3482b, 0x822021, -0x100f809, 0x892021, 0x1440000b, 0x24070008, -0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, -0x24845c2c, 0x3c050009, 0xafa20014, 0x8fa60020, -0x1000001c, 0x34a50200, 0x8f440160, 0x8f450164, -0x8f43000c, 0xaf510018, 0x8f860120, 0x24020010, -0xafa20010, 0xafb10014, 0xafa30018, 0x8f42010c, -0x40f809, 0x24c6001c, 0x14400010, 0x0, -0x8f420340, 0x24420001, 0xaf420340, 0x8f420340, -0x8f820120, 0xafa20010, 0x8f820124, 0x3c040001, -0x24845c34, 0x3c050009, 0xafa20014, 0x8fa60020, -0x34a50300, 0xc002b3b, 0x2603821, 0x8f4202e4, -0x24420001, 0xaf4202e4, 0x8f4202e4, 0x93a2003f, -0x10400069, 0x3c020700, 0x34423000, 0xafa20028, -0x8f530018, 0x240200ff, 0x12620002, 0x8821, -0x26710001, 0x8c020228, 0x1622000e, 0x1330c0, -0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, -0x8c020228, 0x3c040001, 0x24845c24, 0x3c050009, -0xafa00014, 0xafa20010, 0x8fa60028, 0x1000003f, -0x34a50100, 0xd71021, 0x8fa30028, 0x8fa4002c, -0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, -0x8f45017c, 0x1021, 0x24070004, 0xafa70010, -0xafb10014, 0x8f48000c, 0x24c604c0, 0x2e63021, -0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, -0xa3482b, 0x822021, 0x100f809, 0x892021, -0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, -0x8f820124, 0x3c040001, 0x24845c2c, 0x3c050009, -0xafa20014, 0x8fa60028, 0x1000001c, 0x34a50200, -0x8f440160, 0x8f450164, 0x8f43000c, 0xaf510018, -0x8f860120, 0x24020010, 0xafa20010, 0xafb10014, -0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, -0x14400010, 0x0, 0x8f420340, 0x24420001, -0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, -0x8f820124, 0x3c040001, 0x24845c34, 0x3c050009, -0xafa20014, 0x8fa60028, 0x34a50300, 0xc002b3b, -0x2603821, 0x8f4202f0, 0x24420001, 0xaf4202f0, -0x8f4202f0, 0x3c040001, 0x24845fc0, 0xafa00010, -0xafa00014, 0x8fa60028, 0x24052300, 0xc002b3b, -0x3821, 0x10000004, 0x0, 0x8c020264, -0x10400005, 0x0, 0x8f8200a0, 0x30420004, -0x1440fffa, 0x0, 0x8f820044, 0x34420004, -0xaf820044, 0x8f420308, 0x24420001, 0xaf420308, -0x8f420308, 0x8f8200d8, 0x8f8300d4, 0x431023, -0x2442ff80, 0xaf420090, 0x8f420090, 0x2842ff81, -0x10400006, 0x24020001, 0x8f420090, 0x8f430144, -0x431021, 0xaf420090, 0x24020001, 0xaf42008c, -0x32c20008, 0x10400006, 0x0, 0x8f820214, -0x3c038100, 0x3042ffff, 0x431025, 0xaf820214, -0x3c030001, 0x8c636d94, 0x30620002, 0x10400009, -0x30620001, 0x3c040001, 0x24845fcc, 0x3c050000, -0x24a56d50, 0x3c060000, 0x24c671c8, 0x10000012, -0xc53023, 0x10400009, 0x0, 0x3c040001, -0x24845fdc, 0x3c050000, 0x24a571d0, 0x3c060000, -0x24c67678, 0x10000008, 0xc53023, 0x3c040001, -0x24845fec, 0x3c050000, 0x24a56948, 0x3c060000, -0x24c66d48, 0xc53023, 0x27a70030, 0x27a20034, -0xc0017a3, 0xafa20010, 0x3c010001, 0xac226ecc, -0x3c020001, 0x8c426ecc, 0x3c030800, 0x21100, -0x21182, 0x431025, 0xae420040, 0x8f8200a0, -0xafa20010, 0x8f8200b0, 0xafa20014, 0x8f86005c, -0x8f87011c, 0x3c040001, 0x24845ffc, 0x3c010001, -0xac366ea4, 0x3c010001, 0xac206e94, 0x3c010001, -0xac3c6e8c, 0x3c010001, 0xac3b6ebc, 0x3c010001, -0xac376ec0, 0x3c010001, 0xac3a6ea0, 0xc002b3b, -0x24052400, 0x8f820200, 0xafa20010, 0x8f820220, -0xafa20014, 0x8f860044, 0x8f870050, 0x3c040001, -0x24846008, 0xc002b3b, 0x24052500, 0x8f830060, -0x74100b, 0x242000a, 0x200f821, 0x0, -0xd, 0x8fbf0060, 0x8fbe005c, 0x8fb50058, -0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, -0x3e00008, 0x27bd0068, 0x27bdffe0, 0x3c040001, -0x24846014, 0x24052600, 0x3021, 0x3821, -0xafbf0018, 0xafa00010, 0xc002b3b, 0xafa00014, -0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, -0x0, 0x3e00008, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x3e00008, 0x0, 0x3e00008, 0x0, -0x27bdfde0, 0x27a50018, 0x3c04dead, 0x3484beef, -0xafbf0218, 0x8f820150, 0x3c03001f, 0x3463ffff, -0xafa40018, 0xa22823, 0xa32824, 0x8ca20000, -0x1044000a, 0x0, 0xafa50010, 0x8ca20000, -0xafa20014, 0x8f860150, 0x8f870250, 0x3c040001, -0x2484601c, 0xc002b3b, 0x24052700, 0x8fbf0218, -0x3e00008, 0x27bd0220, 0x27bdffe0, 0x3c06abba, -0x34c6babe, 0xafb00018, 0x3c100004, 0x3c07007f, -0x34e7ffff, 0xafbf001c, 0x102840, 0x8e040000, -0x8ca30000, 0xaca00000, 0xae060000, 0x8ca20000, -0xaca30000, 0x10460005, 0xae040000, 0xa08021, -0xf0102b, 0x1040fff5, 0x102840, 0x3c040001, -0x24846028, 0x24052800, 0x2003021, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x2001021, -0x8fbf001c, 0x8fb00018, 0x3e00008, 0x27bd0020, -0x8c020224, 0x3047003f, 0x10e00010, 0x803021, -0x2821, 0x24030020, 0xe31024, 0x10400002, -0x63042, 0xa62821, 0x31842, 0x1460fffb, -0xe31024, 0x2402f000, 0xa22824, 0x3402ffff, -0x45102b, 0x14400003, 0x3c020001, 0x10000008, -0x3c020001, 0x3442ffff, 0x851823, 0x43102b, -0x14400003, 0xa01021, 0x3c02fffe, 0x821021, -0x3e00008, 0x0, 0x27bdffd0, 0xafb50028, -0x8fb50040, 0xafb20020, 0xa09021, 0xafb1001c, -0x24c60003, 0xafbf002c, 0xafb30024, 0xafb00018, -0x8ea20000, 0x2403fffc, 0xc38024, 0x50102b, -0x1440001b, 0xe08821, 0x8e330000, 0xafb00010, -0x8ea20000, 0xafa20014, 0x8e270000, 0x24053000, -0xc002b3b, 0x2403021, 0x8e230000, 0x702021, -0x64102b, 0x10400007, 0x2402821, 0x8ca20000, -0xac620000, 0x24630004, 0x64102b, 0x1440fffb, -0x24a50004, 0x8ea20000, 0x501023, 0xaea20000, -0x8e220000, 0x501021, 0x1000000b, 0xae220000, -0x2402002d, 0xa0820000, 0xafb00010, 0x8ea20000, -0x2409821, 0xafa20014, 0x8e270000, 0x24053100, -0xc002b3b, 0x2603021, 0x2601021, 0x8fbf002c, -0x8fb50028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, -0x8fb00018, 0x3e00008, 0x27bd0030, 0x27bdffe8, -0x3c1cc000, 0x3c05fffe, 0x3c030001, 0x8c636e84, -0x3c040001, 0x8c846e90, 0x34a5bf08, 0x24021ffc, -0x3c010001, 0xac226cd0, 0x3c0200c0, 0x3c010001, -0xac226cd4, 0x3c020020, 0xafbf0010, 0x3c0100c0, -0xac201ffc, 0x431023, 0x441023, 0x245bb000, -0x365b821, 0x3c1d0001, 0x8fbd6ccc, 0x3a0f021, -0x3c0400c0, 0x34840200, 0x3c1a00c0, 0x3c0300c0, -0x346307c8, 0x24021dfc, 0x3c010001, 0xac226cd0, -0x24021834, 0x3c010001, 0xac246cd4, 0x3c010001, -0xac226cd0, 0x3c010001, 0xac236cd4, 0xc00180d, -0x375a0200, 0x8fbf0010, 0x3e00008, 0x27bd0018, -0x27bdffc8, 0x3c040001, 0x24846034, 0x24053200, -0x3c020001, 0x8c426cd0, 0x3c030001, 0x8c636cd4, -0x3021, 0x3603821, 0xafbf0030, 0xafb3002c, -0xafb20028, 0xafb10024, 0xafb00020, 0xafa2001c, -0xafa30018, 0xafb70010, 0xc002b3b, 0xafba0014, -0xc001916, 0x0, 0x8f820240, 0x34420004, -0xaf820240, 0x24020001, 0xaf420000, 0x3c020001, -0x571021, 0x904240f4, 0x10400092, 0x2403fffc, -0x3c100001, 0x2610ac73, 0x3c120001, 0x2652a84c, -0x2121023, 0x438024, 0x8fa3001c, 0x3c040001, -0x24846040, 0x70102b, 0x1440001a, 0x27b30018, -0x8fb10018, 0x24053000, 0x2403021, 0xafb00010, -0xafa30014, 0xc002b3b, 0x2203821, 0x8fa30018, -0x702021, 0x64102b, 0x10400007, 0x2403021, -0x8cc20000, 0xac620000, 0x24630004, 0x64102b, -0x1440fffb, 0x24c60004, 0x8fa2001c, 0x501023, -0xafa2001c, 0x8e620000, 0x501021, 0x1000000a, -0xae620000, 0x2408821, 0x24053100, 0xafb00010, -0xafa30014, 0x8fa70018, 0x2203021, 0x2402002d, -0xc002b3b, 0xa0820000, 0x24070020, 0x8fa3001c, -0x3c040001, 0x2484605c, 0x24120020, 0x3c010001, -0xac316eb0, 0x2c620020, 0x1440001d, 0x27b10018, -0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f50, -0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, -0x8fa30018, 0x3c040001, 0x24846f50, 0x24650020, -0x65102b, 0x10400007, 0x0, 0x8c820000, -0xac620000, 0x24630004, 0x65102b, 0x1440fffb, -0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, -0x8e220000, 0x521021, 0x1000000b, 0xae220000, -0x3c100001, 0x26106f50, 0x24053100, 0xafa70010, -0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, -0xc002b3b, 0xa0820000, 0x24070020, 0x3c040001, -0x24846070, 0x8fa3001c, 0x24120020, 0x3c010001, -0xac306ee4, 0x2c620020, 0x1440001d, 0x27b10018, -0x8fb00018, 0x24053000, 0x3c060001, 0x24c66f70, -0xafa70010, 0xafa30014, 0xc002b3b, 0x2003821, -0x8fa30018, 0x3c040001, 0x24846f70, 0x24650020, -0x65102b, 0x10400007, 0x0, 0x8c820000, -0xac620000, 0x24630004, 0x65102b, 0x1440fffb, -0x24840004, 0x8fa2001c, 0x521023, 0xafa2001c, -0x8e220000, 0x521021, 0x1000000b, 0xae220000, -0x3c100001, 0x26106f70, 0x24053100, 0xafa70010, -0xafa30014, 0x8fa70018, 0x2003021, 0x2402002d, -0xc002b3b, 0xa0820000, 0x3c010001, 0x10000031, -0xac306ee0, 0x3c100001, 0x2610821f, 0x3c120001, -0x2652809c, 0x2121023, 0x438024, 0x8fa3001c, -0x3c040001, 0x24846084, 0x70102b, 0x1440001a, -0x27b30018, 0x8fb10018, 0x24053000, 0x2403021, -0xafb00010, 0xafa30014, 0xc002b3b, 0x2203821, -0x8fa30018, 0x702021, 0x64102b, 0x10400007, -0x2403021, 0x8cc20000, 0xac620000, 0x24630004, -0x64102b, 0x1440fffb, 0x24c60004, 0x8fa2001c, -0x501023, 0xafa2001c, 0x8e620000, 0x501021, -0x1000000a, 0xae620000, 0x2408821, 0x24053100, -0xafb00010, 0xafa30014, 0x8fa70018, 0x2203021, -0x2402002d, 0xc002b3b, 0xa0820000, 0x3c010001, -0xac316eb0, 0x3c030001, 0x8c636eb0, 0x24020400, -0x60f809, 0xaf820070, 0x8fbf0030, 0x8fb3002c, -0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, -0x27bd0038, 0x0, 0x0, 0x8f820040, -0x3c03f000, 0x431024, 0x3c036000, 0x14430006, -0x0, 0x8f820050, 0x2403ff80, 0x431024, -0x34420055, 0xaf820050, 0x8f820054, 0x244203e8, -0xaf820058, 0x240201f4, 0xaf4200e0, 0x24020004, -0xaf4200e8, 0x24020002, 0xaf4001b0, 0xaf4000e4, -0xaf4200dc, 0xaf4000d8, 0xaf4000d4, 0x3e00008, -0xaf4000d0, 0x8f820054, 0x24420005, 0x3e00008, -0xaf820078, 0x27bdffe8, 0xafbf0010, 0x8f820054, -0x244203e8, 0xaf820058, 0x3c020800, 0x2c21024, -0x10400004, 0x3c02f7ff, 0x3442ffff, 0x2c2b024, -0x36940040, 0x3c020001, 0x8c426da8, 0x10400017, -0x3c020200, 0x3c030001, 0x8c636f1c, 0x10600016, -0x282a025, 0x3c020001, 0x8c426e44, 0x14400012, -0x3c020200, 0x3c020001, 0x8c426d94, 0x30420003, -0x1440000d, 0x3c020200, 0x8f830224, 0x3c020002, -0x8c428fec, 0x10620008, 0x3c020200, 0xc003daf, -0x0, 0x10000004, 0x3c020200, 0xc004196, -0x0, 0x3c020200, 0x2c21024, 0x10400003, -0x0, 0xc001f4b, 0x0, 0x8f4200d8, -0x8f4300dc, 0x24420001, 0xaf4200d8, 0x43102b, -0x14400003, 0x0, 0xaf4000d8, 0x36940080, -0x8c030238, 0x1060000c, 0x0, 0x8f4201b0, -0x244203e8, 0xaf4201b0, 0x43102b, 0x14400006, -0x0, 0x934205c5, 0x14400003, 0x0, -0xc001da0, 0x0, 0x8fbf0010, 0x3e00008, -0x27bd0018, 0x3e00008, 0x0, 0x27bdffd8, -0xafbf0020, 0x8f43002c, 0x8f420038, 0x10620059, -0x0, 0x3c020001, 0x571021, 0x904240f0, -0x10400026, 0x24070008, 0x8f440170, 0x8f450174, -0x8f48000c, 0x8f860120, 0x24020020, 0xafa20010, -0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, -0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, -0x370821, 0xa02240f0, 0x8f820124, 0xafa20010, -0x8f820128, 0x3c040001, 0x24846128, 0xafa20014, -0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, -0x34a50900, 0x1000005c, 0x0, 0x8f420300, -0x24420001, 0xaf420300, 0x8f420300, 0x8f42002c, -0xa34005c1, 0x10000027, 0xaf420038, 0x8f440170, -0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, -0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, -0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, -0x24020001, 0x3c010001, 0x370821, 0xa02240f1, -0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, -0x24846134, 0xafa20014, 0x8f46002c, 0x8f870120, -0x3c050009, 0xc002b3b, 0x34a51100, 0x10000036, -0x0, 0x8f420300, 0x8f43002c, 0x24420001, -0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, -0xaf430038, 0x3c010001, 0x370821, 0xa02040f1, -0x3c010001, 0x370821, 0xa02040f0, 0x10000026, -0xaf400034, 0x934205c1, 0x1040001d, 0x0, -0xa34005c1, 0x8f820040, 0x30420001, 0x14400008, -0x2021, 0x8c030104, 0x24020001, 0x50620005, -0x24040001, 0x8c020264, 0x10400003, 0x801021, -0x24040001, 0x801021, 0x10400006, 0x0, -0x8f42030c, 0x24420001, 0xaf42030c, 0x10000008, -0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, -0x8f420308, 0x24420001, 0xaf420308, 0x8f420308, -0x3c010001, 0x370821, 0xa02040f0, 0x3c010001, -0x370821, 0xa02040f1, 0x8f420000, 0x10400007, -0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, -0x0, 0x10000005, 0x0, 0xaf800048, -0x8f820048, 0x1040fffd, 0x0, 0x8f820060, -0x3c03ff7f, 0x3463ffff, 0x431024, 0xaf820060, -0x8f420000, 0x10400003, 0x0, 0x10000002, -0xaf80004c, 0xaf800048, 0x8fbf0020, 0x3e00008, -0x27bd0028, 0x3e00008, 0x0, 0x27bdffd8, -0xafbf0020, 0x8f430044, 0x8f42007c, 0x10620029, -0x24070008, 0x8f440168, 0x8f45016c, 0x8f48000c, -0x8f860120, 0x24020040, 0xafa20010, 0xafa30014, -0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, -0x14400011, 0x24020001, 0x3c010001, 0x370821, -0xa02240f2, 0x8f820124, 0xafa20010, 0x8f820128, -0x3c040001, 0x2484613c, 0xafa20014, 0x8f460044, -0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51300, -0x1000000f, 0x0, 0x8f420304, 0x24420001, -0xaf420304, 0x8f420304, 0x8f420044, 0xaf42007c, -0x3c010001, 0x370821, 0xa02040f2, 0x10000004, -0xaf400078, 0x3c010001, 0x370821, 0xa02040f2, -0x8f420000, 0x10400007, 0x0, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x3c03feff, 0x3463ffff, -0x431024, 0xaf820060, 0x8f420000, 0x10400003, -0x0, 0x10000002, 0xaf80004c, 0xaf800048, -0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, -0x0, 0x3c020001, 0x8c426da8, 0x27bdffa8, -0xafbf0050, 0xafbe004c, 0xafb50048, 0xafb30044, -0xafb20040, 0xafb1003c, 0xafb00038, 0x104000d5, -0x8f900044, 0x8f4200d0, 0x24430001, 0x2842000b, -0x144000e4, 0xaf4300d0, 0x8f420004, 0x30420002, -0x1440009c, 0xaf4000d0, 0x8f420004, 0x3c030001, -0x8c636d98, 0x34420002, 0xaf420004, 0x24020001, -0x14620003, 0x3c020600, 0x10000002, 0x34423000, -0x34421000, 0xafa20020, 0x8f4a0018, 0xafaa0034, -0x27aa0020, 0xafaa002c, 0x8faa0034, 0x240200ff, -0x11420002, 0x1821, 0x25430001, 0x8c020228, -0x609821, 0x1662000e, 0x3c050009, 0x8f42033c, -0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, -0x8fa70034, 0x3c040001, 0x2484610c, 0xafa00014, -0xafa20010, 0x8fa60020, 0x10000070, 0x34a50500, -0x8faa0034, 0xa38c0, 0xf71021, 0x8fa30020, -0x8fa40024, 0xac4304c0, 0xac4404c4, 0x8f830054, -0x8f820054, 0x247103e8, 0x2221023, 0x2c4203e9, -0x1040001b, 0xa821, 0xe09021, 0x265e04c0, -0x8f440178, 0x8f45017c, 0x2401821, 0x240a0004, -0xafaa0010, 0xafb30014, 0x8f48000c, 0x1021, -0x2fe3021, 0xafa80018, 0x8f48010c, 0x24070008, -0xa32821, 0xa3482b, 0x822021, 0x100f809, -0x892021, 0x54400006, 0x24150001, 0x8f820054, -0x2221023, 0x2c4203e9, 0x1440ffe9, 0x0, -0x32a200ff, 0x54400018, 0xaf530018, 0x8f420378, -0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, -0x8faa002c, 0x8fa70034, 0xafa20010, 0x8f820124, -0x3c040001, 0x24846118, 0xafa20014, 0x8d460000, -0x3c050009, 0x10000035, 0x34a50600, 0x8f420308, -0x24150001, 0x24420001, 0xaf420308, 0x8f420308, -0x1000001e, 0x32a200ff, 0x8f830054, 0x8f820054, -0x247103e8, 0x2221023, 0x2c4203e9, 0x10400016, -0xa821, 0x3c1e0020, 0x24120010, 0x8f42000c, -0x8f440160, 0x8f450164, 0x8f860120, 0xafb20010, -0xafb30014, 0x5e1025, 0xafa20018, 0x8f42010c, -0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, -0x0, 0x8f820054, 0x2221023, 0x2c4203e9, -0x1440ffee, 0x0, 0x32a200ff, 0x14400011, -0x3c050009, 0x8f420378, 0x24420001, 0xaf420378, -0x8f420378, 0x8f820120, 0x8faa002c, 0x8fa70034, -0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, -0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, -0x0, 0x8f4202ec, 0x24420001, 0xaf4202ec, -0x8f4202ec, 0x8f420004, 0x30420001, 0x50400029, -0x36100040, 0x3c020400, 0x2c21024, 0x10400013, -0x2404ffdf, 0x8f420250, 0x8f430254, 0x8f4401b4, -0x14640006, 0x36100040, 0x8f420270, 0x8f430274, -0x8f4401b8, 0x10640007, 0x2402ffdf, 0x8f420250, -0x8f430254, 0x8f440270, 0x8f450274, 0x10000012, -0x3a100020, 0x1000002b, 0x2028024, 0x8f420250, -0x8f430254, 0x8f4501b4, 0x14650006, 0x2048024, -0x8f420270, 0x8f430274, 0x8f4401b8, 0x50640021, -0x36100040, 0x8f420250, 0x8f430254, 0x8f440270, -0x8f450274, 0x3a100040, 0xaf4301b4, 0x10000019, -0xaf4501b8, 0x8f4200d4, 0x24430001, 0x10000011, -0x28420033, 0x8f420004, 0x30420001, 0x10400009, -0x3c020400, 0x2c21024, 0x10400004, 0x2402ffdf, -0x2028024, 0x1000000b, 0x36100040, 0x10000009, -0x36100060, 0x8f4200d4, 0x36100040, 0x24430001, -0x284201f5, 0x14400003, 0xaf4300d4, 0xaf4000d4, -0x3a100020, 0xaf900044, 0x2402ff7f, 0x282a024, -0x8fbf0050, 0x8fbe004c, 0x8fb50048, 0x8fb30044, -0x8fb20040, 0x8fb1003c, 0x8fb00038, 0x3e00008, -0x27bd0058, 0x3e00008, 0x0, 0x3c020001, -0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, -0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, -0x104000c7, 0xafb00030, 0x8f4200d0, 0x24430001, -0x2842000b, 0x144000da, 0xaf4300d0, 0x8f420004, -0x30420002, 0x14400097, 0xaf4000d0, 0x8f420004, -0x3c030001, 0x8c636d98, 0x34420002, 0xaf420004, -0x24020001, 0x14620003, 0x3c020600, 0x10000002, -0x34423000, 0x34421000, 0xafa20020, 0x1821, -0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, -0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, -0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, -0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, -0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, -0x8fa60020, 0x1000006d, 0x34a50500, 0xf71021, -0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, -0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, -0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, -0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, -0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, -0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, -0x24070008, 0xa32821, 0xa3482b, 0x822021, -0x100f809, 0x892021, 0x54400006, 0x24130001, -0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, -0x0, 0x326200ff, 0x54400017, 0xaf520018, -0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, -0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, -0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, -0x8d460000, 0x10000035, 0x34a50600, 0x8f420308, -0x24130001, 0x24420001, 0xaf420308, 0x8f420308, -0x1000001e, 0x326200ff, 0x8f830054, 0x8f820054, -0x247003e8, 0x2021023, 0x2c4203e9, 0x10400016, -0x9821, 0x3c150020, 0x24110010, 0x8f42000c, -0x8f440160, 0x8f450164, 0x8f860120, 0xafb10010, -0xafb20014, 0x551025, 0xafa20018, 0x8f42010c, -0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe3, -0x0, 0x8f820054, 0x2021023, 0x2c4203e9, -0x1440ffee, 0x0, 0x326200ff, 0x14400011, -0x0, 0x8f420378, 0x24420001, 0xaf420378, -0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, -0x8f820124, 0x3c040001, 0x24846120, 0x3c050009, -0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, -0x3c03821, 0x8f4202ec, 0x24420001, 0xaf4202ec, -0x8f4202ec, 0x8f420004, 0x30420001, 0x10400018, -0x24040001, 0x8f420250, 0x8f430254, 0x8f4501b4, -0x3c010001, 0x14650006, 0xa0246cf1, 0x8f420270, -0x8f430274, 0x8f4401b8, 0x10640021, 0x0, -0x8f420250, 0x8f430254, 0x3c040001, 0x90846cf0, -0x8f460270, 0x8f470274, 0x38840001, 0xaf4301b4, -0xaf4701b8, 0x3c010001, 0x10000025, 0xa0246cf0, -0x8f4200d4, 0x3c010001, 0xa0206cf0, 0x24430001, -0x28420033, 0x1440001e, 0xaf4300d4, 0x3c020001, -0x90426cf1, 0xaf4000d4, 0x10000017, 0x38420001, -0x8f420004, 0x30420001, 0x10400008, 0x0, -0xc00565a, 0x2021, 0x3c010001, 0xa0206cf1, -0x3c010001, 0x1000000e, 0xa0206cf0, 0x8f4200d4, -0x3c010001, 0xa0206cf0, 0x24430001, 0x284201f5, -0x14400007, 0xaf4300d4, 0x3c020001, 0x90426cf1, -0xaf4000d4, 0x421026, 0x3c010001, 0xa0226cf1, -0x3c030001, 0x8c636d98, 0x24020002, 0x1462000c, -0x3c030002, 0x3c030001, 0x90636cf1, 0x24020001, -0x5462001f, 0x2021, 0x3c020001, 0x90426cf0, -0x1443001b, 0x24040005, 0x10000019, 0x24040006, -0x3c020002, 0x8c428ff4, 0x431024, 0x1040000b, -0x24020001, 0x3c030001, 0x90636cf1, 0x54620010, -0x2021, 0x3c020001, 0x90426cf0, 0x1443000c, -0x24040003, 0x1000000a, 0x24040004, 0x3c030001, -0x90636cf1, 0x14620006, 0x2021, 0x3c020001, -0x90426cf0, 0x24040001, 0x50440001, 0x24040002, -0xc00565a, 0x0, 0x2402ff7f, 0x282a024, -0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, -0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, -0x27bd0050, 0x3e00008, 0x0, 0x3c020001, -0x8c426da8, 0x27bdffb0, 0xafbf0048, 0xafbe0044, -0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, -0x104000de, 0xafb00030, 0x8f4200d0, 0x3c040001, -0x8c846d98, 0x24430001, 0x2842000b, 0xaf4400e8, -0x144000fe, 0xaf4300d0, 0x8f420004, 0x30420002, -0x14400095, 0xaf4000d0, 0x8f420004, 0x34420002, -0xaf420004, 0x24020001, 0x14820003, 0x3c020600, -0x10000002, 0x34423000, 0x34421000, 0xafa20020, -0x1821, 0x8f5e0018, 0x27aa0020, 0x240200ff, -0x13c20002, 0xafaa002c, 0x27c30001, 0x8c020228, -0x609021, 0x1642000e, 0x1e38c0, 0x8f42033c, -0x24420001, 0xaf42033c, 0x8f42033c, 0x8c020228, -0x3c040001, 0x2484610c, 0x3c050009, 0xafa00014, -0xafa20010, 0x8fa60020, 0x1000006d, 0x34a50500, -0xf71021, 0x8fa30020, 0x8fa40024, 0xac4304c0, -0xac4404c4, 0x8f830054, 0x8f820054, 0x247003e8, -0x2021023, 0x2c4203e9, 0x1040001b, 0x9821, -0xe08821, 0x263504c0, 0x8f440178, 0x8f45017c, -0x2201821, 0x240a0004, 0xafaa0010, 0xafb20014, -0x8f48000c, 0x1021, 0x2f53021, 0xafa80018, -0x8f48010c, 0x24070008, 0xa32821, 0xa3482b, -0x822021, 0x100f809, 0x892021, 0x54400006, -0x24130001, 0x8f820054, 0x2021023, 0x2c4203e9, -0x1440ffe9, 0x0, 0x326200ff, 0x54400017, -0xaf520018, 0x8f420378, 0x24420001, 0xaf420378, -0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, -0x8f820124, 0x3c040001, 0x24846118, 0x3c050009, -0xafa20014, 0x8d460000, 0x10000035, 0x34a50600, -0x8f420308, 0x24130001, 0x24420001, 0xaf420308, -0x8f420308, 0x1000001e, 0x326200ff, 0x8f830054, -0x8f820054, 0x247003e8, 0x2021023, 0x2c4203e9, -0x10400016, 0x9821, 0x3c150020, 0x24110010, -0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, -0xafb10010, 0xafb20014, 0x551025, 0xafa20018, -0x8f42010c, 0x24070008, 0x40f809, 0x24c6001c, -0x1440ffe3, 0x0, 0x8f820054, 0x2021023, -0x2c4203e9, 0x1440ffee, 0x0, 0x326200ff, -0x14400011, 0x0, 0x8f420378, 0x24420001, -0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, -0xafa20010, 0x8f820124, 0x3c040001, 0x24846120, -0x3c050009, 0xafa20014, 0x8d460000, 0x34a50700, -0xc002b3b, 0x3c03821, 0x8f4202ec, 0x24420001, -0xaf4202ec, 0x8f4202ec, 0x8f420004, 0x30420001, -0x10400033, 0x3c020400, 0x2c21024, 0x10400017, -0x0, 0x934205c0, 0x8f440250, 0x8f450254, -0x8f4301b4, 0x34420020, 0x14a30006, 0xa34205c0, -0x8f420270, 0x8f430274, 0x8f4401b8, 0x10640008, -0x0, 0x8f420250, 0x8f430254, 0x934405c0, -0x8f460270, 0x8f470274, 0x10000016, 0x38840040, -0x934205c0, 0x10000048, 0x304200bf, 0x934205c0, -0x8f440250, 0x8f450254, 0x8f4301b4, 0x304200bf, -0x14a30006, 0xa34205c0, 0x8f420270, 0x8f430274, -0x8f4401b8, 0x1064000b, 0x0, 0x8f420250, -0x8f430254, 0x934405c0, 0x8f460270, 0x8f470274, -0x38840020, 0xaf4301b4, 0xaf4701b8, 0x10000033, -0xa34405c0, 0x934205c0, 0x1000002f, 0x34420020, -0x934205c0, 0x8f4300d4, 0x34420020, 0xa34205c0, -0x24620001, 0x10000023, 0x28630033, 0x8f4200e4, -0x8f4300e0, 0x24420001, 0xaf4200e4, 0x43102a, -0x14400006, 0x24030001, 0x8f4200e8, 0x14430002, -0xaf4000e4, 0x24030004, 0xaf4300e8, 0x8f420004, -0x30420001, 0x1040000d, 0x3c020400, 0x2c21024, -0x10400007, 0x0, 0x934205c0, 0x34420040, -0xa34205c0, 0x934205c0, 0x1000000f, 0x304200df, -0x934205c0, 0x1000000c, 0x34420060, 0x934205c0, -0x8f4300d4, 0x34420020, 0xa34205c0, 0x24620001, -0x286300fb, 0x14600005, 0xaf4200d4, 0x934205c0, -0xaf4000d4, 0x38420040, 0xa34205c0, 0x934205c0, -0x8f4300e8, 0x3042007f, 0xa34205c0, 0x24020001, -0x14620005, 0x0, 0x934405c0, 0x42102, -0x10000003, 0x348400f0, 0x934405c0, 0x3484000f, -0xc005640, 0x0, 0x2402ff7f, 0x282a024, -0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, -0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, -0x27bd0050, 0x3e00008, 0x0, 0x27bdffb0, -0x274401c0, 0x26e30028, 0x24650400, 0x65102b, -0xafbf0048, 0xafbe0044, 0xafb50040, 0xafb3003c, -0xafb20038, 0xafb10034, 0x10400007, 0xafb00030, -0x8c820000, 0xac620000, 0x24630004, 0x65102b, -0x1440fffb, 0x24840004, 0x8c020080, 0xaee20044, -0x8c0200c0, 0xaee20040, 0x8c020084, 0xaee20030, -0x8c020084, 0xaee2023c, 0x8c020088, 0xaee20240, -0x8c02008c, 0xaee20244, 0x8c020090, 0xaee20248, -0x8c020094, 0xaee2024c, 0x8c020098, 0xaee20250, -0x8c02009c, 0xaee20254, 0x8c0200a0, 0xaee20258, -0x8c0200a4, 0xaee2025c, 0x8c0200a8, 0xaee20260, -0x8c0200ac, 0xaee20264, 0x8c0200b0, 0xaee20268, -0x8c0200b4, 0xaee2026c, 0x8c0200b8, 0xaee20270, -0x8c0200bc, 0x24040001, 0xaee20274, 0xaee00034, -0x41080, 0x571021, 0x8ee30034, 0x8c42023c, -0x24840001, 0x621821, 0x2c82000f, 0xaee30034, -0x1440fff8, 0x41080, 0x8c0200cc, 0xaee20048, -0x8c0200d0, 0xaee2004c, 0x8c0200e0, 0xaee201f8, -0x8c0200e4, 0xaee201fc, 0x8c0200e8, 0xaee20200, -0x8c0200ec, 0xaee20204, 0x8c0200f0, 0xaee20208, -0x8ee400c0, 0x8ee500c4, 0x8c0200fc, 0x45102b, -0x1040000b, 0x0, 0x8ee200c0, 0x8ee300c4, -0x24040001, 0x24050000, 0x651821, 0x65302b, -0x441021, 0x461021, 0xaee200c0, 0xaee300c4, -0x8c0200fc, 0x8ee400c0, 0x8ee500c4, 0x2408ffff, -0x24090000, 0x401821, 0x1021, 0x882024, -0xa92824, 0x822025, 0xa32825, 0xaee400c0, -0xaee500c4, 0x8ee400d0, 0x8ee500d4, 0x8c0200f4, -0x45102b, 0x1040000b, 0x0, 0x8ee200d0, -0x8ee300d4, 0x24040001, 0x24050000, 0x651821, -0x65302b, 0x441021, 0x461021, 0xaee200d0, -0xaee300d4, 0x8c0200f4, 0x8ee400d0, 0x8ee500d4, -0x401821, 0x1021, 0x882024, 0xa92824, -0x822025, 0xa32825, 0xaee400d0, 0xaee500d4, -0x8ee400c8, 0x8ee500cc, 0x8c0200f8, 0x45102b, -0x1040000b, 0x0, 0x8ee200c8, 0x8ee300cc, -0x24040001, 0x24050000, 0x651821, 0x65302b, -0x441021, 0x461021, 0xaee200c8, 0xaee300cc, -0x8c0200f8, 0x8ee400c8, 0x8ee500cc, 0x401821, -0x1021, 0x882024, 0xa92824, 0x822025, -0xa32825, 0x24020008, 0xaee400c8, 0xaee500cc, -0xafa20010, 0xafa00014, 0x8f42000c, 0x8c040208, -0x8c05020c, 0xafa20018, 0x8f42010c, 0x26e60028, -0x40f809, 0x24070400, 0x104000f0, 0x3c020400, -0xafa20020, 0x934205c6, 0x10400089, 0x1821, -0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, -0xafaa002c, 0x27c30001, 0x8c020228, 0x609021, -0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, -0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, -0x2484610c, 0x3c050009, 0xafa00014, 0xafa20010, -0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, -0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, -0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, -0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, -0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, -0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, -0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, -0x24070008, 0xa32821, 0xa3482b, 0x822021, -0x100f809, 0x892021, 0x54400006, 0x24130001, -0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, -0x0, 0x326200ff, 0x54400017, 0xaf520018, -0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, -0x8f820120, 0x8faa002c, 0xafa20010, 0x8f820124, -0x3c040001, 0x24846118, 0x3c050009, 0xafa20014, -0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, -0x24130001, 0x24420001, 0xaf420308, 0x8f420308, -0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, -0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, -0x9821, 0x24110010, 0x8f42000c, 0x8f440160, -0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, -0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, -0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, -0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, -0x326200ff, 0x54400012, 0x24020001, 0x8f420378, -0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, -0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, -0x24846120, 0x3c050009, 0xafa20014, 0x8d460000, -0x34a50700, 0xc002b3b, 0x3c03821, 0x1021, -0x1440005b, 0x24020001, 0x10000065, 0x0, -0x8f510018, 0x240200ff, 0x12220002, 0x8021, -0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, -0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, -0x8c020228, 0x3c040001, 0x248460f4, 0x3c050009, -0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, -0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, -0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, -0x8f45017c, 0x1021, 0x24070004, 0xafa70010, -0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, -0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, -0xa3482b, 0x822021, 0x100f809, 0x892021, -0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, -0x8f820124, 0x3c040001, 0x248460fc, 0x3c050009, -0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, -0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, -0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, -0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, -0x54400011, 0x24020001, 0x8f420340, 0x24420001, -0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, -0x8f820124, 0x3c040001, 0x24846104, 0x3c050009, -0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, -0x2203821, 0x1021, 0x1040000d, 0x24020001, -0x8f4202e8, 0xa34005c6, 0xaf4001b0, 0x24420001, -0xaf4202e8, 0x8f4202e8, 0x8ee20150, 0x24420001, -0xaee20150, 0x10000003, 0x8ee20150, 0x24020001, -0xa34205c6, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, -0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, -0x3e00008, 0x27bd0050, 0x27bdffd8, 0xafbf0020, -0x8f8200b0, 0x30420004, 0x10400068, 0x0, -0x8f430128, 0x8f820104, 0x14620005, 0x0, -0x8f430130, 0x8f8200b4, 0x10620006, 0x0, -0x8f820104, 0xaf420128, 0x8f8200b4, 0x1000005b, -0xaf420130, 0x8f8200b0, 0x3c030080, 0x431024, -0x1040000d, 0x0, 0x8f82011c, 0x34420002, -0xaf82011c, 0x8f8200b0, 0x2403fffb, 0x431024, -0xaf8200b0, 0x8f82011c, 0x2403fffd, 0x431024, -0x1000004a, 0xaf82011c, 0x8f430128, 0x8f820104, -0x14620005, 0x0, 0x8f430130, 0x8f8200b4, -0x10620010, 0x0, 0x8f820104, 0xaf420128, -0x8f8200b4, 0x8f430128, 0xaf420130, 0xafa30010, -0x8f420130, 0x3c040001, 0x24846144, 0xafa20014, -0x8f86011c, 0x8f8700b0, 0x3c050005, 0x10000031, -0x34a50900, 0x8f420128, 0xafa20010, 0x8f420130, -0x3c040001, 0x24846150, 0xafa20014, 0x8f86011c, -0x8f8700b0, 0x3c050005, 0xc002b3b, 0x34a51000, -0x8f82011c, 0x34420002, 0xaf82011c, 0x8f830104, -0x8f8200b0, 0x34420001, 0xaf8200b0, 0x24020008, -0xaf830104, 0xafa20010, 0xafa00014, 0x8f42000c, -0x8c040208, 0x8c05020c, 0xafa20018, 0x8f42010c, -0x26e60028, 0x40f809, 0x24070400, 0x8f82011c, -0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, -0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f420128, -0xafa20010, 0x8f420130, 0x3c040001, 0x2484615c, -0xafa20014, 0x8f86011c, 0x8f8700b0, 0x3c050005, -0x34a51100, 0xc002b3b, 0x0, 0x8f8200a0, -0x30420004, 0x10400069, 0x0, 0x8f43012c, -0x8f820124, 0x14620005, 0x0, 0x8f430134, -0x8f8200a4, 0x10620006, 0x0, 0x8f820124, -0xaf42012c, 0x8f8200a4, 0x1000005c, 0xaf420134, -0x8f8200a0, 0x3c030080, 0x431024, 0x1040000d, -0x0, 0x8f82011c, 0x34420002, 0xaf82011c, -0x8f8200a0, 0x2403fffb, 0x431024, 0xaf8200a0, -0x8f82011c, 0x2403fffd, 0x431024, 0x1000004b, -0xaf82011c, 0x8f43012c, 0x8f820124, 0x14620005, -0x0, 0x8f430134, 0x8f8200a4, 0x10620010, -0x0, 0x8f820124, 0xaf42012c, 0x8f8200a4, -0x8f43012c, 0xaf420134, 0xafa30010, 0x8f420134, -0x3c040001, 0x24846168, 0xafa20014, 0x8f86011c, -0x8f8700a0, 0x3c050005, 0x10000032, 0x34a51200, -0x8f42012c, 0xafa20010, 0x8f420134, 0x3c040001, -0x24846174, 0xafa20014, 0x8f86011c, 0x8f8700a0, -0x3c050005, 0xc002b3b, 0x34a51300, 0x8f82011c, -0x34420002, 0xaf82011c, 0x8f830124, 0x8f8200a0, -0x34420001, 0xaf8200a0, 0x24020080, 0xaf830124, -0xafa20010, 0xafa00014, 0x8f420014, 0x8c040208, -0x8c05020c, 0xafa20018, 0x8f420108, 0x3c060001, -0x24c66ed8, 0x40f809, 0x24070004, 0x8f82011c, -0x2403fffd, 0x431024, 0xaf82011c, 0x8ee201dc, -0x24420001, 0xaee201dc, 0x8ee201dc, 0x8f42012c, -0xafa20010, 0x8f420134, 0x3c040001, 0x24846180, -0xafa20014, 0x8f86011c, 0x8f8700a0, 0x3c050005, -0x34a51400, 0xc002b3b, 0x0, 0x8fbf0020, -0x3e00008, 0x27bd0028, 0x3c081000, 0x24070001, -0x3c060080, 0x3c050100, 0x8f820070, 0x481024, -0x1040fffd, 0x0, 0x8f820054, 0x24420005, -0xaf820078, 0x8c040234, 0x10800016, 0x1821, -0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, -0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, -0x571021, 0x8c4240e8, 0x44102b, 0x14400009, -0x0, 0x3c030080, 0x3c010001, 0x370821, -0xac2040e8, 0x3c010001, 0x370821, 0x1000000b, -0xa02740f0, 0x3c020001, 0x571021, 0x904240f0, -0x54400006, 0x661825, 0x3c020001, 0x571021, -0x904240f1, 0x54400001, 0x661825, 0x8c040230, -0x10800013, 0x0, 0x3c020001, 0x571021, -0x8c4240ec, 0x24420005, 0x3c010001, 0x370821, -0xac2240ec, 0x3c020001, 0x571021, 0x8c4240ec, -0x44102b, 0x14400006, 0x0, 0x3c010001, -0x370821, 0xac2040ec, 0x10000006, 0x651825, -0x3c020001, 0x571021, 0x904240f2, 0x54400001, -0x651825, 0x1060ffbc, 0x0, 0x8f420000, -0x10400007, 0x0, 0xaf80004c, 0x8f82004c, -0x1040fffd, 0x0, 0x10000005, 0x0, -0xaf800048, 0x8f820048, 0x1040fffd, 0x0, -0x8f820060, 0x431025, 0xaf820060, 0x8f420000, -0x10400003, 0x0, 0x1000ffa7, 0xaf80004c, -0x1000ffa5, 0xaf800048, 0x3e00008, 0x0, -0x0, 0x0, 0x0, 0x27bdffe0, -0xafbf0018, 0x8f860064, 0x30c20004, 0x10400025, -0x24040004, 0x8c020114, 0xaf420020, 0xaf840064, -0x8f4202fc, 0x24420001, 0xaf4202fc, 0x8f4202fc, -0x8f820064, 0x30420004, 0x14400005, 0x0, -0x8c030114, 0x8f420020, 0x1462fff2, 0x0, -0x8f420000, 0x10400007, 0x8f43003c, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x431025, 0xaf820060, -0x8f420000, 0x10400073, 0x0, 0x1000006f, -0x0, 0x30c20008, 0x10400020, 0x24040008, -0x8c02011c, 0xaf420048, 0xaf840064, 0x8f4202a8, -0x24420001, 0xaf4202a8, 0x8f4202a8, 0x8f820064, -0x30420008, 0x14400005, 0x0, 0x8c03011c, -0x8f420048, 0x1462fff2, 0x0, 0x8f420000, -0x10400007, 0x0, 0xaf80004c, 0x8f82004c, -0x1040fffd, 0x0, 0x10000005, 0x0, -0xaf800048, 0x8f820048, 0x1040fffd, 0x0, -0x8f820060, 0x1000ffd9, 0x34420200, 0x30c20020, -0x10400023, 0x24040020, 0x8c02012c, 0xaf420068, -0xaf840064, 0x8f4202d8, 0x24420001, 0xaf4202d8, -0x8f4202d8, 0x8f820064, 0x30420020, 0x14400005, -0x32c24000, 0x8c03012c, 0x8f420068, 0x1462fff2, -0x32c24000, 0x14400002, 0x3c020001, 0x2c2b025, -0x8f420000, 0x10400007, 0x0, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x1000ffb4, 0x34420800, -0x30c20010, 0x10400029, 0x24040010, 0x8c020124, -0xaf420058, 0xaf840064, 0x8f4202d4, 0x24420001, -0xaf4202d4, 0x8f4202d4, 0x8f820064, 0x30420010, -0x14400005, 0x32c22000, 0x8c030124, 0x8f420058, -0x1462fff2, 0x32c22000, 0x50400001, 0x36d68000, -0x8f420000, 0x10400007, 0x0, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x34420100, 0xaf820060, -0x8f420000, 0x10400003, 0x0, 0x1000006c, -0xaf80004c, 0x1000006a, 0xaf800048, 0x30c20001, -0x10400004, 0x24020001, 0xaf820064, 0x10000064, -0x0, 0x30c20002, 0x1440000b, 0x3c050003, -0x3c040001, 0x24846244, 0x34a50500, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x2402ffc0, -0x10000057, 0xaf820064, 0x8c05022c, 0x8c02010c, -0x10a20048, 0x51080, 0x8c460300, 0x24a20001, -0x3045003f, 0x24020003, 0xac05022c, 0x61e02, -0x10620005, 0x24020010, 0x1062001d, 0x30c20fff, -0x10000039, 0x0, 0x8f4302a8, 0x8f440000, -0x30c20fff, 0xaf420048, 0x24630001, 0xaf4302a8, -0x10800007, 0x8f4202a8, 0xaf80004c, 0x8f82004c, -0x1040fffd, 0x0, 0x10000005, 0x0, -0xaf800048, 0x8f820048, 0x1040fffd, 0x0, -0x8f820060, 0x34420200, 0xaf820060, 0x8f420000, -0x1040001f, 0x0, 0x1000001b, 0x0, -0xaf420058, 0x32c22000, 0x50400001, 0x36d68000, -0x8f4202d4, 0x8f430000, 0x24420001, 0xaf4202d4, -0x10600007, 0x8f4202d4, 0xaf80004c, 0x8f82004c, -0x1040fffd, 0x0, 0x10000005, 0x0, -0xaf800048, 0x8f820048, 0x1040fffd, 0x0, -0x8f820060, 0x34420100, 0xaf820060, 0x8f420000, -0x10400003, 0x0, 0x10000006, 0xaf80004c, -0x10000004, 0xaf800048, 0xc002196, 0xc02021, -0x402821, 0x8c02010c, 0x14a20002, 0x24020002, -0xaf820064, 0x8f820064, 0x30420002, 0x14400004, -0x0, 0x8c02010c, 0x14a2ffac, 0x0, -0x8fbf0018, 0x3e00008, 0x27bd0020, 0x3e00008, -0x0, 0x27bdffa0, 0xafb00040, 0x808021, -0x101602, 0x2442ffff, 0x304300ff, 0x2c620013, -0xafbf0058, 0xafbe0054, 0xafb50050, 0xafb3004c, -0xafb20048, 0xafb10044, 0x104001f3, 0xafa50034, -0x31080, 0x3c010001, 0x220821, 0x8c226288, -0x400008, 0x0, 0x101302, 0x30440fff, -0x24020001, 0x10820005, 0x24020002, 0x1082000c, -0x2402fffe, 0x10000024, 0x3c050003, 0x8f430004, -0x3c020001, 0x8c426f04, 0xaf440200, 0xaf440204, -0x3c040001, 0x8c846e80, 0x10000009, 0x34630001, -0x8f430004, 0xaf440200, 0xaf440204, 0x3c040001, -0x8c846e80, 0x621824, 0x3c020001, 0x2442ca28, -0x21100, 0x21182, 0xaf430004, 0x3c030800, -0x431025, 0xac820038, 0x8f840054, 0x41442, -0x41c82, 0x431021, 0x41cc2, 0x431023, -0x41d02, 0x431021, 0x41d42, 0x431023, -0x10000009, 0xaf420208, 0x3c040001, 0x24846250, -0x34a51000, 0x2003021, 0x3821, 0xafa00010, -0xc002b3b, 0xafa00014, 0x8f4202a0, 0x24420001, -0xaf4202a0, 0x1000021f, 0x8f4202a0, 0x27b00028, -0x2002021, 0x24050210, 0xc002bbf, 0x24060008, -0xc002518, 0x2002021, 0x10000216, 0x0, -0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, -0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, -0x21080, 0x8c430300, 0x25420001, 0x3042003f, -0xafa20034, 0xac02022c, 0xafa50028, 0xc002518, -0xafa3002c, 0x10000203, 0x0, 0x27b00028, -0x2002021, 0x24050210, 0xc002bbf, 0x24060008, -0xc002657, 0x2002021, 0x100001fa, 0x0, -0x8faa0034, 0x27a40028, 0xa1880, 0x25420001, -0x3042003f, 0xafa20034, 0x8c650300, 0x8faa0034, -0x21080, 0x8c430300, 0x25420001, 0x3042003f, -0xafa20034, 0xac02022c, 0xafa50028, 0xc002657, -0xafa3002c, 0x100001e7, 0x0, 0x101302, -0x30430fff, 0x24020001, 0x10620005, 0x24020002, -0x1062001e, 0x3c020002, 0x10000033, 0x3c050003, -0x3c030002, 0x2c31024, 0x54400037, 0x2c3b025, -0x8f820228, 0x3c010001, 0x370821, 0xac2238d8, -0x8f82022c, 0x3c010001, 0x370821, 0xac2238dc, -0x8f820230, 0x3c010001, 0x370821, 0xac2238e0, -0x8f820234, 0x3c010001, 0x370821, 0xac2238e4, -0x2402ffff, 0xaf820228, 0xaf82022c, 0xaf820230, -0xaf820234, 0x10000020, 0x2c3b025, 0x2c21024, -0x10400012, 0x3c02fffd, 0x3c020001, 0x571021, -0x8c4238d8, 0xaf820228, 0x3c020001, 0x571021, -0x8c4238dc, 0xaf82022c, 0x3c020001, 0x571021, -0x8c4238e0, 0xaf820230, 0x3c020001, 0x571021, -0x8c4238e4, 0xaf820234, 0x3c02fffd, 0x3442ffff, -0x10000009, 0x2c2b024, 0x3c040001, 0x2484625c, -0x34a51100, 0x2003021, 0x3821, 0xafa00010, -0xc002b3b, 0xafa00014, 0x8f4202cc, 0x24420001, -0xaf4202cc, 0x1000019f, 0x8f4202cc, 0x101302, -0x30450fff, 0x24020001, 0x10a20005, 0x24020002, -0x10a2000d, 0x3c0408ff, 0x10000014, 0x3c050003, -0x3c0208ff, 0x3442ffff, 0x8f830220, 0x3c040004, -0x2c4b025, 0x621824, 0x34630008, 0xaf830220, -0x10000012, 0xaf450298, 0x3484fff7, 0x3c03fffb, -0x8f820220, 0x3463ffff, 0x2c3b024, 0x441024, -0xaf820220, 0x10000009, 0xaf450298, 0x3c040001, -0x24846268, 0x34a51200, 0x2003021, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202bc, -0x24420001, 0xaf4202bc, 0x10000176, 0x8f4202bc, -0x27840208, 0x24050200, 0xc002bbf, 0x24060008, -0x27440224, 0x24050200, 0xc002bbf, 0x24060008, -0x8f4202c4, 0x24420001, 0xaf4202c4, 0x10000169, -0x8f4202c4, 0x101302, 0x30430fff, 0x24020001, -0x10620011, 0x28620002, 0x50400005, 0x24020002, -0x10600007, 0x0, 0x10000017, 0x0, -0x1062000f, 0x0, 0x10000013, 0x0, -0x8c060248, 0x2021, 0xc005104, 0x24050004, -0x10000007, 0x0, 0x8c060248, 0x2021, -0xc005104, 0x24050004, 0x10000010, 0x0, -0x8c06024c, 0x2021, 0xc005104, 0x24050001, -0x1000000a, 0x0, 0x3c040001, 0x24846274, -0x3c050003, 0x34a51300, 0x2003021, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x8f4202c0, -0x24420001, 0xaf4202c0, 0x1000013a, 0x8f4202c0, -0xc002426, 0x0, 0x10000136, 0x0, -0x24020001, 0xa34205c5, 0x24100100, 0x8f4401a8, -0x8f4501ac, 0xafb00010, 0xafa00014, 0x8f420014, -0xafa20018, 0x8f420108, 0x26e60028, 0x40f809, -0x24070400, 0x1040fff5, 0x0, 0x10000125, -0x0, 0x3c03ffff, 0x34637fff, 0x8f420368, -0x8f440360, 0x2c3b024, 0x1821, 0xaf400058, -0xaf40005c, 0xaf400060, 0xaf400064, 0x441023, -0xaf420368, 0x3c020900, 0xaf400360, 0xafa20020, -0x8f5e0018, 0x27aa0020, 0x240200ff, 0x13c20002, -0xafaa003c, 0x27c30001, 0x8c020228, 0x609021, -0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, -0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, -0x2484620c, 0x3c050009, 0xafa00014, 0xafa20010, -0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, -0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, -0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, -0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, -0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, -0x240a0004, 0xafaa0010, 0xafb20014, 0x8f48000c, -0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, -0x24070008, 0xa32821, 0xa3482b, 0x822021, -0x100f809, 0x892021, 0x54400006, 0x24130001, -0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, -0x0, 0x326200ff, 0x54400017, 0xaf520018, -0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, -0x8f820120, 0x8faa003c, 0xafa20010, 0x8f820124, -0x3c040001, 0x24846218, 0x3c050009, 0xafa20014, -0x8d460000, 0x10000033, 0x34a50600, 0x8f420308, -0x24130001, 0x24420001, 0xaf420308, 0x8f420308, -0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, -0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, -0x9821, 0x24110010, 0x8f42000c, 0x8f440160, -0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, -0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, -0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, -0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, -0x326200ff, 0x14400011, 0x0, 0x8f420378, -0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, -0x8faa003c, 0xafa20010, 0x8f820124, 0x3c040001, -0x24846220, 0x3c050009, 0xafa20014, 0x8d460000, -0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b0, -0x24420001, 0xaf4202b0, 0x8f4202b0, 0x8f4202f8, -0x24420001, 0xaf4202f8, 0x1000008a, 0x8f4202f8, -0x8c02025c, 0x27440224, 0xaf4201f0, 0x8c020260, -0x24050200, 0x24060008, 0xc002bbf, 0xaf4201f8, -0x8f820220, 0x30420008, 0x14400002, 0x24020001, -0x24020002, 0xaf420298, 0x8f4202ac, 0x24420001, -0xaf4202ac, 0x10000077, 0x8f4202ac, 0x3c0200ff, -0x3442ffff, 0x2021824, 0x32c20180, 0x14400006, -0x3402fffb, 0x43102b, 0x14400003, 0x0, -0x1000006c, 0xaf4300bc, 0x3c040001, 0x24846280, -0x3c050003, 0x34a51500, 0x2003021, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x3c020700, -0x34421000, 0x101e02, 0x621825, 0xafa30020, -0x8f510018, 0x240200ff, 0x12220002, 0x8021, -0x26300001, 0x8c020228, 0x1602000e, 0x1130c0, -0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, -0x8c020228, 0x3c040001, 0x248461f4, 0x3c050009, -0xafa00014, 0xafa20010, 0x8fa60020, 0x1000003f, -0x34a50100, 0xd71021, 0x8fa30020, 0x8fa40024, -0xac4304c0, 0xac4404c4, 0xc01821, 0x8f440178, -0x8f45017c, 0x1021, 0x24070004, 0xafa70010, -0xafb00014, 0x8f48000c, 0x24c604c0, 0x2e63021, -0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, -0xa3482b, 0x822021, 0x100f809, 0x892021, -0x1440000b, 0x24070008, 0x8f820120, 0xafa20010, -0x8f820124, 0x3c040001, 0x248461fc, 0x3c050009, -0xafa20014, 0x8fa60020, 0x1000001c, 0x34a50200, -0x8f440160, 0x8f450164, 0x8f43000c, 0xaf500018, -0x8f860120, 0x24020010, 0xafa20010, 0xafb00014, -0xafa30018, 0x8f42010c, 0x40f809, 0x24c6001c, -0x14400010, 0x0, 0x8f420340, 0x24420001, -0xaf420340, 0x8f420340, 0x8f820120, 0xafa20010, -0x8f820124, 0x3c040001, 0x24846204, 0x3c050009, -0xafa20014, 0x8fa60020, 0x34a50300, 0xc002b3b, -0x2203821, 0x8f4202e0, 0x24420001, 0xaf4202e0, -0x8f4202e0, 0x8f4202f0, 0x24420001, 0xaf4202f0, -0x8f4202f0, 0x8fa20034, 0x8fbf0058, 0x8fbe0054, -0x8fb50050, 0x8fb3004c, 0x8fb20048, 0x8fb10044, -0x8fb00040, 0x3e00008, 0x27bd0060, 0x27bdfff8, -0x2408ffff, 0x10a00014, 0x4821, 0x3c0aedb8, -0x354a8320, 0x90870000, 0x24840001, 0x3021, -0x1071026, 0x30420001, 0x10400002, 0x81842, -0x6a1826, 0x604021, 0x24c60001, 0x2cc20008, -0x1440fff7, 0x73842, 0x25290001, 0x125102b, -0x1440fff0, 0x0, 0x1001021, 0x3e00008, -0x27bd0008, 0x27bdffb0, 0xafbf0048, 0xafbe0044, -0xafb50040, 0xafb3003c, 0xafb20038, 0xafb10034, -0xafb00030, 0x8f870220, 0xafa70024, 0x8f870200, -0xafa7002c, 0x8f820220, 0x3c0308ff, 0x3463ffff, -0x431024, 0x34420004, 0xaf820220, 0x8f820200, -0x3c03c0ff, 0x3463ffff, 0x431024, 0x34420004, -0xaf820200, 0x8f530358, 0x8f55035c, 0x8f5e0360, -0x8f470364, 0xafa70014, 0x8f470368, 0xafa7001c, -0x8f4202d0, 0x274401c0, 0x24420001, 0xaf4202d0, -0x8f5002d0, 0x8f510204, 0x8f520200, 0xc002ba8, -0x24050400, 0xaf530358, 0xaf55035c, 0xaf5e0360, -0x8fa70014, 0xaf470364, 0x8fa7001c, 0xaf470368, -0xaf5002d0, 0xaf510204, 0xaf520200, 0x8c02025c, -0x27440224, 0xaf4201f0, 0x8c020260, 0x24050200, -0x24060008, 0xaf4201f8, 0x24020006, 0xc002bbf, -0xaf4201f4, 0x3c023b9a, 0x3442ca00, 0xaf4201fc, -0x240203e8, 0x24040002, 0x24030001, 0xaf420294, -0xaf440290, 0xaf43029c, 0x8f820220, 0x30420008, -0x10400004, 0x0, 0xaf430298, 0x10000003, -0x3021, 0xaf440298, 0x3021, 0x3c030001, -0x661821, 0x90636d00, 0x3461021, 0x24c60001, -0xa043022c, 0x2cc2000f, 0x1440fff8, 0x3461821, -0x24c60001, 0x8f820040, 0x24040080, 0x24050080, -0x21702, 0x24420030, 0xa062022c, 0x3461021, -0xc002ba8, 0xa040022c, 0x8fa70024, 0x30e20004, -0x14400006, 0x0, 0x8f820220, 0x3c0308ff, -0x3463fffb, 0x431024, 0xaf820220, 0x8fa7002c, -0x30e20004, 0x14400006, 0x0, 0x8f820200, -0x3c03c0ff, 0x3463fffb, 0x431024, 0xaf820200, -0x8fbf0048, 0x8fbe0044, 0x8fb50040, 0x8fb3003c, -0x8fb20038, 0x8fb10034, 0x8fb00030, 0x3e00008, -0x27bd0050, 0x0, 0x0, 0xaf400104, -0x24040001, 0x410c0, 0x2e21821, 0x24820001, -0x3c010001, 0x230821, 0xa42234d0, 0x402021, -0x2c820080, 0x1440fff8, 0x410c0, 0x24020001, -0x3c010001, 0x370821, 0xa42038d0, 0xaf420100, -0xaf800228, 0xaf80022c, 0xaf800230, 0xaf800234, -0x3e00008, 0x0, 0x27bdffe8, 0xafbf0014, -0xafb00010, 0x8f420104, 0x28420005, 0x10400026, -0x808021, 0x3c020001, 0x8f430104, 0x344230d0, -0x2e22021, 0x318c0, 0x621821, 0x2e31821, -0x83102b, 0x10400015, 0x1021, 0x96070000, -0x24840006, 0x24660006, 0x9482fffc, 0x14470009, -0x2821, 0x9483fffe, 0x96020002, 0x14620006, -0xa01021, 0x94820000, 0x96030004, 0x431026, -0x2c450001, 0xa01021, 0x14400009, 0x24840008, -0x86102b, 0x1440fff0, 0x1021, 0x304200ff, -0x14400030, 0x24020001, 0x1000002e, 0x1021, -0x1000fffa, 0x24020001, 0x2002021, 0xc00240c, -0x24050006, 0x3042007f, 0x218c0, 0x2e31021, -0x3c010001, 0x220821, 0x942230d0, 0x1040fff2, -0x2e31021, 0x3c060001, 0xc23021, 0x94c630d0, -0x10c0ffed, 0x3c080001, 0x350834d2, 0x96070000, -0x610c0, 0x572021, 0x882021, 0x94820000, -0x14470009, 0x2821, 0x94830002, 0x96020002, -0x14620006, 0xa01021, 0x94820004, 0x96030004, -0x431026, 0x2c450001, 0xa01021, 0x14400007, -0x610c0, 0x2e21021, 0x3c060001, 0xc23021, -0x94c634d0, 0x14c0ffeb, 0x610c0, 0x10c0ffd2, -0x24020001, 0x8fbf0014, 0x8fb00010, 0x3e00008, -0x27bd0018, 0x3e00008, 0x0, 0x27bdffb0, -0x801021, 0xafb00030, 0x24500002, 0x2002021, -0x24050006, 0xafb10034, 0x408821, 0xafbf0048, -0xafbe0044, 0xafb50040, 0xafb3003c, 0xc00240c, -0xafb20038, 0x3047007f, 0x710c0, 0x2e21021, -0x3c050001, 0xa22821, 0x94a530d0, 0x50a0001c, -0xa03021, 0x3c090001, 0x352934d2, 0x96280002, -0x510c0, 0x572021, 0x892021, 0x94820000, -0x14480009, 0x3021, 0x94830002, 0x96020002, -0x14620006, 0xc01021, 0x94820004, 0x96030004, -0x431026, 0x2c460001, 0xc01021, 0x14400007, -0x510c0, 0x2e21021, 0x3c050001, 0xa22821, -0x94a534d0, 0x14a0ffeb, 0x510c0, 0xa03021, -0x10c00014, 0x610c0, 0x571821, 0x3c010001, -0x230821, 0x8c2334d0, 0x571021, 0xafa30010, -0x3c010001, 0x220821, 0x8c2234d4, 0x3c040001, -0x24846394, 0xafa20014, 0x8e260000, 0x8e270004, -0x3c050004, 0xc002b3b, 0x34a50400, 0x10000063, -0x3c020800, 0x8f450100, 0x10a00006, 0x510c0, -0x2e21021, 0x3c010001, 0x220821, 0x942234d0, -0xaf420100, 0xa03021, 0x14c00011, 0x628c0, -0x710c0, 0x2e21021, 0xafa70010, 0x3c010001, -0x220821, 0x942230d0, 0x3c040001, 0x248463a0, -0xafa20014, 0x8e260000, 0x8e270004, 0x3c050004, -0xc002b3b, 0x34a50500, 0x10000048, 0x3c020800, -0xb71821, 0x3c020001, 0x96040000, 0x344234d2, -0x621821, 0xa4640000, 0x8e020002, 0x720c0, -0xac620002, 0x2e41021, 0x3c030001, 0x621821, -0x946330d0, 0x2e51021, 0x3c010001, 0x220821, -0xa42334d0, 0x2e41021, 0x3c010001, 0x220821, -0xa42630d0, 0x8f420104, 0x24420001, 0x28420080, -0x1040000f, 0x3c020002, 0x8f420104, 0x3c040001, -0x348430d2, 0x96030000, 0x210c0, 0x571021, -0x441021, 0xa4430000, 0x8e030002, 0xac430002, -0x8f420104, 0x24420001, 0xaf420104, 0x3c020002, -0x2c21024, 0x10400011, 0x72142, 0x3c030001, -0x346338d8, 0x24020003, 0x441023, 0x21080, -0x572021, 0x832021, 0x571021, 0x431021, -0x30e5001f, 0x8c430000, 0x24020001, 0xa21004, -0x621825, 0x1000000c, 0xac830000, 0x24020003, -0x441023, 0x21080, 0x5c2821, 0x5c1021, -0x30e4001f, 0x8c430228, 0x24020001, 0x821004, -0x621825, 0xaca30228, 0x3c020800, 0x34421000, -0x1821, 0xafa20020, 0x8f5e0018, 0x27aa0020, -0x240200ff, 0x13c20002, 0xafaa002c, 0x27c30001, -0x8c020228, 0x609021, 0x1642000e, 0x1e38c0, -0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, -0x8c020228, 0x3c040001, 0x2484635c, 0x3c050009, -0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006b, -0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, -0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, -0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, -0x9821, 0xe08821, 0x263504c0, 0x8f440178, -0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, -0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, -0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, -0xa3482b, 0x822021, 0x100f809, 0x892021, -0x54400006, 0x24130001, 0x8f820054, 0x2021023, -0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, -0x54400017, 0xaf520018, 0x8f420378, 0x24420001, -0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, -0xafa20010, 0x8f820124, 0x3c040001, 0x24846368, -0x3c050009, 0xafa20014, 0x8d460000, 0x10000033, -0x34a50600, 0x8f420308, 0x24130001, 0x24420001, -0xaf420308, 0x8f420308, 0x1000001c, 0x326200ff, -0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, -0x2c4203e9, 0x10400014, 0x9821, 0x24110010, -0x8f42000c, 0x8f440160, 0x8f450164, 0x8f860120, -0xafb10010, 0xafb20014, 0xafa20018, 0x8f42010c, -0x24070008, 0x40f809, 0x24c6001c, 0x1440ffe5, -0x0, 0x8f820054, 0x2021023, 0x2c4203e9, -0x1440ffef, 0x0, 0x326200ff, 0x14400011, -0x0, 0x8f420378, 0x24420001, 0xaf420378, -0x8f420378, 0x8f820120, 0x8faa002c, 0xafa20010, -0x8f820124, 0x3c040001, 0x24846370, 0x3c050009, -0xafa20014, 0x8d460000, 0x34a50700, 0xc002b3b, -0x3c03821, 0x8f4202b4, 0x24420001, 0xaf4202b4, -0x8f4202b4, 0x8f4202f4, 0x24420001, 0xaf4202f4, -0x8f4202f4, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, -0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, -0x3e00008, 0x27bd0050, 0x27bdffa0, 0x801021, -0xafb00040, 0x24500002, 0x2002021, 0x24050006, -0xafb10044, 0x408821, 0xafbf0058, 0xafbe0054, -0xafb50050, 0xafb3004c, 0xc00240c, 0xafb20048, -0x3048007f, 0x810c0, 0x2e21021, 0x3c060001, -0xc23021, 0x94c630d0, 0x10c0001c, 0x3821, -0x3c0a0001, 0x354a34d2, 0x96290002, 0x610c0, -0x572021, 0x8a2021, 0x94820000, 0x14490009, -0x2821, 0x94830002, 0x96020002, 0x14620006, -0xa01021, 0x94820004, 0x96030004, 0x431026, -0x2c450001, 0xa01021, 0x14400008, 0x610c0, -0xc03821, 0x2e21021, 0x3c060001, 0xc23021, -0x94c634d0, 0x14c0ffea, 0x610c0, 0x14c00011, -0xafa70028, 0x810c0, 0x2e21021, 0xafa80010, -0x3c010001, 0x220821, 0x942230d0, 0x3c040001, -0x248463ac, 0xafa20014, 0x8e260000, 0x8e270004, -0x3c050004, 0xc002b3b, 0x34a50900, 0x10000075, -0x3c020800, 0x10e0000c, 0x610c0, 0x2e21021, -0x3c030001, 0x621821, 0x946334d0, 0x710c0, -0x2e21021, 0x3c010001, 0x220821, 0xa42334d0, -0x1000000b, 0x3c040001, 0x2e21021, 0x3c030001, -0x621821, 0x946334d0, 0x810c0, 0x2e21021, -0x3c010001, 0x220821, 0xa42330d0, 0x3c040001, -0x348430d0, 0x8f430100, 0x610c0, 0x2e21021, -0x3c010001, 0x220821, 0xa42334d0, 0x8f420104, -0x2e43821, 0x2821, 0x18400029, 0xaf460100, -0x24e60006, 0x94c3fffc, 0x96020000, 0x14620009, -0x2021, 0x94c3fffe, 0x96020002, 0x14620006, -0x801021, 0x94c20000, 0x96030004, 0x431026, -0x2c440001, 0x801021, 0x50400014, 0x24a50001, -0x8f420104, 0x2442ffff, 0xa2102a, 0x1040000b, -0x24e40004, 0x94820006, 0x8c830008, 0xa482fffe, -0xac830000, 0x8f420104, 0x24a50001, 0x2442ffff, -0xa2102a, 0x1440fff7, 0x24840008, 0x8f420104, -0x2442ffff, 0x10000006, 0xaf420104, 0x8f420104, -0x24c60008, 0xa2102a, 0x1440ffda, 0x24e70008, -0x810c0, 0x2e21021, 0x3c010001, 0x220821, -0x942230d0, 0x14400023, 0x3c020800, 0x3c020002, -0x2c21024, 0x10400012, 0x82142, 0x3c030001, -0x346338d8, 0x24020003, 0x441023, 0x21080, -0x572021, 0x832021, 0x571021, 0x431021, -0x3105001f, 0x24030001, 0x8c420000, 0xa31804, -0x31827, 0x431024, 0x1000000d, 0xac820000, -0x24020003, 0x441023, 0x21080, 0x5c2821, -0x5c1021, 0x3104001f, 0x24030001, 0x8c420228, -0x831804, 0x31827, 0x431024, 0xaca20228, -0x3c020800, 0x34422000, 0x1821, 0xafa20020, -0x8f5e0018, 0x27ab0020, 0x240200ff, 0x13c20002, -0xafab0034, 0x27c30001, 0x8c020228, 0x609021, -0x1642000e, 0x1e38c0, 0x8f42033c, 0x24420001, -0xaf42033c, 0x8f42033c, 0x8c020228, 0x3c040001, -0x2484635c, 0x3c050009, 0xafa00014, 0xafa20010, -0x8fa60020, 0x1000006b, 0x34a50500, 0xf71021, -0x8fa30020, 0x8fa40024, 0xac4304c0, 0xac4404c4, -0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, -0x2c4203e9, 0x1040001b, 0x9821, 0xe08821, -0x263504c0, 0x8f440178, 0x8f45017c, 0x2201821, -0x240b0004, 0xafab0010, 0xafb20014, 0x8f48000c, -0x1021, 0x2f53021, 0xafa80018, 0x8f48010c, -0x24070008, 0xa32821, 0xa3482b, 0x822021, -0x100f809, 0x892021, 0x54400006, 0x24130001, -0x8f820054, 0x2021023, 0x2c4203e9, 0x1440ffe9, -0x0, 0x326200ff, 0x54400017, 0xaf520018, -0x8f420378, 0x24420001, 0xaf420378, 0x8f420378, -0x8f820120, 0x8fab0034, 0xafa20010, 0x8f820124, -0x3c040001, 0x24846368, 0x3c050009, 0xafa20014, -0x8d660000, 0x10000033, 0x34a50600, 0x8f420308, -0x24130001, 0x24420001, 0xaf420308, 0x8f420308, -0x1000001c, 0x326200ff, 0x8f830054, 0x8f820054, -0x247003e8, 0x2021023, 0x2c4203e9, 0x10400014, -0x9821, 0x24110010, 0x8f42000c, 0x8f440160, -0x8f450164, 0x8f860120, 0xafb10010, 0xafb20014, -0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, -0x24c6001c, 0x1440ffe5, 0x0, 0x8f820054, -0x2021023, 0x2c4203e9, 0x1440ffef, 0x0, -0x326200ff, 0x14400011, 0x0, 0x8f420378, -0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, -0x8fab0034, 0xafa20010, 0x8f820124, 0x3c040001, -0x24846370, 0x3c050009, 0xafa20014, 0x8d660000, -0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202b8, -0x24420001, 0xaf4202b8, 0x8f4202b8, 0x8f4202f4, -0x24420001, 0xaf4202f4, 0x8f4202f4, 0x8fbf0058, -0x8fbe0054, 0x8fb50050, 0x8fb3004c, 0x8fb20048, -0x8fb10044, 0x8fb00040, 0x3e00008, 0x27bd0060, -0x0, 0x0, 0x0, 0x27bdffe0, -0x27644000, 0xafbf0018, 0xc002ba8, 0x24051000, -0x3c030001, 0x34632cc0, 0x3c040001, 0x34842ec8, -0x24020020, 0xaf82011c, 0x2e31021, 0xaf800100, -0xaf800104, 0xaf800108, 0xaf800110, 0xaf800114, -0xaf800118, 0xaf800120, 0xaf800124, 0xaf800128, -0xaf800130, 0xaf800134, 0xaf800138, 0xaf4200ec, -0x2e31021, 0xaf4200f0, 0x2e41021, 0xaf4200f4, -0x2e41021, 0xaf4200f8, 0x3c020001, 0x571021, -0x904240f4, 0x1440001c, 0x3c050001, 0x8f82011c, -0x3c040001, 0x24846470, 0x3c050001, 0x34420001, -0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, -0x34a50100, 0xc002b3b, 0x3821, 0x8c020218, -0x30420040, 0x10400014, 0x0, 0x8f82011c, -0x3c040001, 0x2484647c, 0x3c050001, 0x34420004, -0xaf82011c, 0xafa00010, 0xafa00014, 0x8f86011c, -0x10000007, 0x34a50200, 0x3c040001, 0x24846484, -0xafa00010, 0xafa00014, 0x8f86011c, 0x34a50300, -0xc002b3b, 0x3821, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x8fa90010, 0x8f83012c, 0x8faa0014, -0x8fab0018, 0x1060000a, 0x27624fe0, 0x14620002, -0x24680020, 0x27684800, 0x8f820128, 0x11020004, -0x0, 0x8f820124, 0x15020007, 0x0, -0x8f430334, 0x1021, 0x24630001, 0xaf430334, -0x10000039, 0x8f430334, 0xac640000, 0xac650004, -0xac660008, 0xa467000e, 0xac690018, 0xac6a001c, -0xac6b0010, 0xac620014, 0xaf880120, 0x8f4200fc, -0x8f4400f4, 0x2442ffff, 0xaf4200fc, 0x8c820000, -0x10490005, 0x3042ff8f, 0x10400019, 0x3122ff8f, -0x10400018, 0x3c020001, 0x8c830004, 0x2c620010, -0x10400013, 0x3c020001, 0x24630001, 0xac830004, -0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, -0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, -0x14440015, 0x24020001, 0x8f820128, 0x24420020, -0xaf820128, 0x8f820128, 0x1000000f, 0x24020001, -0x3c020001, 0x344230c8, 0x2e21021, 0x54820004, -0x24820008, 0x3c020001, 0x34422ec8, 0x2e21021, -0x402021, 0x24020001, 0xaf4400f4, 0xac890000, -0xac820004, 0x24020001, 0x3e00008, 0x0, -0x3e00008, 0x0, 0x8fa90010, 0x8f83010c, -0x8faa0014, 0x8fab0018, 0x1060000a, 0x276247e0, -0x14620002, 0x24680020, 0x27684000, 0x8f820108, -0x11020004, 0x0, 0x8f820104, 0x15020007, -0x0, 0x8f430338, 0x1021, 0x24630001, -0xaf430338, 0x10000035, 0x8f430338, 0xac640000, -0xac650004, 0xac660008, 0xa467000e, 0xac690018, -0xac6a001c, 0xac6b0010, 0xac620014, 0xaf880100, -0x8f4400ec, 0x8c820000, 0x30420006, 0x10400019, -0x31220006, 0x10400018, 0x3c020001, 0x8c830004, -0x2c620010, 0x10400013, 0x3c020001, 0x24630001, -0xac830004, 0x8f4300f0, 0x34422ec0, 0x2e21021, -0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, -0x2e21021, 0x14440015, 0x24020001, 0x8f820108, -0x24420020, 0xaf820108, 0x8f820108, 0x1000000f, -0x24020001, 0x3c020001, 0x34422ec0, 0x2e21021, -0x54820004, 0x24820008, 0x3c020001, 0x34422cc0, -0x2e21021, 0x402021, 0x24020001, 0xaf4400ec, -0xac890000, 0xac820004, 0x24020001, 0x3e00008, -0x0, 0x3e00008, 0x0, 0x27bdffd8, -0x3c040001, 0x2484648c, 0x3c050001, 0xafbf0024, -0xafb20020, 0xafb1001c, 0xafb00018, 0x8f900104, -0x8f9100b0, 0x8f92011c, 0x34a52500, 0x8f820100, -0x2403021, 0x2203821, 0xafa20010, 0xc002b3b, -0xafb00014, 0x8e020008, 0xafa20010, 0x8e02000c, -0x3c040001, 0x24846498, 0xafa20014, 0x8e060000, -0x8e070004, 0x3c050001, 0xc002b3b, 0x34a52510, -0x8e020018, 0xafa20010, 0x8e02001c, 0x3c040001, -0x248464a4, 0xafa20014, 0x8e060010, 0x8e070014, -0x3c050001, 0xc002b3b, 0x34a52520, 0x3c027f00, -0x2221024, 0x3c030800, 0x54430016, 0x3c030200, -0x8f82009c, 0x3042ffff, 0x14400012, 0x3c030200, -0x3c040001, 0x248464b0, 0x3c050002, 0x34a5f030, -0x3021, 0x3821, 0x36420002, 0xaf82011c, -0x36220001, 0xaf8200b0, 0xaf900104, 0xaf92011c, -0xafa00010, 0xc002b3b, 0xafa00014, 0x10000024, -0x0, 0x2c31024, 0x1040000d, 0x2231024, -0x1040000b, 0x36420002, 0xaf82011c, 0x36220001, -0xaf8200b0, 0xaf900104, 0xaf92011c, 0x8f420330, -0x24420001, 0xaf420330, 0x10000015, 0x8f420330, -0x3c040001, 0x248464b8, 0x240202a9, 0xafa20010, -0xafa00014, 0x8f860144, 0x3c070001, 0x24e764c0, -0xc002b3b, 0x3405dead, 0x8f82011c, 0x34420002, -0xaf82011c, 0x8f820220, 0x34420004, 0xaf820220, -0x8f820140, 0x3c030001, 0x431025, 0xaf820140, -0x8fbf0024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, -0x3e00008, 0x27bd0028, 0x27bdffd8, 0x3c040001, -0x248464e8, 0x3c050001, 0xafbf0024, 0xafb20020, -0xafb1001c, 0xafb00018, 0x8f900124, 0x8f9100a0, -0x8f92011c, 0x34a52600, 0x8f820120, 0x2403021, -0x2203821, 0xafa20010, 0xc002b3b, 0xafb00014, -0x8e020008, 0xafa20010, 0x8e02000c, 0x3c040001, -0x248464f4, 0xafa20014, 0x8e060000, 0x8e070004, -0x3c050001, 0xc002b3b, 0x34a52610, 0x8e020018, -0xafa20010, 0x8e02001c, 0x3c040001, 0x24846500, -0xafa20014, 0x8e060010, 0x8e070014, 0x3c050001, -0xc002b3b, 0x34a52620, 0x3c027f00, 0x2221024, -0x3c030800, 0x54430016, 0x3c030200, 0x8f8200ac, -0x3042ffff, 0x14400012, 0x3c030200, 0x3c040001, -0x2484650c, 0x3c050001, 0x34a5f030, 0x3021, -0x3821, 0x36420002, 0xaf82011c, 0x36220001, -0xaf8200a0, 0xaf900124, 0xaf92011c, 0xafa00010, -0xc002b3b, 0xafa00014, 0x10000024, 0x0, -0x2c31024, 0x1040000d, 0x2231024, 0x1040000b, -0x36420002, 0xaf82011c, 0x36220001, 0xaf8200a0, -0xaf900124, 0xaf92011c, 0x8f42032c, 0x24420001, -0xaf42032c, 0x10000015, 0x8f42032c, 0x3c040001, -0x248464b8, 0x240202e2, 0xafa20010, 0xafa00014, -0x8f860144, 0x3c070001, 0x24e764c0, 0xc002b3b, -0x3405dead, 0x8f82011c, 0x34420002, 0xaf82011c, -0x8f820220, 0x34420004, 0xaf820220, 0x8f820140, -0x3c030001, 0x431025, 0xaf820140, 0x8fbf0024, -0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x3e00008, -0x27bd0028, 0x6021, 0x5021, 0x3021, -0x2821, 0x6821, 0x4821, 0x7821, -0x7021, 0x8f880124, 0x8f870104, 0x1580002e, -0x8f8b011c, 0x11a00014, 0x31620800, 0x8f820120, -0x10460029, 0x0, 0x3c040001, 0x8c846ee4, -0x8cc20000, 0x8cc30004, 0xac820000, 0xac830004, -0x8cc20008, 0xac820008, 0x94c2000e, 0xa482000e, -0x8cc20010, 0x240c0001, 0xac820010, 0x8cc20014, -0x10000012, 0x24c60020, 0x10400017, 0x0, -0x3c040001, 0x8c846ee4, 0x8d020000, 0x8d030004, -0xac820000, 0xac830004, 0x8d020008, 0xac820008, -0x9502000e, 0xa482000e, 0x8d020010, 0x25060020, -0xac820010, 0x8d020014, 0x240c0001, 0xc01821, -0xac820014, 0x27624fe0, 0x43102b, 0x54400001, -0x27634800, 0x603021, 0x1540002f, 0x31620100, -0x11200014, 0x31628000, 0x8f820100, 0x1045002a, -0x31620100, 0x3c040001, 0x8c846ee0, 0x8ca20000, -0x8ca30004, 0xac820000, 0xac830004, 0x8ca20008, -0xac820008, 0x94a2000e, 0xa482000e, 0x8ca20010, -0x240a0001, 0xac820010, 0x8ca20014, 0x10000012, -0x24a50020, 0x10400018, 0x31620100, 0x3c040001, -0x8c846ee0, 0x8ce20000, 0x8ce30004, 0xac820000, -0xac830004, 0x8ce20008, 0xac820008, 0x94e2000e, -0xa482000e, 0x8ce20010, 0x24e50020, 0xac820010, -0x8ce20014, 0x240a0001, 0xa01821, 0xac820014, -0x276247e0, 0x43102b, 0x54400001, 0x27634000, -0x602821, 0x31620100, 0x5440001d, 0x31621000, -0x11a00009, 0x31a20800, 0x10400004, 0x25020020, -0x8f8200a8, 0xa5e20000, 0x25020020, 0xaf820124, -0x8f880124, 0x6821, 0x11800011, 0x31621000, -0x3c040001, 0x8c846ee4, 0x8c820000, 0x8c830004, -0xaf820080, 0xaf830084, 0x8c820008, 0xaf8200a4, -0x9482000e, 0xaf8200ac, 0x8c820010, 0x6021, -0xaf8200a0, 0x8c8d0010, 0x8c8f0014, 0x31621000, -0x1440ff82, 0x0, 0x1120000f, 0x31220800, -0x10400004, 0x3c020002, 0x8f8200b8, 0xa5c20000, -0x3c020002, 0x1221024, 0x10400004, 0x24e20020, -0x8f8200b4, 0xaf8200d4, 0x24e20020, 0xaf820104, -0x8f870104, 0x4821, 0x1140ff70, 0x0, -0x3c040001, 0x8c846ee0, 0x8c820000, 0x8c830004, -0xaf820090, 0xaf830094, 0x8c820008, 0xaf8200b4, -0x9482000e, 0xaf82009c, 0x8c820010, 0x5021, -0xaf8200b0, 0x8c890010, 0x1000ff60, 0x8c8e0014, -0x3e00008, 0x0, 0x6021, 0x5821, -0x3021, 0x2821, 0x6821, 0x5021, -0x7821, 0x7021, 0x8f880124, 0x8f870104, -0x3c180100, 0x1580002e, 0x8f89011c, 0x11a00014, -0x31220800, 0x8f820120, 0x10460029, 0x0, -0x3c040001, 0x8c846ee4, 0x8cc20000, 0x8cc30004, -0xac820000, 0xac830004, 0x8cc20008, 0xac820008, -0x94c2000e, 0xa482000e, 0x8cc20010, 0x240c0001, -0xac820010, 0x8cc20014, 0x10000012, 0x24c60020, -0x10400017, 0x0, 0x3c040001, 0x8c846ee4, -0x8d020000, 0x8d030004, 0xac820000, 0xac830004, -0x8d020008, 0xac820008, 0x9502000e, 0xa482000e, -0x8d020010, 0x25060020, 0xac820010, 0x8d020014, -0x240c0001, 0xc01821, 0xac820014, 0x27624fe0, -0x43102b, 0x54400001, 0x27634800, 0x603021, -0x1560002f, 0x31220100, 0x11400014, 0x31228000, -0x8f820100, 0x1045002a, 0x31220100, 0x3c040001, -0x8c846ee0, 0x8ca20000, 0x8ca30004, 0xac820000, -0xac830004, 0x8ca20008, 0xac820008, 0x94a2000e, -0xa482000e, 0x8ca20010, 0x240b0001, 0xac820010, -0x8ca20014, 0x10000012, 0x24a50020, 0x10400018, -0x31220100, 0x3c040001, 0x8c846ee0, 0x8ce20000, -0x8ce30004, 0xac820000, 0xac830004, 0x8ce20008, -0xac820008, 0x94e2000e, 0xa482000e, 0x8ce20010, -0x24e50020, 0xac820010, 0x8ce20014, 0x240b0001, -0xa01821, 0xac820014, 0x276247e0, 0x43102b, -0x54400001, 0x27634000, 0x602821, 0x31220100, -0x5440001d, 0x31221000, 0x11a00009, 0x31a20800, -0x10400004, 0x25020020, 0x8f8200a8, 0xa5e20000, -0x25020020, 0xaf820124, 0x8f880124, 0x6821, -0x11800011, 0x31221000, 0x3c040001, 0x8c846ee4, -0x8c820000, 0x8c830004, 0xaf820080, 0xaf830084, -0x8c820008, 0xaf8200a4, 0x9482000e, 0xaf8200ac, -0x8c820010, 0x6021, 0xaf8200a0, 0x8c8d0010, -0x8c8f0014, 0x31221000, 0x14400022, 0x0, -0x1140000f, 0x31420800, 0x10400004, 0x3c020002, -0x8f8200b8, 0xa5c20000, 0x3c020002, 0x1421024, -0x10400004, 0x24e20020, 0x8f8200b4, 0xaf8200d4, -0x24e20020, 0xaf820104, 0x8f870104, 0x5021, -0x11600010, 0x0, 0x3c040001, 0x8c846ee0, -0x8c820000, 0x8c830004, 0xaf820090, 0xaf830094, -0x8c820008, 0xaf8200b4, 0x9482000e, 0xaf82009c, -0x8c820010, 0x5821, 0xaf8200b0, 0x8c8a0010, -0x8c8e0014, 0x8f820070, 0x3c031000, 0x431024, -0x1040ff5c, 0x0, 0x8f820054, 0x24420005, -0xaf820078, 0x8c040234, 0x10800016, 0x1821, -0x3c020001, 0x571021, 0x8c4240e8, 0x24420005, -0x3c010001, 0x370821, 0xac2240e8, 0x3c020001, -0x571021, 0x8c4240e8, 0x44102b, 0x14400009, -0x24020001, 0x3c030080, 0x3c010001, 0x370821, -0xac2040e8, 0x3c010001, 0x370821, 0x1000000c, -0xa02240f0, 0x3c020001, 0x571021, 0x904240f0, -0x14400006, 0x3c020080, 0x3c020001, 0x571021, -0x904240f1, 0x10400002, 0x3c020080, 0x621825, -0x8c040230, 0x10800013, 0x0, 0x3c020001, -0x571021, 0x8c4240ec, 0x24420005, 0x3c010001, -0x370821, 0xac2240ec, 0x3c020001, 0x571021, -0x8c4240ec, 0x44102b, 0x14400006, 0x0, -0x3c010001, 0x370821, 0xac2040ec, 0x10000006, -0x781825, 0x3c020001, 0x571021, 0x904240f2, -0x54400001, 0x781825, 0x1060ff1a, 0x0, -0x8f420000, 0x10400007, 0x0, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x431025, 0xaf820060, -0x8f420000, 0x10400003, 0x0, 0x1000ff05, -0xaf80004c, 0x1000ff03, 0xaf800048, 0x3e00008, -0x0, 0x0, 0x0, 0x3c020001, -0x8c426d28, 0x27bdffe8, 0xafbf0014, 0x14400012, -0xafb00010, 0x3c100001, 0x26106f90, 0x2002021, -0xc002ba8, 0x24052000, 0x26021fe0, 0x3c010001, -0xac226eec, 0x3c010001, 0xac226ee8, 0xac020250, -0x24022000, 0xac100254, 0xac020258, 0x24020001, -0x3c010001, 0xac226d28, 0x8fbf0014, 0x8fb00010, -0x3e00008, 0x27bd0018, 0x3c090001, 0x8d296eec, -0x8c820000, 0x8fa30010, 0x8fa80014, 0xad220000, -0x8c820004, 0xad250008, 0xad220004, 0x8f820054, -0xad260010, 0xad270014, 0xad230018, 0xad28001c, -0xad22000c, 0x2529ffe0, 0x3c020001, 0x24426f90, -0x122102b, 0x10400003, 0x0, 0x3c090001, -0x8d296ee8, 0x3c020001, 0x8c426d10, 0xad220000, -0x3c020001, 0x8c426d10, 0x3c010001, 0xac296eec, -0xad220004, 0xac090250, 0x3e00008, 0x0, -0x27bdffd0, 0xafb00010, 0x3c100001, 0x8e106eec, -0x3c020001, 0x8c426d10, 0xafb10014, 0x808821, -0xafbe0024, 0x8fbe0040, 0x8fa40048, 0xafb20018, -0xa09021, 0xafbf0028, 0xafb50020, 0xafb3001c, -0xae020000, 0x3c020001, 0x8c426d10, 0xc09821, -0xe0a821, 0x10800006, 0xae020004, 0x26050008, -0xc002bb3, 0x24060018, 0x10000005, 0x2610ffe0, -0x26040008, 0xc002ba8, 0x24050018, 0x2610ffe0, -0x3c030001, 0x24636f90, 0x203102b, 0x10400003, -0x0, 0x3c100001, 0x8e106ee8, 0x8e220000, -0xae020000, 0x8e220004, 0xae120008, 0xae020004, -0x8f820054, 0xae130010, 0xae150014, 0xae1e0018, -0x8fa80044, 0xae08001c, 0xae02000c, 0x2610ffe0, -0x203102b, 0x10400003, 0x0, 0x3c100001, -0x8e106ee8, 0x3c020001, 0x8c426d10, 0xae020000, -0x3c020001, 0x8c426d10, 0x3c010001, 0xac306eec, -0xae020004, 0xac100250, 0x8fbf0028, 0x8fbe0024, -0x8fb50020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, -0x8fb00010, 0x3e00008, 0x27bd0030, 0x851821, -0x83102b, 0x10400006, 0x0, 0xac800000, -0x24840004, 0x83102b, 0x5440fffd, 0xac800000, -0x3e00008, 0x0, 0xa61821, 0xa3102b, -0x10400007, 0x0, 0x8c820000, 0xaca20000, -0x24a50004, 0xa3102b, 0x1440fffb, 0x24840004, -0x3e00008, 0x0, 0x861821, 0x83102b, -0x10400007, 0x0, 0x8ca20000, 0xac820000, -0x24840004, 0x83102b, 0x1440fffb, 0x24a50004, -0x3e00008, 0x0, 0x63080, 0x861821, -0x83102b, 0x10400006, 0x0, 0xac850000, -0x24840004, 0x83102b, 0x5440fffd, 0xac850000, -0x3e00008, 0x0, 0x0, 0x26e50028, -0xa03021, 0x274301c0, 0x8f4d0358, 0x8f47035c, -0x8f480360, 0x8f490364, 0x8f4a0368, 0x8f4b0204, -0x8f4c0200, 0x24640400, 0x64102b, 0x10400008, -0x3c0208ff, 0x8cc20000, 0xac620000, 0x24630004, -0x64102b, 0x1440fffb, 0x24c60004, 0x3c0208ff, -0x3442ffff, 0x3c03c0ff, 0xaf4d0358, 0xaf47035c, -0xaf480360, 0xaf490364, 0xaf4a0368, 0xaf4b0204, -0xaf4c0200, 0x8f840220, 0x3463ffff, 0x8f860200, -0x821024, 0x34420004, 0xc31824, 0x34630004, -0xaf820220, 0xaf830200, 0x8ca20214, 0xac020084, -0x8ca20218, 0xac020088, 0x8ca2021c, 0xac02008c, -0x8ca20220, 0xac020090, 0x8ca20224, 0xac020094, -0x8ca20228, 0xac020098, 0x8ca2022c, 0xac02009c, -0x8ca20230, 0xac0200a0, 0x8ca20234, 0xac0200a4, -0x8ca20238, 0xac0200a8, 0x8ca2023c, 0xac0200ac, -0x8ca20240, 0xac0200b0, 0x8ca20244, 0xac0200b4, -0x8ca20248, 0xac0200b8, 0x8ca2024c, 0xac0200bc, -0x8ca2001c, 0xac020080, 0x8ca20018, 0xac0200c0, -0x8ca20020, 0xac0200cc, 0x8ca20024, 0xac0200d0, -0x8ca201d0, 0xac0200e0, 0x8ca201d4, 0xac0200e4, -0x8ca201d8, 0xac0200e8, 0x8ca201dc, 0xac0200ec, -0x8ca201e0, 0xac0200f0, 0x8ca20098, 0x8ca3009c, -0xac0300fc, 0x8ca200a8, 0x8ca300ac, 0xac0300f4, -0x8ca200a0, 0x8ca300a4, 0x30840004, 0xac0300f8, -0x14800007, 0x30c20004, 0x8f820220, 0x3c0308ff, -0x3463fffb, 0x431024, 0xaf820220, 0x30c20004, -0x14400006, 0x0, 0x8f820200, 0x3c03c0ff, -0x3463fffb, 0x431024, 0xaf820200, 0x8f4202dc, -0xa34005c5, 0x24420001, 0xaf4202dc, 0x8f4202dc, -0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, -0xafb00020, 0x8f430024, 0x8f420020, 0x10620038, -0x0, 0x8f430020, 0x8f420024, 0x622023, -0x4810003, 0x0, 0x8f420040, 0x822021, -0x8f430030, 0x8f420024, 0x43102b, 0x14400005, -0x0, 0x8f430040, 0x8f420024, 0x10000005, -0x621023, 0x8f420030, 0x8f430024, 0x431023, -0x2442ffff, 0x406021, 0x8c102a, 0x54400001, -0x806021, 0x8f4a0024, 0x8f490040, 0x8f480024, -0x8f440180, 0x8f450184, 0x8f460024, 0x8f4b001c, -0x24070001, 0xafa70010, 0x84100, 0x1001821, -0x14c5021, 0x2529ffff, 0x1498024, 0xafb00014, -0x8f470014, 0x1021, 0x63100, 0xafa70018, -0xa32821, 0xa3382b, 0x822021, 0x872021, -0x8f420108, 0x1663021, 0x40f809, 0xc3900, -0x54400001, 0xaf500024, 0x8f430024, 0x8f420020, -0x14620018, 0x0, 0x8f420000, 0x10400007, -0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, -0x0, 0x10000005, 0x0, 0xaf800048, -0x8f820048, 0x1040fffd, 0x0, 0x8f820060, -0x2403ffef, 0x431024, 0xaf820060, 0x8f420000, -0x10400003, 0x0, 0x10000002, 0xaf80004c, -0xaf800048, 0x8fbf0024, 0x8fb00020, 0x3e00008, -0x27bd0028, 0x3e00008, 0x0, 0x27bdffc0, -0x32c20020, 0xafbf0038, 0xafb30034, 0xafb20030, -0xafb1002c, 0x10400004, 0xafb00028, 0x8f530028, -0x10000002, 0x0, 0x8f530020, 0x8f420030, -0x105300eb, 0x21100, 0x8f43001c, 0x628021, -0x8e040000, 0x8e050004, 0x96120008, 0x8f420090, -0x9611000a, 0x3246ffff, 0x46102a, 0x10400017, -0x0, 0x8f8200d8, 0x8f430098, 0x431023, -0x2442dcbe, 0xaf420090, 0x8f420090, 0x2842dcbf, -0x10400005, 0x0, 0x8f420090, 0x8f430144, -0x431021, 0xaf420090, 0x8f420090, 0x46102a, -0x10400006, 0x0, 0x8f420348, 0x24420001, -0xaf420348, 0x100000e1, 0x8f420348, 0x8f8200fc, -0x14400006, 0x0, 0x8f420344, 0x24420001, -0xaf420344, 0x100000d9, 0x8f420344, 0x934205c2, -0x1040000b, 0x32c20008, 0x10400008, 0x32220200, -0x10400006, 0x3c034000, 0x9602000e, 0xaf4300ac, -0x21400, 0x10000002, 0xaf4200b0, 0xaf4000ac, -0x32220004, 0x1040007f, 0x32220800, 0x10400003, -0x3247ffff, 0x10000002, 0x24020020, 0x24020004, -0xafa20010, 0x8f420030, 0xafa20014, 0x8f420010, -0x3c030002, 0x431025, 0xafa20018, 0x8f460098, -0x8f420108, 0x40f809, 0x0, 0x104000b7, -0x0, 0x8f42009c, 0x8f430094, 0x2421021, -0xaf42009c, 0xae03000c, 0x8f4200ac, 0x10400008, -0x3c034000, 0x8f420094, 0x431025, 0xafa20020, -0x8f42009c, 0x8f4300b0, 0x10000004, 0x431025, -0x8f420094, 0xafa20020, 0x8f42009c, 0xafa20024, -0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, -0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, -0x8f440270, 0x8f450274, 0x401821, 0x1021, -0xa32821, 0xa3302b, 0x822021, 0x862021, -0x32230060, 0x24020040, 0xaf440270, 0xaf450274, -0x10620017, 0x2c620041, 0x10400005, 0x24020020, -0x10620008, 0x24020001, 0x10000026, 0x0, -0x24020060, 0x10620019, 0x24020001, 0x10000021, -0x0, 0x8f420278, 0x8f43027c, 0x24630001, -0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, -0x8f420278, 0x8f43027c, 0x10000016, 0x24020001, -0x8f420280, 0x8f430284, 0x24630001, 0x2c640001, -0x441021, 0xaf420280, 0xaf430284, 0x8f420280, -0x8f430284, 0x1000000b, 0x24020001, 0x8f420288, -0x8f43028c, 0x24630001, 0x2c640001, 0x441021, -0xaf420288, 0xaf43028c, 0x8f420288, 0x8f43028c, -0x24020001, 0xa34205c2, 0x8f420098, 0x3244ffff, -0x2406fff8, 0x8f45013c, 0x441021, 0x24420007, -0x461024, 0x24840007, 0xaf420094, 0x8f420090, -0x8f430094, 0x862024, 0x441023, 0x65182b, -0x14600005, 0xaf420090, 0x8f420094, 0x8f430144, -0x431023, 0xaf420094, 0x8f420094, 0x10000023, -0xaf40009c, 0x3247ffff, 0x50e00022, 0x32c20020, -0x14400002, 0x24020010, 0x24020002, 0xafa20010, -0x8f420030, 0xafa20014, 0x8f420010, 0xafa20018, -0x8f460098, 0x8f420108, 0x40f809, 0x0, -0x1040003a, 0x3245ffff, 0x8f420098, 0x8f430090, -0x8f46013c, 0x451021, 0xaf420098, 0x8f42009c, -0x8f440098, 0xa34005c2, 0x651823, 0xaf430090, -0x451021, 0x86202b, 0x14800005, 0xaf42009c, -0x8f420098, 0x8f430144, 0x431023, 0xaf420098, -0x32c20020, 0x10400005, 0x0, 0x8f420358, -0x2442ffff, 0xaf420358, 0x8f420358, 0x8f420030, -0x8f430040, 0x24420001, 0x2463ffff, 0x431024, -0xaf420030, 0x8f420030, 0x14530018, 0x0, -0x8f420000, 0x10400007, 0x0, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x2403fff7, 0x431024, -0xaf820060, 0x8f420000, 0x10400003, 0x0, -0x10000002, 0xaf80004c, 0xaf800048, 0x8fbf0038, -0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, -0x3e00008, 0x27bd0040, 0x3e00008, 0x0, -0x27bdffd0, 0x32c20020, 0xafbf002c, 0xafb20028, -0xafb10024, 0x10400004, 0xafb00020, 0x8f520028, -0x10000002, 0x0, 0x8f520020, 0x8f420030, -0x105200b5, 0x21100, 0x8f43001c, 0x628021, -0x8e040000, 0x8e050004, 0x96110008, 0x8f420090, -0x9607000a, 0x3226ffff, 0x46102a, 0x10400017, -0x0, 0x8f8200d8, 0x8f430098, 0x431023, -0x2442dc46, 0xaf420090, 0x8f420090, 0x2842dc47, -0x10400005, 0x0, 0x8f420090, 0x8f430144, -0x431021, 0xaf420090, 0x8f420090, 0x46102a, -0x10400006, 0x0, 0x8f420348, 0x24420001, -0xaf420348, 0x100000ab, 0x8f420348, 0x8f8600fc, -0x10c0000c, 0x0, 0x8f8200f4, 0x2403fff8, -0x431024, 0x461023, 0x218c3, 0x58600001, -0x24630100, 0x8f42008c, 0x43102b, 0x14400006, -0x712c2, 0x8f420344, 0x24420001, 0xaf420344, -0x10000098, 0x8f420344, 0x934305c2, 0x1060000f, -0x30460001, 0x8f420010, 0x34480400, 0x32c20008, -0x10400008, 0x30e20200, 0x10400006, 0x3c034000, -0x9602000e, 0xaf4300ac, 0x21400, 0x10000004, -0xaf4200b0, 0x10000002, 0xaf4000ac, 0x8f480010, -0x30e20004, 0x10400045, 0x3227ffff, 0x8f4900ac, -0x11200005, 0x30c200ff, 0x14400006, 0x24020040, -0x10000004, 0x24020008, 0x14400002, 0x24020020, -0x24020004, 0xafa20010, 0x8f430030, 0x11200004, -0xafa30014, 0x8f4200b0, 0x621025, 0xafa20014, -0x3c020002, 0x1021025, 0xafa20018, 0x8f460098, -0x8f420108, 0x40f809, 0x0, 0x10400069, -0x3224ffff, 0x8f42008c, 0x8f430094, 0x24420001, -0xaf42008c, 0x24020001, 0xae03000c, 0xa34205c2, -0x8f420098, 0x2406fff8, 0x8f45013c, 0x441021, -0x24420007, 0x461024, 0x24840007, 0xaf420094, -0x8f420090, 0x8f430094, 0x862024, 0x441023, -0x65182b, 0x14600005, 0xaf420090, 0x8f420094, -0x8f430144, 0x431023, 0xaf420094, 0x8f430094, -0x8f420140, 0x43102b, 0x10400009, 0x0, -0x8f43013c, 0x8f440094, 0x8f420090, 0x8f450138, -0x641823, 0x431023, 0xaf420090, 0xaf450094, -0x8f420094, 0x1000001f, 0xaf420098, 0x10e0001d, -0x30c200ff, 0x14400002, 0x24020010, 0x24020002, -0xafa20010, 0x8f420030, 0xafa80018, 0xafa20014, -0x8f460098, 0x8f420108, 0x40f809, 0x0, -0x10400030, 0x3225ffff, 0x8f420098, 0x8f44013c, -0x451021, 0xaf420098, 0x8f420090, 0x8f430098, -0xa34005c2, 0x451023, 0x64182b, 0x14600005, -0xaf420090, 0x8f420098, 0x8f430144, 0x431023, -0xaf420098, 0x8f420030, 0x8f430040, 0x24420001, -0x2463ffff, 0x431024, 0xaf420030, 0x8f420030, -0x14520018, 0x0, 0x8f420000, 0x10400007, -0x0, 0xaf80004c, 0x8f82004c, 0x1040fffd, -0x0, 0x10000005, 0x0, 0xaf800048, -0x8f820048, 0x1040fffd, 0x0, 0x8f820060, -0x2403fff7, 0x431024, 0xaf820060, 0x8f420000, -0x10400003, 0x0, 0x10000002, 0xaf80004c, -0xaf800048, 0x8fbf002c, 0x8fb20028, 0x8fb10024, -0x8fb00020, 0x3e00008, 0x27bd0030, 0x3e00008, -0x0, 0x27bdffd8, 0x3c020001, 0x34422ec0, -0xafbf0020, 0x8f4300f0, 0x8f840108, 0x2e21021, -0x54620004, 0x24620008, 0x3c020001, 0x34422cc0, -0x2e21021, 0x401821, 0xaf4300f0, 0xac600000, -0x8f4200ec, 0x8c660004, 0x14620004, 0x3c020001, -0x24820020, 0x1000000f, 0xaf820108, 0x8f4300f0, -0x34422ec0, 0x2e21021, 0x54620004, 0x24620008, -0x3c020001, 0x34422cc0, 0x2e21021, 0x401821, -0x8c620004, 0x21140, 0x821021, 0xaf820108, -0xac600000, 0x8c850018, 0x30a20036, 0x1040006c, -0x30a20001, 0x8c82001c, 0x8f430040, 0x8f440034, -0x24420001, 0x2463ffff, 0x431024, 0x862021, -0xaf42002c, 0x30a20030, 0x14400006, 0xaf440034, -0x8f420034, 0x8c03023c, 0x43102b, 0x144000b4, -0x0, 0x32c20010, 0x10400028, 0x24070008, -0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, -0x8f860120, 0x24020080, 0xafa20010, 0xafa30014, -0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, -0x14400011, 0x24020001, 0x3c010001, 0x370821, -0xa02240f1, 0x8f820124, 0xafa20010, 0x8f820128, -0x3c040001, 0x248467c4, 0xafa20014, 0x8f46002c, -0x8f870120, 0x3c050009, 0xc002b3b, 0x34a51100, -0x10000036, 0x0, 0x8f420300, 0x8f43002c, -0x24420001, 0xaf420300, 0x8f420300, 0x24020001, -0xa34205c1, 0x10000026, 0xaf430038, 0x8f440170, -0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, -0x24020020, 0xafa20010, 0xafa30014, 0xafa80018, -0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, -0x24020001, 0x3c010001, 0x370821, 0xa02240f0, -0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, -0x248467b8, 0xafa20014, 0x8f46002c, 0x8f870120, -0x3c050009, 0xc002b3b, 0x34a50900, 0x1000000f, -0x0, 0x8f420300, 0x24420001, 0xaf420300, -0x8f420300, 0x8f42002c, 0xa34005c1, 0xaf420038, -0x3c010001, 0x370821, 0xa02040f1, 0x3c010001, -0x370821, 0xa02040f0, 0xaf400034, 0x8f420314, -0x24420001, 0xaf420314, 0x10000059, 0x8f420314, -0x10400022, 0x30a27000, 0x8c85001c, 0x8f420028, -0xa22023, 0x4810003, 0x0, 0x8f420040, -0x822021, 0x8f420358, 0x8f430000, 0xaf450028, -0x441021, 0x10600007, 0xaf420358, 0xaf80004c, -0x8f82004c, 0x1040fffd, 0x0, 0x10000005, -0x0, 0xaf800048, 0x8f820048, 0x1040fffd, -0x0, 0x8f820060, 0x34420008, 0xaf820060, -0x8f420000, 0x10400003, 0x0, 0x10000038, -0xaf80004c, 0x10000036, 0xaf800048, 0x1040002f, -0x30a21000, 0x1040000c, 0x30a24000, 0x8c83001c, -0x8f420050, 0x622023, 0x4820001, 0x24840200, -0x8f42035c, 0x441021, 0xaf42035c, 0x8f420368, -0x1000001a, 0xaf430050, 0x1040000c, 0x32c28000, -0x8c83001c, 0x8f420070, 0x622023, 0x4820001, -0x24840400, 0x8f420364, 0x441021, 0xaf420364, -0x8f420368, 0x1000000d, 0xaf430070, 0x1040000e, -0x3c020800, 0x8c83001c, 0x8f420060, 0x622023, -0x4820001, 0x24840100, 0x8f420360, 0x441021, -0xaf420360, 0x8f420368, 0xaf430060, 0x441021, -0xaf420368, 0x3c020800, 0x2c21024, 0x50400008, -0x36940040, 0x10000006, 0x0, 0x30a20100, -0x10400003, 0x0, 0xc002bd8, 0x0, -0x8fbf0020, 0x3e00008, 0x27bd0028, 0x3e00008, -0x0, 0x27bdffa8, 0xafbf0050, 0xafbe004c, -0xafb50048, 0xafb30044, 0xafb20040, 0xafb1003c, -0xafb00038, 0x8f910108, 0x26220020, 0xaf820108, -0x8e320018, 0xa821, 0x32420024, 0x104001ba, -0xf021, 0x8e26001c, 0x8f43001c, 0x61100, -0x621821, 0x8c70000c, 0x9604000c, 0x962d0016, -0x9473000a, 0x2c8305dd, 0x38828870, 0x2c420001, -0x621825, 0x10600015, 0x2821, 0x32c20040, -0x10400015, 0x24020800, 0x96030014, 0x14620012, -0x3402aaaa, 0x9603000e, 0x14620007, 0x2021, -0x96030010, 0x24020300, 0x14620004, 0x801021, -0x96020012, 0x2c440001, 0x801021, 0x54400006, -0x24050016, 0x10000004, 0x0, 0x24020800, -0x50820001, 0x2405000e, 0x934205c3, 0x14400008, -0x5821, 0x240b0001, 0x32620180, 0xaf4500a8, -0xaf5000a0, 0x10400002, 0xaf4600a4, 0xa34b05c3, -0x10a00085, 0x2054021, 0x91020000, 0x3821, -0x3042000f, 0x25080, 0x32c20002, 0x10400012, -0x10a1821, 0x32620002, 0x10400010, 0x32c20001, -0x1002021, 0x94820000, 0x24840002, 0xe23821, -0x83102b, 0x1440fffb, 0x30e2ffff, 0x71c02, -0x623821, 0x71c02, 0x30e2ffff, 0x623821, -0x71027, 0xa502000a, 0x32c20001, 0x1040006a, -0x32620001, 0x10400068, 0x0, 0x8f4200a8, -0x10400065, 0x0, 0x8f4200a0, 0x8f4300a8, -0x431021, 0x904c0009, 0x318900ff, 0x39230006, -0x3182b, 0x39220011, 0x2102b, 0x621824, -0x1060000c, 0x3c050006, 0x8f4200a4, 0x3c040001, -0x248467d4, 0xafa20010, 0x8f4200a0, 0x34a54600, -0x1203821, 0xc002b3b, 0xafa20014, 0x1000004e, -0x0, 0x32c20004, 0x14400013, 0x2821, -0x316200ff, 0x14400004, 0x0, 0x95020002, -0x1000000d, 0x4a2823, 0x9505000c, 0x9502000e, -0x95030010, 0xa22821, 0xa32821, 0x95030012, -0x91040009, 0x95020002, 0xa32821, 0xa42821, -0x4a1023, 0xa22821, 0x2002021, 0x94820000, -0x24840002, 0xe23821, 0x88102b, 0x1440fffb, -0x71c02, 0x30e2ffff, 0x623821, 0x71c02, -0x30e2ffff, 0x623821, 0x1a52821, 0x51c02, -0x30a2ffff, 0x622821, 0x51c02, 0x30a2ffff, -0x622821, 0xa72823, 0x51402, 0xa22821, -0x30a5ffff, 0x50a00001, 0x3405ffff, 0x316200ff, -0x14400008, 0x318300ff, 0x8f4300a0, 0x8f4200a8, -0x624021, 0x91020000, 0x3042000f, 0x25080, -0x318300ff, 0x24020006, 0x14620003, 0x10a1021, -0x10000002, 0x24440010, 0x24440006, 0x316200ff, -0x14400006, 0x0, 0x94820000, 0xa22821, -0x51c02, 0x30a2ffff, 0x622821, 0x934205c3, -0x10400003, 0x32620100, 0x50400003, 0xa4850000, -0x52827, 0xa4850000, 0x9622000e, 0x8f43009c, -0x621821, 0x32a200ff, 0x10400007, 0xaf43009c, -0x3c024000, 0x2021025, 0xafa20020, 0x8f42009c, -0x10000003, 0x5e1025, 0xafb00020, 0x8f42009c, -0xafa20024, 0x32620080, 0x10400010, 0x32620100, -0x8f4200b4, 0x24430001, 0x210c0, 0x571021, -0xaf4300b4, 0x8fa30020, 0x8fa40024, 0x3c010001, -0x220821, 0xac2338e8, 0x3c010001, 0x220821, -0xac2438ec, 0x100000a5, 0x32c20020, 0x10400064, -0x0, 0x8f4200b4, 0x24430001, 0x210c0, -0x571021, 0xaf4300b4, 0x8fa30020, 0x8fa40024, -0x3c010001, 0x220821, 0xac2338e8, 0x3c010001, -0x220821, 0xac2438ec, 0x8f4200b4, 0x10400051, -0x3821, 0x3c090001, 0x352938e8, 0x3c08001f, -0x3508ffff, 0x240bffff, 0x340affff, 0x710c0, -0x571021, 0x491021, 0x8c430000, 0x8c440004, -0xafa30028, 0xafa4002c, 0x8f8200fc, 0x8fa30028, -0x8fa4002c, 0xac430000, 0xac440004, 0x24420008, -0xaf8200f0, 0x8f42008c, 0x2442ffff, 0xaf42008c, -0x97a2002e, 0x8f440270, 0x8f450274, 0x401821, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaf440270, 0xaf450274, 0x8fa20028, -0x481024, 0x90430000, 0x30630001, 0x1460000b, -0x402021, 0x8f420278, 0x8f43027c, 0x24630001, -0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, -0x8f420278, 0x1000001a, 0x8f43027c, 0x8c820000, -0x144b000e, 0x0, 0x94820004, 0x144a000b, -0x0, 0x8f420288, 0x8f43028c, 0x24630001, -0x2c640001, 0x441021, 0xaf420288, 0xaf43028c, -0x8f420288, 0x1000000a, 0x8f43028c, 0x8f420280, -0x8f430284, 0x24630001, 0x2c640001, 0x441021, -0xaf420280, 0xaf430284, 0x8f420280, 0x8f430284, -0x8f4200b4, 0x24e70001, 0xe2102b, 0x1440ffb8, -0x710c0, 0xa34005c3, 0x1000003f, 0xaf4000b4, -0x8f8200fc, 0x8fa30020, 0x8fa40024, 0xac430000, -0xac440004, 0x24420008, 0xaf8200f0, 0x8f42009c, -0x8f46008c, 0x8f440270, 0x8f450274, 0x401821, -0x1021, 0x24c6ffff, 0xaf46008c, 0xa32821, -0xa3302b, 0x822021, 0x862021, 0xaf440270, -0xaf450274, 0x92020000, 0x30420001, 0x1440000c, -0x2402ffff, 0x8f420278, 0x8f43027c, 0x24630001, -0x2c640001, 0x441021, 0xaf420278, 0xaf43027c, -0x8f420278, 0x8f43027c, 0x1000001c, 0x32c20020, -0x8e030000, 0x1462000f, 0x3402ffff, 0x96030004, -0x1462000c, 0x0, 0x8f420288, 0x8f43028c, -0x24630001, 0x2c640001, 0x441021, 0xaf420288, -0xaf43028c, 0x8f420288, 0x8f43028c, 0x1000000b, -0x32c20020, 0x8f420280, 0x8f430284, 0x24630001, -0x2c640001, 0x441021, 0xaf420280, 0xaf430284, -0x8f420280, 0x8f430284, 0x32c20020, 0x10400005, -0xaf40009c, 0x8f420358, 0x2442ffff, 0xaf420358, -0x8f420358, 0x8e22001c, 0x8f430040, 0x24420001, -0x2463ffff, 0x431024, 0xaf42002c, 0x32420060, -0x14400008, 0x32c20010, 0x8f420034, 0x24420001, -0xaf420034, 0x8c03023c, 0x43102b, 0x14400102, -0x32c20010, 0x10400018, 0x24070008, 0x8f440170, -0x8f450174, 0x8f43002c, 0x8f48000c, 0x8f860120, -0x24020080, 0xafa20010, 0xafa30014, 0xafa80018, -0x8f42010c, 0x40f809, 0x24c6001c, 0x10400047, -0x24020001, 0x8f420300, 0x8f43002c, 0x24420001, -0xaf420300, 0x8f420300, 0x24020001, 0xa34205c1, -0x1000007c, 0xaf430038, 0x8f440170, 0x8f450174, -0x8f43002c, 0x8f48000c, 0x8f860120, 0x24020020, -0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, -0x40f809, 0x24c6001c, 0x10400057, 0x24020001, -0x10000065, 0x0, 0x32420012, 0x10400075, -0x32420001, 0x9622000e, 0x8f43009c, 0x621821, -0x32c20020, 0x10400005, 0xaf43009c, 0x8f420358, -0x2442ffff, 0xaf420358, 0x8f420358, 0x8e22001c, -0x8f430040, 0x24420001, 0x2463ffff, 0x431024, -0xaf42002c, 0x32420010, 0x14400008, 0x32c20010, -0x8f420034, 0x24420001, 0xaf420034, 0x8c03023c, -0x43102b, 0x144000bc, 0x32c20010, 0x10400028, -0x24070008, 0x8f440170, 0x8f450174, 0x8f43002c, -0x8f48000c, 0x8f860120, 0x24020080, 0xafa20010, -0xafa30014, 0xafa80018, 0x8f42010c, 0x40f809, -0x24c6001c, 0x14400011, 0x24020001, 0x3c010001, -0x370821, 0xa02240f1, 0x8f820124, 0xafa20010, -0x8f820128, 0x3c040001, 0x248467c4, 0xafa20014, -0x8f46002c, 0x8f870120, 0x3c050009, 0xc002b3b, -0x34a51100, 0x10000036, 0x0, 0x8f420300, -0x8f43002c, 0x24420001, 0xaf420300, 0x8f420300, -0x24020001, 0xa34205c1, 0x10000026, 0xaf430038, -0x8f440170, 0x8f450174, 0x8f43002c, 0x8f48000c, -0x8f860120, 0x24020020, 0xafa20010, 0xafa30014, -0xafa80018, 0x8f42010c, 0x40f809, 0x24c6001c, -0x14400011, 0x24020001, 0x3c010001, 0x370821, -0xa02240f0, 0x8f820124, 0xafa20010, 0x8f820128, -0x3c040001, 0x248467b8, 0xafa20014, 0x8f46002c, -0x8f870120, 0x3c050009, 0xc002b3b, 0x34a50900, -0x1000000f, 0x0, 0x8f420300, 0x24420001, -0xaf420300, 0x8f420300, 0x8f42002c, 0xa34005c1, -0xaf420038, 0x3c010001, 0x370821, 0xa02040f1, -0x3c010001, 0x370821, 0xa02040f0, 0xaf400034, -0x8f420314, 0x24420001, 0xaf420314, 0x10000062, -0x8f420314, 0x10400022, 0x32427000, 0x8e25001c, -0x8f420028, 0xa22023, 0x4810003, 0x0, -0x8f420040, 0x822021, 0x8f420358, 0x8f430000, -0xaf450028, 0x441021, 0x10600007, 0xaf420358, -0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, -0x10000005, 0x0, 0xaf800048, 0x8f820048, -0x1040fffd, 0x0, 0x8f820060, 0x34420008, -0xaf820060, 0x8f420000, 0x10400003, 0x0, -0x10000041, 0xaf80004c, 0x1000003f, 0xaf800048, -0x1040002f, 0x32421000, 0x1040000c, 0x32424000, -0x8e23001c, 0x8f420050, 0x622023, 0x4820001, -0x24840200, 0x8f42035c, 0x441021, 0xaf42035c, -0x8f420368, 0x1000001a, 0xaf430050, 0x1040000c, -0x32c28000, 0x8e23001c, 0x8f420070, 0x622023, -0x4820001, 0x24840400, 0x8f420364, 0x441021, -0xaf420364, 0x8f420368, 0x1000000d, 0xaf430070, -0x1040000e, 0x3c020800, 0x8e23001c, 0x8f420060, -0x622023, 0x4820001, 0x24840100, 0x8f420360, -0x441021, 0xaf420360, 0x8f420368, 0xaf430060, -0x441021, 0xaf420368, 0x3c020800, 0x2c21024, -0x50400011, 0x36940040, 0x1000000f, 0x0, -0x32420048, 0x10400007, 0x24150001, 0x8e22001c, -0x3c03ffff, 0x43f024, 0x3042ffff, 0x1000fd75, -0xae22001c, 0x32420100, 0x10400003, 0x0, -0xc002bd8, 0x0, 0x8fbf0050, 0x8fbe004c, -0x8fb50048, 0x8fb30044, 0x8fb20040, 0x8fb1003c, -0x8fb00038, 0x3e00008, 0x27bd0058, 0x3e00008, -0x0, 0x0, 0x0, 0x8f8300e4, -0x8f8200e0, 0x2404fff8, 0x441024, 0x621026, -0x2102b, 0x21023, 0x3e00008, 0x621024, -0x3e00008, 0x0, 0x27bdffe0, 0xafbf001c, -0xafb00018, 0x8f8600c4, 0x8f8400e0, 0x8f8500e4, -0x2402fff8, 0x821824, 0x10a30009, 0x27623ff8, -0x14a20002, 0x24a20008, 0x27623000, 0x408021, -0x16030005, 0x30820004, 0x10400004, 0xc02021, -0x10000022, 0x1021, 0x8e040000, 0x8f42011c, -0x14a20003, 0x0, 0x8f420120, 0xaf420114, -0x8ca30000, 0x8f420148, 0x831823, 0x43102b, -0x10400003, 0x0, 0x8f420148, 0x621821, -0x94a20006, 0x24420050, 0x62102b, 0x1440000f, -0xa01021, 0xafa40010, 0xafa30014, 0x8ca60000, -0x8ca70004, 0x3c040001, 0xc002b3b, 0x24846894, -0x8f42020c, 0x24420001, 0xaf42020c, 0x8f42020c, -0x1021, 0xaf9000e8, 0xaf9000e4, 0x8fbf001c, -0x8fb00018, 0x3e00008, 0x27bd0020, 0x3e00008, -0x0, 0x8f8400e0, 0x8f8800c4, 0x8f8300e8, -0x2402fff8, 0x823824, 0xe32023, 0x2c821000, -0x50400001, 0x24841000, 0x420c2, 0x801821, -0x8f440258, 0x8f45025c, 0x1021, 0xa32821, -0xa3302b, 0x822021, 0x862021, 0xaf440258, -0xaf45025c, 0x8f8300c8, 0x8f420148, 0x1032023, -0x82102b, 0x14400004, 0x801821, 0x8f420148, -0x822021, 0x801821, 0x8f440250, 0x8f450254, -0x1021, 0xa32821, 0xa3302b, 0x822021, -0x862021, 0xaf440250, 0xaf450254, 0xaf8800c8, -0xaf8700e4, 0xaf8700e8, 0x3e00008, 0x0, -0x27bdff30, 0x240a0001, 0xafbf00c8, 0xafbe00c4, -0xafb500c0, 0xafb300bc, 0xafb200b8, 0xafb100b4, -0xafb000b0, 0xa3a00097, 0xafa00044, 0xafaa005c, -0x934205c4, 0xa7a0008e, 0x1040000a, 0xa7a00086, -0x8f4b00c4, 0xafab0064, 0x8f4a00c0, 0xafaa006c, -0x8f4b00cc, 0xafab0074, 0x8f4a00c8, 0x10000129, -0xafaa007c, 0x8f420114, 0x40f809, 0x0, -0x403021, 0x10c0034f, 0x0, 0x8cc20000, -0x8cc30004, 0xafa20020, 0xafa30024, 0x8fab0024, -0x8faa0020, 0x3162ffff, 0x2442fffc, 0xafa2006c, -0x3c020006, 0x2c21024, 0xafab007c, 0x14400015, -0xafaa0064, 0x91420000, 0x30420001, 0x10400011, -0x2402ffff, 0x8d430000, 0x14620004, 0x3402ffff, -0x95430004, 0x1062000b, 0x0, 0xc0024bb, -0x8fa40064, 0x304200ff, 0x14400006, 0x0, -0x8f420118, 0x40f809, 0x0, 0x1000032d, -0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, -0x431024, 0x3c03ffff, 0x431824, 0x14600003, -0xafa20024, 0x10000040, 0x1821, 0x3c020080, -0x621024, 0x10400007, 0x0, 0x8f42038c, -0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, -0x24030001, 0x8f420210, 0x24420001, 0xaf420210, -0x8f420210, 0x3c020001, 0x621024, 0x10400006, -0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, -0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, -0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, -0x8f42037c, 0x3c020004, 0x621024, 0x10400006, -0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, -0x8f420380, 0x3c020008, 0x621024, 0x10400006, -0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, -0x8f420384, 0x3c020010, 0x621024, 0x10400006, -0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, -0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, -0x24030001, 0x8f420388, 0x24420001, 0xaf420388, -0x8f420388, 0x24030001, 0x8c020260, 0x8fab006c, -0x4b102b, 0x10400014, 0x307000ff, 0x8f4201e8, -0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8faa007c, -0x8f8200e0, 0x354a0100, 0xafaa007c, 0xafa20010, -0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, -0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, -0xc002b3b, 0x34a50800, 0x12000010, 0x3c020080, -0x2c21024, 0x1440000e, 0x32c20400, 0x8fab007c, -0x3c020080, 0x34420100, 0x1621024, 0x10400005, -0x0, 0x8f42020c, 0x24420001, 0xaf42020c, -0x8f42020c, 0x100002b0, 0x8fa3006c, 0x32c20400, -0x10400015, 0x34028100, 0x8faa0064, 0x9543000c, -0x14620012, 0x3c020100, 0x240b0200, 0xa7ab008e, -0x9542000e, 0x8d430008, 0x8d440004, 0x8d450000, -0x8faa006c, 0x8fab0064, 0x254afffc, 0xafaa006c, -0xa7a20086, 0xad63000c, 0xad640008, 0xad650004, -0x256b0004, 0xafab0064, 0x3c020100, 0x2c21024, -0x10400004, 0x0, 0x8faa006c, 0x254a0004, -0xafaa006c, 0x8f4200bc, 0x5040000a, 0xafa00074, -0x8fab006c, 0x4b102b, 0x50400006, 0xafa00074, -0x8f4200bc, 0x1621023, 0xafa20074, 0x8f4a00bc, -0xafaa006c, 0x8f420080, 0x8fab006c, 0x4b102b, -0x10400056, 0x32c28000, 0x1040005e, 0x240a0003, -0x32c21000, 0x1040005b, 0xafaa005c, 0x10000058, -0x240b0004, 0x8f420350, 0x2403ffbf, 0x283a024, -0x24420001, 0xaf420350, 0x1000024f, 0x8f420350, -0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, -0x3c040001, 0x248468d0, 0x26620001, 0xafa20014, -0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, -0xc002b3b, 0x34a52250, 0x1000023f, 0x0, -0x2c2b025, 0x2402ffbf, 0x282a024, 0x8f830128, -0x3c040001, 0x248468d0, 0x24020002, 0xafa20014, -0xafa30010, 0x8f860120, 0x8f870124, 0x3c050007, -0xc002b3b, 0x34a52450, 0x1000022f, 0x0, -0x8ea20000, 0x8ea30004, 0x3c040001, 0x248468e8, -0xafb00010, 0xafbe0014, 0x8ea70018, 0x34a52800, -0xc002b3b, 0x603021, 0x10000223, 0x0, -0xa6b1000a, 0x8f820124, 0x3c040001, 0x248468f0, -0xafbe0014, 0xafa20010, 0x8f460044, 0x8f870120, -0x3c050007, 0xc002b3b, 0x34a53000, 0x10000216, -0x0, 0xa6b1000a, 0xa6b2000e, 0x8f820124, -0x3c040001, 0x248468fc, 0xafbe0014, 0xafa20010, -0x8f460044, 0x8f870120, 0x3c050007, 0xc002b3b, -0x34a53200, 0x10000208, 0x0, 0x8f420084, -0x8faa006c, 0x4a102b, 0x14400007, 0x3c020001, -0x2c21024, 0x10400004, 0x0, 0x240b0002, -0xafab005c, 0x8faa006c, 0x1140021b, 0x27ab0020, -0xafab00a4, 0x3c0a001f, 0x354affff, 0xafaa009c, -0x8fab005c, 0x240a0001, 0x556a0021, 0x240a0002, -0x8f430054, 0x8f420050, 0x1062000b, 0x274b0054, -0x8f5e0054, 0x3403ecc0, 0xafab004c, 0x27c20001, -0x304201ff, 0xafa20054, 0x1e1140, 0x431021, -0x1000006b, 0x2e2a821, 0x8f420044, 0x8faa006c, -0x3c040001, 0x248468ac, 0xafaa0014, 0xafa20010, -0x8f460054, 0x8f470050, 0x3c050007, 0xc002b3b, -0x34a51300, 0x8f430350, 0x2402ffbf, 0x282a024, -0x24630001, 0xaf430350, 0x100001d3, 0x8f420350, -0x156a001d, 0x0, 0x8f430074, 0x8f420070, -0x1062000a, 0x274b0074, 0x8f5e0074, 0xafab004c, -0x27c20001, 0x304203ff, 0xafa20054, 0x1e1140, -0x24426cc0, 0x1000004a, 0x2e2a821, 0x8f420044, -0x8faa006c, 0x3c040001, 0x248468b8, 0x3c050007, -0xafaa0014, 0xafa20010, 0x8f460074, 0x8f470070, -0x34a51500, 0x240b0001, 0xc002b3b, 0xafab005c, -0x1000ffc3, 0x0, 0x8f430064, 0x8f420060, -0x1062001a, 0x274a0064, 0x8f5e0064, 0x8fab005c, -0xafaa004c, 0x27c20001, 0x304200ff, 0xafa20054, -0x24020004, 0x1562000e, 0x1e1140, 0x1e1180, -0x24420cc0, 0x2e21021, 0xafa20044, 0x9442002a, -0x8faa0044, 0x8fab006c, 0x4b102b, 0x10400024, -0x25550020, 0x240a0001, 0x10000021, 0xa3aa0097, -0x24424cc0, 0x1000001e, 0x2e2a821, 0x8f420044, -0x8fab006c, 0x3c040001, 0x248468c4, 0xafab0014, -0xafa20010, 0x8f460064, 0x8f470060, 0x3c050007, -0xc002b3b, 0x34a51800, 0x3c020008, 0x2c21024, -0x1440ff34, 0x0, 0x8f420370, 0x240a0001, -0xafaa005c, 0x24420001, 0xaf420370, 0x1000ff90, -0x8f420370, 0x27a30036, 0x131040, 0x621821, -0x94620000, 0x441021, 0x10000020, 0xa4620000, -0x8fab0064, 0xaeab0018, 0x93a20097, 0x10400072, -0x9821, 0x8faa0044, 0x8fa4006c, 0x8fa300a4, -0x25420020, 0xafa20028, 0x25420008, 0xafa20030, -0x25420010, 0xafaa002c, 0xafa20034, 0x9542002a, -0xa7a20038, 0x95420018, 0xa7a2003a, 0x9542001a, -0xa7a2003c, 0x9542001c, 0xa7a2003e, 0x94620018, -0x24630002, 0x822023, 0x1880ffde, 0x26730001, -0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, -0x26650001, 0xa2102a, 0x1440002b, 0x24030001, -0x8f83012c, 0x10600023, 0x0, 0x8f820124, -0x431023, 0x22143, 0x58800001, 0x24840040, -0x8f820128, 0x431023, 0x21943, 0x58600001, -0x24630040, 0x64102a, 0x54400001, 0x602021, -0xaf4400fc, 0x8f4200fc, 0xa2102a, 0x10400011, -0x24030001, 0x10000015, 0x306200ff, 0x8fab0064, -0x96070018, 0xafab0010, 0x8e220008, 0x3c040001, -0x248468dc, 0x8c430004, 0x8c420000, 0x34a52400, -0x2403021, 0xc002b3b, 0xafa30014, 0x1000002b, -0x0, 0x8f420334, 0x1821, 0x24420001, -0xaf420334, 0x8f420334, 0x306200ff, 0x5040fedc, -0x3c020800, 0x12600021, 0x9021, 0x8fb100a4, -0x2208021, 0x8e220008, 0x96070018, 0x8fa60064, -0x8c440000, 0x8c450004, 0x240a0001, 0xafaa0010, -0xafbe0014, 0x8f420008, 0xafa20018, 0x8f42010c, -0x40f809, 0x0, 0x1040ffd8, 0x3c050007, -0x96020018, 0x8fab0064, 0x8faa009c, 0x1625821, -0x14b102b, 0x10400004, 0xafab0064, 0x8f420148, -0x1625823, 0xafab0064, 0x26100002, 0x26520001, -0x253102b, 0x1440ffe3, 0x26310004, 0x8fb0006c, -0x10000036, 0x97b10038, 0x8f4200fc, 0x24050002, -0xa2102a, 0x1440001b, 0x24030001, 0x8f83012c, -0x10600013, 0x0, 0x8f820124, 0x431023, -0x22143, 0x58800001, 0x24840040, 0x8f820128, -0x431023, 0x21943, 0x58600001, 0x24630040, -0x64102a, 0x54400001, 0x602021, 0xaf4400fc, -0x8f4200fc, 0xa2102a, 0x14400006, 0x24030001, -0x8f420334, 0x1821, 0x24420001, 0xaf420334, -0x8f420334, 0x306200ff, 0x1040fea5, 0x3c020800, -0x96b1000a, 0x8fb0006c, 0x3223ffff, 0x70102b, -0x54400001, 0x608021, 0x8ea40000, 0x8ea50004, -0x240b0001, 0xafab0010, 0xafbe0014, 0x8f420008, -0x8fa60064, 0xafa20018, 0x8f42010c, 0x40f809, -0x2003821, 0x1040fea2, 0x3c050007, 0x96a3000e, -0x97aa008e, 0x11400007, 0x609021, 0x934205c4, -0x14400004, 0x0, 0x97ab0086, 0x6a1825, -0xa6ab0016, 0x8faa007c, 0x3c02ffff, 0x1421024, -0x10400003, 0xa1402, 0x34630400, 0xa6a20014, -0x8fab006c, 0x560b0072, 0xa6a3000e, 0x34620004, -0xa6a2000e, 0x8faa0074, 0x16a1021, 0xa6a2000a, -0x8f430044, 0x8f4401a0, 0x8f4501a4, 0x34028000, -0xafa20010, 0x8f420044, 0x2a03021, 0x24070020, -0xafa20014, 0x8f42000c, 0x31940, 0x604821, -0xafa20018, 0x8f42010c, 0x4021, 0xa92821, -0xa9182b, 0x882021, 0x40f809, 0x832021, -0x5040fe7f, 0xa6b2000e, 0x8f420368, 0xafa0006c, -0xa34005c4, 0x2442ffff, 0xaf420368, 0x8fab005c, -0x240a0001, 0x8f420368, 0x156a0006, 0x240a0002, -0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, -0x8f42035c, 0x156a0006, 0x0, 0x8f420364, -0x2442ffff, 0xaf420364, 0x10000005, 0x8f420364, -0x8f420360, 0x2442ffff, 0xaf420360, 0x8f420360, -0x8faa0054, 0x8fab004c, 0xad6a0000, 0x8f420044, -0x8f440088, 0x8f430078, 0x24420001, 0x441024, -0x24630001, 0xaf420044, 0xaf430078, 0x8c020240, -0x62182b, 0x14600075, 0x24070008, 0x8f440168, -0x8f45016c, 0x8f430044, 0x8f48000c, 0x8f860120, -0x24020040, 0xafa20010, 0xafa30014, 0xafa80018, -0x8f42010c, 0x40f809, 0x24c6001c, 0x14400011, -0x240b0001, 0x3c010001, 0x370821, 0xa02b40f2, -0x8f820124, 0xafa20010, 0x8f820128, 0x3c040001, -0x2484688c, 0xafa20014, 0x8f460044, 0x8f870120, -0x3c050009, 0xc002b3b, 0x34a51300, 0x1000000b, -0x0, 0x8f420304, 0x24420001, 0xaf420304, -0x8f420304, 0x8f420044, 0xaf42007c, 0x3c010001, -0x370821, 0xa02040f2, 0xaf400078, 0x8f420318, -0x24420001, 0xaf420318, 0x10000048, 0x8f420318, -0xa6b0000a, 0x8f430044, 0x8f4401a0, 0x8f4501a4, -0x34028000, 0xafa20010, 0x8f420044, 0x2a03021, -0x24070020, 0xafa20014, 0x8f42000c, 0x31940, -0x604821, 0xafa20018, 0x8f42010c, 0x4021, -0xa92821, 0xa9182b, 0x882021, 0x40f809, -0x832021, 0x1040fe1f, 0x240a0001, 0xa34a05c4, -0x8fab006c, 0x8faa0064, 0x1705823, 0xafab006c, -0x8fab009c, 0x1505021, 0x16a102b, 0x10400004, -0xafaa0064, 0x8f420148, 0x1425023, 0xafaa0064, -0x8f420368, 0x2442ffff, 0xaf420368, 0x8faa005c, -0x240b0001, 0x8f420368, 0x154b0006, 0x240b0002, -0x8f42035c, 0x2442ffff, 0xaf42035c, 0x1000000c, -0x8f42035c, 0x114b0006, 0x0, 0x8f420360, -0x2442ffff, 0xaf420360, 0x10000005, 0x8f420360, -0x8f420364, 0x2442ffff, 0xaf420364, 0x8f420364, -0x8fab0054, 0x8faa004c, 0xad4b0000, 0x8f420044, -0x8f440088, 0x8f430078, 0x24420001, 0x441024, -0x24630001, 0xaf420044, 0xaf430078, 0x8faa006c, -0x1540fe0b, 0x0, 0x8fab006c, 0x1160001e, -0x0, 0x934205c4, 0x10400009, 0x0, -0x8faa0064, 0xaf4a00c4, 0xaf4b00c0, 0x8fab007c, -0xaf4b00c8, 0x8faa0074, 0x1000000e, 0xaf4a00cc, -0x97ab008e, 0x1160000b, 0x34038100, 0x8fa20020, -0x8c46000c, 0xa443000c, 0x97aa0086, 0x8c440004, -0x8c450008, 0xa44a000e, 0xac440000, 0xac450004, -0xac460008, 0x8f42034c, 0x24420001, 0xaf42034c, -0x10000010, 0x8f42034c, 0x8fab007c, 0x3164ffff, -0x2484fffc, 0x801821, 0x8f440250, 0x8f450254, -0x8f460118, 0x1021, 0xa32821, 0xa3382b, -0x822021, 0x872021, 0xaf440250, 0xc0f809, -0xaf450254, 0x8fbf00c8, 0x8fbe00c4, 0x8fb500c0, -0x8fb300bc, 0x8fb200b8, 0x8fb100b4, 0x8fb000b0, -0x3e00008, 0x27bd00d0, 0x3e00008, 0x0, -0x27bdff38, 0x240b0001, 0xafbf00c0, 0xafbe00bc, -0xafb500b8, 0xafb300b4, 0xafb200b0, 0xafb100ac, -0xafb000a8, 0xa3a00087, 0xafa00044, 0xafab005c, -0x934205c4, 0xa7a00076, 0x10400007, 0xa7a0007e, -0x8f4c00c0, 0xafac0064, 0x8f4b00c8, 0x8f5e00c4, -0x10000130, 0xafab006c, 0x8f420114, 0x40f809, -0x0, 0x403021, 0x10c002a1, 0x0, -0x8cc20000, 0x8cc30004, 0xafa20020, 0xafa30024, -0x8fac0024, 0x8fbe0020, 0x3182ffff, 0x2442fffc, -0xafa20064, 0x3c020006, 0x2c21024, 0x14400015, -0xafac006c, 0x93c20000, 0x30420001, 0x10400011, -0x2402ffff, 0x8fc30000, 0x14620004, 0x3402ffff, -0x97c30004, 0x1062000b, 0x0, 0xc0024bb, -0x3c02021, 0x304200ff, 0x14400006, 0x0, -0x8f420118, 0x40f809, 0x0, 0x10000280, -0x0, 0x8fa20024, 0x3c03ffbf, 0x3463ffff, -0x431024, 0x3c03ffff, 0x431824, 0x14600003, -0xafa20024, 0x10000040, 0x8021, 0x3c020080, -0x621024, 0x10400007, 0x0, 0x8f42038c, -0x24420001, 0xaf42038c, 0x8f42038c, 0x10000036, -0x24100001, 0x8f420210, 0x24420001, 0xaf420210, -0x8f420210, 0x3c020001, 0x621024, 0x10400006, -0x3c020002, 0x8f4201c4, 0x24420001, 0xaf4201c4, -0x8f4201c4, 0x3c020002, 0x621024, 0x10400006, -0x3c020004, 0x8f42037c, 0x24420001, 0xaf42037c, -0x8f42037c, 0x3c020004, 0x621024, 0x10400006, -0x3c020008, 0x8f420380, 0x24420001, 0xaf420380, -0x8f420380, 0x3c020008, 0x621024, 0x10400006, -0x3c020010, 0x8f420384, 0x24420001, 0xaf420384, -0x8f420384, 0x3c020010, 0x621024, 0x10400006, -0x3c020020, 0x8f4201c0, 0x24420001, 0xaf4201c0, -0x8f4201c0, 0x3c020020, 0x621024, 0x10400006, -0x24100001, 0x8f420388, 0x24420001, 0xaf420388, -0x8f420388, 0x24100001, 0x8c020260, 0x8fab0064, -0x4b102b, 0x10400015, 0x320200ff, 0x8f4201e8, -0x24420001, 0xaf4201e8, 0x8f4201e8, 0x8fac006c, -0x8f8200e0, 0x358c0100, 0xafac006c, 0xafa20010, -0x8f8200e4, 0x24100001, 0x3c040001, 0x248468a0, -0xafa20014, 0x8fa60020, 0x8fa70024, 0x3c050007, -0xc002b3b, 0x34a53600, 0x320200ff, 0x10400010, -0x3c020080, 0x2c21024, 0x1440000e, 0x32c20400, -0x8fab006c, 0x3c020080, 0x34420100, 0x1621024, -0x10400005, 0x0, 0x8f42020c, 0x24420001, -0xaf42020c, 0x8f42020c, 0x10000202, 0x8fa30064, -0x32c20400, 0x10400012, 0x34028100, 0x97c3000c, -0x1462000f, 0x0, 0x240c0200, 0xa7ac0076, -0x97c2000e, 0x8fc30008, 0x8fc40004, 0x8fab0064, -0x8fc50000, 0x256bfffc, 0xafab0064, 0xa7a2007e, -0xafc3000c, 0xafc40008, 0xafc50004, 0x27de0004, -0x8fa70064, 0x320200ff, 0x14400034, 0x3c020100, -0x97c4000c, 0x2c8305dd, 0x38828870, 0x2c420001, -0x621825, 0x10600015, 0x2821, 0x32c20800, -0x10400015, 0x24020800, 0x97c30014, 0x14620012, -0x3402aaaa, 0x97c3000e, 0x14620007, 0x2021, -0x97c30010, 0x24020300, 0x14620004, 0x801021, -0x97c20012, 0x2c440001, 0x801021, 0x54400006, -0x24050016, 0x10000004, 0x0, 0x24020800, -0x50820001, 0x2405000e, 0x10a00013, 0x3c52021, -0x24830009, 0x3c02001f, 0x3442ffff, 0x43102b, -0x10400003, 0x0, 0x8f420148, 0x621823, -0x90620000, 0x38430006, 0x2c630001, 0x38420011, -0x2c420001, 0x621825, 0x10600004, 0x3c020100, -0x94820002, 0x453821, 0x3c020100, 0x2c21024, -0x5040000e, 0xafa70064, 0x8fac0064, 0x10ec0008, -0x3c050007, 0x3c040001, 0x24846908, 0x8fa60064, -0x34a54000, 0xafa00010, 0xc002b3b, 0xafa00014, -0x8fab0064, 0x256b0004, 0xafab0064, 0x8f420080, -0x8fac0064, 0x4c102b, 0x1040002c, 0x32c28000, -0x10400034, 0x240b0003, 0x32c21000, 0x10400031, -0xafab005c, 0x1000002e, 0x240c0004, 0x8f420350, -0x2403ffbf, 0x283a024, 0x24420001, 0xaf420350, -0x10000173, 0x8f420350, 0x3c020800, 0x2c2b025, -0x2402ffbf, 0x282a024, 0x8f830128, 0x3c040001, -0x248468d0, 0x26620001, 0xafa20014, 0xafa30010, -0x8f860120, 0x8f870124, 0x3c050007, 0xc002b3b, -0x34a55300, 0x10000162, 0x0, 0x8ea20000, -0x8ea30004, 0x3c040001, 0x248468e8, 0xafb00010, -0xafb10014, 0x8ea70018, 0x34a55900, 0xc002b3b, -0x603021, 0x10000156, 0x0, 0x8f420084, -0x8fab0064, 0x4b102b, 0x14400007, 0x3c020001, -0x2c21024, 0x10400004, 0x0, 0x240c0002, -0xafac005c, 0x8fab0064, 0x11600166, 0x27ac0020, -0xafac008c, 0x8fab005c, 0x240c0001, 0x556c0021, -0x240c0002, 0x8f430054, 0x8f420050, 0x1062000b, -0x274b0054, 0x8f510054, 0x3403ecc0, 0xafab004c, -0x26220001, 0x304201ff, 0xafa20054, 0x111140, -0x431021, 0x1000006b, 0x2e2a821, 0x8f420044, -0x8fac0064, 0x3c040001, 0x248468ac, 0xafac0014, -0xafa20010, 0x8f460054, 0x8f470050, 0x3c050007, -0xc002b3b, 0x34a54300, 0x8f430350, 0x2402ffbf, -0x282a024, 0x24630001, 0xaf430350, 0x10000124, -0x8f420350, 0x156c001d, 0x0, 0x8f430074, -0x8f420070, 0x1062000a, 0x274b0074, 0x8f510074, -0xafab004c, 0x26220001, 0x304203ff, 0xafa20054, -0x111140, 0x24426cc0, 0x1000004a, 0x2e2a821, -0x8f420044, 0x8fac0064, 0x3c040001, 0x248468b8, -0x3c050007, 0xafac0014, 0xafa20010, 0x8f460074, -0x8f470070, 0x34a54500, 0x240b0001, 0xc002b3b, -0xafab005c, 0x1000ffc3, 0x0, 0x8f430064, -0x8f420060, 0x1062001a, 0x274c0064, 0x8f510064, -0x8fab005c, 0xafac004c, 0x26220001, 0x304200ff, -0xafa20054, 0x24020004, 0x1562000e, 0x111140, -0x111180, 0x24420cc0, 0x2e21021, 0xafa20044, -0x9442002a, 0x8fac0044, 0x8fab0064, 0x4b102b, -0x10400024, 0x25950020, 0x240c0001, 0x10000021, -0xa3ac0087, 0x24424cc0, 0x1000001e, 0x2e2a821, -0x8f420044, 0x8fab0064, 0x3c040001, 0x248468c4, -0xafab0014, 0xafa20010, 0x8f460064, 0x8f470060, -0x3c050007, 0xc002b3b, 0x34a54800, 0x3c020008, -0x2c21024, 0x1440ff61, 0x0, 0x8f420370, -0x240c0001, 0xafac005c, 0x24420001, 0xaf420370, -0x1000ff90, 0x8f420370, 0x27a30036, 0x131040, -0x621821, 0x94620000, 0x441021, 0x1000001f, -0xa4620000, 0xaebe0018, 0x93a20087, 0x10400084, -0x9821, 0x8fab0044, 0x8fa40064, 0x8fa3008c, -0x25620020, 0xafa20028, 0x25620008, 0xafa20030, -0x25620010, 0xafab002c, 0xafa20034, 0x9562002a, -0xa7a20038, 0x95620018, 0xa7a2003a, 0x9562001a, -0xa7a2003c, 0x9562001c, 0xa7a2003e, 0x94620018, -0x24630002, 0x822023, 0x1880ffdf, 0x26730001, -0x2e620004, 0x1440fff9, 0x0, 0x8f4200fc, -0x262102a, 0x14400030, 0x24030001, 0x8f83012c, -0x10600028, 0x0, 0x8f820124, 0x431023, -0x22143, 0x58800001, 0x24840040, 0x8f820128, -0x431023, 0x21943, 0x58600001, 0x24630040, -0x64102a, 0x54400001, 0x602021, 0xaf4400fc, -0x8f4200fc, 0x262102a, 0x10400016, 0x24030001, -0x1000001a, 0x306200ff, 0x8fac008c, 0x101040, -0x4c1021, 0x94470018, 0x101080, 0x4c1021, -0xafbe0010, 0x8c420008, 0x3c040001, 0x248468dc, -0x3c050007, 0x8c430004, 0x8c420000, 0x34a55500, -0x2003021, 0xc002b3b, 0xafa30014, 0x10000039, -0x0, 0x8f420334, 0x1821, 0x24420001, -0xaf420334, 0x8f420334, 0x306200ff, 0x1040ff06, -0x8021, 0x8f430008, 0x2402fbff, 0x1260002d, -0x625024, 0x3c0b4000, 0x22b4025, 0x8fb1008c, -0x2669ffff, 0x2209021, 0x8e420008, 0x96270018, -0x8c440000, 0x8c450004, 0x56090004, 0x240b0001, -0x240c0002, 0x10000002, 0xafac0010, 0xafab0010, -0x16000004, 0xafa80014, 0x8f420008, 0x10000002, -0xafa20018, 0xafaa0018, 0x8f42010c, 0x3c03021, -0xafa80098, 0xafa9009c, 0x40f809, 0xafaa00a0, -0x8fa80098, 0x8fa9009c, 0x8faa00a0, 0x1040ffc2, -0x3c02001f, 0x96230018, 0x3442ffff, 0x3c3f021, -0x5e102b, 0x10400003, 0x26310002, 0x8f420148, -0x3c2f023, 0x26100001, 0x213102b, 0x1440ffda, -0x26520004, 0x8fb00064, 0x1000001a, 0x0, -0x96a3000a, 0x8fb00064, 0x70102b, 0x54400001, -0x608021, 0x8ea40000, 0x8ea50004, 0x8fab005c, -0x240c0002, 0xafac0010, 0x934305c4, 0xb1700, -0x10600003, 0x2223025, 0x3c020800, 0xc23025, -0xafa60014, 0x8f420008, 0xafa20018, 0x8f42010c, -0x3c03021, 0x40f809, 0x2003821, 0x1040fecb, -0x3c050007, 0x97ac0076, 0x11800007, 0x96a3000e, -0x934205c4, 0x14400004, 0x0, 0x97ab007e, -0x6c1825, 0xa6ab0016, 0x8fac006c, 0x3c02ffff, -0x1821024, 0x10400003, 0xc1402, 0x34630400, -0xa6a20014, 0xa6b0000a, 0x8fab0064, 0x560b0006, -0x3d0f021, 0x34620004, 0xafa00064, 0xa6a2000e, -0x1000000d, 0xa34005c4, 0x8fac0064, 0x3c02001f, -0x3442ffff, 0x5e102b, 0x1906023, 0xafac0064, -0xa6a3000e, 0x240b0001, 0x10400003, 0xa34b05c4, -0x8f420148, 0x3c2f023, 0x8fab0054, 0x8fac004c, -0xad8b0000, 0x8fac0064, 0x1580feba, 0x0, -0x8fab0064, 0x1160001b, 0x0, 0x934205c4, -0x10400006, 0x0, 0xaf5e00c4, 0xaf4b00c0, -0x8fac006c, 0x1000000e, 0xaf4c00c8, 0x97ab0076, -0x1160000b, 0x34038100, 0x8fa20020, 0x8c46000c, -0xa443000c, 0x97ac007e, 0x8c440004, 0x8c450008, -0xa44c000e, 0xac440000, 0xac450004, 0xac460008, -0x8f42034c, 0x24420001, 0xaf42034c, 0x10000010, -0x8f42034c, 0x8fab006c, 0x3164ffff, 0x2484fffc, -0x801821, 0x8f440250, 0x8f450254, 0x8f460118, -0x1021, 0xa32821, 0xa3382b, 0x822021, -0x872021, 0xaf440250, 0xc0f809, 0xaf450254, -0x8fbf00c0, 0x8fbe00bc, 0x8fb500b8, 0x8fb300b4, -0x8fb200b0, 0x8fb100ac, 0x8fb000a8, 0x3e00008, -0x27bd00c8, 0x3e00008, 0x0, 0x27bdffd8, -0xafbf0024, 0xafb00020, 0x8f43004c, 0x8f420048, -0x10620034, 0x0, 0x8f430048, 0x8f42004c, -0x622023, 0x4820001, 0x24840200, 0x8f430054, -0x8f42004c, 0x43102b, 0x14400004, 0x24020200, -0x8f43004c, 0x10000005, 0x431023, 0x8f420054, -0x8f43004c, 0x431023, 0x2442ffff, 0x405021, -0x8a102a, 0x54400001, 0x805021, 0x8f49004c, -0x8f48004c, 0x8f440188, 0x8f45018c, 0x8f46004c, -0x24071000, 0xafa70010, 0x84140, 0x1001821, -0x12a4821, 0x313001ff, 0xafb00014, 0x8f470014, -0x1021, 0x63140, 0xafa70018, 0xa32821, -0xa3382b, 0x822021, 0x872021, 0x3402ecc0, -0xc23021, 0x8f420108, 0x2e63021, 0x40f809, -0xa3940, 0x54400001, 0xaf50004c, 0x8f43004c, -0x8f420048, 0x14620018, 0x0, 0x8f420000, -0x10400007, 0x0, 0xaf80004c, 0x8f82004c, -0x1040fffd, 0x0, 0x10000005, 0x0, -0xaf800048, 0x8f820048, 0x1040fffd, 0x0, -0x8f820060, 0x2403fdff, 0x431024, 0xaf820060, -0x8f420000, 0x10400003, 0x0, 0x10000002, -0xaf80004c, 0xaf800048, 0x8fbf0024, 0x8fb00020, -0x3e00008, 0x27bd0028, 0x3e00008, 0x0, -0x27bdffd8, 0xafbf0024, 0xafb00020, 0x8f43005c, -0x8f420058, 0x10620049, 0x0, 0x8f430058, -0x8f42005c, 0x622023, 0x4820001, 0x24840100, -0x8f430064, 0x8f42005c, 0x43102b, 0x14400004, -0x24020100, 0x8f43005c, 0x10000005, 0x431023, -0x8f420064, 0x8f43005c, 0x431023, 0x2442ffff, -0x403821, 0x87102a, 0x54400001, 0x803821, -0x8f42005c, 0x471021, 0x305000ff, 0x32c21000, -0x10400015, 0x24082000, 0x8f49005c, 0x8f440190, -0x8f450194, 0x8f46005c, 0x73980, 0xafa80010, -0xafb00014, 0x8f480014, 0x94980, 0x1201821, -0x1021, 0xa32821, 0xa3482b, 0x822021, -0x892021, 0x63180, 0xafa80018, 0x8f420108, -0x10000014, 0x24c60cc0, 0x8f49005c, 0x8f440190, -0x8f450194, 0x8f46005c, 0x73940, 0xafa80010, -0xafb00014, 0x8f480014, 0x94940, 0x1201821, -0x1021, 0xa32821, 0xa3482b, 0x822021, -0x892021, 0x63140, 0xafa80018, 0x8f420108, -0x24c64cc0, 0x40f809, 0x2e63021, 0x54400001, -0xaf50005c, 0x8f43005c, 0x8f420058, 0x14620018, -0x0, 0x8f420000, 0x10400007, 0x0, -0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, -0x10000005, 0x0, 0xaf800048, 0x8f820048, -0x1040fffd, 0x0, 0x8f820060, 0x2403feff, -0x431024, 0xaf820060, 0x8f420000, 0x10400003, -0x0, 0x10000002, 0xaf80004c, 0xaf800048, -0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, -0x3e00008, 0x0, 0x27bdffd8, 0xafbf0024, -0xafb00020, 0x8f43006c, 0x8f420068, 0x10620033, -0x0, 0x8f430068, 0x8f42006c, 0x622023, -0x4820001, 0x24840400, 0x8f430074, 0x8f42006c, -0x43102b, 0x14400004, 0x24020400, 0x8f43006c, -0x10000005, 0x431023, 0x8f420074, 0x8f43006c, -0x431023, 0x2442ffff, 0x405021, 0x8a102a, -0x54400001, 0x805021, 0x8f49006c, 0x8f48006c, -0x8f440198, 0x8f45019c, 0x8f46006c, 0x24074000, -0xafa70010, 0x84140, 0x1001821, 0x12a4821, -0x313003ff, 0xafb00014, 0x8f470014, 0x1021, -0x63140, 0x24c66cc0, 0xafa70018, 0xa32821, -0xa3382b, 0x822021, 0x872021, 0x8f420108, -0x2e63021, 0x40f809, 0xa3940, 0x54400001, -0xaf50006c, 0x8f43006c, 0x8f420068, 0x14620018, -0x0, 0x8f420000, 0x10400007, 0x0, -0xaf80004c, 0x8f82004c, 0x1040fffd, 0x0, -0x10000005, 0x0, 0xaf800048, 0x8f820048, -0x1040fffd, 0x0, 0x8f820060, 0x2403f7ff, -0x431024, 0xaf820060, 0x8f420000, 0x10400003, -0x0, 0x10000002, 0xaf80004c, 0xaf800048, -0x8fbf0024, 0x8fb00020, 0x3e00008, 0x27bd0028, -0x3e00008, 0x0, 0x8f4200fc, 0x3c030001, -0x8f4400f8, 0x346330c8, 0x24420001, 0xaf4200fc, -0x8f850128, 0x2e31021, 0x54820004, 0x24820008, -0x3c020001, 0x34422ec8, 0x2e21021, 0x401821, -0xaf4300f8, 0xac600000, 0x8f4200f4, 0x14620004, -0x3c020001, 0x24a20020, 0x1000000f, 0xaf820128, -0x8f4300f8, 0x344230c8, 0x2e21021, 0x54620004, -0x24620008, 0x3c020001, 0x34422ec8, 0x2e21021, -0x401821, 0x8c620004, 0x21140, 0xa21021, -0xaf820128, 0xac600000, 0x8ca30018, 0x30620070, -0x1040002d, 0x30620020, 0x10400004, 0x3c020010, -0x2c21024, 0x1040000d, 0x0, 0x30620040, -0x10400004, 0x3c020020, 0x2c21024, 0x10400007, -0x0, 0x30620010, 0x1040001f, 0x3c020040, -0x2c21024, 0x1440001c, 0x0, 0x8f820040, -0x30420001, 0x14400008, 0x2021, 0x8c030104, -0x24020001, 0x50620005, 0x24040001, 0x8c020264, -0x10400003, 0x801021, 0x24040001, 0x801021, -0x10400006, 0x0, 0x8f42030c, 0x24420001, -0xaf42030c, 0x10000008, 0x8f42030c, 0x8f820044, -0x34420004, 0xaf820044, 0x8f420308, 0x24420001, -0xaf420308, 0x8f420308, 0x3e00008, 0x0, -0x3e00008, 0x0, 0x27bdff98, 0xafbf0060, -0xafbe005c, 0xafb50058, 0xafb30054, 0xafb20050, -0xafb1004c, 0xafb00048, 0x8f4200fc, 0x24420001, -0xaf4200fc, 0x8f880128, 0x25020020, 0xaf820128, -0x8d030018, 0x30620070, 0x1040002e, 0x30620020, -0x10400004, 0x3c020010, 0x2c21024, 0x1040000d, -0x0, 0x30620040, 0x10400004, 0x3c020020, -0x2c21024, 0x10400007, 0x0, 0x30620010, -0x104001a9, 0x3c020040, 0x2c21024, 0x144001a6, -0x0, 0x8f820040, 0x30420001, 0x14400008, -0x2021, 0x8c030104, 0x24020001, 0x50620005, -0x24040001, 0x8c020264, 0x10400003, 0x801021, -0x24040001, 0x801021, 0x10400006, 0x0, -0x8f42030c, 0x24420001, 0xaf42030c, 0x10000192, -0x8f42030c, 0x8f820044, 0x34420004, 0xaf820044, -0x8f420308, 0x24420001, 0xaf420308, 0x1000018a, -0x8f420308, 0x30620002, 0x1040014b, 0x3c020800, -0x8d1e001c, 0x1e5702, 0xafaa0034, 0x950a0016, -0x3c22024, 0xafaa0024, 0x8faa0034, 0x24020001, -0x15420006, 0x33deffff, 0x1e1140, 0x3403ecc0, -0x431021, 0x10000010, 0x2e2a821, 0x24020002, -0x15420005, 0x24020003, 0x1e1140, 0x24426cc0, -0x10000009, 0x2e2a821, 0x15420005, 0x1e1180, -0x1e1140, 0x24424cc0, 0x10000003, 0x2e2a821, -0x571021, 0x24550ce0, 0x96a2000e, 0x304afffc, -0x30420400, 0x10400003, 0xafaa002c, 0x100000e1, -0x8821, 0x10800004, 0x8821, 0x97b10026, -0x100000dd, 0xa6b10012, 0x8eb30018, 0x966a000c, -0xa7aa003e, 0x97a5003e, 0x2ca305dd, 0x38a28870, -0x2c420001, 0x621825, 0x10600015, 0x2021, -0x32c20800, 0x10400015, 0x24020800, 0x96630014, -0x14620012, 0x3402aaaa, 0x9663000e, 0x14620007, -0x2821, 0x96630010, 0x24020300, 0x14620004, -0xa01021, 0x96620012, 0x2c450001, 0xa01021, -0x54400006, 0x24040016, 0x10000004, 0x0, -0x24020800, 0x50a20001, 0x2404000e, 0x108000b9, -0x2649021, 0x92420000, 0x3042000f, 0x28080, -0x32c20100, 0x10400020, 0x2501821, 0x3c020020, -0x43102b, 0x1440000e, 0x2402021, 0x2821, -0x94820000, 0x24840002, 0xa22821, 0x83102b, -0x1440fffb, 0x30a2ffff, 0x51c02, 0x622821, -0x51c02, 0x30a2ffff, 0x10000009, 0x622821, -0x8f470148, 0x8f420110, 0x102842, 0x3c060020, -0x40f809, 0xafa80040, 0x3045ffff, 0x8fa80040, -0x50a00001, 0x3405ffff, 0x8faa002c, 0x354a0002, -0x10000002, 0xafaa002c, 0x2821, 0x32c20080, -0x10400090, 0xa6a50010, 0x26430009, 0x3c02001f, -0x3442ffff, 0x43102b, 0x10400003, 0x0, -0x8f420148, 0x621823, 0x90660000, 0x30c200ff, -0x38430006, 0x2c630001, 0x38420011, 0x2c420001, -0x621825, 0x1060007f, 0x24020800, 0x8821, -0x97a3003e, 0x1462000f, 0x2602021, 0x96710000, -0x96620002, 0x96630004, 0x96640006, 0x2228821, -0x2238821, 0x2248821, 0x96620008, 0x9663000a, -0x9664000c, 0x2228821, 0x2238821, 0x10000007, -0x2248821, 0x94820000, 0x24840002, 0x2228821, -0x92102b, 0x1440fffb, 0x0, 0x111c02, -0x3222ffff, 0x628821, 0x111c02, 0x3222ffff, -0x628821, 0x32c20200, 0x10400003, 0x26440006, -0x1000003e, 0x8021, 0x3c05001f, 0x34a5ffff, -0xa4102b, 0x10400003, 0x0, 0x8f420148, -0x822023, 0x94820000, 0x30421fff, 0x10400004, -0x2644000c, 0x96420002, 0x10000030, 0x508023, -0x96420002, 0x26430014, 0x508023, 0x3c020020, -0x43102b, 0x1440000a, 0xd08021, 0x9642000c, -0x2028021, 0x9642000e, 0x96430010, 0x96440012, -0x2028021, 0x2038021, 0x10000020, 0x2048021, -0xa4102b, 0x10400003, 0x0, 0x8f420148, -0x822023, 0x94820000, 0x24840002, 0x2028021, -0xa4102b, 0x10400003, 0x0, 0x8f420148, -0x822023, 0x94820000, 0x24840002, 0x2028021, -0xa4102b, 0x10400003, 0x0, 0x8f420148, -0x822023, 0x94820000, 0x24840002, 0x2028021, -0xa4102b, 0x10400003, 0x0, 0x8f420148, -0x822023, 0x94820000, 0x2028021, 0x3c020100, -0x2c21024, 0x1040000e, 0x0, 0x8faa002c, -0x31420004, 0x1040000a, 0x0, 0x9504000e, -0x2642021, 0xc003eec, 0x2484fffc, 0x3042ffff, -0x2228821, 0x111c02, 0x3222ffff, 0x628821, -0x8faa0024, 0x1518823, 0x111402, 0x2228821, -0x2308821, 0x111402, 0x2228821, 0x3231ffff, -0x52200001, 0x3411ffff, 0x8faa002c, 0x354a0001, -0xafaa002c, 0xa6b10012, 0x97aa002e, 0xa6aa000e, -0x8faa002c, 0x31420004, 0x10400002, 0x24091000, -0x34098000, 0x8f480044, 0x8f4401a0, 0x8f4501a4, -0xafa90010, 0x8f490044, 0x84140, 0x1001821, -0xafa90014, 0x8f48000c, 0x2a03021, 0x24070020, -0xafa80018, 0x8f48010c, 0x1021, 0xa32821, -0xa3482b, 0x822021, 0x100f809, 0x892021, -0x1440000b, 0x0, 0x8f820128, 0x3c040001, -0x24846914, 0xafbe0014, 0xafa20010, 0x8f860124, -0x8f870120, 0x3c050007, 0xc002b3b, 0x34a59920, -0x8f420368, 0x2442ffff, 0xaf420368, 0x8f420044, -0x8f430088, 0x24420001, 0x431024, 0xaf420044, -0x8faa0034, 0x8f440368, 0x24020001, 0x15420006, -0x24020002, 0x8f42035c, 0x2442ffff, 0xaf42035c, -0x10000049, 0x8f42035c, 0x15420006, 0x0, -0x8f420364, 0x2442ffff, 0xaf420364, 0x10000042, -0x8f420364, 0x8f420360, 0x2442ffff, 0xaf420360, -0x1000003d, 0x8f420360, 0x30621000, 0x10400005, -0x30628000, 0x8f420078, 0x24420001, 0x10000036, -0xaf420078, 0x10400034, 0x0, 0x8f420078, -0x24420001, 0xaf420078, 0x8c030240, 0x43102b, -0x1440002d, 0x24070008, 0x8f440168, 0x8f45016c, -0x8f430044, 0x8f48000c, 0x8f860120, 0x24020040, -0xafa20010, 0xafa30014, 0xafa80018, 0x8f42010c, -0x40f809, 0x24c6001c, 0x14400011, 0x24020001, -0x3c010001, 0x370821, 0xa02240f2, 0x8f820124, -0xafa20010, 0x8f820128, 0x3c040001, 0x2484688c, -0xafa20014, 0x8f460044, 0x8f870120, 0x3c050009, -0xc002b3b, 0x34a51300, 0x1000000b, 0x0, -0x8f420304, 0x24420001, 0xaf420304, 0x8f420304, -0x8f420044, 0xaf42007c, 0x3c010001, 0x370821, -0xa02040f2, 0xaf400078, 0x8f420318, 0x24420001, -0xaf420318, 0x8f420318, 0x8fbf0060, 0x8fbe005c, -0x8fb50058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, -0x8fb00048, 0x3e00008, 0x27bd0068, 0x3e00008, -0x0, 0x0, 0x0, 0x8f42013c, -0xaf8200c0, 0x8f42013c, 0xaf8200c4, 0x8f42013c, -0xaf8200c8, 0x8f420138, 0xaf8200d0, 0x8f420138, -0xaf8200d4, 0x8f420138, 0x3e00008, 0xaf8200d8, -0x27bdffe0, 0x27840208, 0x24050200, 0xafbf0018, -0xc002bbf, 0x24060008, 0x8c020204, 0xc004012, -0xaf820210, 0x3c020001, 0x8c426d94, 0x30420002, -0x1040000e, 0x2021, 0x8c060248, 0x24020002, -0x3c010001, 0xac226d98, 0xc005104, 0x24050002, -0x2021, 0x8c060248, 0x24020001, 0x3c010001, -0xac226d98, 0x10000011, 0x24050001, 0x8c060248, -0x24020004, 0x3c010001, 0xac226d98, 0xc005104, -0x24050004, 0x3c020001, 0x8c426d94, 0x30420001, -0x10400008, 0x24020001, 0x3c010001, 0xac226d98, -0x2021, 0x24050001, 0x3c06601b, 0xc005104, -0x0, 0x3c040001, 0x248469d0, 0x8f420150, -0x8f430154, 0x3c050008, 0x8f460158, 0x21640, -0x31940, 0x34630403, 0x431025, 0x633c0, -0x461025, 0xaf82021c, 0xafa00010, 0xafa00014, -0x8f86021c, 0x34a50200, 0xc002b3b, 0x3821, -0x3c010001, 0xac206d90, 0x3c010001, 0xac206da8, -0x8fbf0018, 0x3e00008, 0x27bd0020, 0x27bdffe0, -0x3c050008, 0x34a50300, 0xafbf0018, 0xafa00010, -0xafa00014, 0x8f860200, 0x3c040001, 0x248469dc, -0xc002b3b, 0x3821, 0x8f420410, 0x24420001, -0xaf420410, 0x8f420410, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x27bdffd8, 0xafbf0020, 0xafb1001c, -0xafb00018, 0x8f4203a4, 0x24420001, 0xaf4203a4, -0x8f4203a4, 0x8f900220, 0x8f8200e0, 0xafa20010, -0x8f8200e4, 0xafa20014, 0x8f8600c4, 0x8f8700c8, -0x3c040001, 0x248469e8, 0xc002b3b, 0x2002821, -0x3c044000, 0x2041024, 0x504000b4, 0x3c040100, -0x8f4203bc, 0x24420001, 0xaf4203bc, 0x8f4203bc, -0x8f8700c4, 0x8f8300c8, 0x8f420148, 0x671823, -0x43102b, 0x10400003, 0x0, 0x8f420148, -0x621821, 0x10600005, 0x0, 0x8f42014c, -0x43102b, 0x1040000b, 0x0, 0x8f8200e0, -0x8f430124, 0xaf42011c, 0xaf430114, 0x8f820220, -0x3c0308ff, 0x3463fffb, 0x431024, 0x100000ce, -0x441025, 0x8f820220, 0x3c0308ff, 0x3463ffff, -0x431024, 0x34420004, 0xaf820220, 0x8f8200e0, -0x8f430124, 0xaf42011c, 0xaf430114, 0x8f8600c8, -0x8f840120, 0x8f830124, 0x10000005, 0x2821, -0x14620002, 0x24620020, 0x27624800, 0x401821, -0x1064000c, 0x30a200ff, 0x8c620018, 0x30420003, -0x1040fff7, 0x27624fe0, 0x8f4203d0, 0x24050001, -0x24420001, 0xaf4203d0, 0x8f4203d0, 0x8c660008, -0x30a200ff, 0x14400058, 0x0, 0x934205c4, -0x14400055, 0x0, 0x8f8700c4, 0x8f8800e0, -0x8f8400e4, 0x2402fff8, 0x1024024, 0x1041023, -0x218c3, 0x4620001, 0x24630200, 0x10600005, -0x24020001, 0x10620009, 0x0, 0x1000001f, -0x0, 0x8f4203c0, 0xe03021, 0x24420001, -0xaf4203c0, 0x10000040, 0x8f4203c0, 0x8f4203c4, -0x24420001, 0xaf4203c4, 0x8c860000, 0x8f420148, -0x8f4303c4, 0xe61823, 0x43102b, 0x10400004, -0x2c62233f, 0x8f420148, 0x621821, 0x2c62233f, -0x14400031, 0x0, 0x8f42020c, 0x24420001, -0xaf42020c, 0x8f42020c, 0xe03021, 0x24820008, -0xaf8200e4, 0x10000028, 0xaf8200e8, 0x8f4203c8, -0x24420001, 0xaf4203c8, 0x8f4203c8, 0x8c850000, -0x8f420148, 0xa71823, 0x43102b, 0x10400003, -0x0, 0x8f420148, 0x621821, 0x8f42014c, -0x43102b, 0x5440000a, 0xa03021, 0x8f42020c, -0x24420001, 0xaf42020c, 0x8f42020c, 0x24820008, -0xaf8200e4, 0x8f8400e4, 0x1488ffec, 0xaf8400e8, -0x1488000d, 0x27623000, 0x14820002, 0x2482fff8, -0x27623ff8, 0x94430006, 0x3c02001f, 0x3442ffff, -0xc33021, 0x46102b, 0x10400003, 0x0, -0x8f420148, 0xc23023, 0xaf8600c8, 0x8f8300c4, -0x8f420148, 0xc31823, 0x43102b, 0x10400003, -0x0, 0x8f420148, 0x621821, 0x10600005, -0x0, 0x8f42014c, 0x43102b, 0x50400008, -0x3c02fdff, 0x8f820220, 0x3c0308ff, 0x3463fffb, -0x431024, 0x3c034000, 0x1000003f, 0x431025, -0x8f4303cc, 0x3442ffff, 0x282a024, 0x24630001, -0xaf4303cc, 0x10000039, 0x8f4203cc, 0x2041024, -0x1040000e, 0x3c110200, 0x8f4203a8, 0x24420001, -0xaf4203a8, 0x8f4203a8, 0x8f820220, 0x3c0308ff, -0x3463ffff, 0x431024, 0x441025, 0xc003daf, -0xaf820220, 0x10000029, 0x0, 0x2111024, -0x50400008, 0x3c110400, 0x8f4203ac, 0x24420001, -0xaf4203ac, 0xc003daf, 0x8f4203ac, 0x10000019, -0x0, 0x2111024, 0x1040001c, 0x0, -0x8f830224, 0x24021402, 0x14620009, 0x3c050008, -0x3c040001, 0x248469f4, 0xafa00010, 0xafa00014, -0x8f860224, 0x34a50500, 0xc002b3b, 0x3821, -0x8f4203b0, 0x24420001, 0xaf4203b0, 0x8f4203b0, -0x8f820220, 0x2002021, 0x34420002, 0xc004e9c, -0xaf820220, 0x8f820220, 0x3c0308ff, 0x3463ffff, -0x431024, 0x511025, 0xaf820220, 0x8fbf0020, -0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0028, -0x3e00008, 0x0, 0x3c020001, 0x8c426da8, -0x27bdffb0, 0xafbf0048, 0xafbe0044, 0xafb50040, -0xafb3003c, 0xafb20038, 0xafb10034, 0x1040000f, -0xafb00030, 0x3c040001, 0x24846a00, 0x3c050008, -0xafa00010, 0xafa00014, 0x8f860220, 0x34a50600, -0x24020001, 0x3c010001, 0xac206da8, 0x3c010001, -0xac226d9c, 0xc002b3b, 0x3821, 0x3c037fff, -0x8c020268, 0x3463ffff, 0x3c04fdff, 0x431024, -0xac020268, 0x8f420004, 0x3484ffff, 0x30420002, -0x10400092, 0x284a024, 0x3c040600, 0x34842000, -0x8f420004, 0x2821, 0x2403fffd, 0x431024, -0xaf420004, 0xafa40020, 0x8f5e0018, 0x27aa0020, -0x240200ff, 0x13c20002, 0xafaa002c, 0x27c50001, -0x8c020228, 0xa09021, 0x1642000e, 0x1e38c0, -0x8f42033c, 0x24420001, 0xaf42033c, 0x8f42033c, -0x8c020228, 0x3c040001, 0x24846998, 0x3c050009, -0xafa00014, 0xafa20010, 0x8fa60020, 0x1000006d, -0x34a50500, 0xf71021, 0x8fa30020, 0x8fa40024, -0xac4304c0, 0xac4404c4, 0x8f830054, 0x8f820054, -0x247003e8, 0x2021023, 0x2c4203e9, 0x1040001b, -0x9821, 0xe08821, 0x263504c0, 0x8f440178, -0x8f45017c, 0x2201821, 0x240a0004, 0xafaa0010, -0xafb20014, 0x8f48000c, 0x1021, 0x2f53021, -0xafa80018, 0x8f48010c, 0x24070008, 0xa32821, -0xa3482b, 0x822021, 0x100f809, 0x892021, -0x54400006, 0x24130001, 0x8f820054, 0x2021023, -0x2c4203e9, 0x1440ffe9, 0x0, 0x326200ff, -0x54400017, 0xaf520018, 0x8f420378, 0x24420001, -0xaf420378, 0x8f420378, 0x8f820120, 0x8faa002c, -0xafa20010, 0x8f820124, 0x3c040001, 0x248469a4, -0x3c050009, 0xafa20014, 0x8d460000, 0x10000035, -0x34a50600, 0x8f420308, 0x24130001, 0x24420001, -0xaf420308, 0x8f420308, 0x1000001e, 0x326200ff, -0x8f830054, 0x8f820054, 0x247003e8, 0x2021023, -0x2c4203e9, 0x10400016, 0x9821, 0x3c150020, -0x24110010, 0x8f42000c, 0x8f440160, 0x8f450164, -0x8f860120, 0xafb10010, 0xafb20014, 0x551025, -0xafa20018, 0x8f42010c, 0x24070008, 0x40f809, -0x24c6001c, 0x1440ffe3, 0x0, 0x8f820054, -0x2021023, 0x2c4203e9, 0x1440ffee, 0x0, -0x326200ff, 0x14400011, 0x0, 0x8f420378, -0x24420001, 0xaf420378, 0x8f420378, 0x8f820120, -0x8faa002c, 0xafa20010, 0x8f820124, 0x3c040001, -0x248469ac, 0x3c050009, 0xafa20014, 0x8d460000, -0x34a50700, 0xc002b3b, 0x3c03821, 0x8f4202ec, -0x24420001, 0xaf4202ec, 0x8f4202ec, 0x8fbf0048, -0x8fbe0044, 0x8fb50040, 0x8fb3003c, 0x8fb20038, -0x8fb10034, 0x8fb00030, 0x3e00008, 0x27bd0050, -0x3c020001, 0x8c426da8, 0x27bdffe0, 0x1440000d, -0xafbf0018, 0x3c040001, 0x24846a0c, 0x3c050008, -0xafa00010, 0xafa00014, 0x8f860220, 0x34a50700, -0x24020001, 0x3c010001, 0xac226da8, 0xc002b3b, -0x3821, 0x3c020004, 0x2c21024, 0x10400007, -0x0, 0x8f820220, 0x3c0308ff, 0x3463ffff, -0x431024, 0x34420008, 0xaf820220, 0x3c050001, -0x8ca56d98, 0x24020001, 0x14a20007, 0x2021, -0xc00529b, 0x24050001, 0xac02026c, 0x8c03026c, -0x10000006, 0x3c020007, 0xc00529b, 0x2021, -0xac020268, 0x8c030268, 0x3c020007, 0x621824, -0x3c020002, 0x5062000d, 0x3c0205f5, 0x43102b, -0x14400006, 0x3c020004, 0x3c020001, 0x10620009, -0x3c020098, 0x1000000b, 0x0, 0x14620009, -0x3c023b9a, 0x10000004, 0x3442ca00, 0x10000002, -0x3442e100, 0x34429680, 0xaf4201fc, 0x8f4201fc, -0xaee20064, 0x8fbf0018, 0x3e00008, 0x27bd0020, -0x0, 0x0, 0x0, 0x86102b, -0x50400001, 0x872023, 0xc41023, 0x24843, -0x125102b, 0x1040001b, 0x91040, 0x824021, -0x88102b, 0x10400007, 0x1821, 0x94820000, -0x24840002, 0x621821, 0x88102b, 0x1440fffb, -0x0, 0x602021, 0xc73023, 0xa91023, -0x21040, 0xc22821, 0xc5102b, 0x10400007, -0x1821, 0x94c20000, 0x24c60002, 0x621821, -0xc5102b, 0x1440fffb, 0x0, 0x1000000d, -0x832021, 0x51040, 0x822821, 0x85102b, -0x10400007, 0x1821, 0x94820000, 0x24840002, -0x621821, 0x85102b, 0x1440fffb, 0x0, -0x602021, 0x41c02, 0x3082ffff, 0x622021, -0x41c02, 0x3082ffff, 0x622021, 0x3e00008, -0x3082ffff, 0x3e00008, 0x0, 0x802821, -0x30a20001, 0x1040002b, 0x3c03001f, 0x3463ffff, -0x24a20004, 0x62102b, 0x54400007, 0x65102b, -0x90a20001, 0x90a40003, 0x90a30000, 0x90a50002, -0x1000002a, 0x441021, 0x10400003, 0x0, -0x8f420148, 0xa22823, 0x90a40000, 0x24a50001, -0x65102b, 0x10400003, 0x0, 0x8f420148, -0xa22823, 0x90a20000, 0x24a50001, 0x21200, -0x822021, 0x65102b, 0x10400003, 0x0, -0x8f420148, 0xa22823, 0x90a20000, 0x24a50001, -0x822021, 0x65102b, 0x10400003, 0x0, -0x8f420148, 0xa22823, 0x90a20000, 0x1000002d, -0x21200, 0x3463ffff, 0x24a20004, 0x62102b, -0x5440000a, 0x65102b, 0x90a20000, 0x90a40002, -0x90a30001, 0x90a50003, 0x441021, 0x21200, -0x651821, 0x10000020, 0x432021, 0x10400003, -0x0, 0x8f420148, 0xa22823, 0x90a20000, -0x24a50001, 0x22200, 0x65102b, 0x10400003, -0x0, 0x8f420148, 0xa22823, 0x90a20000, -0x24a50001, 0x822021, 0x65102b, 0x10400003, -0x0, 0x8f420148, 0xa22823, 0x90a20000, -0x24a50001, 0x21200, 0x822021, 0x65102b, -0x10400003, 0x0, 0x8f420148, 0xa22823, -0x90a20000, 0x822021, 0x41c02, 0x3082ffff, -0x622021, 0x41c02, 0x3082ffff, 0x622021, -0x3e00008, 0x3082ffff, 0x0, 0x8f820220, -0x34420002, 0xaf820220, 0x3c020002, 0x8c428ff8, -0x30424000, 0x10400054, 0x24040001, 0x8f820200, -0x24067fff, 0x8f830200, 0x30450002, 0x2402fffd, -0x621824, 0xaf830200, 0xaf840204, 0x8f830054, -0x8f820054, 0x10000002, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x8f820224, 0x1444004d, 0x42040, 0xc4102b, -0x1040fff1, 0x0, 0x8f820200, 0x451025, -0xaf820200, 0x8f820220, 0x34428000, 0xaf820220, -0x8f830054, 0x8f820054, 0x10000002, 0x24630001, -0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, -0x0, 0x8f820220, 0x3c030004, 0x431024, -0x1440000f, 0x0, 0x8f820220, 0x3c03ffff, -0x34637fff, 0x431024, 0xaf820220, 0x8f830054, -0x8f820054, 0x10000002, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x8f820220, 0x3c030004, 0x431024, 0x1440000d, -0x0, 0x8f820220, 0x34428000, 0xaf820220, -0x8f830054, 0x8f820054, 0x10000002, 0x24630001, -0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, -0x0, 0x8f820220, 0x3c030004, 0x431024, -0x1040001b, 0x1021, 0x8f830220, 0x24020001, -0x10000015, 0x3c04f700, 0x8f820220, 0x3c04f700, -0x441025, 0xaf820220, 0x8f820220, 0x2403fffd, -0x431024, 0xaf820220, 0x8f820220, 0x3c030300, -0x431024, 0x14400003, 0x0, 0x10000008, -0x1021, 0x8f820220, 0x34420002, 0xaf820220, -0x8f830220, 0x24020001, 0x641825, 0xaf830220, -0x3e00008, 0x0, 0x2021, 0x3c050100, -0x24020001, 0xaf80021c, 0xaf820200, 0xaf820220, -0x27625000, 0xaf8200c0, 0x27625000, 0xaf8200c4, -0x27625000, 0xaf8200c8, 0x27625000, 0xaf8200d0, -0x27625000, 0xaf8200d4, 0x27625000, 0xaf8200d8, -0x27623000, 0xaf8200e0, 0x27623000, 0xaf8200e4, -0x27623000, 0xaf8200e8, 0x27622800, 0xaf8200f0, -0x27622800, 0xaf8200f4, 0x27622800, 0xaf8200f8, -0x418c0, 0x24840001, 0x3631021, 0xac453004, -0x3631021, 0xac403000, 0x28820200, 0x1440fff9, -0x418c0, 0x2021, 0x418c0, 0x24840001, -0x3631021, 0xac402804, 0x3631021, 0xac402800, -0x28820100, 0x1440fff9, 0x418c0, 0xaf80023c, -0x24030080, 0x24040100, 0xac600000, 0x24630004, -0x64102b, 0x5440fffd, 0xac600000, 0x8f830040, -0x3c02f000, 0x621824, 0x3c025000, 0x1062000c, -0x43102b, 0x14400006, 0x3c026000, 0x3c024000, -0x10620008, 0x24020800, 0x10000008, 0x0, -0x10620004, 0x24020800, 0x10000004, 0x0, -0x24020700, 0x3c010001, 0xac226dac, 0x3e00008, -0x0, 0x3c020001, 0x8c426dbc, 0x27bdffd0, -0xafbf002c, 0xafb20028, 0xafb10024, 0xafb00020, -0x3c010001, 0x10400005, 0xac206d94, 0xc004d9e, -0x0, 0x3c010001, 0xac206dbc, 0x8f830054, -0x8f820054, 0x10000002, 0x24630064, 0x8f820054, -0x621023, 0x2c420065, 0x1440fffc, 0x0, -0xc004db9, 0x0, 0x24040001, 0x2821, -0x27a60018, 0x34028000, 0xc0045be, 0xa7a20018, -0x8f830054, 0x8f820054, 0x10000002, 0x24630064, -0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, -0x24040001, 0x24050001, 0xc00457c, 0x27a60018, -0x8f830054, 0x8f820054, 0x10000002, 0x24630064, -0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, -0x24040001, 0x24050001, 0xc00457c, 0x27a60018, -0x8f830054, 0x8f820054, 0x10000002, 0x24630064, -0x8f820054, 0x621023, 0x2c420065, 0x1440fffc, -0x24040001, 0x3c060001, 0x24c66f24, 0xc00457c, -0x24050002, 0x8f830054, 0x8f820054, 0x10000002, -0x24630064, 0x8f820054, 0x621023, 0x2c420065, -0x1440fffc, 0x24040001, 0x24050003, 0x3c100001, -0x26106f26, 0xc00457c, 0x2003021, 0x97a60018, -0x3c070001, 0x94e76f24, 0x3c040001, 0x24846ae0, -0xafa00014, 0x96020000, 0x3c05000d, 0x34a50100, -0xc002b3b, 0xafa20010, 0x97a20018, 0x1040004d, -0x24036040, 0x96020000, 0x3042fff0, 0x1443000c, -0x24020020, 0x3c030001, 0x94636f24, 0x1462000b, -0x24027830, 0x24020003, 0x3c010001, 0xac226d94, -0x24020005, 0x3c010001, 0x1000003f, 0xac226f34, -0x3c030001, 0x94636f24, 0x24027830, 0x1462000c, -0x24030010, 0x3c020001, 0x94426f26, 0x3042fff0, -0x14430007, 0x24020003, 0x3c010001, 0xac226d94, -0x24020006, 0x3c010001, 0x1000002f, 0xac226f34, -0x3c020001, 0x8c426d94, 0x3c030001, 0x94636f24, -0x34420001, 0x3c010001, 0xac226d94, 0x24020015, -0x1462000b, 0x0, 0x3c020001, 0x94426f26, -0x3042fff0, 0x3843f420, 0x2c630001, 0x3842f430, -0x2c420001, 0x621825, 0x1460001b, 0x24020003, -0x3c030001, 0x94636f24, 0x24027810, 0x14620016, -0x24020002, 0x3c020001, 0x94426f26, 0x3042fff0, -0x14400011, 0x24020002, 0x1000000f, 0x24020004, -0x3c020001, 0x8c426d94, 0x34420008, 0x3c010001, -0xac226d94, 0x1000005e, 0x24020004, 0x3c020001, -0x8c426d94, 0x34420004, 0x3c010001, 0x100000af, -0xac226d94, 0x24020001, 0x3c010001, 0xac226f40, -0x3c020001, 0x8c426d94, 0x30420002, 0x144000b2, -0x3c09fff0, 0x24020e00, 0xaf820238, 0x8f840054, -0x8f820054, 0x24030008, 0x3c010001, 0xac236d98, -0x10000002, 0x248401f4, 0x8f820054, 0x821023, -0x2c4201f5, 0x1440fffc, 0x3c0200c8, 0x344201fb, -0xaf820238, 0x8f830054, 0x8f820054, 0x10000002, -0x246301f4, 0x8f820054, 0x621023, 0x2c4201f5, -0x1440fffc, 0x8021, 0x24120001, 0x24110009, -0xc004482, 0x0, 0x3c010001, 0xac326db4, -0xc004547, 0x0, 0x3c020001, 0x8c426db4, -0x1451fffb, 0x3c0200c8, 0x344201f6, 0xaf820238, -0x8f830054, 0x8f820054, 0x10000002, 0x2463000a, -0x8f820054, 0x621023, 0x2c42000b, 0x1440fffc, -0x0, 0x8f820220, 0x24040001, 0x34420002, -0xaf820220, 0x8f830200, 0x24057fff, 0x2402fffd, -0x621824, 0xaf830200, 0xaf840204, 0x8f830054, -0x8f820054, 0x10000002, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x8f820224, 0x14440005, 0x34028000, 0x42040, -0xa4102b, 0x1040fff0, 0x34028000, 0x1082ffa0, -0x26100001, 0x2e020014, 0x1440ffcd, 0x24020004, -0x3c010001, 0xac226d98, 0x8021, 0x24120009, -0x3c11ffff, 0x36313f7f, 0xc004482, 0x0, -0x24020001, 0x3c010001, 0xac226db4, 0xc004547, -0x0, 0x3c020001, 0x8c426db4, 0x1452fffb, -0x0, 0x8f820044, 0x511024, 0x34425080, -0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, -0x2463000a, 0x8f820054, 0x621023, 0x2c42000b, -0x1440fffc, 0x0, 0x8f820044, 0x511024, -0x3442f080, 0xaf820044, 0x8f830054, 0x8f820054, -0x10000002, 0x2463000a, 0x8f820054, 0x621023, -0x2c42000b, 0x1440fffc, 0x0, 0x8f820220, -0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, -0x8f820054, 0x10000002, 0x24630064, 0x8f820054, -0x621023, 0x2c420065, 0x1440fffc, 0x0, -0x8f820220, 0x24040001, 0x34420002, 0xaf820220, -0x8f830200, 0x24057fff, 0x2402fffd, 0x621824, -0xaf830200, 0xaf840204, 0x8f830054, 0x8f820054, -0x10000002, 0x24630001, 0x8f820054, 0x621023, -0x2c420002, 0x1440fffc, 0x0, 0x8f820224, -0x14440005, 0x34028000, 0x42040, 0xa4102b, -0x1040fff0, 0x34028000, 0x1082ff50, 0x26100001, -0x2e020064, 0x1440ffb0, 0x0, 0x3c020001, -0x8c426d94, 0x30420004, 0x14400007, 0x3c09fff0, -0x8f820044, 0x3c03ffff, 0x34633f7f, 0x431024, -0xaf820044, 0x3c09fff0, 0x3529bdc0, 0x3c060001, -0x8cc66d94, 0x3c040001, 0x24846ae0, 0x24020001, -0x3c010001, 0xac226d9c, 0x8f820054, 0x3c070001, -0x8ce76f40, 0x3c030001, 0x94636f24, 0x3c080001, -0x95086f26, 0x3c05000d, 0x34a50100, 0x3c010001, -0xac206d98, 0x491021, 0x3c010001, 0xac226f30, -0xafa30010, 0xc002b3b, 0xafa80014, 0x8fbf002c, -0x8fb20028, 0x8fb10024, 0x8fb00020, 0x3e00008, -0x27bd0030, 0x27bdffe8, 0x3c050001, 0x8ca56d98, -0x24060004, 0x24020001, 0x14a20014, 0xafbf0010, -0x3c020002, 0x8c428ffc, 0x30428000, 0x10400005, -0x3c04000f, 0x3c030001, 0x8c636f40, 0x10000005, -0x34844240, 0x3c040004, 0x3c030001, 0x8c636f40, -0x348493e0, 0x24020005, 0x14620016, 0x0, -0x3c04003d, 0x10000013, 0x34840900, 0x3c020002, -0x8c428ff8, 0x30428000, 0x10400005, 0x3c04001e, -0x3c030001, 0x8c636f40, 0x10000005, 0x34848480, -0x3c04000f, 0x3c030001, 0x8c636f40, 0x34844240, -0x24020005, 0x14620003, 0x0, 0x3c04007a, -0x34841200, 0x3c020001, 0x8c426f30, 0x8f830054, -0x441021, 0x431023, 0x44102b, 0x1440004c, -0x0, 0x3c020001, 0x8c426da0, 0x14400048, -0x0, 0x3c010001, 0x10c00025, 0xac206db0, -0x3c090001, 0x8d296d94, 0x24070001, 0x3c044000, -0x3c080002, 0x25088ffc, 0x250afffc, 0x52842, -0x14a00002, 0x24c6ffff, 0x24050008, 0xa91024, -0x10400010, 0x0, 0x14a70008, 0x0, -0x8d020000, 0x441024, 0x1040000a, 0x0, -0x3c010001, 0x10000007, 0xac256db0, 0x8d420000, -0x441024, 0x10400003, 0x0, 0x3c010001, -0xac276db0, 0x3c020001, 0x8c426db0, 0x6182b, -0x2c420001, 0x431024, 0x5440ffe5, 0x52842, -0x8f820054, 0x3c030001, 0x8c636db0, 0x3c010001, -0xac226f30, 0x1060003b, 0x24020005, 0x3c030001, -0x8c636f40, 0x3c010001, 0xac256d98, 0x14620012, -0x24020001, 0x3c020002, 0x8c428ff8, 0x3c032000, -0x34635000, 0x431024, 0x14400006, 0x24020001, -0x3c010001, 0xac206f1c, 0x3c010001, 0xac226d98, -0x24020001, 0x3c010001, 0xac226e24, 0x3c010001, -0xac226da4, 0x24020001, 0x3c010001, 0xac226d9c, -0x3c020001, 0x8c426db0, 0x1040001e, 0x0, -0x3c020001, 0x8c426d9c, 0x10400008, 0x24020001, -0x3c010001, 0xac206d9c, 0xaee204b8, 0x3c010001, -0xac206e1c, 0x3c010001, 0xac226dd4, 0x8ee304b8, -0x24020008, 0x10620005, 0x24020001, 0xc004239, -0x0, 0x1000000b, 0x0, 0x3c030001, -0x8c636d98, 0x10620007, 0x2402000e, 0x3c030002, -0x8c638f90, 0x10620003, 0x0, 0xc004e9c, -0x8f840220, 0x8fbf0010, 0x3e00008, 0x27bd0018, -0x27bdffe0, 0x3c03fdff, 0x3c040001, 0x8c846d98, -0x3c020001, 0x8c426dc0, 0x3463ffff, 0x283a024, -0x14820006, 0xafbf0018, 0x8ee304b8, 0x3c020001, -0x8c426dc4, 0x10620006, 0x0, 0x8ee204b8, -0x3c010001, 0xac246dc0, 0x3c010001, 0xac226dc4, -0x3c030001, 0x8c636d98, 0x24020002, 0x1062019c, -0x2c620003, 0x10400005, 0x24020001, 0x1062000a, -0x0, 0x10000226, 0x0, 0x24020004, -0x106200b6, 0x24020008, 0x1062010a, 0x24020001, -0x1000021f, 0x0, 0x8ee204b8, 0x2443ffff, -0x2c620008, 0x1040021c, 0x31080, 0x3c010001, -0x220821, 0x8c226af8, 0x400008, 0x0, -0x3c030001, 0x8c636f40, 0x24020005, 0x14620010, -0x0, 0x3c020001, 0x8c426da4, 0x10400008, -0x24020003, 0xc004482, 0x0, 0x24020002, -0xaee204b8, 0x3c010001, 0x10000002, 0xac206da4, -0xaee204b8, 0x3c010001, 0x10000203, 0xac206d30, -0xc004482, 0x0, 0x3c020001, 0x8c426da4, -0x3c010001, 0xac206d30, 0x1440017a, 0x24020002, -0x1000019d, 0x24020007, 0x3c030001, 0x8c636f40, -0x24020005, 0x14620003, 0x24020001, 0x3c010001, -0xac226dd0, 0xc0045ff, 0x0, 0x3c030001, -0x8c636dd0, 0x10000174, 0x24020011, 0x3c050001, -0x8ca56d98, 0x3c060002, 0x8cc68ffc, 0xc005104, -0x2021, 0x24020005, 0x3c010001, 0xac206da4, -0x100001e1, 0xaee204b8, 0x3c040001, 0x24846aec, -0x3c05000f, 0x34a50100, 0x3021, 0x3821, -0xafa00010, 0xc002b3b, 0xafa00014, 0x100001d6, -0x0, 0x8f820220, 0x3c030004, 0x431024, -0x14400175, 0x24020007, 0x8f830054, 0x3c020001, -0x8c426f28, 0x2463d8f0, 0x431023, 0x2c422710, -0x14400003, 0x24020001, 0x3c010001, 0xac226d9c, -0x3c020002, 0x8c428ffc, 0x30425000, 0x104001c2, -0x0, 0x8f820220, 0x30428000, 0x1040017d, -0x0, 0x10000175, 0x0, 0x3c050001, -0x8ca56d98, 0xc00529b, 0x2021, 0xc00551b, -0x2021, 0x3c030002, 0x8c638ff4, 0x46101b0, -0x24020001, 0x3c020008, 0x621024, 0x10400006, -0x0, 0x8f820214, 0x3c03ffff, 0x431024, -0x10000005, 0x3442251f, 0x8f820214, 0x3c03ffff, -0x431024, 0x3442241f, 0xaf820214, 0x8f820220, -0x3c030200, 0x34420002, 0xaf820220, 0x24020008, -0xaee204b8, 0x8f820220, 0x283a025, 0x3c030004, -0x431024, 0x14400016, 0x0, 0x3c020002, -0x8c428ffc, 0x30425000, 0x1040000d, 0x0, -0x8f820220, 0x30428000, 0x10400006, 0x0, -0x8f820220, 0x3c03ffff, 0x34637fff, 0x10000003, -0x431024, 0x8f820220, 0x34428000, 0xaf820220, -0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, -0x3c030001, 0x8c636f40, 0x24020005, 0x1462000a, -0x0, 0x3c020001, 0x94426f26, 0x24429fbc, -0x2c420004, 0x10400004, 0x24040018, 0x24050002, -0xc004ddb, 0x24060020, 0xc003e6d, 0x0, -0x3c010001, 0x10000170, 0xac206e20, 0x8ee204b8, -0x2443ffff, 0x2c620008, 0x1040016b, 0x31080, -0x3c010001, 0x220821, 0x8c226b18, 0x400008, -0x0, 0xc004547, 0x0, 0x3c030001, -0x8c636db4, 0x100000e8, 0x24020009, 0x3c020002, -0x8c428ff8, 0x30424000, 0x10400004, 0x0, -0x8f820044, 0x10000006, 0x3442f080, 0x8f820044, -0x3c03ffff, 0x34633f7f, 0x431024, 0x3442a080, -0xaf820044, 0x8f830054, 0x100000ea, 0x24020004, -0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, -0x431023, 0x2c422710, 0x14400147, 0x24020005, -0x100000d8, 0x0, 0x8f820220, 0x3c03f700, -0x431025, 0xaf820220, 0xaf800204, 0x3c010002, -0x100000d6, 0xac208fe0, 0x8f830054, 0x3c020001, -0x8c426f28, 0x2463fff6, 0x431023, 0x2c42000a, -0x14400135, 0x24020007, 0x100000d7, 0x0, -0xc003f50, 0x0, 0x1040012d, 0x24020001, -0x8f820214, 0x3c03ffff, 0x3c040001, 0x8c846f1c, -0x431024, 0x3442251f, 0xaf820214, 0x24020008, -0x10800005, 0xaee204b8, 0x3c020001, 0x8c426e44, -0x10400064, 0x24020001, 0x8f820220, 0x3c030008, -0x431024, 0x1040006a, 0x3c020200, 0x10000078, -0x0, 0x8ee204b8, 0x2443ffff, 0x2c620007, -0x10400115, 0x31080, 0x3c010001, 0x220821, -0x8c226b38, 0x400008, 0x0, 0xc003daf, -0x0, 0x3c010001, 0xac206d9c, 0xaf800204, -0x3c010002, 0xc004482, 0xac208fe0, 0x24020001, -0x3c010001, 0xac226db4, 0x24020002, 0x10000102, -0xaee204b8, 0xc004547, 0x0, 0x3c030001, -0x8c636db4, 0x10000084, 0x24020009, 0x3c020002, -0x8c428ff8, 0x30424000, 0x10400003, 0x3c0200c8, -0x10000002, 0x344201f6, 0x344201fe, 0xaf820238, -0x8f830054, 0x1000008b, 0x24020004, 0x8f830054, -0x3c020001, 0x8c426f28, 0x2463d8f0, 0x431023, -0x2c422710, 0x144000e8, 0x24020005, 0x10000079, -0x0, 0x8f820220, 0x3c03f700, 0x431025, -0xaf820220, 0xaf800204, 0x3c010002, 0x10000077, -0xac208fe0, 0x8f830054, 0x3c020001, 0x8c426f28, -0x2463fff6, 0x431023, 0x2c42000a, 0x144000d6, -0x24020007, 0x10000078, 0x0, 0xc003f50, -0x0, 0x104000ce, 0x24020001, 0x8f820214, -0x3c03ffff, 0x3c040001, 0x8c846f1c, 0x431024, -0x3442251f, 0xaf820214, 0x24020008, 0x1080000f, -0xaee204b8, 0x3c020001, 0x8c426e44, 0x1440000b, -0x0, 0x8f820220, 0x34420002, 0xaf820220, -0x24020001, 0x3c010002, 0xac228f90, 0xc004e9c, -0x8f840220, 0x10000016, 0x0, 0x8f820220, -0x3c030008, 0x431024, 0x14400011, 0x3c020200, -0x282a025, 0x2402000e, 0x3c010002, 0xac228f90, -0xc00551b, 0x2021, 0x8f820220, 0x34420002, -0xc003e6d, 0xaf820220, 0x3c050001, 0x8ca56d98, -0xc00529b, 0x2021, 0x100000a3, 0x0, -0x3c020001, 0x8c426e44, 0x1040009f, 0x0, -0x3c020001, 0x8c426e40, 0x2442ffff, 0x3c010001, -0xac226e40, 0x14400098, 0x24020002, 0x3c010001, -0xac206e44, 0x3c010001, 0x10000093, 0xac226e40, -0x8ee204b8, 0x2443ffff, 0x2c620007, 0x1040008e, -0x31080, 0x3c010001, 0x220821, 0x8c226b58, -0x400008, 0x0, 0x3c020001, 0x8c426da4, -0x10400018, 0x24020005, 0xc004482, 0x0, -0x24020002, 0xaee204b8, 0x3c010001, 0x1000007e, -0xac206da4, 0xc004963, 0x0, 0x3c030001, -0x8c636dd4, 0x24020006, 0x14620077, 0x24020003, -0x10000075, 0xaee204b8, 0x3c050001, 0x8ca56d98, -0x3c060002, 0x8cc68ff8, 0xc005104, 0x2021, -0x24020005, 0x1000006c, 0xaee204b8, 0x8f820220, -0x3c03f700, 0x431025, 0xaf820220, 0x8f830054, -0x24020006, 0xaee204b8, 0x3c010001, 0x10000062, -0xac236f28, 0x8f820220, 0x3c030004, 0x431024, -0x10400003, 0x24020007, 0x1000005b, 0xaee204b8, -0x8f830054, 0x3c020001, 0x8c426f28, 0x2463d8f0, -0x431023, 0x2c422710, 0x14400003, 0x24020001, -0x3c010001, 0xac226d9c, 0x3c020002, 0x8c428ff8, -0x30425000, 0x1040004c, 0x0, 0x8f820220, -0x30428000, 0x10400007, 0x0, 0x8f820220, -0x3c03ffff, 0x34637fff, 0x431024, 0x10000042, -0xaf820220, 0x8f820220, 0x34428000, 0x1000003e, -0xaf820220, 0x3c050001, 0x8ca56d98, 0xc00529b, -0x2021, 0xc00551b, 0x2021, 0x3c020002, -0x8c428ff0, 0x4410032, 0x24020001, 0x8f820214, -0x3c03ffff, 0x431024, 0x3442251f, 0xaf820214, -0x24020008, 0xaee204b8, 0x8f820220, 0x34420002, -0xaf820220, 0x8f820220, 0x3c030004, 0x431024, -0x14400016, 0x0, 0x3c020002, 0x8c428ff8, -0x30425000, 0x1040000d, 0x0, 0x8f820220, -0x30428000, 0x10400006, 0x0, 0x8f820220, -0x3c03ffff, 0x34637fff, 0x10000003, 0x431024, -0x8f820220, 0x34428000, 0xaf820220, 0x8f820220, -0x3c03f700, 0x431025, 0xaf820220, 0x3c020001, -0x94426f26, 0x24429fbc, 0x2c420004, 0x10400004, -0x24040018, 0x24050002, 0xc004ddb, 0x24060020, -0xc003e6d, 0x0, 0x10000003, 0x0, -0x3c010001, 0xac226d9c, 0x8fbf0018, 0x3e00008, -0x27bd0020, 0x8f820200, 0x8f820220, 0x8f820220, -0x34420004, 0xaf820220, 0x8f820200, 0x3c050001, -0x8ca56d98, 0x34420004, 0xaf820200, 0x24020002, -0x10a2004b, 0x2ca20003, 0x10400005, 0x24020001, -0x10a2000a, 0x0, 0x100000b1, 0x0, -0x24020004, 0x10a20072, 0x24020008, 0x10a20085, -0x3c02f0ff, 0x100000aa, 0x0, 0x8f830050, -0x3c02f0ff, 0x3442ffff, 0x3c040001, 0x8c846f40, -0x621824, 0x3c020700, 0x621825, 0x24020e00, -0x2484fffb, 0x2c840002, 0xaf830050, 0xaf850200, -0xaf850220, 0x14800006, 0xaf820238, 0x8f820044, -0x3c03ffff, 0x34633f7f, 0x431024, 0xaf820044, -0x3c030001, 0x8c636f40, 0x24020005, 0x14620004, -0x0, 0x8f820044, 0x34425000, 0xaf820044, -0x3c020001, 0x8c426d88, 0x3c030001, 0x8c636f40, -0x34420022, 0x2463fffc, 0x2c630002, 0x1460000c, -0xaf820200, 0x3c020001, 0x8c426dac, 0x3c030001, -0x8c636d90, 0x3c040001, 0x8c846d8c, 0x34428000, -0x621825, 0x641825, 0x1000000a, 0x34620002, -0x3c020001, 0x8c426d90, 0x3c030001, 0x8c636dac, -0x3c040001, 0x8c846d8c, 0x431025, 0x441025, -0x34420002, 0xaf820220, 0x1000002f, 0x24020001, -0x24020e01, 0xaf820238, 0x8f830050, 0x3c02f0ff, -0x3442ffff, 0x3c040001, 0x8c846f1c, 0x621824, -0x3c020d00, 0x621825, 0x24020001, 0xaf830050, -0xaf820200, 0xaf820220, 0x10800005, 0x3c033f00, -0x3c020001, 0x8c426d80, 0x10000004, 0x34630070, -0x3c020001, 0x8c426d80, 0x34630072, 0x431025, -0xaf820200, 0x3c030001, 0x8c636d84, 0x3c02f700, -0x621825, 0x3c020001, 0x8c426d90, 0x3c040001, -0x8c846dac, 0x3c050001, 0x8ca56f40, 0x431025, -0x441025, 0xaf820220, 0x24020005, 0x14a20006, -0x24020001, 0x8f820044, 0x2403afff, 0x431024, -0xaf820044, 0x24020001, 0x1000003d, 0xaf820238, -0x8f830050, 0x3c02f0ff, 0x3442ffff, 0x3c040001, -0x8c846f1c, 0x621824, 0x3c020a00, 0x621825, -0x24020001, 0xaf830050, 0xaf820200, 0x1080001e, -0xaf820220, 0x3c020001, 0x8c426e44, 0x1440001a, -0x3c033f00, 0x3c020001, 0x8c426d80, 0x1000001a, -0x346300e0, 0x8f830050, 0x3c040001, 0x8c846f1c, -0x3442ffff, 0x621824, 0x1080000f, 0xaf830050, -0x3c020001, 0x8c426e44, 0x1440000b, 0x3c043f00, -0x3c030001, 0x8c636d80, 0x348400e0, 0x24020001, -0xaf820200, 0xaf820220, 0x641825, 0xaf830200, -0x10000008, 0x3c05f700, 0x3c020001, 0x8c426d80, -0x3c033f00, 0x346300e2, 0x431025, 0xaf820200, -0x3c05f700, 0x34a58000, 0x3c030001, 0x8c636d84, -0x3c020001, 0x8c426d90, 0x3c040001, 0x8c846dac, -0x651825, 0x431025, 0x441025, 0xaf820220, -0x3e00008, 0x0, 0x3c030001, 0x8c636db4, -0x3c020001, 0x8c426db8, 0x10620003, 0x24020002, -0x3c010001, 0xac236db8, 0x1062001d, 0x2c620003, -0x10400025, 0x24020001, 0x14620023, 0x24020004, -0x3c030001, 0x8c636d98, 0x10620006, 0x24020008, -0x1462000c, 0x3c0200c8, 0x344201fb, 0x10000009, -0xaf820238, 0x24020e01, 0xaf820238, 0x8f820044, -0x3c03ffff, 0x34633f7f, 0x431024, 0x34420080, -0xaf820044, 0x8f830054, 0x24020002, 0x3c010001, -0xac226db4, 0x3c010001, 0x1000000b, 0xac236f2c, -0x8f830054, 0x3c020001, 0x8c426f2c, 0x2463d8f0, -0x431023, 0x2c422710, 0x14400003, 0x24020009, -0x3c010001, 0xac226db4, 0x3e00008, 0x0, -0x0, 0x0, 0x0, 0x27bdffd8, -0xafb20018, 0x809021, 0xafb3001c, 0xa09821, -0xafb10014, 0xc08821, 0xafb00010, 0x8021, -0xafbf0020, 0xa6200000, 0xc004d78, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x24100010, 0x2501024, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x2501024, 0x24100010, 0x2701024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x2701024, 0xc004db9, 0x34108000, -0xc004db9, 0x0, 0xc004d58, 0x0, -0x50400005, 0x108042, 0x96220000, 0x501025, -0xa6220000, 0x108042, 0x1600fff7, 0x0, -0xc004db9, 0x0, 0x8fbf0020, 0x8fb3001c, -0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, -0x27bd0028, 0x27bdffd8, 0xafb10014, 0x808821, -0xafb20018, 0xa09021, 0xafb3001c, 0xc09821, -0xafb00010, 0x8021, 0xafbf0020, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0x24100010, 0x2301024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x2301024, 0x24100010, 0x2501024, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x2501024, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x34108000, -0x96620000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fff8, -0x0, 0xc004db9, 0x0, 0x8fbf0020, -0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, -0x3e00008, 0x27bd0028, 0x3c040001, 0x8c846dd0, -0x3c020001, 0x8c426e18, 0x27bdffd8, 0xafbf0020, -0xafb1001c, 0x10820003, 0xafb00018, 0x3c010001, -0xac246e18, 0x3c030001, 0x8c636f40, 0x24020005, -0x14620005, 0x2483ffff, 0xc004963, 0x0, -0x1000034c, 0x0, 0x2c620013, 0x10400349, -0x31080, 0x3c010001, 0x220821, 0x8c226b80, -0x400008, 0x0, 0xc004db9, 0x8021, -0x34028000, 0xa7a20010, 0x27b10010, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, -0x2021, 0x108042, 0x1600fffc, 0x0, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x1000030e, 0x24020002, 0x27b10010, 0xa7a00010, -0x8021, 0xc004d78, 0x24040001, 0x26100001, -0x2e020020, 0x1440fffb, 0x0, 0xc004d78, -0x2021, 0xc004d78, 0x24040001, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x24100010, -0x32020001, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020001, -0x24100010, 0xc004d78, 0x2021, 0x108042, -0x1600fffc, 0x0, 0xc004db9, 0x34108000, -0xc004db9, 0x0, 0xc004d58, 0x0, -0x50400005, 0x108042, 0x96220000, 0x501025, -0xa6220000, 0x108042, 0x1600fff7, 0x0, -0xc004db9, 0x0, 0x97a20010, 0x30428000, -0x144002dc, 0x24020003, 0x100002d8, 0x0, -0x24021200, 0xa7a20010, 0x27b10010, 0x8021, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0xc004d78, 0x2021, 0x108042, 0x1600fffc, -0x0, 0xc004d78, 0x24040001, 0xc004d78, -0x2021, 0x34108000, 0x96220000, 0x501024, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fff8, 0x0, 0xc004db9, -0x0, 0x8f830054, 0x10000296, 0x24020004, -0x8f830054, 0x3c020001, 0x8c426f3c, 0x2463ff9c, -0x431023, 0x2c420064, 0x1440029e, 0x24020002, -0x3c030001, 0x8c636f40, 0x10620297, 0x2c620003, -0x14400296, 0x24020011, 0x24020003, 0x10620005, -0x24020004, 0x10620291, 0x2402000f, 0x1000028f, -0x24020011, 0x1000028d, 0x24020005, 0x24020014, -0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x32020012, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020012, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x34108000, -0x96220000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fff8, -0x0, 0xc004db9, 0x0, 0x8f830054, -0x10000248, 0x24020006, 0x8f830054, 0x3c020001, -0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, -0x14400250, 0x24020007, 0x1000024c, 0x0, -0x24020006, 0xa7a20010, 0x27b10010, 0x8021, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020013, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020013, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x8f830054, 0x10000207, 0x24020008, 0x8f830054, -0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, -0x2c420064, 0x1440020f, 0x24020009, 0x1000020b, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020018, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x8021, -0x97a20010, 0x27b10010, 0x34420001, 0xa7a20010, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020018, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x8f830054, 0x10000193, 0x2402000a, 0x8f830054, -0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, -0x2c420064, 0x1440019b, 0x2402000b, 0x10000197, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020017, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020017, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x8021, -0x97a20010, 0x27b10010, 0x34420700, 0xa7a20010, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020017, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020017, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x8f830054, 0x1000011f, 0x2402000c, 0x8f830054, -0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, -0x2c420064, 0x14400127, 0x24020012, 0x10000123, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020014, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020014, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x8021, -0x97a20010, 0x27b10010, 0x34420010, 0xa7a20010, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020014, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020014, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x8f830054, 0x100000ab, 0x24020013, 0x8f830054, -0x3c020001, 0x8c426f3c, 0x2463ff9c, 0x431023, -0x2c420064, 0x144000b3, 0x2402000d, 0x100000af, -0x0, 0x27b10010, 0xa7a00010, 0x8021, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020018, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x8021, -0x97a20010, 0x27b10010, 0x3042fffe, 0xa7a20010, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020018, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x96220000, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x8f830054, 0x10000037, 0x2402000e, 0x24020840, -0xa7a20010, 0x27b10010, 0x8021, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x32020013, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020013, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x34108000, -0x96220000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fff8, -0x0, 0xc004db9, 0x0, 0x8f830054, -0x24020010, 0x3c010001, 0xac226dd0, 0x3c010001, -0x1000000c, 0xac236f3c, 0x8f830054, 0x3c020001, -0x8c426f3c, 0x2463ff9c, 0x431023, 0x2c420064, -0x14400004, 0x0, 0x24020011, 0x3c010001, -0xac226dd0, 0x8fbf0020, 0x8fb1001c, 0x8fb00018, -0x3e00008, 0x27bd0028, 0x3c030001, 0x8c636d98, -0x27bdffc8, 0x24020002, 0xafbf0034, 0xafb20030, -0xafb1002c, 0x14620004, 0xafb00028, 0x3c120002, -0x10000003, 0x8e528ff8, 0x3c120002, 0x8e528ffc, -0x3c030001, 0x8c636dd4, 0x3c020001, 0x8c426e1c, -0x50620004, 0x2463ffff, 0x3c010001, 0xac236e1c, -0x2463ffff, 0x2c620006, 0x10400377, 0x31080, -0x3c010001, 0x220821, 0x8c226bd8, 0x400008, -0x0, 0x2021, 0x2821, 0xc004ddb, -0x34068000, 0x24040010, 0x24050002, 0x24060002, -0x24020002, 0xc004ddb, 0xa7a20018, 0x24020002, -0x3c010001, 0x10000364, 0xac226dd4, 0x27b10018, -0xa7a00018, 0x8021, 0xc004d78, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x24100010, 0x32020001, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x32020001, 0x24100010, 0xc004d78, 0x2021, -0x108042, 0x1600fffc, 0x0, 0xc004db9, -0x34108000, 0xc004db9, 0x0, 0xc004d58, -0x0, 0x50400005, 0x108042, 0x96220000, -0x501025, 0xa6220000, 0x108042, 0x1600fff7, -0x0, 0xc004db9, 0x0, 0x97a20018, -0x30428000, 0x14400004, 0x24020003, 0x3c010001, -0xac226dd4, 0x24020003, 0x3c010001, 0x1000032a, -0xac226dd4, 0x24040010, 0x24050002, 0x24060002, -0x24020002, 0xc004ddb, 0xa7a20018, 0x3c030001, -0x8c636e20, 0x24020001, 0x146201e1, 0x8021, -0x27b10018, 0xa7a00018, 0xc004d78, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x24100010, 0x32020001, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x32020001, 0x24100010, 0x32020018, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, -0xc004db9, 0x0, 0xc004d58, 0x0, -0x50400005, 0x108042, 0x96220000, 0x501025, -0xa6220000, 0x108042, 0x1600fff7, 0x0, -0xc004db9, 0x8021, 0x27b10018, 0xa7a00018, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0x24100010, 0x32020001, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x32020001, 0x24100010, -0x32020018, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020018, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x96220000, 0x501025, 0xa6220000, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x8021, -0x24040018, 0x2821, 0xc004ddb, 0x24060404, -0xa7a0001a, 0xc004d78, 0x24040001, 0x26100001, -0x2e020020, 0x1440fffb, 0x0, 0xc004d78, -0x2021, 0xc004d78, 0x24040001, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x24100010, -0x32020001, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020001, -0x24100010, 0x32020018, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x32020018, 0xc004db9, 0x34108000, 0xc004db9, -0x0, 0xc004d58, 0x0, 0x50400005, -0x108042, 0x97a2001a, 0x501025, 0xa7a2001a, -0x108042, 0x1600fff7, 0x0, 0xc004db9, -0x8021, 0xa7a0001a, 0xc004d78, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x24100010, 0x32020001, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x32020001, 0x24100010, 0x32020018, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020018, 0xc004db9, 0x34108000, -0xc004db9, 0x0, 0xc004d58, 0x0, -0x50400005, 0x108042, 0x97a2001a, 0x501025, -0xa7a2001a, 0x108042, 0x1600fff7, 0x0, -0xc004db9, 0x8021, 0xa7a0001c, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x24040001, 0xc004d78, -0x2021, 0x24100010, 0xc004d78, 0x2021, -0x108042, 0x1600fffc, 0x0, 0x24100010, -0x3202001e, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x3202001e, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x97a2001c, 0x501025, 0xa7a2001c, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x8021, -0xa7a0001c, 0xc004d78, 0x24040001, 0x26100001, -0x2e020020, 0x1440fffb, 0x0, 0xc004d78, -0x2021, 0xc004d78, 0x24040001, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x24100010, -0xc004d78, 0x2021, 0x108042, 0x1600fffc, -0x0, 0x24100010, 0x3202001e, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x3202001e, 0xc004db9, 0x34108000, -0xc004db9, 0x0, 0xc004d58, 0x0, -0x50400005, 0x108042, 0x97a2001c, 0x501025, -0xa7a2001c, 0x108042, 0x1600fff7, 0x0, -0xc004db9, 0x8021, 0x24020002, 0xa7a2001e, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0x24100010, 0xc004d78, -0x2021, 0x108042, 0x1600fffc, 0x0, -0x24100010, 0x3202001e, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x3202001e, 0xc004d78, 0x24040001, 0xc004d78, -0x2021, 0x34108000, 0x97a2001e, 0x501024, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fff8, 0x0, 0xc004db9, -0x8021, 0xa7a00020, 0xc004d78, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x24100010, 0xc004d78, 0x2021, 0x108042, -0x1600fffc, 0x0, 0x24100010, 0x3202001e, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x3202001e, 0xc004db9, -0x34108000, 0xc004db9, 0x0, 0xc004d58, -0x0, 0x50400005, 0x108042, 0x97a20020, -0x501025, 0xa7a20020, 0x108042, 0x1600fff7, -0x0, 0xc004db9, 0x8021, 0xa7a00020, -0xc004d78, 0x24040001, 0x26100001, 0x2e020020, -0x1440fffb, 0x0, 0xc004d78, 0x2021, -0xc004d78, 0x24040001, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0x24100010, 0xc004d78, -0x2021, 0x108042, 0x1600fffc, 0x0, -0x24100010, 0x3202001e, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fffa, -0x3202001e, 0xc004db9, 0x34108000, 0xc004db9, -0x0, 0xc004d58, 0x0, 0x50400005, -0x108042, 0x97a20020, 0x501025, 0xa7a20020, -0x108042, 0x1600fff7, 0x0, 0xc004db9, -0x8021, 0xa7a00022, 0xc004d78, 0x24040001, -0x26100001, 0x2e020020, 0x1440fffb, 0x0, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0xc004d78, 0x2021, 0xc004d78, 0x24040001, -0x24100010, 0xc004d78, 0x2021, 0x108042, -0x1600fffc, 0x0, 0x24100010, 0xc004d78, -0x2021, 0x108042, 0x1600fffc, 0x0, -0xc004d78, 0x24040001, 0xc004d78, 0x2021, -0x34108000, 0x97a20022, 0x501024, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fff8, 0x0, 0xc004db9, 0x0, -0x24040018, 0x24050002, 0xc004ddb, 0x24060004, -0x3c100001, 0x8e106e24, 0x24020001, 0x1602011d, -0x0, 0x3c020001, 0x94426f26, 0x3c010001, -0xac206e24, 0x24429fbc, 0x2c420004, 0x1040000c, -0x24040009, 0x24050001, 0xc004ddb, 0x24060400, -0x24040018, 0x24050001, 0xc004ddb, 0x24060020, -0x24040018, 0x24050001, 0xc004ddb, 0x24062000, -0x3c024000, 0x2421024, 0x10400123, 0x3c022000, -0x2421024, 0x10400004, 0x0, 0x3c010001, -0x10000003, 0xac306f1c, 0x3c010001, 0xac206f1c, -0x3c030001, 0x8c636f34, 0x24020005, 0x146200f9, -0x0, 0x3c020001, 0x8c426f1c, 0x10400067, -0x3c020004, 0x2421024, 0x10400011, 0xa7a00018, -0x3c020008, 0x2421024, 0x10400002, 0x24020200, -0xa7a20018, 0x3c020010, 0x2421024, 0x10400004, -0x0, 0x97a20018, 0x34420100, 0xa7a20018, -0x97a60018, 0x24040009, 0x10000004, 0x2821, -0x24040009, 0x2821, 0x3021, 0xc004ddb, -0x0, 0x24020001, 0xa7a2001a, 0x3c020008, -0x2421024, 0x1040000c, 0x3c020002, 0x2421024, -0x10400002, 0x24020101, 0xa7a2001a, 0x3c020001, -0x2421024, 0x10400005, 0x3c020010, 0x97a2001a, -0x34420040, 0xa7a2001a, 0x3c020010, 0x2421024, -0x1040000e, 0x3c020002, 0x2421024, 0x10400005, -0x3c020001, 0x97a2001a, 0x34420080, 0xa7a2001a, -0x3c020001, 0x2421024, 0x10400005, 0x3c0300a0, -0x97a2001a, 0x34420020, 0xa7a2001a, 0x3c0300a0, -0x2431024, 0x54430004, 0x3c020020, 0x97a2001a, -0x1000000c, 0x34420400, 0x2421024, 0x50400004, -0x3c020080, 0x97a2001a, 0x10000006, 0x34420800, -0x2421024, 0x10400004, 0x0, 0x97a2001a, -0x34420c00, 0xa7a2001a, 0x97a6001a, 0x24040004, -0xc004ddb, 0x2821, 0x3c020004, 0x2421024, -0x10400004, 0xa7a0001c, 0x32425000, 0x14400004, -0x0, 0x32424000, 0x10400005, 0x2021, -0xc004cf9, 0x2402021, 0x10000096, 0x0, -0x97a6001c, 0x2821, 0x34c61200, 0xc004ddb, -0xa7a6001c, 0x1000008f, 0x0, 0x2421024, -0x10400004, 0xa7a00018, 0x32425000, 0x14400004, -0x0, 0x32424000, 0x10400005, 0x3c020010, -0xc004cf9, 0x2402021, 0x10000019, 0xa7a0001a, -0x2421024, 0x10400004, 0x0, 0x97a20018, -0x10000004, 0xa7a20018, 0x97a20018, 0x34420100, -0xa7a20018, 0x3c020001, 0x2421024, 0x10400004, -0x0, 0x97a20018, 0x10000004, 0xa7a20018, -0x97a20018, 0x34422000, 0xa7a20018, 0x97a60018, -0x2021, 0xc004ddb, 0x2821, 0xa7a0001a, -0x8021, 0xc004d78, 0x24040001, 0x26100001, -0x2e020020, 0x1440fffb, 0x0, 0xc004d78, -0x2021, 0xc004d78, 0x24040001, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x24100010, -0x32020001, 0x10400002, 0x2021, 0x24040001, -0xc004d78, 0x108042, 0x1600fffa, 0x32020001, -0x24100010, 0xc004d78, 0x2021, 0x108042, -0x1600fffc, 0x0, 0xc004db9, 0x34108000, -0xc004db9, 0x0, 0xc004d58, 0x0, -0x50400005, 0x108042, 0x97a2001a, 0x501025, -0xa7a2001a, 0x108042, 0x1600fff7, 0x0, -0xc004db9, 0x8021, 0xa7a0001a, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x24040001, 0xc004d78, -0x2021, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0xc004d78, -0x2021, 0x108042, 0x1600fffc, 0x0, -0xc004db9, 0x34108000, 0xc004db9, 0x0, -0xc004d58, 0x0, 0x50400005, 0x108042, -0x97a2001a, 0x501025, 0xa7a2001a, 0x108042, -0x1600fff7, 0x0, 0xc004db9, 0x0, -0x3c040001, 0x24846bcc, 0x97a60018, 0x97a7001a, -0x3c020001, 0x8c426d98, 0x3c030001, 0x8c636f1c, -0x3c05000d, 0x34a50205, 0xafa20010, 0xc002b3b, -0xafa30014, 0x8f830054, 0x24020004, 0x3c010001, -0xac226dd4, 0x3c010001, 0x10000017, 0xac236f38, -0x8f830054, 0x3c020001, 0x8c426f38, 0x2463ff9c, -0x431023, 0x2c420064, 0x1440000f, 0x0, -0x8f820220, 0x24030005, 0x3c010001, 0xac236dd4, -0x3c03f700, 0x431025, 0x10000007, 0xaf820220, -0x24020006, 0x3c010001, 0xac226dd4, 0x24020011, -0x3c010001, 0xac226dd0, 0x8fbf0034, 0x8fb20030, -0x8fb1002c, 0x8fb00028, 0x3e00008, 0x27bd0038, -0x27bdffd8, 0xafb00018, 0x808021, 0xafb1001c, -0x8821, 0x32024000, 0x10400013, 0xafbf0020, -0x3c020010, 0x2021024, 0x2c420001, 0x21023, -0x30434100, 0x3c020001, 0x2021024, 0x14400006, -0x34714000, 0x3c020002, 0x2021024, 0x14400002, -0x34716000, 0x34714040, 0x2021, 0x2821, -0x10000036, 0x2203021, 0x32021000, 0x10400035, -0x2021, 0x2821, 0xc004ddb, 0x24060040, -0x24040018, 0x2821, 0xc004ddb, 0x24060c00, -0x24040017, 0x2821, 0xc004ddb, 0x24060400, -0x24040016, 0x2821, 0xc004ddb, 0x24060006, -0x24040017, 0x2821, 0xc004ddb, 0x24062500, -0x24040016, 0x2821, 0xc004ddb, 0x24060006, -0x24040017, 0x2821, 0xc004ddb, 0x24064600, -0x24040016, 0x2821, 0xc004ddb, 0x24060006, -0x24040017, 0x2821, 0xc004ddb, 0x24066700, -0x24040016, 0x2821, 0xc004ddb, 0x24060006, -0x2404001f, 0x2821, 0xc004ddb, 0x24060010, -0x24040009, 0x2821, 0xc004ddb, 0x24061500, -0x24040009, 0x2821, 0x24061d00, 0xc004ddb, -0x0, 0x3c040001, 0x24846bf0, 0x3c05000e, -0x34a50100, 0x2003021, 0x2203821, 0xafa00010, -0xc002b3b, 0xafa00014, 0x8fbf0020, 0x8fb1001c, -0x8fb00018, 0x3e00008, 0x27bd0028, 0x8f850044, -0x8f820044, 0x3c030001, 0x431025, 0x3c030008, -0xaf820044, 0x8f840054, 0x8f820054, 0xa32824, -0x10000002, 0x24840001, 0x8f820054, 0x821023, -0x2c420002, 0x1440fffc, 0x0, 0x8f820044, -0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820044, -0x8f830054, 0x8f820054, 0x10000002, 0x24630001, -0x8f820054, 0x621023, 0x2c420002, 0x1440fffc, -0x0, 0x3e00008, 0xa01021, 0x8f830044, -0x3c02fff0, 0x3442ffff, 0x42480, 0x621824, -0x3c020002, 0x822025, 0x641825, 0xaf830044, -0x8f820044, 0x3c03fffe, 0x3463ffff, 0x431024, -0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x8f820044, 0x3c030001, -0x431025, 0xaf820044, 0x8f830054, 0x8f820054, -0x10000002, 0x24630001, 0x8f820054, 0x621023, -0x2c420002, 0x1440fffc, 0x0, 0x3e00008, -0x0, 0x8f820044, 0x2403ff7f, 0x431024, -0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x8f820044, 0x34420080, -0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x3e00008, 0x0, -0x8f820044, 0x3c03fff0, 0x3463ffff, 0x431024, -0xaf820044, 0x8f820044, 0x3c030001, 0x431025, -0xaf820044, 0x8f830054, 0x8f820054, 0x10000002, -0x24630001, 0x8f820054, 0x621023, 0x2c420002, -0x1440fffc, 0x0, 0x8f820044, 0x3c03fffe, -0x3463ffff, 0x431024, 0xaf820044, 0x8f830054, -0x8f820054, 0x10000002, 0x24630001, 0x8f820054, -0x621023, 0x2c420002, 0x1440fffc, 0x0, -0x3e00008, 0x0, 0x27bdffc8, 0xafb30024, -0x809821, 0xafbe002c, 0xa0f021, 0xafb20020, -0xc09021, 0x33c2ffff, 0xafbf0030, 0xafb50028, -0xafb1001c, 0xafb00018, 0x14400034, 0xa7b20010, -0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x2301024, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x2301024, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x34108000, -0x96420000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x12000075, -0x0, 0x1000fff6, 0x0, 0x3275ffff, -0x27b10010, 0xa7a00010, 0x8021, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x24040001, 0xc004d78, -0x2021, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x2b01024, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x2b01024, 0xc004db9, -0x34108000, 0xc004db9, 0x0, 0xc004d58, -0x0, 0x50400005, 0x108042, 0x96220000, -0x501025, 0xa6220000, 0x108042, 0x1600fff7, -0x0, 0xc004db9, 0x0, 0x33c5ffff, -0x24020001, 0x54a20004, 0x24020002, 0x97a20010, -0x10000006, 0x521025, 0x14a20006, 0x3271ffff, -0x97a20010, 0x121827, 0x431024, 0xa7a20010, -0x3271ffff, 0x27b20010, 0x8021, 0xc004d78, -0x24040001, 0x26100001, 0x2e020020, 0x1440fffb, -0x0, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0xc004d78, -0x24040001, 0x24100010, 0x32020001, 0x10400002, -0x2021, 0x24040001, 0xc004d78, 0x108042, -0x1600fffa, 0x32020001, 0x24100010, 0x2301024, -0x10400002, 0x2021, 0x24040001, 0xc004d78, -0x108042, 0x1600fffa, 0x2301024, 0xc004d78, -0x24040001, 0xc004d78, 0x2021, 0x34108000, -0x96420000, 0x501024, 0x10400002, 0x2021, -0x24040001, 0xc004d78, 0x108042, 0x1600fff8, -0x0, 0xc004db9, 0x0, 0x8fbf0030, -0x8fbe002c, 0x8fb50028, 0x8fb30024, 0x8fb20020, -0x8fb1001c, 0x8fb00018, 0x3e00008, 0x27bd0038, -0x0, 0x0, 0x0, 0x27bdffe8, -0xafbf0010, 0x8ee304b8, 0x24020008, 0x146201e0, -0x0, 0x3c020001, 0x8c426f1c, 0x14400005, -0x0, 0xc003daf, 0x8f840224, 0x100001d8, -0x0, 0x8f820220, 0x3c030008, 0x431024, -0x10400026, 0x24020001, 0x8f840224, 0x8f820220, -0x3c030400, 0x431024, 0x10400006, 0x0, -0x3c010002, 0xac208fa0, 0x3c010002, 0x1000000b, -0xac208fc0, 0x3c030002, 0x24638fa0, 0x8c620000, -0x24420001, 0xac620000, 0x2c420002, 0x14400003, -0x24020001, 0x3c010002, 0xac228fc0, 0x3c020002, -0x8c428fc0, 0x10400006, 0x30820040, 0x10400004, -0x24020001, 0x3c010002, 0x10000003, 0xac228fc4, -0x3c010002, 0xac208fc4, 0x3c010002, 0xac248f9c, -0x3c010002, 0x1000000b, 0xac208fd0, 0x3c010002, -0xac228fd0, 0x3c010002, 0xac208fc0, 0x3c010002, -0xac208fa0, 0x3c010002, 0xac208fc4, 0x3c010002, -0xac208f9c, 0x3c030002, 0x8c638f90, 0x3c020002, -0x8c428f94, 0x50620004, 0x2463ffff, 0x3c010002, -0xac238f94, 0x2463ffff, 0x2c62000e, 0x10400194, -0x31080, 0x3c010001, 0x220821, 0x8c226c00, -0x400008, 0x0, 0x24020002, 0x3c010002, -0xac208fc0, 0x3c010002, 0xac208fa0, 0x3c010002, -0xac208f9c, 0x3c010002, 0xac208fc4, 0x3c010002, -0xac208fb8, 0x3c010002, 0xac208fb0, 0xaf800224, -0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fd0, -0x1440004f, 0x3c02fdff, 0x3442ffff, 0xc003daf, -0x282a024, 0xaf800204, 0x8f820200, 0x2403fffd, -0x431024, 0xaf820200, 0x3c010002, 0xac208fe0, -0x8f830054, 0x3c020002, 0x8c428fb8, 0x24040001, -0x3c010002, 0xac248fcc, 0x24420001, 0x3c010002, -0xac228fb8, 0x2c420004, 0x3c010002, 0xac238fb4, -0x14400006, 0x24020003, 0x3c010001, 0xac246d9c, -0x3c010002, 0x1000015e, 0xac208fb8, 0x3c010002, -0x1000015b, 0xac228f90, 0x8f830054, 0x3c020002, -0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, -0x14400003, 0x24020004, 0x3c010002, 0xac228f90, -0x3c020002, 0x8c428fd0, 0x14400021, 0x3c02fdff, -0x3442ffff, 0x1000014a, 0x282a024, 0x3c040001, -0x8c846f20, 0x3c010002, 0xc005084, 0xac208fa8, -0x3c020002, 0x8c428fdc, 0xaf820204, 0x3c020002, -0x8c428fd0, 0x14400012, 0x3c03fdff, 0x8f820204, -0x3463ffff, 0x30420030, 0x1440012f, 0x283a024, -0x3c030002, 0x8c638fdc, 0x24020005, 0x3c010002, -0xac228f90, 0x3c010002, 0x10000131, 0xac238fe0, -0x3c020002, 0x8c428fd0, 0x10400010, 0x3c02fdff, -0x3c020001, 0x8c426e3c, 0x24420001, 0x3c010001, -0xac226e3c, 0x2c420002, 0x14400125, 0x24020001, -0x3c010001, 0xac226e44, 0x3c010001, 0xac206e3c, -0x3c010001, 0x1000011e, 0xac226d9c, 0x3c030002, -0x8c638fc0, 0x3442ffff, 0x10600119, 0x282a024, -0x3c020002, 0x8c428f9c, 0x10400115, 0x0, -0x3c010002, 0xac228fc8, 0x24020003, 0x3c010002, -0xac228fa0, 0x100000b8, 0x24020006, 0x3c010002, -0xac208fa8, 0x8f820204, 0x34420040, 0xaf820204, -0x3c020002, 0x8c428fe0, 0x24030007, 0x3c010002, -0xac238f90, 0x34420040, 0x3c010002, 0xac228fe0, -0x3c020002, 0x8c428fc0, 0x10400005, 0x0, -0x3c020002, 0x8c428f9c, 0x104000f0, 0x24020002, -0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, -0x104000ea, 0x24020002, 0x3c020002, 0x8c428fc4, -0x104000ef, 0x2404ffbf, 0x3c020002, 0x8c428f9c, -0x3c030002, 0x8c638fc8, 0x441024, 0x641824, -0x10430004, 0x24020001, 0x3c010002, 0x100000e4, -0xac228f90, 0x24020003, 0xaca20000, 0x24020008, -0x3c010002, 0xac228f90, 0x3c020002, 0x8c428fcc, -0x1040000c, 0x24020001, 0x3c040002, 0xc005091, -0x8c848f9c, 0x3c020002, 0x8c428fe8, 0x14400005, -0x24020001, 0x3c020002, 0x8c428fe4, 0x10400006, -0x24020001, 0x3c010001, 0xac226d9c, 0x3c010002, -0x100000cb, 0xac208fb8, 0x3c020002, 0x8c428fb0, -0x3c030002, 0x8c638f9c, 0x2c420001, 0x210c0, -0x30630008, 0x3c010002, 0xac228fb0, 0x3c010002, -0xac238fac, 0x8f830054, 0x24020009, 0x3c010002, -0xac228f90, 0x3c010002, 0x100000b9, 0xac238fb4, -0x8f830054, 0x3c020002, 0x8c428fb4, 0x2463d8f0, -0x431023, 0x2c422710, 0x1440009f, 0x0, -0x3c020002, 0x8c428fc0, 0x10400005, 0x0, -0x3c020002, 0x8c428f9c, 0x104000a0, 0x24020002, -0x3c030002, 0x24638fa0, 0x8c620000, 0x2c424e21, -0x1040009a, 0x24020002, 0x3c020002, 0x8c428fcc, -0x1040000e, 0x0, 0x3c020002, 0x8c428f9c, -0x3c010002, 0xac208fcc, 0x30420080, 0x1040002f, -0x2402000c, 0x8f820204, 0x30420080, 0x1440000c, -0x24020003, 0x10000029, 0x2402000c, 0x3c020002, -0x8c428f9c, 0x30420080, 0x14400005, 0x24020003, -0x8f820204, 0x30420080, 0x1040001f, 0x24020003, -0xac620000, 0x2402000a, 0x3c010002, 0xac228f90, -0x3c040002, 0x24848fd8, 0x8c820000, 0x3c030002, -0x8c638fb0, 0x431025, 0xaf820204, 0x8c830000, -0x3c040002, 0x8c848fb0, 0x2402000b, 0x3c010002, -0xac228f90, 0x641825, 0x3c010002, 0xac238fe0, -0x3c050002, 0x24a58fa0, 0x8ca20000, 0x2c424e21, -0x10400066, 0x24020002, 0x3c020002, 0x8c428fd0, -0x10400005, 0x0, 0x2402000c, 0x3c010002, -0x10000067, 0xac228f90, 0x3c020002, 0x8c428fc0, -0x10400063, 0x0, 0x3c040002, 0x8c848f9c, -0x10800055, 0x30820008, 0x3c030002, 0x8c638fac, -0x1062005b, 0x24020003, 0x3c010002, 0xac248fc8, -0xaca20000, 0x24020006, 0x3c010002, 0x10000054, -0xac228f90, 0x8f820200, 0x34420002, 0xaf820200, -0x8f830054, 0x2402000d, 0x3c010002, 0xac228f90, -0x3c010002, 0xac238fb4, 0x8f830054, 0x3c020002, -0x8c428fb4, 0x2463d8f0, 0x431023, 0x2c422710, -0x14400031, 0x0, 0x3c020002, 0x8c428fd0, -0x10400020, 0x2402000e, 0x3c030002, 0x8c638fe4, -0x3c010002, 0x14600015, 0xac228f90, 0xc003e6d, -0x0, 0x3c050001, 0x8ca56d98, 0xc00529b, -0x2021, 0x3c030001, 0x8c636d98, 0x24020004, -0x14620005, 0x2403fffb, 0x3c020001, 0x8c426d94, -0x10000003, 0x2403fff7, 0x3c020001, 0x8c426d94, -0x431024, 0x3c010001, 0xac226d94, 0x8f830224, -0x3c020200, 0x3c010002, 0xac238fec, 0x10000020, -0x282a025, 0x3c020002, 0x8c428fc0, 0x10400005, -0x0, 0x3c020002, 0x8c428f9c, 0x1040000f, -0x24020002, 0x3c020002, 0x8c428fa0, 0x2c424e21, -0x1040000a, 0x24020002, 0x3c020002, 0x8c428fc0, -0x1040000f, 0x0, 0x3c020002, 0x8c428f9c, -0x1440000b, 0x0, 0x24020002, 0x3c010002, -0x10000007, 0xac228f90, 0x3c020002, 0x8c428fc0, -0x10400003, 0x0, 0xc003daf, 0x0, -0x8f820220, 0x3c03f700, 0x431025, 0xaf820220, -0x8fbf0010, 0x3e00008, 0x27bd0018, 0x3c030002, -0x24638fe8, 0x8c620000, 0x10400005, 0x34422000, -0x3c010002, 0xac228fdc, 0x10000003, 0xac600000, -0x3c010002, 0xac248fdc, 0x3e00008, 0x0, -0x27bdffe0, 0x30820030, 0xafbf0018, 0x3c010002, -0xac228fe4, 0x14400067, 0x3c02ffff, 0x34421f0e, -0x821024, 0x14400061, 0x24020030, 0x30822000, -0x1040005d, 0x30838000, 0x31a02, 0x30820001, -0x21200, 0x3c040001, 0x8c846f20, 0x621825, -0x331c2, 0x3c030001, 0x24636e48, 0x30828000, -0x21202, 0x30840001, 0x42200, 0x441025, -0x239c2, 0x61080, 0x431021, 0x471021, -0x90430000, 0x24020001, 0x10620025, 0x0, -0x10600007, 0x24020002, 0x10620013, 0x24020003, -0x1062002c, 0x3c05000f, 0x10000037, 0x0, -0x8f820200, 0x2403feff, 0x431024, 0xaf820200, -0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, -0xaf820220, 0x3c010002, 0xac209004, 0x3c010002, -0x10000034, 0xac20900c, 0x8f820200, 0x34420100, -0xaf820200, 0x8f820220, 0x3c03fffe, 0x3463ffff, -0x431024, 0xaf820220, 0x24020100, 0x3c010002, -0xac229004, 0x3c010002, 0x10000026, 0xac20900c, -0x8f820200, 0x2403feff, 0x431024, 0xaf820200, -0x8f820220, 0x3c030001, 0x431025, 0xaf820220, -0x3c010002, 0xac209004, 0x3c010002, 0x10000019, -0xac23900c, 0x8f820200, 0x34420100, 0xaf820200, -0x8f820220, 0x3c030001, 0x431025, 0xaf820220, -0x24020100, 0x3c010002, 0xac229004, 0x3c010002, -0x1000000c, 0xac23900c, 0x34a5ffff, 0x3c040001, -0x24846c38, 0xafa30010, 0xc002b3b, 0xafa00014, -0x10000004, 0x0, 0x24020030, 0x3c010002, -0xac228fe8, 0x8fbf0018, 0x3e00008, 0x27bd0020, -0x0, 0x0, 0x0, 0x27bdffc8, -0xafb20028, 0x809021, 0xafb3002c, 0xa09821, -0xafb00020, 0xc08021, 0x3c040001, 0x24846c50, -0x3c050009, 0x3c020001, 0x8c426d98, 0x34a59001, -0x2403021, 0x2603821, 0xafbf0030, 0xafb10024, -0xa7a0001a, 0xafb00014, 0xc002b3b, 0xafa20010, -0x24020002, 0x12620083, 0x2e620003, 0x10400005, -0x24020001, 0x1262000a, 0x0, 0x10000173, -0x0, 0x24020004, 0x126200f8, 0x24020008, -0x126200f7, 0x3c02ffec, 0x1000016c, 0x0, -0x3c020001, 0x8c426d94, 0x30420002, 0x14400004, -0x128940, 0x3c02fffb, 0x3442ffff, 0x2028024, -0x3c010002, 0x310821, 0xac308ffc, 0x3c024000, -0x2021024, 0x1040004e, 0x1023c2, 0x30840030, -0x101382, 0x3042001c, 0x3c030001, 0x24636dd8, -0x431021, 0x823821, 0x3c020020, 0x2021024, -0x10400006, 0x24020100, 0x3c010002, 0x310821, -0xac229000, 0x10000005, 0x3c020080, 0x3c010002, -0x310821, 0xac209000, 0x3c020080, 0x2021024, -0x10400006, 0x121940, 0x3c020001, 0x3c010002, -0x230821, 0x10000005, 0xac229008, 0x121140, -0x3c010002, 0x220821, 0xac209008, 0x94e40000, -0x3c030001, 0x8c636f40, 0x24020005, 0x10620010, -0xa7a40018, 0x32024000, 0x10400002, 0x34824000, -0xa7a20018, 0x24040001, 0x94e20002, 0x24050004, -0x24e60002, 0x34420001, 0xc0045be, 0xa4e20002, -0x24040001, 0x2821, 0xc0045be, 0x27a60018, -0x3c020001, 0x8c426d98, 0x24110001, 0x3c010001, -0xac316da4, 0x14530004, 0x32028000, 0xc003daf, -0x0, 0x32028000, 0x1040011c, 0x0, -0xc003daf, 0x0, 0x3c030001, 0x8c636f40, -0x24020005, 0x10620115, 0x24020002, 0x3c010001, -0xac316d9c, 0x3c010001, 0x10000110, 0xac226d98, -0x24040001, 0x24050004, 0x27b0001a, 0xc0045be, -0x2003021, 0x24040001, 0x2821, 0xc0045be, -0x2003021, 0x3c020002, 0x511021, 0x8c428ff4, -0x3c040001, 0x8c846d98, 0x3c03bfff, 0x3463ffff, -0x3c010001, 0xac336da4, 0x431024, 0x3c010002, -0x310821, 0x109300f7, 0xac228ff4, 0x100000f7, -0x0, 0x3c022000, 0x2021024, 0x10400005, -0x24020001, 0x3c010001, 0xac226f1c, 0x10000004, -0x128940, 0x3c010001, 0xac206f1c, 0x128940, -0x3c010002, 0x310821, 0xac308ff8, 0x3c024000, -0x2021024, 0x14400014, 0x0, 0x3c020001, -0x8c426f1c, 0x10400006, 0x24040004, 0x24050001, -0xc004ddb, 0x24062000, 0x24020001, 0xaee204b8, -0x3c020002, 0x511021, 0x8c428ff0, 0x3c03bfff, -0x3463ffff, 0x431024, 0x3c010002, 0x310821, -0x100000d0, 0xac228ff0, 0x3c020001, 0x8c426f1c, -0x10400028, 0x3c0300a0, 0x2031024, 0x5443000d, -0x3c020020, 0x3c020001, 0x8c426f20, 0x24030100, -0x3c010002, 0x310821, 0xac239004, 0x3c030001, -0x3c010002, 0x310821, 0xac23900c, 0x10000015, -0x34420400, 0x2021024, 0x10400008, 0x24030100, -0x3c020001, 0x8c426f20, 0x3c010002, 0x310821, -0xac239004, 0x1000000b, 0x34420800, 0x3c020080, -0x2021024, 0x1040002e, 0x3c030001, 0x3c020001, -0x8c426f20, 0x3c010002, 0x310821, 0xac23900c, -0x34420c00, 0x3c010001, 0xac226f20, 0x10000025, -0x24040001, 0x3c020020, 0x2021024, 0x10400006, -0x24020100, 0x3c010002, 0x310821, 0xac229004, -0x10000005, 0x3c020080, 0x3c010002, 0x310821, -0xac209004, 0x3c020080, 0x2021024, 0x10400007, -0x121940, 0x3c020001, 0x3c010002, 0x230821, -0xac22900c, 0x10000006, 0x24040001, 0x121140, -0x3c010002, 0x220821, 0xac20900c, 0x24040001, -0x2821, 0x27b0001e, 0xc00457c, 0x2003021, -0x24040001, 0x2821, 0xc00457c, 0x2003021, -0x24040001, 0x24050001, 0x27b0001c, 0xc00457c, -0x2003021, 0x24040001, 0x24050001, 0xc00457c, -0x2003021, 0x10000077, 0x0, 0x3c02ffec, -0x3442ffff, 0x2028024, 0x3c020008, 0x2028025, -0x121140, 0x3c010002, 0x220821, 0xac308ff8, -0x3c022000, 0x2021024, 0x10400009, 0x0, -0x3c020001, 0x8c426e44, 0x14400005, 0x24020001, -0x3c010001, 0xac226f1c, 0x10000004, 0x3c024000, -0x3c010001, 0xac206f1c, 0x3c024000, 0x2021024, -0x1440001d, 0x24020e01, 0x3c030001, 0x8c636f1c, -0xaf820238, 0x3c010001, 0xac206db0, 0x10600005, -0x24022020, 0x3c010001, 0xac226f20, 0x24020001, -0xaee204b8, 0x3c04bfff, 0x121940, 0x3c020002, -0x431021, 0x8c428ff0, 0x3c050001, 0x8ca56d98, -0x3484ffff, 0x441024, 0x3c010002, 0x230821, -0xac228ff0, 0x24020001, 0x10a20044, 0x0, -0x10000040, 0x0, 0x3c020001, 0x8c426f1c, -0x1040001c, 0x24022000, 0x3c010001, 0xac226f20, -0x3c0300a0, 0x2031024, 0x14430005, 0x121140, -0x3402a000, 0x3c010001, 0x1000002d, 0xac226f20, -0x3c030002, 0x621821, 0x8c638ff8, 0x3c020020, -0x621024, 0x10400004, 0x24022001, 0x3c010001, -0x10000023, 0xac226f20, 0x3c020080, 0x621024, -0x1040001f, 0x3402a001, 0x3c010001, 0x1000001c, -0xac226f20, 0x3c020020, 0x2021024, 0x10400007, -0x121940, 0x24020100, 0x3c010002, 0x230821, -0xac229004, 0x10000006, 0x3c020080, 0x121140, -0x3c010002, 0x220821, 0xac209004, 0x3c020080, -0x2021024, 0x10400006, 0x121940, 0x3c020001, -0x3c010002, 0x230821, 0x10000005, 0xac22900c, -0x121140, 0x3c010002, 0x220821, 0xac20900c, -0x3c030001, 0x8c636d98, 0x24020001, 0x10620003, -0x0, 0xc003daf, 0x0, 0x8fbf0030, -0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, -0x3e00008, 0x27bd0038, 0x27bdffb0, 0xafb3003c, -0x9821, 0xafb50040, 0xa821, 0xafb10034, -0x8821, 0x24020002, 0xafbf0048, 0xafbe0044, -0xafb20038, 0xafb00030, 0xafa4002c, 0xa7a0001a, -0xa7a00018, 0xa7a00020, 0xa7a0001e, 0xa7a00022, -0x10a20130, 0xa7a0001c, 0x2ca20003, 0x10400005, -0x24020001, 0x10a2000a, 0x3c024000, 0x1000025d, -0x2201021, 0x24020004, 0x10a2020a, 0x24020008, -0x10a20208, 0x2201021, 0x10000256, 0x0, -0x8fa8002c, 0x88140, 0x3c030002, 0x701821, -0x8c638ffc, 0x621024, 0x14400009, 0x24040001, -0x3c027fff, 0x3442ffff, 0x628824, 0x3c010002, -0x300821, 0xac318ff4, 0x10000246, 0x2201021, -0x24050001, 0xc00457c, 0x27a60018, 0x24040001, -0x24050001, 0xc00457c, 0x27a60018, 0x97a20018, -0x30420004, 0x104000d9, 0x3c114000, 0x3c020001, -0x8c426f40, 0x2443ffff, 0x2c620006, 0x104000d9, -0x31080, 0x3c010001, 0x220821, 0x8c226c68, -0x400008, 0x0, 0x24040001, 0x24050011, -0x27b0001a, 0xc00457c, 0x2003021, 0x24040001, -0x24050011, 0xc00457c, 0x2003021, 0x97a3001a, -0x30624000, 0x10400002, 0x3c150010, 0x3c150008, -0x30628000, 0x104000aa, 0x3c130001, 0x100000a8, -0x3c130002, 0x24040001, 0x24050014, 0x27b0001a, -0xc00457c, 0x2003021, 0x24040001, 0x24050014, -0xc00457c, 0x2003021, 0x97a3001a, 0x30621000, -0x10400002, 0x3c150010, 0x3c150008, 0x30620800, -0x10400097, 0x3c130001, 0x10000095, 0x3c130002, -0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, -0x2003021, 0x24040001, 0x24050019, 0xc00457c, -0x2003021, 0x97a2001c, 0x30430700, 0x24020400, -0x10620027, 0x28620401, 0x1040000e, 0x24020200, -0x1062001f, 0x28620201, 0x10400005, 0x24020100, -0x5062001e, 0x3c130001, 0x1000001e, 0x24040001, -0x24020300, 0x50620019, 0x3c130002, 0x10000019, -0x24040001, 0x24020600, 0x1062000d, 0x28620601, -0x10400005, 0x24020500, 0x5062000b, 0x3c130002, -0x10000010, 0x24040001, 0x24020700, 0x1462000d, -0x24040001, 0x3c130004, 0x1000000a, 0x3c150008, -0x10000006, 0x3c130004, 0x10000005, 0x3c150008, -0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, -0x24040001, 0x24050018, 0x27b0001e, 0xc00457c, -0x2003021, 0x24040001, 0x24050018, 0xc00457c, -0x2003021, 0x8fa8002c, 0x97a7001e, 0x81140, -0x3c060002, 0xc23021, 0x8cc68ff4, 0x97a20022, -0x3c100001, 0x26106c5c, 0x2002021, 0xafa20010, -0x97a2001c, 0x3c05000c, 0x34a50303, 0xc002b3b, -0xafa20014, 0x3c020004, 0x16620010, 0x3c020001, -0x8f840054, 0x24030001, 0x24020002, 0x3c010001, -0xac236d9c, 0x3c010001, 0xac226d98, 0x3c010001, -0xac236da4, 0x3c010001, 0xac236e24, 0x3c010001, -0xac246f30, 0x1000004f, 0x2b38825, 0x16620039, -0x3c028000, 0x3c020001, 0x8c426e20, 0x1440001e, -0x24040018, 0x2021, 0x2821, 0xc004ddb, -0x34068000, 0x8f830054, 0x8f820054, 0x2b38825, -0x10000002, 0x24630032, 0x8f820054, 0x621023, -0x2c420033, 0x1440fffc, 0x0, 0x8f830054, -0x24020001, 0x3c010001, 0xac226e20, 0x3c010001, -0xac226d9c, 0x3c010001, 0xac226d98, 0x3c010001, -0xac226da4, 0x3c010001, 0xac226e24, 0x3c010001, -0x1000002c, 0xac236f30, 0x2821, 0xc004ddb, -0x24060404, 0x2021, 0x2405001e, 0x27a60018, -0x24020002, 0xc0045be, 0xa7a20018, 0x2021, -0x2821, 0x27a60018, 0xc0045be, 0xa7a00018, -0x24040018, 0x24050002, 0xc004ddb, 0x24060004, -0x3c028000, 0x2221025, 0x2b31825, 0x10000015, -0x438825, 0x2221025, 0x2751825, 0x438825, -0x2002021, 0x97a6001c, 0x3c070001, 0x8ce76d98, -0x3c05000c, 0x34a50326, 0xafb30010, 0xc002b3b, -0xafb10014, 0x10000007, 0x0, 0x3c110002, -0x2308821, 0x8e318ffc, 0x3c027fff, 0x3442ffff, -0x2228824, 0x3c020001, 0x8c426da8, 0x1040001e, -0x0, 0x3c020001, 0x8c426f1c, 0x10400002, -0x3c022000, 0x2228825, 0x8fa8002c, 0x81140, -0x3c010002, 0x220821, 0x8c229000, 0x10400003, -0x3c020020, 0x10000005, 0x2228825, 0x3c02ffdf, -0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, -0x3c010002, 0x220821, 0x8c229008, 0x10400003, -0x3c020080, 0x10000004, 0x2228825, 0x3c02ff7f, -0x3442ffff, 0x2228824, 0x8fa8002c, 0x81140, -0x3c010002, 0x220821, 0xac318ff4, 0x10000135, -0x2201021, 0x8fa8002c, 0x8f140, 0x3c030002, -0x7e1821, 0x8c638ff8, 0x3c024000, 0x621024, -0x14400009, 0x24040001, 0x3c027fff, 0x3442ffff, -0x628824, 0x3c010002, 0x3e0821, 0xac318ff0, -0x10000124, 0x2201021, 0x2821, 0xc00457c, -0x27a60018, 0x24040001, 0x2821, 0xc00457c, -0x27a60018, 0x24040001, 0x24050001, 0x27b20020, -0xc00457c, 0x2403021, 0x24040001, 0x24050001, -0xc00457c, 0x2403021, 0x24040001, 0x24050004, -0x27b1001e, 0xc00457c, 0x2203021, 0x24040001, -0x24050004, 0xc00457c, 0x2203021, 0x24040001, -0x24050005, 0x27b00022, 0xc00457c, 0x2003021, -0x24040001, 0x24050005, 0xc00457c, 0x2003021, -0x24040001, 0x24050010, 0xc00457c, 0x27a60018, -0x24040001, 0x24050010, 0xc00457c, 0x27a60018, -0x24040001, 0x2405000a, 0xc00457c, 0x2403021, -0x24040001, 0x2405000a, 0xc00457c, 0x2403021, -0x24040001, 0x24050018, 0xc00457c, 0x2203021, -0x24040001, 0x24050018, 0xc00457c, 0x2203021, -0x24040001, 0x24050001, 0xc00457c, 0x27a60018, -0x24040001, 0x24050001, 0xc00457c, 0x27a60018, -0x97a20018, 0x30420004, 0x10400066, 0x3c114000, -0x3c030001, 0x8c636f34, 0x24020005, 0x14620067, -0x24040001, 0x24050019, 0x27b0001c, 0xc00457c, -0x2003021, 0x24040001, 0x24050019, 0xc00457c, -0x2003021, 0x97a2001c, 0x30430700, 0x24020400, -0x10620027, 0x28620401, 0x1040000e, 0x24020200, -0x1062001f, 0x28620201, 0x10400005, 0x24020100, -0x5062001e, 0x3c130001, 0x1000001e, 0x3c020004, -0x24020300, 0x50620019, 0x3c130002, 0x10000019, -0x3c020004, 0x24020600, 0x1062000d, 0x28620601, -0x10400005, 0x24020500, 0x5062000b, 0x3c130002, -0x10000010, 0x3c020004, 0x24020700, 0x1462000d, -0x3c020004, 0x3c130004, 0x1000000a, 0x3c150008, -0x10000006, 0x3c130004, 0x10000005, 0x3c150008, -0x3c130001, 0x10000002, 0x3c150008, 0x3c150010, -0x3c020004, 0x12620017, 0x3c028000, 0x8f820054, -0x24100001, 0x3c010001, 0xac306d9c, 0x3c010001, -0xac306d98, 0x3c010001, 0xac306da4, 0x3c010001, -0xac306e24, 0x3c010001, 0xac226f30, 0x3c020001, -0x16620022, 0x2758825, 0x2021, 0x2821, -0xc004ddb, 0x34068000, 0x3c010001, 0x1000001b, -0xac306e20, 0x2221025, 0x2b31825, 0x438825, -0x97a6001c, 0x3c020001, 0x8c426f1c, 0x3c070001, -0x8ce76d98, 0x3c040001, 0x24846c5c, 0xafa20010, -0x97a2001e, 0x3c05000c, 0x34a50323, 0x3c010001, -0xac206e20, 0xc002b3b, 0xafa20014, 0x10000007, -0x0, 0x3c110002, 0x23e8821, 0x8e318ff0, -0x3c027fff, 0x3442ffff, 0x2228824, 0x3c020001, -0x8c426da8, 0x10400069, 0x0, 0x3c020001, -0x8c426f1c, 0x10400002, 0x3c022000, 0x2228825, -0x8fa8002c, 0x81140, 0x3c010002, 0x220821, -0x8c229004, 0x10400003, 0x3c020020, 0x10000005, -0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, -0x8fa8002c, 0x81140, 0x3c010002, 0x220821, -0x8c22900c, 0x10400003, 0x3c020080, 0x1000004f, -0x2228825, 0x3c02ff7f, 0x3442ffff, 0x1000004b, -0x2228824, 0x8fa8002c, 0x82940, 0x3c030002, -0x651821, 0x8c638ff8, 0x3c024000, 0x621024, -0x14400008, 0x3c027fff, 0x3442ffff, 0x628824, -0x3c010002, 0x250821, 0xac318ff0, 0x10000041, -0x2201021, 0x3c020001, 0x8c426da8, 0x10400034, -0x3c11c00c, 0x3c020001, 0x8c426e44, 0x3c04c00c, -0x34842000, 0x3c030001, 0x8c636f1c, 0x2102b, -0x21023, 0x441024, 0x10600003, 0x518825, -0x3c022000, 0x2228825, 0x3c020002, 0x451021, -0x8c429004, 0x10400003, 0x3c020020, 0x10000004, -0x2228825, 0x3c02ffdf, 0x3442ffff, 0x2228824, -0x8fa8002c, 0x81140, 0x3c010002, 0x220821, -0x8c22900c, 0x10400003, 0x3c020080, 0x10000004, -0x2228825, 0x3c02ff7f, 0x3442ffff, 0x2228824, -0x3c020001, 0x8c426e30, 0x10400002, 0x3c020800, -0x2228825, 0x3c020001, 0x8c426e34, 0x10400002, -0x3c020400, 0x2228825, 0x3c020001, 0x8c426e38, -0x10400006, 0x3c020100, 0x10000004, 0x2228825, -0x3c027fff, 0x3442ffff, 0x628824, 0x8fa8002c, -0x81140, 0x3c010002, 0x220821, 0xac318ff0, -0x2201021, 0x8fbf0048, 0x8fbe0044, 0x8fb50040, -0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, -0x3e00008, 0x27bd0050, 0x27bdffd0, 0xafb20028, -0x809021, 0xafbf002c, 0xafb10024, 0xafb00020, -0x8f840200, 0x3c100001, 0x8e106d98, 0x8f860220, -0x24020002, 0x1202005c, 0x2e020003, 0x10400005, -0x24020001, 0x1202000a, 0x121940, 0x1000010c, -0x0, 0x24020004, 0x120200bf, 0x24020008, -0x120200be, 0x128940, 0x10000105, 0x0, -0x3c050002, 0xa32821, 0x8ca58ffc, 0x3c100002, -0x2038021, 0x8e108ff4, 0x3c024000, 0xa21024, -0x10400038, 0x3c020008, 0x2021024, 0x10400020, -0x34840002, 0x3c020002, 0x431021, 0x8c429000, -0x10400005, 0x34840020, 0x34840100, 0x3c020020, -0x10000006, 0x2028025, 0x2402feff, 0x822024, -0x3c02ffdf, 0x3442ffff, 0x2028024, 0x121140, -0x3c010002, 0x220821, 0x8c229008, 0x10400005, -0x3c020001, 0xc23025, 0x3c020080, 0x10000016, -0x2028025, 0x3c02fffe, 0x3442ffff, 0xc23024, -0x3c02ff7f, 0x3442ffff, 0x1000000f, 0x2028024, -0x2402fedf, 0x822024, 0x3c02fffe, 0x3442ffff, -0xc23024, 0x3c02ff5f, 0x3442ffff, 0x2028024, -0x3c010002, 0x230821, 0xac209000, 0x3c010002, -0x230821, 0xac209008, 0xaf840200, 0xaf860220, -0x8f820220, 0x34420002, 0xaf820220, 0x1000000a, -0x121140, 0x3c02bfff, 0x3442ffff, 0x8f830200, -0x2028024, 0x2402fffd, 0x621824, 0xc003daf, -0xaf830200, 0x121140, 0x3c010002, 0x220821, -0x100000b7, 0xac308ff4, 0x3c020001, 0x8c426f1c, -0x10400069, 0x24050004, 0x24040001, 0xc00457c, -0x27a60018, 0x24040001, 0x24050005, 0xc00457c, -0x27a6001a, 0x97a30018, 0x97a2001a, 0x3c040001, -0x24846e48, 0x30630c00, 0x31a82, 0x30420c00, -0x21282, 0xa7a2001a, 0x21080, 0x441021, -0x431021, 0xa7a30018, 0x90480000, 0x24020001, -0x3103ffff, 0x10620029, 0x28620002, 0x10400005, -0x0, 0x10600009, 0x0, 0x1000003d, -0x0, 0x10700013, 0x24020003, 0x1062002c, -0x0, 0x10000037, 0x0, 0x8f820200, -0x2403feff, 0x431024, 0xaf820200, 0x8f820220, -0x3c03fffe, 0x3463ffff, 0x431024, 0xaf820220, -0x3c010002, 0xac209004, 0x3c010002, 0x10000032, -0xac20900c, 0x8f820200, 0x34420100, 0xaf820200, -0x8f820220, 0x3c03fffe, 0x3463ffff, 0x431024, -0xaf820220, 0x24020100, 0x3c010002, 0xac229004, -0x3c010002, 0x10000024, 0xac20900c, 0x8f820200, -0x2403feff, 0x431024, 0xaf820200, 0x8f820220, -0x3c030001, 0x431025, 0xaf820220, 0x3c010002, -0xac209004, 0x3c010002, 0x10000017, 0xac23900c, -0x8f820200, 0x34420100, 0xaf820200, 0x8f820220, -0x3c030001, 0x431025, 0xaf820220, 0x24020100, -0x3c010002, 0xac229004, 0x3c010002, 0x1000000a, -0xac23900c, 0x3c040001, 0x24846c80, 0x97a6001a, -0x97a70018, 0x3c050001, 0x34a5ffff, 0xafa80010, -0xc002b3b, 0xafa00014, 0x8f820200, 0x34420002, -0x1000004b, 0xaf820200, 0x128940, 0x3c050002, -0xb12821, 0x8ca58ff8, 0x3c100002, 0x2118021, -0x8e108ff0, 0x3c024000, 0xa21024, 0x14400010, -0x0, 0x3c020001, 0x8c426f1c, 0x14400005, -0x3c02bfff, 0x8f820200, 0x34420002, 0xaf820200, -0x3c02bfff, 0x3442ffff, 0xc003daf, 0x2028024, -0x3c010002, 0x310821, 0x10000031, 0xac308ff0, -0x3c020001, 0x8c426f1c, 0x10400005, 0x3c020020, -0x3c020001, 0x8c426e44, 0x10400025, 0x3c020020, -0xa21024, 0x10400007, 0x34840020, 0x24020100, -0x3c010002, 0x310821, 0xac229004, 0x10000006, -0x34840100, 0x3c010002, 0x310821, 0xac209004, -0x2402feff, 0x822024, 0x3c020080, 0xa21024, -0x10400007, 0x121940, 0x3c020001, 0x3c010002, -0x230821, 0xac22900c, 0x10000008, 0xc23025, -0x121140, 0x3c010002, 0x220821, 0xac20900c, -0x3c02fffe, 0x3442ffff, 0xc23024, 0xaf840200, -0xaf860220, 0x8f820220, 0x34420002, 0xaf820220, -0x121140, 0x3c010002, 0x220821, 0xac308ff0, -0x8fbf002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, -0x3e00008, 0x27bd0030, 0x0, 0x1821, -0x308400ff, 0x2405ffdf, 0x2406ffbf, 0x641007, -0x30420001, 0x10400004, 0x0, 0x8f820044, -0x10000003, 0x34420040, 0x8f820044, 0x461024, -0xaf820044, 0x8f820044, 0x34420020, 0xaf820044, -0x8f820044, 0x451024, 0xaf820044, 0x24630001, -0x28620008, 0x5440ffee, 0x641007, 0x3e00008, -0x0, 0x2c820008, 0x1040001b, 0x0, -0x2405ffdf, 0x2406ffbf, 0x41880, 0x3c020001, -0x24426e60, 0x621821, 0x24640004, 0x90620000, -0x10400004, 0x0, 0x8f820044, 0x10000003, -0x34420040, 0x8f820044, 0x461024, 0xaf820044, -0x8f820044, 0x34420020, 0xaf820044, 0x8f820044, -0x451024, 0xaf820044, 0x24630001, 0x64102b, -0x1440ffee, 0x0, 0x3e00008, 0x0, -0x0, 0x0, 0x0, 0x8f8400c4, -0x8f8600e0, 0x8f8700e4, 0x2402fff8, 0xc22824, -0x10e5001a, 0x27623ff8, 0x14e20002, 0x24e80008, -0x27683000, 0x55050004, 0x8d0a0000, 0x30c20004, -0x14400012, 0x805021, 0x8ce90000, 0x8f42013c, -0x1494823, 0x49182b, 0x94eb0006, 0x10600002, -0x25630050, 0x494821, 0x123182b, 0x50400003, -0x8f4201fc, 0x3e00008, 0xe01021, 0xaf8800e8, -0x24420001, 0xaf4201fc, 0xaf8800e4, 0x3e00008, -0x1021, 0x3e00008, 0x0, 0x8f8300e4, -0x27623ff8, 0x10620004, 0x24620008, 0xaf8200e8, -0x3e00008, 0xaf8200e4, 0x27623000, 0xaf8200e8, -0x3e00008, 0xaf8200e4, 0x3e00008, 0x0, -0x0, 0x0, 0x0, 0x8f880120, -0x27624fe0, 0x8f830128, 0x15020002, 0x25090020, -0x27694800, 0x11230012, 0x8fa20010, 0xad040000, -0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, -0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, -0xad020010, 0xad030014, 0xaf890120, 0x8f4300fc, -0x24020001, 0x2463ffff, 0x3e00008, 0xaf4300fc, -0x8f430324, 0x1021, 0x24630001, 0x3e00008, -0xaf430324, 0x3e00008, 0x0, 0x8f880100, -0x276247e0, 0x8f830108, 0x15020002, 0x25090020, -0x27694000, 0x1123000f, 0x8fa20010, 0xad040000, -0xad050004, 0xad060008, 0xa507000e, 0x8fa30014, -0xad020018, 0x8fa20018, 0xad03001c, 0x25030016, -0xad020010, 0xad030014, 0xaf890100, 0x3e00008, -0x24020001, 0x8f430328, 0x1021, 0x24630001, -0x3e00008, 0xaf430328, 0x3e00008, 0x0, -0x0, 0x0, 0x0, 0x0 }; -static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __devinitdata = { -0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f6677, 0x6d61696e, 0x2e632c76, 0x20312e31, -0x2e322e34, 0x35203139, 0x39392f30, 0x312f3234, -0x2030303a, 0x31303a35, 0x35207368, 0x75616e67, -0x20457870, 0x20240000, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x6261644d, 0x656d537a, -0x0, 0x68775665, 0x72000000, 0x62616448, -0x77566572, 0x0, 0x2a2a4441, 0x574e5f41, -0x0, 0x74785278, 0x4266537a, 0x0, -0x62664174, 0x6e4d726b, 0x0, 0x7265645a, -0x6f6e6531, 0x0, 0x70636943, 0x6f6e6600, -0x67656e43, 0x6f6e6600, 0x2a646d61, 0x5244666c, -0x0, 0x2a50414e, 0x49432a00, 0x2e2e2f2e, -0x2e2f2e2e, 0x2f2e2e2f, 0x2e2e2f73, 0x72632f6e, -0x69632f66, 0x77322f63, 0x6f6d6d6f, 0x6e2f6677, -0x6d61696e, 0x2e630000, 0x72636246, 0x6c616773, -0x0, 0x62616452, 0x78526362, 0x0, -0x676c6f62, 0x466c6773, 0x0, 0x2b5f6469, -0x73705f6c, 0x6f6f7000, 0x2b65765f, 0x68616e64, -0x6c657200, 0x63616e74, 0x31446d61, 0x0, -0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x635f636b, -0x73756d00, 0x2b685f73, 0x656e645f, 0x64617461, -0x5f726561, 0x64795f63, 0x6b73756d, 0x0, -0x2b685f64, 0x6d615f72, 0x645f6173, 0x73697374, -0x5f636b73, 0x756d0000, 0x74436b73, 0x6d4f6e00, -0x2b715f64, 0x6d615f74, 0x6f5f6e69, 0x63000000, -0x2b685f73, 0x656e645f, 0x64617461, 0x5f726561, -0x64790000, 0x2b685f64, 0x6d615f72, 0x645f6173, -0x73697374, 0x0, 0x74436b73, 0x6d4f6666, -0x0, 0x2b685f73, 0x656e645f, 0x62645f72, -0x65616479, 0x0, 0x68737453, 0x52696e67, -0x0, 0x62616453, 0x52696e67, 0x0, -0x6e696353, 0x52696e67, 0x0, 0x77446d61, -0x416c6c41, 0x0, 0x2b715f64, 0x6d615f74, -0x6f5f686f, 0x73745f63, 0x6b73756d, 0x0, -0x2b685f6d, 0x61635f72, 0x785f636f, 0x6d705f63, -0x6b73756d, 0x0, 0x2b685f64, 0x6d615f77, -0x725f6173, 0x73697374, 0x5f636b73, 0x756d0000, -0x72436b73, 0x6d4f6e00, 0x2b715f64, 0x6d615f74, -0x6f5f686f, 0x73740000, 0x2b685f6d, 0x61635f72, -0x785f636f, 0x6d700000, 0x2b685f64, 0x6d615f77, -0x725f6173, 0x73697374, 0x0, 0x72436b73, -0x6d4f6666, 0x0, 0x2b685f72, 0x6563765f, -0x62645f72, 0x65616479, 0x0, 0x2b685f72, -0x6563765f, 0x6a756d62, 0x6f5f6264, 0x5f726561, -0x64790000, 0x2b685f72, 0x6563765f, 0x6d696e69, -0x5f62645f, 0x72656164, 0x79000000, 0x2b6d685f, -0x636f6d6d, 0x616e6400, 0x2b685f74, 0x696d6572, -0x0, 0x2b685f64, 0x6f5f7570, 0x64617465, -0x5f74785f, 0x636f6e73, 0x0, 0x2b685f64, -0x6f5f7570, 0x64617465, 0x5f72785f, 0x70726f64, -0x0, 0x2b636b73, 0x756d3136, 0x0, -0x2b706565, 0x6b5f6d61, 0x635f7278, 0x5f776100, -0x2b706565, 0x6b5f6d61, 0x635f7278, 0x0, -0x2b646571, 0x5f6d6163, 0x5f727800, 0x2b685f6d, -0x61635f72, 0x785f6174, 0x746e0000, 0x62616452, -0x6574537a, 0x0, 0x72784264, 0x4266537a, -0x0, 0x2b6e756c, 0x6c5f6861, 0x6e646c65, -0x72000000, 0x66774f70, 0x4661696c, 0x0, -0x2b685f75, 0x70646174, 0x655f6c65, 0x64340000, -0x2b685f75, 0x70646174, 0x655f6c65, 0x64360000, -0x2b685f75, 0x70646174, 0x655f6c65, 0x64320000, -0x696e7453, 0x74617465, 0x0, 0x2a2a696e, -0x69744370, 0x0, 0x23736372, 0x65616d00, -0x69537461, 0x636b4572, 0x0, 0x70726f62, -0x654d656d, 0x0, 0x2a2a4441, 0x574e5f42, -0x0, 0x2b73775f, 0x646d615f, 0x61737369, -0x73745f70, 0x6c75735f, 0x74696d65, 0x72000000, -0x2b267072, 0x656c6f61, 0x645f7772, 0x5f646573, -0x63720000, 0x2b267072, 0x656c6f61, 0x645f7264, -0x5f646573, 0x63720000, 0x2b685f68, 0x665f7469, -0x6d657200, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f7469, 0x6d65722e, 0x632c7620, 0x312e312e, -0x322e3335, 0x20313939, 0x392f3031, 0x2f323720, -0x31393a30, 0x393a3530, 0x20686179, 0x65732045, -0x78702024, 0x0, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x542d446d, 0x61526432, -0x0, 0x542d446d, 0x61526431, 0x0, -0x542d446d, 0x61526442, 0x0, 0x542d446d, -0x61577232, 0x0, 0x542d446d, 0x61577231, -0x0, 0x542d446d, 0x61577242, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f636f, 0x6d6d616e, 0x642e632c, 0x7620312e, -0x312e322e, 0x32382031, 0x3939392f, 0x30312f32, -0x30203139, 0x3a34393a, 0x34392073, 0x6875616e, -0x67204578, 0x70202400, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x3f48636d, 0x644d6278, -0x0, 0x3f636d64, 0x48737453, 0x0, -0x3f636d64, 0x4d634d64, 0x0, 0x3f636d64, -0x50726f6d, 0x0, 0x3f636d64, 0x4c696e6b, -0x0, 0x3f636d64, 0x45727200, 0x86ac, -0x8e5c, 0x8e5c, 0x8de4, 0x8b78, -0x8e30, 0x8e5c, 0x8790, 0x8800, -0x8990, 0x8a68, 0x8a34, 0x8e5c, -0x8870, 0x8b24, 0x8e5c, 0x8b34, -0x87b4, 0x8824, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f6d63, 0x6173742e, 0x632c7620, 0x312e312e, -0x322e3820, 0x31393938, 0x2f31322f, 0x30382030, -0x323a3336, 0x3a333620, 0x73687561, 0x6e672045, -0x78702024, 0x0, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x6164644d, 0x63447570, -0x0, 0x6164644d, 0x6346756c, 0x0, -0x64656c4d, 0x634e6f45, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f646d, 0x612e632c, 0x7620312e, 0x312e322e, -0x32342031, 0x3939382f, 0x31322f32, 0x31203030, -0x3a33333a, 0x30392073, 0x6875616e, 0x67204578, -0x70202400, 0x65767452, 0x6e674600, 0x51657674, -0x46000000, 0x51657674, 0x505f4600, 0x4d657674, -0x526e6746, 0x0, 0x4d516576, 0x74460000, -0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, -0x0, 0x5173436f, 0x6e734600, 0x51725072, -0x6f644600, 0x7377446d, 0x614f6666, 0x0, -0x31446d61, 0x4f6e0000, 0x7377446d, 0x614f6e00, -0x2372446d, 0x6141544e, 0x0, 0x72446d61, -0x41544e30, 0x0, 0x72446d61, 0x41544e31, -0x0, 0x72446d61, 0x34476200, 0x2a50414e, -0x49432a00, 0x2e2e2f2e, 0x2e2f2e2e, 0x2f2e2e2f, -0x2e2e2f73, 0x72632f6e, 0x69632f66, 0x77322f63, -0x6f6d6d6f, 0x6e2f646d, 0x612e6300, 0x2377446d, -0x6141544e, 0x0, 0x77446d61, 0x41544e30, -0x0, 0x77446d61, 0x41544e31, 0x0, -0x77446d61, 0x34476200, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f7472, 0x6163652e, 0x632c7620, 0x312e312e, -0x322e3520, 0x31393938, 0x2f30392f, 0x33302031, -0x383a3530, 0x3a323820, 0x73687561, 0x6e672045, -0x78702024, 0x0, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f6461, 0x74612e63, 0x2c762031, 0x2e312e32, -0x2e313220, 0x31393939, 0x2f30312f, 0x32302031, -0x393a3439, 0x3a353120, 0x73687561, 0x6e672045, -0x78702024, 0x0, 0x46575f56, 0x45525349, -0x4f4e3a20, 0x23312046, 0x72692041, 0x70722037, -0x2031373a, 0x35373a35, 0x32205044, 0x54203230, -0x30300000, 0x46575f43, 0x4f4d5049, 0x4c455f54, -0x494d453a, 0x2031373a, 0x35373a35, 0x32000000, -0x46575f43, 0x4f4d5049, 0x4c455f42, 0x593a2064, -0x65767263, 0x73000000, 0x46575f43, 0x4f4d5049, -0x4c455f48, 0x4f53543a, 0x20636f6d, 0x70757465, -0x0, 0x46575f43, 0x4f4d5049, 0x4c455f44, -0x4f4d4149, 0x4e3a2065, 0x6e672e61, 0x6374656f, -0x6e2e636f, 0x6d000000, 0x46575f43, 0x4f4d5049, -0x4c45523a, 0x20676363, 0x20766572, 0x73696f6e, -0x20322e37, 0x2e320000, 0x0, 0x12041100, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f6d65, 0x6d2e632c, 0x7620312e, 0x312e322e, -0x35203139, 0x39382f30, 0x392f3330, 0x2031383a, -0x35303a30, 0x38207368, 0x75616e67, 0x20457870, -0x20240000, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f7365, 0x6e642e63, 0x2c762031, 0x2e312e32, -0x2e343420, 0x31393938, 0x2f31322f, 0x32312030, -0x303a3333, 0x3a313820, 0x73687561, 0x6e672045, -0x78702024, 0x0, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x69736e74, 0x54637055, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f7265, 0x63762e63, 0x2c762031, 0x2e312e32, -0x2e353320, 0x31393939, 0x2f30312f, 0x31362030, -0x323a3535, 0x3a343320, 0x73687561, 0x6e672045, -0x78702024, 0x0, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x724d6163, 0x43686b30, -0x0, 0x72784672, 0x6d324c67, 0x0, -0x72784e6f, 0x53744264, 0x0, 0x72784e6f, -0x4d694264, 0x0, 0x72784e6f, 0x4a6d4264, -0x0, 0x7278436b, 0x446d6146, 0x0, -0x72785144, 0x6d457846, 0x0, 0x72785144, -0x6d614600, 0x72785144, 0x4c426446, 0x0, -0x72785144, 0x6d426446, 0x0, 0x72784372, -0x63506164, 0x0, 0x72536d51, 0x446d6146, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f6d61, 0x632e632c, 0x7620312e, 0x312e322e, -0x32322031, 0x3939382f, 0x31322f30, 0x38203032, -0x3a33363a, 0x33302073, 0x6875616e, 0x67204578, -0x70202400, 0x65767452, 0x6e674600, 0x51657674, -0x46000000, 0x51657674, 0x505f4600, 0x4d657674, -0x526e6746, 0x0, 0x4d516576, 0x74460000, -0x4d516576, 0x505f4600, 0x5173436f, 0x6e495f46, -0x0, 0x5173436f, 0x6e734600, 0x51725072, -0x6f644600, 0x6d616354, 0x68726573, 0x0, -0x23744d61, 0x6341544e, 0x0, 0x23724d61, -0x6341544e, 0x0, 0x72656d41, 0x73737274, -0x0, 0x6c696e6b, 0x444f574e, 0x0, -0x6c696e6b, 0x55500000, 0x0, 0x0, -0x0, 0x24486561, 0x6465723a, 0x202f7072, -0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, -0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, -0x6e2f636b, 0x73756d2e, 0x632c7620, 0x312e312e, -0x322e3920, 0x31393939, 0x2f30312f, 0x31342030, -0x303a3033, 0x3a343820, 0x73687561, 0x6e672045, -0x78702024, 0x0, 0x65767452, 0x6e674600, -0x51657674, 0x46000000, 0x51657674, 0x505f4600, -0x4d657674, 0x526e6746, 0x0, 0x4d516576, -0x74460000, 0x4d516576, 0x505f4600, 0x5173436f, -0x6e495f46, 0x0, 0x5173436f, 0x6e734600, -0x51725072, 0x6f644600, 0x0, 0x0, -0x0, 0x50726f62, 0x65506879, 0x0, -0x6c6e6b41, 0x53535254, 0x0, 0x109a4, -0x10a1c, 0x10a50, 0x10a7c, 0x11050, -0x10aa8, 0x10b10, 0x111fc, 0x10dc0, -0x10c68, 0x10c80, 0x10cc4, 0x10cec, -0x10d0c, 0x10d34, 0x111fc, 0x10dc0, -0x10df8, 0x10e10, 0x10e40, 0x10e68, -0x10e88, 0x10eb0, 0x0, 0x10fdc, -0x11008, 0x1102c, 0x111fc, 0x11050, -0x11078, 0x11108, 0x0, 0x0, -0x0, 0x1186c, 0x1193c, 0x11a14, -0x11ae4, 0x11b40, 0x11c1c, 0x11c44, -0x11d20, 0x11d48, 0x11ef0, 0x11f18, -0x120c0, 0x122b8, 0x1254c, 0x12460, -0x1254c, 0x12578, 0x120e8, 0x12290, -0x7273745f, 0x676d6969, 0x0, 0x12608, -0x12640, 0x12728, 0x13374, 0x133b4, -0x133cc, 0x7365746c, 0x6f6f7000, 0x0, -0x0, 0x13bbc, 0x13bfc, 0x13c8c, -0x13cd0, 0x13d34, 0x13dc0, 0x13df4, -0x13e7c, 0x13f14, 0x13fe4, 0x14024, -0x140a8, 0x140cc, 0x141dc, 0x646f4261, -0x73655067, 0x0, 0x0, 0x0, -0x0, 0x73746d61, 0x634c4e4b, 0x0, -0x6765746d, 0x636c6e6b, 0x0, 0x14ed8, -0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, -0x14ed8, 0x7365746d, 0x61636163, 0x74000000, -0x0, 0x0 }; -static u32 tigon2FwData[(MAX_DATA_LEN/4) + 1] __devinitdata = { -0x1, -0x1, 0x1, 0xc001fc, 0x3ffc, -0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, -0x43205600, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x416c7465, -0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, -0x0, 0x0, 0x0, 0x1ffffc, -0x1fff7c, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x60cf00, -0x60, 0xcf000000, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x0, -0x0, 0x0, 0x3, 0x0, -0x1, 0x0, 0x0, 0x0, -0x1, 0x0, 0x1, 0x0, -0x0, 0x0, 0x0, 0x1, -0x1, 0x0, 0x0, 0x0, -0x0, 0x0, 0x1000000, 0x21000000, -0x12000140, 0x0, 0x0, 0x20000000, -0x120000a0, 0x0, 0x12000060, 0x12000180, -0x120001e0, 0x0, 0x0, 0x0, -0x1, 0x0, 0x0, 0x0, -0x0, 0x0, 0x0, 0x2, -0x0, 0x0, 0x30001, 0x1, -0x30201, 0x0, 0x0, 0x1010101, -0x1010100, 0x10100, 0x1010001, 0x10001, -0x1000101, 0x101, 0x0, 0x0 }; diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index 187ac6eb6e94..7709992bb6bf 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -1813,6 +1813,25 @@ static void __devinit amd8111e_probe_ext_phy(struct net_device* dev) lp->ext_phy_addr = 1; } +static const struct net_device_ops amd8111e_netdev_ops = { + .ndo_open = amd8111e_open, + .ndo_stop = amd8111e_close, + .ndo_start_xmit = amd8111e_start_xmit, + .ndo_tx_timeout = amd8111e_tx_timeout, + .ndo_get_stats = amd8111e_get_stats, + .ndo_set_multicast_list = amd8111e_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = amd8111e_set_mac_address, + .ndo_do_ioctl = amd8111e_ioctl, + .ndo_change_mtu = amd8111e_change_mtu, +#if AMD8111E_VLAN_TAG_USED + .ndo_vlan_rx_register = amd8111e_vlan_rx_register, +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = amd8111e_poll, +#endif +}; + static int __devinit amd8111e_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1872,7 +1891,6 @@ static int __devinit amd8111e_probe_one(struct pci_dev *pdev, #if AMD8111E_VLAN_TAG_USED dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX ; - dev->vlan_rx_register =amd8111e_vlan_rx_register; #endif lp = netdev_priv(dev); @@ -1901,27 +1919,16 @@ static int __devinit amd8111e_probe_one(struct pci_dev *pdev, if(dynamic_ipg[card_idx++]) lp->options |= OPTION_DYN_IPG_ENABLE; + /* Initialize driver entry points */ - dev->open = amd8111e_open; - dev->hard_start_xmit = amd8111e_start_xmit; - dev->stop = amd8111e_close; - dev->get_stats = amd8111e_get_stats; - dev->set_multicast_list = amd8111e_set_multicast_list; - dev->set_mac_address = amd8111e_set_mac_address; - dev->do_ioctl = amd8111e_ioctl; - dev->change_mtu = amd8111e_change_mtu; + dev->netdev_ops = &amd8111e_netdev_ops; SET_ETHTOOL_OPS(dev, &ops); dev->irq =pdev->irq; - dev->tx_timeout = amd8111e_tx_timeout; dev->watchdog_timeo = AMD8111E_TX_TIMEOUT; netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32); -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = amd8111e_poll; -#endif #if AMD8111E_VLAN_TAG_USED dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; - dev->vlan_rx_register =amd8111e_vlan_rx_register; #endif /* Probe the external PHY */ amd8111e_probe_ext_phy(dev); diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index 9a0be9b2eaad..da64ba88d7f8 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -48,12 +48,18 @@ static int ipddp_mode = IPDDP_DECAP; /* Index to functions, as function prototypes. */ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev); -static struct net_device_stats *ipddp_get_stats(struct net_device *dev); static int ipddp_create(struct ipddp_route *new_rt); static int ipddp_delete(struct ipddp_route *rt); static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt); static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static const struct net_device_ops ipddp_netdev_ops = { + .ndo_start_xmit = ipddp_xmit, + .ndo_do_ioctl = ipddp_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; static struct net_device * __init ipddp_init(void) { @@ -61,7 +67,7 @@ static struct net_device * __init ipddp_init(void) struct net_device *dev; int err; - dev = alloc_etherdev(sizeof(struct net_device_stats)); + dev = alloc_etherdev(0); if (!dev) return ERR_PTR(-ENOMEM); @@ -71,9 +77,7 @@ static struct net_device * __init ipddp_init(void) printk(version); /* Initalize the device structure. */ - dev->hard_start_xmit = ipddp_xmit; - dev->get_stats = ipddp_get_stats; - dev->do_ioctl = ipddp_ioctl; + dev->netdev_ops = &ipddp_netdev_ops; dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ dev->mtu = 585; @@ -103,13 +107,6 @@ static struct net_device * __init ipddp_init(void) return dev; } -/* - * Get the current statistics. This may be called with the card open or closed. - */ -static struct net_device_stats *ipddp_get_stats(struct net_device *dev) -{ - return netdev_priv(dev); -} /* * Transmit LLAP/ELAP frame using aarp_send_ddp. @@ -170,8 +167,8 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ - ((struct net_device_stats *) netdev_priv(dev))->tx_packets++; - ((struct net_device_stats *) netdev_priv(dev))->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0) dev_kfree_skb(skb); diff --git a/drivers/net/atp.c b/drivers/net/atp.c index ea493ce23982..4317b3edb3d7 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -204,8 +204,7 @@ static irqreturn_t atp_interrupt(int irq, void *dev_id); static void net_rx(struct net_device *dev); static void read_block(long ioaddr, int length, unsigned char *buffer, int data_mode); static int net_close(struct net_device *dev); -static void set_rx_mode_8002(struct net_device *dev); -static void set_rx_mode_8012(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); static void tx_timeout(struct net_device *dev); @@ -242,6 +241,17 @@ static int __init atp_init(void) return -ENODEV; } +static const struct net_device_ops atp_netdev_ops = { + .ndo_open = net_open, + .ndo_stop = net_close, + .ndo_start_xmit = atp_send_packet, + .ndo_set_multicast_list = set_rx_mode, + .ndo_tx_timeout = tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __init atp_probe1(long ioaddr) { struct net_device *dev = NULL; @@ -342,12 +352,7 @@ static int __init atp_probe1(long ioaddr) if (dev->mem_end & 0xf) net_debug = dev->mem_end & 7; - dev->open = net_open; - dev->stop = net_close; - dev->hard_start_xmit = atp_send_packet; - dev->set_multicast_list = - lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012; - dev->tx_timeout = tx_timeout; + dev->netdev_ops = &atp_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; res = register_netdev(dev); @@ -903,6 +908,17 @@ static void set_rx_mode_8012(struct net_device *dev) write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */ } +static void set_rx_mode(struct net_device *dev) +{ + struct net_local *lp = netdev_priv(dev); + + if (lp->chip_type == RTL8002) + return set_rx_mode_8002(dev); + else + return set_rx_mode_8012(dev); +} + + static int __init atp_init_module(void) { if (debug) /* Emit version even if no cards detected. */ printk(KERN_INFO "%s", version); diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 0e7470a201f0..6926ebedfdc9 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -2108,6 +2108,22 @@ static int __devinit b44_get_invariants(struct b44 *bp) return err; } +static const struct net_device_ops b44_netdev_ops = { + .ndo_open = b44_open, + .ndo_stop = b44_close, + .ndo_start_xmit = b44_start_xmit, + .ndo_get_stats = b44_get_stats, + .ndo_set_multicast_list = b44_set_rx_mode, + .ndo_set_mac_address = b44_set_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = b44_ioctl, + .ndo_tx_timeout = b44_tx_timeout, + .ndo_change_mtu = b44_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = b44_poll_controller, +#endif +}; + static int __devinit b44_init_one(struct ssb_device *sdev, const struct ssb_device_id *ent) { @@ -2145,20 +2161,9 @@ static int __devinit b44_init_one(struct ssb_device *sdev, bp->rx_pending = B44_DEF_RX_RING_PENDING; bp->tx_pending = B44_DEF_TX_RING_PENDING; - dev->open = b44_open; - dev->stop = b44_close; - dev->hard_start_xmit = b44_start_xmit; - dev->get_stats = b44_get_stats; - dev->set_multicast_list = b44_set_rx_mode; - dev->set_mac_address = b44_set_mac_addr; - dev->do_ioctl = b44_ioctl; - dev->tx_timeout = b44_tx_timeout; + dev->netdev_ops = &b44_netdev_ops; netif_napi_add(dev, &bp->napi, b44_poll, 64); dev->watchdog_timeo = B44_TX_TIMEOUT; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = b44_poll_controller; -#endif - dev->change_mtu = b44_change_mtu; dev->irq = sdev->irq; SET_ETHTOOL_OPS(dev, &b44_ethtool_ops); diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index ef8103b3523e..4be05847f86f 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -8243,6 +8243,9 @@ static int bnx2x_set_eeprom(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); int rc; + if (!netif_running(dev)) + return -EAGAIN; + DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n" DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n", eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset, diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 460c2cad2755..9fb388388fb7 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4148,7 +4148,7 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu) bond_for_each_slave(bond, slave, i) { pr_debug("s %p s->p %p c_m %p\n", slave, - slave->prev, slave->dev->change_mtu); + slave->prev, slave->dev->netdev_ops->ndo_change_mtu); res = dev_set_mtu(slave->dev, new_mtu); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 321f43d9f0e2..840b3d1a22f5 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -4977,6 +4977,22 @@ static void __devinit cas_program_bridge(struct pci_dev *cas_pdev) pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xff); } +static const struct net_device_ops cas_netdev_ops = { + .ndo_open = cas_open, + .ndo_stop = cas_close, + .ndo_start_xmit = cas_start_xmit, + .ndo_get_stats = cas_get_stats, + .ndo_set_multicast_list = cas_set_multicast, + .ndo_do_ioctl = cas_ioctl, + .ndo_tx_timeout = cas_tx_timeout, + .ndo_change_mtu = cas_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cas_netpoll, +#endif +}; + static int __devinit cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -5166,22 +5182,13 @@ static int __devinit cas_init_one(struct pci_dev *pdev, for (i = 0; i < N_RX_FLOWS; i++) skb_queue_head_init(&cp->rx_flows[i]); - dev->open = cas_open; - dev->stop = cas_close; - dev->hard_start_xmit = cas_start_xmit; - dev->get_stats = cas_get_stats; - dev->set_multicast_list = cas_set_multicast; - dev->do_ioctl = cas_ioctl; + dev->netdev_ops = &cas_netdev_ops; dev->ethtool_ops = &cas_ethtool_ops; - dev->tx_timeout = cas_tx_timeout; dev->watchdog_timeo = CAS_TX_TIMEOUT; - dev->change_mtu = cas_change_mtu; + #ifdef USE_NAPI netif_napi_add(dev, &cp->napi, cas_poll, 64); #endif -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = cas_netpoll; -#endif dev->irq = pdev->irq; dev->dma = 0; diff --git a/drivers/net/de600.c b/drivers/net/de600.c index 970f820ba814..de63f1d41d32 100644 --- a/drivers/net/de600.c +++ b/drivers/net/de600.c @@ -378,6 +378,16 @@ static void de600_rx_intr(struct net_device *dev) */ } +static const struct net_device_ops de600_netdev_ops = { + .ndo_open = de600_open, + .ndo_stop = de600_close, + .ndo_start_xmit = de600_start_xmit, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + + static struct net_device * __init de600_probe(void) { int i; @@ -439,9 +449,7 @@ static struct net_device * __init de600_probe(void) printk(", Ethernet Address: %pM\n", dev->dev_addr); - dev->open = de600_open; - dev->stop = de600_close; - dev->hard_start_xmit = &de600_start_xmit; + dev->netdev_ops = &de600_netdev_ops; dev->flags&=~IFF_MULTICAST; diff --git a/drivers/net/de620.c b/drivers/net/de620.c index bdfa89403389..d52f34cc9526 100644 --- a/drivers/net/de620.c +++ b/drivers/net/de620.c @@ -784,6 +784,17 @@ static int adapter_init(struct net_device *dev) return 0; /* all ok */ } +static const struct net_device_ops de620_netdev_ops = { + .ndo_open = de620_open, + .ndo_stop = de620_close, + .ndo_start_xmit = de620_start_xmit, + .ndo_tx_timeout = de620_timeout, + .ndo_set_multicast_list = de620_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + /****************************************************************************** * * Only start-up code below @@ -861,12 +872,8 @@ struct net_device * __init de620_probe(int unit) else printk(" UTP)\n"); - dev->open = de620_open; - dev->stop = de620_close; - dev->hard_start_xmit = de620_start_xmit; - dev->tx_timeout = de620_timeout; + dev->netdev_ops = &de620_netdev_ops; dev->watchdog_timeo = HZ*2; - dev->set_multicast_list = de620_set_multicast_list; /* base_addr and irq are already set, see above! */ diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 134b2d60b479..86bb876fb123 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -161,6 +161,7 @@ #include <linux/skbuff.h> #include <linux/ethtool.h> #include <linux/string.h> +#include <linux/firmware.h> #include <asm/unaligned.h> @@ -174,10 +175,17 @@ #define E100_WATCHDOG_PERIOD (2 * HZ) #define E100_NAPI_WEIGHT 16 +#define FIRMWARE_D101M "e100/d101m_ucode.bin" +#define FIRMWARE_D101S "e100/d101s_ucode.bin" +#define FIRMWARE_D102E "e100/d102e_ucode.bin" + MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +MODULE_FIRMWARE(FIRMWARE_D101M); +MODULE_FIRMWARE(FIRMWARE_D101S); +MODULE_FIRMWARE(FIRMWARE_D102E); static int debug = 3; static int eeprom_bad_csum_allow = 0; @@ -1049,178 +1057,6 @@ static void e100_configure(struct nic *nic, struct cb *cb, struct sk_buff *skb) c[16], c[17], c[18], c[19], c[20], c[21], c[22], c[23]); } -/********************************************************/ -/* Micro code for 8086:1229 Rev 8 */ -/********************************************************/ - -/* Parameter values for the D101M B-step */ -#define D101M_CPUSAVER_TIMER_DWORD 78 -#define D101M_CPUSAVER_BUNDLE_DWORD 65 -#define D101M_CPUSAVER_MIN_SIZE_DWORD 126 - -#define D101M_B_RCVBUNDLE_UCODE \ -{\ -0x00550215, 0xFFFF0437, 0xFFFFFFFF, 0x06A70789, 0xFFFFFFFF, 0x0558FFFF, \ -0x000C0001, 0x00101312, 0x000C0008, 0x00380216, \ -0x0010009C, 0x00204056, 0x002380CC, 0x00380056, \ -0x0010009C, 0x00244C0B, 0x00000800, 0x00124818, \ -0x00380438, 0x00000000, 0x00140000, 0x00380555, \ -0x00308000, 0x00100662, 0x00100561, 0x000E0408, \ -0x00134861, 0x000C0002, 0x00103093, 0x00308000, \ -0x00100624, 0x00100561, 0x000E0408, 0x00100861, \ -0x000C007E, 0x00222C21, 0x000C0002, 0x00103093, \ -0x00380C7A, 0x00080000, 0x00103090, 0x00380C7A, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x0010009C, 0x00244C2D, 0x00010004, 0x00041000, \ -0x003A0437, 0x00044010, 0x0038078A, 0x00000000, \ -0x00100099, 0x00206C7A, 0x0010009C, 0x00244C48, \ -0x00130824, 0x000C0001, 0x00101213, 0x00260C75, \ -0x00041000, 0x00010004, 0x00130826, 0x000C0006, \ -0x002206A8, 0x0013C926, 0x00101313, 0x003806A8, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00080600, 0x00101B10, 0x00050004, 0x00100826, \ -0x00101210, 0x00380C34, 0x00000000, 0x00000000, \ -0x0021155B, 0x00100099, 0x00206559, 0x0010009C, \ -0x00244559, 0x00130836, 0x000C0000, 0x00220C62, \ -0x000C0001, 0x00101B13, 0x00229C0E, 0x00210C0E, \ -0x00226C0E, 0x00216C0E, 0x0022FC0E, 0x00215C0E, \ -0x00214C0E, 0x00380555, 0x00010004, 0x00041000, \ -0x00278C67, 0x00040800, 0x00018100, 0x003A0437, \ -0x00130826, 0x000C0001, 0x00220559, 0x00101313, \ -0x00380559, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00130831, 0x0010090B, 0x00124813, \ -0x000CFF80, 0x002606AB, 0x00041000, 0x00010004, \ -0x003806A8, 0x00000000, 0x00000000, 0x00000000, \ -} - -/********************************************************/ -/* Micro code for 8086:1229 Rev 9 */ -/********************************************************/ - -/* Parameter values for the D101S */ -#define D101S_CPUSAVER_TIMER_DWORD 78 -#define D101S_CPUSAVER_BUNDLE_DWORD 67 -#define D101S_CPUSAVER_MIN_SIZE_DWORD 128 - -#define D101S_RCVBUNDLE_UCODE \ -{\ -0x00550242, 0xFFFF047E, 0xFFFFFFFF, 0x06FF0818, 0xFFFFFFFF, 0x05A6FFFF, \ -0x000C0001, 0x00101312, 0x000C0008, 0x00380243, \ -0x0010009C, 0x00204056, 0x002380D0, 0x00380056, \ -0x0010009C, 0x00244F8B, 0x00000800, 0x00124818, \ -0x0038047F, 0x00000000, 0x00140000, 0x003805A3, \ -0x00308000, 0x00100610, 0x00100561, 0x000E0408, \ -0x00134861, 0x000C0002, 0x00103093, 0x00308000, \ -0x00100624, 0x00100561, 0x000E0408, 0x00100861, \ -0x000C007E, 0x00222FA1, 0x000C0002, 0x00103093, \ -0x00380F90, 0x00080000, 0x00103090, 0x00380F90, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x0010009C, 0x00244FAD, 0x00010004, 0x00041000, \ -0x003A047E, 0x00044010, 0x00380819, 0x00000000, \ -0x00100099, 0x00206FFD, 0x0010009A, 0x0020AFFD, \ -0x0010009C, 0x00244FC8, 0x00130824, 0x000C0001, \ -0x00101213, 0x00260FF7, 0x00041000, 0x00010004, \ -0x00130826, 0x000C0006, 0x00220700, 0x0013C926, \ -0x00101313, 0x00380700, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00080600, 0x00101B10, 0x00050004, 0x00100826, \ -0x00101210, 0x00380FB6, 0x00000000, 0x00000000, \ -0x002115A9, 0x00100099, 0x002065A7, 0x0010009A, \ -0x0020A5A7, 0x0010009C, 0x002445A7, 0x00130836, \ -0x000C0000, 0x00220FE4, 0x000C0001, 0x00101B13, \ -0x00229F8E, 0x00210F8E, 0x00226F8E, 0x00216F8E, \ -0x0022FF8E, 0x00215F8E, 0x00214F8E, 0x003805A3, \ -0x00010004, 0x00041000, 0x00278FE9, 0x00040800, \ -0x00018100, 0x003A047E, 0x00130826, 0x000C0001, \ -0x002205A7, 0x00101313, 0x003805A7, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00130831, \ -0x0010090B, 0x00124813, 0x000CFF80, 0x00260703, \ -0x00041000, 0x00010004, 0x00380700 \ -} - -/********************************************************/ -/* Micro code for the 8086:1229 Rev F/10 */ -/********************************************************/ - -/* Parameter values for the D102 E-step */ -#define D102_E_CPUSAVER_TIMER_DWORD 42 -#define D102_E_CPUSAVER_BUNDLE_DWORD 54 -#define D102_E_CPUSAVER_MIN_SIZE_DWORD 46 - -#define D102_E_RCVBUNDLE_UCODE \ -{\ -0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x0EF70E36, 0x1FFF1FFF, \ -0x00E014B9, 0x00000000, 0x00000000, 0x00000000, \ -0x00E014BD, 0x00000000, 0x00000000, 0x00000000, \ -0x00E014D5, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00E014C1, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00E014C8, 0x00000000, 0x00000000, 0x00000000, \ -0x00200600, 0x00E014EE, 0x00000000, 0x00000000, \ -0x0030FF80, 0x00940E46, 0x00038200, 0x00102000, \ -0x00E00E43, 0x00000000, 0x00000000, 0x00000000, \ -0x00300006, 0x00E014FB, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00906E41, 0x00800E3C, 0x00E00E39, 0x00000000, \ -0x00906EFD, 0x00900EFD, 0x00E00EF8, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -0x00000000, 0x00000000, 0x00000000, 0x00000000, \ -} - -static void e100_setup_ucode(struct nic *nic, struct cb *cb, struct sk_buff *skb) -{ -/* *INDENT-OFF* */ - static struct { - u32 ucode[UCODE_SIZE + 1]; - u8 mac; - u8 timer_dword; - u8 bundle_dword; - u8 min_size_dword; - } ucode_opts[] = { - { D101M_B_RCVBUNDLE_UCODE, - mac_82559_D101M, - D101M_CPUSAVER_TIMER_DWORD, - D101M_CPUSAVER_BUNDLE_DWORD, - D101M_CPUSAVER_MIN_SIZE_DWORD }, - { D101S_RCVBUNDLE_UCODE, - mac_82559_D101S, - D101S_CPUSAVER_TIMER_DWORD, - D101S_CPUSAVER_BUNDLE_DWORD, - D101S_CPUSAVER_MIN_SIZE_DWORD }, - { D102_E_RCVBUNDLE_UCODE, - mac_82551_F, - D102_E_CPUSAVER_TIMER_DWORD, - D102_E_CPUSAVER_BUNDLE_DWORD, - D102_E_CPUSAVER_MIN_SIZE_DWORD }, - { D102_E_RCVBUNDLE_UCODE, - mac_82551_10, - D102_E_CPUSAVER_TIMER_DWORD, - D102_E_CPUSAVER_BUNDLE_DWORD, - D102_E_CPUSAVER_MIN_SIZE_DWORD }, - { {0}, 0, 0, 0, 0} - }, *opts; -/* *INDENT-ON* */ - /************************************************************************* * CPUSaver parameters * @@ -1280,42 +1116,101 @@ static void e100_setup_ucode(struct nic *nic, struct cb *cb, struct sk_buff *skb #define BUNDLEMAX (u16)6 #define INTDELAY (u16)1536 /* 0x600 */ +/* Initialize firmware */ +static const struct firmware *e100_request_firmware(struct nic *nic) +{ + const char *fw_name; + const struct firmware *fw; + u8 timer, bundle, min_size; + int err; + /* do not load u-code for ICH devices */ if (nic->flags & ich) - goto noloaducode; + return NULL; /* Search for ucode match against h/w revision */ - for (opts = ucode_opts; opts->mac; opts++) { - int i; - u32 *ucode = opts->ucode; - if (nic->mac != opts->mac) - continue; - - /* Insert user-tunable settings */ - ucode[opts->timer_dword] &= 0xFFFF0000; - ucode[opts->timer_dword] |= INTDELAY; - ucode[opts->bundle_dword] &= 0xFFFF0000; - ucode[opts->bundle_dword] |= BUNDLEMAX; - ucode[opts->min_size_dword] &= 0xFFFF0000; - ucode[opts->min_size_dword] |= (BUNDLESMALL) ? 0xFFFF : 0xFF80; - - for (i = 0; i < UCODE_SIZE; i++) - cb->u.ucode[i] = cpu_to_le32(ucode[i]); - cb->command = cpu_to_le16(cb_ucode | cb_el); - return; - } - -noloaducode: - cb->command = cpu_to_le16(cb_nop | cb_el); -} - -static inline int e100_exec_cb_wait(struct nic *nic, struct sk_buff *skb, - void (*cb_prepare)(struct nic *, struct cb *, struct sk_buff *)) -{ + if (nic->mac == mac_82559_D101M) + fw_name = FIRMWARE_D101M; + else if (nic->mac == mac_82559_D101S) + fw_name = FIRMWARE_D101S; + else if (nic->mac == mac_82551_F || nic->mac == mac_82551_10) + fw_name = FIRMWARE_D102E; + else /* No ucode on other devices */ + return NULL; + + err = request_firmware(&fw, fw_name, &nic->pdev->dev); + if (err) { + DPRINTK(PROBE, ERR, "Failed to load firmware \"%s\": %d\n", + fw_name, err); + return ERR_PTR(err); + } + /* Firmware should be precisely UCODE_SIZE (words) plus three bytes + indicating the offsets for BUNDLESMALL, BUNDLEMAX, INTDELAY */ + if (fw->size != UCODE_SIZE * 4 + 3) { + DPRINTK(PROBE, ERR, "Firmware \"%s\" has wrong size %zu\n", + fw_name, fw->size); + release_firmware(fw); + return ERR_PTR(-EINVAL); + } + + /* Read timer, bundle and min_size from end of firmware blob */ + timer = fw->data[UCODE_SIZE * 4]; + bundle = fw->data[UCODE_SIZE * 4 + 1]; + min_size = fw->data[UCODE_SIZE * 4 + 2]; + + if (timer >= UCODE_SIZE || bundle >= UCODE_SIZE || + min_size >= UCODE_SIZE) { + DPRINTK(PROBE, ERR, + "\"%s\" has bogus offset values (0x%x,0x%x,0x%x)\n", + fw_name, timer, bundle, min_size); + release_firmware(fw); + return ERR_PTR(-EINVAL); + } + /* OK, firmware is validated and ready to use... */ + return fw; +} + +static void e100_setup_ucode(struct nic *nic, struct cb *cb, + struct sk_buff *skb) +{ + const struct firmware *fw = (void *)skb; + u8 timer, bundle, min_size; + + /* It's not a real skb; we just abused the fact that e100_exec_cb + will pass it through to here... */ + cb->skb = NULL; + + /* firmware is stored as little endian already */ + memcpy(cb->u.ucode, fw->data, UCODE_SIZE * 4); + + /* Read timer, bundle and min_size from end of firmware blob */ + timer = fw->data[UCODE_SIZE * 4]; + bundle = fw->data[UCODE_SIZE * 4 + 1]; + min_size = fw->data[UCODE_SIZE * 4 + 2]; + + /* Insert user-tunable settings in cb->u.ucode */ + cb->u.ucode[timer] &= cpu_to_le32(0xFFFF0000); + cb->u.ucode[timer] |= cpu_to_le32(INTDELAY); + cb->u.ucode[bundle] &= cpu_to_le32(0xFFFF0000); + cb->u.ucode[bundle] |= cpu_to_le32(BUNDLEMAX); + cb->u.ucode[min_size] &= cpu_to_le32(0xFFFF0000); + cb->u.ucode[min_size] |= cpu_to_le32((BUNDLESMALL) ? 0xFFFF : 0xFF80); + + cb->command = cpu_to_le16(cb_ucode | cb_el); +} + +static inline int e100_load_ucode_wait(struct nic *nic) +{ + const struct firmware *fw; int err = 0, counter = 50; struct cb *cb = nic->cb_to_clean; - if ((err = e100_exec_cb(nic, NULL, e100_setup_ucode))) + fw = e100_request_firmware(nic); + /* If it's NULL, then no ucode is required */ + if (!fw || IS_ERR(fw)) + return PTR_ERR(fw); + + if ((err = e100_exec_cb(nic, (void *)fw, e100_setup_ucode))) DPRINTK(PROBE,ERR, "ucode cmd failed with error %d\n", err); /* must restart cuc */ @@ -1435,7 +1330,7 @@ static int e100_hw_init(struct nic *nic) return err; if ((err = e100_exec_cmd(nic, ruc_load_base, 0))) return err; - if ((err = e100_exec_cb_wait(nic, NULL, e100_setup_ucode))) + if ((err = e100_load_ucode_wait(nic))) return err; if ((err = e100_exec_cb(nic, NULL, e100_configure))) return err; diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index d4639facd1bd..91817d0afcaf 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -4807,7 +4807,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, } } - err = pci_request_selected_regions(pdev, + err = pci_request_selected_regions_exclusive(pdev, pci_select_bars(pdev, IORESOURCE_MEM), e1000e_driver_name); if (err) diff --git a/drivers/net/ehea/ehea_phyp.c b/drivers/net/ehea/ehea_phyp.c index 2a33a613d9e6..8fe9dcaa7538 100644 --- a/drivers/net/ehea/ehea_phyp.c +++ b/drivers/net/ehea/ehea_phyp.c @@ -214,7 +214,7 @@ u64 ehea_h_alloc_resource_qp(const u64 adapter_handle, u64 *qp_handle, struct h_epas *h_epas) { u64 hret; - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; u64 allocate_controls = EHEA_BMASK_SET(H_ALL_RES_QP_EQPO, init_attr->low_lat_rq1 ? 1 : 0) @@ -312,7 +312,7 @@ u64 ehea_h_alloc_resource_cq(const u64 adapter_handle, u64 *cq_handle, struct h_epas *epas) { u64 hret; - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; hret = ehea_plpar_hcall9(H_ALLOC_HEA_RESOURCE, outs, @@ -374,7 +374,7 @@ u64 ehea_h_alloc_resource_eq(const u64 adapter_handle, struct ehea_eq_attr *eq_attr, u64 *eq_handle) { u64 hret, allocate_controls; - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; /* resource type */ allocate_controls = @@ -407,7 +407,7 @@ u64 ehea_h_modify_ehea_qp(const u64 adapter_handle, const u8 cat, u16 *out_swr, u16 *out_rwr) { u64 hret; - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; hret = ehea_plpar_hcall9(H_MODIFY_HEA_QP, outs, @@ -449,7 +449,7 @@ u64 ehea_h_register_smr(const u64 adapter_handle, const u64 orig_mr_handle, struct ehea_mr *mr) { u64 hret; - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; hret = ehea_plpar_hcall9(H_REGISTER_SMR, outs, @@ -468,7 +468,7 @@ u64 ehea_h_register_smr(const u64 adapter_handle, const u64 orig_mr_handle, u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle) { - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; return ehea_plpar_hcall9(H_DISABLE_AND_GET_HEA, outs, @@ -493,7 +493,7 @@ u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr, const u32 pd, u64 *mr_handle, u32 *lkey) { u64 hret; - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; hret = ehea_plpar_hcall9(H_ALLOC_HEA_RESOURCE, outs, @@ -564,7 +564,7 @@ u64 ehea_h_modify_ehea_port(const u64 adapter_handle, const u16 port_num, const u8 cb_cat, const u64 select_mask, void *cb_addr) { - u64 outs[PLPAR_HCALL9_BUFSIZE]; + unsigned long outs[PLPAR_HCALL9_BUFSIZE]; u64 port_info; u64 arr_index = 0; u64 cb_logaddr = virt_to_abs(cb_addr); diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index cefe1d98f93e..fc6cc038c7b8 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -1531,6 +1531,17 @@ static int enc28j60_chipset_init(struct net_device *dev) return enc28j60_hw_init(priv); } +static const struct net_device_ops enc28j60_netdev_ops = { + .ndo_open = enc28j60_net_open, + .ndo_stop = enc28j60_net_close, + .ndo_start_xmit = enc28j60_send_packet, + .ndo_set_multicast_list = enc28j60_set_multicast_list, + .ndo_set_mac_address = enc28j60_set_mac_address, + .ndo_tx_timeout = enc28j60_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit enc28j60_probe(struct spi_device *spi) { struct net_device *dev; @@ -1585,12 +1596,7 @@ static int __devinit enc28j60_probe(struct spi_device *spi) dev->if_port = IF_PORT_10BASET; dev->irq = spi->irq; - dev->open = enc28j60_net_open; - dev->stop = enc28j60_net_close; - dev->hard_start_xmit = enc28j60_send_packet; - dev->set_multicast_list = &enc28j60_set_multicast_list; - dev->set_mac_address = enc28j60_set_mac_address; - dev->tx_timeout = &enc28j60_tx_timeout; + dev->netdev_ops = &enc28j60_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops); diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index f9b37c80dda6..a539bc3163cf 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -308,7 +308,18 @@ static int epic_close(struct net_device *dev); static struct net_device_stats *epic_get_stats(struct net_device *dev); static void set_rx_mode(struct net_device *dev); - +static const struct net_device_ops epic_netdev_ops = { + .ndo_open = epic_open, + .ndo_stop = epic_close, + .ndo_start_xmit = epic_start_xmit, + .ndo_tx_timeout = epic_tx_timeout, + .ndo_get_stats = epic_get_stats, + .ndo_set_multicast_list = set_rx_mode, + .ndo_do_ioctl = netdev_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; static int __devinit epic_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -483,15 +494,9 @@ static int __devinit epic_init_one (struct pci_dev *pdev, dev->if_port = ep->default_port = option; /* The Epic-specific entries in the device structure. */ - dev->open = &epic_open; - dev->hard_start_xmit = &epic_start_xmit; - dev->stop = &epic_close; - dev->get_stats = &epic_get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &netdev_ioctl; + dev->netdev_ops = &epic_netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - dev->tx_timeout = &epic_tx_timeout; netif_napi_add(dev, &ep->napi, epic_poll, 64); ret = register_netdev(dev); diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 31ab1ff623fc..daf7272c3352 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -467,6 +467,18 @@ static void stop_nic_rxtx(void __iomem *ioaddr, long crvalue) } } +static const struct net_device_ops netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_get_stats = get_stats, + .ndo_set_multicast_list = set_rx_mode, + .ndo_do_ioctl = mii_ioctl, + .ndo_tx_timeout = fealnx_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; static int __devinit fealnx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -649,15 +661,8 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, np->mii.force_media = 1; } - /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &mii_ioctl; + dev->netdev_ops = &netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; - dev->tx_timeout = &fealnx_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; err = register_netdev(dev); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index c672ecfc9595..1b8deca8b9f8 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -238,8 +238,8 @@ static int gfar_of_init(struct net_device *dev) goto err_out; } - snprintf(priv->phy_bus_id, BUS_ID_SIZE, PHY_ID_FMT, "0", - fixed_link[0]); + snprintf(priv->phy_bus_id, sizeof(priv->phy_bus_id), + PHY_ID_FMT, "0", fixed_link[0]); } else { phy = of_find_node_by_phandle(*ph); @@ -256,7 +256,7 @@ static int gfar_of_init(struct net_device *dev) of_node_put(mdio); gfar_mdio_bus_name(bus_name, mdio); - snprintf(priv->phy_bus_id, BUS_ID_SIZE, "%s:%02x", + snprintf(priv->phy_bus_id, sizeof(priv->phy_bus_id), "%s:%02x", bus_name, *id); } @@ -1973,6 +1973,8 @@ static void adjust_link(struct net_device *dev) case 1000: tempval = ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII); + + ecntrl &= ~(ECNTRL_R100); break; case 100: case 10: diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index ebe7651fcb86..ad8be7e78290 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -425,6 +425,28 @@ struct net_device * __init hp100_probe(int unit) } #endif /* !MODULE && CONFIG_ISA */ +static const struct net_device_ops hp100_bm_netdev_ops = { + .ndo_open = hp100_open, + .ndo_stop = hp100_close, + .ndo_start_xmit = hp100_start_xmit_bm, + .ndo_get_stats = hp100_get_stats, + .ndo_set_multicast_list = hp100_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static const struct net_device_ops hp100_netdev_ops = { + .ndo_open = hp100_open, + .ndo_stop = hp100_close, + .ndo_start_xmit = hp100_start_xmit, + .ndo_get_stats = hp100_get_stats, + .ndo_set_multicast_list = hp100_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit hp100_probe1(struct net_device *dev, int ioaddr, u_char bus, struct pci_dev *pci_dev) { @@ -657,16 +679,10 @@ static int __devinit hp100_probe1(struct net_device *dev, int ioaddr, lp->virt_memory_size = virt_memory_size; lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ - dev->open = hp100_open; - dev->stop = hp100_close; - if (lp->mode == 1) /* busmaster */ - dev->hard_start_xmit = hp100_start_xmit_bm; + dev->netdev_ops = &hp100_bm_netdev_ops; else - dev->hard_start_xmit = hp100_start_xmit; - - dev->get_stats = hp100_get_stats; - dev->set_multicast_list = &hp100_set_multicast_list; + dev->netdev_ops = &hp100_netdev_ops; /* Ask the card for which IRQ line it is configured */ if (bus == HP100_BUS_PCI) { diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 9bc0f178f24b..ca3bb9f7321b 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -754,7 +754,7 @@ static int ibmveth_set_csum_offload(struct net_device *dev, u32 data, void (*done) (struct net_device *, u32)) { struct ibmveth_adapter *adapter = netdev_priv(dev); - u64 set_attr, clr_attr, ret_attr; + unsigned long set_attr, clr_attr, ret_attr; long ret; int rc1 = 0, rc2 = 0; int restart = 0; @@ -1209,7 +1209,7 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_ long ret; struct net_device *netdev; struct ibmveth_adapter *adapter; - u64 set_attr, ret_attr; + unsigned long set_attr, ret_attr; unsigned char *mac_addr_p; unsigned int *mcastFilterSize_p; diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h index d28186948752..ec76ace66c6b 100644 --- a/drivers/net/ibmveth.h +++ b/drivers/net/ibmveth.h @@ -39,11 +39,11 @@ #define IbmVethMcastRemoveFilter 0x2UL #define IbmVethMcastClearFilterTable 0x3UL -#define IBMVETH_ILLAN_PADDED_PKT_CSUM 0x0000000000002000ULL -#define IBMVETH_ILLAN_TRUNK_PRI_MASK 0x0000000000000F00ULL -#define IBMVETH_ILLAN_IPV6_TCP_CSUM 0x0000000000000004ULL -#define IBMVETH_ILLAN_IPV4_TCP_CSUM 0x0000000000000002ULL -#define IBMVETH_ILLAN_ACTIVE_TRUNK 0x0000000000000001ULL +#define IBMVETH_ILLAN_PADDED_PKT_CSUM 0x0000000000002000UL +#define IBMVETH_ILLAN_TRUNK_PRI_MASK 0x0000000000000F00UL +#define IBMVETH_ILLAN_IPV6_TCP_CSUM 0x0000000000000004UL +#define IBMVETH_ILLAN_IPV4_TCP_CSUM 0x0000000000000002UL +#define IBMVETH_ILLAN_ACTIVE_TRUNK 0x0000000000000001UL /* hcall macros */ #define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \ diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c index 7b6d435a8468..360aa5e35fda 100644 --- a/drivers/net/ipg.c +++ b/drivers/net/ipg.c @@ -2210,6 +2210,19 @@ static void __devexit ipg_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } +static const struct net_device_ops ipg_netdev_ops = { + .ndo_open = ipg_nic_open, + .ndo_stop = ipg_nic_stop, + .ndo_start_xmit = ipg_nic_hard_start_xmit, + .ndo_get_stats = ipg_nic_get_stats, + .ndo_set_multicast_list = ipg_nic_set_multicast_list, + .ndo_do_ioctl = ipg_ioctl, + .ndo_tx_timeout = ipg_tx_timeout, + .ndo_change_mtu = ipg_nic_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit ipg_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -2258,15 +2271,7 @@ static int __devinit ipg_probe(struct pci_dev *pdev, /* Declare IPG NIC functions for Ethernet device methods. */ - dev->open = &ipg_nic_open; - dev->stop = &ipg_nic_stop; - dev->hard_start_xmit = &ipg_nic_hard_start_xmit; - dev->get_stats = &ipg_nic_get_stats; - dev->set_multicast_list = &ipg_nic_set_multicast_list; - dev->do_ioctl = ipg_ioctl; - dev->tx_timeout = ipg_tx_timeout; - dev->change_mtu = &ipg_nic_change_mtu; - + dev->netdev_ops = &ipg_netdev_ops; SET_NETDEV_DEV(dev, &pdev->dev); SET_ETHTOOL_OPS(dev, &ipg_ethtool_ops); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 3c58e67ef1e4..17779f9bffc4 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -109,7 +109,6 @@ static int ali_ircc_net_open(struct net_device *dev); static int ali_ircc_net_close(struct net_device *dev); static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); -static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev); /* SIR function */ static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev); @@ -366,7 +365,6 @@ static int ali_ircc_open(int i, chipio_t *info) dev->open = ali_ircc_net_open; dev->stop = ali_ircc_net_close; dev->do_ioctl = ali_ircc_net_ioctl; - dev->get_stats = ali_ircc_net_get_stats; err = register_netdev(dev); if (err) { @@ -876,7 +874,7 @@ static void ali_ircc_sir_receive(struct ali_ircc_cb *self) * async_unwrap_char will deliver all found frames */ do { - async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, inb(iobase+UART_RX)); /* Make sure we don't stay here too long */ @@ -943,7 +941,7 @@ static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self) netif_wake_queue(self->netdev); } - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; /* Turn on receive interrupts */ outb(UART_IER_RDI, iobase+UART_IER); @@ -1467,7 +1465,7 @@ static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; self->tx_fifo.tail += skb->len; - self->stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); @@ -1661,12 +1659,12 @@ static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self) { IRDA_ERROR("%s(), ********* LSR_FRAME_ABORT *********\n", __func__); - self->stats.tx_errors++; - self->stats.tx_fifo_errors++; + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; } else { - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; } /* Check if we need to change the speed */ @@ -1831,35 +1829,35 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) IRDA_DEBUG(0,"%s(), ************* RX Errors ************ \n", __func__ ); /* Skip frame */ - self->stats.rx_errors++; + self->netdev->stats.rx_errors++; self->rx_buff.data += len; if (status & LSR_FIFO_UR) { - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; IRDA_DEBUG(0,"%s(), ************* FIFO Errors ************ \n", __func__ ); } if (status & LSR_FRAME_ERROR) { - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; IRDA_DEBUG(0,"%s(), ************* FRAME Errors ************ \n", __func__ ); } if (status & LSR_CRC_ERROR) { - self->stats.rx_crc_errors++; + self->netdev->stats.rx_crc_errors++; IRDA_DEBUG(0,"%s(), ************* CRC Errors ************ \n", __func__ ); } if(self->rcvFramesOverflow) { - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; IRDA_DEBUG(0,"%s(), ************* Overran DMA buffer ************ \n", __func__ ); } if(len == 0) { - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; IRDA_DEBUG(0,"%s(), ********** Receive Frame Size = 0 ********* \n", __func__ ); } } @@ -1910,7 +1908,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) IRDA_WARNING("%s(), memory squeeze, " "dropping frame.\n", __func__); - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; return FALSE; } @@ -1924,8 +1922,8 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) /* Move to next frame */ self->rx_buff.data += len; - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); @@ -1994,7 +1992,7 @@ static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - self->stats.tx_bytes += self->tx_buff.len; + self->netdev->stats.tx_bytes += self->tx_buff.len; /* Turn on transmit finished interrupt. Will fire immediately! */ outb(UART_IER_THRI, iobase+UART_IER); @@ -2111,17 +2109,6 @@ static int ali_ircc_is_receiving(struct ali_ircc_cb *self) return status; } -static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev) -{ - struct ali_ircc_cb *self = netdev_priv(dev); - - IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __func__ ); - - IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __func__ ); - - return &self->stats; -} - static int ali_ircc_suspend(struct platform_device *dev, pm_message_t state) { struct ali_ircc_cb *self = platform_get_drvdata(dev); diff --git a/drivers/net/irda/ali-ircc.h b/drivers/net/irda/ali-ircc.h index ed35d99763d5..0c8edb41bd0a 100644 --- a/drivers/net/irda/ali-ircc.h +++ b/drivers/net/irda/ali-ircc.h @@ -191,7 +191,6 @@ struct ali_ircc_cb { struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ struct qos_info qos; /* QoS capabilities for this device */ diff --git a/drivers/net/irda/au1000_ircc.h b/drivers/net/irda/au1000_ircc.h index b4763f24dded..c072c09a8d91 100644 --- a/drivers/net/irda/au1000_ircc.h +++ b/drivers/net/irda/au1000_ircc.h @@ -107,7 +107,6 @@ struct au1k_private { iobuff_t rx_buff; struct net_device *netdev; - struct net_device_stats stats; struct timeval stamp; struct timeval now; diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index 6c4b53ffbcac..75a1d0a86dee 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -53,7 +53,6 @@ static int au1k_irda_hard_xmit(struct sk_buff *, struct net_device *); static int au1k_irda_rx(struct net_device *); static void au1k_irda_interrupt(int, void *); static void au1k_tx_timeout(struct net_device *); -static struct net_device_stats *au1k_irda_stats(struct net_device *); static int au1k_irda_ioctl(struct net_device *, struct ifreq *, int); static int au1k_irda_set_speed(struct net_device *dev, int speed); @@ -213,7 +212,6 @@ static int au1k_irda_net_init(struct net_device *dev) dev->open = au1k_irda_start; dev->hard_start_xmit = au1k_irda_hard_xmit; dev->stop = au1k_irda_stop; - dev->get_stats = au1k_irda_stats; dev->do_ioctl = au1k_irda_ioctl; dev->tx_timeout = au1k_tx_timeout; @@ -832,13 +830,6 @@ au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) return ret; } - -static struct net_device_stats *au1k_irda_stats(struct net_device *dev) -{ - struct au1k_private *aup = netdev_priv(dev); - return &aup->stats; -} - MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>"); MODULE_DESCRIPTION("Au1000 IrDA Device Driver"); diff --git a/drivers/net/irda/donauboe.h b/drivers/net/irda/donauboe.h index 1e67720f1066..0dbd1932b72f 100644 --- a/drivers/net/irda/donauboe.h +++ b/drivers/net/irda/donauboe.h @@ -308,7 +308,6 @@ struct OboeRing struct toshoboe_cb { struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct net_device_stats stats; struct tty_driver ttydev; struct irlap_cb *irlap; /* The link layer we are binded to */ diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 205e4e825a97..29118f58a141 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -122,7 +122,6 @@ static int irda_usb_net_open(struct net_device *dev); static int irda_usb_net_close(struct net_device *dev); static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void irda_usb_net_timeout(struct net_device *dev); -static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev); /************************ TRANSMIT ROUTINES ************************/ /* @@ -525,13 +524,13 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) /* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */ if ((res = usb_submit_urb(urb, GFP_ATOMIC))) { IRDA_WARNING("%s(), failed Tx URB\n", __func__); - self->stats.tx_errors++; + netdev->stats.tx_errors++; /* Let USB recover : We will catch that in the watchdog */ /*netif_start_queue(netdev);*/ } else { /* Increment packet stats */ - self->stats.tx_packets++; - self->stats.tx_bytes += skb->len; + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; netdev->trans_start = jiffies; } @@ -677,7 +676,7 @@ static void irda_usb_net_timeout(struct net_device *netdev) IRDA_DEBUG(0, "%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags); /* Increase error count */ - self->stats.tx_errors++; + netdev->stats.tx_errors++; #ifdef IU_BUG_KICK_TIMEOUT /* Can't be a bad idea to reset the speed ;-) - Jean II */ @@ -826,7 +825,7 @@ static void irda_usb_receive(struct urb *urb) if (urb->status != 0) { switch (urb->status) { case -EILSEQ: - self->stats.rx_crc_errors++; + self->netdev->stats.rx_crc_errors++; /* Also precursor to a hot-unplug on UHCI. */ /* Fallthrough... */ case -ECONNRESET: @@ -839,7 +838,7 @@ static void irda_usb_receive(struct urb *urb) case -ETIME: /* Usually precursor to a hot-unplug on OHCI. */ default: - self->stats.rx_errors++; + self->netdev->stats.rx_errors++; IRDA_DEBUG(0, "%s(), RX status %d, transfer_flags 0x%04X \n", __func__, urb->status, urb->transfer_flags); break; } @@ -890,7 +889,7 @@ static void irda_usb_receive(struct urb *urb) IRDA_SKB_MAX_MTU); if (!newskb) { - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; /* We could deliver the current skb, but this would stall * the Rx path. Better drop the packet... Jean II */ goto done; @@ -927,8 +926,8 @@ static void irda_usb_receive(struct urb *urb) netif_rx(dataskb); /* Keep stats up to date */ - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; done: /* Note : at this point, the URB we've just received (urb) @@ -1342,14 +1341,6 @@ static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } /*------------------------------------------------------------------*/ -/* - * Get device stats (for /proc/net/dev and ifconfig) - */ -static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev) -{ - struct irda_usb_cb *self = netdev_priv(dev); - return &self->stats; -} /********************* IRDA CONFIG SUBROUTINES *********************/ /* @@ -1428,7 +1419,6 @@ static inline int irda_usb_open(struct irda_usb_cb *self) netdev->watchdog_timeo = 250*HZ/1000; /* 250 ms > USB timeout */ netdev->open = irda_usb_net_open; netdev->stop = irda_usb_net_close; - netdev->get_stats = irda_usb_net_get_stats; netdev->do_ioctl = irda_usb_net_ioctl; return register_netdev(netdev); diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h index a0ca9c1fe196..ac0443d52e50 100644 --- a/drivers/net/irda/irda-usb.h +++ b/drivers/net/irda/irda-usb.h @@ -152,7 +152,6 @@ struct irda_usb_cb { struct urb *speed_urb; /* URB used to send speed commands */ struct net_device *netdev; /* Yes! we are some kind of netdev. */ - struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ struct qos_info qos; char *speed_buff; /* Buffer for speed changes */ diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c index c747c874d44d..b4a61717254a 100644 --- a/drivers/net/irda/kingsun-sir.c +++ b/drivers/net/irda/kingsun-sir.c @@ -105,7 +105,7 @@ struct kingsun_cb { struct usb_device *usbdev; /* init: probe_irda */ struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ - struct net_device_stats stats; /* network statistics */ + struct qos_info qos; __u8 *in_buf; /* receive buffer */ @@ -186,12 +186,12 @@ static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev) case -EPIPE: break; default: - kingsun->stats.tx_errors++; + netdev->stats.tx_errors++; netif_start_queue(netdev); } } else { - kingsun->stats.tx_packets++; - kingsun->stats.tx_bytes += skb->len; + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; } dev_kfree_skb(skb); @@ -232,7 +232,7 @@ static void kingsun_rcv_irq(struct urb *urb) if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { for (i = 1; i <= bytes[0]; i++) { async_unwrap_char(kingsun->netdev, - &kingsun->stats, + &kingsun->netdev->stats, &kingsun->rx_buff, bytes[i]); } do_gettimeofday(&kingsun->rx_time); @@ -418,15 +418,6 @@ static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, return ret; } -/* - * Get device stats (for /proc/net/dev and ifconfig) - */ -static struct net_device_stats * -kingsun_net_get_stats(struct net_device *netdev) -{ - struct kingsun_cb *kingsun = netdev_priv(netdev); - return &kingsun->stats; -} /* * This routine is called by the USB subsystem for each new device @@ -532,7 +523,6 @@ static int kingsun_probe(struct usb_interface *intf, net->hard_start_xmit = kingsun_hard_xmit; net->open = kingsun_net_open; net->stop = kingsun_net_close; - net->get_stats = kingsun_net_get_stats; net->do_ioctl = kingsun_net_ioctl; ret = register_netdev(net); diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c index 600d96f9cdb7..55322fb92cf1 100644 --- a/drivers/net/irda/ks959-sir.c +++ b/drivers/net/irda/ks959-sir.c @@ -174,7 +174,7 @@ struct ks959_cb { struct usb_device *usbdev; /* init: probe_irda */ struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ - struct net_device_stats stats; /* network statistics */ + struct qos_info qos; struct usb_ctrlrequest *tx_setuprequest; @@ -366,7 +366,7 @@ static void ks959_send_irq(struct urb *urb) case -EPIPE: break; default: - kingsun->stats.tx_errors++; + netdev->stats.tx_errors++; netif_start_queue(netdev); } } @@ -416,12 +416,12 @@ static int ks959_hard_xmit(struct sk_buff *skb, struct net_device *netdev) case -EPIPE: break; default: - kingsun->stats.tx_errors++; + netdev->stats.tx_errors++; netif_start_queue(netdev); } } else { - kingsun->stats.tx_packets++; - kingsun->stats.tx_bytes += skb->len; + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; } @@ -469,7 +469,7 @@ static void ks959_rcv_irq(struct urb *urb) */ if (kingsun->rx_variable_xormask != 0) { async_unwrap_char(kingsun->netdev, - &kingsun->stats, + &kingsun->netdev->stats, &kingsun->rx_unwrap_buff, bytes[i]); } @@ -669,15 +669,6 @@ static int ks959_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) } /* - * Get device stats (for /proc/net/dev and ifconfig) - */ -static struct net_device_stats *ks959_net_get_stats(struct net_device *netdev) -{ - struct ks959_cb *kingsun = netdev_priv(netdev); - return &kingsun->stats; -} - -/* * This routine is called by the USB subsystem for each new device * in the system. We need to check if the device is ours, and in * this case start handling it. @@ -792,7 +783,6 @@ static int ks959_probe(struct usb_interface *intf, net->hard_start_xmit = ks959_hard_xmit; net->open = ks959_net_open; net->stop = ks959_net_close; - net->get_stats = ks959_net_get_stats; net->do_ioctl = ks959_net_ioctl; ret = register_netdev(net); diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c index 0e7f89337b25..5b327b09acd8 100644 --- a/drivers/net/irda/ksdazzle-sir.c +++ b/drivers/net/irda/ksdazzle-sir.c @@ -140,7 +140,7 @@ struct ksdazzle_cb { struct usb_device *usbdev; /* init: probe_irda */ struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ - struct net_device_stats stats; /* network statistics */ + struct qos_info qos; struct urb *tx_urb; @@ -278,7 +278,7 @@ static void ksdazzle_send_irq(struct urb *urb) case -EPIPE: break; default: - kingsun->stats.tx_errors++; + netdev->stats.tx_errors++; netif_start_queue(netdev); } } @@ -329,12 +329,12 @@ static int ksdazzle_hard_xmit(struct sk_buff *skb, struct net_device *netdev) case -EPIPE: break; default: - kingsun->stats.tx_errors++; + netdev->stats.tx_errors++; netif_start_queue(netdev); } } else { - kingsun->stats.tx_packets++; - kingsun->stats.tx_bytes += skb->len; + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; } @@ -348,9 +348,10 @@ static int ksdazzle_hard_xmit(struct sk_buff *skb, struct net_device *netdev) static void ksdazzle_rcv_irq(struct urb *urb) { struct ksdazzle_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; /* in process of stopping, just drop data */ - if (!netif_running(kingsun->netdev)) { + if (!netif_running(netdev)) { kingsun->receiving = 0; return; } @@ -368,7 +369,7 @@ static void ksdazzle_rcv_irq(struct urb *urb) unsigned int i; for (i = 0; i < urb->actual_length; i++) { - async_unwrap_char(kingsun->netdev, &kingsun->stats, + async_unwrap_char(netdev, &netdev->stats, &kingsun->rx_unwrap_buff, bytes[i]); } kingsun->receiving = @@ -562,16 +563,6 @@ static int ksdazzle_net_ioctl(struct net_device *netdev, struct ifreq *rq, } /* - * Get device stats (for /proc/net/dev and ifconfig) - */ -static struct net_device_stats *ksdazzle_net_get_stats(struct net_device - *netdev) -{ - struct ksdazzle_cb *kingsun = netdev_priv(netdev); - return &kingsun->stats; -} - -/* * This routine is called by the USB subsystem for each new device * in the system. We need to check if the device is ours, and in * this case start handling it. @@ -696,7 +687,6 @@ static int ksdazzle_probe(struct usb_interface *intf, net->hard_start_xmit = ksdazzle_hard_xmit; net->open = ksdazzle_net_open; net->stop = ksdazzle_net_close; - net->get_stats = ksdazzle_net_get_stats; net->do_ioctl = ksdazzle_net_ioctl; ret = register_netdev(net); diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index 904c9610c0dd..7eafdca19f34 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -403,8 +403,8 @@ static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) if(unlikely(new_len <= 0)) { IRDA_ERROR("%s short frame length %d\n", mcs->netdev->name, new_len); - ++mcs->stats.rx_errors; - ++mcs->stats.rx_length_errors; + ++mcs->netdev->stats.rx_errors; + ++mcs->netdev->stats.rx_length_errors; return; } fcs = 0; @@ -413,14 +413,14 @@ static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) if(fcs != GOOD_FCS) { IRDA_ERROR("crc error calc 0x%x len %d\n", fcs, new_len); - mcs->stats.rx_errors++; - mcs->stats.rx_crc_errors++; + mcs->netdev->stats.rx_errors++; + mcs->netdev->stats.rx_crc_errors++; return; } skb = dev_alloc_skb(new_len + 1); if(unlikely(!skb)) { - ++mcs->stats.rx_dropped; + ++mcs->netdev->stats.rx_dropped; return; } @@ -433,8 +433,8 @@ static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) netif_rx(skb); - mcs->stats.rx_packets++; - mcs->stats.rx_bytes += new_len; + mcs->netdev->stats.rx_packets++; + mcs->netdev->stats.rx_bytes += new_len; return; } @@ -458,22 +458,22 @@ static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) if(unlikely(new_len <= 0)) { IRDA_ERROR("%s short frame length %d\n", mcs->netdev->name, new_len); - ++mcs->stats.rx_errors; - ++mcs->stats.rx_length_errors; + ++mcs->netdev->stats.rx_errors; + ++mcs->netdev->stats.rx_length_errors; return; } fcs = ~(crc32_le(~0, buf, new_len)); if(fcs != get_unaligned_le32(buf + new_len)) { IRDA_ERROR("crc error calc 0x%x len %d\n", fcs, new_len); - mcs->stats.rx_errors++; - mcs->stats.rx_crc_errors++; + mcs->netdev->stats.rx_errors++; + mcs->netdev->stats.rx_crc_errors++; return; } skb = dev_alloc_skb(new_len + 1); if(unlikely(!skb)) { - ++mcs->stats.rx_dropped; + ++mcs->netdev->stats.rx_dropped; return; } @@ -486,8 +486,8 @@ static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) netif_rx(skb); - mcs->stats.rx_packets++; - mcs->stats.rx_bytes += new_len; + mcs->netdev->stats.rx_packets++; + mcs->netdev->stats.rx_bytes += new_len; return; } @@ -756,14 +756,6 @@ static int mcs_net_open(struct net_device *netdev) return ret; } - -/* Get device stats for /proc/net/dev and ifconfig */ -static struct net_device_stats *mcs_net_get_stats(struct net_device *netdev) -{ - struct mcs_cb *mcs = netdev_priv(netdev); - return &mcs->stats; -} - /* Receive callback function. */ static void mcs_receive_irq(struct urb *urb) { @@ -786,14 +778,14 @@ static void mcs_receive_irq(struct urb *urb) */ /* SIR speed */ if(mcs->speed < 576000) { - async_unwrap_char(mcs->netdev, &mcs->stats, + async_unwrap_char(mcs->netdev, &mcs->netdev->stats, &mcs->rx_buff, 0xc0); for (i = 0; i < urb->actual_length; i++) - async_unwrap_char(mcs->netdev, &mcs->stats, + async_unwrap_char(mcs->netdev, &mcs->netdev->stats, &mcs->rx_buff, bytes[i]); - async_unwrap_char(mcs->netdev, &mcs->stats, + async_unwrap_char(mcs->netdev, &mcs->netdev->stats, &mcs->rx_buff, 0xc1); } /* MIR speed */ @@ -868,12 +860,12 @@ static int mcs_hard_xmit(struct sk_buff *skb, struct net_device *ndev) case -EPIPE: break; default: - mcs->stats.tx_errors++; + mcs->netdev->stats.tx_errors++; netif_start_queue(ndev); } } else { - mcs->stats.tx_packets++; - mcs->stats.tx_bytes += skb->len; + mcs->netdev->stats.tx_packets++; + mcs->netdev->stats.tx_bytes += skb->len; } dev_kfree_skb(skb); @@ -931,7 +923,6 @@ static int mcs_probe(struct usb_interface *intf, ndev->hard_start_xmit = mcs_hard_xmit; ndev->open = mcs_net_open; ndev->stop = mcs_net_close; - ndev->get_stats = mcs_net_get_stats; ndev->do_ioctl = mcs_net_ioctl; if (!intf->cur_altsetting) diff --git a/drivers/net/irda/mcs7780.h b/drivers/net/irda/mcs7780.h index b18148cee638..6bdc621e67c6 100644 --- a/drivers/net/irda/mcs7780.h +++ b/drivers/net/irda/mcs7780.h @@ -104,7 +104,6 @@ struct mcs_cb { struct usb_device *usbdev; /* init: probe_irda */ struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ - struct net_device_stats stats; /* network statistics */ struct qos_info qos; unsigned int speed; /* Current speed */ unsigned int new_speed; /* new speed */ @@ -154,7 +153,6 @@ static int mcs_speed_change(struct mcs_cb *mcs); static int mcs_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd); static int mcs_net_close(struct net_device *netdev); static int mcs_net_open(struct net_device *netdev); -static struct net_device_stats *mcs_net_get_stats(struct net_device *netdev); static void mcs_receive_irq(struct urb *urb); static void mcs_send_irq(struct urb *urb); diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 2c6bf2d11bb1..61e509cb712a 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -185,7 +185,6 @@ static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); static int nsc_ircc_net_open(struct net_device *dev); static int nsc_ircc_net_close(struct net_device *dev); static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev); /* Globals */ static int pnp_registered; @@ -446,7 +445,6 @@ static int __init nsc_ircc_open(chipio_t *info) dev->open = nsc_ircc_net_open; dev->stop = nsc_ircc_net_close; dev->do_ioctl = nsc_ircc_net_ioctl; - dev->get_stats = nsc_ircc_net_get_stats; err = register_netdev(dev); if (err) { @@ -1401,7 +1399,7 @@ static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - self->stats.tx_bytes += self->tx_buff.len; + dev->stats.tx_bytes += self->tx_buff.len; /* Add interrupt on tx low level (will fire immediately) */ switch_bank(iobase, BANK0); @@ -1473,7 +1471,7 @@ static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; self->tx_fifo.tail += skb->len; - self->stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); @@ -1652,13 +1650,13 @@ static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) /* Check for underrrun! */ if (inb(iobase+ASCR) & ASCR_TXUR) { - self->stats.tx_errors++; - self->stats.tx_fifo_errors++; + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; /* Clear bit, by writing 1 into it */ outb(ASCR_TXUR, iobase+ASCR); } else { - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; } /* Finished with this frame, so prepare for next */ @@ -1793,28 +1791,28 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) if (status & FRM_ST_ERR_MSK) { if (status & FRM_ST_LOST_FR) { /* Add number of lost frames to stats */ - self->stats.rx_errors += len; + self->netdev->stats.rx_errors += len; } else { /* Skip frame */ - self->stats.rx_errors++; + self->netdev->stats.rx_errors++; self->rx_buff.data += len; if (status & FRM_ST_MAX_LEN) - self->stats.rx_length_errors++; + self->netdev->stats.rx_length_errors++; if (status & FRM_ST_PHY_ERR) - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; if (status & FRM_ST_BAD_CRC) - self->stats.rx_crc_errors++; + self->netdev->stats.rx_crc_errors++; } /* The errors below can be reported in both cases */ if (status & FRM_ST_OVR1) - self->stats.rx_fifo_errors++; + self->netdev->stats.rx_fifo_errors++; if (status & FRM_ST_OVR2) - self->stats.rx_fifo_errors++; + self->netdev->stats.rx_fifo_errors++; } else { /* * First we must make sure that the frame we @@ -1863,7 +1861,7 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) IRDA_WARNING("%s(), memory squeeze, " "dropping frame.\n", __func__); - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; /* Restore bank register */ outb(bank, iobase+BSR); @@ -1889,8 +1887,8 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) /* Move to next frame */ self->rx_buff.data += len; - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); @@ -1920,8 +1918,8 @@ static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) /* Receive all characters in Rx FIFO */ do { byte = inb(iobase+RXD); - async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, - byte); + async_unwrap_char(self->netdev, &self->netdev->stats, + &self->rx_buff, byte); } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */ } @@ -1952,7 +1950,7 @@ static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) self->ier = IER_TXLDL_IE; else { - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; netif_wake_queue(self->netdev); self->ier = IER_TXEMP_IE; } @@ -2307,13 +2305,6 @@ static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return ret; } -static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev) -{ - struct nsc_ircc_cb *self = netdev_priv(dev); - - return &self->stats; -} - static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state) { struct nsc_ircc_cb *self = platform_get_drvdata(dev); diff --git a/drivers/net/irda/nsc-ircc.h b/drivers/net/irda/nsc-ircc.h index 71cd3c5a0762..7ba7738759b9 100644 --- a/drivers/net/irda/nsc-ircc.h +++ b/drivers/net/irda/nsc-ircc.h @@ -251,7 +251,6 @@ struct nsc_ircc_cb { struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ struct qos_info qos; /* QoS capabilities for this device */ diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 004a9aab3a50..31794c2363ec 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -108,7 +108,6 @@ struct pxa_irda { int txdma; int rxdma; - struct net_device_stats stats; struct irlap_cb *irlap; struct qos_info qos; @@ -258,14 +257,15 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) data = STRBR; if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) { printk(KERN_DEBUG "pxa_ir: sir receiving error\n"); - si->stats.rx_errors++; + dev->stats.rx_errors++; if (lsr & LSR_FE) - si->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; if (lsr & LSR_OE) - si->stats.rx_fifo_errors++; + dev->stats.rx_fifo_errors++; } else { - si->stats.rx_bytes++; - async_unwrap_char(dev, &si->stats, &si->rx_buff, data); + dev->stats.rx_bytes++; + async_unwrap_char(dev, &dev->stats, + &si->rx_buff, data); } lsr = STLSR; } @@ -277,8 +277,8 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) case 0x0C: /* Character Timeout Indication */ do { - si->stats.rx_bytes++; - async_unwrap_char(dev, &si->stats, &si->rx_buff, STRBR); + dev->stats.rx_bytes++; + async_unwrap_char(dev, &dev->stats, &si->rx_buff, STRBR); } while (STLSR & LSR_DR); si->last_oscr = OSCR; break; @@ -290,9 +290,8 @@ static irqreturn_t pxa_irda_sir_irq(int irq, void *dev_id) } if (si->tx_buff.len == 0) { - si->stats.tx_packets++; - si->stats.tx_bytes += si->tx_buff.data - - si->tx_buff.head; + dev->stats.tx_packets++; + dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; /* We need to ensure that the transmitter has finished. */ while ((STLSR & LSR_TEMT) == 0) @@ -343,10 +342,10 @@ static void pxa_irda_fir_dma_tx_irq(int channel, void *data) DCSR(channel) = dcsr & ~DCSR_RUN; if (dcsr & DCSR_ENDINTR) { - si->stats.tx_packets++; - si->stats.tx_bytes += si->dma_tx_buff_len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += si->dma_tx_buff_len; } else { - si->stats.tx_errors++; + dev->stats.tx_errors++; } while (ICSR1 & ICSR1_TBY) @@ -392,14 +391,14 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in data = ICDR; if (stat & (ICSR1_CRE | ICSR1_ROR)) { - si->stats.rx_errors++; + dev->stats.rx_errors++; if (stat & ICSR1_CRE) { printk(KERN_DEBUG "pxa_ir: fir receive CRC error\n"); - si->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; } if (stat & ICSR1_ROR) { printk(KERN_DEBUG "pxa_ir: fir receive overrun\n"); - si->stats.rx_over_errors++; + dev->stats.rx_over_errors++; } } else { si->dma_rx_buff[len++] = data; @@ -415,14 +414,14 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in if (icsr0 & ICSR0_FRE) { printk(KERN_ERR "pxa_ir: dropping erroneous frame\n"); - si->stats.rx_dropped++; + dev->stats.rx_dropped++; return; } skb = alloc_skb(len+1,GFP_ATOMIC); if (!skb) { printk(KERN_ERR "pxa_ir: fir out of memory for receive skb\n"); - si->stats.rx_dropped++; + dev->stats.rx_dropped++; return; } @@ -437,8 +436,8 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, in skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); - si->stats.rx_packets++; - si->stats.rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; } } @@ -457,10 +456,10 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) if (icsr0 & (ICSR0_FRE | ICSR0_RAB)) { if (icsr0 & ICSR0_FRE) { printk(KERN_DEBUG "pxa_ir: fir receive frame error\n"); - si->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; } else { printk(KERN_DEBUG "pxa_ir: fir receive abort\n"); - si->stats.rx_errors++; + dev->stats.rx_errors++; } ICSR0 = icsr0 & (ICSR0_FRE | ICSR0_RAB); } @@ -589,12 +588,6 @@ static int pxa_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) return ret; } -static struct net_device_stats *pxa_irda_stats(struct net_device *dev) -{ - struct pxa_irda *si = netdev_priv(dev); - return &si->stats; -} - static void pxa_irda_startup(struct pxa_irda *si) { /* Disable STUART interrupts */ @@ -857,7 +850,6 @@ static int pxa_irda_probe(struct platform_device *pdev) dev->open = pxa_irda_start; dev->stop = pxa_irda_stop; dev->do_ioctl = pxa_irda_ioctl; - dev->get_stats = pxa_irda_stats; irda_init_max_qos_capabilies(&si->qos); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index d302bcf4c148..7a2b003954ca 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -60,7 +60,6 @@ struct sa1100_irda { dma_regs_t *txdma; dma_regs_t *rxdma; - struct net_device_stats stats; struct device *dev; struct irda_platform_data *pdata; struct irlap_cb *irlap; @@ -375,13 +374,13 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev) data = Ser2UTDR; if (stat & (UTSR1_FRE | UTSR1_ROR)) { - si->stats.rx_errors++; + dev->stats.rx_errors++; if (stat & UTSR1_FRE) - si->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; if (stat & UTSR1_ROR) - si->stats.rx_fifo_errors++; + dev->stats.rx_fifo_errors++; } else - async_unwrap_char(dev, &si->stats, &si->rx_buff, data); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, data); status = Ser2UTSR0; } @@ -396,9 +395,9 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev) * There are at least 4 bytes in the FIFO. Read 3 bytes * and leave the rest to the block below. */ - async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR); - async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR); - async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); } if (status & (UTSR0_RFS | UTSR0_RID)) { @@ -406,7 +405,7 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev) * Fifo contains more than 1 character. */ do { - async_unwrap_char(dev, &si->stats, &si->rx_buff, + async_unwrap_char(dev, &dev->stats, &si->rx_buff, Ser2UTDR); } while (Ser2UTSR1 & UTSR1_RNE); @@ -422,8 +421,8 @@ static void sa1100_irda_hpsir_irq(struct net_device *dev) } while (Ser2UTSR1 & UTSR1_TNF && si->tx_buff.len); if (si->tx_buff.len == 0) { - si->stats.tx_packets++; - si->stats.tx_bytes += si->tx_buff.data - + dev->stats.tx_packets++; + dev->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head; /* @@ -482,11 +481,11 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev data = Ser2HSDR; if (stat & (HSSR1_CRE | HSSR1_ROR)) { - si->stats.rx_errors++; + dev->stats.rx_errors++; if (stat & HSSR1_CRE) - si->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; if (stat & HSSR1_ROR) - si->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; } else skb->data[len++] = data; @@ -505,8 +504,8 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev skb->dev = dev; skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); - si->stats.rx_packets++; - si->stats.rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; /* * Before we pass the buffer up, allocate a new one. @@ -545,10 +544,10 @@ static void sa1100_irda_fir_irq(struct net_device *dev) * from the fifo. */ if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) { - si->stats.rx_errors++; + dev->stats.rx_errors++; if (Ser2HSSR0 & HSSR0_FRE) - si->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; /* * Clear out the DMA... @@ -633,8 +632,8 @@ static void sa1100_irda_txdma_irq(void *id) */ if (skb) { dma_unmap_single(si->dev, si->txbuf_dma, skb->len, DMA_TO_DEVICE); - si->stats.tx_packets ++; - si->stats.tx_bytes += skb->len; + dev->stats.tx_packets ++; + dev->stats.tx_bytes += skb->len; dev_kfree_skb_irq(skb); } @@ -762,12 +761,6 @@ sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) return ret; } -static struct net_device_stats *sa1100_irda_stats(struct net_device *dev) -{ - struct sa1100_irda *si = netdev_priv(dev); - return &si->stats; -} - static int sa1100_irda_start(struct net_device *dev) { struct sa1100_irda *si = netdev_priv(dev); @@ -924,7 +917,6 @@ static int sa1100_irda_probe(struct platform_device *pdev) dev->open = sa1100_irda_start; dev->stop = sa1100_irda_stop; dev->do_ioctl = sa1100_irda_ioctl; - dev->get_stats = sa1100_irda_stats; dev->irq = IRQ_Ser2ICP; irda_init_max_qos_capabilies(&si->qos); diff --git a/drivers/net/irda/sir-dev.h b/drivers/net/irda/sir-dev.h index 2a57bc67ce35..6d5b1e2b1289 100644 --- a/drivers/net/irda/sir-dev.h +++ b/drivers/net/irda/sir-dev.h @@ -160,7 +160,6 @@ static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode) struct sir_dev { struct net_device *netdev; - struct net_device_stats stats; struct irlap_cb *irlap; diff --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c index ceef040aa76d..5b5862499def 100644 --- a/drivers/net/irda/sir_dev.c +++ b/drivers/net/irda/sir_dev.c @@ -455,8 +455,8 @@ void sirdev_write_complete(struct sir_dev *dev) if ((skb=dev->tx_skb) != NULL) { dev->tx_skb = NULL; dev_kfree_skb_any(skb); - dev->stats.tx_errors++; - dev->stats.tx_dropped++; + dev->netdev->stats.tx_errors++; + dev->netdev->stats.tx_dropped++; } dev->tx_buff.len = 0; } @@ -493,8 +493,8 @@ void sirdev_write_complete(struct sir_dev *dev) if ((skb=dev->tx_skb) != NULL) { dev->tx_skb = NULL; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; + dev->netdev->stats.tx_packets++; + dev->netdev->stats.tx_bytes += skb->len; dev_kfree_skb_any(skb); } @@ -548,7 +548,7 @@ int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) * just update stats and set media busy */ irda_device_set_media_busy(dev->netdev, TRUE); - dev->stats.rx_dropped++; + dev->netdev->stats.rx_dropped++; IRDA_DEBUG(0, "%s; rx-drop: %zd\n", __func__, count); return 0; } @@ -557,7 +557,7 @@ int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) if (likely(atomic_read(&dev->enable_rx))) { while (count--) /* Unwrap and destuff one byte */ - async_unwrap_char(dev->netdev, &dev->stats, + async_unwrap_char(dev->netdev, &dev->netdev->stats, &dev->rx_buff, *cp++); } else { while (count--) { @@ -582,13 +582,6 @@ EXPORT_SYMBOL(sirdev_receive); /* callbacks from network layer */ -static struct net_device_stats *sirdev_get_stats(struct net_device *ndev) -{ - struct sir_dev *dev = netdev_priv(ndev); - - return (dev) ? &dev->stats : NULL; -} - static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev) { struct sir_dev *dev = netdev_priv(ndev); @@ -654,7 +647,7 @@ static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev) */ atomic_set(&dev->enable_rx, 0); if (unlikely(sirdev_is_receiving(dev))) - dev->stats.collisions++; + dev->netdev->stats.collisions++; actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); @@ -669,8 +662,8 @@ static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev) IRDA_ERROR("%s: drv->do_write failed (%d)\n", __func__, actual); dev_kfree_skb_any(skb); - dev->stats.tx_errors++; - dev->stats.tx_dropped++; + dev->netdev->stats.tx_errors++; + dev->netdev->stats.tx_dropped++; netif_wake_queue(ndev); } spin_unlock_irqrestore(&dev->tx_lock, flags); @@ -918,7 +911,6 @@ struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *n ndev->hard_start_xmit = sirdev_hard_xmit; ndev->open = sirdev_open; ndev->stop = sirdev_close; - ndev->get_stats = sirdev_get_stats; ndev->do_ioctl = sirdev_ioctl; if (register_netdev(ndev)) { diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 5d09e157e15b..dd73cce10991 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -150,7 +150,6 @@ struct smsc_chip_address { /* Private data for each instance */ struct smsc_ircc_cb { struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ chipio_t io; /* IrDA controller information */ @@ -215,7 +214,6 @@ static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cm #if SMSC_IRCC2_C_NET_TIMEOUT static void smsc_ircc_timeout(struct net_device *dev); #endif -static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev); static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self); static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self); static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed); @@ -529,7 +527,6 @@ static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u dev->open = smsc_ircc_net_open; dev->stop = smsc_ircc_net_close; dev->do_ioctl = smsc_ircc_net_ioctl; - dev->get_stats = smsc_ircc_net_get_stats; self = netdev_priv(dev); self->netdev = dev; @@ -834,13 +831,6 @@ static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd return ret; } -static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev) -{ - struct smsc_ircc_cb *self = netdev_priv(dev); - - return &self->stats; -} - #if SMSC_IRCC2_C_NET_TIMEOUT /* * Function smsc_ircc_timeout (struct net_device *dev) @@ -920,7 +910,7 @@ static int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - self->stats.tx_bytes += self->tx_buff.len; + dev->stats.tx_bytes += self->tx_buff.len; /* Turn on transmit finished interrupt. Will fire immediately! */ outb(UART_IER_THRI, self->io.sir_base + UART_IER); @@ -1320,16 +1310,16 @@ static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self) /* Check for underrun! */ register_bank(iobase, 0); if (inb(iobase + IRCC_LSR) & IRCC_LSR_UNDERRUN) { - self->stats.tx_errors++; - self->stats.tx_fifo_errors++; + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; /* Reset error condition */ register_bank(iobase, 0); outb(IRCC_MASTER_ERROR_RESET, iobase + IRCC_MASTER); outb(0x00, iobase + IRCC_MASTER); } else { - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; + self->netdev->stats.tx_packets++; + self->netdev->stats.tx_bytes += self->tx_buff.len; } /* Check if it's time to change the speed */ @@ -1429,15 +1419,15 @@ static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self) /* Look for errors */ if (lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) { - self->stats.rx_errors++; + self->netdev->stats.rx_errors++; if (lsr & IRCC_LSR_FRAME_ERROR) - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; if (lsr & IRCC_LSR_CRC_ERROR) - self->stats.rx_crc_errors++; + self->netdev->stats.rx_crc_errors++; if (lsr & IRCC_LSR_SIZE_ERROR) - self->stats.rx_length_errors++; + self->netdev->stats.rx_length_errors++; if (lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) - self->stats.rx_length_errors++; + self->netdev->stats.rx_length_errors++; return; } @@ -1460,8 +1450,8 @@ static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self) skb_reserve(skb, 1); memcpy(skb_put(skb, len), self->rx_buff.data, len); - self->stats.rx_packets++; - self->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; skb->dev = self->netdev; skb_reset_mac_header(skb); @@ -1489,7 +1479,7 @@ static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self) * async_unwrap_char will deliver all found frames */ do { - async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, inb(iobase + UART_RX)); /* Make sure we don't stay here to long */ @@ -1992,7 +1982,7 @@ static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self) /* Tell network layer that we want more frames */ netif_wake_queue(self->netdev); } - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; if (self->io.speed <= 115200) { /* diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index ca4cd9266e55..8b1658c6c925 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -164,7 +164,7 @@ struct stir_cb { struct usb_device *usbdev; /* init: probe_irda */ struct net_device *netdev; /* network layer */ struct irlap_cb *irlap; /* The link layer we are binded to */ - struct net_device_stats stats; /* network statistics */ + struct qos_info qos; unsigned speed; /* Current speed */ @@ -323,16 +323,16 @@ static void fir_eof(struct stir_cb *stir) pr_debug("%s: short frame len %d\n", stir->netdev->name, len); - ++stir->stats.rx_errors; - ++stir->stats.rx_length_errors; + ++stir->netdev->stats.rx_errors; + ++stir->netdev->stats.rx_length_errors; return; } fcs = ~(crc32_le(~0, rx_buff->data, len)); if (fcs != get_unaligned_le32(rx_buff->data + len)) { pr_debug("crc error calc 0x%x len %d\n", fcs, len); - stir->stats.rx_errors++; - stir->stats.rx_crc_errors++; + stir->netdev->stats.rx_errors++; + stir->netdev->stats.rx_crc_errors++; return; } @@ -340,7 +340,7 @@ static void fir_eof(struct stir_cb *stir) if (len < IRDA_RX_COPY_THRESHOLD) { nskb = dev_alloc_skb(len + 1); if (unlikely(!nskb)) { - ++stir->stats.rx_dropped; + ++stir->netdev->stats.rx_dropped; return; } skb_reserve(nskb, 1); @@ -349,7 +349,7 @@ static void fir_eof(struct stir_cb *stir) } else { nskb = dev_alloc_skb(rx_buff->truesize); if (unlikely(!nskb)) { - ++stir->stats.rx_dropped; + ++stir->netdev->stats.rx_dropped; return; } skb_reserve(nskb, 1); @@ -366,8 +366,8 @@ static void fir_eof(struct stir_cb *stir) netif_rx(skb); - stir->stats.rx_packets++; - stir->stats.rx_bytes += len; + stir->netdev->stats.rx_packets++; + stir->netdev->stats.rx_bytes += len; rx_buff->data = rx_buff->head; rx_buff->len = 0; @@ -437,7 +437,7 @@ static void stir_fir_chars(struct stir_cb *stir, if (unlikely(rx_buff->len >= rx_buff->truesize)) { pr_debug("%s: fir frame exceeds %d\n", stir->netdev->name, rx_buff->truesize); - ++stir->stats.rx_over_errors; + ++stir->netdev->stats.rx_over_errors; goto error_recovery; } @@ -445,10 +445,10 @@ static void stir_fir_chars(struct stir_cb *stir, continue; frame_error: - ++stir->stats.rx_frame_errors; + ++stir->netdev->stats.rx_frame_errors; error_recovery: - ++stir->stats.rx_errors; + ++stir->netdev->stats.rx_errors; rx_buff->state = OUTSIDE_FRAME; rx_buff->in_frame = FALSE; } @@ -461,7 +461,7 @@ static void stir_sir_chars(struct stir_cb *stir, int i; for (i = 0; i < len; i++) - async_unwrap_char(stir->netdev, &stir->stats, + async_unwrap_char(stir->netdev, &stir->netdev->stats, &stir->rx_buff, bytes[i]); } @@ -692,7 +692,7 @@ static void receive_stop(struct stir_cb *stir) usb_kill_urb(stir->rx_urb); if (stir->rx_buff.in_frame) - stir->stats.collisions++; + stir->netdev->stats.collisions++; } /* * Wrap data in socket buffer and send it. @@ -718,15 +718,15 @@ static void stir_send(struct stir_cb *stir, struct sk_buff *skb) if (!first_frame) fifo_txwait(stir, wraplen); - stir->stats.tx_packets++; - stir->stats.tx_bytes += skb->len; + stir->netdev->stats.tx_packets++; + stir->netdev->stats.tx_bytes += skb->len; stir->netdev->trans_start = jiffies; pr_debug("send %d (%d)\n", skb->len, wraplen); if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), stir->io_buf, wraplen, NULL, TRANSMIT_TIMEOUT)) - stir->stats.tx_errors++; + stir->netdev->stats.tx_errors++; } /* @@ -1008,15 +1008,6 @@ static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) } /* - * Get device stats (for /proc/net/dev and ifconfig) - */ -static struct net_device_stats *stir_net_get_stats(struct net_device *netdev) -{ - struct stir_cb *stir = netdev_priv(netdev); - return &stir->stats; -} - -/* * This routine is called by the USB subsystem for each new device * in the system. We need to check if the device is ours, and in * this case start handling it. @@ -1066,7 +1057,6 @@ static int stir_probe(struct usb_interface *intf, net->hard_start_xmit = stir_hard_xmit; net->open = stir_net_open; net->stop = stir_net_close; - net->get_stats = stir_net_get_stats; net->do_ioctl = stir_net_ioctl; ret = register_netdev(net); diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 74c78cf7a333..8b3e545924cc 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -101,8 +101,6 @@ static int via_ircc_net_open(struct net_device *dev); static int via_ircc_net_close(struct net_device *dev); static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *via_ircc_net_get_stats(struct net_device - *dev); static void via_ircc_change_dongle_speed(int iobase, int speed, int dongle_id); static int RxTimerHandler(struct via_ircc_cb *self, int iobase); @@ -434,7 +432,6 @@ static __devinit int via_ircc_open(int i, chipio_t * info, unsigned int id) dev->open = via_ircc_net_open; dev->stop = via_ircc_net_close; dev->do_ioctl = via_ircc_net_ioctl; - dev->get_stats = via_ircc_net_get_stats; err = register_netdev(dev); if (err) @@ -855,7 +852,7 @@ static int via_ircc_hard_xmit_sir(struct sk_buff *skb, async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - self->stats.tx_bytes += self->tx_buff.len; + dev->stats.tx_bytes += self->tx_buff.len; /* Send this frame with old speed */ SetBaudRate(iobase, self->io.speed); SetPulseWidth(iobase, 12); @@ -921,7 +918,7 @@ static int via_ircc_hard_xmit_fir(struct sk_buff *skb, self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; self->tx_fifo.tail += skb->len; - self->stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); self->tx_fifo.len++; @@ -990,12 +987,12 @@ static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self) /* Clear bit, by writing 1 into it */ Tx_status = GetTXStatus(iobase); if (Tx_status & 0x08) { - self->stats.tx_errors++; - self->stats.tx_fifo_errors++; + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; hwreset(self); // how to clear underrrun ? } else { - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; ResetChip(iobase, 3); ResetChip(iobase, 4); } @@ -1119,8 +1116,8 @@ static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, } // Move to next frame self->rx_buff.data += len; - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); @@ -1180,7 +1177,7 @@ F01_E */ */ if ((skb == NULL) || (skb->data == NULL) || (self->rx_buff.data == NULL) || (len < 6)) { - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; return TRUE; } skb_reserve(skb, 1); @@ -1192,8 +1189,8 @@ F01_E */ // Move to next frame self->rx_buff.data += len; - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); @@ -1220,13 +1217,13 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase) IRDA_DEBUG(2, "%s(): len=%x\n", __func__, len); if ((len - 4) < 2) { - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; return FALSE; } skb = dev_alloc_skb(len + 1); if (skb == NULL) { - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; return FALSE; } skb_reserve(skb, 1); @@ -1238,8 +1235,8 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase) st_fifo->tail = 0; // Move to next frame self->rx_buff.data += len; - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); @@ -1295,7 +1292,7 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) */ if ((skb == NULL) || (skb->data == NULL) || (self->rx_buff.data == NULL) || (len < 6)) { - self->stats.rx_dropped++; + self->netdev->stats.rx_dropped++; continue; } skb_reserve(skb, 1); @@ -1307,8 +1304,8 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) // Move to next frame self->rx_buff.data += len; - self->stats.rx_bytes += len; - self->stats.rx_packets++; + self->netdev->stats.rx_bytes += len; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); @@ -1523,7 +1520,7 @@ static int via_ircc_net_open(struct net_device *dev) IRDA_ASSERT(dev != NULL, return -1;); self = netdev_priv(dev); - self->stats.rx_packets = 0; + dev->stats.rx_packets = 0; IRDA_ASSERT(self != NULL, return 0;); iobase = self->io.fir_base; if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) { @@ -1660,14 +1657,6 @@ static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, return ret; } -static struct net_device_stats *via_ircc_net_get_stats(struct net_device - *dev) -{ - struct via_ircc_cb *self = netdev_priv(dev); - - return &self->stats; -} - MODULE_AUTHOR("VIA Technologies,inc"); MODULE_DESCRIPTION("VIA IrDA Device Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h index 403c3f77634c..d9d1db03fa2d 100644 --- a/drivers/net/irda/via-ircc.h +++ b/drivers/net/irda/via-ircc.h @@ -95,7 +95,6 @@ struct via_ircc_cb { struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ struct qos_info qos; /* QoS capabilities for this device */ diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 0d30f8d659a1..723c4588c803 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -291,14 +291,14 @@ static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) now.tv_sec - idev->last_rx.tv_sec - delta1, delta2); seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", - idev->stats.rx_packets, idev->stats.rx_bytes, idev->stats.rx_errors, - idev->stats.rx_dropped); + ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors, + ndev->stats.rx_dropped); seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n", - idev->stats.rx_over_errors, idev->stats.rx_length_errors, - idev->stats.rx_frame_errors, idev->stats.rx_crc_errors); + ndev->stats.rx_over_errors, ndev->stats.rx_length_errors, + ndev->stats.rx_frame_errors, ndev->stats.rx_crc_errors); seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n", - idev->stats.tx_packets, idev->stats.tx_bytes, idev->stats.tx_errors, - idev->stats.tx_dropped, idev->stats.tx_fifo_errors); + ndev->stats.tx_packets, ndev->stats.tx_bytes, ndev->stats.tx_errors, + ndev->stats.tx_dropped, ndev->stats.tx_fifo_errors); } @@ -651,21 +651,21 @@ static void vlsi_rx_interrupt(struct net_device *ndev) if (ret < 0) { ret = -ret; - idev->stats.rx_errors++; + ndev->stats.rx_errors++; if (ret & VLSI_RX_DROP) - idev->stats.rx_dropped++; + ndev->stats.rx_dropped++; if (ret & VLSI_RX_OVER) - idev->stats.rx_over_errors++; + ndev->stats.rx_over_errors++; if (ret & VLSI_RX_LENGTH) - idev->stats.rx_length_errors++; + ndev->stats.rx_length_errors++; if (ret & VLSI_RX_FRAME) - idev->stats.rx_frame_errors++; + ndev->stats.rx_frame_errors++; if (ret & VLSI_RX_CRC) - idev->stats.rx_crc_errors++; + ndev->stats.rx_crc_errors++; } else if (ret > 0) { - idev->stats.rx_packets++; - idev->stats.rx_bytes += ret; + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += ret; } } @@ -686,6 +686,7 @@ static void vlsi_rx_interrupt(struct net_device *ndev) static void vlsi_unarm_rx(vlsi_irda_dev_t *idev) { + struct net_device *ndev = pci_get_drvdata(idev->pdev); struct vlsi_ring *r = idev->rx_ring; struct ring_descr *rd; int ret; @@ -711,21 +712,21 @@ static void vlsi_unarm_rx(vlsi_irda_dev_t *idev) if (ret < 0) { ret = -ret; - idev->stats.rx_errors++; + ndev->stats.rx_errors++; if (ret & VLSI_RX_DROP) - idev->stats.rx_dropped++; + ndev->stats.rx_dropped++; if (ret & VLSI_RX_OVER) - idev->stats.rx_over_errors++; + ndev->stats.rx_over_errors++; if (ret & VLSI_RX_LENGTH) - idev->stats.rx_length_errors++; + ndev->stats.rx_length_errors++; if (ret & VLSI_RX_FRAME) - idev->stats.rx_frame_errors++; + ndev->stats.rx_frame_errors++; if (ret & VLSI_RX_CRC) - idev->stats.rx_crc_errors++; + ndev->stats.rx_crc_errors++; } else if (ret > 0) { - idev->stats.rx_packets++; - idev->stats.rx_bytes += ret; + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += ret; } } } @@ -1050,8 +1051,8 @@ drop_unlock: drop: IRDA_WARNING("%s: dropping packet - %s\n", __func__, msg); dev_kfree_skb_any(skb); - idev->stats.tx_errors++; - idev->stats.tx_dropped++; + ndev->stats.tx_errors++; + ndev->stats.tx_dropped++; /* Don't even think about returning NET_XMIT_DROP (=1) here! * In fact any retval!=0 causes the packet scheduler to requeue the * packet for later retry of transmission - which isn't exactly @@ -1078,15 +1079,15 @@ static void vlsi_tx_interrupt(struct net_device *ndev) if (ret < 0) { ret = -ret; - idev->stats.tx_errors++; + ndev->stats.tx_errors++; if (ret & VLSI_TX_DROP) - idev->stats.tx_dropped++; + ndev->stats.tx_dropped++; if (ret & VLSI_TX_FIFO) - idev->stats.tx_fifo_errors++; + ndev->stats.tx_fifo_errors++; } else if (ret > 0){ - idev->stats.tx_packets++; - idev->stats.tx_bytes += ret; + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += ret; } } @@ -1122,6 +1123,7 @@ static void vlsi_tx_interrupt(struct net_device *ndev) static void vlsi_unarm_tx(vlsi_irda_dev_t *idev) { + struct net_device *ndev = pci_get_drvdata(idev->pdev); struct vlsi_ring *r = idev->tx_ring; struct ring_descr *rd; int ret; @@ -1145,15 +1147,15 @@ static void vlsi_unarm_tx(vlsi_irda_dev_t *idev) if (ret < 0) { ret = -ret; - idev->stats.tx_errors++; + ndev->stats.tx_errors++; if (ret & VLSI_TX_DROP) - idev->stats.tx_dropped++; + ndev->stats.tx_dropped++; if (ret & VLSI_TX_FIFO) - idev->stats.tx_fifo_errors++; + ndev->stats.tx_fifo_errors++; } else if (ret > 0){ - idev->stats.tx_packets++; - idev->stats.tx_bytes += ret; + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += ret; } } @@ -1373,13 +1375,6 @@ static int vlsi_stop_hw(vlsi_irda_dev_t *idev) /**************************************************************/ -static struct net_device_stats * vlsi_get_stats(struct net_device *ndev) -{ - vlsi_irda_dev_t *idev = netdev_priv(ndev); - - return &idev->stats; -} - static void vlsi_tx_timeout(struct net_device *ndev) { vlsi_irda_dev_t *idev = netdev_priv(ndev); @@ -1615,7 +1610,6 @@ static int vlsi_irda_init(struct net_device *ndev) ndev->open = vlsi_open; ndev->stop = vlsi_close; - ndev->get_stats = vlsi_get_stats; ndev->hard_start_xmit = vlsi_hard_start_xmit; ndev->do_ioctl = vlsi_ioctl; ndev->tx_timeout = vlsi_tx_timeout; diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h index 9b1884329fba..3050d1a0cccf 100644 --- a/drivers/net/irda/vlsi_ir.h +++ b/drivers/net/irda/vlsi_ir.h @@ -712,7 +712,6 @@ static inline struct ring_descr *ring_get(struct vlsi_ring *r) typedef struct vlsi_irda_dev { struct pci_dev *pdev; - struct net_device_stats stats; struct irlap_cb *irlap; diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 30ec9131c5ce..dc0a2e4d830f 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -102,7 +102,6 @@ static int w83977af_is_receiving(struct w83977af_ir *self); static int w83977af_net_open(struct net_device *dev); static int w83977af_net_close(struct net_device *dev); static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev); /* * Function w83977af_init () @@ -237,7 +236,6 @@ static int w83977af_open(int i, unsigned int iobase, unsigned int irq, dev->open = w83977af_net_open; dev->stop = w83977af_net_close; dev->do_ioctl = w83977af_net_ioctl; - dev->get_stats = w83977af_net_get_stats; err = register_netdev(dev); if (err) { @@ -702,13 +700,13 @@ static void w83977af_dma_xmit_complete(struct w83977af_ir *self) if (inb(iobase+AUDR) & AUDR_UNDR) { IRDA_DEBUG(0, "%s(), Transmit underrun!\n", __func__ ); - self->stats.tx_errors++; - self->stats.tx_fifo_errors++; + self->netdev->stats.tx_errors++; + self->netdev->stats.tx_fifo_errors++; /* Clear bit, by writing 1 to it */ outb(AUDR_UNDR, iobase+AUDR); } else - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; if (self->new_speed) { @@ -846,28 +844,28 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) if (status & FS_FO_ERR_MSK) { if (status & FS_FO_LST_FR) { /* Add number of lost frames to stats */ - self->stats.rx_errors += len; + self->netdev->stats.rx_errors += len; } else { /* Skip frame */ - self->stats.rx_errors++; + self->netdev->stats.rx_errors++; self->rx_buff.data += len; if (status & FS_FO_MX_LEX) - self->stats.rx_length_errors++; + self->netdev->stats.rx_length_errors++; if (status & FS_FO_PHY_ERR) - self->stats.rx_frame_errors++; + self->netdev->stats.rx_frame_errors++; if (status & FS_FO_CRC_ERR) - self->stats.rx_crc_errors++; + self->netdev->stats.rx_crc_errors++; } /* The errors below can be reported in both cases */ if (status & FS_FO_RX_OV) - self->stats.rx_fifo_errors++; + self->netdev->stats.rx_fifo_errors++; if (status & FS_FO_FSF_OV) - self->stats.rx_fifo_errors++; + self->netdev->stats.rx_fifo_errors++; } else { /* Check if we have transferred all data to memory */ @@ -917,7 +915,7 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) /* Move to next frame */ self->rx_buff.data += len; - self->stats.rx_packets++; + self->netdev->stats.rx_packets++; skb->dev = self->netdev; skb_reset_mac_header(skb); @@ -951,7 +949,7 @@ static void w83977af_pio_receive(struct w83977af_ir *self) /* Receive all characters in Rx FIFO */ do { byte = inb(iobase+RBR); - async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + async_unwrap_char(self->netdev, &self->netdev->stats, &self->rx_buff, byte); } while (inb(iobase+USR) & USR_RDR); /* Data available */ } @@ -994,7 +992,7 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) outb(AUDR_SFEND, iobase+AUDR); outb(set, iobase+SSR); - self->stats.tx_packets++; + self->netdev->stats.tx_packets++; /* Feed me more packets */ netif_wake_queue(self->netdev); @@ -1336,13 +1334,6 @@ out: return ret; } -static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev) -{ - struct w83977af_ir *self = netdev_priv(dev); - - return &self->stats; -} - MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/w83977af_ir.h b/drivers/net/irda/w83977af_ir.h index 87c3975baf62..fefe9b11e200 100644 --- a/drivers/net/irda/w83977af_ir.h +++ b/drivers/net/irda/w83977af_ir.h @@ -172,7 +172,6 @@ struct w83977af_ir { int tx_len; /* Number of frames in tx_buff */ struct net_device *netdev; /* Yes! we are some kind of netdevice */ - struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ struct qos_info qos; /* QoS capabilities for this device */ diff --git a/drivers/net/mlx4/en_params.c b/drivers/net/mlx4/en_params.c index cfeef0f1bacc..c1bd040b9e05 100644 --- a/drivers/net/mlx4/en_params.c +++ b/drivers/net/mlx4/en_params.c @@ -399,8 +399,10 @@ static int mlx4_en_set_ringparam(struct net_device *dev, rx_size = roundup_pow_of_two(param->rx_pending); rx_size = max_t(u32, rx_size, MLX4_EN_MIN_RX_SIZE); + rx_size = min_t(u32, rx_size, MLX4_EN_MAX_RX_SIZE); tx_size = roundup_pow_of_two(param->tx_pending); tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE); + tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE); if (rx_size == priv->prof->rx_ring_size && tx_size == priv->prof->tx_ring_size) @@ -440,8 +442,8 @@ static void mlx4_en_get_ringparam(struct net_device *dev, struct mlx4_en_dev *mdev = priv->mdev; memset(param, 0, sizeof(*param)); - param->rx_max_pending = mdev->dev->caps.max_rq_sg; - param->tx_max_pending = mdev->dev->caps.max_sq_sg; + param->rx_max_pending = MLX4_EN_MAX_RX_SIZE; + param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; param->rx_pending = mdev->profile.prof[priv->port].rx_ring_size; param->tx_pending = mdev->profile.prof[priv->port].tx_ring_size; } diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index ff4d75205c25..4afd5993e31c 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -203,19 +203,21 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, /* Optimize the common case when there are no wraparounds */ if (likely((void *) tx_desc + tx_info->nr_txbb * TXBB_SIZE <= end)) { - if (tx_info->linear) { - pci_unmap_single(mdev->pdev, - (dma_addr_t) be64_to_cpu(data->addr), + if (!tx_info->inl) { + if (tx_info->linear) { + pci_unmap_single(mdev->pdev, + (dma_addr_t) be64_to_cpu(data->addr), be32_to_cpu(data->byte_count), PCI_DMA_TODEVICE); - ++data; - } + ++data; + } - for (i = 0; i < frags; i++) { - frag = &skb_shinfo(skb)->frags[i]; - pci_unmap_page(mdev->pdev, - (dma_addr_t) be64_to_cpu(data[i].addr), - frag->size, PCI_DMA_TODEVICE); + for (i = 0; i < frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + pci_unmap_page(mdev->pdev, + (dma_addr_t) be64_to_cpu(data[i].addr), + frag->size, PCI_DMA_TODEVICE); + } } /* Stamp the freed descriptor */ for (i = 0; i < tx_info->nr_txbb * TXBB_SIZE; i += STAMP_STRIDE) { @@ -224,27 +226,29 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, } } else { - if ((void *) data >= end) { - data = (struct mlx4_wqe_data_seg *) - (ring->buf + ((void *) data - end)); - } + if (!tx_info->inl) { + if ((void *) data >= end) { + data = (struct mlx4_wqe_data_seg *) + (ring->buf + ((void *) data - end)); + } - if (tx_info->linear) { - pci_unmap_single(mdev->pdev, - (dma_addr_t) be64_to_cpu(data->addr), + if (tx_info->linear) { + pci_unmap_single(mdev->pdev, + (dma_addr_t) be64_to_cpu(data->addr), be32_to_cpu(data->byte_count), PCI_DMA_TODEVICE); - ++data; - } + ++data; + } - for (i = 0; i < frags; i++) { - /* Check for wraparound before unmapping */ - if ((void *) data >= end) - data = (struct mlx4_wqe_data_seg *) ring->buf; - frag = &skb_shinfo(skb)->frags[i]; - pci_unmap_page(mdev->pdev, + for (i = 0; i < frags; i++) { + /* Check for wraparound before unmapping */ + if ((void *) data >= end) + data = (struct mlx4_wqe_data_seg *) ring->buf; + frag = &skb_shinfo(skb)->frags[i]; + pci_unmap_page(mdev->pdev, (dma_addr_t) be64_to_cpu(data->addr), frag->size, PCI_DMA_TODEVICE); + } } /* Stamp the freed descriptor */ for (i = 0; i < tx_info->nr_txbb * TXBB_SIZE; i += STAMP_STRIDE) { @@ -790,8 +794,11 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) wmb(); data->byte_count = cpu_to_be32(skb_headlen(skb) - lso_header_size); } - } else + tx_info->inl = 0; + } else { build_inline_wqe(tx_desc, skb, real_size, &vlan_tag, tx_ind, fragptr); + tx_info->inl = 1; + } ring->prod += nr_txbb; diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index 2e96c7b2180a..e9af32d41ca4 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -115,6 +115,10 @@ enum { }; #define MLX4_EN_MAX_RX_FRAGS 4 +/* Maximum ring sizes */ +#define MLX4_EN_MAX_TX_SIZE 8192 +#define MLX4_EN_MAX_RX_SIZE 8192 + /* Minimum ring size for our page-allocation sceme to work */ #define MLX4_EN_MIN_RX_SIZE (MLX4_EN_ALLOC_SIZE / SMP_CACHE_BYTES) #define MLX4_EN_MIN_TX_SIZE (4096 / TXBB_SIZE) @@ -202,6 +206,7 @@ struct mlx4_en_tx_info { u32 nr_txbb; u8 linear; u8 data_offset; + u8 inl; }; diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 478edb92bca3..c5dec54251bf 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -779,6 +779,22 @@ static void __devinit natsemi_init_media (struct net_device *dev) } +static const struct net_device_ops natsemi_netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_get_stats = get_stats, + .ndo_set_multicast_list = set_rx_mode, + .ndo_change_mtu = natsemi_change_mtu, + .ndo_do_ioctl = netdev_ioctl, + .ndo_tx_timeout = ns_tx_timeout, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = natsemi_poll_controller, +#endif +}; + static int __devinit natsemi_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -911,20 +927,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, if (find_cnt < MAX_UNITS && full_duplex[find_cnt]) np->full_duplex = 1; - /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->change_mtu = &natsemi_change_mtu; - dev->do_ioctl = &netdev_ioctl; - dev->tx_timeout = &ns_tx_timeout; + dev->netdev_ops = &natsemi_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = &natsemi_poll_controller; -#endif SET_ETHTOOL_OPS(dev, ðtool_ops); if (mtu) diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 46b0772489e4..42021aca1ddd 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -1957,6 +1957,9 @@ static const struct net_device_ops netdev_ops = { .ndo_set_multicast_list = ns83820_set_multicast, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = ns83820_tx_timeout, +#ifdef NS83820_VLAN_ACCEL_SUPPORT + .ndo_vlan_rx_register = ns83820_vlan_rx_register, +#endif }; static int __devinit ns83820_init_one(struct pci_dev *pci_dev, @@ -2216,7 +2219,6 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, #ifdef NS83820_VLAN_ACCEL_SUPPORT /* We also support hardware vlan acceleration */ ndev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; - ndev->vlan_rx_register = ns83820_vlan_rx_register; #endif if (using_dac) { diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 044b7b07f5f4..665a4286da39 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1568,6 +1568,22 @@ pcnet32_probe_pci(struct pci_dev *pdev, const struct pci_device_id *ent) return err; } +static const struct net_device_ops pcnet32_netdev_ops = { + .ndo_open = pcnet32_open, + .ndo_stop = pcnet32_close, + .ndo_start_xmit = pcnet32_start_xmit, + .ndo_tx_timeout = pcnet32_tx_timeout, + .ndo_get_stats = pcnet32_get_stats, + .ndo_set_multicast_list = pcnet32_set_multicast_list, + .ndo_do_ioctl = pcnet32_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = pcnet32_poll_controller, +#endif +}; + /* pcnet32_probe1 * Called from both pcnet32_probe_vlbus and pcnet_probe_pci. * pdev will be NULL when called from pcnet32_probe_vlbus. @@ -1934,20 +1950,10 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) lp->watchdog_timer.function = (void *)&pcnet32_watchdog; /* The PCNET32-specific entries in the device structure. */ - dev->open = &pcnet32_open; - dev->hard_start_xmit = &pcnet32_start_xmit; - dev->stop = &pcnet32_close; - dev->get_stats = &pcnet32_get_stats; - dev->set_multicast_list = &pcnet32_set_multicast_list; - dev->do_ioctl = &pcnet32_ioctl; + dev->netdev_ops = &pcnet32_netdev_ops; dev->ethtool_ops = &pcnet32_ethtool_ops; - dev->tx_timeout = pcnet32_tx_timeout; dev->watchdog_timeo = (5 * HZ); -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = pcnet32_poll_controller; -#endif - /* Fill in the generic fields of the device structure. */ if (register_netdev(dev)) goto err_free_ring; @@ -2276,7 +2282,7 @@ static int pcnet32_open(struct net_device *dev) if (lp->chip_version >= PCNET32_79C970A) { /* Print the link status and start the watchdog */ pcnet32_check_media(dev, 1); - mod_timer(&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT); + mod_timer(&lp->watchdog_timer, PCNET32_WATCHDOG_TIMEOUT); } i = 0; @@ -2911,7 +2917,7 @@ static void pcnet32_watchdog(struct net_device *dev) pcnet32_check_media(dev, 0); spin_unlock_irqrestore(&lp->lock, flags); - mod_timer(&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT); + mod_timer(&lp->watchdog_timer, round_jiffies(PCNET32_WATCHDOG_TIMEOUT)); } static int pcnet32_pm_suspend(struct pci_dev *pdev, pm_message_t state) diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 0c46d603b8fe..0be0f0b164f3 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -265,6 +265,13 @@ static const struct header_ops plip_header_ops = { .cache = plip_hard_header_cache, }; +static const struct net_device_ops plip_netdev_ops = { + .ndo_open = plip_open, + .ndo_stop = plip_close, + .ndo_start_xmit = plip_tx_packet, + .ndo_do_ioctl = plip_ioctl, +}; + /* Entry point of PLIP driver. Probe the hardware, and register/initialize the driver. @@ -280,15 +287,11 @@ plip_init_netdev(struct net_device *dev) struct net_local *nl = netdev_priv(dev); /* Then, override parts of it */ - dev->hard_start_xmit = plip_tx_packet; - dev->open = plip_open; - dev->stop = plip_close; - dev->do_ioctl = plip_ioctl; - dev->tx_queue_len = 10; dev->flags = IFF_POINTOPOINT|IFF_NOARP; memset(dev->dev_addr, 0xfc, ETH_ALEN); + dev->netdev_ops = &plip_netdev_ops; dev->header_ops = &plip_header_ops; diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 53bbddfc8c95..cf3a082bc89d 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -49,8 +49,8 @@ #include <asm/processor.h> #define DRV_NAME "r6040" -#define DRV_VERSION "0.19" -#define DRV_RELDATE "18Dec2008" +#define DRV_VERSION "0.20" +#define DRV_RELDATE "07Jan2009" /* PHY CHIP Address */ #define PHY1_ADDR 1 /* For MAC1 */ @@ -200,7 +200,7 @@ struct r6040_private { static char version[] __devinitdata = KERN_INFO DRV_NAME ": RDC R6040 NAPI net driver," - "version "DRV_VERSION " (" DRV_RELDATE ")\n"; + "version "DRV_VERSION " (" DRV_RELDATE ")"; static int phy_table[] = { PHY1_ADDR, PHY2_ADDR }; @@ -330,7 +330,7 @@ static int r6040_alloc_rxbufs(struct net_device *dev) do { skb = netdev_alloc_skb(dev, MAX_BUF_SIZE); if (!skb) { - printk(KERN_ERR "%s: failed to alloc skb for rx\n", dev->name); + printk(KERN_ERR DRV_NAME "%s: failed to alloc skb for rx\n", dev->name); rc = -ENOMEM; goto err_exit; } @@ -1077,20 +1077,20 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, /* this should always be supported */ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (err) { - printk(KERN_ERR DRV_NAME "32-bit PCI DMA addresses" + printk(KERN_ERR DRV_NAME ": 32-bit PCI DMA addresses" "not supported by the card\n"); goto err_out; } err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (err) { - printk(KERN_ERR DRV_NAME "32-bit PCI DMA addresses" + printk(KERN_ERR DRV_NAME ": 32-bit PCI DMA addresses" "not supported by the card\n"); goto err_out; } /* IO Size check */ if (pci_resource_len(pdev, 0) < io_size) { - printk(KERN_ERR DRV_NAME "Insufficient PCI resources, aborting\n"); + printk(KERN_ERR DRV_NAME ": Insufficient PCI resources, aborting\n"); err = -EIO; goto err_out; } @@ -1100,7 +1100,7 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(struct r6040_private)); if (!dev) { - printk(KERN_ERR DRV_NAME "Failed to allocate etherdev\n"); + printk(KERN_ERR DRV_NAME ": Failed to allocate etherdev\n"); err = -ENOMEM; goto err_out; } @@ -1116,11 +1116,15 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, ioaddr = pci_iomap(pdev, bar, io_size); if (!ioaddr) { - printk(KERN_ERR "ioremap failed for device %s\n", + printk(KERN_ERR DRV_NAME ": ioremap failed for device %s\n", pci_name(pdev)); err = -EIO; goto err_out_free_res; } + /* If PHY status change register is still set to zero it means the + * bootloader didn't initialize it */ + if (ioread16(ioaddr + PHY_CC) == 0) + iowrite16(0x9f07, ioaddr + PHY_CC); /* Init system & device */ lp->base = ioaddr; @@ -1137,6 +1141,11 @@ static int __devinit r6040_init_one(struct pci_dev *pdev, adrp[1] = ioread16(ioaddr + MID_0M); adrp[2] = ioread16(ioaddr + MID_0H); + /* Some bootloader/BIOSes do not initialize + * MAC address, warn about that */ + if (!(adrp[0] || adrp[1] || adrp[2])) + printk(KERN_WARNING DRV_NAME ": MAC address not initialized\n"); + /* Link new device into r6040_root_dev */ lp->pdev = pdev; lp->dev = dev; diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index be3025310e90..fc0e38bddeeb 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -134,6 +134,16 @@ static const struct pnp_device_id sb1000_pnp_ids[] = { }; MODULE_DEVICE_TABLE(pnp, sb1000_pnp_ids); +static const struct net_device_ops sb1000_netdev_ops = { + .ndo_open = sb1000_open, + .ndo_start_xmit = sb1000_start_xmit, + .ndo_do_ioctl = sb1000_dev_ioctl, + .ndo_stop = sb1000_close, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int sb1000_probe_one(struct pnp_dev *pdev, const struct pnp_device_id *id) { @@ -192,11 +202,7 @@ sb1000_probe_one(struct pnp_dev *pdev, const struct pnp_device_id *id) if (sb1000_debug > 0) printk(KERN_NOTICE "%s", version); - /* The SB1000-specific entries in the device structure. */ - dev->open = sb1000_open; - dev->do_ioctl = sb1000_dev_ioctl; - dev->hard_start_xmit = sb1000_start_xmit; - dev->stop = sb1000_close; + dev->netdev_ops = &sb1000_netdev_ops; /* hardware address is 0:0:serial_number */ dev->dev_addr[2] = serial_number >> 24 & 0xff; diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 6884dc8c1f82..5b9f2d9cc4ed 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1403,9 +1403,9 @@ static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx) } /* Disable both devices */ - pci_disable_device(efx->pci_dev); + pci_clear_master(efx->pci_dev); if (FALCON_IS_DUAL_FUNC(efx)) - pci_disable_device(nic_data->pci_dev2); + pci_clear_master(nic_data->pci_dev2); falcon_disable_interrupts(efx); if (++n_int_errors < FALCON_MAX_INT_ERRORS) { diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index 83cc3c5f7946..a9732686134b 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -1782,6 +1782,21 @@ static int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL); } +static const struct net_device_ops sis190_netdev_ops = { + .ndo_open = sis190_open, + .ndo_stop = sis190_close, + .ndo_do_ioctl = sis190_ioctl, + .ndo_start_xmit = sis190_start_xmit, + .ndo_tx_timeout = sis190_tx_timeout, + .ndo_set_multicast_list = sis190_set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = sis190_netpoll, +#endif +}; + static int __devinit sis190_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1815,19 +1830,12 @@ static int __devinit sis190_init_one(struct pci_dev *pdev, INIT_WORK(&tp->phy_task, sis190_phy_task); - dev->open = sis190_open; - dev->stop = sis190_close; - dev->do_ioctl = sis190_ioctl; - dev->tx_timeout = sis190_tx_timeout; - dev->watchdog_timeo = SIS190_TX_TIMEOUT; - dev->hard_start_xmit = sis190_start_xmit; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = sis190_netpoll; -#endif - dev->set_multicast_list = sis190_set_rx_mode; + dev->netdev_ops = &sis190_netdev_ops; + SET_ETHTOOL_OPS(dev, &sis190_ethtool_ops); dev->irq = pdev->irq; dev->base_addr = (unsigned long) 0xdead; + dev->watchdog_timeo = SIS190_TX_TIMEOUT; spin_lock_init(&tp->lock); diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 8e1c0baf6958..5c61d5fad908 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -603,7 +603,6 @@ static int sl_init(struct net_device *dev) dev->mtu = sl->mtu; dev->type = ARPHRD_SLIP + sl->mode; #ifdef SL_CHECK_TRANSMIT - dev->tx_timeout = sl_tx_timeout; dev->watchdog_timeo = 20*HZ; #endif return 0; @@ -617,19 +616,26 @@ static void sl_uninit(struct net_device *dev) sl_free_bufs(sl); } +static const struct net_device_ops sl_netdev_ops = { + .ndo_init = sl_init, + .ndo_uninit = sl_uninit, + .ndo_open = sl_open, + .ndo_stop = sl_close, + .ndo_start_xmit = sl_xmit, + .ndo_get_stats = sl_get_stats, + .ndo_change_mtu = sl_change_mtu, + .ndo_tx_timeout = sl_tx_timeout, +#ifdef CONFIG_SLIP_SMART + .ndo_do_ioctl = sl_ioctl, +#endif +}; + + static void sl_setup(struct net_device *dev) { - dev->init = sl_init; - dev->uninit = sl_uninit; - dev->open = sl_open; + dev->netdev_ops = &sl_netdev_ops; dev->destructor = free_netdev; - dev->stop = sl_close; - dev->get_stats = sl_get_stats; - dev->change_mtu = sl_change_mtu; - dev->hard_start_xmit = sl_xmit; -#ifdef CONFIG_SLIP_SMART - dev->do_ioctl = sl_ioctl; -#endif + dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 10; diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 57fb1f71c47b..da3a76b18eff 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -648,6 +648,24 @@ static void netdev_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) #endif /* VLAN_SUPPORT */ +static const struct net_device_ops netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_tx_timeout = tx_timeout, + .ndo_get_stats = get_stats, + .ndo_set_multicast_list = &set_rx_mode, + .ndo_do_ioctl = netdev_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef VLAN_SUPPORT + .ndo_vlan_rx_register = netdev_vlan_rx_register, + .ndo_vlan_rx_add_vid = netdev_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = netdev_vlan_rx_kill_vid, +#endif +}; + static int __devinit starfire_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -710,11 +728,9 @@ static int __devinit starfire_init_one(struct pci_dev *pdev, if (enable_hw_cksum) dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; #endif /* ZEROCOPY */ + #ifdef VLAN_SUPPORT dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; - dev->vlan_rx_register = netdev_vlan_rx_register; - dev->vlan_rx_add_vid = netdev_vlan_rx_add_vid; - dev->vlan_rx_kill_vid = netdev_vlan_rx_kill_vid; #endif /* VLAN_RX_KILL_VID */ #ifdef ADDR_64BITS dev->features |= NETIF_F_HIGHDMA; @@ -810,18 +826,12 @@ static int __devinit starfire_init_one(struct pci_dev *pdev, } } - /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->tx_timeout = tx_timeout; + dev->netdev_ops = &netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; - netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work); - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &netdev_ioctl; SET_ETHTOOL_OPS(dev, ðtool_ops); + netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work); + if (mtu) dev->mtu = mtu; diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index 698893b92003..feaf0e0577d7 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -449,6 +449,19 @@ static void sundance_reset(struct net_device *dev, unsigned long reset_cmd) } } +static const struct net_device_ops netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_get_stats = get_stats, + .ndo_set_multicast_list = set_rx_mode, + .ndo_do_ioctl = netdev_ioctl, + .ndo_tx_timeout = tx_timeout, + .ndo_change_mtu = change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit sundance_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -530,16 +543,10 @@ static int __devinit sundance_probe1 (struct pci_dev *pdev, np->mii_if.reg_num_mask = 0x1f; /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &netdev_ioctl; + dev->netdev_ops = &netdev_ops; SET_ETHTOOL_OPS(dev, ðtool_ops); - dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; - dev->change_mtu = &change_mtu; + pci_set_drvdata(pdev, dev); i = register_netdev(dev); diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 8a7460412482..86c765d83de1 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -2989,6 +2989,19 @@ static void gem_remove_one(struct pci_dev *pdev) } } +static const struct net_device_ops gem_netdev_ops = { + .ndo_open = gem_open, + .ndo_stop = gem_close, + .ndo_start_xmit = gem_start_xmit, + .ndo_get_stats = gem_get_stats, + .ndo_set_multicast_list = gem_set_multicast, + .ndo_do_ioctl = gem_ioctl, + .ndo_tx_timeout = gem_tx_timeout, + .ndo_change_mtu = gem_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit gem_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -3142,17 +3155,10 @@ static int __devinit gem_init_one(struct pci_dev *pdev, if (gem_get_device_address(gp)) goto err_out_free_consistent; - dev->open = gem_open; - dev->stop = gem_close; - dev->hard_start_xmit = gem_start_xmit; - dev->get_stats = gem_get_stats; - dev->set_multicast_list = gem_set_multicast; - dev->do_ioctl = gem_ioctl; + dev->netdev_ops = &gem_netdev_ops; netif_napi_add(dev, &gp->napi, gem_poll, 64); dev->ethtool_ops = &gem_ethtool_ops; - dev->tx_timeout = gem_tx_timeout; dev->watchdog_timeo = 5 * HZ; - dev->change_mtu = gem_change_mtu; dev->irq = pdev->irq; dev->dma = 0; dev->set_mac_address = gem_set_mac_address; diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index b22d3355fb45..7a72a3112f0a 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -2607,6 +2607,18 @@ static struct quattro * __devinit quattro_pci_find(struct pci_dev *pdev) } #endif /* CONFIG_PCI */ +static const struct net_device_ops hme_netdev_ops = { + .ndo_open = happy_meal_open, + .ndo_stop = happy_meal_close, + .ndo_start_xmit = happy_meal_start_xmit, + .ndo_tx_timeout = happy_meal_tx_timeout, + .ndo_get_stats = happy_meal_get_stats, + .ndo_set_multicast_list = happy_meal_set_multicast, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + #ifdef CONFIG_SBUS static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) { @@ -2750,12 +2762,7 @@ static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) init_timer(&hp->happy_timer); hp->dev = dev; - dev->open = &happy_meal_open; - dev->stop = &happy_meal_close; - dev->hard_start_xmit = &happy_meal_start_xmit; - dev->get_stats = &happy_meal_get_stats; - dev->set_multicast_list = &happy_meal_set_multicast; - dev->tx_timeout = &happy_meal_tx_timeout; + dev->netdev_ops = &hme_netdev_ops; dev->watchdog_timeo = 5*HZ; dev->ethtool_ops = &hme_ethtool_ops; @@ -3076,12 +3083,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, init_timer(&hp->happy_timer); hp->dev = dev; - dev->open = &happy_meal_open; - dev->stop = &happy_meal_close; - dev->hard_start_xmit = &happy_meal_start_xmit; - dev->get_stats = &happy_meal_get_stats; - dev->set_multicast_list = &happy_meal_set_multicast; - dev->tx_timeout = &happy_meal_tx_timeout; + dev->netdev_ops = &hme_netdev_ops; dev->watchdog_timeo = 5*HZ; dev->ethtool_ops = &hme_ethtool_ops; dev->irq = pdev->irq; diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index 233f1cda36e5..611230fef2b6 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -336,7 +336,7 @@ static int vnet_walk_rx_one(struct vnet_port *port, if (IS_ERR(desc)) return PTR_ERR(desc); - viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%lx:%lx]\n", + viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n", desc->hdr.state, desc->hdr.ack, desc->size, desc->ncookies, desc->cookies[0].cookie_addr, @@ -394,14 +394,14 @@ static int vnet_rx(struct vnet_port *port, void *msgbuf) struct vio_dring_state *dr = &port->vio.drings[VIO_DRIVER_RX_RING]; struct vio_driver_state *vio = &port->vio; - viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016lx] rcv_nxt[%016lx]\n", + viodbg(DATA, "vnet_rx stype_env[%04x] seq[%016llx] rcv_nxt[%016llx]\n", pkt->tag.stype_env, pkt->seq, dr->rcv_nxt); if (unlikely(pkt->tag.stype_env != VIO_DRING_DATA)) return 0; if (unlikely(pkt->seq != dr->rcv_nxt)) { - printk(KERN_ERR PFX "RX out of sequence seq[0x%lx] " - "rcv_nxt[0x%lx]\n", pkt->seq, dr->rcv_nxt); + printk(KERN_ERR PFX "RX out of sequence seq[0x%llx] " + "rcv_nxt[0x%llx]\n", pkt->seq, dr->rcv_nxt); return 0; } diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index 85ef8b744557..68b967b585aa 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -831,6 +831,21 @@ static void TLan_Poll(struct net_device *dev) } #endif +static const struct net_device_ops TLan_netdev_ops = { + .ndo_open = TLan_Open, + .ndo_stop = TLan_Close, + .ndo_start_xmit = TLan_StartTx, + .ndo_tx_timeout = TLan_tx_timeout, + .ndo_get_stats = TLan_GetStats, + .ndo_set_multicast_list = TLan_SetMulticastList, + .ndo_do_ioctl = TLan_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = TLan_Poll, +#endif +}; @@ -892,16 +907,7 @@ static int TLan_Init( struct net_device *dev ) netif_carrier_off(dev); /* Device methods */ - dev->open = &TLan_Open; - dev->hard_start_xmit = &TLan_StartTx; - dev->stop = &TLan_Close; - dev->get_stats = &TLan_GetStats; - dev->set_multicast_list = &TLan_SetMulticastList; - dev->do_ioctl = &TLan_ioctl; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = &TLan_Poll; -#endif - dev->tx_timeout = &TLan_tx_timeout; + dev->netdev_ops = &TLan_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; return 0; diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index 5166be930a52..d5d53b633cf8 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -1922,6 +1922,18 @@ bad_srom: goto fill_defaults; } +static const struct net_device_ops de_netdev_ops = { + .ndo_open = de_open, + .ndo_stop = de_close, + .ndo_set_multicast_list = de_set_rx_mode, + .ndo_start_xmit = de_start_xmit, + .ndo_get_stats = de_get_stats, + .ndo_tx_timeout = de_tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit de_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1944,14 +1956,9 @@ static int __devinit de_init_one (struct pci_dev *pdev, if (!dev) return -ENOMEM; + dev->netdev_ops = &de_netdev_ops; SET_NETDEV_DEV(dev, &pdev->dev); - dev->open = de_open; - dev->stop = de_close; - dev->set_multicast_list = de_set_rx_mode; - dev->hard_start_xmit = de_start_xmit; - dev->get_stats = de_get_stats; dev->ethtool_ops = &de_ethtool_ops; - dev->tx_timeout = de_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; de = netdev_priv(dev); diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 67bfd6f43366..6418f74415d7 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -1077,6 +1077,18 @@ static int (*dc_infoblock[])(struct net_device *dev, u_char, u_char *) = { mdelay(2); /* Wait for 2ms */\ } +static const struct net_device_ops de4x5_netdev_ops = { + .ndo_open = de4x5_open, + .ndo_stop = de4x5_close, + .ndo_start_xmit = de4x5_queue_pkt, + .ndo_get_stats = de4x5_get_stats, + .ndo_set_multicast_list = set_multicast_list, + .ndo_do_ioctl = de4x5_ioctl, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address= eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static int __devinit de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev) @@ -1258,13 +1270,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev) /* The DE4X5-specific entries in the device structure. */ SET_NETDEV_DEV(dev, gendev); - dev->open = &de4x5_open; - dev->hard_start_xmit = &de4x5_queue_pkt; - dev->stop = &de4x5_close; - dev->get_stats = &de4x5_get_stats; - dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &de4x5_ioctl; - + dev->netdev_ops = &de4x5_netdev_ops; dev->mem_start = 0; /* Fill in the generic fields of the device structure. */ diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index 28a5c51b43a0..2e5c99941f35 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -257,9 +257,6 @@ struct dmfe_board_info { u8 wol_mode; /* user WOL settings */ struct timer_list timer; - /* System defined statistic counter */ - struct net_device_stats stats; - /* Driver defined statistic counter */ unsigned long tx_fifo_underrun; unsigned long tx_loss_carrier; @@ -316,7 +313,6 @@ static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control static int dmfe_open(struct DEVICE *); static int dmfe_start_xmit(struct sk_buff *, struct DEVICE *); static int dmfe_stop(struct DEVICE *); -static struct net_device_stats * dmfe_get_stats(struct DEVICE *); static void dmfe_set_filter_mode(struct DEVICE *); static const struct ethtool_ops netdev_ethtool_ops; static u16 read_srom_word(long ,int); @@ -351,6 +347,19 @@ static void dmfe_set_phyxcer(struct dmfe_board_info *); /* DM910X network board routine ---------------------------- */ +static const struct net_device_ops netdev_ops = { + .ndo_open = dmfe_open, + .ndo_stop = dmfe_stop, + .ndo_start_xmit = dmfe_start_xmit, + .ndo_set_multicast_list = dmfe_set_filter_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = poll_dmfe, +#endif +}; + /* * Search DM910X board ,allocate space and register it */ @@ -442,14 +451,7 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev, dev->base_addr = db->ioaddr; dev->irq = pdev->irq; pci_set_drvdata(pdev, dev); - dev->open = &dmfe_open; - dev->hard_start_xmit = &dmfe_start_xmit; - dev->stop = &dmfe_stop; - dev->get_stats = &dmfe_get_stats; - dev->set_multicast_list = &dmfe_set_filter_mode; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = &poll_dmfe; -#endif + dev->netdev_ops = &netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; netif_carrier_off(dev); spin_lock_init(&db->lock); @@ -867,15 +869,15 @@ static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) /* A packet sent completed */ db->tx_packet_cnt--; - db->stats.tx_packets++; + dev->stats.tx_packets++; /* Transmit statistic counter */ if ( tdes0 != 0x7fffffff ) { /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ - db->stats.collisions += (tdes0 >> 3) & 0xf; - db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; + dev->stats.collisions += (tdes0 >> 3) & 0xf; + dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; if (tdes0 & TDES0_ERR_MASK) { - db->stats.tx_errors++; + dev->stats.tx_errors++; if (tdes0 & 0x0002) { /* UnderRun */ db->tx_fifo_underrun++; @@ -969,13 +971,13 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) if (rdes0 & 0x8000) { /* This is a error packet */ //printk(DRV_NAME ": rdes0: %lx\n", rdes0); - db->stats.rx_errors++; + dev->stats.rx_errors++; if (rdes0 & 1) - db->stats.rx_fifo_errors++; + dev->stats.rx_fifo_errors++; if (rdes0 & 2) - db->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; if (rdes0 & 0x80) - db->stats.rx_length_errors++; + dev->stats.rx_length_errors++; } if ( !(rdes0 & 0x8000) || @@ -1008,8 +1010,8 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); - db->stats.rx_packets++; - db->stats.rx_bytes += rxlen; + dev->stats.rx_packets++; + dev->stats.rx_bytes += rxlen; } } else { /* Reuse SKB buffer when the packet is error */ @@ -1024,20 +1026,6 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) db->rx_ready_ptr = rxptr; } - -/* - * Get statistics from driver. - */ - -static struct net_device_stats * dmfe_get_stats(struct DEVICE *dev) -{ - struct dmfe_board_info *db = netdev_priv(dev); - - DMFE_DBUG(0, "dmfe_get_stats", 0); - return &db->stats; -} - - /* * Set DM910X multicast address */ @@ -1161,7 +1149,7 @@ static void dmfe_timer(unsigned long data) /* Operating Mode Check */ if ( (db->dm910x_chk_mode & 0x1) && - (db->stats.rx_packets > MAX_CHECK_PACKET) ) + (dev->stats.rx_packets > MAX_CHECK_PACKET) ) db->dm910x_chk_mode = 0x4; /* Dynamic reset DM910X : system error or transmit time-out */ diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index ff84babb3ff3..bee75fa87a9c 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -1225,6 +1225,22 @@ static int tulip_uli_dm_quirk(struct pci_dev *pdev) return 0; } +static const struct net_device_ops tulip_netdev_ops = { + .ndo_open = tulip_open, + .ndo_start_xmit = tulip_start_xmit, + .ndo_tx_timeout = tulip_tx_timeout, + .ndo_stop = tulip_close, + .ndo_get_stats = tulip_get_stats, + .ndo_do_ioctl = private_ioctl, + .ndo_set_multicast_list = set_rx_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = poll_tulip, +#endif +}; + static int __devinit tulip_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1601,20 +1617,11 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, } /* The Tulip-specific entries in the device structure. */ - dev->open = tulip_open; - dev->hard_start_xmit = tulip_start_xmit; - dev->tx_timeout = tulip_tx_timeout; + dev->netdev_ops = &tulip_netdev_ops; dev->watchdog_timeo = TX_TIMEOUT; #ifdef CONFIG_TULIP_NAPI netif_napi_add(dev, &tp->napi, tulip_poll, 16); #endif - dev->stop = tulip_close; - dev->get_stats = tulip_get_stats; - dev->do_ioctl = private_ioctl; - dev->set_multicast_list = set_rx_mode; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = &poll_tulip; -#endif SET_ETHTOOL_OPS(dev, &ops); if (register_netdev(dev)) diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c index 00cbc5251dcc..030e02e63023 100644 --- a/drivers/net/tulip/uli526x.c +++ b/drivers/net/tulip/uli526x.c @@ -168,9 +168,6 @@ struct uli526x_board_info { u8 wait_reset; /* Hardware failed, need to reset */ struct timer_list timer; - /* System defined statistic counter */ - struct net_device_stats stats; - /* Driver defined statistic counter */ unsigned long tx_fifo_underrun; unsigned long tx_loss_carrier; @@ -220,7 +217,6 @@ static int mode = 8; static int uli526x_open(struct net_device *); static int uli526x_start_xmit(struct sk_buff *, struct net_device *); static int uli526x_stop(struct net_device *); -static struct net_device_stats * uli526x_get_stats(struct net_device *); static void uli526x_set_filter_mode(struct net_device *); static const struct ethtool_ops netdev_ethtool_ops; static u16 read_srom_word(long, int); @@ -251,6 +247,19 @@ static void uli526x_set_phyxcer(struct uli526x_board_info *); /* ULI526X network board routine ---------------------------- */ +static const struct net_device_ops netdev_ops = { + .ndo_open = uli526x_open, + .ndo_stop = uli526x_stop, + .ndo_start_xmit = uli526x_start_xmit, + .ndo_set_multicast_list = uli526x_set_filter_mode, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = uli526x_poll, +#endif +}; + /* * Search ULI526X board, allocate space and register it */ @@ -335,15 +344,9 @@ static int __devinit uli526x_init_one (struct pci_dev *pdev, pci_set_drvdata(pdev, dev); /* Register some necessary functions */ - dev->open = &uli526x_open; - dev->hard_start_xmit = &uli526x_start_xmit; - dev->stop = &uli526x_stop; - dev->get_stats = &uli526x_get_stats; - dev->set_multicast_list = &uli526x_set_filter_mode; + dev->netdev_ops = &netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = &uli526x_poll; -#endif + spin_lock_init(&db->lock); @@ -733,7 +736,8 @@ static void uli526x_poll(struct net_device *dev) * Free TX resource after TX complete */ -static void uli526x_free_tx_pkt(struct net_device *dev, struct uli526x_board_info * db) +static void uli526x_free_tx_pkt(struct net_device *dev, + struct uli526x_board_info * db) { struct tx_desc *txptr; u32 tdes0; @@ -747,15 +751,15 @@ static void uli526x_free_tx_pkt(struct net_device *dev, struct uli526x_board_inf /* A packet sent completed */ db->tx_packet_cnt--; - db->stats.tx_packets++; + dev->stats.tx_packets++; /* Transmit statistic counter */ if ( tdes0 != 0x7fffffff ) { /* printk(DRV_NAME ": tdes0=%x\n", tdes0); */ - db->stats.collisions += (tdes0 >> 3) & 0xf; - db->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; + dev->stats.collisions += (tdes0 >> 3) & 0xf; + dev->stats.tx_bytes += le32_to_cpu(txptr->tdes1) & 0x7ff; if (tdes0 & TDES0_ERR_MASK) { - db->stats.tx_errors++; + dev->stats.tx_errors++; if (tdes0 & 0x0002) { /* UnderRun */ db->tx_fifo_underrun++; if ( !(db->cr6_data & CR6_SFT) ) { @@ -825,13 +829,13 @@ static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info if (rdes0 & 0x8000) { /* This is a error packet */ //printk(DRV_NAME ": rdes0: %lx\n", rdes0); - db->stats.rx_errors++; + dev->stats.rx_errors++; if (rdes0 & 1) - db->stats.rx_fifo_errors++; + dev->stats.rx_fifo_errors++; if (rdes0 & 2) - db->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; if (rdes0 & 0x80) - db->stats.rx_length_errors++; + dev->stats.rx_length_errors++; } if ( !(rdes0 & 0x8000) || @@ -854,8 +858,8 @@ static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); - db->stats.rx_packets++; - db->stats.rx_bytes += rxlen; + dev->stats.rx_packets++; + dev->stats.rx_bytes += rxlen; } else { /* Reuse SKB buffer when the packet is error */ @@ -872,19 +876,6 @@ static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info /* - * Get statistics from driver. - */ - -static struct net_device_stats * uli526x_get_stats(struct net_device *dev) -{ - struct uli526x_board_info *db = netdev_priv(dev); - - ULI526X_DBUG(0, "uli526x_get_stats", 0); - return &db->stats; -} - - -/* * Set ULI526X multicast address */ diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index 022d99af8646..f467bf87817d 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -343,7 +343,18 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static const struct ethtool_ops netdev_ethtool_ops; static int netdev_close(struct net_device *dev); - +static const struct net_device_ops netdev_ops = { + .ndo_open = netdev_open, + .ndo_stop = netdev_close, + .ndo_start_xmit = start_tx, + .ndo_get_stats = get_stats, + .ndo_set_multicast_list = set_rx_mode, + .ndo_do_ioctl = netdev_ioctl, + .ndo_tx_timeout = tx_timeout, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; static int __devinit w840_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -420,14 +431,8 @@ static int __devinit w840_probe1 (struct pci_dev *pdev, np->mii_if.force_media = 1; /* The chip-specific entries in the device structure. */ - dev->open = &netdev_open; - dev->hard_start_xmit = &start_tx; - dev->stop = &netdev_close; - dev->get_stats = &get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &netdev_ioctl; + dev->netdev_ops = &netdev_ops; dev->ethtool_ops = &netdev_ethtool_ops; - dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; i = register_netdev(dev); @@ -1555,7 +1560,7 @@ static void __devexit w840_remove1 (struct pci_dev *pdev) * rtnl_lock, & netif_device_detach after the rtnl_unlock. * - get_stats: * spin_lock_irq(np->lock), doesn't touch hw if not present - * - hard_start_xmit: + * - start_xmit: * synchronize_irq + netif_tx_disable; * - tx_timeout: * netif_device_detach + netif_tx_disable; diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c index 13c8703ecb9f..c2ca9f40e40e 100644 --- a/drivers/net/tulip/xircom_cb.c +++ b/drivers/net/tulip/xircom_cb.c @@ -104,10 +104,8 @@ struct xircom_private { */ spinlock_t lock; - struct pci_dev *pdev; struct net_device *dev; - struct net_device_stats stats; }; @@ -119,7 +117,6 @@ static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev); static int xircom_open(struct net_device *dev); static int xircom_close(struct net_device *dev); static void xircom_up(struct xircom_private *card); -static struct net_device_stats *xircom_get_stats(struct net_device *dev); #ifdef CONFIG_NET_POLL_CONTROLLER static void xircom_poll_controller(struct net_device *dev); #endif @@ -194,6 +191,18 @@ static const struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, }; +static const struct net_device_ops netdev_ops = { + .ndo_open = xircom_open, + .ndo_stop = xircom_close, + .ndo_start_xmit = xircom_start_xmit, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = xircom_poll_controller, +#endif +}; + /* xircom_probe is the code that gets called on device insertion. it sets up the hardware and registers the device to the networklayer. @@ -266,13 +275,7 @@ static int __devinit xircom_probe(struct pci_dev *pdev, const struct pci_device_ read_mac_address(private); setup_descriptors(private); - dev->open = &xircom_open; - dev->hard_start_xmit = &xircom_start_xmit; - dev->stop = &xircom_close; - dev->get_stats = &xircom_get_stats; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = &xircom_poll_controller; -#endif + dev->netdev_ops = &netdev_ops; SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); pci_set_drvdata(pdev, dev); @@ -497,14 +500,6 @@ static int xircom_close(struct net_device *dev) } - -static struct net_device_stats *xircom_get_stats(struct net_device *dev) -{ - struct xircom_private *card = netdev_priv(dev); - return &card->stats; -} - - #ifdef CONFIG_NET_POLL_CONTROLLER static void xircom_poll_controller(struct net_device *dev) { @@ -1193,7 +1188,7 @@ static void investigate_read_descriptor(struct net_device *dev,struct xircom_pri skb = dev_alloc_skb(pkt_len + 2); if (skb == NULL) { - card->stats.rx_dropped++; + dev->stats.rx_dropped++; goto out; } skb_reserve(skb, 2); @@ -1201,8 +1196,8 @@ static void investigate_read_descriptor(struct net_device *dev,struct xircom_pri skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); - card->stats.rx_packets++; - card->stats.rx_bytes += pkt_len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; out: /* give the buffer back to the card */ @@ -1232,16 +1227,16 @@ static void investigate_write_descriptor(struct net_device *dev, struct xircom_p #endif if (status > 0) { /* bit 31 is 0 when done */ if (card->tx_skb[descnr]!=NULL) { - card->stats.tx_bytes += card->tx_skb[descnr]->len; + dev->stats.tx_bytes += card->tx_skb[descnr]->len; dev_kfree_skb_irq(card->tx_skb[descnr]); } card->tx_skb[descnr] = NULL; /* Bit 8 in the status field is 1 if there was a collision */ if (status&(1<<8)) - card->stats.collisions++; + dev->stats.collisions++; card->tx_buffer[4*descnr] = 0; /* descriptor is free again */ netif_wake_queue (dev); - card->stats.tx_packets++; + dev->stats.tx_packets++; } leave("investigate_write_descriptor"); diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 0009f4e34433..3af9a9516ccb 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -2296,6 +2296,19 @@ out: return mode; } +static const struct net_device_ops typhoon_netdev_ops = { + .ndo_open = typhoon_open, + .ndo_stop = typhoon_close, + .ndo_start_xmit = typhoon_start_tx, + .ndo_set_multicast_list = typhoon_set_rx_mode, + .ndo_tx_timeout = typhoon_tx_timeout, + .ndo_get_stats = typhoon_get_stats, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = typhoon_set_mac_address, + .ndo_change_mtu = eth_change_mtu, + .ndo_vlan_rx_register = typhoon_vlan_rx_register, +}; + static int __devinit typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -2495,16 +2508,9 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } /* The chip-specific entries in the device structure. */ - dev->open = typhoon_open; - dev->hard_start_xmit = typhoon_start_tx; - dev->stop = typhoon_close; - dev->set_multicast_list = typhoon_set_rx_mode; - dev->tx_timeout = typhoon_tx_timeout; + dev->netdev_ops = &typhoon_netdev_ops; netif_napi_add(dev, &tp->napi, typhoon_poll, 16); dev->watchdog_timeo = TX_TIMEOUT; - dev->get_stats = typhoon_get_stats; - dev->set_mac_address = typhoon_set_mac_address; - dev->vlan_rx_register = typhoon_vlan_rx_register; SET_ETHTOOL_OPS(dev, &typhoon_ethtool_ops); diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index edd244f3acb5..5b67bbf1987e 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -23,7 +23,7 @@ #include <linux/usb/usbnet.h> /* datasheet: - http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf + http://ptm2.cc.utu.fi/ftp/network/cards/DM9601/From_NET/DM9601-DS-P01-930914.pdf */ /* control requests */ @@ -397,16 +397,24 @@ static void dm9601_set_multicast(struct net_device *net) dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl); } +static void __dm9601_set_mac_address(struct usbnet *dev) +{ + dm_write_async(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr); +} + static int dm9601_set_mac_address(struct net_device *net, void *p) { struct sockaddr *addr = p; struct usbnet *dev = netdev_priv(net); - if (!is_valid_ether_addr(addr->sa_data)) + if (!is_valid_ether_addr(addr->sa_data)) { + dev_err(&net->dev, "not setting invalid mac address %pM\n", + addr->sa_data); return -EINVAL; + } memcpy(net->dev_addr, addr->sa_data, net->addr_len); - dm_write_async(dev, DM_PHY_ADDR, net->addr_len, net->dev_addr); + __dm9601_set_mac_address(dev); return 0; } @@ -414,6 +422,7 @@ static int dm9601_set_mac_address(struct net_device *net, void *p) static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) { int ret; + u8 mac[ETH_ALEN]; ret = usbnet_get_endpoints(dev, intf); if (ret) @@ -438,12 +447,24 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) udelay(20); /* read MAC */ - if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr) < 0) { + if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, mac) < 0) { printk(KERN_ERR "Error reading MAC address\n"); ret = -ENODEV; goto out; } + /* + * Overwrite the auto-generated address only with good ones. + */ + if (is_valid_ether_addr(mac)) + memcpy(dev->net->dev_addr, mac, ETH_ALEN); + else { + printk(KERN_WARNING + "dm9601: No valid MAC address in EEPROM, using %pM\n", + dev->net->dev_addr); + __dm9601_set_mac_address(dev); + } + /* power up phy */ dm_write_reg(dev, DM_GPR_CTRL, 1); dm_write_reg(dev, DM_GPR_DATA, 0); diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 2ee034f70d1c..7cb10a0a5316 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -251,7 +251,6 @@ struct kaweth_device struct net_device_stats stats; }; - /**************************************************************** * kaweth_control ****************************************************************/ @@ -283,9 +282,9 @@ static int kaweth_control(struct kaweth_device *kaweth, dr->bRequestType= requesttype; dr->bRequest = request; - dr->wValue = cpu_to_le16p(&value); - dr->wIndex = cpu_to_le16p(&index); - dr->wLength = cpu_to_le16p(&size); + dr->wValue = cpu_to_le16(value); + dr->wIndex = cpu_to_le16(index); + dr->wLength = cpu_to_le16(size); return kaweth_internal_control_msg(kaweth->dev, pipe, @@ -975,6 +974,17 @@ static int kaweth_resume(struct usb_interface *intf) /**************************************************************** * kaweth_probe ****************************************************************/ + + +static const struct net_device_ops kaweth_netdev_ops = { + .ndo_open = kaweth_open, + .ndo_stop = kaweth_close, + .ndo_start_xmit = kaweth_start_xmit, + .ndo_tx_timeout = kaweth_tx_timeout, + .ndo_set_multicast_list = kaweth_set_rx_mode, + .ndo_get_stats = kaweth_netdev_stats, +}; + static int kaweth_probe( struct usb_interface *intf, const struct usb_device_id *id /* from id_table */ @@ -1147,22 +1157,13 @@ err_fw: memcpy(netdev->dev_addr, &kaweth->configuration.hw_addr, sizeof(kaweth->configuration.hw_addr)); - netdev->open = kaweth_open; - netdev->stop = kaweth_close; - + netdev->netdev_ops = &kaweth_netdev_ops; netdev->watchdog_timeo = KAWETH_TX_TIMEOUT; - netdev->tx_timeout = kaweth_tx_timeout; - - netdev->hard_start_xmit = kaweth_start_xmit; - netdev->set_multicast_list = kaweth_set_rx_mode; - netdev->get_stats = kaweth_netdev_stats; netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size); SET_ETHTOOL_OPS(netdev, &ops); /* kaweth is zeroed as part of alloc_netdev */ - INIT_DELAYED_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl); - usb_set_intfdata(intf, kaweth); #if 0 diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 166880c113d6..a8228d87c8cf 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -93,6 +93,7 @@ module_param (msg_level, int, 0); MODULE_PARM_DESC (msg_level, "Override default message level"); MODULE_DEVICE_TABLE(usb, pegasus_ids); +static const struct net_device_ops pegasus_netdev_ops; static int update_eth_regs_async(pegasus_t *); /* Aargh!!! I _really_ hate such tweaks */ @@ -150,8 +151,8 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, pegasus->dr.bRequestType = PEGASUS_REQT_READ; pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS; pegasus->dr.wValue = cpu_to_le16(0); - pegasus->dr.wIndex = cpu_to_le16p(&indx); - pegasus->dr.wLength = cpu_to_le16p(&size); + pegasus->dr.wIndex = cpu_to_le16(indx); + pegasus->dr.wLength = cpu_to_le16(size); pegasus->ctrl_urb->transfer_buffer_length = size; usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, @@ -208,8 +209,8 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; pegasus->dr.wValue = cpu_to_le16(0); - pegasus->dr.wIndex = cpu_to_le16p(&indx); - pegasus->dr.wLength = cpu_to_le16p(&size); + pegasus->dr.wIndex = cpu_to_le16(indx); + pegasus->dr.wLength = cpu_to_le16(size); pegasus->ctrl_urb->transfer_buffer_length = size; usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb, @@ -261,7 +262,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; pegasus->dr.wValue = cpu_to_le16(data); - pegasus->dr.wIndex = cpu_to_le16p(&indx); + pegasus->dr.wIndex = cpu_to_le16(indx); pegasus->dr.wLength = cpu_to_le16(1); pegasus->ctrl_urb->transfer_buffer_length = 1; @@ -476,7 +477,7 @@ static inline void get_node_id(pegasus_t * pegasus, __u8 * id) for (i = 0; i < 3; i++) { read_eprom_word(pegasus, i, &w16); - ((__le16 *) id)[i] = cpu_to_le16p(&w16); + ((__le16 *) id)[i] = cpu_to_le16(w16); } } @@ -1360,14 +1361,10 @@ static int pegasus_probe(struct usb_interface *intf, pegasus->intf = intf; pegasus->usb = dev; pegasus->net = net; - net->open = pegasus_open; - net->stop = pegasus_close; + + net->watchdog_timeo = PEGASUS_TX_TIMEOUT; - net->tx_timeout = pegasus_tx_timeout; - net->do_ioctl = pegasus_ioctl; - net->hard_start_xmit = pegasus_start_xmit; - net->set_multicast_list = pegasus_set_multicast; - net->get_stats = pegasus_netdev_stats; + net->netdev_ops = &pegasus_netdev_ops; SET_ETHTOOL_OPS(net, &ops); pegasus->mii.dev = net; pegasus->mii.mdio_read = mdio_read; @@ -1482,6 +1479,16 @@ static int pegasus_resume (struct usb_interface *intf) return 0; } +static const struct net_device_ops pegasus_netdev_ops = { + .ndo_open = pegasus_open, + .ndo_stop = pegasus_close, + .ndo_do_ioctl = pegasus_ioctl, + .ndo_start_xmit = pegasus_start_xmit, + .ndo_set_multicast_list = pegasus_set_multicast, + .ndo_get_stats = pegasus_netdev_stats, + .ndo_tx_timeout = pegasus_tx_timeout, +}; + static struct usb_driver pegasus_driver = { .name = driver_name, .probe = pegasus_probe, diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b7004ff36451..43f6523c40be 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -624,6 +624,18 @@ static int virtnet_change_mtu(struct net_device *dev, int new_mtu) return 0; } +static const struct net_device_ops virtnet_netdev = { + .ndo_open = virtnet_open, + .ndo_stop = virtnet_close, + .ndo_start_xmit = start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = virtnet_change_mtu, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = virtnet_netpoll, +#endif +}; + static int virtnet_probe(struct virtio_device *vdev) { int err; @@ -636,14 +648,8 @@ static int virtnet_probe(struct virtio_device *vdev) return -ENOMEM; /* Set up network device as normal. */ - dev->open = virtnet_open; - dev->stop = virtnet_close; - dev->hard_start_xmit = start_xmit; - dev->change_mtu = virtnet_change_mtu; + dev->netdev_ops = &virtnet_netdev; dev->features = NETIF_F_HIGHDMA; -#ifdef CONFIG_NET_POLL_CONTROLLER - dev->poll_controller = virtnet_netpoll; -#endif SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops); SET_NETDEV_DEV(dev, &vdev->dev); diff --git a/drivers/net/wimax/Kconfig b/drivers/net/wimax/Kconfig new file mode 100644 index 000000000000..565018ec1e3b --- /dev/null +++ b/drivers/net/wimax/Kconfig @@ -0,0 +1,17 @@ +# +# WiMAX LAN device drivers configuration +# + + +comment "Enable WiMAX (Networking options) to see the WiMAX drivers" + depends on WIMAX = n + +if WIMAX + +menu "WiMAX Wireless Broadband devices" + +source "drivers/net/wimax/i2400m/Kconfig" + +endmenu + +endif diff --git a/drivers/net/wimax/Makefile b/drivers/net/wimax/Makefile new file mode 100644 index 000000000000..992bc02bc016 --- /dev/null +++ b/drivers/net/wimax/Makefile @@ -0,0 +1,5 @@ + +obj-$(CONFIG_WIMAX_I2400M) += i2400m/ + +# (from Sam Ravnborg) force kbuild to create built-in.o +obj- := dummy.o diff --git a/drivers/net/wimax/i2400m/Kconfig b/drivers/net/wimax/i2400m/Kconfig new file mode 100644 index 000000000000..d623b3d99a4b --- /dev/null +++ b/drivers/net/wimax/i2400m/Kconfig @@ -0,0 +1,49 @@ + +config WIMAX_I2400M + tristate + depends on WIMAX + select FW_LOADER + +comment "Enable USB support to see WiMAX USB drivers" + depends on USB = n + +comment "Enable MMC support to see WiMAX SDIO drivers" + depends on MMC = n + +config WIMAX_I2400M_USB + tristate "Intel Wireless WiMAX Connection 2400 over USB (including 5x50)" + depends on WIMAX && USB + select WIMAX_I2400M + help + Select if you have a device based on the Intel WiMAX + Connection 2400 over USB (like any of the Intel Wireless + WiMAX/WiFi Link 5x50 series). + + If unsure, it is safe to select M (module). + +config WIMAX_I2400M_SDIO + tristate "Intel Wireless WiMAX Connection 2400 over SDIO" + depends on WIMAX && MMC + select WIMAX_I2400M + help + Select if you have a device based on the Intel WiMAX + Connection 2400 over SDIO. + + If unsure, it is safe to select M (module). + +config WIMAX_I2400M_DEBUG_LEVEL + int "WiMAX i2400m debug level" + depends on WIMAX_I2400M + default 8 + help + + Select the maximum debug verbosity level to be compiled into + the WiMAX i2400m driver code. + + By default, this is disabled at runtime and can be + selectively enabled at runtime for different parts of the + code using the sysfs debug-levels file. + + If set at zero, this will compile out all the debug code. + + It is recommended that it is left at 8. diff --git a/drivers/net/wimax/i2400m/Makefile b/drivers/net/wimax/i2400m/Makefile new file mode 100644 index 000000000000..1696e936cf5a --- /dev/null +++ b/drivers/net/wimax/i2400m/Makefile @@ -0,0 +1,29 @@ + +obj-$(CONFIG_WIMAX_I2400M) += i2400m.o +obj-$(CONFIG_WIMAX_I2400M_USB) += i2400m-usb.o +obj-$(CONFIG_WIMAX_I2400M_SDIO) += i2400m-sdio.o + +i2400m-y := \ + control.o \ + driver.o \ + fw.o \ + op-rfkill.o \ + netdev.o \ + tx.o \ + rx.o + +i2400m-$(CONFIG_DEBUG_FS) += debugfs.o + +i2400m-usb-y := \ + usb-fw.o \ + usb-notif.o \ + usb-tx.o \ + usb-rx.o \ + usb.o + + +i2400m-sdio-y := \ + sdio.o \ + sdio-tx.o \ + sdio-fw.o \ + sdio-rx.o diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c new file mode 100644 index 000000000000..d3d37fed6893 --- /dev/null +++ b/drivers/net/wimax/i2400m/control.c @@ -0,0 +1,1291 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Miscellaneous control functions for managing the device + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Initial implementation + * + * This is a collection of functions used to control the device (plus + * a few helpers). + * + * There are utilities for handling TLV buffers, hooks on the device's + * reports to act on device changes of state [i2400m_report_hook()], + * on acks to commands [i2400m_msg_ack_hook()], a helper for sending + * commands to the device and blocking until a reply arrives + * [i2400m_msg_to_dev()], a few high level commands for manipulating + * the device state, powersving mode and configuration plus the + * routines to setup the device once communication is stablished with + * it [i2400m_dev_initialize()]. + * + * ROADMAP + * + * i2400m_dev_initalize() Called by i2400m_dev_start() + * i2400m_set_init_config() + * i2400m_firmware_check() + * i2400m_cmd_get_state() + * i2400m_dev_shutdown() Called by i2400m_dev_stop() + * i2400m->bus_reset() + * + * i2400m_{cmd,get,set}_*() + * i2400m_msg_to_dev() + * i2400m_msg_check_status() + * + * i2400m_report_hook() Called on reception of an event + * i2400m_report_state_hook() + * i2400m_tlv_buffer_walk() + * i2400m_tlv_match() + * i2400m_report_tlv_system_state() + * i2400m_report_tlv_rf_switches_status() + * i2400m_report_tlv_media_status() + * i2400m_cmd_enter_powersave() + * + * i2400m_msg_ack_hook() Called on reception of a reply to a + * command, get or set + */ + +#include <stdarg.h> +#include "i2400m.h" +#include <linux/kernel.h> +#include <linux/wimax/i2400m.h> + + +#define D_SUBMODULE control +#include "debug-levels.h" + + +/* + * Return if a TLV is of a give type and size + * + * @tlv_hdr: pointer to the TLV + * @tlv_type: type of the TLV we are looking for + * @tlv_size: expected size of the TLV we are looking for (if -1, + * don't check the size). This includes the header + * Returns: 0 if the TLV matches + * < 0 if it doesn't match at all + * > 0 total TLV + payload size, if the type matches, but not + * the size + */ +static +ssize_t i2400m_tlv_match(const struct i2400m_tlv_hdr *tlv, + enum i2400m_tlv tlv_type, ssize_t tlv_size) +{ + if (le16_to_cpu(tlv->type) != tlv_type) /* Not our type? skip */ + return -1; + if (tlv_size != -1 + && le16_to_cpu(tlv->length) + sizeof(*tlv) != tlv_size) { + size_t size = le16_to_cpu(tlv->length) + sizeof(*tlv); + printk(KERN_WARNING "W: tlv type 0x%x mismatched because of " + "size (got %zu vs %zu expected)\n", + tlv_type, size, tlv_size); + return size; + } + return 0; +} + + +/* + * Given a buffer of TLVs, iterate over them + * + * @i2400m: device instance + * @tlv_buf: pointer to the beginning of the TLV buffer + * @buf_size: buffer size in bytes + * @tlv_pos: seek position; this is assumed to be a pointer returned + * by i2400m_tlv_buffer_walk() [and thus, validated]. The + * TLV returned will be the one following this one. + * + * Usage: + * + * tlv_itr = NULL; + * while (tlv_itr = i2400m_tlv_buffer_walk(i2400m, buf, size, tlv_itr)) { + * ... + * // Do stuff with tlv_itr, DON'T MODIFY IT + * ... + * } + */ +static +const struct i2400m_tlv_hdr *i2400m_tlv_buffer_walk( + struct i2400m *i2400m, + const void *tlv_buf, size_t buf_size, + const struct i2400m_tlv_hdr *tlv_pos) +{ + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_tlv_hdr *tlv_top = tlv_buf + buf_size; + size_t offset, length, avail_size; + unsigned type; + + if (tlv_pos == NULL) /* Take the first one? */ + tlv_pos = tlv_buf; + else /* Nope, the next one */ + tlv_pos = (void *) tlv_pos + + le16_to_cpu(tlv_pos->length) + sizeof(*tlv_pos); + if (tlv_pos == tlv_top) { /* buffer done */ + tlv_pos = NULL; + goto error_beyond_end; + } + if (tlv_pos > tlv_top) { + tlv_pos = NULL; + WARN_ON(1); + goto error_beyond_end; + } + offset = (void *) tlv_pos - (void *) tlv_buf; + avail_size = buf_size - offset; + if (avail_size < sizeof(*tlv_pos)) { + dev_err(dev, "HW BUG? tlv_buf %p [%zu bytes], tlv @%zu: " + "short header\n", tlv_buf, buf_size, offset); + goto error_short_header; + } + type = le16_to_cpu(tlv_pos->type); + length = le16_to_cpu(tlv_pos->length); + if (avail_size < sizeof(*tlv_pos) + length) { + dev_err(dev, "HW BUG? tlv_buf %p [%zu bytes], " + "tlv type 0x%04x @%zu: " + "short data (%zu bytes vs %zu needed)\n", + tlv_buf, buf_size, type, offset, avail_size, + sizeof(*tlv_pos) + length); + goto error_short_header; + } +error_short_header: +error_beyond_end: + return tlv_pos; +} + + +/* + * Find a TLV in a buffer of sequential TLVs + * + * @i2400m: device descriptor + * @tlv_hdr: pointer to the first TLV in the sequence + * @size: size of the buffer in bytes; all TLVs are assumed to fit + * fully in the buffer (otherwise we'll complain). + * @tlv_type: type of the TLV we are looking for + * @tlv_size: expected size of the TLV we are looking for (if -1, + * don't check the size). This includes the header + * + * Returns: NULL if the TLV is not found, otherwise a pointer to + * it. If the sizes don't match, an error is printed and NULL + * returned. + */ +static +const struct i2400m_tlv_hdr *i2400m_tlv_find( + struct i2400m *i2400m, + const struct i2400m_tlv_hdr *tlv_hdr, size_t size, + enum i2400m_tlv tlv_type, ssize_t tlv_size) +{ + ssize_t match; + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_tlv_hdr *tlv = NULL; + while ((tlv = i2400m_tlv_buffer_walk(i2400m, tlv_hdr, size, tlv))) { + match = i2400m_tlv_match(tlv, tlv_type, tlv_size); + if (match == 0) /* found it :) */ + break; + if (match > 0) + dev_warn(dev, "TLV type 0x%04x found with size " + "mismatch (%zu vs %zu needed)\n", + tlv_type, match, tlv_size); + } + return tlv; +} + + +static const struct +{ + char *msg; + int errno; +} ms_to_errno[I2400M_MS_MAX] = { + [I2400M_MS_DONE_OK] = { "", 0 }, + [I2400M_MS_DONE_IN_PROGRESS] = { "", 0 }, + [I2400M_MS_INVALID_OP] = { "invalid opcode", -ENOSYS }, + [I2400M_MS_BAD_STATE] = { "invalid state", -EILSEQ }, + [I2400M_MS_ILLEGAL_VALUE] = { "illegal value", -EINVAL }, + [I2400M_MS_MISSING_PARAMS] = { "missing parameters", -ENOMSG }, + [I2400M_MS_VERSION_ERROR] = { "bad version", -EIO }, + [I2400M_MS_ACCESSIBILITY_ERROR] = { "accesibility error", -EIO }, + [I2400M_MS_BUSY] = { "busy", -EBUSY }, + [I2400M_MS_CORRUPTED_TLV] = { "corrupted TLV", -EILSEQ }, + [I2400M_MS_UNINITIALIZED] = { "not unitialized", -EILSEQ }, + [I2400M_MS_UNKNOWN_ERROR] = { "unknown error", -EIO }, + [I2400M_MS_PRODUCTION_ERROR] = { "production error", -EIO }, + [I2400M_MS_NO_RF] = { "no RF", -EIO }, + [I2400M_MS_NOT_READY_FOR_POWERSAVE] = + { "not ready for powersave", -EACCES }, + [I2400M_MS_THERMAL_CRITICAL] = { "thermal critical", -EL3HLT }, +}; + + +/* + * i2400m_msg_check_status - translate a message's status code + * + * @i2400m: device descriptor + * @l3l4_hdr: message header + * @strbuf: buffer to place a formatted error message (unless NULL). + * @strbuf_size: max amount of available space; larger messages will + * be truncated. + * + * Returns: errno code corresponding to the status code in @l3l4_hdr + * and a message in @strbuf describing the error. + */ +int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *l3l4_hdr, + char *strbuf, size_t strbuf_size) +{ + int result; + enum i2400m_ms status = le16_to_cpu(l3l4_hdr->status); + const char *str; + + if (status == 0) + return 0; + if (status > ARRAY_SIZE(ms_to_errno)) { + str = "unknown status code"; + result = -EBADR; + } else { + str = ms_to_errno[status].msg; + result = ms_to_errno[status].errno; + } + if (strbuf) + snprintf(strbuf, strbuf_size, "%s (%d)", str, status); + return result; +} + + +/* + * Act on a TLV System State reported by the device + * + * @i2400m: device descriptor + * @ss: validated System State TLV + */ +static +void i2400m_report_tlv_system_state(struct i2400m *i2400m, + const struct i2400m_tlv_system_state *ss) +{ + struct device *dev = i2400m_dev(i2400m); + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + enum i2400m_system_state i2400m_state = le32_to_cpu(ss->state); + + d_fnstart(3, dev, "(i2400m %p ss %p [%u])\n", i2400m, ss, i2400m_state); + + if (unlikely(i2400m->ready == 0)) /* act if up */ + goto out; + if (i2400m->state != i2400m_state) { + i2400m->state = i2400m_state; + wake_up_all(&i2400m->state_wq); + } + switch (i2400m_state) { + case I2400M_SS_UNINITIALIZED: + case I2400M_SS_INIT: + case I2400M_SS_CONFIG: + case I2400M_SS_PRODUCTION: + wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); + break; + + case I2400M_SS_RF_OFF: + case I2400M_SS_RF_SHUTDOWN: + wimax_state_change(wimax_dev, WIMAX_ST_RADIO_OFF); + break; + + case I2400M_SS_READY: + case I2400M_SS_STANDBY: + case I2400M_SS_SLEEPACTIVE: + wimax_state_change(wimax_dev, WIMAX_ST_READY); + break; + + case I2400M_SS_CONNECTING: + case I2400M_SS_WIMAX_CONNECTED: + wimax_state_change(wimax_dev, WIMAX_ST_READY); + break; + + case I2400M_SS_SCAN: + case I2400M_SS_OUT_OF_ZONE: + wimax_state_change(wimax_dev, WIMAX_ST_SCANNING); + break; + + case I2400M_SS_IDLE: + d_printf(1, dev, "entering BS-negotiated idle mode\n"); + case I2400M_SS_DISCONNECTING: + case I2400M_SS_DATA_PATH_CONNECTED: + wimax_state_change(wimax_dev, WIMAX_ST_CONNECTED); + break; + + default: + /* Huh? just in case, shut it down */ + dev_err(dev, "HW BUG? unknown state %u: shutting down\n", + i2400m_state); + i2400m->bus_reset(i2400m, I2400M_RT_WARM); + break; + }; +out: + d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n", + i2400m, ss, i2400m_state); +} + + +/* + * Parse and act on a TLV Media Status sent by the device + * + * @i2400m: device descriptor + * @ms: validated Media Status TLV + * + * This will set the carrier up on down based on the device's link + * report. This is done asides of what the WiMAX stack does based on + * the device's state as sometimes we need to do a link-renew (the BS + * wants us to renew a DHCP lease, for example). + * + * In fact, doc says that everytime we get a link-up, we should do a + * DHCP negotiation... + */ +static +void i2400m_report_tlv_media_status(struct i2400m *i2400m, + const struct i2400m_tlv_media_status *ms) +{ + struct device *dev = i2400m_dev(i2400m); + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + struct net_device *net_dev = wimax_dev->net_dev; + enum i2400m_media_status status = le32_to_cpu(ms->media_status); + + d_fnstart(3, dev, "(i2400m %p ms %p [%u])\n", i2400m, ms, status); + + if (unlikely(i2400m->ready == 0)) /* act if up */ + goto out; + switch (status) { + case I2400M_MEDIA_STATUS_LINK_UP: + netif_carrier_on(net_dev); + break; + case I2400M_MEDIA_STATUS_LINK_DOWN: + netif_carrier_off(net_dev); + break; + /* + * This is the network telling us we need to retrain the DHCP + * lease -- so far, we are trusting the WiMAX Network Service + * in user space to pick this up and poke the DHCP client. + */ + case I2400M_MEDIA_STATUS_LINK_RENEW: + netif_carrier_on(net_dev); + break; + default: + dev_err(dev, "HW BUG? unknown media status %u\n", + status); + }; +out: + d_fnend(3, dev, "(i2400m %p ms %p [%u]) = void\n", + i2400m, ms, status); +} + + +/* + * Parse a 'state report' and extract carrier on/off information + * + * @i2400m: device descriptor + * @l3l4_hdr: pointer to message; it has been already validated for + * consistent size. + * @size: size of the message (header + payload). The header length + * declaration is assumed to be congruent with @size (as in + * sizeof(*l3l4_hdr) + l3l4_hdr->length == size) + * + * Extract from the report state the system state TLV and infer from + * there if we have a carrier or not. Update our local state and tell + * netdev. + * + * When setting the carrier, it's fine to set OFF twice (for example), + * as netif_carrier_off() will not generate two OFF events (just on + * the transitions). + */ +static +void i2400m_report_state_hook(struct i2400m *i2400m, + const struct i2400m_l3l4_hdr *l3l4_hdr, + size_t size, const char *tag) +{ + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_tlv_hdr *tlv; + const struct i2400m_tlv_system_state *ss; + const struct i2400m_tlv_rf_switches_status *rfss; + const struct i2400m_tlv_media_status *ms; + size_t tlv_size = le16_to_cpu(l3l4_hdr->length); + + d_fnstart(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s)\n", + i2400m, l3l4_hdr, size, tag); + tlv = NULL; + + while ((tlv = i2400m_tlv_buffer_walk(i2400m, &l3l4_hdr->pl, + tlv_size, tlv))) { + if (0 == i2400m_tlv_match(tlv, I2400M_TLV_SYSTEM_STATE, + sizeof(*ss))) { + ss = container_of(tlv, typeof(*ss), hdr); + d_printf(2, dev, "%s: system state TLV " + "found (0x%04x), state 0x%08x\n", + tag, I2400M_TLV_SYSTEM_STATE, + le32_to_cpu(ss->state)); + i2400m_report_tlv_system_state(i2400m, ss); + } + if (0 == i2400m_tlv_match(tlv, I2400M_TLV_RF_STATUS, + sizeof(*rfss))) { + rfss = container_of(tlv, typeof(*rfss), hdr); + d_printf(2, dev, "%s: RF status TLV " + "found (0x%04x), sw 0x%02x hw 0x%02x\n", + tag, I2400M_TLV_RF_STATUS, + le32_to_cpu(rfss->sw_rf_switch), + le32_to_cpu(rfss->hw_rf_switch)); + i2400m_report_tlv_rf_switches_status(i2400m, rfss); + } + if (0 == i2400m_tlv_match(tlv, I2400M_TLV_MEDIA_STATUS, + sizeof(*ms))) { + ms = container_of(tlv, typeof(*ms), hdr); + d_printf(2, dev, "%s: Media Status TLV: %u\n", + tag, le32_to_cpu(ms->media_status)); + i2400m_report_tlv_media_status(i2400m, ms); + } + } + d_fnend(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s) = void\n", + i2400m, l3l4_hdr, size, tag); +} + + +/* + * i2400m_report_hook - (maybe) act on a report + * + * @i2400m: device descriptor + * @l3l4_hdr: pointer to message; it has been already validated for + * consistent size. + * @size: size of the message (header + payload). The header length + * declaration is assumed to be congruent with @size (as in + * sizeof(*l3l4_hdr) + l3l4_hdr->length == size) + * + * Extract information we might need (like carrien on/off) from a + * device report. + */ +void i2400m_report_hook(struct i2400m *i2400m, + const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size) +{ + struct device *dev = i2400m_dev(i2400m); + unsigned msg_type; + + d_fnstart(3, dev, "(i2400m %p l3l4_hdr %p size %zu)\n", + i2400m, l3l4_hdr, size); + /* Chew on the message, we might need some information from + * here */ + msg_type = le16_to_cpu(l3l4_hdr->type); + switch (msg_type) { + case I2400M_MT_REPORT_STATE: /* carrier detection... */ + i2400m_report_state_hook(i2400m, + l3l4_hdr, size, "REPORT STATE"); + break; + /* If the device is ready for power save, then ask it to do + * it. */ + case I2400M_MT_REPORT_POWERSAVE_READY: /* zzzzz */ + if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) { + d_printf(1, dev, "ready for powersave, requesting\n"); + i2400m_cmd_enter_powersave(i2400m); + } + break; + }; + d_fnend(3, dev, "(i2400m %p l3l4_hdr %p size %zu) = void\n", + i2400m, l3l4_hdr, size); +} + + +/* + * i2400m_msg_ack_hook - process cmd/set/get ack for internal status + * + * @i2400m: device descriptor + * @l3l4_hdr: pointer to message; it has been already validated for + * consistent size. + * @size: size of the message + * + * Extract information we might need from acks to commands and act on + * it. This is akin to i2400m_report_hook(). Note most of this + * processing should be done in the function that calls the + * command. This is here for some cases where it can't happen... + */ +void i2400m_msg_ack_hook(struct i2400m *i2400m, + const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + unsigned ack_type, ack_status; + char strerr[32]; + + /* Chew on the message, we might need some information from + * here */ + ack_type = le16_to_cpu(l3l4_hdr->type); + ack_status = le16_to_cpu(l3l4_hdr->status); + switch (ack_type) { + case I2400M_MT_CMD_ENTER_POWERSAVE: + /* This is just left here for the sake of example, as + * the processing is done somewhere else. */ + if (0) { + result = i2400m_msg_check_status( + l3l4_hdr, strerr, sizeof(strerr)); + if (result >= 0) + d_printf(1, dev, "ready for power save: %zd\n", + size); + } + break; + }; + return; +} + + +/* + * i2400m_msg_size_check() - verify message size and header are congruent + * + * It is ok if the total message size is larger than the expected + * size, as there can be padding. + */ +int i2400m_msg_size_check(struct i2400m *i2400m, + const struct i2400m_l3l4_hdr *l3l4_hdr, + size_t msg_size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + size_t expected_size; + d_fnstart(4, dev, "(i2400m %p l3l4_hdr %p msg_size %zu)\n", + i2400m, l3l4_hdr, msg_size); + if (msg_size < sizeof(*l3l4_hdr)) { + dev_err(dev, "bad size for message header " + "(expected at least %zu, got %zu)\n", + (size_t) sizeof(*l3l4_hdr), msg_size); + result = -EIO; + goto error_hdr_size; + } + expected_size = le16_to_cpu(l3l4_hdr->length) + sizeof(*l3l4_hdr); + if (msg_size < expected_size) { + dev_err(dev, "bad size for message code 0x%04x (expected %zu, " + "got %zu)\n", le16_to_cpu(l3l4_hdr->type), + expected_size, msg_size); + result = -EIO; + } else + result = 0; +error_hdr_size: + d_fnend(4, dev, + "(i2400m %p l3l4_hdr %p msg_size %zu) = %d\n", + i2400m, l3l4_hdr, msg_size, result); + return result; +} + + + +/* + * Cancel a wait for a command ACK + * + * @i2400m: device descriptor + * @code: [negative] errno code to cancel with (don't use + * -EINPROGRESS) + * + * If there is an ack already filled out, free it. + */ +void i2400m_msg_to_dev_cancel_wait(struct i2400m *i2400m, int code) +{ + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&i2400m->rx_lock, flags); + ack_skb = i2400m->ack_skb; + if (ack_skb && !IS_ERR(ack_skb)) + kfree(ack_skb); + i2400m->ack_skb = ERR_PTR(code); + spin_unlock_irqrestore(&i2400m->rx_lock, flags); +} + + +/** + * i2400m_msg_to_dev - Send a control message to the device and get a response + * + * @i2400m: device descriptor + * + * @msg_skb: an skb * + * + * @buf: pointer to the buffer containing the message to be sent; it + * has to start with a &struct i2400M_l3l4_hdr and then + * followed by the payload. Once this function returns, the + * buffer can be reused. + * + * @buf_len: buffer size + * + * Returns: + * + * Pointer to skb containing the ack message. You need to check the + * pointer with IS_ERR(), as it might be an error code. Error codes + * could happen because: + * + * - the message wasn't formatted correctly + * - couldn't send the message + * - failed waiting for a response + * - the ack message wasn't formatted correctly + * + * The returned skb has been allocated with wimax_msg_to_user_alloc(), + * it contains the reponse in a netlink attribute and is ready to be + * passed up to user space with wimax_msg_to_user_send(). To access + * the payload and its length, use wimax_msg_{data,len}() on the skb. + * + * The skb has to be freed with kfree_skb() once done. + * + * Description: + * + * This function delivers a message/command to the device and waits + * for an ack to be received. The format is described in + * linux/wimax/i2400m.h. In summary, a command/get/set is followed by an + * ack. + * + * This function will not check the ack status, that's left up to the + * caller. Once done with the ack skb, it has to be kfree_skb()ed. + * + * The i2400m handles only one message at the same time, thus we need + * the mutex to exclude other players. + * + * We write the message and then wait for an answer to come back. The + * RX path intercepts control messages and handles them in + * i2400m_rx_ctl(). Reports (notifications) are (maybe) processed + * locally and then forwarded (as needed) to user space on the WiMAX + * stack message pipe. Acks are saved and passed back to us through an + * skb in i2400m->ack_skb which is ready to be given to generic + * netlink if need be. + */ +struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, + const void *buf, size_t buf_len) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_l3l4_hdr *msg_l3l4_hdr; + struct sk_buff *ack_skb; + const struct i2400m_l3l4_hdr *ack_l3l4_hdr; + size_t ack_len; + int ack_timeout; + unsigned msg_type; + unsigned long flags; + + d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n", + i2400m, buf, buf_len); + + if (i2400m->boot_mode) + return ERR_PTR(-ENODEV); + + msg_l3l4_hdr = buf; + /* Check msg & payload consistency */ + result = i2400m_msg_size_check(i2400m, msg_l3l4_hdr, buf_len); + if (result < 0) + goto error_bad_msg; + msg_type = le16_to_cpu(msg_l3l4_hdr->type); + d_printf(1, dev, "CMD/GET/SET 0x%04x %zu bytes\n", + msg_type, buf_len); + d_dump(2, dev, buf, buf_len); + + /* Setup the completion, ack_skb ("we are waiting") and send + * the message to the device */ + mutex_lock(&i2400m->msg_mutex); + spin_lock_irqsave(&i2400m->rx_lock, flags); + i2400m->ack_skb = ERR_PTR(-EINPROGRESS); + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + init_completion(&i2400m->msg_completion); + result = i2400m_tx(i2400m, buf, buf_len, I2400M_PT_CTRL); + if (result < 0) { + dev_err(dev, "can't send message 0x%04x: %d\n", + le16_to_cpu(msg_l3l4_hdr->type), result); + goto error_tx; + } + + /* Some commands take longer to execute because of crypto ops, + * so we give them some more leeway on timeout */ + switch (msg_type) { + case I2400M_MT_GET_TLS_OPERATION_RESULT: + case I2400M_MT_CMD_SEND_EAP_RESPONSE: + ack_timeout = 5 * HZ; + break; + default: + ack_timeout = HZ; + }; + + /* The RX path in rx.c will put any response for this message + * in i2400m->ack_skb and wake us up. If we cancel the wait, + * we need to change the value of i2400m->ack_skb to something + * not -EINPROGRESS so RX knows there is no one waiting. */ + result = wait_for_completion_interruptible_timeout( + &i2400m->msg_completion, ack_timeout); + if (result == 0) { + dev_err(dev, "timeout waiting for reply to message 0x%04x\n", + msg_type); + result = -ETIMEDOUT; + i2400m_msg_to_dev_cancel_wait(i2400m, result); + goto error_wait_for_completion; + } else if (result < 0) { + dev_err(dev, "error waiting for reply to message 0x%04x: %d\n", + msg_type, result); + i2400m_msg_to_dev_cancel_wait(i2400m, result); + goto error_wait_for_completion; + } + + /* Pull out the ack data from i2400m->ack_skb -- see if it is + * an error and act accordingly */ + spin_lock_irqsave(&i2400m->rx_lock, flags); + ack_skb = i2400m->ack_skb; + if (IS_ERR(ack_skb)) + result = PTR_ERR(ack_skb); + else + result = 0; + i2400m->ack_skb = NULL; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + if (result < 0) + goto error_ack_status; + ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len); + + /* Check the ack and deliver it if it is ok */ + result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len); + if (result < 0) { + dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n", + msg_type, result); + goto error_bad_ack_len; + } + if (msg_type != le16_to_cpu(ack_l3l4_hdr->type)) { + dev_err(dev, "HW BUG? bad reply 0x%04x to message 0x%04x\n", + le16_to_cpu(ack_l3l4_hdr->type), msg_type); + result = -EIO; + goto error_bad_ack_type; + } + i2400m_msg_ack_hook(i2400m, ack_l3l4_hdr, ack_len); + mutex_unlock(&i2400m->msg_mutex); + d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %p\n", + i2400m, buf, buf_len, ack_skb); + return ack_skb; + +error_bad_ack_type: +error_bad_ack_len: + kfree_skb(ack_skb); +error_ack_status: +error_wait_for_completion: +error_tx: + mutex_unlock(&i2400m->msg_mutex); +error_bad_msg: + d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %d\n", + i2400m, buf, buf_len, result); + return ERR_PTR(result); +} + + +/* + * Definitions for the Enter Power Save command + * + * The Enter Power Save command requests the device to go into power + * saving mode. The device will ack or nak the command depending on it + * being ready for it. If it acks, we tell the USB subsystem to + * + * As well, the device might request to go into power saving mode by + * sending a report (REPORT_POWERSAVE_READY), in which case, we issue + * this command. The hookups in the RX coder allow + */ +enum { + I2400M_WAKEUP_ENABLED = 0x01, + I2400M_WAKEUP_DISABLED = 0x02, + I2400M_TLV_TYPE_WAKEUP_MODE = 144, +}; + +struct i2400m_cmd_enter_power_save { + struct i2400m_l3l4_hdr hdr; + struct i2400m_tlv_hdr tlv; + __le32 val; +} __attribute__((packed)); + + +/* + * Request entering power save + * + * This command is (mainly) executed when the device indicates that it + * is ready to go into powersave mode via a REPORT_POWERSAVE_READY. + */ +int i2400m_cmd_enter_powersave(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct i2400m_cmd_enter_power_save *cmd; + char strerr[32]; + + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_ENTER_POWERSAVE); + cmd->hdr.length = cpu_to_le16(sizeof(*cmd) - sizeof(cmd->hdr)); + cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION); + cmd->tlv.type = cpu_to_le16(I2400M_TLV_TYPE_WAKEUP_MODE); + cmd->tlv.length = cpu_to_le16(sizeof(cmd->val)); + cmd->val = cpu_to_le32(I2400M_WAKEUP_ENABLED); + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + result = PTR_ERR(ack_skb); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'Enter power save' command: %d\n", + result); + goto error_msg_to_dev; + } + result = i2400m_msg_check_status(wimax_msg_data(ack_skb), + strerr, sizeof(strerr)); + if (result == -EACCES) + d_printf(1, dev, "Cannot enter power save mode\n"); + else if (result < 0) + dev_err(dev, "'Enter power save' (0x%04x) command failed: " + "%d - %s\n", I2400M_MT_CMD_ENTER_POWERSAVE, + result, strerr); + else + d_printf(1, dev, "device ready to power save\n"); + kfree_skb(ack_skb); +error_msg_to_dev: + kfree(cmd); +error_alloc: + return result; +} +EXPORT_SYMBOL_GPL(i2400m_cmd_enter_powersave); + + +/* + * Definitions for getting device information + */ +enum { + I2400M_TLV_DETAILED_DEVICE_INFO = 140 +}; + +/** + * i2400m_get_device_info - Query the device for detailed device information + * + * @i2400m: device descriptor + * + * Returns: an skb whose skb->data points to a 'struct + * i2400m_tlv_detailed_device_info'. When done, kfree_skb() it. The + * skb is *guaranteed* to contain the whole TLV data structure. + * + * On error, IS_ERR(skb) is true and ERR_PTR(skb) is the error + * code. + */ +struct sk_buff *i2400m_get_device_info(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct i2400m_l3l4_hdr *cmd; + const struct i2400m_l3l4_hdr *ack; + size_t ack_len; + const struct i2400m_tlv_hdr *tlv; + const struct i2400m_tlv_detailed_device_info *ddi; + char strerr[32]; + + ack_skb = ERR_PTR(-ENOMEM); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->type = cpu_to_le16(I2400M_MT_GET_DEVICE_INFO); + cmd->length = 0; + cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'get device info' command: %ld\n", + PTR_ERR(ack_skb)); + goto error_msg_to_dev; + } + ack = wimax_msg_data_len(ack_skb, &ack_len); + result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); + if (result < 0) { + dev_err(dev, "'get device info' (0x%04x) command failed: " + "%d - %s\n", I2400M_MT_GET_DEVICE_INFO, result, + strerr); + goto error_cmd_failed; + } + tlv = i2400m_tlv_find(i2400m, ack->pl, ack_len - sizeof(*ack), + I2400M_TLV_DETAILED_DEVICE_INFO, sizeof(*ddi)); + if (tlv == NULL) { + dev_err(dev, "GET DEVICE INFO: " + "detailed device info TLV not found (0x%04x)\n", + I2400M_TLV_DETAILED_DEVICE_INFO); + result = -EIO; + goto error_no_tlv; + } + skb_pull(ack_skb, (void *) tlv - (void *) ack_skb->data); +error_msg_to_dev: + kfree(cmd); +error_alloc: + return ack_skb; + +error_no_tlv: +error_cmd_failed: + kfree_skb(ack_skb); + kfree(cmd); + return ERR_PTR(result); +} + + +/* Firmware interface versions we support */ +enum { + I2400M_HDIv_MAJOR = 9, + I2400M_HDIv_MAJOR_2 = 8, + I2400M_HDIv_MINOR = 1, +}; + + +/** + * i2400m_firmware_check - check firmware versions are compatible with + * the driver + * + * @i2400m: device descriptor + * + * Returns: 0 if ok, < 0 errno code an error and a message in the + * kernel log. + * + * Long function, but quite simple; first chunk launches the command + * and double checks the reply for the right TLV. Then we process the + * TLV (where the meat is). + */ +int i2400m_firmware_check(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct i2400m_l3l4_hdr *cmd; + const struct i2400m_l3l4_hdr *ack; + size_t ack_len; + const struct i2400m_tlv_hdr *tlv; + const struct i2400m_tlv_l4_message_versions *l4mv; + char strerr[32]; + unsigned major, minor, branch; + + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->type = cpu_to_le16(I2400M_MT_GET_LM_VERSION); + cmd->length = 0; + cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + if (IS_ERR(ack_skb)) { + result = PTR_ERR(ack_skb); + dev_err(dev, "Failed to issue 'get lm version' command: %-d\n", + result); + goto error_msg_to_dev; + } + ack = wimax_msg_data_len(ack_skb, &ack_len); + result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); + if (result < 0) { + dev_err(dev, "'get lm version' (0x%04x) command failed: " + "%d - %s\n", I2400M_MT_GET_LM_VERSION, result, + strerr); + goto error_cmd_failed; + } + tlv = i2400m_tlv_find(i2400m, ack->pl, ack_len - sizeof(*ack), + I2400M_TLV_L4_MESSAGE_VERSIONS, sizeof(*l4mv)); + if (tlv == NULL) { + dev_err(dev, "get lm version: TLV not found (0x%04x)\n", + I2400M_TLV_L4_MESSAGE_VERSIONS); + result = -EIO; + goto error_no_tlv; + } + l4mv = container_of(tlv, typeof(*l4mv), hdr); + major = le16_to_cpu(l4mv->major); + minor = le16_to_cpu(l4mv->minor); + branch = le16_to_cpu(l4mv->branch); + result = -EINVAL; + if (major != I2400M_HDIv_MAJOR + && major != I2400M_HDIv_MAJOR_2) { + dev_err(dev, "unsupported major fw interface version " + "%u.%u.%u\n", major, minor, branch); + goto error_bad_major; + } + if (major == I2400M_HDIv_MAJOR_2) + dev_err(dev, "deprecated major fw interface version " + "%u.%u.%u\n", major, minor, branch); + result = 0; + if (minor != I2400M_HDIv_MINOR) + dev_warn(dev, "untested minor fw firmware version %u.%u.%u\n", + major, minor, branch); +error_bad_major: + dev_info(dev, "firmware interface version %u.%u.%u\n", + major, minor, branch); +error_no_tlv: +error_cmd_failed: + kfree_skb(ack_skb); +error_msg_to_dev: + kfree(cmd); +error_alloc: + return result; +} + + +/* + * Send an DoExitIdle command to the device to ask it to go out of + * basestation-idle mode. + * + * @i2400m: device descriptor + * + * This starts a renegotiation with the basestation that might involve + * another crypto handshake with user space. + * + * Returns: 0 if ok, < 0 errno code on error. + */ +int i2400m_cmd_exit_idle(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct i2400m_l3l4_hdr *cmd; + char strerr[32]; + + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->type = cpu_to_le16(I2400M_MT_CMD_EXIT_IDLE); + cmd->length = 0; + cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + result = PTR_ERR(ack_skb); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'exit idle' command: %d\n", + result); + goto error_msg_to_dev; + } + result = i2400m_msg_check_status(wimax_msg_data(ack_skb), + strerr, sizeof(strerr)); + kfree_skb(ack_skb); +error_msg_to_dev: + kfree(cmd); +error_alloc: + return result; + +} + + +/* + * Query the device for its state, update the WiMAX stack's idea of it + * + * @i2400m: device descriptor + * + * Returns: 0 if ok, < 0 errno code on error. + * + * Executes a 'Get State' command and parses the returned + * TLVs. + * + * Because this is almost identical to a 'Report State', we use + * i2400m_report_state_hook() to parse the answer. This will set the + * carrier state, as well as the RF Kill switches state. + */ +int i2400m_cmd_get_state(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct i2400m_l3l4_hdr *cmd; + const struct i2400m_l3l4_hdr *ack; + size_t ack_len; + char strerr[32]; + + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->type = cpu_to_le16(I2400M_MT_GET_STATE); + cmd->length = 0; + cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'get state' command: %ld\n", + PTR_ERR(ack_skb)); + result = PTR_ERR(ack_skb); + goto error_msg_to_dev; + } + ack = wimax_msg_data_len(ack_skb, &ack_len); + result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); + if (result < 0) { + dev_err(dev, "'get state' (0x%04x) command failed: " + "%d - %s\n", I2400M_MT_GET_STATE, result, strerr); + goto error_cmd_failed; + } + i2400m_report_state_hook(i2400m, ack, ack_len - sizeof(*ack), + "GET STATE"); + result = 0; + kfree_skb(ack_skb); +error_cmd_failed: +error_msg_to_dev: + kfree(cmd); +error_alloc: + return result; +} +EXPORT_SYMBOL_GPL(i2400m_cmd_get_state); + + +/** + * Set basic configuration settings + * + * @i2400m: device descriptor + * @args: array of pointers to the TLV headers to send for + * configuration (each followed by its payload). + * TLV headers and payloads must be properly initialized, with the + * right endianess (LE). + * @arg_size: number of pointers in the @args array + */ +int i2400m_set_init_config(struct i2400m *i2400m, + const struct i2400m_tlv_hdr **arg, size_t args) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct i2400m_l3l4_hdr *cmd; + char strerr[32]; + unsigned argc, argsize, tlv_size; + const struct i2400m_tlv_hdr *tlv_hdr; + void *buf, *itr; + + d_fnstart(3, dev, "(i2400m %p arg %p args %zu)\n", i2400m, arg, args); + result = 0; + if (args == 0) + goto none; + /* Compute the size of all the TLVs, so we can alloc a + * contiguous command block to copy them. */ + argsize = 0; + for (argc = 0; argc < args; argc++) { + tlv_hdr = arg[argc]; + argsize += sizeof(*tlv_hdr) + le16_to_cpu(tlv_hdr->length); + } + WARN_ON(argc >= 9); /* As per hw spec */ + + /* Alloc the space for the command and TLVs*/ + result = -ENOMEM; + buf = kzalloc(sizeof(*cmd) + argsize, GFP_KERNEL); + if (buf == NULL) + goto error_alloc; + cmd = buf; + cmd->type = cpu_to_le16(I2400M_MT_SET_INIT_CONFIG); + cmd->length = cpu_to_le16(argsize); + cmd->version = cpu_to_le16(I2400M_L3L4_VERSION); + + /* Copy the TLVs */ + itr = buf + sizeof(*cmd); + for (argc = 0; argc < args; argc++) { + tlv_hdr = arg[argc]; + tlv_size = sizeof(*tlv_hdr) + le16_to_cpu(tlv_hdr->length); + memcpy(itr, tlv_hdr, tlv_size); + itr += tlv_size; + } + + /* Send the message! */ + ack_skb = i2400m_msg_to_dev(i2400m, buf, sizeof(*cmd) + argsize); + result = PTR_ERR(ack_skb); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'init config' command: %d\n", + result); + + goto error_msg_to_dev; + } + result = i2400m_msg_check_status(wimax_msg_data(ack_skb), + strerr, sizeof(strerr)); + if (result < 0) + dev_err(dev, "'init config' (0x%04x) command failed: %d - %s\n", + I2400M_MT_SET_INIT_CONFIG, result, strerr); + kfree_skb(ack_skb); +error_msg_to_dev: + kfree(buf); +error_alloc: +none: + d_fnend(3, dev, "(i2400m %p arg %p args %zu) = %d\n", + i2400m, arg, args, result); + return result; + +} +EXPORT_SYMBOL_GPL(i2400m_set_init_config); + + +/** + * i2400m_dev_initialize - Initialize the device once communications are ready + * + * @i2400m: device descriptor + * + * Returns: 0 if ok, < 0 errno code on error. + * + * Configures the device to work the way we like it. + * + * At the point of this call, the device is registered with the WiMAX + * and netdev stacks, firmware is uploaded and we can talk to the + * device normally. + */ +int i2400m_dev_initialize(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct i2400m_tlv_config_idle_parameters idle_params; + const struct i2400m_tlv_hdr *args[9]; + unsigned argc = 0; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + /* Useless for now...might change */ + if (i2400m_idle_mode_disabled) { + idle_params.hdr.type = + cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS); + idle_params.hdr.length = cpu_to_le16( + sizeof(idle_params) - sizeof(idle_params.hdr)); + idle_params.idle_timeout = 0; + idle_params.idle_paging_interval = 0; + args[argc++] = &idle_params.hdr; + } + result = i2400m_set_init_config(i2400m, args, argc); + if (result < 0) + goto error; + result = i2400m_firmware_check(i2400m); /* fw versions ok? */ + if (result < 0) + goto error; + /* + * Update state: Here it just calls a get state; parsing the + * result (System State TLV and RF Status TLV [done in the rx + * path hooks]) will set the hardware and software RF-Kill + * status. + */ + result = i2400m_cmd_get_state(i2400m); +error: + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; +} + + +/** + * i2400m_dev_shutdown - Shutdown a running device + * + * @i2400m: device descriptor + * + * Gracefully stops the device, moving it to the lowest power + * consumption state possible. + */ +void i2400m_dev_shutdown(struct i2400m *i2400m) +{ + int result = -ENODEV; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); + d_fnend(3, dev, "(i2400m %p) = void [%d]\n", i2400m, result); + return; +} diff --git a/drivers/net/wimax/i2400m/debug-levels.h b/drivers/net/wimax/i2400m/debug-levels.h new file mode 100644 index 000000000000..3183baa16a52 --- /dev/null +++ b/drivers/net/wimax/i2400m/debug-levels.h @@ -0,0 +1,45 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Debug levels control file for the i2400m module + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __debug_levels__h__ +#define __debug_levels__h__ + +/* Maximum compile and run time debug level for all submodules */ +#define D_MODULENAME i2400m +#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL + +#include <linux/wimax/debug.h> + +/* List of all the enabled modules */ +enum d_module { + D_SUBMODULE_DECLARE(control), + D_SUBMODULE_DECLARE(driver), + D_SUBMODULE_DECLARE(debugfs), + D_SUBMODULE_DECLARE(fw), + D_SUBMODULE_DECLARE(netdev), + D_SUBMODULE_DECLARE(rfkill), + D_SUBMODULE_DECLARE(rx), + D_SUBMODULE_DECLARE(tx), +}; + + +#endif /* #ifndef __debug_levels__h__ */ diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c new file mode 100644 index 000000000000..626632985977 --- /dev/null +++ b/drivers/net/wimax/i2400m/debugfs.c @@ -0,0 +1,392 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Debugfs interfaces to manipulate driver and device information + * + * + * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/debugfs.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include "i2400m.h" + + +#define D_SUBMODULE debugfs +#include "debug-levels.h" + +static +int debugfs_netdev_queue_stopped_get(void *data, u64 *val) +{ + struct i2400m *i2400m = data; + *val = netif_queue_stopped(i2400m->wimax_dev.net_dev); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_netdev_queue_stopped, + debugfs_netdev_queue_stopped_get, + NULL, "%llu\n"); + + +static +struct dentry *debugfs_create_netdev_queue_stopped( + const char *name, struct dentry *parent, struct i2400m *i2400m) +{ + return debugfs_create_file(name, 0400, parent, i2400m, + &fops_netdev_queue_stopped); +} + + +/* + * inode->i_private has the @data argument to debugfs_create_file() + */ +static +int i2400m_stats_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +/* + * We don't allow partial reads of this file, as then the reader would + * get weirdly confused data as it is updated. + * + * So or you read it all or nothing; if you try to read with an offset + * != 0, we consider you are done reading. + */ +static +ssize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct i2400m *i2400m = filp->private_data; + char buf[128]; + unsigned long flags; + + if (*ppos != 0) + return 0; + if (count < sizeof(buf)) + return -ENOSPC; + spin_lock_irqsave(&i2400m->rx_lock, flags); + snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", + i2400m->rx_pl_num, i2400m->rx_pl_min, + i2400m->rx_pl_max, i2400m->rx_num, + i2400m->rx_size_acc, + i2400m->rx_size_min, i2400m->rx_size_max); + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); +} + + +/* Any write clears the stats */ +static +ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct i2400m *i2400m = filp->private_data; + unsigned long flags; + + spin_lock_irqsave(&i2400m->rx_lock, flags); + i2400m->rx_pl_num = 0; + i2400m->rx_pl_max = 0; + i2400m->rx_pl_min = UINT_MAX; + i2400m->rx_num = 0; + i2400m->rx_size_acc = 0; + i2400m->rx_size_min = UINT_MAX; + i2400m->rx_size_max = 0; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + return count; +} + +static +const struct file_operations i2400m_rx_stats_fops = { + .owner = THIS_MODULE, + .open = i2400m_stats_open, + .read = i2400m_rx_stats_read, + .write = i2400m_rx_stats_write, +}; + + +/* See i2400m_rx_stats_read() */ +static +ssize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct i2400m *i2400m = filp->private_data; + char buf[128]; + unsigned long flags; + + if (*ppos != 0) + return 0; + if (count < sizeof(buf)) + return -ENOSPC; + spin_lock_irqsave(&i2400m->tx_lock, flags); + snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n", + i2400m->tx_pl_num, i2400m->tx_pl_min, + i2400m->tx_pl_max, i2400m->tx_num, + i2400m->tx_size_acc, + i2400m->tx_size_min, i2400m->tx_size_max); + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); +} + +/* Any write clears the stats */ +static +ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct i2400m *i2400m = filp->private_data; + unsigned long flags; + + spin_lock_irqsave(&i2400m->tx_lock, flags); + i2400m->tx_pl_num = 0; + i2400m->tx_pl_max = 0; + i2400m->tx_pl_min = UINT_MAX; + i2400m->tx_num = 0; + i2400m->tx_size_acc = 0; + i2400m->tx_size_min = UINT_MAX; + i2400m->tx_size_max = 0; + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + return count; +} + +static +const struct file_operations i2400m_tx_stats_fops = { + .owner = THIS_MODULE, + .open = i2400m_stats_open, + .read = i2400m_tx_stats_read, + .write = i2400m_tx_stats_write, +}; + + +/* Write 1 to ask the device to go into suspend */ +static +int debugfs_i2400m_suspend_set(void *data, u64 val) +{ + int result; + struct i2400m *i2400m = data; + result = i2400m_cmd_enter_powersave(i2400m); + if (result >= 0) + result = 0; + return result; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_suspend, + NULL, debugfs_i2400m_suspend_set, + "%llu\n"); + +static +struct dentry *debugfs_create_i2400m_suspend( + const char *name, struct dentry *parent, struct i2400m *i2400m) +{ + return debugfs_create_file(name, 0200, parent, i2400m, + &fops_i2400m_suspend); +} + + +/* + * Reset the device + * + * Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus + * reset (as defined by enum i2400m_reset_type). + */ +static +int debugfs_i2400m_reset_set(void *data, u64 val) +{ + int result; + struct i2400m *i2400m = data; + enum i2400m_reset_type rt = val; + switch(rt) { + case I2400M_RT_WARM: + case I2400M_RT_COLD: + case I2400M_RT_BUS: + result = i2400m->bus_reset(i2400m, rt); + if (result >= 0) + result = 0; + default: + result = -EINVAL; + } + return result; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_reset, + NULL, debugfs_i2400m_reset_set, + "%llu\n"); + +static +struct dentry *debugfs_create_i2400m_reset( + const char *name, struct dentry *parent, struct i2400m *i2400m) +{ + return debugfs_create_file(name, 0200, parent, i2400m, + &fops_i2400m_reset); +} + +/* + * Debug levels control; see debug.h + */ +struct d_level D_LEVEL[] = { + D_SUBMODULE_DEFINE(control), + D_SUBMODULE_DEFINE(driver), + D_SUBMODULE_DEFINE(debugfs), + D_SUBMODULE_DEFINE(fw), + D_SUBMODULE_DEFINE(netdev), + D_SUBMODULE_DEFINE(rfkill), + D_SUBMODULE_DEFINE(rx), + D_SUBMODULE_DEFINE(tx), +}; +size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); + +#define __debugfs_register(prefix, name, parent) \ +do { \ + result = d_level_register_debugfs(prefix, name, parent); \ + if (result < 0) \ + goto error; \ +} while (0) + + +int i2400m_debugfs_add(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry; + struct dentry *fd; + + dentry = debugfs_create_dir("i2400m", dentry); + result = PTR_ERR(dentry); + if (IS_ERR(dentry)) { + if (result == -ENODEV) + result = 0; /* No debugfs support */ + goto error; + } + i2400m->debugfs_dentry = dentry; + __debugfs_register("dl_", control, dentry); + __debugfs_register("dl_", driver, dentry); + __debugfs_register("dl_", debugfs, dentry); + __debugfs_register("dl_", fw, dentry); + __debugfs_register("dl_", netdev, dentry); + __debugfs_register("dl_", rfkill, dentry); + __debugfs_register("dl_", rx, dentry); + __debugfs_register("dl_", tx, dentry); + + fd = debugfs_create_size_t("tx_in", 0400, dentry, + &i2400m->tx_in); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "tx_in: %d\n", result); + goto error; + } + + fd = debugfs_create_size_t("tx_out", 0400, dentry, + &i2400m->tx_out); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "tx_out: %d\n", result); + goto error; + } + + fd = debugfs_create_u32("state", 0600, dentry, + &i2400m->state); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "state: %d\n", result); + goto error; + } + + /* + * Trace received messages from user space + * + * In order to tap the bidirectional message stream in the + * 'msg' pipe, user space can read from the 'msg' pipe; + * however, due to limitations in libnl, we can't know what + * the different applications are sending down to the kernel. + * + * So we have this hack where the driver will echo any message + * received on the msg pipe from user space [through a call to + * wimax_dev->op_msg_from_user() into + * i2400m_op_msg_from_user()] into the 'trace' pipe that this + * driver creates. + * + * So then, reading from both the 'trace' and 'msg' pipes in + * user space will provide a full dump of the traffic. + * + * Write 1 to activate, 0 to clear. + * + * It is not really very atomic, but it is also not too + * critical. + */ + fd = debugfs_create_u8("trace_msg_from_user", 0600, dentry, + &i2400m->trace_msg_from_user); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "trace_msg_from_user: %d\n", result); + goto error; + } + + fd = debugfs_create_netdev_queue_stopped("netdev_queue_stopped", + dentry, i2400m); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "netdev_queue_stopped: %d\n", result); + goto error; + } + + fd = debugfs_create_file("rx_stats", 0600, dentry, i2400m, + &i2400m_rx_stats_fops); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "rx_stats: %d\n", result); + goto error; + } + + fd = debugfs_create_file("tx_stats", 0600, dentry, i2400m, + &i2400m_tx_stats_fops); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "tx_stats: %d\n", result); + goto error; + } + + fd = debugfs_create_i2400m_suspend("suspend", dentry, i2400m); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry suspend: %d\n", + result); + goto error; + } + + fd = debugfs_create_i2400m_reset("reset", dentry, i2400m); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry reset: %d\n", result); + goto error; + } + + result = 0; +error: + return result; +} + +void i2400m_debugfs_rm(struct i2400m *i2400m) +{ + debugfs_remove_recursive(i2400m->debugfs_dentry); +} diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c new file mode 100644 index 000000000000..5f98047e18cf --- /dev/null +++ b/drivers/net/wimax/i2400m/driver.c @@ -0,0 +1,728 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Generic probe/disconnect, reset and message passing + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * See i2400m.h for driver documentation. This contains helpers for + * the driver model glue [_setup()/_release()], handling device resets + * [_dev_reset_handle()], and the backends for the WiMAX stack ops + * reset [_op_reset()] and message from user [_op_msg_from_user()]. + * + * ROADMAP: + * + * i2400m_op_msg_from_user() + * i2400m_msg_to_dev() + * wimax_msg_to_user_send() + * + * i2400m_op_reset() + * i240m->bus_reset() + * + * i2400m_dev_reset_handle() + * __i2400m_dev_reset_handle() + * __i2400m_dev_stop() + * __i2400m_dev_start() + * + * i2400m_setup() + * i2400m_bootrom_init() + * register_netdev() + * i2400m_dev_start() + * __i2400m_dev_start() + * i2400m_dev_bootstrap() + * i2400m_tx_setup() + * i2400m->bus_dev_start() + * i2400m_check_mac_addr() + * wimax_dev_add() + * + * i2400m_release() + * wimax_dev_rm() + * i2400m_dev_stop() + * __i2400m_dev_stop() + * i2400m_dev_shutdown() + * i2400m->bus_dev_stop() + * i2400m_tx_release() + * unregister_netdev() + */ +#include "i2400m.h" +#include <linux/wimax/i2400m.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#define D_SUBMODULE driver +#include "debug-levels.h" + + +int i2400m_idle_mode_disabled; /* 0 (idle mode enabled) by default */ +module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644); +MODULE_PARM_DESC(idle_mode_disabled, + "If true, the device will not enable idle mode negotiation " + "with the base station (when connected) to save power."); + +/** + * i2400m_queue_work - schedule work on a i2400m's queue + * + * @i2400m: device descriptor + * + * @fn: function to run to execute work. It gets passed a 'struct + * work_struct' that is wrapped in a 'struct i2400m_work'. Once + * done, you have to (1) i2400m_put(i2400m_work->i2400m) and then + * (2) kfree(i2400m_work). + * + * @gfp_flags: GFP flags for memory allocation. + * + * @pl: pointer to a payload buffer that you want to pass to the _work + * function. Use this to pack (for example) a struct with extra + * arguments. + * + * @pl_size: size of the payload buffer. + * + * We do this quite often, so this just saves typing; allocate a + * wrapper for a i2400m, get a ref to it, pack arguments and launch + * the work. + * + * A usual workflow is: + * + * struct my_work_args { + * void *something; + * int whatever; + * }; + * ... + * + * struct my_work_args my_args = { + * .something = FOO, + * .whaetever = BLAH + * }; + * i2400m_queue_work(i2400m, 1, my_work_function, GFP_KERNEL, + * &args, sizeof(args)) + * + * And now the work function can unpack the arguments and call the + * real function (or do the job itself): + * + * static + * void my_work_fn((struct work_struct *ws) + * { + * struct i2400m_work *iw = + * container_of(ws, struct i2400m_work, ws); + * struct my_work_args *my_args = (void *) iw->pl; + * + * my_work(iw->i2400m, my_args->something, my_args->whatevert); + * } + */ +int i2400m_queue_work(struct i2400m *i2400m, + void (*fn)(struct work_struct *), gfp_t gfp_flags, + const void *pl, size_t pl_size) +{ + int result; + struct i2400m_work *iw; + + BUG_ON(i2400m->work_queue == NULL); + result = -ENOMEM; + iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags); + if (iw == NULL) + goto error_kzalloc; + iw->i2400m = i2400m_get(i2400m); + memcpy(iw->pl, pl, pl_size); + INIT_WORK(&iw->ws, fn); + result = queue_work(i2400m->work_queue, &iw->ws); +error_kzalloc: + return result; +} +EXPORT_SYMBOL_GPL(i2400m_queue_work); + + +/* + * Schedule i2400m's specific work on the system's queue. + * + * Used for a few cases where we really need it; otherwise, identical + * to i2400m_queue_work(). + * + * Returns < 0 errno code on error, 1 if ok. + * + * If it returns zero, something really bad happened, as it means the + * works struct was already queued, but we have just allocated it, so + * it should not happen. + */ +int i2400m_schedule_work(struct i2400m *i2400m, + void (*fn)(struct work_struct *), gfp_t gfp_flags) +{ + int result; + struct i2400m_work *iw; + + BUG_ON(i2400m->work_queue == NULL); + result = -ENOMEM; + iw = kzalloc(sizeof(*iw), gfp_flags); + if (iw == NULL) + goto error_kzalloc; + iw->i2400m = i2400m_get(i2400m); + INIT_WORK(&iw->ws, fn); + result = schedule_work(&iw->ws); + if (result == 0) + result = -ENXIO; +error_kzalloc: + return result; +} + + +/* + * WiMAX stack operation: relay a message from user space + * + * @wimax_dev: device descriptor + * @pipe_name: named pipe the message is for + * @msg_buf: pointer to the message bytes + * @msg_len: length of the buffer + * @genl_info: passed by the generic netlink layer + * + * The WiMAX stack will call this function when a message was received + * from user space. + * + * For the i2400m, this is an L3L4 message, as specified in + * include/linux/wimax/i2400m.h, and thus prefixed with a 'struct + * i2400m_l3l4_hdr'. Driver (and device) expect the messages to be + * coded in Little Endian. + * + * This function just verifies that the header declaration and the + * payload are consistent and then deals with it, either forwarding it + * to the device or procesing it locally. + * + * In the i2400m, messages are basically commands that will carry an + * ack, so we use i2400m_msg_to_dev() and then deliver the ack back to + * user space. The rx.c code might intercept the response and use it + * to update the driver's state, but then it will pass it on so it can + * be relayed back to user space. + * + * Note that asynchronous events from the device are processed and + * sent to user space in rx.c. + */ +static +int i2400m_op_msg_from_user(struct wimax_dev *wimax_dev, + const char *pipe_name, + const void *msg_buf, size_t msg_len, + const struct genl_info *genl_info) +{ + int result; + struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + + d_fnstart(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p " + "msg_len %zu genl_info %p)\n", wimax_dev, i2400m, + msg_buf, msg_len, genl_info); + ack_skb = i2400m_msg_to_dev(i2400m, msg_buf, msg_len); + result = PTR_ERR(ack_skb); + if (IS_ERR(ack_skb)) + goto error_msg_to_dev; + if (unlikely(i2400m->trace_msg_from_user)) + wimax_msg(&i2400m->wimax_dev, "trace", + msg_buf, msg_len, GFP_KERNEL); + result = wimax_msg_send(&i2400m->wimax_dev, ack_skb); +error_msg_to_dev: + d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu " + "genl_info %p) = %d\n", wimax_dev, i2400m, msg_buf, msg_len, + genl_info, result); + return result; +} + + +/* + * Context to wait for a reset to finalize + */ +struct i2400m_reset_ctx { + struct completion completion; + int result; +}; + + +/* + * WiMAX stack operation: reset a device + * + * @wimax_dev: device descriptor + * + * See the documentation for wimax_reset() and wimax_dev->op_reset for + * the requirements of this function. The WiMAX stack guarantees + * serialization on calls to this function. + * + * Do a warm reset on the device; if it fails, resort to a cold reset + * and return -ENODEV. On successful warm reset, we need to block + * until it is complete. + * + * The bus-driver implementation of reset takes care of falling back + * to cold reset if warm fails. + */ +static +int i2400m_op_reset(struct wimax_dev *wimax_dev) +{ + int result; + struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); + struct device *dev = i2400m_dev(i2400m); + struct i2400m_reset_ctx ctx = { + .completion = COMPLETION_INITIALIZER_ONSTACK(ctx.completion), + .result = 0, + }; + + d_fnstart(4, dev, "(wimax_dev %p)\n", wimax_dev); + mutex_lock(&i2400m->init_mutex); + i2400m->reset_ctx = &ctx; + mutex_unlock(&i2400m->init_mutex); + result = i2400m->bus_reset(i2400m, I2400M_RT_WARM); + if (result < 0) + goto out; + result = wait_for_completion_timeout(&ctx.completion, 4*HZ); + if (result == 0) + result = -ETIMEDOUT; + else if (result > 0) + result = ctx.result; + /* if result < 0, pass it on */ + mutex_lock(&i2400m->init_mutex); + i2400m->reset_ctx = NULL; + mutex_unlock(&i2400m->init_mutex); +out: + d_fnend(4, dev, "(wimax_dev %p) = %d\n", wimax_dev, result); + return result; +} + + +/* + * Check the MAC address we got from boot mode is ok + * + * @i2400m: device descriptor + * + * Returns: 0 if ok, < 0 errno code on error. + */ +static +int i2400m_check_mac_addr(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *skb; + const struct i2400m_tlv_detailed_device_info *ddi; + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + const unsigned char zeromac[ETH_ALEN] = { 0 }; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + skb = i2400m_get_device_info(i2400m); + if (IS_ERR(skb)) { + result = PTR_ERR(skb); + dev_err(dev, "Cannot verify MAC address, error reading: %d\n", + result); + goto error; + } + /* Extract MAC addresss */ + ddi = (void *) skb->data; + BUILD_BUG_ON(ETH_ALEN != sizeof(ddi->mac_address)); + d_printf(2, dev, "GET DEVICE INFO: mac addr " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + ddi->mac_address[0], ddi->mac_address[1], + ddi->mac_address[2], ddi->mac_address[3], + ddi->mac_address[4], ddi->mac_address[5]); + if (!memcmp(net_dev->perm_addr, ddi->mac_address, + sizeof(ddi->mac_address))) + goto ok; + dev_warn(dev, "warning: device reports a different MAC address " + "to that of boot mode's\n"); + dev_warn(dev, "device reports %02x:%02x:%02x:%02x:%02x:%02x\n", + ddi->mac_address[0], ddi->mac_address[1], + ddi->mac_address[2], ddi->mac_address[3], + ddi->mac_address[4], ddi->mac_address[5]); + dev_warn(dev, "boot mode reported %02x:%02x:%02x:%02x:%02x:%02x\n", + net_dev->perm_addr[0], net_dev->perm_addr[1], + net_dev->perm_addr[2], net_dev->perm_addr[3], + net_dev->perm_addr[4], net_dev->perm_addr[5]); + if (!memcmp(zeromac, ddi->mac_address, sizeof(zeromac))) + dev_err(dev, "device reports an invalid MAC address, " + "not updating\n"); + else { + dev_warn(dev, "updating MAC address\n"); + net_dev->addr_len = ETH_ALEN; + memcpy(net_dev->perm_addr, ddi->mac_address, ETH_ALEN); + memcpy(net_dev->dev_addr, ddi->mac_address, ETH_ALEN); + } +ok: + result = 0; + kfree_skb(skb); +error: + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; +} + + +/** + * __i2400m_dev_start - Bring up driver communication with the device + * + * @i2400m: device descriptor + * @flags: boot mode flags + * + * Returns: 0 if ok, < 0 errno code on error. + * + * Uploads firmware and brings up all the resources needed to be able + * to communicate with the device. + * + * TX needs to be setup before the bus-specific code (otherwise on + * shutdown, the bus-tx code could try to access it). + */ +static +int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags) +{ + int result; + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + struct net_device *net_dev = wimax_dev->net_dev; + struct device *dev = i2400m_dev(i2400m); + int times = 3; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); +retry: + result = i2400m_dev_bootstrap(i2400m, flags); + if (result < 0) { + dev_err(dev, "cannot bootstrap device: %d\n", result); + goto error_bootstrap; + } + result = i2400m_tx_setup(i2400m); + if (result < 0) + goto error_tx_setup; + result = i2400m->bus_dev_start(i2400m); + if (result < 0) + goto error_bus_dev_start; + i2400m->work_queue = create_singlethread_workqueue(wimax_dev->name); + if (i2400m->work_queue == NULL) { + result = -ENOMEM; + dev_err(dev, "cannot create workqueue\n"); + goto error_create_workqueue; + } + /* At this point is ok to send commands to the device */ + result = i2400m_check_mac_addr(i2400m); + if (result < 0) + goto error_check_mac_addr; + i2400m->ready = 1; + wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); + result = i2400m_dev_initialize(i2400m); + if (result < 0) + goto error_dev_initialize; + /* At this point, reports will come for the device and set it + * to the right state if it is different than UNINITIALIZED */ + d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", + net_dev, i2400m, result); + return result; + +error_dev_initialize: +error_check_mac_addr: + destroy_workqueue(i2400m->work_queue); +error_create_workqueue: + i2400m->bus_dev_stop(i2400m); +error_bus_dev_start: + i2400m_tx_release(i2400m); +error_tx_setup: +error_bootstrap: + if (result == -ERESTARTSYS && times-- > 0) { + flags = I2400M_BRI_SOFT; + goto retry; + } + d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", + net_dev, i2400m, result); + return result; +} + + +static +int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags) +{ + int result; + mutex_lock(&i2400m->init_mutex); /* Well, start the device */ + result = __i2400m_dev_start(i2400m, bm_flags); + if (result >= 0) + i2400m->updown = 1; + mutex_unlock(&i2400m->init_mutex); + return result; +} + + +/** + * i2400m_dev_stop - Tear down driver communication with the device + * + * @i2400m: device descriptor + * + * Returns: 0 if ok, < 0 errno code on error. + * + * Releases all the resources allocated to communicate with the device. + */ +static +void __i2400m_dev_stop(struct i2400m *i2400m) +{ + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING); + i2400m_dev_shutdown(i2400m); + i2400m->ready = 0; + destroy_workqueue(i2400m->work_queue); + i2400m->bus_dev_stop(i2400m); + i2400m_tx_release(i2400m); + wimax_state_change(wimax_dev, WIMAX_ST_DOWN); + d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m); +} + + +/* + * Watch out -- we only need to stop if there is a need for it. The + * device could have reset itself and failed to come up again (see + * _i2400m_dev_reset_handle()). + */ +static +void i2400m_dev_stop(struct i2400m *i2400m) +{ + mutex_lock(&i2400m->init_mutex); + if (i2400m->updown) { + __i2400m_dev_stop(i2400m); + i2400m->updown = 0; + } + mutex_unlock(&i2400m->init_mutex); +} + + +/* + * The device has rebooted; fix up the device and the driver + * + * Tear down the driver communication with the device, reload the + * firmware and reinitialize the communication with the device. + * + * If someone calls a reset when the device's firmware is down, in + * theory we won't see it because we are not listening. However, just + * in case, leave the code to handle it. + * + * If there is a reset context, use it; this means someone is waiting + * for us to tell him when the reset operation is complete and the + * device is ready to rock again. + * + * NOTE: if we are in the process of bringing up or down the + * communication with the device [running i2400m_dev_start() or + * _stop()], don't do anything, let it fail and handle it. + * + * This function is ran always in a thread context + */ +static +void __i2400m_dev_reset_handle(struct work_struct *ws) +{ + int result; + struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws); + struct i2400m *i2400m = iw->i2400m; + struct device *dev = i2400m_dev(i2400m); + enum wimax_st wimax_state; + struct i2400m_reset_ctx *ctx = i2400m->reset_ctx; + + d_fnstart(3, dev, "(ws %p i2400m %p)\n", ws, i2400m); + result = 0; + if (mutex_trylock(&i2400m->init_mutex) == 0) { + /* We are still in i2400m_dev_start() [let it fail] or + * i2400m_dev_stop() [we are shutting down anyway, so + * ignore it] or we are resetting somewhere else. */ + dev_err(dev, "device rebooted\n"); + i2400m_msg_to_dev_cancel_wait(i2400m, -ERESTARTSYS); + complete(&i2400m->msg_completion); + goto out; + } + wimax_state = wimax_state_get(&i2400m->wimax_dev); + if (wimax_state < WIMAX_ST_UNINITIALIZED) { + dev_info(dev, "device rebooted: it is down, ignoring\n"); + goto out_unlock; /* ifconfig up/down wasn't called */ + } + dev_err(dev, "device rebooted: reinitializing driver\n"); + __i2400m_dev_stop(i2400m); + i2400m->updown = 0; + result = __i2400m_dev_start(i2400m, + I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT); + if (result < 0) { + dev_err(dev, "device reboot: cannot start the device: %d\n", + result); + result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); + if (result >= 0) + result = -ENODEV; + } else + i2400m->updown = 1; +out_unlock: + if (i2400m->reset_ctx) { + ctx->result = result; + complete(&ctx->completion); + } + mutex_unlock(&i2400m->init_mutex); +out: + i2400m_put(i2400m); + kfree(iw); + d_fnend(3, dev, "(ws %p i2400m %p) = void\n", ws, i2400m); + return; +} + + +/** + * i2400m_dev_reset_handle - Handle a device's reset in a thread context + * + * Schedule a device reset handling out on a thread context, so it + * is safe to call from atomic context. We can't use the i2400m's + * queue as we are going to destroy it and reinitialize it as part of + * the driver bringup/bringup process. + * + * See __i2400m_dev_reset_handle() for details; that takes care of + * reinitializing the driver to handle the reset, calling into the + * bus-specific functions ops as needed. + */ +int i2400m_dev_reset_handle(struct i2400m *i2400m) +{ + return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle, + GFP_ATOMIC); +} +EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle); + + +/** + * i2400m_setup - bus-generic setup function for the i2400m device + * + * @i2400m: device descriptor (bus-specific parts have been initialized) + * + * Returns: 0 if ok, < 0 errno code on error. + * + * Initializes the bus-generic parts of the i2400m driver; the + * bus-specific parts have been initialized, function pointers filled + * out by the bus-specific probe function. + * + * As well, this registers the WiMAX and net device nodes. Once this + * function returns, the device is operative and has to be ready to + * receive and send network traffic and WiMAX control operations. + */ +int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) +{ + int result = -ENODEV; + struct device *dev = i2400m_dev(i2400m); + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + + snprintf(wimax_dev->name, sizeof(wimax_dev->name), + "i2400m-%s:%s", dev->bus->name, dev->bus_id); + + i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); + if (i2400m->bm_cmd_buf == NULL) { + dev_err(dev, "cannot allocate USB command buffer\n"); + goto error_bm_cmd_kzalloc; + } + i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL); + if (i2400m->bm_ack_buf == NULL) { + dev_err(dev, "cannot allocate USB ack buffer\n"); + goto error_bm_ack_buf_kzalloc; + } + result = i2400m_bootrom_init(i2400m, bm_flags); + if (result < 0) { + dev_err(dev, "read mac addr: bootrom init " + "failed: %d\n", result); + goto error_bootrom_init; + } + result = i2400m_read_mac_addr(i2400m); + if (result < 0) + goto error_read_mac_addr; + + result = register_netdev(net_dev); /* Okey dokey, bring it up */ + if (result < 0) { + dev_err(dev, "cannot register i2400m network device: %d\n", + result); + goto error_register_netdev; + } + netif_carrier_off(net_dev); + + result = i2400m_dev_start(i2400m, bm_flags); + if (result < 0) + goto error_dev_start; + + i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user; + i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle; + i2400m->wimax_dev.op_reset = i2400m_op_reset; + result = wimax_dev_add(&i2400m->wimax_dev, net_dev); + if (result < 0) + goto error_wimax_dev_add; + /* User space needs to do some init stuff */ + wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); + + /* Now setup all that requires a registered net and wimax device. */ + result = i2400m_debugfs_add(i2400m); + if (result < 0) { + dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); + goto error_debugfs_setup; + } + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; + +error_debugfs_setup: + wimax_dev_rm(&i2400m->wimax_dev); +error_wimax_dev_add: + i2400m_dev_stop(i2400m); +error_dev_start: + unregister_netdev(net_dev); +error_register_netdev: +error_read_mac_addr: +error_bootrom_init: + kfree(i2400m->bm_ack_buf); +error_bm_ack_buf_kzalloc: + kfree(i2400m->bm_cmd_buf); +error_bm_cmd_kzalloc: + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; +} +EXPORT_SYMBOL_GPL(i2400m_setup); + + +/** + * i2400m_release - release the bus-generic driver resources + * + * Sends a disconnect message and undoes any setup done by i2400m_setup() + */ +void i2400m_release(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + netif_stop_queue(i2400m->wimax_dev.net_dev); + + i2400m_debugfs_rm(i2400m); + wimax_dev_rm(&i2400m->wimax_dev); + i2400m_dev_stop(i2400m); + unregister_netdev(i2400m->wimax_dev.net_dev); + kfree(i2400m->bm_ack_buf); + kfree(i2400m->bm_cmd_buf); + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} +EXPORT_SYMBOL_GPL(i2400m_release); + + +static +int __init i2400m_driver_init(void) +{ + return 0; +} +module_init(i2400m_driver_init); + +static +void __exit i2400m_driver_exit(void) +{ + /* for scheds i2400m_dev_reset_handle() */ + flush_scheduled_work(); + return; +} +module_exit(i2400m_driver_exit); + +MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); +MODULE_DESCRIPTION("Intel 2400M WiMAX networking bus-generic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c new file mode 100644 index 000000000000..1d8271f34c38 --- /dev/null +++ b/drivers/net/wimax/i2400m/fw.c @@ -0,0 +1,1095 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Firmware uploader + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Initial implementation + * + * + * THE PROCEDURE + * + * (this is decribed for USB, but for SDIO is similar) + * + * The 2400m works in two modes: boot-mode or normal mode. In boot + * mode we can execute only a handful of commands targeted at + * uploading the firmware and launching it. + * + * The 2400m enters boot mode when it is first connected to the + * system, when it crashes and when you ask it to reboot. There are + * two submodes of the boot mode: signed and non-signed. Signed takes + * firmwares signed with a certain private key, non-signed takes any + * firmware. Normal hardware takes only signed firmware. + * + * Upon entrance to boot mode, the device sends a few zero length + * packets (ZLPs) on the notification endpoint, then a reboot barker + * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by + * sending the same barker on the bulk out endpoint. The device acks + * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and + * then the device is fully rebooted. At this point we can upload the + * firmware. + * + * This process is accomplished by the i2400m_bootrom_init() + * function. All the device interaction happens through the + * i2400m_bm_cmd() [boot mode command]. Special return values will + * indicate if the device resets. + * + * After this, we read the MAC address and then (if needed) + * reinitialize the device. We need to read it ahead of time because + * in the future, we might not upload the firmware until userspace + * 'ifconfig up's the device. + * + * We can then upload the firmware file. The file is composed of a BCF + * header (basic data, keys and signatures) and a list of write + * commands and payloads. We first upload the header + * [i2400m_dnload_init()] and then pass the commands and payloads + * verbatim to the i2400m_bm_cmd() function + * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new + * firmware [i2400m_dnload_finalize()]. + * + * Once firmware is uploaded, we are good to go :) + * + * When we don't know in which mode we are, we first try by sending a + * warm reset request that will take us to boot-mode. If we time out + * waiting for a reboot barker, that means maybe we are already in + * boot mode, so we send a reboot barker. + * + * COMMAND EXECUTION + * + * This code (and process) is single threaded; for executing commands, + * we post a URB to the notification endpoint, post the command, wait + * for data on the notification buffer. We don't need to worry about + * others as we know we are the only ones in there. + * + * BACKEND IMPLEMENTATION + * + * This code is bus-generic; the bus-specific driver provides back end + * implementations to send a boot mode command to the device and to + * read an acknolwedgement from it (or an asynchronous notification) + * from it. + * + * ROADMAP + * + * i2400m_dev_bootstrap Called by __i2400m_dev_start() + * request_firmware + * i2400m_fw_check + * i2400m_fw_dnload + * release_firmware + * + * i2400m_fw_dnload + * i2400m_bootrom_init + * i2400m_bm_cmd + * i2400m->bus_reset + * i2400m_dnload_init + * i2400m_dnload_init_signed + * i2400m_dnload_init_nonsigned + * i2400m_download_chunk + * i2400m_bm_cmd + * i2400m_dnload_bcf + * i2400m_bm_cmd + * i2400m_dnload_finalize + * i2400m_bm_cmd + * + * i2400m_bm_cmd + * i2400m->bus_bm_cmd_send() + * i2400m->bus_bm_wait_for_ack + * __i2400m_bm_ack_verify + * + * i2400m_bm_cmd_prepare Used by bus-drivers to prep + * commands before sending + */ +#include <linux/firmware.h> +#include <linux/sched.h> +#include <linux/usb.h> +#include "i2400m.h" + + +#define D_SUBMODULE fw +#include "debug-levels.h" + + +static const __le32 i2400m_ACK_BARKER[4] = { + __constant_cpu_to_le32(I2400M_ACK_BARKER), + __constant_cpu_to_le32(I2400M_ACK_BARKER), + __constant_cpu_to_le32(I2400M_ACK_BARKER), + __constant_cpu_to_le32(I2400M_ACK_BARKER) +}; + + +/** + * Prepare a boot-mode command for delivery + * + * @cmd: pointer to bootrom header to prepare + * + * Computes checksum if so needed. After calling this function, DO NOT + * modify the command or header as the checksum won't work anymore. + * + * We do it from here because some times we cannot do it in the + * original context the command was sent (it is a const), so when we + * copy it to our staging buffer, we add the checksum there. + */ +void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *cmd) +{ + if (i2400m_brh_get_use_checksum(cmd)) { + int i; + u32 checksum = 0; + const u32 *checksum_ptr = (void *) cmd->payload; + for (i = 0; i < cmd->data_size / 4; i++) + checksum += cpu_to_le32(*checksum_ptr++); + checksum += cmd->command + cmd->target_addr + cmd->data_size; + cmd->block_checksum = cpu_to_le32(checksum); + } +} +EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare); + + +/* + * Verify the ack data received + * + * Given a reply to a boot mode command, chew it and verify everything + * is ok. + * + * @opcode: opcode which generated this ack. For error messages. + * @ack: pointer to ack data we received + * @ack_size: size of that data buffer + * @flags: I2400M_BM_CMD_* flags we called the command with. + * + * Way too long function -- maybe it should be further split + */ +static +ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode, + struct i2400m_bootrom_header *ack, + size_t ack_size, int flags) +{ + ssize_t result = -ENOMEM; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(8, dev, "(i2400m %p opcode %d ack %p size %zu)\n", + i2400m, opcode, ack, ack_size); + if (ack_size < sizeof(*ack)) { + result = -EIO; + dev_err(dev, "boot-mode cmd %d: HW BUG? notification didn't " + "return enough data (%zu bytes vs %zu expected)\n", + opcode, ack_size, sizeof(*ack)); + goto error_ack_short; + } + if (ack_size == sizeof(i2400m_NBOOT_BARKER) + && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) { + result = -ERESTARTSYS; + i2400m->sboot = 0; + d_printf(6, dev, "boot-mode cmd %d: " + "HW non-signed boot barker\n", opcode); + goto error_reboot; + } + if (ack_size == sizeof(i2400m_SBOOT_BARKER) + && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) { + result = -ERESTARTSYS; + i2400m->sboot = 1; + d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n", + opcode); + goto error_reboot; + } + if (ack_size == sizeof(i2400m_ACK_BARKER) + && memcmp(ack, i2400m_ACK_BARKER, sizeof(*ack)) == 0) { + result = -EISCONN; + d_printf(3, dev, "boot-mode cmd %d: HW reboot ack barker\n", + opcode); + goto error_reboot_ack; + } + result = 0; + if (flags & I2400M_BM_CMD_RAW) + goto out_raw; + ack->data_size = le32_to_cpu(ack->data_size); + ack->target_addr = le32_to_cpu(ack->target_addr); + ack->block_checksum = le32_to_cpu(ack->block_checksum); + d_printf(5, dev, "boot-mode cmd %d: notification for opcode %u " + "response %u csum %u rr %u da %u\n", + opcode, i2400m_brh_get_opcode(ack), + i2400m_brh_get_response(ack), + i2400m_brh_get_use_checksum(ack), + i2400m_brh_get_response_required(ack), + i2400m_brh_get_direct_access(ack)); + result = -EIO; + if (i2400m_brh_get_signature(ack) != 0xcbbc) { + dev_err(dev, "boot-mode cmd %d: HW BUG? wrong signature " + "0x%04x\n", opcode, i2400m_brh_get_signature(ack)); + goto error_ack_signature; + } + if (opcode != -1 && opcode != i2400m_brh_get_opcode(ack)) { + dev_err(dev, "boot-mode cmd %d: HW BUG? " + "received response for opcode %u, expected %u\n", + opcode, i2400m_brh_get_opcode(ack), opcode); + goto error_ack_opcode; + } + if (i2400m_brh_get_response(ack) != 0) { /* failed? */ + dev_err(dev, "boot-mode cmd %d: error; hw response %u\n", + opcode, i2400m_brh_get_response(ack)); + goto error_ack_failed; + } + if (ack_size < ack->data_size + sizeof(*ack)) { + dev_err(dev, "boot-mode cmd %d: SW BUG " + "driver provided only %zu bytes for %zu bytes " + "of data\n", opcode, ack_size, + (size_t) le32_to_cpu(ack->data_size) + sizeof(*ack)); + goto error_ack_short_buffer; + } + result = ack_size; + /* Don't you love this stack of empty targets? Well, I don't + * either, but it helps track exactly who comes in here and + * why :) */ +error_ack_short_buffer: +error_ack_failed: +error_ack_opcode: +error_ack_signature: +out_raw: +error_reboot_ack: +error_reboot: +error_ack_short: + d_fnend(8, dev, "(i2400m %p opcode %d ack %p size %zu) = %d\n", + i2400m, opcode, ack, ack_size, (int) result); + return result; +} + + +/** + * i2400m_bm_cmd - Execute a boot mode command + * + * @cmd: buffer containing the command data (pointing at the header). + * This data can be ANYWHERE (for USB, we will copy it to an + * specific buffer). Make sure everything is in proper little + * endian. + * + * A raw buffer can be also sent, just cast it and set flags to + * I2400M_BM_CMD_RAW. + * + * This function will generate a checksum for you if the + * checksum bit in the command is set (unless I2400M_BM_CMD_RAW + * is set). + * + * You can use the i2400m->bm_cmd_buf to stage your commands and + * send them. + * + * If NULL, no command is sent (we just wait for an ack). + * + * @cmd_size: size of the command. Will be auto padded to the + * bus-specific drivers padding requirements. + * + * @ack: buffer where to place the acknowledgement. If it is a regular + * command response, all fields will be returned with the right, + * native endianess. + * + * You *cannot* use i2400m->bm_ack_buf for this buffer. + * + * @ack_size: size of @ack, 16 aligned; you need to provide at least + * sizeof(*ack) bytes and then enough to contain the return data + * from the command + * + * @flags: see I2400M_BM_CMD_* above. + * + * @returns: bytes received by the notification; if < 0, an errno code + * denoting an error or: + * + * -ERESTARTSYS The device has rebooted + * + * Executes a boot-mode command and waits for a response, doing basic + * validation on it; if a zero length response is received, it retries + * waiting for a response until a non-zero one is received (timing out + * after %I2400M_BOOT_RETRIES retries). + */ +static +ssize_t i2400m_bm_cmd(struct i2400m *i2400m, + const struct i2400m_bootrom_header *cmd, size_t cmd_size, + struct i2400m_bootrom_header *ack, size_t ack_size, + int flags) +{ + ssize_t result = -ENOMEM, rx_bytes; + struct device *dev = i2400m_dev(i2400m); + int opcode = cmd == NULL ? -1 : i2400m_brh_get_opcode(cmd); + + d_fnstart(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu)\n", + i2400m, cmd, cmd_size, ack, ack_size); + BUG_ON(ack_size < sizeof(*ack)); + BUG_ON(i2400m->boot_mode == 0); + + if (cmd != NULL) { /* send the command */ + memcpy(i2400m->bm_cmd_buf, cmd, cmd_size); + result = i2400m->bus_bm_cmd_send(i2400m, cmd, cmd_size, flags); + if (result < 0) + goto error_cmd_send; + if ((flags & I2400M_BM_CMD_RAW) == 0) + d_printf(5, dev, + "boot-mode cmd %d csum %u rr %u da %u: " + "addr 0x%04x size %u block csum 0x%04x\n", + opcode, i2400m_brh_get_use_checksum(cmd), + i2400m_brh_get_response_required(cmd), + i2400m_brh_get_direct_access(cmd), + cmd->target_addr, cmd->data_size, + cmd->block_checksum); + } + result = i2400m->bus_bm_wait_for_ack(i2400m, ack, ack_size); + if (result < 0) { + dev_err(dev, "boot-mode cmd %d: error waiting for an ack: %d\n", + opcode, (int) result); /* bah, %zd doesn't work */ + goto error_wait_for_ack; + } + rx_bytes = result; + /* verify the ack and read more if neccessary [result is the + * final amount of bytes we get in the ack] */ + result = __i2400m_bm_ack_verify(i2400m, opcode, ack, ack_size, flags); + if (result < 0) + goto error_bad_ack; + /* Don't you love this stack of empty targets? Well, I don't + * either, but it helps track exactly who comes in here and + * why :) */ + result = rx_bytes; +error_bad_ack: +error_wait_for_ack: +error_cmd_send: + d_fnend(6, dev, "(i2400m %p cmd %p size %zu ack %p size %zu) = %d\n", + i2400m, cmd, cmd_size, ack, ack_size, (int) result); + return result; +} + + +/** + * i2400m_download_chunk - write a single chunk of data to the device's memory + * + * @i2400m: device descriptor + * @buf: the buffer to write + * @buf_len: length of the buffer to write + * @addr: address in the device memory space + * @direct: bootrom write mode + * @do_csum: should a checksum validation be performed + */ +static int i2400m_download_chunk(struct i2400m *i2400m, const void *chunk, + size_t __chunk_len, unsigned long addr, + unsigned int direct, unsigned int do_csum) +{ + int ret; + size_t chunk_len = ALIGN(__chunk_len, I2400M_PL_PAD); + struct device *dev = i2400m_dev(i2400m); + struct { + struct i2400m_bootrom_header cmd; + u8 cmd_payload[chunk_len]; + } __attribute__((packed)) *buf; + struct i2400m_bootrom_header ack; + + d_fnstart(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx " + "direct %u do_csum %u)\n", i2400m, chunk, __chunk_len, + addr, direct, do_csum); + buf = i2400m->bm_cmd_buf; + memcpy(buf->cmd_payload, chunk, __chunk_len); + memset(buf->cmd_payload + __chunk_len, 0xad, chunk_len - __chunk_len); + + buf->cmd.command = i2400m_brh_command(I2400M_BRH_WRITE, + __chunk_len & 0x3 ? 0 : do_csum, + __chunk_len & 0xf ? 0 : direct); + buf->cmd.target_addr = cpu_to_le32(addr); + buf->cmd.data_size = cpu_to_le32(__chunk_len); + ret = i2400m_bm_cmd(i2400m, &buf->cmd, sizeof(buf->cmd) + chunk_len, + &ack, sizeof(ack), 0); + if (ret >= 0) + ret = 0; + d_fnend(5, dev, "(i2400m %p chunk %p __chunk_len %zu addr 0x%08lx " + "direct %u do_csum %u) = %d\n", i2400m, chunk, __chunk_len, + addr, direct, do_csum, ret); + return ret; +} + + +/* + * Download a BCF file's sections to the device + * + * @i2400m: device descriptor + * @bcf: pointer to firmware data (followed by the payloads). Assumed + * verified and consistent. + * @bcf_len: length (in bytes) of the @bcf buffer. + * + * Returns: < 0 errno code on error or the offset to the jump instruction. + * + * Given a BCF file, downloads each section (a command and a payload) + * to the device's address space. Actually, it just executes each + * command i the BCF file. + * + * The section size has to be aligned to 4 bytes AND the padding has + * to be taken from the firmware file, as the signature takes it into + * account. + */ +static +ssize_t i2400m_dnload_bcf(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf, size_t bcf_len) +{ + ssize_t ret; + struct device *dev = i2400m_dev(i2400m); + size_t offset, /* iterator offset */ + data_size, /* Size of the data payload */ + section_size, /* Size of the whole section (cmd + payload) */ + section = 1; + const struct i2400m_bootrom_header *bh; + struct i2400m_bootrom_header ack; + + d_fnstart(3, dev, "(i2400m %p bcf %p bcf_len %zu)\n", + i2400m, bcf, bcf_len); + /* Iterate over the command blocks in the BCF file that start + * after the header */ + offset = le32_to_cpu(bcf->header_len) * sizeof(u32); + while (1) { /* start sending the file */ + bh = (void *) bcf + offset; + data_size = le32_to_cpu(bh->data_size); + section_size = ALIGN(sizeof(*bh) + data_size, 4); + d_printf(7, dev, + "downloading section #%zu (@%zu %zu B) to 0x%08x\n", + section, offset, sizeof(*bh) + data_size, + le32_to_cpu(bh->target_addr)); + if (i2400m_brh_get_opcode(bh) == I2400M_BRH_SIGNED_JUMP) { + /* Secure boot needs to stop here */ + d_printf(5, dev, "signed jump found @%zu\n", offset); + break; + } + if (offset + section_size == bcf_len) + /* Non-secure boot stops here */ + break; + if (offset + section_size > bcf_len) { + dev_err(dev, "fw %s: bad section #%zu, " + "end (@%zu) beyond EOF (@%zu)\n", + i2400m->bus_fw_name, section, + offset + section_size, bcf_len); + ret = -EINVAL; + goto error_section_beyond_eof; + } + __i2400m_msleep(20); + ret = i2400m_bm_cmd(i2400m, bh, section_size, + &ack, sizeof(ack), I2400M_BM_CMD_RAW); + if (ret < 0) { + dev_err(dev, "fw %s: section #%zu (@%zu %zu B) " + "failed %d\n", i2400m->bus_fw_name, section, + offset, sizeof(*bh) + data_size, (int) ret); + goto error_send; + } + offset += section_size; + section++; + } + ret = offset; +error_section_beyond_eof: +error_send: + d_fnend(3, dev, "(i2400m %p bcf %p bcf_len %zu) = %d\n", + i2400m, bcf, bcf_len, (int) ret); + return ret; +} + + +/* + * Do the final steps of uploading firmware + * + * Depending on the boot mode (signed vs non-signed), different + * actions need to be taken. + */ +static +int i2400m_dnload_finalize(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf, size_t offset) +{ + int ret = 0; + struct device *dev = i2400m_dev(i2400m); + struct i2400m_bootrom_header *cmd, ack; + struct { + struct i2400m_bootrom_header cmd; + u8 cmd_pl[0]; + } __attribute__((packed)) *cmd_buf; + size_t signature_block_offset, signature_block_size; + + d_fnstart(3, dev, "offset %zu\n", offset); + cmd = (void *) bcf + offset; + if (i2400m->sboot == 0) { + struct i2400m_bootrom_header jump_ack; + d_printf(3, dev, "unsecure boot, jumping to 0x%08x\n", + le32_to_cpu(cmd->target_addr)); + i2400m_brh_set_opcode(cmd, I2400M_BRH_JUMP); + cmd->data_size = 0; + ret = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), + &jump_ack, sizeof(jump_ack), 0); + } else { + d_printf(3, dev, "secure boot, jumping to 0x%08x\n", + le32_to_cpu(cmd->target_addr)); + cmd_buf = i2400m->bm_cmd_buf; + memcpy(&cmd_buf->cmd, cmd, sizeof(*cmd)); + signature_block_offset = + sizeof(*bcf) + + le32_to_cpu(bcf->key_size) * sizeof(u32) + + le32_to_cpu(bcf->exponent_size) * sizeof(u32); + signature_block_size = + le32_to_cpu(bcf->modulus_size) * sizeof(u32); + memcpy(cmd_buf->cmd_pl, (void *) bcf + signature_block_offset, + signature_block_size); + ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, + sizeof(cmd_buf->cmd) + signature_block_size, + &ack, sizeof(ack), I2400M_BM_CMD_RAW); + } + d_fnend(3, dev, "returning %d\n", ret); + return ret; +} + + +/** + * i2400m_bootrom_init - Reboots a powered device into boot mode + * + * @i2400m: device descriptor + * @flags: + * I2400M_BRI_SOFT: a reboot notification has been seen + * already, so don't wait for it. + * + * I2400M_BRI_NO_REBOOT: Don't send a reboot command, but wait + * for a reboot barker notification. This is a one shot; if + * the state machine needs to send a reboot command it will. + * + * Returns: + * + * < 0 errno code on error, 0 if ok. + * + * i2400m->sboot set to 0 for unsecure boot process, 1 for secure + * boot process. + * + * Description: + * + * Tries hard enough to put the device in boot-mode. There are two + * main phases to this: + * + * a. (1) send a reboot command and (2) get a reboot barker + * b. (1) ack the reboot sending a reboot barker and (2) getting an + * ack barker in return + * + * We want to skip (a) in some cases [soft]. The state machine is + * horrible, but it is basically: on each phase, send what has to be + * sent (if any), wait for the answer and act on the answer. We might + * have to backtrack and retry, so we keep a max tries counter for + * that. + * + * If we get a timeout after sending a warm reset, we do it again. + */ +int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct i2400m_bootrom_header *cmd; + struct i2400m_bootrom_header ack; + int count = I2400M_BOOT_RETRIES; + int ack_timeout_cnt = 1; + + BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER)); + BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER)); + + d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags); + result = -ENOMEM; + cmd = i2400m->bm_cmd_buf; + if (flags & I2400M_BRI_SOFT) + goto do_reboot_ack; +do_reboot: + if (--count < 0) + goto error_timeout; + d_printf(4, dev, "device reboot: reboot command [%d # left]\n", + count); + if ((flags & I2400M_BRI_NO_REBOOT) == 0) + i2400m->bus_reset(i2400m, I2400M_RT_WARM); + result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack), + I2400M_BM_CMD_RAW); + flags &= ~I2400M_BRI_NO_REBOOT; + switch (result) { + case -ERESTARTSYS: + d_printf(4, dev, "device reboot: got reboot barker\n"); + break; + case -EISCONN: /* we don't know how it got here...but we follow it */ + d_printf(4, dev, "device reboot: got ack barker - whatever\n"); + goto do_reboot; + case -ETIMEDOUT: /* device has timed out, we might be in boot + * mode already and expecting an ack, let's try + * that */ + dev_info(dev, "warm reset timed out, trying an ack\n"); + goto do_reboot_ack; + case -EPROTO: + case -ESHUTDOWN: /* dev is gone */ + case -EINTR: /* user cancelled */ + goto error_dev_gone; + default: + dev_err(dev, "device reboot: error %d while waiting " + "for reboot barker - rebooting\n", result); + goto do_reboot; + } + /* At this point we ack back with 4 REBOOT barkers and expect + * 4 ACK barkers. This is ugly, as we send a raw command -- + * hence the cast. _bm_cmd() will catch the reboot ack + * notification and report it as -EISCONN. */ +do_reboot_ack: + d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count); + if (i2400m->sboot == 0) + memcpy(cmd, i2400m_NBOOT_BARKER, + sizeof(i2400m_NBOOT_BARKER)); + else + memcpy(cmd, i2400m_SBOOT_BARKER, + sizeof(i2400m_SBOOT_BARKER)); + result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), + &ack, sizeof(ack), I2400M_BM_CMD_RAW); + switch (result) { + case -ERESTARTSYS: + d_printf(4, dev, "reboot ack: got reboot barker - retrying\n"); + if (--count < 0) + goto error_timeout; + goto do_reboot_ack; + case -EISCONN: + d_printf(4, dev, "reboot ack: got ack barker - good\n"); + break; + case -ETIMEDOUT: /* no response, maybe it is the other type? */ + if (ack_timeout_cnt-- >= 0) { + d_printf(4, dev, "reboot ack timedout: " + "trying the other type?\n"); + i2400m->sboot = !i2400m->sboot; + goto do_reboot_ack; + } else { + dev_err(dev, "reboot ack timedout too long: " + "trying reboot\n"); + goto do_reboot; + } + break; + case -EPROTO: + case -ESHUTDOWN: /* dev is gone */ + goto error_dev_gone; + default: + dev_err(dev, "device reboot ack: error %d while waiting for " + "reboot ack barker - rebooting\n", result); + goto do_reboot; + } + d_printf(2, dev, "device reboot ack: got ack barker - boot done\n"); + result = 0; +exit_timeout: +error_dev_gone: + d_fnend(4, dev, "(i2400m %p flags 0x%08x) = %d\n", + i2400m, flags, result); + return result; + +error_timeout: + dev_err(dev, "Timed out waiting for reboot ack, resetting\n"); + i2400m->bus_reset(i2400m, I2400M_RT_BUS); + result = -ETIMEDOUT; + goto exit_timeout; +} + + +/* + * Read the MAC addr + * + * The position this function reads is fixed in device memory and + * always available, even without firmware. + * + * Note we specify we want to read only six bytes, but provide space + * for 16, as we always get it rounded up. + */ +int i2400m_read_mac_addr(struct i2400m *i2400m) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + struct i2400m_bootrom_header *cmd; + struct { + struct i2400m_bootrom_header ack; + u8 ack_pl[16]; + } __attribute__((packed)) ack_buf; + + d_fnstart(5, dev, "(i2400m %p)\n", i2400m); + cmd = i2400m->bm_cmd_buf; + cmd->command = i2400m_brh_command(I2400M_BRH_READ, 0, 1); + cmd->target_addr = cpu_to_le32(0x00203fe8); + cmd->data_size = cpu_to_le32(6); + result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), + &ack_buf.ack, sizeof(ack_buf), 0); + if (result < 0) { + dev_err(dev, "BM: read mac addr failed: %d\n", result); + goto error_read_mac; + } + d_printf(2, dev, + "mac addr is %02x:%02x:%02x:%02x:%02x:%02x\n", + ack_buf.ack_pl[0], ack_buf.ack_pl[1], + ack_buf.ack_pl[2], ack_buf.ack_pl[3], + ack_buf.ack_pl[4], ack_buf.ack_pl[5]); + if (i2400m->bus_bm_mac_addr_impaired == 1) { + ack_buf.ack_pl[0] = 0x00; + ack_buf.ack_pl[1] = 0x16; + ack_buf.ack_pl[2] = 0xd3; + get_random_bytes(&ack_buf.ack_pl[3], 3); + dev_err(dev, "BM is MAC addr impaired, faking MAC addr to " + "mac addr is %02x:%02x:%02x:%02x:%02x:%02x\n", + ack_buf.ack_pl[0], ack_buf.ack_pl[1], + ack_buf.ack_pl[2], ack_buf.ack_pl[3], + ack_buf.ack_pl[4], ack_buf.ack_pl[5]); + result = 0; + } + net_dev->addr_len = ETH_ALEN; + memcpy(net_dev->perm_addr, ack_buf.ack_pl, ETH_ALEN); + memcpy(net_dev->dev_addr, ack_buf.ack_pl, ETH_ALEN); +error_read_mac: + d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; +} + + +/* + * Initialize a non signed boot + * + * This implies sending some magic values to the device's memory. Note + * we convert the values to little endian in the same array + * declaration. + */ +static +int i2400m_dnload_init_nonsigned(struct i2400m *i2400m) +{ +#define POKE(a, d) { \ + .address = __constant_cpu_to_le32(a), \ + .data = __constant_cpu_to_le32(d) \ +} + static const struct { + __le32 address; + __le32 data; + } i2400m_pokes[] = { + POKE(0x081A58, 0xA7810230), + POKE(0x080040, 0x00000000), + POKE(0x080048, 0x00000082), + POKE(0x08004C, 0x0000081F), + POKE(0x080054, 0x00000085), + POKE(0x080058, 0x00000180), + POKE(0x08005C, 0x00000018), + POKE(0x080060, 0x00000010), + POKE(0x080574, 0x00000001), + POKE(0x080550, 0x00000005), + POKE(0xAE0000, 0x00000000), + }; +#undef POKE + unsigned i; + int ret; + struct device *dev = i2400m_dev(i2400m); + + dev_warn(dev, "WARNING!!! non-signed boot UNTESTED PATH!\n"); + + d_fnstart(5, dev, "(i2400m %p)\n", i2400m); + for (i = 0; i < ARRAY_SIZE(i2400m_pokes); i++) { + ret = i2400m_download_chunk(i2400m, &i2400m_pokes[i].data, + sizeof(i2400m_pokes[i].data), + i2400m_pokes[i].address, 1, 1); + if (ret < 0) + break; + } + d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); + return ret; +} + + +/* + * Initialize the signed boot process + * + * @i2400m: device descriptor + * + * @bcf_hdr: pointer to the firmware header; assumes it is fully in + * memory (it has gone through basic validation). + * + * Returns: 0 if ok, < 0 errno code on error, -ERESTARTSYS if the hw + * rebooted. + * + * This writes the firmware BCF header to the device using the + * HASH_PAYLOAD_ONLY command. + */ +static +int i2400m_dnload_init_signed(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf_hdr) +{ + int ret; + struct device *dev = i2400m_dev(i2400m); + struct { + struct i2400m_bootrom_header cmd; + struct i2400m_bcf_hdr cmd_pl; + } __attribute__((packed)) *cmd_buf; + struct i2400m_bootrom_header ack; + + d_fnstart(5, dev, "(i2400m %p bcf_hdr %p)\n", i2400m, bcf_hdr); + cmd_buf = i2400m->bm_cmd_buf; + cmd_buf->cmd.command = + i2400m_brh_command(I2400M_BRH_HASH_PAYLOAD_ONLY, 0, 0); + cmd_buf->cmd.target_addr = 0; + cmd_buf->cmd.data_size = cpu_to_le32(sizeof(cmd_buf->cmd_pl)); + memcpy(&cmd_buf->cmd_pl, bcf_hdr, sizeof(*bcf_hdr)); + ret = i2400m_bm_cmd(i2400m, &cmd_buf->cmd, sizeof(*cmd_buf), + &ack, sizeof(ack), 0); + if (ret >= 0) + ret = 0; + d_fnend(5, dev, "(i2400m %p bcf_hdr %p) = %d\n", i2400m, bcf_hdr, ret); + return ret; +} + + +/* + * Initialize the firmware download at the device size + * + * Multiplex to the one that matters based on the device's mode + * (signed or non-signed). + */ +static +int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + u32 module_id = le32_to_cpu(bcf->module_id); + + if (i2400m->sboot == 0 + && (module_id & I2400M_BCF_MOD_ID_POKES) == 0) { + /* non-signed boot process without pokes */ + result = i2400m_dnload_init_nonsigned(i2400m); + if (result == -ERESTARTSYS) + return result; + if (result < 0) + dev_err(dev, "fw %s: non-signed download " + "initialization failed: %d\n", + i2400m->bus_fw_name, result); + } else if (i2400m->sboot == 0 + && (module_id & I2400M_BCF_MOD_ID_POKES)) { + /* non-signed boot process with pokes, nothing to do */ + result = 0; + } else { /* signed boot process */ + result = i2400m_dnload_init_signed(i2400m, bcf); + if (result == -ERESTARTSYS) + return result; + if (result < 0) + dev_err(dev, "fw %s: signed boot download " + "initialization failed: %d\n", + i2400m->bus_fw_name, result); + } + return result; +} + + +/* + * Run quick consistency tests on the firmware file + * + * Check for the firmware being made for the i2400m device, + * etc...These checks are mostly informative, as the device will make + * them too; but the driver's response is more informative on what + * went wrong. + */ +static +int i2400m_fw_check(struct i2400m *i2400m, + const struct i2400m_bcf_hdr *bcf, + size_t bcf_size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + unsigned module_type, header_len, major_version, minor_version, + module_id, module_vendor, date, size; + + /* Check hard errors */ + result = -EINVAL; + if (bcf_size < sizeof(*bcf)) { /* big enough header? */ + dev_err(dev, "firmware %s too short: " + "%zu B vs %zu (at least) expected\n", + i2400m->bus_fw_name, bcf_size, sizeof(*bcf)); + goto error; + } + + module_type = bcf->module_type; + header_len = sizeof(u32) * le32_to_cpu(bcf->header_len); + major_version = le32_to_cpu(bcf->header_version) & 0xffff0000 >> 16; + minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff; + module_id = le32_to_cpu(bcf->module_id); + module_vendor = le32_to_cpu(bcf->module_vendor); + date = le32_to_cpu(bcf->date); + size = sizeof(u32) * le32_to_cpu(bcf->size); + + if (bcf_size != size) { /* annoyingly paranoid */ + dev_err(dev, "firmware %s: bad size, got " + "%zu B vs %u expected\n", + i2400m->bus_fw_name, bcf_size, size); + goto error; + } + + d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) " + "date %08x (%zu B)\n", + module_type, module_id, module_vendor, + major_version, minor_version, (size_t) header_len, + date, (size_t) size); + + if (module_type != 6) { /* built for the right hardware? */ + dev_err(dev, "bad fw %s: unexpected module type 0x%x; " + "aborting\n", i2400m->bus_fw_name, module_type); + goto error; + } + + /* Check soft-er errors */ + result = 0; + if (module_vendor != 0x8086) + dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n", + i2400m->bus_fw_name, module_vendor); + if (date < 0x20080300) + dev_err(dev, "bad fw %s? build date too old %08x\n", + i2400m->bus_fw_name, date); +error: + return result; +} + + +/* + * Download the firmware to the device + * + * @i2400m: device descriptor + * @bcf: pointer to loaded (and minimally verified for consistency) + * firmware + * @bcf_size: size of the @bcf buffer (header plus payloads) + * + * The process for doing this is described in this file's header. + * + * Note we only reinitialize boot-mode if the flags say so. Some hw + * iterations need it, some don't. In any case, if we loop, we always + * need to reinitialize the boot room, hence the flags modification. + */ +static +int i2400m_fw_dnload(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf, + size_t bcf_size, enum i2400m_bri flags) +{ + int ret = 0; + struct device *dev = i2400m_dev(i2400m); + int count = I2400M_BOOT_RETRIES; + + d_fnstart(5, dev, "(i2400m %p bcf %p size %zu)\n", + i2400m, bcf, bcf_size); + i2400m->boot_mode = 1; +hw_reboot: + if (count-- == 0) { + ret = -ERESTARTSYS; + dev_err(dev, "device rebooted too many times, aborting\n"); + goto error_too_many_reboots; + } + if (flags & I2400M_BRI_MAC_REINIT) { + ret = i2400m_bootrom_init(i2400m, flags); + if (ret < 0) { + dev_err(dev, "bootrom init failed: %d\n", ret); + goto error_bootrom_init; + } + } + flags |= I2400M_BRI_MAC_REINIT; + + /* + * Initialize the download, push the bytes to the device and + * then jump to the new firmware. Note @ret is passed with the + * offset of the jump instruction to _dnload_finalize() + */ + ret = i2400m_dnload_init(i2400m, bcf); /* Init device's dnload */ + if (ret == -ERESTARTSYS) + goto error_dev_rebooted; + if (ret < 0) + goto error_dnload_init; + + ret = i2400m_dnload_bcf(i2400m, bcf, bcf_size); + if (ret == -ERESTARTSYS) + goto error_dev_rebooted; + if (ret < 0) { + dev_err(dev, "fw %s: download failed: %d\n", + i2400m->bus_fw_name, ret); + goto error_dnload_bcf; + } + + ret = i2400m_dnload_finalize(i2400m, bcf, ret); + if (ret == -ERESTARTSYS) + goto error_dev_rebooted; + if (ret < 0) { + dev_err(dev, "fw %s: " + "download finalization failed: %d\n", + i2400m->bus_fw_name, ret); + goto error_dnload_finalize; + } + + d_printf(2, dev, "fw %s successfully uploaded\n", + i2400m->bus_fw_name); + i2400m->boot_mode = 0; +error_dnload_finalize: +error_dnload_bcf: +error_dnload_init: +error_bootrom_init: +error_too_many_reboots: + d_fnend(5, dev, "(i2400m %p bcf %p size %zu) = %d\n", + i2400m, bcf, bcf_size, ret); + return ret; + +error_dev_rebooted: + dev_err(dev, "device rebooted, %d tries left\n", count); + /* we got the notification already, no need to wait for it again */ + flags |= I2400M_BRI_SOFT; + goto hw_reboot; +} + + +/** + * i2400m_dev_bootstrap - Bring the device to a known state and upload firmware + * + * @i2400m: device descriptor + * + * Returns: >= 0 if ok, < 0 errno code on error. + * + * This sets up the firmware upload environment, loads the firmware + * file from disk, verifies and then calls the firmware upload process + * per se. + * + * Can be called either from probe, or after a warm reset. Can not be + * called from within an interrupt. All the flow in this code is + * single-threade; all I/Os are synchronous. + */ +int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) +{ + int ret = 0; + struct device *dev = i2400m_dev(i2400m); + const struct firmware *fw; + const struct i2400m_bcf_hdr *bcf; /* Firmware data */ + + d_fnstart(5, dev, "(i2400m %p)\n", i2400m); + /* Load firmware files to memory. */ + ret = request_firmware(&fw, i2400m->bus_fw_name, dev); + if (ret) { + dev_err(dev, "fw %s: request failed: %d\n", + i2400m->bus_fw_name, ret); + goto error_fw_req; + } + bcf = (void *) fw->data; + + ret = i2400m_fw_check(i2400m, bcf, fw->size); + if (ret < 0) + goto error_fw_bad; + ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); +error_fw_bad: + release_firmware(fw); +error_fw_req: + d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); + return ret; +} +EXPORT_SYMBOL_GPL(i2400m_dev_bootstrap); diff --git a/drivers/net/wimax/i2400m/i2400m-sdio.h b/drivers/net/wimax/i2400m/i2400m-sdio.h new file mode 100644 index 000000000000..08c2fb739234 --- /dev/null +++ b/drivers/net/wimax/i2400m/i2400m-sdio.h @@ -0,0 +1,132 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * SDIO-specific i2400m driver definitions + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Brian Bian <brian.bian@intel.com> + * Dirk Brandewie <dirk.j.brandewie@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * + * + * This driver implements the bus-specific part of the i2400m for + * SDIO. Check i2400m.h for a generic driver description. + * + * ARCHITECTURE + * + * This driver sits under the bus-generic i2400m driver, providing the + * connection to the device. + * + * When probed, all the function pointers are setup and then the + * bus-generic code called. The generic driver will then use the + * provided pointers for uploading firmware (i2400ms_bus_bm*() in + * sdio-fw.c) and then setting up the device (i2400ms_dev_*() in + * sdio.c). + * + * Once firmware is uploaded, TX functions (sdio-tx.c) are called when + * data is ready for transmission in the TX fifo; then the SDIO IRQ is + * fired and data is available (sdio-rx.c), it is sent to the generic + * driver for processing with i2400m_rx. + */ + +#ifndef __I2400M_SDIO_H__ +#define __I2400M_SDIO_H__ + +#include "i2400m.h" + +/* Host-Device interface for SDIO */ +enum { + I2400MS_BLK_SIZE = 256, + I2400MS_PL_SIZE_MAX = 0x3E00, + + I2400MS_DATA_ADDR = 0x0, + I2400MS_INTR_STATUS_ADDR = 0x13, + I2400MS_INTR_CLEAR_ADDR = 0x13, + I2400MS_INTR_ENABLE_ADDR = 0x14, + I2400MS_INTR_GET_SIZE_ADDR = 0x2C, + /* The number of ticks to wait for the device to signal that + * it is ready */ + I2400MS_INIT_SLEEP_INTERVAL = 10, +}; + + +/** + * struct i2400ms - descriptor for a SDIO connected i2400m + * + * @i2400m: bus-generic i2400m implementation; has to be first (see + * it's documentation in i2400m.h). + * + * @func: pointer to our SDIO function + * + * @tx_worker: workqueue struct used to TX data when the bus-generic + * code signals packets are pending for transmission to the device. + * + * @tx_workqueue: workqeueue used for data TX; we don't use the + * system's workqueue as that might cause deadlocks with code in + * the bus-generic driver. + */ +struct i2400ms { + struct i2400m i2400m; /* FIRST! See doc */ + struct sdio_func *func; + + struct work_struct tx_worker; + struct workqueue_struct *tx_workqueue; + char tx_wq_name[32]; + + struct dentry *debugfs_dentry; +}; + + +static inline +void i2400ms_init(struct i2400ms *i2400ms) +{ + i2400m_init(&i2400ms->i2400m); +} + + +extern int i2400ms_rx_setup(struct i2400ms *); +extern void i2400ms_rx_release(struct i2400ms *); +extern ssize_t __i2400ms_rx_get_size(struct i2400ms *); + +extern int i2400ms_tx_setup(struct i2400ms *); +extern void i2400ms_tx_release(struct i2400ms *); +extern void i2400ms_bus_tx_kick(struct i2400m *); + +extern ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *, + const struct i2400m_bootrom_header *, + size_t, int); +extern ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *, + struct i2400m_bootrom_header *, + size_t); +#endif /* #ifndef __I2400M_SDIO_H__ */ diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h new file mode 100644 index 000000000000..6f76558b170f --- /dev/null +++ b/drivers/net/wimax/i2400m/i2400m-usb.h @@ -0,0 +1,264 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * USB-specific i2400m driver definitions + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * + * + * This driver implements the bus-specific part of the i2400m for + * USB. Check i2400m.h for a generic driver description. + * + * ARCHITECTURE + * + * This driver listens to notifications sent from the notification + * endpoint (in usb-notif.c); when data is ready to read, the code in + * there schedules a read from the device (usb-rx.c) and then passes + * the data to the generic RX code (rx.c). + * + * When the generic driver needs to send data (network or control), it + * queues up in the TX FIFO (tx.c) and that will notify the driver + * through the i2400m->bus_tx_kick() callback + * (usb-tx.c:i2400mu_bus_tx_kick) which will send the items in the + * FIFO queue. + * + * This driver, as well, implements the USB-specific ops for the generic + * driver to be able to setup/teardown communication with the device + * [i2400m_bus_dev_start() and i2400m_bus_dev_stop()], reseting the + * device [i2400m_bus_reset()] and performing firmware upload + * [i2400m_bus_bm_cmd() and i2400_bus_bm_wait_for_ack()]. + */ + +#ifndef __I2400M_USB_H__ +#define __I2400M_USB_H__ + +#include "i2400m.h" +#include <linux/kthread.h> + + +/* + * Error Density Count: cheapo error density (over time) counter + * + * Originally by Reinette Chatre <reinette.chatre@intel.com> + * + * Embed an 'struct edc' somewhere. Each time there is a soft or + * retryable error, call edc_inc() and check if the error top + * watermark has been reached. + */ +enum { + EDC_MAX_ERRORS = 10, + EDC_ERROR_TIMEFRAME = HZ, +}; + +/* error density counter */ +struct edc { + unsigned long timestart; + u16 errorcount; +}; + +static inline void edc_init(struct edc *edc) +{ + edc->timestart = jiffies; +} + +/** + * edc_inc - report a soft error and check if we are over the watermark + * + * @edc: pointer to error density counter. + * @max_err: maximum number of errors we can accept over the timeframe + * @timeframe: lenght of the timeframe (in jiffies). + * + * Returns: !0 1 if maximum acceptable errors per timeframe has been + * exceeded. 0 otherwise. + * + * This is way to determine if the number of acceptable errors per time + * period has been exceeded. It is not accurate as there are cases in which + * this scheme will not work, for example if there are periodic occurences + * of errors that straddle updates to the start time. This scheme is + * sufficient for our usage. + * + * To use, embed a 'struct edc' somewhere, initialize it with + * edc_init() and when an error hits: + * + * if (do_something_fails_with_a_soft_error) { + * if (edc_inc(&my->edc, MAX_ERRORS, MAX_TIMEFRAME)) + * Ops, hard error, do something about it + * else + * Retry or ignore, depending on whatever + * } + */ +static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe) +{ + unsigned long now; + + now = jiffies; + if (now - edc->timestart > timeframe) { + edc->errorcount = 1; + edc->timestart = now; + } else if (++edc->errorcount > max_err) { + edc->errorcount = 0; + edc->timestart = now; + return 1; + } + return 0; +} + +/* Host-Device interface for USB */ +enum { + I2400MU_MAX_NOTIFICATION_LEN = 256, + I2400MU_BLK_SIZE = 16, + I2400MU_PL_SIZE_MAX = 0x3EFF, + + /* Endpoints */ + I2400MU_EP_BULK_OUT = 0, + I2400MU_EP_NOTIFICATION, + I2400MU_EP_RESET_COLD, + I2400MU_EP_BULK_IN, +}; + + +/** + * struct i2400mu - descriptor for a USB connected i2400m + * + * @i2400m: bus-generic i2400m implementation; has to be first (see + * it's documentation in i2400m.h). + * + * @usb_dev: pointer to our USB device + * + * @usb_iface: pointer to our USB interface + * + * @urb_edc: error density counter; used to keep a density-on-time tab + * on how many soft (retryable or ignorable) errors we get. If we + * go over the threshold, we consider the bus transport is failing + * too much and reset. + * + * @notif_urb: URB for receiving notifications from the device. + * + * @tx_kthread: thread we use for data TX. We use a thread because in + * order to do deep power saving and put the device to sleep, we + * need to call usb_autopm_*() [blocking functions]. + * + * @tx_wq: waitqueue for the TX kthread to sleep when there is no data + * to be sent; when more data is available, it is woken up by + * i2400mu_bus_tx_kick(). + * + * @rx_kthread: thread we use for data RX. We use a thread because in + * order to do deep power saving and put the device to sleep, we + * need to call usb_autopm_*() [blocking functions]. + * + * @rx_wq: waitqueue for the RX kthread to sleep when there is no data + * to receive. When data is available, it is woken up by + * usb-notif.c:i2400mu_notification_grok(). + * + * @rx_pending_count: number of rx-data-ready notifications that were + * still not handled by the RX kthread. + * + * @rx_size: current RX buffer size that is being used. + * + * @rx_size_acc: accumulator of the sizes of the previous read + * transactions. + * + * @rx_size_cnt: number of read transactions accumulated in + * @rx_size_acc. + * + * @do_autopm: disable(0)/enable(>0) calling the + * usb_autopm_get/put_interface() barriers when executing + * commands. See doc in i2400mu_suspend() for more information. + * + * @rx_size_auto_shrink: if true, the rx_size is shrinked + * automatically based on the average size of the received + * transactions. This allows the receive code to allocate smaller + * chunks of memory and thus reduce pressure on the memory + * allocator by not wasting so much space. By default it is + * enabled. + * + * @debugfs_dentry: hookup for debugfs files. + * These have to be in a separate directory, a child of + * (wimax_dev->debugfs_dentry) so they can be removed when the + * module unloads, as we don't keep each dentry. + */ +struct i2400mu { + struct i2400m i2400m; /* FIRST! See doc */ + + struct usb_device *usb_dev; + struct usb_interface *usb_iface; + struct edc urb_edc; /* Error density counter */ + + struct urb *notif_urb; + struct task_struct *tx_kthread; + wait_queue_head_t tx_wq; + + struct task_struct *rx_kthread; + wait_queue_head_t rx_wq; + atomic_t rx_pending_count; + size_t rx_size, rx_size_acc, rx_size_cnt; + atomic_t do_autopm; + u8 rx_size_auto_shrink; + + struct dentry *debugfs_dentry; +}; + + +static inline +void i2400mu_init(struct i2400mu *i2400mu) +{ + i2400m_init(&i2400mu->i2400m); + edc_init(&i2400mu->urb_edc); + init_waitqueue_head(&i2400mu->tx_wq); + atomic_set(&i2400mu->rx_pending_count, 0); + init_waitqueue_head(&i2400mu->rx_wq); + i2400mu->rx_size = PAGE_SIZE - sizeof(struct skb_shared_info); + atomic_set(&i2400mu->do_autopm, 1); + i2400mu->rx_size_auto_shrink = 1; +} + +extern int i2400mu_notification_setup(struct i2400mu *); +extern void i2400mu_notification_release(struct i2400mu *); + +extern int i2400mu_rx_setup(struct i2400mu *); +extern void i2400mu_rx_release(struct i2400mu *); +extern void i2400mu_rx_kick(struct i2400mu *); + +extern int i2400mu_tx_setup(struct i2400mu *); +extern void i2400mu_tx_release(struct i2400mu *); +extern void i2400mu_bus_tx_kick(struct i2400m *); + +extern ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *, + const struct i2400m_bootrom_header *, + size_t, int); +extern ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *, + struct i2400m_bootrom_header *, + size_t); +#endif /* #ifndef __I2400M_USB_H__ */ diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h new file mode 100644 index 000000000000..067c871cc226 --- /dev/null +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -0,0 +1,755 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Declarations for bus-generic internal APIs + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * + * + * GENERAL DRIVER ARCHITECTURE + * + * The i2400m driver is split in the following two major parts: + * + * - bus specific driver + * - bus generic driver (this part) + * + * The bus specific driver sets up stuff specific to the bus the + * device is connected to (USB, SDIO, PCI, tam-tam...non-authoritative + * nor binding list) which is basically the device-model management + * (probe/disconnect, etc), moving data from device to kernel and + * back, doing the power saving details and reseting the device. + * + * For details on each bus-specific driver, see it's include file, + * i2400m-BUSNAME.h + * + * The bus-generic functionality break up is: + * + * - Firmware upload: fw.c - takes care of uploading firmware to the + * device. bus-specific driver just needs to provides a way to + * execute boot-mode commands and to reset the device. + * + * - RX handling: rx.c - receives data from the bus-specific code and + * feeds it to the network or WiMAX stack or uses it to modify + * the driver state. bus-specific driver only has to receive + * frames and pass them to this module. + * + * - TX handling: tx.c - manages the TX FIFO queue and provides means + * for the bus-specific TX code to pull data from the FIFO + * queue. bus-specific code just pulls frames from this module + * to sends them to the device. + * + * - netdev glue: netdev.c - interface with Linux networking + * stack. Pass around data frames, and configure when the + * device is up and running or shutdown (through ifconfig up / + * down). Bus-generic only. + * + * - control ops: control.c - implements various commmands for + * controlling the device. bus-generic only. + * + * - device model glue: driver.c - implements helpers for the + * device-model glue done by the bus-specific layer + * (setup/release the driver resources), turning the device on + * and off, handling the device reboots/resets and a few simple + * WiMAX stack ops. + * + * Code is also broken up in linux-glue / device-glue. + * + * Linux glue contains functions that deal mostly with gluing with the + * rest of the Linux kernel. + * + * Device-glue are functions that deal mostly with the way the device + * does things and talk the device's language. + * + * device-glue code is licensed BSD so other open source OSes can take + * it to implement their drivers. + * + * + * APIs AND HEADER FILES + * + * This bus generic code exports three APIs: + * + * - HDI (host-device interface) definitions common to all busses + * (include/linux/wimax/i2400m.h); these can be also used by user + * space code. + * - internal API for the bus-generic code + * - external API for the bus-specific drivers + * + * + * LIFE CYCLE: + * + * When the bus-specific driver probes, it allocates a network device + * with enough space for it's data structue, that must contain a + * &struct i2400m at the top. + * + * On probe, it needs to fill the i2400m members marked as [fill], as + * well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The + * i2400m driver will only register with the WiMAX and network stacks; + * the only access done to the device is to read the MAC address so we + * can register a network device. This calls i2400m_dev_start() to + * load firmware, setup communication with the device and configure it + * for operation. + * + * At this point, control and data communications are possible. + * + * On disconnect/driver unload, the bus-specific disconnect function + * calls i2400m_release() to undo i2400m_setup(). i2400m_dev_stop() + * shuts the firmware down and releases resources uses to communicate + * with the device. + * + * While the device is up, it might reset. The bus-specific driver has + * to catch that situation and call i2400m_dev_reset_handle() to deal + * with it (reset the internal driver structures and go back to square + * one). + */ + +#ifndef __I2400M_H__ +#define __I2400M_H__ + +#include <linux/usb.h> +#include <linux/netdevice.h> +#include <linux/completion.h> +#include <linux/rwsem.h> +#include <asm/atomic.h> +#include <net/wimax.h> +#include <linux/wimax/i2400m.h> +#include <asm/byteorder.h> + +/* Misc constants */ +enum { + /* Firmware uploading */ + I2400M_BOOT_RETRIES = 3, + /* Size of the Boot Mode Command buffer */ + I2400M_BM_CMD_BUF_SIZE = 16 * 1024, + I2400M_BM_ACK_BUF_SIZE = 256, +}; + + +/* Firmware version we request when pulling the fw image file */ +#define I2400M_FW_VERSION "1.3" + + +/** + * i2400m_reset_type - methods to reset a device + * + * @I2400M_RT_WARM: Reset without device disconnection, device handles + * are kept valid but state is back to power on, with firmware + * re-uploaded. + * @I2400M_RT_COLD: Tell the device to disconnect itself from the bus + * and reconnect. Renders all device handles invalid. + * @I2400M_RT_BUS: Tells the bus to reset the device; last measure + * used when both types above don't work. + */ +enum i2400m_reset_type { + I2400M_RT_WARM, /* first measure */ + I2400M_RT_COLD, /* second measure */ + I2400M_RT_BUS, /* call in artillery */ +}; + +struct i2400m_reset_ctx; + +/** + * struct i2400m - descriptor for an Intel 2400m + * + * Members marked with [fill] must be filled out/initialized before + * calling i2400m_setup(). + * + * @bus_tx_block_size: [fill] SDIO imposes a 256 block size, USB 16, + * so we have a tx_blk_size variable that the bus layer sets to + * tell the engine how much of that we need. + * + * @bus_pl_size_max: [fill] Maximum payload size. + * + * @bus_dev_start: [fill] Function called by the bus-generic code + * [i2400m_dev_start()] to setup the bus-specific communications + * to the the device. See LIFE CYCLE above. + * + * NOTE: Doesn't need to upload the firmware, as that is taken + * care of by the bus-generic code. + * + * @bus_dev_stop: [fill] Function called by the bus-generic code + * [i2400m_dev_stop()] to shutdown the bus-specific communications + * to the the device. See LIFE CYCLE above. + * + * This function does not need to reset the device, just tear down + * all the host resources created to handle communication with + * the device. + * + * @bus_tx_kick: [fill] Function called by the bus-generic code to let + * the bus-specific code know that there is data available in the + * TX FIFO for transmission to the device. + * + * This function cannot sleep. + * + * @bus_reset: [fill] Function called by the bus-generic code to reset + * the device in in various ways. Doesn't need to wait for the + * reset to finish. + * + * If warm or cold reset fail, this function is expected to do a + * bus-specific reset (eg: USB reset) to get the device to a + * working state (even if it implies device disconecction). + * + * Note the warm reset is used by the firmware uploader to + * reinitialize the device. + * + * IMPORTANT: this is called very early in the device setup + * process, so it cannot rely on common infrastructure being laid + * out. + * + * @bus_bm_cmd_send: [fill] Function called to send a boot-mode + * command. Flags are defined in 'enum i2400m_bm_cmd_flags'. This + * is synchronous and has to return 0 if ok or < 0 errno code in + * any error condition. + * + * @bus_bm_wait_for_ack: [fill] Function called to wait for a + * boot-mode notification (that can be a response to a previously + * issued command or an asynchronous one). Will read until all the + * indicated size is read or timeout. Reading more or less data + * than asked for is an error condition. Return 0 if ok, < 0 errno + * code on error. + * + * The caller to this function will check if the response is a + * barker that indicates the device going into reset mode. + * + * @bus_fw_name: [fill] name of the firmware image (in most cases, + * they are all the same for a single release, except that they + * have the type of the bus embedded in the name (eg: + * i2400m-fw-X-VERSION.sbcf, where X is the bus name). + * + * @bus_bm_mac_addr_impaired: [fill] Set to true if the device's MAC + * address provided in boot mode is kind of broken and needs to + * be re-read later on. + * + * + * @wimax_dev: WiMAX generic device for linkage into the kernel WiMAX + * stack. Due to the way a net_device is allocated, we need to + * force this to be the first field so that we can get from + * netdev_priv() the right pointer. + * + * @state: device's state (as reported by it) + * + * @state_wq: waitqueue that is woken up whenever the state changes + * + * @tx_lock: spinlock to protect TX members + * + * @tx_buf: FIFO buffer for TX; we queue data here + * + * @tx_in: FIFO index for incoming data. Note this doesn't wrap around + * and it is always greater than @tx_out. + * + * @tx_out: FIFO index for outgoing data + * + * @tx_msg: current TX message that is active in the FIFO for + * appending payloads. + * + * @tx_sequence: current sequence number for TX messages from the + * device to the host. + * + * @tx_msg_size: size of the current message being transmitted by the + * bus-specific code. + * + * @tx_pl_num: total number of payloads sent + * + * @tx_pl_max: maximum number of payloads sent in a TX message + * + * @tx_pl_min: minimum number of payloads sent in a TX message + * + * @tx_num: number of TX messages sent + * + * @tx_size_acc: number of bytes in all TX messages sent + * (this is different to net_dev's statistics as it also counts + * control messages). + * + * @tx_size_min: smallest TX message sent. + * + * @tx_size_max: biggest TX message sent. + * + * @rx_lock: spinlock to protect RX members + * + * @rx_pl_num: total number of payloads received + * + * @rx_pl_max: maximum number of payloads received in a RX message + * + * @rx_pl_min: minimum number of payloads received in a RX message + * + * @rx_num: number of RX messages received + * + * @rx_size_acc: number of bytes in all RX messages received + * (this is different to net_dev's statistics as it also counts + * control messages). + * + * @rx_size_min: smallest RX message received. + * + * @rx_size_max: buggest RX message received. + * + * @init_mutex: Mutex used for serializing the device bringup + * sequence; this way if the device reboots in the middle, we + * don't try to do a bringup again while we are tearing down the + * one that failed. + * + * Can't reuse @msg_mutex because from within the bringup sequence + * we need to send messages to the device and thus use @msg_mutex. + * + * @msg_mutex: mutex used to send control commands to the device (we + * only allow one at a time, per host-device interface design). + * + * @msg_completion: used to wait for an ack to a control command sent + * to the device. + * + * @ack_skb: used to store the actual ack to a control command if the + * reception of the command was successful. Otherwise, a ERR_PTR() + * errno code that indicates what failed with the ack reception. + * + * Only valid after @msg_completion is woken up. Only updateable + * if @msg_completion is armed. Only touched by + * i2400m_msg_to_dev(). + * + * Protected by @rx_lock. In theory the command execution flow is + * sequential, but in case the device sends an out-of-phase or + * very delayed response, we need to avoid it trampling current + * execution. + * + * @bm_cmd_buf: boot mode command buffer for composing firmware upload + * commands. + * + * USB can't r/w to stack, vmalloc, etc...as well, we end up + * having to alloc/free a lot to compose commands, so we use these + * for stagging and not having to realloc all the time. + * + * This assumes the code always runs serialized. Only one thread + * can call i2400m_bm_cmd() at the same time. + * + * @bm_ack_buf: boot mode acknoledge buffer for staging reception of + * responses to commands. + * + * See @bm_cmd_buf. + * + * @work_queue: work queue for processing device reports. This + * workqueue cannot be used for processing TX or RX to the device, + * as from it we'll process device reports, which might require + * further communication with the device. + * + * @debugfs_dentry: hookup for debugfs files. + * These have to be in a separate directory, a child of + * (wimax_dev->debugfs_dentry) so they can be removed when the + * module unloads, as we don't keep each dentry. + */ +struct i2400m { + struct wimax_dev wimax_dev; /* FIRST! See doc */ + + unsigned updown:1; /* Network device is up or down */ + unsigned boot_mode:1; /* is the device in boot mode? */ + unsigned sboot:1; /* signed or unsigned fw boot */ + unsigned ready:1; /* all probing steps done */ + u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ + /* typed u8 so debugfs/u8 can tweak */ + enum i2400m_system_state state; + wait_queue_head_t state_wq; /* Woken up when on state updates */ + + size_t bus_tx_block_size; + size_t bus_pl_size_max; + int (*bus_dev_start)(struct i2400m *); + void (*bus_dev_stop)(struct i2400m *); + void (*bus_tx_kick)(struct i2400m *); + int (*bus_reset)(struct i2400m *, enum i2400m_reset_type); + ssize_t (*bus_bm_cmd_send)(struct i2400m *, + const struct i2400m_bootrom_header *, + size_t, int flags); + ssize_t (*bus_bm_wait_for_ack)(struct i2400m *, + struct i2400m_bootrom_header *, size_t); + const char *bus_fw_name; + unsigned bus_bm_mac_addr_impaired:1; + + spinlock_t tx_lock; /* protect TX state */ + void *tx_buf; + size_t tx_in, tx_out; + struct i2400m_msg_hdr *tx_msg; + size_t tx_sequence, tx_msg_size; + /* TX stats */ + unsigned tx_pl_num, tx_pl_max, tx_pl_min, + tx_num, tx_size_acc, tx_size_min, tx_size_max; + + /* RX stats */ + spinlock_t rx_lock; /* protect RX state */ + unsigned rx_pl_num, rx_pl_max, rx_pl_min, + rx_num, rx_size_acc, rx_size_min, rx_size_max; + + struct mutex msg_mutex; /* serialize command execution */ + struct completion msg_completion; + struct sk_buff *ack_skb; /* protected by rx_lock */ + + void *bm_ack_buf; /* for receiving acks over USB */ + void *bm_cmd_buf; /* for issuing commands over USB */ + + struct workqueue_struct *work_queue; + + struct mutex init_mutex; /* protect bringup seq */ + struct i2400m_reset_ctx *reset_ctx; /* protected by init_mutex */ + + struct work_struct wake_tx_ws; + struct sk_buff *wake_tx_skb; + + struct dentry *debugfs_dentry; +}; + + +/* + * Initialize a 'struct i2400m' from all zeroes + * + * This is a bus-generic API call. + */ +static inline +void i2400m_init(struct i2400m *i2400m) +{ + wimax_dev_init(&i2400m->wimax_dev); + + i2400m->boot_mode = 1; + init_waitqueue_head(&i2400m->state_wq); + + spin_lock_init(&i2400m->tx_lock); + i2400m->tx_pl_min = UINT_MAX; + i2400m->tx_size_min = UINT_MAX; + + spin_lock_init(&i2400m->rx_lock); + i2400m->rx_pl_min = UINT_MAX; + i2400m->rx_size_min = UINT_MAX; + + mutex_init(&i2400m->msg_mutex); + init_completion(&i2400m->msg_completion); + + mutex_init(&i2400m->init_mutex); + /* wake_tx_ws is initialized in i2400m_tx_setup() */ +} + + +/* + * Bus-generic internal APIs + * ------------------------- + */ + +static inline +struct i2400m *wimax_dev_to_i2400m(struct wimax_dev *wimax_dev) +{ + return container_of(wimax_dev, struct i2400m, wimax_dev); +} + +static inline +struct i2400m *net_dev_to_i2400m(struct net_device *net_dev) +{ + return wimax_dev_to_i2400m(netdev_priv(net_dev)); +} + +/* + * Boot mode support + */ + +/** + * i2400m_bm_cmd_flags - flags to i2400m_bm_cmd() + * + * @I2400M_BM_CMD_RAW: send the command block as-is, without doing any + * extra processing for adding CRC. + */ +enum i2400m_bm_cmd_flags { + I2400M_BM_CMD_RAW = 1 << 2, +}; + +/** + * i2400m_bri - Boot-ROM indicators + * + * Flags for i2400m_bootrom_init() and i2400m_dev_bootstrap() [which + * are passed from things like i2400m_setup()]. Can be combined with + * |. + * + * @I2400M_BRI_SOFT: The device rebooted already and a reboot + * barker received, proceed directly to ack the boot sequence. + * @I2400M_BRI_NO_REBOOT: Do not reboot the device and proceed + * directly to wait for a reboot barker from the device. + * @I2400M_BRI_MAC_REINIT: We need to reinitialize the boot + * rom after reading the MAC adress. This is quite a dirty hack, + * if you ask me -- the device requires the bootrom to be + * intialized after reading the MAC address. + */ +enum i2400m_bri { + I2400M_BRI_SOFT = 1 << 1, + I2400M_BRI_NO_REBOOT = 1 << 2, + I2400M_BRI_MAC_REINIT = 1 << 3, +}; + +extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *); +extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); +extern int i2400m_read_mac_addr(struct i2400m *); +extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); + +/* Make/grok boot-rom header commands */ + +static inline +__le32 i2400m_brh_command(enum i2400m_brh_opcode opcode, unsigned use_checksum, + unsigned direct_access) +{ + return cpu_to_le32( + I2400M_BRH_SIGNATURE + | (direct_access ? I2400M_BRH_DIRECT_ACCESS : 0) + | I2400M_BRH_RESPONSE_REQUIRED /* response always required */ + | (use_checksum ? I2400M_BRH_USE_CHECKSUM : 0) + | (opcode & I2400M_BRH_OPCODE_MASK)); +} + +static inline +void i2400m_brh_set_opcode(struct i2400m_bootrom_header *hdr, + enum i2400m_brh_opcode opcode) +{ + hdr->command = cpu_to_le32( + (le32_to_cpu(hdr->command) & ~I2400M_BRH_OPCODE_MASK) + | (opcode & I2400M_BRH_OPCODE_MASK)); +} + +static inline +unsigned i2400m_brh_get_opcode(const struct i2400m_bootrom_header *hdr) +{ + return le32_to_cpu(hdr->command) & I2400M_BRH_OPCODE_MASK; +} + +static inline +unsigned i2400m_brh_get_response(const struct i2400m_bootrom_header *hdr) +{ + return (le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_MASK) + >> I2400M_BRH_RESPONSE_SHIFT; +} + +static inline +unsigned i2400m_brh_get_use_checksum(const struct i2400m_bootrom_header *hdr) +{ + return le32_to_cpu(hdr->command) & I2400M_BRH_USE_CHECKSUM; +} + +static inline +unsigned i2400m_brh_get_response_required( + const struct i2400m_bootrom_header *hdr) +{ + return le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_REQUIRED; +} + +static inline +unsigned i2400m_brh_get_direct_access(const struct i2400m_bootrom_header *hdr) +{ + return le32_to_cpu(hdr->command) & I2400M_BRH_DIRECT_ACCESS; +} + +static inline +unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr) +{ + return (le32_to_cpu(hdr->command) & I2400M_BRH_SIGNATURE_MASK) + >> I2400M_BRH_SIGNATURE_SHIFT; +} + + +/* + * Driver / device setup and internal functions + */ +extern void i2400m_netdev_setup(struct net_device *net_dev); +extern int i2400m_tx_setup(struct i2400m *); +extern void i2400m_wake_tx_work(struct work_struct *); +extern void i2400m_tx_release(struct i2400m *); + +extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, + const void *, int); +enum i2400m_pt; +extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); + +#ifdef CONFIG_DEBUG_FS +extern int i2400m_debugfs_add(struct i2400m *); +extern void i2400m_debugfs_rm(struct i2400m *); +#else +static inline int i2400m_debugfs_add(struct i2400m *i2400m) +{ + return 0; +} +static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {} +#endif + +/* Called by _dev_start()/_dev_stop() to initialize the device itself */ +extern int i2400m_dev_initialize(struct i2400m *); +extern void i2400m_dev_shutdown(struct i2400m *); + +extern struct attribute_group i2400m_dev_attr_group; + +extern int i2400m_schedule_work(struct i2400m *, + void (*)(struct work_struct *), gfp_t); + +/* HDI message's payload description handling */ + +static inline +size_t i2400m_pld_size(const struct i2400m_pld *pld) +{ + return I2400M_PLD_SIZE_MASK & le32_to_cpu(pld->val); +} + +static inline +enum i2400m_pt i2400m_pld_type(const struct i2400m_pld *pld) +{ + return (I2400M_PLD_TYPE_MASK & le32_to_cpu(pld->val)) + >> I2400M_PLD_TYPE_SHIFT; +} + +static inline +void i2400m_pld_set(struct i2400m_pld *pld, size_t size, + enum i2400m_pt type) +{ + pld->val = cpu_to_le32( + ((type << I2400M_PLD_TYPE_SHIFT) & I2400M_PLD_TYPE_MASK) + | (size & I2400M_PLD_SIZE_MASK)); +} + + +/* + * API for the bus-specific drivers + * -------------------------------- + */ + +static inline +struct i2400m *i2400m_get(struct i2400m *i2400m) +{ + dev_hold(i2400m->wimax_dev.net_dev); + return i2400m; +} + +static inline +void i2400m_put(struct i2400m *i2400m) +{ + dev_put(i2400m->wimax_dev.net_dev); +} + +extern int i2400m_dev_reset_handle(struct i2400m *); + +/* + * _setup()/_release() are called by the probe/disconnect functions of + * the bus-specific drivers. + */ +extern int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags); +extern void i2400m_release(struct i2400m *); + +extern int i2400m_rx(struct i2400m *, struct sk_buff *); +extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); +extern void i2400m_tx_msg_sent(struct i2400m *); + +static const __le32 i2400m_NBOOT_BARKER[4] = { + __constant_cpu_to_le32(I2400M_NBOOT_BARKER), + __constant_cpu_to_le32(I2400M_NBOOT_BARKER), + __constant_cpu_to_le32(I2400M_NBOOT_BARKER), + __constant_cpu_to_le32(I2400M_NBOOT_BARKER) +}; + +static const __le32 i2400m_SBOOT_BARKER[4] = { + __constant_cpu_to_le32(I2400M_SBOOT_BARKER), + __constant_cpu_to_le32(I2400M_SBOOT_BARKER), + __constant_cpu_to_le32(I2400M_SBOOT_BARKER), + __constant_cpu_to_le32(I2400M_SBOOT_BARKER) +}; + + +/* + * Utility functions + */ + +static inline +struct device *i2400m_dev(struct i2400m *i2400m) +{ + return i2400m->wimax_dev.net_dev->dev.parent; +} + +/* + * Helper for scheduling simple work functions + * + * This struct can get any kind of payload attached (normally in the + * form of a struct where you pack the stuff you want to pass to the + * _work function). + */ +struct i2400m_work { + struct work_struct ws; + struct i2400m *i2400m; + u8 pl[0]; +}; +extern int i2400m_queue_work(struct i2400m *, + void (*)(struct work_struct *), gfp_t, + const void *, size_t); + +extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, + char *, size_t); +extern int i2400m_msg_size_check(struct i2400m *, + const struct i2400m_l3l4_hdr *, size_t); +extern struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t); +extern void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int); +extern void i2400m_msg_ack_hook(struct i2400m *, + const struct i2400m_l3l4_hdr *, size_t); +extern void i2400m_report_hook(struct i2400m *, + const struct i2400m_l3l4_hdr *, size_t); +extern int i2400m_cmd_enter_powersave(struct i2400m *); +extern int i2400m_cmd_get_state(struct i2400m *); +extern int i2400m_cmd_exit_idle(struct i2400m *); +extern struct sk_buff *i2400m_get_device_info(struct i2400m *); +extern int i2400m_firmware_check(struct i2400m *); +extern int i2400m_set_init_config(struct i2400m *, + const struct i2400m_tlv_hdr **, size_t); + +static inline +struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep) +{ + return &iface->cur_altsetting->endpoint[ep].desc; +} + +extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, + enum wimax_rf_state); +extern void i2400m_report_tlv_rf_switches_status( + struct i2400m *, const struct i2400m_tlv_rf_switches_status *); + + +/* + * Do a millisecond-sleep for allowing wireshark to dump all the data + * packets. Used only for debugging. + */ +static inline +void __i2400m_msleep(unsigned ms) +{ +#if 1 +#else + msleep(ms); +#endif +} + +/* Module parameters */ + +extern int i2400m_idle_mode_disabled; + + +#endif /* #ifndef __I2400M_H__ */ diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c new file mode 100644 index 000000000000..63fe708e8a31 --- /dev/null +++ b/drivers/net/wimax/i2400m/netdev.c @@ -0,0 +1,524 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Glue with the networking stack + * + * + * Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This implements an ethernet device for the i2400m. + * + * We fake being an ethernet device to simplify the support from user + * space and from the other side. The world is (sadly) configured to + * take in only Ethernet devices... + * + * Because of this, currently there is an copy-each-rxed-packet + * overhead on the RX path. Each IP packet has to be reallocated to + * add an ethernet header (as there is no space in what we get from + * the device). This is a known drawback and coming versions of the + * device's firmware are being changed to add header space that can be + * used to insert the ethernet header without having to reallocate and + * copy. + * + * TX error handling is tricky; because we have to FIFO/queue the + * buffers for transmission (as the hardware likes it aggregated), we + * just give the skb to the TX subsystem and by the time it is + * transmitted, we have long forgotten about it. So we just don't care + * too much about it. + * + * Note that when the device is in idle mode with the basestation, we + * need to negotiate coming back up online. That involves negotiation + * and possible user space interaction. Thus, we defer to a workqueue + * to do all that. By default, we only queue a single packet and drop + * the rest, as potentially the time to go back from idle to normal is + * long. + * + * ROADMAP + * + * i2400m_open Called on ifconfig up + * i2400m_stop Called on ifconfig down + * + * i2400m_hard_start_xmit Called by the network stack to send a packet + * i2400m_net_wake_tx Wake up device from basestation-IDLE & TX + * i2400m_wake_tx_work + * i2400m_cmd_exit_idle + * i2400m_tx + * i2400m_net_tx TX a data frame + * i2400m_tx + * + * i2400m_change_mtu Called on ifconfig mtu XXX + * + * i2400m_tx_timeout Called when the device times out + * + * i2400m_net_rx Called by the RX code when a data frame is + * available. + * i2400m_netdev_setup Called to setup all the netdev stuff from + * alloc_netdev. + */ +#include <linux/if_arp.h> +#include <linux/netdevice.h> +#include "i2400m.h" + + +#define D_SUBMODULE netdev +#include "debug-levels.h" + +enum { +/* netdev interface */ + /* + * Out of NWG spec (R1_v1.2.2), 3.3.3 ASN Bearer Plane MTU Size + * + * The MTU is 1400 or less + */ + I2400M_MAX_MTU = 1400, + I2400M_TX_TIMEOUT = HZ, + I2400M_TX_QLEN = 5, +}; + + +static +int i2400m_open(struct net_device *net_dev) +{ + int result; + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m); + if (i2400m->ready == 0) { + dev_err(dev, "Device is still initializing\n"); + result = -EBUSY; + } else + result = 0; + d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n", + net_dev, i2400m, result); + return result; +} + + +/* + * + * On kernel versions where cancel_work_sync() didn't return anything, + * we rely on wake_tx_skb() being non-NULL. + */ +static +int i2400m_stop(struct net_device *net_dev) +{ + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m); + /* See i2400m_hard_start_xmit(), references are taken there + * and here we release them if the work was still + * pending. Note we can't differentiate work not pending vs + * never scheduled, so the NULL check does that. */ + if (cancel_work_sync(&i2400m->wake_tx_ws) == 0 + && i2400m->wake_tx_skb != NULL) { + unsigned long flags; + struct sk_buff *wake_tx_skb; + spin_lock_irqsave(&i2400m->tx_lock, flags); + wake_tx_skb = i2400m->wake_tx_skb; /* compat help */ + i2400m->wake_tx_skb = NULL; /* compat help */ + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + i2400m_put(i2400m); + kfree_skb(wake_tx_skb); + } + d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m); + return 0; +} + + +/* + * Wake up the device and transmit a held SKB, then restart the net queue + * + * When the device goes into basestation-idle mode, we need to tell it + * to exit that mode; it will negotiate with the base station, user + * space may have to intervene to rehandshake crypto and then tell us + * when it is ready to transmit the packet we have "queued". Still we + * need to give it sometime after it reports being ok. + * + * On error, there is not much we can do. If the error was on TX, we + * still wake the queue up to see if the next packet will be luckier. + * + * If _cmd_exit_idle() fails...well, it could be many things; most + * commonly it is that something else took the device out of IDLE mode + * (for example, the base station). In that case we get an -EILSEQ and + * we are just going to ignore that one. If the device is back to + * connected, then fine -- if it is someother state, the packet will + * be dropped anyway. + */ +void i2400m_wake_tx_work(struct work_struct *ws) +{ + int result; + struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws); + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *skb = i2400m->wake_tx_skb; + unsigned long flags; + + spin_lock_irqsave(&i2400m->tx_lock, flags); + skb = i2400m->wake_tx_skb; + i2400m->wake_tx_skb = NULL; + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + + d_fnstart(3, dev, "(ws %p i2400m %p skb %p)\n", ws, i2400m, skb); + result = -EINVAL; + if (skb == NULL) { + dev_err(dev, "WAKE&TX: skb dissapeared!\n"); + goto out_put; + } + result = i2400m_cmd_exit_idle(i2400m); + if (result == -EILSEQ) + result = 0; + if (result < 0) { + dev_err(dev, "WAKE&TX: device didn't get out of idle: " + "%d\n", result); + goto error; + } + result = wait_event_timeout(i2400m->state_wq, + i2400m->state != I2400M_SS_IDLE, 5 * HZ); + if (result == 0) + result = -ETIMEDOUT; + if (result < 0) { + dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: " + "%d\n", result); + goto error; + } + msleep(20); /* device still needs some time or it drops it */ + result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA); + netif_wake_queue(i2400m->wimax_dev.net_dev); +error: + kfree_skb(skb); /* refcount transferred by _hard_start_xmit() */ +out_put: + i2400m_put(i2400m); + d_fnend(3, dev, "(ws %p i2400m %p skb %p) = void [%d]\n", + ws, i2400m, skb, result); +} + + +/* + * Prepare the data payload TX header + * + * The i2400m expects a 4 byte header in front of a data packet. + * + * Because we pretend to be an ethernet device, this packet comes with + * an ethernet header. Pull it and push our header. + */ +static +void i2400m_tx_prep_header(struct sk_buff *skb) +{ + struct i2400m_pl_data_hdr *pl_hdr; + skb_pull(skb, ETH_HLEN); + pl_hdr = (struct i2400m_pl_data_hdr *) skb_push(skb, sizeof(*pl_hdr)); + pl_hdr->reserved = 0; +} + + +/* + * TX an skb to an idle device + * + * When the device is in basestation-idle mode, we need to wake it up + * and then TX. So we queue a work_struct for doing so. + * + * We need to get an extra ref for the skb (so it is not dropped), as + * well as be careful not to queue more than one request (won't help + * at all). If more than one request comes or there are errors, we + * just drop the packets (see i2400m_hard_start_xmit()). + */ +static +int i2400m_net_wake_tx(struct i2400m *i2400m, struct net_device *net_dev, + struct sk_buff *skb) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + unsigned long flags; + + d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev); + if (net_ratelimit()) { + d_printf(3, dev, "WAKE&NETTX: " + "skb %p sending %d bytes to radio\n", + skb, skb->len); + d_dump(4, dev, skb->data, skb->len); + } + /* We hold a ref count for i2400m and skb, so when + * stopping() the device, we need to cancel that work + * and if pending, release those resources. */ + result = 0; + spin_lock_irqsave(&i2400m->tx_lock, flags); + if (!work_pending(&i2400m->wake_tx_ws)) { + netif_stop_queue(net_dev); + i2400m_get(i2400m); + i2400m->wake_tx_skb = skb_get(skb); /* transfer ref count */ + i2400m_tx_prep_header(skb); + result = schedule_work(&i2400m->wake_tx_ws); + WARN_ON(result == 0); + } + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + if (result == 0) { + /* Yes, this happens even if we stopped the + * queue -- blame the queue disciplines that + * queue without looking -- I guess there is a reason + * for that. */ + if (net_ratelimit()) + d_printf(1, dev, "NETTX: device exiting idle, " + "dropping skb %p, queue running %d\n", + skb, netif_queue_stopped(net_dev)); + result = -EBUSY; + } + d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result); + return result; +} + + +/* + * Transmit a packet to the base station on behalf of the network stack. + * + * Returns: 0 if ok, < 0 errno code on error. + * + * We need to pull the ethernet header and add the hardware header, + * which is currently set to all zeroes and reserved. + */ +static +int i2400m_net_tx(struct i2400m *i2400m, struct net_device *net_dev, + struct sk_buff *skb) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p net_dev %p skb %p)\n", + i2400m, net_dev, skb); + /* FIXME: check eth hdr, only IPv4 is routed by the device as of now */ + net_dev->trans_start = jiffies; + i2400m_tx_prep_header(skb); + d_printf(3, dev, "NETTX: skb %p sending %d bytes to radio\n", + skb, skb->len); + d_dump(4, dev, skb->data, skb->len); + result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA); + d_fnend(3, dev, "(i2400m %p net_dev %p skb %p) = %d\n", + i2400m, net_dev, skb, result); + return result; +} + + +/* + * Transmit a packet to the base station on behalf of the network stack + * + * + * Returns: NETDEV_TX_OK (always, even in case of error) + * + * In case of error, we just drop it. Reasons: + * + * - we add a hw header to each skb, and if the network stack + * retries, we have no way to know if that skb has it or not. + * + * - network protocols have their own drop-recovery mechanisms + * + * - there is not much else we can do + * + * If the device is idle, we need to wake it up; that is an operation + * that will sleep. See i2400m_net_wake_tx() for details. + */ +static +int i2400m_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev) +{ + int result; + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev); + if (i2400m->state == I2400M_SS_IDLE) + result = i2400m_net_wake_tx(i2400m, net_dev, skb); + else + result = i2400m_net_tx(i2400m, net_dev, skb); + if (result < 0) + net_dev->stats.tx_dropped++; + else { + net_dev->stats.tx_packets++; + net_dev->stats.tx_bytes += skb->len; + } + kfree_skb(skb); + result = NETDEV_TX_OK; + d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result); + return result; +} + + +static +int i2400m_change_mtu(struct net_device *net_dev, int new_mtu) +{ + int result; + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct device *dev = i2400m_dev(i2400m); + + if (new_mtu >= I2400M_MAX_MTU) { + dev_err(dev, "Cannot change MTU to %d (max is %d)\n", + new_mtu, I2400M_MAX_MTU); + result = -EINVAL; + } else { + net_dev->mtu = new_mtu; + result = 0; + } + return result; +} + + +static +void i2400m_tx_timeout(struct net_device *net_dev) +{ + /* + * We might want to kick the device + * + * There is not much we can do though, as the device requires + * that we send the data aggregated. By the time we receive + * this, there might be data pending to be sent or not... + */ + net_dev->stats.tx_errors++; + return; +} + + +/* + * Create a fake ethernet header + * + * For emulating an ethernet device, every received IP header has to + * be prefixed with an ethernet header. + * + * What we receive has (potentially) many IP packets concatenated with + * no ETH_HLEN bytes prefixed. Thus there is no space for an eth + * header. + * + * We would have to reallocate or do ugly fragment tricks in order to + * add it. + * + * But what we do is use the header space of the RX transaction + * (*msg_hdr) as we don't need it anymore; then we'll point all the + * data skbs there, as they share the same backing store. + * + * We only support IPv4 for v3 firmware. + */ +static +void i2400m_rx_fake_eth_header(struct net_device *net_dev, + void *_eth_hdr) +{ + struct ethhdr *eth_hdr = _eth_hdr; + + memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); + memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest)); + eth_hdr->h_proto = __constant_cpu_to_be16(ETH_P_IP); +} + + +/* + * i2400m_net_rx - pass a network packet to the stack + * + * @i2400m: device instance + * @skb_rx: the skb where the buffer pointed to by @buf is + * @i: 1 if payload is the only one + * @buf: pointer to the buffer containing the data + * @len: buffer's length + * + * We just clone the skb and set it up so that it's skb->data pointer + * points to "buf" and it's length. + * + * Note that if the payload is the last (or the only one) in a + * multi-payload message, we don't clone the SKB but just reuse it. + * + * This function is normally run from a thread context. However, we + * still use netif_rx() instead of netif_receive_skb() as was + * recommended in the mailing list. Reason is in some stress tests + * when sending/receiving a lot of data we seem to hit a softlock in + * the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using + * netif_rx() took care of the issue. + * + * This is, of course, still open to do more research on why running + * with netif_receive_skb() hits this softlock. FIXME. + * + * FIXME: currently we don't do any efforts at distinguishing if what + * we got was an IPv4 or IPv6 header, to setup the protocol field + * correctly. + */ +void i2400m_net_rx(struct i2400m *i2400m, struct sk_buff *skb_rx, + unsigned i, const void *buf, int buf_len) +{ + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *skb; + + d_fnstart(2, dev, "(i2400m %p buf %p buf_len %d)\n", + i2400m, buf, buf_len); + if (i) { + skb = skb_get(skb_rx); + d_printf(2, dev, "RX: reusing first payload skb %p\n", skb); + skb_pull(skb, buf - (void *) skb->data); + skb_trim(skb, (void *) skb_end_pointer(skb) - buf); + } else { + /* Yes, this is bad -- a lot of overhead -- see + * comments at the top of the file */ + skb = __netdev_alloc_skb(net_dev, buf_len, GFP_KERNEL); + if (skb == NULL) { + dev_err(dev, "NETRX: no memory to realloc skb\n"); + net_dev->stats.rx_dropped++; + goto error_skb_realloc; + } + memcpy(skb_put(skb, buf_len), buf, buf_len); + } + i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, + skb->data - ETH_HLEN); + skb_set_mac_header(skb, -ETH_HLEN); + skb->dev = i2400m->wimax_dev.net_dev; + skb->protocol = htons(ETH_P_IP); + net_dev->stats.rx_packets++; + net_dev->stats.rx_bytes += buf_len; + d_printf(3, dev, "NETRX: receiving %d bytes to network stack\n", + buf_len); + d_dump(4, dev, buf, buf_len); + netif_rx_ni(skb); /* see notes in function header */ +error_skb_realloc: + d_fnend(2, dev, "(i2400m %p buf %p buf_len %d) = void\n", + i2400m, buf, buf_len); +} + + +/** + * i2400m_netdev_setup - Setup setup @net_dev's i2400m private data + * + * Called by alloc_netdev() + */ +void i2400m_netdev_setup(struct net_device *net_dev) +{ + d_fnstart(3, NULL, "(net_dev %p)\n", net_dev); + ether_setup(net_dev); + net_dev->mtu = I2400M_MAX_MTU; + net_dev->tx_queue_len = I2400M_TX_QLEN; + net_dev->features = + NETIF_F_VLAN_CHALLENGED + | NETIF_F_HIGHDMA; + net_dev->flags = + IFF_NOARP /* i2400m is apure IP device */ + & (~IFF_BROADCAST /* i2400m is P2P */ + & ~IFF_MULTICAST); + net_dev->watchdog_timeo = I2400M_TX_TIMEOUT; + net_dev->open = i2400m_open; + net_dev->stop = i2400m_stop; + net_dev->hard_start_xmit = i2400m_hard_start_xmit; + net_dev->change_mtu = i2400m_change_mtu; + net_dev->tx_timeout = i2400m_tx_timeout; + d_fnend(3, NULL, "(net_dev %p) = void\n", net_dev); +} +EXPORT_SYMBOL_GPL(i2400m_netdev_setup); + diff --git a/drivers/net/wimax/i2400m/op-rfkill.c b/drivers/net/wimax/i2400m/op-rfkill.c new file mode 100644 index 000000000000..487ec58cea46 --- /dev/null +++ b/drivers/net/wimax/i2400m/op-rfkill.c @@ -0,0 +1,207 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Implement backend for the WiMAX stack rfkill support + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * The WiMAX kernel stack integrates into RF-Kill and keeps the + * switches's status. We just need to: + * + * - report changes in the HW RF Kill switch [with + * wimax_rfkill_{sw,hw}_report(), which happens when we detect those + * indications coming through hardware reports]. We also do it on + * initialization to let the stack know the intial HW state. + * + * - implement indications from the stack to change the SW RF Kill + * switch (coming from sysfs, the wimax stack or user space). + */ +#include "i2400m.h" +#include <linux/wimax/i2400m.h> + + + +#define D_SUBMODULE rfkill +#include "debug-levels.h" + +/* + * Return true if the i2400m radio is in the requested wimax_rf_state state + * + */ +static +int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state) +{ + if (state == WIMAX_RF_OFF) + return i2400m->state == I2400M_SS_RF_OFF + || i2400m->state == I2400M_SS_RF_SHUTDOWN; + else if (state == WIMAX_RF_ON) + /* state == WIMAX_RF_ON */ + return i2400m->state != I2400M_SS_RF_OFF + && i2400m->state != I2400M_SS_RF_SHUTDOWN; + else + BUG(); +} + + +/* + * WiMAX stack operation: implement SW RFKill toggling + * + * @wimax_dev: device descriptor + * @skb: skb where the message has been received; skb->data is + * expected to point to the message payload. + * @genl_info: passed by the generic netlink layer + * + * Generic Netlink will call this function when a message is sent from + * userspace to change the software RF-Kill switch status. + * + * This function will set the device's sofware RF-Kill switch state to + * match what is requested. + * + * NOTE: the i2400m has a strict state machine; we can only set the + * RF-Kill switch when it is on, the HW RF-Kill is on and the + * device is initialized. So we ignore errors steaming from not + * being in the right state (-EILSEQ). + */ +int i2400m_op_rfkill_sw_toggle(struct wimax_dev *wimax_dev, + enum wimax_rf_state state) +{ + int result; + struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct { + struct i2400m_l3l4_hdr hdr; + struct i2400m_tlv_rf_operation sw_rf; + } __attribute__((packed)) *cmd; + char strerr[32]; + + d_fnstart(4, dev, "(wimax_dev %p state %d)\n", wimax_dev, state); + + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_RF_CONTROL); + cmd->hdr.length = sizeof(cmd->sw_rf); + cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION); + cmd->sw_rf.hdr.type = cpu_to_le16(I2400M_TLV_RF_OPERATION); + cmd->sw_rf.hdr.length = cpu_to_le16(sizeof(cmd->sw_rf.status)); + switch (state) { + case WIMAX_RF_OFF: /* RFKILL ON, radio OFF */ + cmd->sw_rf.status = cpu_to_le32(2); + break; + case WIMAX_RF_ON: /* RFKILL OFF, radio ON */ + cmd->sw_rf.status = cpu_to_le32(1); + break; + default: + BUG(); + } + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + result = PTR_ERR(ack_skb); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'RF Control' command: %d\n", + result); + goto error_msg_to_dev; + } + result = i2400m_msg_check_status(wimax_msg_data(ack_skb), + strerr, sizeof(strerr)); + if (result < 0) { + dev_err(dev, "'RF Control' (0x%04x) command failed: %d - %s\n", + I2400M_MT_CMD_RF_CONTROL, result, strerr); + goto error_cmd; + } + + /* Now we wait for the state to change to RADIO_OFF or RADIO_ON */ + result = wait_event_timeout( + i2400m->state_wq, i2400m_radio_is(i2400m, state), + 5 * HZ); + if (result == 0) + result = -ETIMEDOUT; + if (result < 0) + dev_err(dev, "Error waiting for device to toggle RF state: " + "%d\n", result); + result = 0; +error_cmd: + kfree_skb(ack_skb); +error_msg_to_dev: +error_alloc: + d_fnend(4, dev, "(wimax_dev %p state %d) = %d\n", + wimax_dev, state, result); + return result; +} + + +/* + * Inform the WiMAX stack of changes in the RF Kill switches reported + * by the device + * + * @i2400m: device descriptor + * @rfss: TLV for RF Switches status; already validated + * + * NOTE: the reports on RF switch status cannot be trusted + * or used until the device is in a state of RADIO_OFF + * or greater. + */ +void i2400m_report_tlv_rf_switches_status( + struct i2400m *i2400m, + const struct i2400m_tlv_rf_switches_status *rfss) +{ + struct device *dev = i2400m_dev(i2400m); + enum i2400m_rf_switch_status hw, sw; + enum wimax_st wimax_state; + + sw = le32_to_cpu(rfss->sw_rf_switch); + hw = le32_to_cpu(rfss->hw_rf_switch); + + d_fnstart(3, dev, "(i2400m %p rfss %p [hw %u sw %u])\n", + i2400m, rfss, hw, sw); + /* We only process rw switch evens when the device has been + * fully initialized */ + wimax_state = wimax_state_get(&i2400m->wimax_dev); + if (wimax_state < WIMAX_ST_RADIO_OFF) { + d_printf(3, dev, "ignoring RF switches report, state %u\n", + wimax_state); + goto out; + } + switch (sw) { + case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */ + wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_ON); + break; + case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */ + wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_OFF); + break; + default: + dev_err(dev, "HW BUG? Unknown RF SW state 0x%x\n", sw); + } + + switch (hw) { + case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */ + wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_ON); + break; + case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */ + wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_OFF); + break; + default: + dev_err(dev, "HW BUG? Unknown RF HW state 0x%x\n", hw); + } +out: + d_fnend(3, dev, "(i2400m %p rfss %p [hw %u sw %u]) = void\n", + i2400m, rfss, hw, sw); +} diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c new file mode 100644 index 000000000000..6922022710ac --- /dev/null +++ b/drivers/net/wimax/i2400m/rx.c @@ -0,0 +1,534 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Handle incoming traffic and deliver it to the control or data planes + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Use skb_clone(), break up processing in chunks + * - Split transport/device specific + * - Make buffer size dynamic to exert less memory pressure + * + * + * This handles the RX path. + * + * We receive an RX message from the bus-specific driver, which + * contains one or more payloads that have potentially different + * destinataries (data or control paths). + * + * So we just take that payload from the transport specific code in + * the form of an skb, break it up in chunks (a cloned skb each in the + * case of network packets) and pass it to netdev or to the + * command/ack handler (and from there to the WiMAX stack). + * + * PROTOCOL FORMAT + * + * The format of the buffer is: + * + * HEADER (struct i2400m_msg_hdr) + * PAYLOAD DESCRIPTOR 0 (struct i2400m_pld) + * PAYLOAD DESCRIPTOR 1 + * ... + * PAYLOAD DESCRIPTOR N + * PAYLOAD 0 (raw bytes) + * PAYLOAD 1 + * ... + * PAYLOAD N + * + * See tx.c for a deeper description on alignment requirements and + * other fun facts of it. + * + * ROADMAP + * + * i2400m_rx + * i2400m_rx_msg_hdr_check + * i2400m_rx_pl_descr_check + * i2400m_rx_payload + * i2400m_net_rx + * i2400m_rx_ctl + * i2400m_msg_size_check + * i2400m_report_hook_work [in a workqueue] + * i2400m_report_hook + * wimax_msg_to_user + * i2400m_rx_ctl_ack + * wimax_msg_to_user_alloc + * i2400m_rx_trace + * i2400m_msg_size_check + * wimax_msg + */ +#include <linux/kernel.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> +#include <linux/workqueue.h> +#include "i2400m.h" + + +#define D_SUBMODULE rx +#include "debug-levels.h" + +struct i2400m_report_hook_args { + struct sk_buff *skb_rx; + const struct i2400m_l3l4_hdr *l3l4_hdr; + size_t size; +}; + + +/* + * Execute i2400m_report_hook in a workqueue + * + * Unpacks arguments from the deferred call, executes it and then + * drops the references. + * + * Obvious NOTE: References are needed because we are a separate + * thread; otherwise the buffer changes under us because it is + * released by the original caller. + */ +static +void i2400m_report_hook_work(struct work_struct *ws) +{ + struct i2400m_work *iw = + container_of(ws, struct i2400m_work, ws); + struct i2400m_report_hook_args *args = (void *) iw->pl; + i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size); + kfree_skb(args->skb_rx); + i2400m_put(iw->i2400m); + kfree(iw); +} + + +/* + * Process an ack to a command + * + * @i2400m: device descriptor + * @payload: pointer to message + * @size: size of the message + * + * Pass the acknodledgment (in an skb) to the thread that is waiting + * for it in i2400m->msg_completion. + * + * We need to coordinate properly with the thread waiting for the + * ack. Check if it is waiting or if it is gone. We loose the spinlock + * to avoid allocating on atomic contexts (yeah, could use GFP_ATOMIC, + * but this is not so speed critical). + */ +static +void i2400m_rx_ctl_ack(struct i2400m *i2400m, + const void *payload, size_t size) +{ + struct device *dev = i2400m_dev(i2400m); + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + unsigned long flags; + struct sk_buff *ack_skb; + + /* Anyone waiting for an answer? */ + spin_lock_irqsave(&i2400m->rx_lock, flags); + if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) { + dev_err(dev, "Huh? reply to command with no waiters\n"); + goto error_no_waiter; + } + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + + ack_skb = wimax_msg_alloc(wimax_dev, NULL, payload, size, GFP_KERNEL); + + /* Check waiter didn't time out waiting for the answer... */ + spin_lock_irqsave(&i2400m->rx_lock, flags); + if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) { + d_printf(1, dev, "Huh? waiter for command reply cancelled\n"); + goto error_waiter_cancelled; + } + if (ack_skb == NULL) { + dev_err(dev, "CMD/GET/SET ack: cannot allocate SKB\n"); + i2400m->ack_skb = ERR_PTR(-ENOMEM); + } else + i2400m->ack_skb = ack_skb; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + complete(&i2400m->msg_completion); + return; + +error_waiter_cancelled: + if (ack_skb) + kfree_skb(ack_skb); +error_no_waiter: + spin_unlock_irqrestore(&i2400m->rx_lock, flags); + return; +} + + +/* + * Receive and process a control payload + * + * @i2400m: device descriptor + * @skb_rx: skb that contains the payload (for reference counting) + * @payload: pointer to message + * @size: size of the message + * + * There are two types of control RX messages: reports (asynchronous, + * like your every day interrupts) and 'acks' (reponses to a command, + * get or set request). + * + * If it is a report, we run hooks on it (to extract information for + * things we need to do in the driver) and then pass it over to the + * WiMAX stack to send it to user space. + * + * NOTE: report processing is done in a workqueue specific to the + * generic driver, to avoid deadlocks in the system. + * + * If it is not a report, it is an ack to a previously executed + * command, set or get, so wake up whoever is waiting for it from + * i2400m_msg_to_dev(). i2400m_rx_ctl_ack() takes care of that. + * + * Note that the sizes we pass to other functions from here are the + * sizes of the _l3l4_hdr + payload, not full buffer sizes, as we have + * verified in _msg_size_check() that they are congruent. + * + * For reports: We can't clone the original skb where the data is + * because we need to send this up via netlink; netlink has to add + * headers and we can't overwrite what's preceeding the payload...as + * it is another message. So we just dup them. + */ +static +void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx, + const void *payload, size_t size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_l3l4_hdr *l3l4_hdr = payload; + unsigned msg_type; + + result = i2400m_msg_size_check(i2400m, l3l4_hdr, size); + if (result < 0) { + dev_err(dev, "HW BUG? device sent a bad message: %d\n", + result); + goto error_check; + } + msg_type = le16_to_cpu(l3l4_hdr->type); + d_printf(1, dev, "%s 0x%04x: %zu bytes\n", + msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", + msg_type, size); + d_dump(2, dev, l3l4_hdr, size); + if (msg_type & I2400M_MT_REPORT_MASK) { + /* These hooks have to be ran serialized; as well, the + * handling might force the execution of commands, and + * that might cause reentrancy issues with + * bus-specific subdrivers and workqueues. So we run + * it in a separate workqueue. */ + struct i2400m_report_hook_args args = { + .skb_rx = skb_rx, + .l3l4_hdr = l3l4_hdr, + .size = size + }; + if (unlikely(i2400m->ready == 0)) /* only send if up */ + return; + skb_get(skb_rx); + i2400m_queue_work(i2400m, i2400m_report_hook_work, + GFP_KERNEL, &args, sizeof(args)); + result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size, + GFP_KERNEL); + if (result < 0) + dev_err(dev, "error sending report to userspace: %d\n", + result); + } else /* an ack to a CMD, GET or SET */ + i2400m_rx_ctl_ack(i2400m, payload, size); +error_check: + return; +} + + + + +/* + * Receive and send up a trace + * + * @i2400m: device descriptor + * @skb_rx: skb that contains the trace (for reference counting) + * @payload: pointer to trace message inside the skb + * @size: size of the message + * + * THe i2400m might produce trace information (diagnostics) and we + * send them through a different kernel-to-user pipe (to avoid + * clogging it). + * + * As in i2400m_rx_ctl(), we can't clone the original skb where the + * data is because we need to send this up via netlink; netlink has to + * add headers and we can't overwrite what's preceeding the + * payload...as it is another message. So we just dup them. + */ +static +void i2400m_rx_trace(struct i2400m *i2400m, + const void *payload, size_t size) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + const struct i2400m_l3l4_hdr *l3l4_hdr = payload; + unsigned msg_type; + + result = i2400m_msg_size_check(i2400m, l3l4_hdr, size); + if (result < 0) { + dev_err(dev, "HW BUG? device sent a bad trace message: %d\n", + result); + goto error_check; + } + msg_type = le16_to_cpu(l3l4_hdr->type); + d_printf(1, dev, "Trace %s 0x%04x: %zu bytes\n", + msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET", + msg_type, size); + d_dump(2, dev, l3l4_hdr, size); + if (unlikely(i2400m->ready == 0)) /* only send if up */ + return; + result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL); + if (result < 0) + dev_err(dev, "error sending trace to userspace: %d\n", + result); +error_check: + return; +} + + +/* + * Act on a received payload + * + * @i2400m: device instance + * @skb_rx: skb where the transaction was received + * @single: 1 if there is only one payload, 0 otherwise + * @pld: payload descriptor + * @payload: payload data + * + * Upon reception of a payload, look at its guts in the payload + * descriptor and decide what to do with it. + */ +static +void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, + unsigned single, const struct i2400m_pld *pld, + const void *payload) +{ + struct device *dev = i2400m_dev(i2400m); + size_t pl_size = i2400m_pld_size(pld); + enum i2400m_pt pl_type = i2400m_pld_type(pld); + + switch (pl_type) { + case I2400M_PT_DATA: + d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size); + i2400m_net_rx(i2400m, skb_rx, single, payload, pl_size); + break; + case I2400M_PT_CTRL: + i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size); + break; + case I2400M_PT_TRACE: + i2400m_rx_trace(i2400m, payload, pl_size); + break; + default: /* Anything else shouldn't come to the host */ + if (printk_ratelimit()) + dev_err(dev, "RX: HW BUG? unexpected payload type %u\n", + pl_type); + } +} + + +/* + * Check a received transaction's message header + * + * @i2400m: device descriptor + * @msg_hdr: message header + * @buf_size: size of the received buffer + * + * Check that the declarations done by a RX buffer message header are + * sane and consistent with the amount of data that was received. + */ +static +int i2400m_rx_msg_hdr_check(struct i2400m *i2400m, + const struct i2400m_msg_hdr *msg_hdr, + size_t buf_size) +{ + int result = -EIO; + struct device *dev = i2400m_dev(i2400m); + if (buf_size < sizeof(*msg_hdr)) { + dev_err(dev, "RX: HW BUG? message with short header (%zu " + "vs %zu bytes expected)\n", buf_size, sizeof(*msg_hdr)); + goto error; + } + if (msg_hdr->barker != cpu_to_le32(I2400M_D2H_MSG_BARKER)) { + dev_err(dev, "RX: HW BUG? message received with unknown " + "barker 0x%08x (buf_size %zu bytes)\n", + le32_to_cpu(msg_hdr->barker), buf_size); + goto error; + } + if (msg_hdr->num_pls == 0) { + dev_err(dev, "RX: HW BUG? zero payload packets in message\n"); + goto error; + } + if (le16_to_cpu(msg_hdr->num_pls) > I2400M_MAX_PLS_IN_MSG) { + dev_err(dev, "RX: HW BUG? message contains more payload " + "than maximum; ignoring.\n"); + goto error; + } + result = 0; +error: + return result; +} + + +/* + * Check a payload descriptor against the received data + * + * @i2400m: device descriptor + * @pld: payload descriptor + * @pl_itr: offset (in bytes) in the received buffer the payload is + * located + * @buf_size: size of the received buffer + * + * Given a payload descriptor (part of a RX buffer), check it is sane + * and that the data it declares fits in the buffer. + */ +static +int i2400m_rx_pl_descr_check(struct i2400m *i2400m, + const struct i2400m_pld *pld, + size_t pl_itr, size_t buf_size) +{ + int result = -EIO; + struct device *dev = i2400m_dev(i2400m); + size_t pl_size = i2400m_pld_size(pld); + enum i2400m_pt pl_type = i2400m_pld_type(pld); + + if (pl_size > i2400m->bus_pl_size_max) { + dev_err(dev, "RX: HW BUG? payload @%zu: size %zu is " + "bigger than maximum %zu; ignoring message\n", + pl_itr, pl_size, i2400m->bus_pl_size_max); + goto error; + } + if (pl_itr + pl_size > buf_size) { /* enough? */ + dev_err(dev, "RX: HW BUG? payload @%zu: size %zu " + "goes beyond the received buffer " + "size (%zu bytes); ignoring message\n", + pl_itr, pl_size, buf_size); + goto error; + } + if (pl_type >= I2400M_PT_ILLEGAL) { + dev_err(dev, "RX: HW BUG? illegal payload type %u; " + "ignoring message\n", pl_type); + goto error; + } + result = 0; +error: + return result; +} + + +/** + * i2400m_rx - Receive a buffer of data from the device + * + * @i2400m: device descriptor + * @skb: skbuff where the data has been received + * + * Parse in a buffer of data that contains an RX message sent from the + * device. See the file header for the format. Run all checks on the + * buffer header, then run over each payload's descriptors, verify + * their consistency and act on each payload's contents. If + * everything is succesful, update the device's statistics. + * + * Note: You need to set the skb to contain only the length of the + * received buffer; for that, use skb_trim(skb, RECEIVED_SIZE). + * + * Returns: + * + * 0 if ok, < 0 errno on error + * + * If ok, this function owns now the skb and the caller DOESN'T have + * to run kfree_skb() on it. However, on error, the caller still owns + * the skb and it is responsible for releasing it. + */ +int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) +{ + int i, result; + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_msg_hdr *msg_hdr; + size_t pl_itr, pl_size, skb_len; + unsigned long flags; + unsigned num_pls; + + skb_len = skb->len; + d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n", + i2400m, skb, skb_len); + result = -EIO; + msg_hdr = (void *) skb->data; + result = i2400m_rx_msg_hdr_check(i2400m, msg_hdr, skb->len); + if (result < 0) + goto error_msg_hdr_check; + result = -EIO; + num_pls = le16_to_cpu(msg_hdr->num_pls); + pl_itr = sizeof(*msg_hdr) + /* Check payload descriptor(s) */ + num_pls * sizeof(msg_hdr->pld[0]); + pl_itr = ALIGN(pl_itr, I2400M_PL_PAD); + if (pl_itr > skb->len) { /* got all the payload descriptors? */ + dev_err(dev, "RX: HW BUG? message too short (%u bytes) for " + "%u payload descriptors (%zu each, total %zu)\n", + skb->len, num_pls, sizeof(msg_hdr->pld[0]), pl_itr); + goto error_pl_descr_short; + } + /* Walk each payload payload--check we really got it */ + for (i = 0; i < num_pls; i++) { + /* work around old gcc warnings */ + pl_size = i2400m_pld_size(&msg_hdr->pld[i]); + result = i2400m_rx_pl_descr_check(i2400m, &msg_hdr->pld[i], + pl_itr, skb->len); + if (result < 0) + goto error_pl_descr_check; + i2400m_rx_payload(i2400m, skb, num_pls == 1, &msg_hdr->pld[i], + skb->data + pl_itr); + pl_itr += ALIGN(pl_size, I2400M_PL_PAD); + cond_resched(); /* Don't monopolize */ + } + kfree_skb(skb); + /* Update device statistics */ + spin_lock_irqsave(&i2400m->rx_lock, flags); + i2400m->rx_pl_num += i; + if (i > i2400m->rx_pl_max) + i2400m->rx_pl_max = i; + if (i < i2400m->rx_pl_min) + i2400m->rx_pl_min = i; + i2400m->rx_num++; + i2400m->rx_size_acc += skb->len; + if (skb->len < i2400m->rx_size_min) + i2400m->rx_size_min = skb->len; + if (skb->len > i2400m->rx_size_max) + i2400m->rx_size_max = skb->len; + spin_unlock_irqrestore(&i2400m->rx_lock, flags); +error_pl_descr_check: +error_pl_descr_short: +error_msg_hdr_check: + d_fnend(4, dev, "(i2400m %p skb %p [size %zu]) = %d\n", + i2400m, skb, skb_len, result); + return result; +} +EXPORT_SYMBOL_GPL(i2400m_rx); diff --git a/drivers/net/wimax/i2400m/sdio-debug-levels.h b/drivers/net/wimax/i2400m/sdio-debug-levels.h new file mode 100644 index 000000000000..c51998741301 --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-debug-levels.h @@ -0,0 +1,22 @@ +/* + * debug levels control file for the i2400m module's + */ +#ifndef __debug_levels__h__ +#define __debug_levels__h__ + +/* Maximum compile and run time debug level for all submodules */ +#define D_MODULENAME i2400m_sdio +#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL + +#include <linux/wimax/debug.h> + +/* List of all the enabled modules */ +enum d_module { + D_SUBMODULE_DECLARE(main), + D_SUBMODULE_DECLARE(tx), + D_SUBMODULE_DECLARE(rx), + D_SUBMODULE_DECLARE(fw) +}; + + +#endif /* #ifndef __debug_levels__h__ */ diff --git a/drivers/net/wimax/i2400m/sdio-fw.c b/drivers/net/wimax/i2400m/sdio-fw.c new file mode 100644 index 000000000000..3487205d8f50 --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-fw.c @@ -0,0 +1,224 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Firmware uploader's SDIO specifics + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Initial implementation + * + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Bus generic/specific split for USB + * + * Dirk Brandewie <dirk.j.brandewie@intel.com> + * - Initial implementation for SDIO + * + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - SDIO rehash for changes in the bus-driver model + * + * THE PROCEDURE + * + * See fw.c for the generic description of this procedure. + * + * This file implements only the SDIO specifics. It boils down to how + * to send a command and waiting for an acknowledgement from the + * device. We do polled reads. + * + * COMMAND EXECUTION + * + * THe generic firmware upload code will call i2400m_bus_bm_cmd_send() + * to send commands. + * + * The SDIO devices expects things in 256 byte blocks, so it will pad + * it, compute the checksum (if needed) and pass it to SDIO. + * + * ACK RECEPTION + * + * This works in polling mode -- the fw loader says when to wait for + * data and for that it calls i2400ms_bus_bm_wait_for_ack(). + * + * This will poll the device for data until it is received. We need to + * receive at least as much bytes as where asked for (although it'll + * always be a multiple of 256 bytes). + */ +#include <linux/mmc/sdio_func.h> +#include "i2400m-sdio.h" + + +#define D_SUBMODULE fw +#include "sdio-debug-levels.h" + +/* + * Send a boot-mode command to the SDIO function + * + * We use a bounce buffer (i2400m->bm_cmd_buf) because we need to + * touch the header if the RAW flag is not set. + * + * @flags: pass thru from i2400m_bm_cmd() + * @return: cmd_size if ok, < 0 errno code on error. + * + * Note the command is padded to the SDIO block size for the device. + */ +ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m, + const struct i2400m_bootrom_header *_cmd, + size_t cmd_size, int flags) +{ + ssize_t result; + struct device *dev = i2400m_dev(i2400m); + struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd); + struct i2400m_bootrom_header *cmd; + /* SDIO restriction */ + size_t cmd_size_a = ALIGN(cmd_size, I2400MS_BLK_SIZE); + + d_fnstart(5, dev, "(i2400m %p cmd %p size %zu)\n", + i2400m, _cmd, cmd_size); + result = -E2BIG; + if (cmd_size > I2400M_BM_CMD_BUF_SIZE) + goto error_too_big; + + memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); /* Prep command */ + cmd = i2400m->bm_cmd_buf; + if (cmd_size_a > cmd_size) /* Zero pad space */ + memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); + if ((flags & I2400M_BM_CMD_RAW) == 0) { + if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0)) + dev_warn(dev, "SW BUG: response_required == 0\n"); + i2400m_bm_cmd_prepare(cmd); + } + d_printf(4, dev, "BM cmd %d: %zu bytes (%zu padded)\n", + opcode, cmd_size, cmd_size_a); + d_dump(5, dev, cmd, cmd_size); + + sdio_claim_host(i2400ms->func); /* Send & check */ + result = sdio_memcpy_toio(i2400ms->func, I2400MS_DATA_ADDR, + i2400m->bm_cmd_buf, cmd_size_a); + sdio_release_host(i2400ms->func); + if (result < 0) { + dev_err(dev, "BM cmd %d: cannot send: %ld\n", + opcode, (long) result); + goto error_cmd_send; + } + result = cmd_size; +error_cmd_send: +error_too_big: + d_fnend(5, dev, "(i2400m %p cmd %p size %zu) = %d\n", + i2400m, _cmd, cmd_size, (int) result); + return result; +} + + +/* + * Read an ack from the device's boot-mode (polling) + * + * @i2400m: + * @_ack: pointer to where to store the read data + * @ack_size: how many bytes we should read + * + * Returns: < 0 errno code on error; otherwise, amount of received bytes. + * + * The ACK for a BM command is always at least sizeof(*ack) bytes, so + * check for that. We don't need to check for device reboots + * + * NOTE: We do an artificial timeout of 1 sec over the SDIO timeout; + * this way we have control over it...there is no way that I know + * of setting an SDIO transaction timeout. + */ +ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m, + struct i2400m_bootrom_header *ack, + size_t ack_size) +{ + int result; + ssize_t rx_size; + u64 timeout; + struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + + BUG_ON(sizeof(*ack) > ack_size); + + d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n", + i2400m, ack, ack_size); + + timeout = get_jiffies_64() + 2 * HZ; + sdio_claim_host(func); + while (1) { + if (time_after64(get_jiffies_64(), timeout)) { + rx_size = -ETIMEDOUT; + dev_err(dev, "timeout waiting for ack data\n"); + goto error_timedout; + } + + /* Find the RX size, check if it fits or not -- it if + * doesn't fit, fail, as we have no way to dispose of + * the extra data. */ + rx_size = __i2400ms_rx_get_size(i2400ms); + if (rx_size < 0) + goto error_rx_get_size; + result = -ENOSPC; /* Check it fits */ + if (rx_size < sizeof(*ack)) { + rx_size = -EIO; + dev_err(dev, "HW BUG? received is too small (%zu vs " + "%zu needed)\n", sizeof(*ack), rx_size); + goto error_too_small; + } + if (rx_size > I2400M_BM_ACK_BUF_SIZE) { + dev_err(dev, "SW BUG? BM_ACK_BUF is too small (%u vs " + "%zu needed)\n", I2400M_BM_ACK_BUF_SIZE, + rx_size); + goto error_too_small; + } + + /* Read it */ + result = sdio_memcpy_fromio(func, i2400m->bm_ack_buf, + I2400MS_DATA_ADDR, rx_size); + if (result == -ETIMEDOUT || result == -ETIME) + continue; + if (result < 0) { + dev_err(dev, "BM SDIO receive (%zu B) failed: %d\n", + rx_size, result); + goto error_read; + } else + break; + } + rx_size = min((ssize_t)ack_size, rx_size); + memcpy(ack, i2400m->bm_ack_buf, rx_size); +error_read: +error_too_small: +error_rx_get_size: +error_timedout: + sdio_release_host(func); + d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %ld\n", + i2400m, ack, ack_size, (long) rx_size); + return rx_size; +} diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c new file mode 100644 index 000000000000..a3008b904f7d --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-rx.c @@ -0,0 +1,255 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * SDIO RX handling + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Dirk Brandewie <dirk.j.brandewie@intel.com> + * - Initial implementation + * + * + * This handles the RX path on SDIO. + * + * The SDIO bus driver calls the "irq" routine when data is available. + * This is not a traditional interrupt routine since the SDIO bus + * driver calls us from its irq thread context. Because of this + * sleeping in the SDIO RX IRQ routine is okay. + * + * From there on, we obtain the size of the data that is available, + * allocate an skb, copy it and then pass it to the generic driver's + * RX routine [i2400m_rx()]. + * + * ROADMAP + * + * i2400ms_irq() + * i2400ms_rx() + * __i2400ms_rx_get_size() + * i2400m_rx() + * + * i2400ms_rx_setup() + * + * i2400ms_rx_release() + */ +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/skbuff.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_func.h> +#include "i2400m-sdio.h" + +#define D_SUBMODULE rx +#include "sdio-debug-levels.h" + + +/* + * Read and return the amount of bytes available for RX + * + * The RX size has to be read like this: byte reads of three + * sequential locations; then glue'em together. + * + * sdio_readl() doesn't work. + */ +ssize_t __i2400ms_rx_get_size(struct i2400ms *i2400ms) +{ + int ret, cnt, val; + ssize_t rx_size; + unsigned xfer_size_addr; + struct sdio_func *func = i2400ms->func; + struct device *dev = &i2400ms->func->dev; + + d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); + xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR; + rx_size = 0; + for (cnt = 0; cnt < 3; cnt++) { + val = sdio_readb(func, xfer_size_addr + cnt, &ret); + if (ret < 0) { + dev_err(dev, "RX: Can't read byte %d of RX size from " + "0x%08x: %d\n", cnt, xfer_size_addr + cnt, ret); + rx_size = ret; + goto error_read; + } + rx_size = rx_size << 8 | (val & 0xff); + } + d_printf(6, dev, "RX: rx_size is %ld\n", (long) rx_size); +error_read: + d_fnend(7, dev, "(i2400ms %p) = %ld\n", i2400ms, (long) rx_size); + return rx_size; +} + + +/* + * Read data from the device (when in normal) + * + * Allocate an SKB of the right size, read the data in and then + * deliver it to the generic layer. + * + * We also check for a reboot barker. That means the device died and + * we have to reboot it. + */ +static +void i2400ms_rx(struct i2400ms *i2400ms) +{ + int ret; + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + struct i2400m *i2400m = &i2400ms->i2400m; + struct sk_buff *skb; + ssize_t rx_size; + + d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms); + rx_size = __i2400ms_rx_get_size(i2400ms); + if (rx_size < 0) { + ret = rx_size; + goto error_get_size; + } + ret = -ENOMEM; + skb = alloc_skb(rx_size, GFP_ATOMIC); + if (NULL == skb) { + dev_err(dev, "RX: unable to alloc skb\n"); + goto error_alloc_skb; + } + + ret = sdio_memcpy_fromio(func, skb->data, + I2400MS_DATA_ADDR, rx_size); + if (ret < 0) { + dev_err(dev, "RX: SDIO data read failed: %d\n", ret); + goto error_memcpy_fromio; + } + /* Check if device has reset */ + if (!memcmp(skb->data, i2400m_NBOOT_BARKER, + sizeof(i2400m_NBOOT_BARKER)) + || !memcmp(skb->data, i2400m_SBOOT_BARKER, + sizeof(i2400m_SBOOT_BARKER))) { + ret = i2400m_dev_reset_handle(i2400m); + kfree_skb(skb); + } else { + skb_put(skb, rx_size); + i2400m_rx(i2400m, skb); + } + d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); + return; + +error_memcpy_fromio: + kfree_skb(skb); +error_alloc_skb: +error_get_size: + d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); + return; +} + + +/* + * Process an interrupt from the SDIO card + * + * FIXME: need to process other events that are not just ready-to-read + * + * Checks there is data ready and then proceeds to read it. + */ +static +void i2400ms_irq(struct sdio_func *func) +{ + int ret; + struct i2400ms *i2400ms = sdio_get_drvdata(func); + struct i2400m *i2400m = &i2400ms->i2400m; + struct device *dev = &func->dev; + int val; + + d_fnstart(6, dev, "(i2400ms %p)\n", i2400ms); + val = sdio_readb(func, I2400MS_INTR_STATUS_ADDR, &ret); + if (ret < 0) { + dev_err(dev, "RX: Can't read interrupt status: %d\n", ret); + goto error_no_irq; + } + if (!val) { + dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); + goto error_no_irq; + } + sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); + if (WARN_ON(i2400m->boot_mode != 0)) + dev_err(dev, "RX: SW BUG? boot mode and IRQ is up?\n"); + else + i2400ms_rx(i2400ms); +error_no_irq: + d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); + return; +} + + +/* + * Setup SDIO RX + * + * Hooks up the IRQ handler and then enables IRQs. + */ +int i2400ms_rx_setup(struct i2400ms *i2400ms) +{ + int result; + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + + d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); + sdio_claim_host(func); + result = sdio_claim_irq(func, i2400ms_irq); + if (result < 0) { + dev_err(dev, "Cannot claim IRQ: %d\n", result); + goto error_irq_claim; + } + result = 0; + sdio_writeb(func, 1, I2400MS_INTR_ENABLE_ADDR, &result); + if (result < 0) { + sdio_release_irq(func); + dev_err(dev, "Failed to enable interrupts %d\n", result); + } +error_irq_claim: + sdio_release_host(func); + d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); + return result; +} + + +/* + * Tear down SDIO RX + * + * Disables IRQs in the device and removes the IRQ handler. + */ +void i2400ms_rx_release(struct i2400ms *i2400ms) +{ + int result; + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + + d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); + sdio_claim_host(func); + sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result); + sdio_release_irq(func); + sdio_release_host(func); + d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); +} diff --git a/drivers/net/wimax/i2400m/sdio-tx.c b/drivers/net/wimax/i2400m/sdio-tx.c new file mode 100644 index 000000000000..5105a5ebc44f --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio-tx.c @@ -0,0 +1,153 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * SDIO TX transaction backends + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Dirk Brandewie <dirk.j.brandewie@intel.com> + * - Initial implementation + * + * + * Takes the TX messages in the i2400m's driver TX FIFO and sends them + * to the device until there are no more. + * + * If we fail sending the message, we just drop it. There isn't much + * we can do at this point. Most of the traffic is network, which has + * recovery methods for dropped packets. + * + * The SDIO functions are not atomic, so we can't run from the context + * where i2400m->bus_tx_kick() [i2400ms_bus_tx_kick()] is being called + * (some times atomic). Thus, the actual TX work is deferred to a + * workqueue. + * + * ROADMAP + * + * i2400ms_bus_tx_kick() + * i2400ms_tx_submit() [through workqueue] + * + * i2400m_tx_setup() + * + * i2400m_tx_release() + */ +#include <linux/mmc/sdio_func.h> +#include "i2400m-sdio.h" + +#define D_SUBMODULE tx +#include "sdio-debug-levels.h" + + +/* + * Pull TX transations from the TX FIFO and send them to the device + * until there are no more. + */ +static +void i2400ms_tx_submit(struct work_struct *ws) +{ + int result; + struct i2400ms *i2400ms = container_of(ws, struct i2400ms, tx_worker); + struct i2400m *i2400m = &i2400ms->i2400m; + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + struct i2400m_msg_hdr *tx_msg; + size_t tx_msg_size; + + d_fnstart(4, dev, "(i2400ms %p, i2400m %p)\n", i2400ms, i2400ms); + + while (NULL != (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size))) { + d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size); + d_dump(5, dev, tx_msg, tx_msg_size); + + sdio_claim_host(func); + result = sdio_memcpy_toio(func, 0, tx_msg, tx_msg_size); + sdio_release_host(func); + + i2400m_tx_msg_sent(i2400m); + + if (result < 0) { + dev_err(dev, "TX: cannot submit TX; tx_msg @%zu %zu B:" + " %d\n", (void *) tx_msg - i2400m->tx_buf, + tx_msg_size, result); + } + + d_printf(2, dev, "TX: %zub submitted\n", tx_msg_size); + } + + d_fnend(4, dev, "(i2400ms %p) = void\n", i2400ms); +} + + +/* + * The generic driver notifies us that there is data ready for TX + * + * Schedule a run of i2400ms_tx_submit() to handle it. + */ +void i2400ms_bus_tx_kick(struct i2400m *i2400m) +{ + struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + struct device *dev = &i2400ms->func->dev; + + d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m); + + /* schedule tx work, this is because tx may block, therefore + * it has to run in a thread context. + */ + queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker); + + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} + +int i2400ms_tx_setup(struct i2400ms *i2400ms) +{ + int result; + struct device *dev = &i2400ms->func->dev; + struct i2400m *i2400m = &i2400ms->i2400m; + + d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms); + + INIT_WORK(&i2400ms->tx_worker, i2400ms_tx_submit); + snprintf(i2400ms->tx_wq_name, sizeof(i2400ms->tx_wq_name), + "%s-tx", i2400m->wimax_dev.name); + i2400ms->tx_workqueue = + create_singlethread_workqueue(i2400ms->tx_wq_name); + if (NULL == i2400ms->tx_workqueue) { + dev_err(dev, "TX: failed to create workqueue\n"); + result = -ENOMEM; + } else + result = 0; + d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result); + return result; +} + +void i2400ms_tx_release(struct i2400ms *i2400ms) +{ + destroy_workqueue(i2400ms->tx_workqueue); +} diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c new file mode 100644 index 000000000000..1bfa283bbd8a --- /dev/null +++ b/drivers/net/wimax/i2400m/sdio.c @@ -0,0 +1,511 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Linux driver model glue for the SDIO device, reset & fw upload + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Dirk Brandewie <dirk.j.brandewie@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * See i2400m-sdio.h for a general description of this driver. + * + * This file implements driver model glue, and hook ups for the + * generic driver to implement the bus-specific functions (device + * communication setup/tear down, firmware upload and resetting). + * + * ROADMAP + * + * i2400m_probe() + * alloc_netdev() + * i2400ms_netdev_setup() + * i2400ms_init() + * i2400m_netdev_setup() + * i2400ms_enable_function() + * i2400m_setup() + * + * i2400m_remove() + * i2400m_release() + * free_netdev(net_dev) + * + * i2400ms_bus_reset() Called by i2400m->bus_reset + * __i2400ms_reset() + * __i2400ms_send_barker() + * + * i2400ms_bus_dev_start() Called by i2400m_dev_start() [who is + * i2400ms_tx_setup() called by i2400m_setup()] + * i2400ms_rx_setup() + * + * i2400ms_bus_dev_stop() Called by i2400m_dev_stop() [who is + * i2400ms_rx_release() is called by i2400m_release()] + * i2400ms_tx_release() + * + */ + +#include <linux/debugfs.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_func.h> +#include "i2400m-sdio.h" +#include <linux/wimax/i2400m.h> + +#define D_SUBMODULE main +#include "sdio-debug-levels.h" + +/* IOE WiMAX function timeout in seconds */ +static int ioe_timeout = 2; +module_param(ioe_timeout, int, 0); + +/* Our firmware file name */ +#define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-" I2400M_FW_VERSION ".sbcf" + +/* + * Enable the SDIO function + * + * Tries to enable the SDIO function; might fail if it is still not + * ready (in some hardware, the SDIO WiMAX function is only enabled + * when we ask it to explicitly doing). Tries until a timeout is + * reached. + * + * The reverse of this is...sdio_disable_function() + * + * Returns: 0 if the SDIO function was enabled, < 0 errno code on + * error (-ENODEV when it was unable to enable the function). + */ +static +int i2400ms_enable_function(struct sdio_func *func) +{ + u64 timeout; + int err; + struct device *dev = &func->dev; + + d_fnstart(3, dev, "(func %p)\n", func); + /* Setup timeout (FIXME: This needs to read the CIS table to + * get a real timeout) and then wait for the device to signal + * it is ready */ + timeout = get_jiffies_64() + ioe_timeout * HZ; + err = -ENODEV; + while (err != 0 && time_before64(get_jiffies_64(), timeout)) { + sdio_claim_host(func); + err = sdio_enable_func(func); + if (0 == err) { + sdio_release_host(func); + d_printf(2, dev, "SDIO function enabled\n"); + goto function_enabled; + } + d_printf(2, dev, "SDIO function failed to enable: %d\n", err); + sdio_disable_func(func); + sdio_release_host(func); + msleep(I2400MS_INIT_SLEEP_INTERVAL); + } + /* If timed out, device is not there yet -- get -ENODEV so + * the device driver core will retry later on. */ + if (err == -ETIME) { + dev_err(dev, "Can't enable WiMAX function; " + " has the function been enabled?\n"); + err = -ENODEV; + } +function_enabled: + d_fnend(3, dev, "(func %p) = %d\n", func, err); + return err; +} + + +/* + * Setup driver resources needed to communicate with the device + * + * The fw needs some time to settle, and it was just uploaded, + * so give it a break first. I'd prefer to just wait for the device to + * send something, but seems the poking we do to enable SDIO stuff + * interferes with it, so just give it a break before starting... + */ +static +int i2400ms_bus_dev_start(struct i2400m *i2400m) +{ + int result; + struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + msleep(200); + result = i2400ms_rx_setup(i2400ms); + if (result < 0) + goto error_rx_setup; + result = i2400ms_tx_setup(i2400ms); + if (result < 0) + goto error_tx_setup; + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; + + i2400ms_tx_release(i2400ms); +error_tx_setup: + i2400ms_rx_release(i2400ms); +error_rx_setup: + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); + return result; +} + + +static +void i2400ms_bus_dev_stop(struct i2400m *i2400m) +{ + struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + i2400ms_rx_release(i2400ms); + i2400ms_tx_release(i2400ms); + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} + + +/* + * Sends a barker buffer to the device + * + * This helper will allocate a kmalloced buffer and use it to transmit + * (then free it). Reason for this is that the SDIO host controller + * expects alignment (unknown exactly which) which the stack won't + * really provide and certain arches/host-controller combinations + * cannot use stack/vmalloc/text areas for DMA transfers. + */ +static +int __i2400ms_send_barker(struct i2400ms *i2400ms, + const __le32 *barker, size_t barker_size) +{ + int ret; + struct sdio_func *func = i2400ms->func; + struct device *dev = &func->dev; + void *buffer; + + ret = -ENOMEM; + buffer = kmalloc(I2400MS_BLK_SIZE, GFP_KERNEL); + if (buffer == NULL) + goto error_kzalloc; + + memcpy(buffer, barker, barker_size); + sdio_claim_host(func); + ret = sdio_memcpy_toio(func, 0, buffer, I2400MS_BLK_SIZE); + sdio_release_host(func); + + if (ret < 0) + d_printf(0, dev, "E: barker error: %d\n", ret); + + kfree(buffer); +error_kzalloc: + return ret; +} + + +/* + * Reset a device at different levels (warm, cold or bus) + * + * @i2400ms: device descriptor + * @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS) + * + * FIXME: not tested -- need to confirm expected effects + * + * Warm and cold resets get an SDIO reset if they fail (unimplemented) + * + * Warm reset: + * + * The device will be fully reset internally, but won't be + * disconnected from the USB bus (so no reenumeration will + * happen). Firmware upload will be neccessary. + * + * The device will send a reboot barker in the notification endpoint + * that will trigger the driver to reinitialize the state + * automatically from notif.c:i2400m_notification_grok() into + * i2400m_dev_bootstrap_delayed(). + * + * Cold and bus (USB) reset: + * + * The device will be fully reset internally, disconnected from the + * USB bus an a reenumeration will happen. Firmware upload will be + * neccessary. Thus, we don't do any locking or struct + * reinitialization, as we are going to be fully disconnected and + * reenumerated. + * + * Note we need to return -ENODEV if a warm reset was requested and we + * had to resort to a bus reset. See i2400m_op_reset(), wimax_reset() + * and wimax_dev->op_reset. + * + * WARNING: no driver state saved/fixed + */ +static +int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) +{ + int result; + struct i2400ms *i2400ms = + container_of(i2400m, struct i2400ms, i2400m); + struct device *dev = i2400m_dev(i2400m); + static const __le32 i2400m_WARM_BOOT_BARKER[4] = { + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + }; + static const __le32 i2400m_COLD_BOOT_BARKER[4] = { + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + }; + + if (rt == I2400M_RT_WARM) + result = __i2400ms_send_barker(i2400ms, i2400m_WARM_BOOT_BARKER, + sizeof(i2400m_WARM_BOOT_BARKER)); + else if (rt == I2400M_RT_COLD) + result = __i2400ms_send_barker(i2400ms, i2400m_COLD_BOOT_BARKER, + sizeof(i2400m_COLD_BOOT_BARKER)); + else if (rt == I2400M_RT_BUS) { +do_bus_reset: + dev_err(dev, "FIXME: SDIO bus reset not implemented\n"); + result = rt == I2400M_RT_WARM ? -ENODEV : -ENOSYS; + } else + BUG(); + if (result < 0 && rt != I2400M_RT_BUS) { + dev_err(dev, "%s reset failed (%d); trying SDIO reset\n", + rt == I2400M_RT_WARM ? "warm" : "cold", result); + rt = I2400M_RT_BUS; + goto do_bus_reset; + } + return result; +} + + +static +void i2400ms_netdev_setup(struct net_device *net_dev) +{ + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m); + i2400ms_init(i2400ms); + i2400m_netdev_setup(net_dev); +} + + +/* + * Debug levels control; see debug.h + */ +struct d_level D_LEVEL[] = { + D_SUBMODULE_DEFINE(main), + D_SUBMODULE_DEFINE(tx), + D_SUBMODULE_DEFINE(rx), + D_SUBMODULE_DEFINE(fw), +}; +size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); + + +#define __debugfs_register(prefix, name, parent) \ +do { \ + result = d_level_register_debugfs(prefix, name, parent); \ + if (result < 0) \ + goto error; \ +} while (0) + + +static +int i2400ms_debugfs_add(struct i2400ms *i2400ms) +{ + int result; + struct dentry *dentry = i2400ms->i2400m.wimax_dev.debugfs_dentry; + + dentry = debugfs_create_dir("i2400m-usb", dentry); + result = PTR_ERR(dentry); + if (IS_ERR(dentry)) { + if (result == -ENODEV) + result = 0; /* No debugfs support */ + goto error; + } + i2400ms->debugfs_dentry = dentry; + __debugfs_register("dl_", main, dentry); + __debugfs_register("dl_", tx, dentry); + __debugfs_register("dl_", rx, dentry); + __debugfs_register("dl_", fw, dentry); + + return 0; + +error: + debugfs_remove_recursive(i2400ms->debugfs_dentry); + return result; +} + + +/* + * Probe a i2400m interface and register it + * + * @func: SDIO function + * @id: SDIO device ID + * @returns: 0 if ok, < 0 errno code on error. + * + * Alloc a net device, initialize the bus-specific details and then + * calls the bus-generic initialization routine. That will register + * the wimax and netdev devices, upload the firmware [using + * _bus_bm_*()], call _bus_dev_start() to finalize the setup of the + * communication with the device and then will start to talk to it to + * finnish setting it up. + * + * Initialization is tricky; some instances of the hw are packed with + * others in a way that requires a third driver that enables the WiMAX + * function. In those cases, we can't enable the SDIO function and + * we'll return with -ENODEV. When the driver that enables the WiMAX + * function does its thing, it has to do a bus_rescan_devices() on the + * SDIO bus so this driver is called again to enumerate the WiMAX + * function. + */ +static +int i2400ms_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int result; + struct net_device *net_dev; + struct device *dev = &func->dev; + struct i2400m *i2400m; + struct i2400ms *i2400ms; + + /* Allocate instance [calls i2400m_netdev_setup() on it]. */ + result = -ENOMEM; + net_dev = alloc_netdev(sizeof(*i2400ms), "wmx%d", + i2400ms_netdev_setup); + if (net_dev == NULL) { + dev_err(dev, "no memory for network device instance\n"); + goto error_alloc_netdev; + } + SET_NETDEV_DEV(net_dev, dev); + i2400m = net_dev_to_i2400m(net_dev); + i2400ms = container_of(i2400m, struct i2400ms, i2400m); + i2400m->wimax_dev.net_dev = net_dev; + i2400ms->func = func; + sdio_set_drvdata(func, i2400ms); + + i2400m->bus_tx_block_size = I2400MS_BLK_SIZE; + i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX; + i2400m->bus_dev_start = i2400ms_bus_dev_start; + i2400m->bus_dev_stop = i2400ms_bus_dev_stop; + i2400m->bus_tx_kick = i2400ms_bus_tx_kick; + i2400m->bus_reset = i2400ms_bus_reset; + i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send; + i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack; + i2400m->bus_fw_name = I2400MS_FW_FILE_NAME; + i2400m->bus_bm_mac_addr_impaired = 1; + + result = i2400ms_enable_function(i2400ms->func); + if (result < 0) { + dev_err(dev, "Cannot enable SDIO function: %d\n", result); + goto error_func_enable; + } + + sdio_claim_host(func); + result = sdio_set_block_size(func, I2400MS_BLK_SIZE); + if (result < 0) { + dev_err(dev, "Failed to set block size: %d\n", result); + goto error_set_blk_size; + } + sdio_release_host(func); + + result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT); + if (result < 0) { + dev_err(dev, "cannot setup device: %d\n", result); + goto error_setup; + } + + result = i2400ms_debugfs_add(i2400ms); + if (result < 0) { + dev_err(dev, "cannot create SDIO debugfs: %d\n", + result); + goto error_debugfs_add; + } + return 0; + +error_debugfs_add: + i2400m_release(i2400m); +error_setup: + sdio_set_drvdata(func, NULL); + sdio_claim_host(func); +error_set_blk_size: + sdio_disable_func(func); + sdio_release_host(func); +error_func_enable: + free_netdev(net_dev); +error_alloc_netdev: + return result; +} + + +static +void i2400ms_remove(struct sdio_func *func) +{ + struct device *dev = &func->dev; + struct i2400ms *i2400ms = sdio_get_drvdata(func); + struct i2400m *i2400m = &i2400ms->i2400m; + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + + d_fnstart(3, dev, "SDIO func %p\n", func); + debugfs_remove_recursive(i2400ms->debugfs_dentry); + i2400m_release(i2400m); + sdio_set_drvdata(func, NULL); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + free_netdev(net_dev); + d_fnend(3, dev, "SDIO func %p\n", func); +} + +enum { + I2400MS_INTEL_VID = 0x89, +}; + +static +const struct sdio_device_id i2400ms_sdio_ids[] = { + /* Intel: i2400m WiMAX over SDIO */ + { SDIO_DEVICE(I2400MS_INTEL_VID, 0x1402) }, + { }, /* end: all zeroes */ +}; +MODULE_DEVICE_TABLE(sdio, i2400ms_sdio_ids); + + +static +struct sdio_driver i2400m_sdio_driver = { + .name = KBUILD_MODNAME, + .probe = i2400ms_probe, + .remove = i2400ms_remove, + .id_table = i2400ms_sdio_ids, +}; + + +static +int __init i2400ms_driver_init(void) +{ + return sdio_register_driver(&i2400m_sdio_driver); +} +module_init(i2400ms_driver_init); + + +static +void __exit i2400ms_driver_exit(void) +{ + flush_scheduled_work(); /* for the stuff we schedule */ + sdio_unregister_driver(&i2400m_sdio_driver); +} +module_exit(i2400ms_driver_exit); + + +MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); +MODULE_DESCRIPTION("Intel 2400M WiMAX networking for SDIO"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(I2400MS_FW_FILE_NAME); diff --git a/drivers/net/wimax/i2400m/tx.c b/drivers/net/wimax/i2400m/tx.c new file mode 100644 index 000000000000..613a88ffd651 --- /dev/null +++ b/drivers/net/wimax/i2400m/tx.c @@ -0,0 +1,817 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Generic (non-bus specific) TX handling + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * + * Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Rewritten to use a single FIFO to lower the memory allocation + * pressure and optimize cache hits when copying to the queue, as + * well as splitting out bus-specific code. + * + * + * Implements data transmission to the device; this is done through a + * software FIFO, as data/control frames can be coalesced (while the + * device is reading the previous tx transaction, others accumulate). + * + * A FIFO is used because at the end it is resource-cheaper that trying + * to implement scatter/gather over USB. As well, most traffic is going + * to be download (vs upload). + * + * The format for sending/receiving data to/from the i2400m is + * described in detail in rx.c:PROTOCOL FORMAT. In here we implement + * the transmission of that. This is split between a bus-independent + * part that just prepares everything and a bus-specific part that + * does the actual transmission over the bus to the device (in the + * bus-specific driver). + * + * + * The general format of a device-host transaction is MSG-HDR, PLD1, + * PLD2...PLDN, PL1, PL2,...PLN, PADDING. + * + * Because we need the send payload descriptors and then payloads and + * because it is kind of expensive to do scatterlists in USB (one URB + * per node), it becomes cheaper to append all the data to a FIFO + * (copying to a FIFO potentially in cache is cheaper). + * + * Then the bus-specific code takes the parts of that FIFO that are + * written and passes them to the device. + * + * So the concepts to keep in mind there are: + * + * We use a FIFO to queue the data in a linear buffer. We first append + * a MSG-HDR, space for I2400M_TX_PLD_MAX payload descriptors and then + * go appending payloads until we run out of space or of payload + * descriptors. Then we append padding to make the whole transaction a + * multiple of i2400m->bus_tx_block_size (as defined by the bus layer). + * + * - A TX message: a combination of a message header, payload + * descriptors and payloads. + * + * Open: it is marked as active (i2400m->tx_msg is valid) and we + * can keep adding payloads to it. + * + * Closed: we are not appending more payloads to this TX message + * (exahusted space in the queue, too many payloads or + * whichever). We have appended padding so the whole message + * length is aligned to i2400m->bus_tx_block_size (as set by the + * bus/transport layer). + * + * - Most of the time we keep a TX message open to which we append + * payloads. + * + * - If we are going to append and there is no more space (we are at + * the end of the FIFO), we close the message, mark the rest of the + * FIFO space unusable (skip_tail), create a new message at the + * beginning of the FIFO (if there is space) and append the message + * there. + * + * This is because we need to give linear TX messages to the bus + * engine. So we don't write a message to the remaining FIFO space + * until the tail and continue at the head of it. + * + * - We overload one of the fields in the message header to use it as + * 'size' of the TX message, so we can iterate over them. It also + * contains a flag that indicates if we have to skip it or not. + * When we send the buffer, we update that to its real on-the-wire + * value. + * + * - The MSG-HDR PLD1...PLD2 stuff has to be a size multiple of 16. + * + * It follows that if MSG-HDR says we have N messages, the whole + * header + descriptors is 16 + 4*N; for those to be a multiple of + * 16, it follows that N can be 4, 8, 12, ... (32, 48, 64, 80... + * bytes). + * + * So if we have only 1 payload, we have to submit a header that in + * all truth has space for 4. + * + * The implication is that we reserve space for 12 (64 bytes); but + * if we fill up only (eg) 2, our header becomes 32 bytes only. So + * the TX engine has to shift those 32 bytes of msg header and 2 + * payloads and padding so that right after it the payloads start + * and the TX engine has to know about that. + * + * It is cheaper to move the header up than the whole payloads down. + * + * We do this in i2400m_tx_close(). See 'i2400m_msg_hdr->offset'. + * + * - Each payload has to be size-padded to 16 bytes; before appending + * it, we just do it. + * + * - The whole message has to be padded to i2400m->bus_tx_block_size; + * we do this at close time. Thus, when reserving space for the + * payload, we always make sure there is also free space for this + * padding that sooner or later will happen. + * + * When we append a message, we tell the bus specific code to kick in + * TXs. It will TX (in parallel) until the buffer is exhausted--hence + * the lockin we do. The TX code will only send a TX message at the + * time (which remember, might contain more than one payload). Of + * course, when the bus-specific driver attempts to TX a message that + * is still open, it gets closed first. + * + * Gee, this is messy; well a picture. In the example below we have a + * partially full FIFO, with a closed message ready to be delivered + * (with a moved message header to make sure it is size-aligned to + * 16), TAIL room that was unusable (and thus is marked with a message + * header that says 'skip this') and at the head of the buffer, an + * imcomplete message with a couple of payloads. + * + * N ___________________________________________________ + * | | + * | TAIL room | + * | | + * | msg_hdr to skip (size |= 0x80000) | + * |---------------------------------------------------|------- + * | | /|\ + * | | | + * | TX message padding | | + * | | | + * | | | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | + * | | | + * | payload 1 | | + * | | N * tx_block_size + * | | | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | + * | | | + * | payload 1 | | + * | | | + * | | | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -|- -|- - - - + * | padding 3 /|\ | | /|\ + * | padding 2 | | | | + * | pld 1 32 bytes (2 * 16) | | | + * | pld 0 | | | | + * | moved msg_hdr \|/ | \|/ | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -|- - - | + * | | _PLD_SIZE + * | unused | | + * | | | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -| | + * | msg_hdr (size X) [this message is closed] | \|/ + * |===================================================|========== <=== OUT + * | | + * | | + * | | + * | Free rooom | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * | | + * |===================================================|========== <=== IN + * | | + * | | + * | | + * | | + * | payload 1 | + * | | + * | | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -| + * | | + * | payload 0 | + * | | + * | | + * |- - - - - - - - - - - - - - - - - - - - - - - - - -| + * | pld 11 /|\ | + * | ... | | + * | pld 1 64 bytes (2 * 16) | + * | pld 0 | | + * | msg_hdr (size X) \|/ [message is open] | + * 0 --------------------------------------------------- + * + * + * ROADMAP + * + * i2400m_tx_setup() Called by i2400m_setup + * i2400m_tx_release() Called by i2400m_release() + * + * i2400m_tx() Called to send data or control frames + * i2400m_tx_fifo_push() Allocates append-space in the FIFO + * i2400m_tx_new() Opens a new message in the FIFO + * i2400m_tx_fits() Checks if a new payload fits in the message + * i2400m_tx_close() Closes an open message in the FIFO + * i2400m_tx_skip_tail() Marks unusable FIFO tail space + * i2400m->bus_tx_kick() + * + * Now i2400m->bus_tx_kick() is the the bus-specific driver backend + * implementation; that would do: + * + * i2400m->bus_tx_kick() + * i2400m_tx_msg_get() Gets first message ready to go + * ...sends it... + * i2400m_tx_msg_sent() Ack the message is sent; repeat from + * _tx_msg_get() until it returns NULL + * (FIFO empty). + */ +#include <linux/netdevice.h> +#include "i2400m.h" + + +#define D_SUBMODULE tx +#include "debug-levels.h" + +enum { + /** + * TX Buffer size + * + * Doc says maximum transaction is 16KiB. If we had 16KiB en + * route and 16KiB being queued, it boils down to needing + * 32KiB. + */ + I2400M_TX_BUF_SIZE = 32768, + /** + * Message header and payload descriptors have to be 16 + * aligned (16 + 4 * N = 16 * M). If we take that average sent + * packets are MTU size (~1400-~1500) it follows that we could + * fit at most 10-11 payloads in one transaction. To meet the + * alignment requirement, that means we need to leave space + * for 12 (64 bytes). To simplify, we leave space for that. If + * at the end there are less, we pad up to the nearest + * multiple of 16. + */ + I2400M_TX_PLD_MAX = 12, + I2400M_TX_PLD_SIZE = sizeof(struct i2400m_msg_hdr) + + I2400M_TX_PLD_MAX * sizeof(struct i2400m_pld), + I2400M_TX_SKIP = 0x80000000, +}; + +#define TAIL_FULL ((void *)~(unsigned long)NULL) + +/* + * Allocate @size bytes in the TX fifo, return a pointer to it + * + * @i2400m: device descriptor + * @size: size of the buffer we need to allocate + * @padding: ensure that there is at least this many bytes of free + * contiguous space in the fifo. This is needed because later on + * we might need to add padding. + * + * Returns: + * + * Pointer to the allocated space. NULL if there is no + * space. TAIL_FULL if there is no space at the tail but there is at + * the head (Case B below). + * + * These are the two basic cases we need to keep an eye for -- it is + * much better explained in linux/kernel/kfifo.c, but this code + * basically does the same. No rocket science here. + * + * Case A Case B + * N ___________ ___________ + * | tail room | | data | + * | | | | + * |<- IN ->| |<- OUT ->| + * | | | | + * | data | | room | + * | | | | + * |<- OUT ->| |<- IN ->| + * | | | | + * | head room | | data | + * 0 ----------- ----------- + * + * We allocate only *contiguous* space. + * + * We can allocate only from 'room'. In Case B, it is simple; in case + * A, we only try from the tail room; if it is not enough, we just + * fail and return TAIL_FULL and let the caller figure out if we wants to + * skip the tail room and try to allocate from the head. + * + * Note: + * + * Assumes i2400m->tx_lock is taken, and we use that as a barrier + * + * The indexes keep increasing and we reset them to zero when we + * pop data off the queue + */ +static +void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding) +{ + struct device *dev = i2400m_dev(i2400m); + size_t room, tail_room, needed_size; + void *ptr; + + needed_size = size + padding; + room = I2400M_TX_BUF_SIZE - (i2400m->tx_in - i2400m->tx_out); + if (room < needed_size) { /* this takes care of Case B */ + d_printf(2, dev, "fifo push %zu/%zu: no space\n", + size, padding); + return NULL; + } + /* Is there space at the tail? */ + tail_room = I2400M_TX_BUF_SIZE - i2400m->tx_in % I2400M_TX_BUF_SIZE; + if (tail_room < needed_size) { + if (i2400m->tx_out % I2400M_TX_BUF_SIZE + < i2400m->tx_in % I2400M_TX_BUF_SIZE) { + d_printf(2, dev, "fifo push %zu/%zu: tail full\n", + size, padding); + return TAIL_FULL; /* There might be head space */ + } else { + d_printf(2, dev, "fifo push %zu/%zu: no head space\n", + size, padding); + return NULL; /* There is no space */ + } + } + ptr = i2400m->tx_buf + i2400m->tx_in % I2400M_TX_BUF_SIZE; + d_printf(2, dev, "fifo push %zu/%zu: at @%zu\n", size, padding, + i2400m->tx_in % I2400M_TX_BUF_SIZE); + i2400m->tx_in += size; + return ptr; +} + + +/* + * Mark the tail of the FIFO buffer as 'to-skip' + * + * We should never hit the BUG_ON() because all the sizes we push to + * the FIFO are padded to be a multiple of 16 -- the size of *msg + * (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the + * header). + * + * Note: + * + * Assumes i2400m->tx_lock is taken, and we use that as a barrier + */ +static +void i2400m_tx_skip_tail(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE; + size_t tail_room = I2400M_TX_BUF_SIZE - tx_in; + struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in; + BUG_ON(tail_room < sizeof(*msg)); + msg->size = tail_room | I2400M_TX_SKIP; + d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n", + tail_room, tx_in); + i2400m->tx_in += tail_room; +} + + +/* + * Check if a skb will fit in the TX queue's current active TX + * message (if there are still descriptors left unused). + * + * Returns: + * 0 if the message won't fit, 1 if it will. + * + * Note: + * + * Assumes a TX message is active (i2400m->tx_msg). + * + * Assumes i2400m->tx_lock is taken, and we use that as a barrier + */ +static +unsigned i2400m_tx_fits(struct i2400m *i2400m) +{ + struct i2400m_msg_hdr *msg_hdr = i2400m->tx_msg; + return le16_to_cpu(msg_hdr->num_pls) < I2400M_TX_PLD_MAX; + +} + + +/* + * Start a new TX message header in the queue. + * + * Reserve memory from the base FIFO engine and then just initialize + * the message header. + * + * We allocate the biggest TX message header we might need (one that'd + * fit I2400M_TX_PLD_MAX payloads) -- when it is closed it will be + * 'ironed it out' and the unneeded parts removed. + * + * NOTE: + * + * Assumes that the previous message is CLOSED (eg: either + * there was none or 'i2400m_tx_close()' was called on it). + * + * Assumes i2400m->tx_lock is taken, and we use that as a barrier + */ +static +void i2400m_tx_new(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + struct i2400m_msg_hdr *tx_msg; + BUG_ON(i2400m->tx_msg != NULL); +try_head: + tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, 0); + if (tx_msg == NULL) + goto out; + else if (tx_msg == TAIL_FULL) { + i2400m_tx_skip_tail(i2400m); + d_printf(2, dev, "new TX message: tail full, trying head\n"); + goto try_head; + } + memset(tx_msg, 0, I2400M_TX_PLD_SIZE); + tx_msg->size = I2400M_TX_PLD_SIZE; +out: + i2400m->tx_msg = tx_msg; + d_printf(2, dev, "new TX message: %p @%zu\n", + tx_msg, (void *) tx_msg - i2400m->tx_buf); +} + + +/* + * Finalize the current TX message header + * + * Sets the message header to be at the proper location depending on + * how many descriptors we have (check documentation at the file's + * header for more info on that). + * + * Appends padding bytes to make sure the whole TX message (counting + * from the 'relocated' message header) is aligned to + * tx_block_size. We assume the _append() code has left enough space + * in the FIFO for that. If there are no payloads, just pass, as it + * won't be transferred. + * + * The amount of padding bytes depends on how many payloads are in the + * TX message, as the "msg header and payload descriptors" will be + * shifted up in the buffer. + */ +static +void i2400m_tx_close(struct i2400m *i2400m) +{ + struct device *dev = i2400m_dev(i2400m); + struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; + struct i2400m_msg_hdr *tx_msg_moved; + size_t aligned_size, padding, hdr_size; + void *pad_buf; + + if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */ + goto out; + + /* Relocate the message header + * + * Find the current header size, align it to 16 and if we need + * to move it so the tail is next to the payloads, move it and + * set the offset. + * + * If it moved, this header is good only for transmission; the + * original one (it is kept if we moved) is still used to + * figure out where the next TX message starts (and where the + * offset to the moved header is). + */ + hdr_size = sizeof(*tx_msg) + + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); + hdr_size = ALIGN(hdr_size, I2400M_PL_PAD); + tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; + tx_msg_moved = (void *) tx_msg + tx_msg->offset; + memmove(tx_msg_moved, tx_msg, hdr_size); + tx_msg_moved->size -= tx_msg->offset; + /* + * Now figure out how much we have to add to the (moved!) + * message so the size is a multiple of i2400m->bus_tx_block_size. + */ + aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size); + padding = aligned_size - tx_msg_moved->size; + if (padding > 0) { + pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0); + if (unlikely(WARN_ON(pad_buf == NULL + || pad_buf == TAIL_FULL))) { + /* This should not happen -- append should verify + * there is always space left at least to append + * tx_block_size */ + dev_err(dev, + "SW BUG! Possible data leakage from memory the " + "device should not read for padding - " + "size %lu aligned_size %zu tx_buf %p in " + "%zu out %zu\n", + (unsigned long) tx_msg_moved->size, + aligned_size, i2400m->tx_buf, i2400m->tx_in, + i2400m->tx_out); + } else + memset(pad_buf, 0xad, padding); + } + tx_msg_moved->padding = cpu_to_le16(padding); + tx_msg_moved->size += padding; + if (tx_msg != tx_msg_moved) + tx_msg->size += padding; +out: + i2400m->tx_msg = NULL; +} + + +/** + * i2400m_tx - send the data in a buffer to the device + * + * @buf: pointer to the buffer to transmit + * + * @buf_len: buffer size + * + * @pl_type: type of the payload we are sending. + * + * Returns: + * 0 if ok, < 0 errno code on error (-ENOSPC, if there is no more + * room for the message in the queue). + * + * Appends the buffer to the TX FIFO and notifies the bus-specific + * part of the driver that there is new data ready to transmit. + * Once this function returns, the buffer has been copied, so it can + * be reused. + * + * The steps followed to append are explained in detail in the file + * header. + * + * Whenever we write to a message, we increase msg->size, so it + * reflects exactly how big the message is. This is needed so that if + * we concatenate two messages before they can be sent, the code that + * sends the messages can find the boundaries (and it will replace the + * size with the real barker before sending). + * + * Note: + * + * Cold and warm reset payloads need to be sent as a single + * payload, so we handle that. + */ +int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, + enum i2400m_pt pl_type) +{ + int result = -ENOSPC; + struct device *dev = i2400m_dev(i2400m); + unsigned long flags; + size_t padded_len; + void *ptr; + unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM + || pl_type == I2400M_PT_RESET_COLD; + + d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", + i2400m, buf, buf_len, pl_type); + padded_len = ALIGN(buf_len, I2400M_PL_PAD); + d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); + /* If there is no current TX message, create one; if the + * current one is out of payload slots or we have a singleton, + * close it and start a new one */ + spin_lock_irqsave(&i2400m->tx_lock, flags); +try_new: + if (unlikely(i2400m->tx_msg == NULL)) + i2400m_tx_new(i2400m); + else if (unlikely(!i2400m_tx_fits(i2400m) + || (is_singleton && i2400m->tx_msg->num_pls != 0))) { + d_printf(2, dev, "closing TX message (fits %u singleton " + "%u num_pls %u)\n", i2400m_tx_fits(i2400m), + is_singleton, i2400m->tx_msg->num_pls); + i2400m_tx_close(i2400m); + i2400m_tx_new(i2400m); + } + if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) { + d_printf(2, dev, "TX: message too big, going new\n"); + i2400m_tx_close(i2400m); + i2400m_tx_new(i2400m); + } + if (i2400m->tx_msg == NULL) + goto error_tx_new; + /* So we have a current message header; now append space for + * the message -- if there is not enough, try the head */ + ptr = i2400m_tx_fifo_push(i2400m, padded_len, + i2400m->bus_tx_block_size); + if (ptr == TAIL_FULL) { /* Tail is full, try head */ + d_printf(2, dev, "pl append: tail full\n"); + i2400m_tx_close(i2400m); + i2400m_tx_skip_tail(i2400m); + goto try_new; + } else if (ptr == NULL) { /* All full */ + result = -ENOSPC; + d_printf(2, dev, "pl append: all full\n"); + } else { /* Got space, copy it, set padding */ + struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; + unsigned num_pls = le16_to_cpu(tx_msg->num_pls); + memcpy(ptr, buf, buf_len); + memset(ptr + buf_len, 0xad, padded_len - buf_len); + i2400m_pld_set(&tx_msg->pld[num_pls], buf_len, pl_type); + d_printf(3, dev, "pld 0x%08x (type 0x%1x len 0x%04zx\n", + le32_to_cpu(tx_msg->pld[num_pls].val), + pl_type, buf_len); + tx_msg->num_pls = le16_to_cpu(num_pls+1); + tx_msg->size += padded_len; + d_printf(2, dev, "TX: appended %zu b (up to %u b) pl #%u \n", + padded_len, tx_msg->size, num_pls+1); + d_printf(2, dev, + "TX: appended hdr @%zu %zu b pl #%u @%zu %zu/%zu b\n", + (void *)tx_msg - i2400m->tx_buf, (size_t)tx_msg->size, + num_pls+1, ptr - i2400m->tx_buf, buf_len, padded_len); + result = 0; + if (is_singleton) + i2400m_tx_close(i2400m); + } +error_tx_new: + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + i2400m->bus_tx_kick(i2400m); /* always kick, might free up space */ + d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n", + i2400m, buf, buf_len, pl_type, result); + return result; +} +EXPORT_SYMBOL_GPL(i2400m_tx); + + +/** + * i2400m_tx_msg_get - Get the first TX message in the FIFO to start sending it + * + * @i2400m: device descriptors + * @bus_size: where to place the size of the TX message + * + * Called by the bus-specific driver to get the first TX message at + * the FIF that is ready for transmission. + * + * It sets the state in @i2400m to indicate the bus-specific driver is + * transfering that message (i2400m->tx_msg_size). + * + * Once the transfer is completed, call i2400m_tx_msg_sent(). + * + * Notes: + * + * The size of the TX message to be transmitted might be smaller than + * that of the TX message in the FIFO (in case the header was + * shorter). Hence, we copy it in @bus_size, for the bus layer to + * use. We keep the message's size in i2400m->tx_msg_size so that + * when the bus later is done transferring we know how much to + * advance the fifo. + * + * We collect statistics here as all the data is available and we + * assume it is going to work [see i2400m_tx_msg_sent()]. + */ +struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *i2400m, + size_t *bus_size) +{ + struct device *dev = i2400m_dev(i2400m); + struct i2400m_msg_hdr *tx_msg, *tx_msg_moved; + unsigned long flags, pls; + + d_fnstart(3, dev, "(i2400m %p bus_size %p)\n", i2400m, bus_size); + spin_lock_irqsave(&i2400m->tx_lock, flags); +skip: + tx_msg_moved = NULL; + if (i2400m->tx_in == i2400m->tx_out) { /* Empty FIFO? */ + i2400m->tx_in = 0; + i2400m->tx_out = 0; + d_printf(2, dev, "TX: FIFO empty: resetting\n"); + goto out_unlock; + } + tx_msg = i2400m->tx_buf + i2400m->tx_out % I2400M_TX_BUF_SIZE; + if (tx_msg->size & I2400M_TX_SKIP) { /* skip? */ + d_printf(2, dev, "TX: skip: msg @%zu (%zu b)\n", + i2400m->tx_out % I2400M_TX_BUF_SIZE, + (size_t) tx_msg->size & ~I2400M_TX_SKIP); + i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP; + goto skip; + } + + if (tx_msg->num_pls == 0) { /* No payloads? */ + if (tx_msg == i2400m->tx_msg) { /* open, we are done */ + d_printf(2, dev, + "TX: FIFO empty: open msg w/o payloads @%zu\n", + (void *) tx_msg - i2400m->tx_buf); + tx_msg = NULL; + goto out_unlock; + } else { /* closed, skip it */ + d_printf(2, dev, + "TX: skip msg w/o payloads @%zu (%zu b)\n", + (void *) tx_msg - i2400m->tx_buf, + (size_t) tx_msg->size); + i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP; + goto skip; + } + } + if (tx_msg == i2400m->tx_msg) /* open msg? */ + i2400m_tx_close(i2400m); + + /* Now we have a valid TX message (with payloads) to TX */ + tx_msg_moved = (void *) tx_msg + tx_msg->offset; + i2400m->tx_msg_size = tx_msg->size; + *bus_size = tx_msg_moved->size; + d_printf(2, dev, "TX: pid %d msg hdr at @%zu offset +@%zu " + "size %zu bus_size %zu\n", + current->pid, (void *) tx_msg - i2400m->tx_buf, + (size_t) tx_msg->offset, (size_t) tx_msg->size, + (size_t) tx_msg_moved->size); + tx_msg_moved->barker = le32_to_cpu(I2400M_H2D_PREVIEW_BARKER); + tx_msg_moved->sequence = le32_to_cpu(i2400m->tx_sequence++); + + pls = le32_to_cpu(tx_msg_moved->num_pls); + i2400m->tx_pl_num += pls; /* Update stats */ + if (pls > i2400m->tx_pl_max) + i2400m->tx_pl_max = pls; + if (pls < i2400m->tx_pl_min) + i2400m->tx_pl_min = pls; + i2400m->tx_num++; + i2400m->tx_size_acc += *bus_size; + if (*bus_size < i2400m->tx_size_min) + i2400m->tx_size_min = *bus_size; + if (*bus_size > i2400m->tx_size_max) + i2400m->tx_size_max = *bus_size; +out_unlock: + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + d_fnstart(3, dev, "(i2400m %p bus_size %p [%zu]) = %p\n", + i2400m, bus_size, *bus_size, tx_msg_moved); + return tx_msg_moved; +} +EXPORT_SYMBOL_GPL(i2400m_tx_msg_get); + + +/** + * i2400m_tx_msg_sent - indicate the transmission of a TX message + * + * @i2400m: device descriptor + * + * Called by the bus-specific driver when a message has been sent; + * this pops it from the FIFO; and as there is space, start the queue + * in case it was stopped. + * + * Should be called even if the message send failed and we are + * dropping this TX message. + */ +void i2400m_tx_msg_sent(struct i2400m *i2400m) +{ + unsigned n; + unsigned long flags; + struct device *dev = i2400m_dev(i2400m); + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + spin_lock_irqsave(&i2400m->tx_lock, flags); + i2400m->tx_out += i2400m->tx_msg_size; + d_printf(2, dev, "TX: sent %zu b\n", (size_t) i2400m->tx_msg_size); + i2400m->tx_msg_size = 0; + BUG_ON(i2400m->tx_out > i2400m->tx_in); + /* level them FIFO markers off */ + n = i2400m->tx_out / I2400M_TX_BUF_SIZE; + i2400m->tx_out %= I2400M_TX_BUF_SIZE; + i2400m->tx_in -= n * I2400M_TX_BUF_SIZE; + netif_start_queue(i2400m->wimax_dev.net_dev); + spin_unlock_irqrestore(&i2400m->tx_lock, flags); + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} +EXPORT_SYMBOL_GPL(i2400m_tx_msg_sent); + + +/** + * i2400m_tx_setup - Initialize the TX queue and infrastructure + * + * Make sure we reset the TX sequence to zero, as when this function + * is called, the firmware has been just restarted. + */ +int i2400m_tx_setup(struct i2400m *i2400m) +{ + int result; + + /* Do this here only once -- can't do on + * i2400m_hard_start_xmit() as we'll cause race conditions if + * the WS was scheduled on another CPU */ + INIT_WORK(&i2400m->wake_tx_ws, i2400m_wake_tx_work); + + i2400m->tx_sequence = 0; + i2400m->tx_buf = kmalloc(I2400M_TX_BUF_SIZE, GFP_KERNEL); + if (i2400m->tx_buf == NULL) + result = -ENOMEM; + else + result = 0; + /* Huh? the bus layer has to define this... */ + BUG_ON(i2400m->bus_tx_block_size == 0); + return result; + +} + + +/** + * i2400m_tx_release - Tear down the TX queue and infrastructure + */ +void i2400m_tx_release(struct i2400m *i2400m) +{ + kfree(i2400m->tx_buf); +} diff --git a/drivers/net/wimax/i2400m/usb-debug-levels.h b/drivers/net/wimax/i2400m/usb-debug-levels.h new file mode 100644 index 000000000000..e4358bd880be --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-debug-levels.h @@ -0,0 +1,42 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Debug levels control file for the i2400m-usb module + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __debug_levels__h__ +#define __debug_levels__h__ + +/* Maximum compile and run time debug level for all submodules */ +#define D_MODULENAME i2400m_usb +#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL + +#include <linux/wimax/debug.h> + +/* List of all the enabled modules */ +enum d_module { + D_SUBMODULE_DECLARE(usb), + D_SUBMODULE_DECLARE(fw), + D_SUBMODULE_DECLARE(notif), + D_SUBMODULE_DECLARE(rx), + D_SUBMODULE_DECLARE(tx), +}; + + +#endif /* #ifndef __debug_levels__h__ */ diff --git a/drivers/net/wimax/i2400m/usb-fw.c b/drivers/net/wimax/i2400m/usb-fw.c new file mode 100644 index 000000000000..5ad287c228b8 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-fw.c @@ -0,0 +1,340 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Firmware uploader's USB specifics + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Initial implementation + * + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - bus generic/specific split + * + * THE PROCEDURE + * + * See fw.c for the generic description of this procedure. + * + * This file implements only the USB specifics. It boils down to how + * to send a command and waiting for an acknowledgement from the + * device. + * + * This code (and process) is single threaded. It assumes it is the + * only thread poking around (guaranteed by fw.c). + * + * COMMAND EXECUTION + * + * A write URB is posted with the buffer to the bulk output endpoint. + * + * ACK RECEPTION + * + * We just post a URB to the notification endpoint and wait for + * data. We repeat until we get all the data we expect (as indicated + * by the call from the bus generic code). + * + * The data is not read from the bulk in endpoint for boot mode. + * + * ROADMAP + * + * i2400mu_bus_bm_cmd_send + * i2400m_bm_cmd_prepare... + * i2400mu_tx_bulk_out + * + * i2400mu_bus_bm_wait_for_ack + * i2400m_notif_submit + */ +#include <linux/usb.h> +#include "i2400m-usb.h" + + +#define D_SUBMODULE fw +#include "usb-debug-levels.h" + + +/* + * Synchronous write to the device + * + * Takes care of updating EDC counts and thus, handle device errors. + */ +static +ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size) +{ + int result; + struct device *dev = &i2400mu->usb_iface->dev; + int len; + struct usb_endpoint_descriptor *epd; + int pipe, do_autopm = 1; + + result = usb_autopm_get_interface(i2400mu->usb_iface); + if (result < 0) { + dev_err(dev, "BM-CMD: can't get autopm: %d\n", result); + do_autopm = 0; + } + epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); + pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); +retry: + result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, HZ); + switch (result) { + case 0: + if (len != buf_size) { + dev_err(dev, "BM-CMD: short write (%u B vs %zu " + "expected)\n", len, buf_size); + result = -EIO; + break; + } + result = len; + break; + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* just ignore it */ + case -ESHUTDOWN: /* and exit */ + case -ECONNRESET: + result = -ESHUTDOWN; + break; + case -ETIMEDOUT: /* bah... */ + break; + default: /* any other? */ + if (edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "BM-CMD: maximum errors in " + "URB exceeded; resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + result = -ENODEV; + break; + } + dev_err(dev, "BM-CMD: URB error %d, retrying\n", + result); + goto retry; + } + result = len; + if (do_autopm) + usb_autopm_put_interface(i2400mu->usb_iface); + return result; +} + + +/* + * Send a boot-mode command over the bulk-out pipe + * + * Command can be a raw command, which requires no preparation (and + * which might not even be following the command format). Checks that + * the right amount of data was transfered. + * + * To satisfy USB requirements (no onstack, vmalloc or in data segment + * buffers), we copy the command to i2400m->bm_cmd_buf and send it from + * there. + * + * @flags: pass thru from i2400m_bm_cmd() + * @return: cmd_size if ok, < 0 errno code on error. + */ +ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m, + const struct i2400m_bootrom_header *_cmd, + size_t cmd_size, int flags) +{ + ssize_t result; + struct device *dev = i2400m_dev(i2400m); + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd); + struct i2400m_bootrom_header *cmd; + size_t cmd_size_a = ALIGN(cmd_size, 16); /* USB restriction */ + + d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n", + i2400m, _cmd, cmd_size); + result = -E2BIG; + if (cmd_size > I2400M_BM_CMD_BUF_SIZE) + goto error_too_big; + memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); + cmd = i2400m->bm_cmd_buf; + if (cmd_size_a > cmd_size) /* Zero pad space */ + memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size); + if ((flags & I2400M_BM_CMD_RAW) == 0) { + if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0)) + dev_warn(dev, "SW BUG: response_required == 0\n"); + i2400m_bm_cmd_prepare(cmd); + } + result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size); + if (result < 0) { + dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n", + opcode, result); + goto error_cmd_send; + } + if (result != cmd_size) { /* all was transferred? */ + dev_err(dev, "boot-mode cmd %d: incomplete transfer " + "(%zu vs %zu submitted)\n", opcode, result, cmd_size); + result = -EIO; + goto error_cmd_size; + } +error_cmd_size: +error_cmd_send: +error_too_big: + d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n", + i2400m, _cmd, cmd_size, result); + return result; +} + + +static +void __i2400mu_bm_notif_cb(struct urb *urb) +{ + complete(urb->context); +} + + +/* + * submit a read to the notification endpoint + * + * @i2400m: device descriptor + * @urb: urb to use + * @completion: completion varible to complete when done + * + * Data is always read to i2400m->bm_ack_buf + */ +static +int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb, + struct completion *completion) +{ + struct i2400m *i2400m = &i2400mu->i2400m; + struct usb_endpoint_descriptor *epd; + int pipe; + + epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION); + pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress); + usb_fill_int_urb(urb, i2400mu->usb_dev, pipe, + i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE, + __i2400mu_bm_notif_cb, completion, + epd->bInterval); + return usb_submit_urb(urb, GFP_KERNEL); +} + + +/* + * Read an ack from the notification endpoint + * + * @i2400m: + * @_ack: pointer to where to store the read data + * @ack_size: how many bytes we should read + * + * Returns: < 0 errno code on error; otherwise, amount of received bytes. + * + * Submits a notification read, appends the read data to the given ack + * buffer and then repeats (until @ack_size bytes have been + * received). + */ +ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m, + struct i2400m_bootrom_header *_ack, + size_t ack_size) +{ + ssize_t result = -ENOMEM; + struct device *dev = i2400m_dev(i2400m); + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + struct urb notif_urb; + void *ack = _ack; + size_t offset, len; + long val; + int do_autopm = 1; + DECLARE_COMPLETION_ONSTACK(notif_completion); + + d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n", + i2400m, ack, ack_size); + BUG_ON(_ack == i2400m->bm_ack_buf); + result = usb_autopm_get_interface(i2400mu->usb_iface); + if (result < 0) { + dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result); + do_autopm = 0; + } + usb_init_urb(¬if_urb); /* ready notifications */ + usb_get_urb(¬if_urb); + offset = 0; + while (offset < ack_size) { + init_completion(¬if_completion); + result = i2400mu_notif_submit(i2400mu, ¬if_urb, + ¬if_completion); + if (result < 0) + goto error_notif_urb_submit; + val = wait_for_completion_interruptible_timeout( + ¬if_completion, HZ); + if (val == 0) { + result = -ETIMEDOUT; + usb_kill_urb(¬if_urb); /* Timedout */ + goto error_notif_wait; + } + if (val == -ERESTARTSYS) { + result = -EINTR; /* Interrupted */ + usb_kill_urb(¬if_urb); + goto error_notif_wait; + } + result = notif_urb.status; /* How was the ack? */ + switch (result) { + case 0: + break; + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* just ignore it */ + case -ESHUTDOWN: /* and exit */ + case -ECONNRESET: + result = -ESHUTDOWN; + goto error_dev_gone; + default: /* any other? */ + usb_kill_urb(¬if_urb); /* Timedout */ + if (edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) + goto error_exceeded; + dev_err(dev, "BM-ACK: URB error %d, " + "retrying\n", notif_urb.status); + continue; /* retry */ + } + if (notif_urb.actual_length == 0) { + d_printf(6, dev, "ZLP received, retrying\n"); + continue; + } + /* Got data, append it to the buffer */ + len = min(ack_size - offset, (size_t) notif_urb.actual_length); + memcpy(ack + offset, i2400m->bm_ack_buf, len); + offset += len; + } + result = offset; +error_notif_urb_submit: +error_notif_wait: +error_dev_gone: +out: + if (do_autopm) + usb_autopm_put_interface(i2400mu->usb_iface); + d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %zd\n", + i2400m, ack, ack_size, result); + return result; + +error_exceeded: + dev_err(dev, "bm: maximum errors in notification URB exceeded; " + "resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + goto out; +} diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c new file mode 100644 index 000000000000..9702c22b2497 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-notif.c @@ -0,0 +1,269 @@ +/* + * Intel Wireless WiMAX Connection 2400m over USB + * Notification handling + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Initial implementation + * + * + * The notification endpoint is active when the device is not in boot + * mode; in here we just read and get notifications; based on those, + * we act to either reinitialize the device after a reboot or to + * submit a RX request. + * + * ROADMAP + * + * i2400mu_usb_notification_setup() + * + * i2400mu_usb_notification_release() + * + * i2400mu_usb_notification_cb() Called when a URB is ready + * i2400mu_notif_grok() + * i2400m_dev_reset_handle() + * i2400mu_rx_kick() + */ +#include <linux/usb.h> +#include "i2400m-usb.h" + + +#define D_SUBMODULE notif +#include "usb-debug-levels.h" + + +static const +__le32 i2400m_ZERO_BARKER[4] = { 0, 0, 0, 0 }; + + +/* + * Process a received notification + * + * In normal operation mode, we can only receive two types of payloads + * on the notification endpoint: + * + * - a reboot barker, we do a bootstrap (the device has reseted). + * + * - a block of zeroes: there is pending data in the IN endpoint + */ +static +int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf, + size_t buf_len) +{ + int ret; + struct device *dev = &i2400mu->usb_iface->dev; + struct i2400m *i2400m = &i2400mu->i2400m; + + d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n", + i2400mu, buf, buf_len); + ret = -EIO; + if (buf_len < sizeof(i2400m_NBOOT_BARKER)) + /* Not a bug, just ignore */ + goto error_bad_size; + if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER)) + || !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER))) + ret = i2400m_dev_reset_handle(i2400m); + else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) { + i2400mu_rx_kick(i2400mu); + ret = 0; + } else { /* Unknown or unexpected data in the notif message */ + char prefix[64]; + ret = -EIO; + dev_err(dev, "HW BUG? Unknown/unexpected data in notification " + "message (%zu bytes)\n", buf_len); + snprintf(prefix, sizeof(prefix), "%s %s: ", + dev_driver_string(dev) , dev->bus_id); + if (buf_len > 64) { + print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, + 8, 4, buf, 64, 0); + printk(KERN_ERR "%s... (only first 64 bytes " + "dumped)\n", prefix); + } else + print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, + 8, 4, buf, buf_len, 0); + } +error_bad_size: + d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n", + i2400mu, buf, buf_len, ret); + return ret; +} + + +/* + * URB callback for the notification endpoint + * + * @urb: the urb received from the notification endpoint + * + * This function will just process the USB side of the transaction, + * checking everything is fine, pass the processing to + * i2400m_notification_grok() and resubmit the URB. + */ +static +void i2400mu_notification_cb(struct urb *urb) +{ + int ret; + struct i2400mu *i2400mu = urb->context; + struct device *dev = &i2400mu->usb_iface->dev; + + d_fnstart(4, dev, "(urb %p status %d actual_length %d)\n", + urb, urb->status, urb->actual_length); + ret = urb->status; + switch (ret) { + case 0: + ret = i2400mu_notification_grok(i2400mu, urb->transfer_buffer, + urb->actual_length); + if (ret == -EIO && edc_inc(&i2400mu->urb_edc, EDC_MAX_ERRORS, + EDC_ERROR_TIMEFRAME)) + goto error_exceeded; + if (ret == -ENOMEM) /* uff...power cycle? shutdown? */ + goto error_exceeded; + break; + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* ditto */ + case -ESHUTDOWN: /* URB killed */ + case -ECONNRESET: /* disconnection */ + goto out; /* Notify around */ + default: /* Some error? */ + if (edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) + goto error_exceeded; + dev_err(dev, "notification: URB error %d, retrying\n", + urb->status); + } + usb_mark_last_busy(i2400mu->usb_dev); + ret = usb_submit_urb(i2400mu->notif_urb, GFP_ATOMIC); + switch (ret) { + case 0: + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* ditto */ + case -ESHUTDOWN: /* URB killed */ + case -ECONNRESET: /* disconnection */ + break; /* just ignore */ + default: /* Some error? */ + dev_err(dev, "notification: cannot submit URB: %d\n", ret); + goto error_submit; + } + d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n", + urb, urb->status, urb->actual_length); + return; + +error_exceeded: + dev_err(dev, "maximum errors in notification URB exceeded; " + "resetting device\n"); +error_submit: + usb_queue_reset_device(i2400mu->usb_iface); +out: + d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n", + urb, urb->status, urb->actual_length); + return; +} + + +/* + * setup the notification endpoint + * + * @i2400m: device descriptor + * + * This procedure prepares the notification urb and handler for receiving + * unsolicited barkers from the device. + */ +int i2400mu_notification_setup(struct i2400mu *i2400mu) +{ + struct device *dev = &i2400mu->usb_iface->dev; + int usb_pipe, ret = 0; + struct usb_endpoint_descriptor *epd; + char *buf; + + d_fnstart(4, dev, "(i2400m %p)\n", i2400mu); + buf = kmalloc(I2400MU_MAX_NOTIFICATION_LEN, GFP_KERNEL | GFP_DMA); + if (buf == NULL) { + dev_err(dev, "notification: buffer allocation failed\n"); + ret = -ENOMEM; + goto error_buf_alloc; + } + + i2400mu->notif_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!i2400mu->notif_urb) { + ret = -ENOMEM; + dev_err(dev, "notification: cannot allocate URB\n"); + goto error_alloc_urb; + } + epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION); + usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress); + usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe, + buf, I2400MU_MAX_NOTIFICATION_LEN, + i2400mu_notification_cb, i2400mu, epd->bInterval); + ret = usb_submit_urb(i2400mu->notif_urb, GFP_KERNEL); + if (ret != 0) { + dev_err(dev, "notification: cannot submit URB: %d\n", ret); + goto error_submit; + } + d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret); + return ret; + +error_submit: + usb_free_urb(i2400mu->notif_urb); +error_alloc_urb: + kfree(buf); +error_buf_alloc: + d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret); + return ret; +} + + +/* + * Tear down of the notification mechanism + * + * @i2400m: device descriptor + * + * Kill the interrupt endpoint urb, free any allocated resources. + * + * We need to check if we have done it before as for example, + * _suspend() call this; if after a suspend() we get a _disconnect() + * (as the case is when hibernating), nothing bad happens. + */ +void i2400mu_notification_release(struct i2400mu *i2400mu) +{ + struct device *dev = &i2400mu->usb_iface->dev; + + d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + if (i2400mu->notif_urb != NULL) { + usb_kill_urb(i2400mu->notif_urb); + kfree(i2400mu->notif_urb->transfer_buffer); + usb_free_urb(i2400mu->notif_urb); + i2400mu->notif_urb = NULL; + } + d_fnend(4, dev, "(i2400mu %p)\n", i2400mu); +} diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c new file mode 100644 index 000000000000..074cc1f89853 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-rx.c @@ -0,0 +1,417 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * USB RX handling + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Use skb_clone(), break up processing in chunks + * - Split transport/device specific + * - Make buffer size dynamic to exert less memory pressure + * + * + * This handles the RX path on USB. + * + * When a notification is received that says 'there is RX data ready', + * we call i2400mu_rx_kick(); that wakes up the RX kthread, which + * reads a buffer from USB and passes it to i2400m_rx() in the generic + * handling code. The RX buffer has an specific format that is + * described in rx.c. + * + * We use a kernel thread in a loop because: + * + * - we want to be able to call the USB power management get/put + * functions (blocking) before each transaction. + * + * - We might get a lot of notifications and we don't want to submit + * a zillion reads; by serializing, we are throttling. + * + * - RX data processing can get heavy enough so that it is not + * appropiate for doing it in the USB callback; thus we run it in a + * process context. + * + * We provide a read buffer of an arbitrary size (short of a page); if + * the callback reports -EOVERFLOW, it means it was too small, so we + * just double the size and retry (being careful to append, as + * sometimes the device provided some data). Every now and then we + * check if the average packet size is smaller than the current packet + * size and if so, we halve it. At the end, the size of the + * preallocated buffer should be following the average received + * transaction size, adapting dynamically to it. + * + * ROADMAP + * + * i2400mu_rx_kick() Called from notif.c when we get a + * 'data ready' notification + * i2400mu_rxd() Kernel RX daemon + * i2400mu_rx() Receive USB data + * i2400m_rx() Send data to generic i2400m RX handling + * + * i2400mu_rx_setup() called from i2400mu_bus_dev_start() + * + * i2400mu_rx_release() called from i2400mu_bus_dev_stop() + */ +#include <linux/workqueue.h> +#include <linux/usb.h> +#include "i2400m-usb.h" + + +#define D_SUBMODULE rx +#include "usb-debug-levels.h" + +/* + * Dynamic RX size + * + * We can't let the rx_size be a multiple of 512 bytes (the RX + * endpoint's max packet size). On some USB host controllers (we + * haven't been able to fully characterize which), if the device is + * about to send (for example) X bytes and we only post a buffer to + * receive n*512, it will fail to mark that as babble (so that + * i2400mu_rx() [case -EOVERFLOW] can resize the buffer and get the + * rest). + * + * So on growing or shrinking, if it is a multiple of the + * maxpacketsize, we remove some (instead of incresing some, so in a + * buddy allocator we try to waste less space). + * + * Note we also need a hook for this on i2400mu_rx() -- when we do the + * first read, we are sure we won't hit this spot because + * i240mm->rx_size has been set properly. However, if we have to + * double because of -EOVERFLOW, when we launch the read to get the + * rest of the data, we *have* to make sure that also is not a + * multiple of the max_pkt_size. + */ + +static +size_t i2400mu_rx_size_grow(struct i2400mu *i2400mu) +{ + struct device *dev = &i2400mu->usb_iface->dev; + size_t rx_size; + const size_t max_pkt_size = 512; + + rx_size = 2 * i2400mu->rx_size; + if (rx_size % max_pkt_size == 0) { + rx_size -= 8; + d_printf(1, dev, + "RX: expected size grew to %zu [adjusted -8] " + "from %zu\n", + rx_size, i2400mu->rx_size); + } else + d_printf(1, dev, + "RX: expected size grew to %zu from %zu\n", + rx_size, i2400mu->rx_size); + return rx_size; +} + + +static +void i2400mu_rx_size_maybe_shrink(struct i2400mu *i2400mu) +{ + const size_t max_pkt_size = 512; + struct device *dev = &i2400mu->usb_iface->dev; + + if (unlikely(i2400mu->rx_size_cnt >= 100 + && i2400mu->rx_size_auto_shrink)) { + size_t avg_rx_size = + i2400mu->rx_size_acc / i2400mu->rx_size_cnt; + size_t new_rx_size = i2400mu->rx_size / 2; + if (avg_rx_size < new_rx_size) { + if (new_rx_size % max_pkt_size == 0) { + new_rx_size -= 8; + d_printf(1, dev, + "RX: expected size shrank to %zu " + "[adjusted -8] from %zu\n", + new_rx_size, i2400mu->rx_size); + } else + d_printf(1, dev, + "RX: expected size shrank to %zu " + "from %zu\n", + new_rx_size, i2400mu->rx_size); + i2400mu->rx_size = new_rx_size; + i2400mu->rx_size_cnt = 0; + i2400mu->rx_size_acc = i2400mu->rx_size; + } + } +} + +/* + * Receive a message with payloads from the USB bus into an skb + * + * @i2400mu: USB device descriptor + * @rx_skb: skb where to place the received message + * + * Deals with all the USB-specifics of receiving, dynamically + * increasing the buffer size if so needed. Returns the payload in the + * skb, ready to process. On a zero-length packet, we retry. + * + * On soft USB errors, we retry (until they become too frequent and + * then are promoted to hard); on hard USB errors, we reset the + * device. On other errors (skb realloacation, we just drop it and + * hope for the next invocation to solve it). + * + * Returns: pointer to the skb if ok, ERR_PTR on error. + * NOTE: this function might realloc the skb (if it is too small), + * so always update with the one returned. + * ERR_PTR() is < 0 on error. + */ +static +struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb) +{ + int result = 0; + struct device *dev = &i2400mu->usb_iface->dev; + int usb_pipe, read_size, rx_size, do_autopm; + struct usb_endpoint_descriptor *epd; + const size_t max_pkt_size = 512; + + d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + do_autopm = atomic_read(&i2400mu->do_autopm); + result = do_autopm ? + usb_autopm_get_interface(i2400mu->usb_iface) : 0; + if (result < 0) { + dev_err(dev, "RX: can't get autopm: %d\n", result); + do_autopm = 0; + } + epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_IN); + usb_pipe = usb_rcvbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); +retry: + rx_size = skb_end_pointer(rx_skb) - rx_skb->data - rx_skb->len; + if (unlikely(rx_size % max_pkt_size == 0)) { + rx_size -= 8; + d_printf(1, dev, "RX: rx_size adapted to %d [-8]\n", rx_size); + } + result = usb_bulk_msg( + i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len, + rx_size, &read_size, HZ); + usb_mark_last_busy(i2400mu->usb_dev); + switch (result) { + case 0: + if (read_size == 0) + goto retry; /* ZLP, just resubmit */ + skb_put(rx_skb, read_size); + break; + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* just ignore it */ + case -ESHUTDOWN: + case -ECONNRESET: + break; + case -EOVERFLOW: { /* too small, reallocate */ + struct sk_buff *new_skb; + rx_size = i2400mu_rx_size_grow(i2400mu); + if (rx_size <= (1 << 16)) /* cap it */ + i2400mu->rx_size = rx_size; + else if (printk_ratelimit()) { + dev_err(dev, "BUG? rx_size up to %d\n", rx_size); + result = -EINVAL; + goto out; + } + skb_put(rx_skb, read_size); + new_skb = skb_copy_expand(rx_skb, 0, rx_size - rx_skb->len, + GFP_KERNEL); + if (new_skb == NULL) { + if (printk_ratelimit()) + dev_err(dev, "RX: Can't reallocate skb to %d; " + "RX dropped\n", rx_size); + kfree(rx_skb); + result = 0; + goto out; /* drop it...*/ + } + kfree_skb(rx_skb); + rx_skb = new_skb; + i2400mu->rx_size_cnt = 0; + i2400mu->rx_size_acc = i2400mu->rx_size; + d_printf(1, dev, "RX: size changed to %d, received %d, " + "copied %d, capacity %ld\n", + rx_size, read_size, rx_skb->len, + (long) (skb_end_pointer(new_skb) - new_skb->head)); + goto retry; + } + /* In most cases, it happens due to the hardware scheduling a + * read when there was no data - unfortunately, we have no way + * to tell this timeout from a USB timeout. So we just ignore + * it. */ + case -ETIMEDOUT: + dev_err(dev, "RX: timeout: %d\n", result); + result = 0; + break; + default: /* Any error */ + if (edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) + goto error_reset; + dev_err(dev, "RX: error receiving URB: %d, retrying\n", result); + goto retry; + } +out: + if (do_autopm) + usb_autopm_put_interface(i2400mu->usb_iface); + d_fnend(4, dev, "(i2400mu %p) = %p\n", i2400mu, rx_skb); + return rx_skb; + +error_reset: + dev_err(dev, "RX: maximum errors in URB exceeded; " + "resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + rx_skb = ERR_PTR(result); + goto out; +} + + +/* + * Kernel thread for USB reception of data + * + * This thread waits for a kick; once kicked, it will allocate an skb + * and receive a single message to it from USB (using + * i2400mu_rx()). Once received, it is passed to the generic i2400m RX + * code for processing. + * + * When done processing, it runs some dirty statistics to verify if + * the last 100 messages received were smaller than half of the + * current RX buffer size. In that case, the RX buffer size is + * halved. This will helps lowering the pressure on the memory + * allocator. + * + * Hard errors force the thread to exit. + */ +static +int i2400mu_rxd(void *_i2400mu) +{ + int result = 0; + struct i2400mu *i2400mu = _i2400mu; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = &i2400mu->usb_iface->dev; + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + size_t pending; + int rx_size; + struct sk_buff *rx_skb; + + d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + while (1) { + d_printf(2, dev, "TX: waiting for messages\n"); + pending = 0; + wait_event_interruptible( + i2400mu->rx_wq, + (kthread_should_stop() /* check this first! */ + || (pending = atomic_read(&i2400mu->rx_pending_count))) + ); + if (kthread_should_stop()) + break; + if (pending == 0) + continue; + rx_size = i2400mu->rx_size; + d_printf(2, dev, "RX: reading up to %d bytes\n", rx_size); + rx_skb = __netdev_alloc_skb(net_dev, rx_size, GFP_KERNEL); + if (rx_skb == NULL) { + dev_err(dev, "RX: can't allocate skb [%d bytes]\n", + rx_size); + msleep(50); /* give it some time? */ + continue; + } + + /* Receive the message with the payloads */ + rx_skb = i2400mu_rx(i2400mu, rx_skb); + result = PTR_ERR(rx_skb); + if (IS_ERR(rx_skb)) + goto out; + atomic_dec(&i2400mu->rx_pending_count); + if (rx_skb->len == 0) { /* some ignorable condition */ + kfree_skb(rx_skb); + continue; + } + + /* Deliver the message to the generic i2400m code */ + i2400mu->rx_size_cnt++; + i2400mu->rx_size_acc += rx_skb->len; + result = i2400m_rx(i2400m, rx_skb); + if (result == -EIO + && edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + goto error_reset; + } + + /* Maybe adjust RX buffer size */ + i2400mu_rx_size_maybe_shrink(i2400mu); + } + result = 0; +out: + d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); + return result; + +error_reset: + dev_err(dev, "RX: maximum errors in received buffer exceeded; " + "resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + goto out; +} + + +/* + * Start reading from the device + * + * @i2400m: device instance + * + * Notify the RX thread that there is data pending. + */ +void i2400mu_rx_kick(struct i2400mu *i2400mu) +{ + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = &i2400mu->usb_iface->dev; + + d_fnstart(3, dev, "(i2400mu %p)\n", i2400m); + atomic_inc(&i2400mu->rx_pending_count); + wake_up_all(&i2400mu->rx_wq); + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} + + +int i2400mu_rx_setup(struct i2400mu *i2400mu) +{ + int result = 0; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = &i2400mu->usb_iface->dev; + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + + i2400mu->rx_kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx", + wimax_dev->name); + if (IS_ERR(i2400mu->rx_kthread)) { + result = PTR_ERR(i2400mu->rx_kthread); + dev_err(dev, "RX: cannot start thread: %d\n", result); + } + return result; +} + +void i2400mu_rx_release(struct i2400mu *i2400mu) +{ + kthread_stop(i2400mu->rx_kthread); +} + diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c new file mode 100644 index 000000000000..dfd893356f49 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb-tx.c @@ -0,0 +1,229 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * USB specific TX handling + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Intel Corporation <linux-wimax@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * - Initial implementation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * - Split transport/device specific + * + * + * Takes the TX messages in the i2400m's driver TX FIFO and sends them + * to the device until there are no more. + * + * If we fail sending the message, we just drop it. There isn't much + * we can do at this point. We could also retry, but the USB stack has + * already retried and still failed, so there is not much of a + * point. As well, most of the traffic is network, which has recovery + * methods for dropped packets. + * + * For sending we just obtain a FIFO buffer to send, send it to the + * USB bulk out, tell the TX FIFO code we have sent it; query for + * another one, etc... until done. + * + * We use a thread so we can call usb_autopm_enable() and + * usb_autopm_disable() for each transaction; this way when the device + * goes idle, it will suspend. It also has less overhead than a + * dedicated workqueue, as it is being used for a single task. + * + * ROADMAP + * + * i2400mu_tx_setup() + * i2400mu_tx_release() + * + * i2400mu_bus_tx_kick() - Called by the tx.c code when there + * is new data in the FIFO. + * i2400mu_txd() + * i2400m_tx_msg_get() + * i2400m_tx_msg_sent() + */ +#include "i2400m-usb.h" + + +#define D_SUBMODULE tx +#include "usb-debug-levels.h" + + +/* + * Get the next TX message in the TX FIFO and send it to the device + * + * Note that any iteration consumes a message to be sent, no matter if + * it succeeds or fails (we have no real way to retry or complain). + * + * Return: 0 if ok, < 0 errno code on hard error. + */ +static +int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg, + size_t tx_msg_size) +{ + int result = 0; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = &i2400mu->usb_iface->dev; + int usb_pipe, sent_size, do_autopm; + struct usb_endpoint_descriptor *epd; + + d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + do_autopm = atomic_read(&i2400mu->do_autopm); + result = do_autopm ? + usb_autopm_get_interface(i2400mu->usb_iface) : 0; + if (result < 0) { + dev_err(dev, "TX: can't get autopm: %d\n", result); + do_autopm = 0; + } + epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT); + usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); +retry: + result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe, + tx_msg, tx_msg_size, &sent_size, HZ); + usb_mark_last_busy(i2400mu->usb_dev); + switch (result) { + case 0: + if (sent_size != tx_msg_size) { /* Too short? drop it */ + dev_err(dev, "TX: short write (%d B vs %zu " + "expected)\n", sent_size, tx_msg_size); + result = -EIO; + } + break; + case -EINVAL: /* while removing driver */ + case -ENODEV: /* dev disconnect ... */ + case -ENOENT: /* just ignore it */ + case -ESHUTDOWN: /* and exit */ + case -ECONNRESET: + result = -ESHUTDOWN; + break; + default: /* Some error? */ + if (edc_inc(&i2400mu->urb_edc, + EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { + dev_err(dev, "TX: maximum errors in URB " + "exceeded; resetting device\n"); + usb_queue_reset_device(i2400mu->usb_iface); + } else { + dev_err(dev, "TX: cannot send URB; retrying. " + "tx_msg @%zu %zu B [%d sent]: %d\n", + (void *) tx_msg - i2400m->tx_buf, + tx_msg_size, sent_size, result); + goto retry; + } + } + if (do_autopm) + usb_autopm_put_interface(i2400mu->usb_iface); + d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu); + return result; +} + + +/* + * Get the next TX message in the TX FIFO and send it to the device + * + * Note we exit the loop if i2400mu_tx() fails; that funtion only + * fails on hard error (failing to tx a buffer not being one of them, + * see its doc). + * + * Return: 0 + */ +static +int i2400mu_txd(void *_i2400mu) +{ + int result = 0; + struct i2400mu *i2400mu = _i2400mu; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = &i2400mu->usb_iface->dev; + struct i2400m_msg_hdr *tx_msg; + size_t tx_msg_size; + + d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); + + while (1) { + d_printf(2, dev, "TX: waiting for messages\n"); + tx_msg = NULL; + wait_event_interruptible( + i2400mu->tx_wq, + (kthread_should_stop() /* check this first! */ + || (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size))) + ); + if (kthread_should_stop()) + break; + WARN_ON(tx_msg == NULL); /* should not happen...*/ + d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size); + d_dump(5, dev, tx_msg, tx_msg_size); + /* Yeah, we ignore errors ... not much we can do */ + i2400mu_tx(i2400mu, tx_msg, tx_msg_size); + i2400m_tx_msg_sent(i2400m); /* ack it, advance the FIFO */ + if (result < 0) + break; + } + d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); + return result; +} + + +/* + * i2400m TX engine notifies us that there is data in the FIFO ready + * for TX + * + * If there is a URB in flight, don't do anything; when it finishes, + * it will see there is data in the FIFO and send it. Else, just + * submit a write. + */ +void i2400mu_bus_tx_kick(struct i2400m *i2400m) +{ + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + struct device *dev = &i2400mu->usb_iface->dev; + + d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m); + wake_up_all(&i2400mu->tx_wq); + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} + + +int i2400mu_tx_setup(struct i2400mu *i2400mu) +{ + int result = 0; + struct i2400m *i2400m = &i2400mu->i2400m; + struct device *dev = &i2400mu->usb_iface->dev; + struct wimax_dev *wimax_dev = &i2400m->wimax_dev; + + i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", + wimax_dev->name); + if (IS_ERR(i2400mu->tx_kthread)) { + result = PTR_ERR(i2400mu->tx_kthread); + dev_err(dev, "TX: cannot start thread: %d\n", result); + } + return result; +} + +void i2400mu_tx_release(struct i2400mu *i2400mu) +{ + kthread_stop(i2400mu->tx_kthread); +} diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c new file mode 100644 index 000000000000..c6d93465c7e2 --- /dev/null +++ b/drivers/net/wimax/i2400m/usb.c @@ -0,0 +1,597 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Linux driver model glue for USB device, reset & fw upload + * + * + * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com> + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * Yanir Lubetkin <yanirx.lubetkin@intel.com> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * See i2400m-usb.h for a general description of this driver. + * + * This file implements driver model glue, and hook ups for the + * generic driver to implement the bus-specific functions (device + * communication setup/tear down, firmware upload and resetting). + * + * ROADMAP + * + * i2400mu_probe() + * alloc_netdev()... + * i2400mu_netdev_setup() + * i2400mu_init() + * i2400m_netdev_setup() + * i2400m_setup()... + * + * i2400mu_disconnect + * i2400m_release() + * free_netdev() + * + * i2400mu_suspend() + * i2400m_cmd_enter_powersave() + * i2400mu_notification_release() + * + * i2400mu_resume() + * i2400mu_notification_setup() + * + * i2400mu_bus_dev_start() Called by i2400m_dev_start() [who is + * i2400mu_tx_setup() called by i2400m_setup()] + * i2400mu_rx_setup() + * i2400mu_notification_setup() + * + * i2400mu_bus_dev_stop() Called by i2400m_dev_stop() [who is + * i2400mu_notification_release() called by i2400m_release()] + * i2400mu_rx_release() + * i2400mu_tx_release() + * + * i2400mu_bus_reset() Called by i2400m->bus_reset + * __i2400mu_reset() + * __i2400mu_send_barker() + * usb_reset_device() + */ +#include "i2400m-usb.h" +#include <linux/wimax/i2400m.h> +#include <linux/debugfs.h> + + +#define D_SUBMODULE usb +#include "usb-debug-levels.h" + + +/* Our firmware file name */ +#define I2400MU_FW_FILE_NAME "i2400m-fw-usb-" I2400M_FW_VERSION ".sbcf" + +static +int i2400mu_bus_dev_start(struct i2400m *i2400m) +{ + int result; + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + struct device *dev = &i2400mu->usb_iface->dev; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + result = i2400mu_tx_setup(i2400mu); + if (result < 0) + goto error_usb_tx_setup; + result = i2400mu_rx_setup(i2400mu); + if (result < 0) + goto error_usb_rx_setup; + result = i2400mu_notification_setup(i2400mu); + if (result < 0) + goto error_notif_setup; + d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); + return result; + +error_notif_setup: + i2400mu_rx_release(i2400mu); +error_usb_rx_setup: + i2400mu_tx_release(i2400mu); +error_usb_tx_setup: + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); + return result; +} + + +static +void i2400mu_bus_dev_stop(struct i2400m *i2400m) +{ + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + struct device *dev = &i2400mu->usb_iface->dev; + + d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + i2400mu_notification_release(i2400mu); + i2400mu_rx_release(i2400mu); + i2400mu_tx_release(i2400mu); + d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); +} + + +/* + * Sends a barker buffer to the device + * + * This helper will allocate a kmalloced buffer and use it to transmit + * (then free it). Reason for this is that other arches cannot use + * stack/vmalloc/text areas for DMA transfers. + * + * Error recovery here is simpler: anything is considered a hard error + * and will move the reset code to use a last-resort bus-based reset. + */ +static +int __i2400mu_send_barker(struct i2400mu *i2400mu, + const __le32 *barker, + size_t barker_size, + unsigned endpoint) +{ + struct usb_endpoint_descriptor *epd = NULL; + int pipe, actual_len, ret; + struct device *dev = &i2400mu->usb_iface->dev; + void *buffer; + int do_autopm = 1; + + ret = usb_autopm_get_interface(i2400mu->usb_iface); + if (ret < 0) { + dev_err(dev, "RESET: can't get autopm: %d\n", ret); + do_autopm = 0; + } + ret = -ENOMEM; + buffer = kmalloc(barker_size, GFP_KERNEL); + if (buffer == NULL) + goto error_kzalloc; + epd = usb_get_epd(i2400mu->usb_iface, endpoint); + pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress); + memcpy(buffer, barker, barker_size); + ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size, + &actual_len, HZ); + if (ret < 0) { + if (ret != -EINVAL) + dev_err(dev, "E: barker error: %d\n", ret); + } else if (actual_len != barker_size) { + dev_err(dev, "E: only %d bytes transmitted\n", actual_len); + ret = -EIO; + } + kfree(buffer); +error_kzalloc: + if (do_autopm) + usb_autopm_put_interface(i2400mu->usb_iface); + return ret; +} + + +/* + * Reset a device at different levels (warm, cold or bus) + * + * @i2400m: device descriptor + * @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS) + * + * Warm and cold resets get a USB reset if they fail. + * + * Warm reset: + * + * The device will be fully reset internally, but won't be + * disconnected from the USB bus (so no reenumeration will + * happen). Firmware upload will be neccessary. + * + * The device will send a reboot barker in the notification endpoint + * that will trigger the driver to reinitialize the state + * automatically from notif.c:i2400m_notification_grok() into + * i2400m_dev_bootstrap_delayed(). + * + * Cold and bus (USB) reset: + * + * The device will be fully reset internally, disconnected from the + * USB bus an a reenumeration will happen. Firmware upload will be + * neccessary. Thus, we don't do any locking or struct + * reinitialization, as we are going to be fully disconnected and + * reenumerated. + * + * Note we need to return -ENODEV if a warm reset was requested and we + * had to resort to a bus reset. See i2400m_op_reset(), wimax_reset() + * and wimax_dev->op_reset. + * + * WARNING: no driver state saved/fixed + */ +static +int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) +{ + int result; + struct i2400mu *i2400mu = + container_of(i2400m, struct i2400mu, i2400m); + struct device *dev = i2400m_dev(i2400m); + static const __le32 i2400m_WARM_BOOT_BARKER[4] = { + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + __constant_cpu_to_le32(I2400M_WARM_RESET_BARKER), + }; + static const __le32 i2400m_COLD_BOOT_BARKER[4] = { + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + __constant_cpu_to_le32(I2400M_COLD_RESET_BARKER), + }; + + d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt); + if (rt == I2400M_RT_WARM) + result = __i2400mu_send_barker(i2400mu, i2400m_WARM_BOOT_BARKER, + sizeof(i2400m_WARM_BOOT_BARKER), + I2400MU_EP_BULK_OUT); + else if (rt == I2400M_RT_COLD) + result = __i2400mu_send_barker(i2400mu, i2400m_COLD_BOOT_BARKER, + sizeof(i2400m_COLD_BOOT_BARKER), + I2400MU_EP_RESET_COLD); + else if (rt == I2400M_RT_BUS) { +do_bus_reset: + result = usb_reset_device(i2400mu->usb_dev); + switch (result) { + case 0: + case -EINVAL: /* device is gone */ + case -ENODEV: + case -ENOENT: + case -ESHUTDOWN: + result = rt == I2400M_RT_WARM ? -ENODEV : 0; + break; /* We assume the device is disconnected */ + default: + dev_err(dev, "USB reset failed (%d), giving up!\n", + result); + } + } else + BUG(); + if (result < 0 + && result != -EINVAL /* device is gone */ + && rt != I2400M_RT_BUS) { + dev_err(dev, "%s reset failed (%d); trying USB reset\n", + rt == I2400M_RT_WARM ? "warm" : "cold", result); + rt = I2400M_RT_BUS; + goto do_bus_reset; + } + d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); + return result; +} + + +static +void i2400mu_netdev_setup(struct net_device *net_dev) +{ + struct i2400m *i2400m = net_dev_to_i2400m(net_dev); + struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); + i2400mu_init(i2400mu); + i2400m_netdev_setup(net_dev); +} + + +/* + * Debug levels control; see debug.h + */ +struct d_level D_LEVEL[] = { + D_SUBMODULE_DEFINE(usb), + D_SUBMODULE_DEFINE(fw), + D_SUBMODULE_DEFINE(notif), + D_SUBMODULE_DEFINE(rx), + D_SUBMODULE_DEFINE(tx), +}; +size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); + + +#define __debugfs_register(prefix, name, parent) \ +do { \ + result = d_level_register_debugfs(prefix, name, parent); \ + if (result < 0) \ + goto error; \ +} while (0) + + +static +int i2400mu_debugfs_add(struct i2400mu *i2400mu) +{ + int result; + struct device *dev = &i2400mu->usb_iface->dev; + struct dentry *dentry = i2400mu->i2400m.wimax_dev.debugfs_dentry; + struct dentry *fd; + + dentry = debugfs_create_dir("i2400m-usb", dentry); + result = PTR_ERR(dentry); + if (IS_ERR(dentry)) { + if (result == -ENODEV) + result = 0; /* No debugfs support */ + goto error; + } + i2400mu->debugfs_dentry = dentry; + __debugfs_register("dl_", usb, dentry); + __debugfs_register("dl_", fw, dentry); + __debugfs_register("dl_", notif, dentry); + __debugfs_register("dl_", rx, dentry); + __debugfs_register("dl_", tx, dentry); + + /* Don't touch these if you don't know what you are doing */ + fd = debugfs_create_u8("rx_size_auto_shrink", 0600, dentry, + &i2400mu->rx_size_auto_shrink); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "rx_size_auto_shrink: %d\n", result); + goto error; + } + + fd = debugfs_create_size_t("rx_size", 0600, dentry, + &i2400mu->rx_size); + result = PTR_ERR(fd); + if (IS_ERR(fd) && result != -ENODEV) { + dev_err(dev, "Can't create debugfs entry " + "rx_size: %d\n", result); + goto error; + } + + return 0; + +error: + debugfs_remove_recursive(i2400mu->debugfs_dentry); + return result; +} + + +/* + * Probe a i2400m interface and register it + * + * @iface: USB interface to link to + * @id: USB class/subclass/protocol id + * @returns: 0 if ok, < 0 errno code on error. + * + * Alloc a net device, initialize the bus-specific details and then + * calls the bus-generic initialization routine. That will register + * the wimax and netdev devices, upload the firmware [using + * _bus_bm_*()], call _bus_dev_start() to finalize the setup of the + * communication with the device and then will start to talk to it to + * finnish setting it up. + */ +static +int i2400mu_probe(struct usb_interface *iface, + const struct usb_device_id *id) +{ + int result; + struct net_device *net_dev; + struct device *dev = &iface->dev; + struct i2400m *i2400m; + struct i2400mu *i2400mu; + struct usb_device *usb_dev = interface_to_usbdev(iface); + + if (usb_dev->speed != USB_SPEED_HIGH) + dev_err(dev, "device not connected as high speed\n"); + + /* Allocate instance [calls i2400m_netdev_setup() on it]. */ + result = -ENOMEM; + net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d", + i2400mu_netdev_setup); + if (net_dev == NULL) { + dev_err(dev, "no memory for network device instance\n"); + goto error_alloc_netdev; + } + SET_NETDEV_DEV(net_dev, dev); + i2400m = net_dev_to_i2400m(net_dev); + i2400mu = container_of(i2400m, struct i2400mu, i2400m); + i2400m->wimax_dev.net_dev = net_dev; + i2400mu->usb_dev = usb_get_dev(usb_dev); + i2400mu->usb_iface = iface; + usb_set_intfdata(iface, i2400mu); + + i2400m->bus_tx_block_size = I2400MU_BLK_SIZE; + i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX; + i2400m->bus_dev_start = i2400mu_bus_dev_start; + i2400m->bus_dev_stop = i2400mu_bus_dev_stop; + i2400m->bus_tx_kick = i2400mu_bus_tx_kick; + i2400m->bus_reset = i2400mu_bus_reset; + i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; + i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; + i2400m->bus_fw_name = I2400MU_FW_FILE_NAME; + i2400m->bus_bm_mac_addr_impaired = 0; + +#ifdef CONFIG_PM + iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */ + device_init_wakeup(dev, 1); + usb_autopm_enable(i2400mu->usb_iface); + usb_dev->autosuspend_delay = 15 * HZ; + usb_dev->autosuspend_disabled = 0; +#endif + + result = i2400m_setup(i2400m, I2400M_BRI_MAC_REINIT); + if (result < 0) { + dev_err(dev, "cannot setup device: %d\n", result); + goto error_setup; + } + result = i2400mu_debugfs_add(i2400mu); + if (result < 0) { + dev_err(dev, "Can't register i2400mu's debugfs: %d\n", result); + goto error_debugfs_add; + } + return 0; + +error_debugfs_add: + i2400m_release(i2400m); +error_setup: + usb_set_intfdata(iface, NULL); + usb_put_dev(i2400mu->usb_dev); + free_netdev(net_dev); +error_alloc_netdev: + return result; +} + + +/* + * Disconect a i2400m from the system. + * + * i2400m_stop() has been called before, so al the rx and tx contexts + * have been taken down already. Make sure the queue is stopped, + * unregister netdev and i2400m, free and kill. + */ +static +void i2400mu_disconnect(struct usb_interface *iface) +{ + struct i2400mu *i2400mu = usb_get_intfdata(iface); + struct i2400m *i2400m = &i2400mu->i2400m; + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + struct device *dev = &iface->dev; + + d_fnstart(3, dev, "(iface %p i2400m %p)\n", iface, i2400m); + + debugfs_remove_recursive(i2400mu->debugfs_dentry); + i2400m_release(i2400m); + usb_set_intfdata(iface, NULL); + usb_put_dev(i2400mu->usb_dev); + free_netdev(net_dev); + d_fnend(3, dev, "(iface %p i2400m %p) = void\n", iface, i2400m); +} + + +/* + * Get the device ready for USB port or system standby and hibernation + * + * USB port and system standby are handled the same. + * + * When the system hibernates, the USB device is powered down and then + * up, so we don't really have to do much here, as it will be seen as + * a reconnect. Still for simplicity we consider this case the same as + * suspend, so that the device has a chance to do notify the base + * station (if connected). + * + * So at the end, the three cases require common handling. + * + * If at the time of this call the device's firmware is not loaded, + * nothing has to be done. + * + * If the firmware is loaded, we need to: + * + * - tell the device to go into host interface power save mode, wait + * for it to ack + * + * This is quite more interesting than it is; we need to execute a + * command, but this time, we don't want the code in usb-{tx,rx}.c + * to call the usb_autopm_get/put_interface() barriers as it'd + * deadlock, so we need to decrement i2400mu->do_autopm, that acts + * as a poor man's semaphore. Ugly, but it works. + * + * As well, the device might refuse going to sleep for whichever + * reason. In this case we just fail. For system suspend/hibernate, + * we *can't* fail. We look at usb_dev->auto_pm to see if the + * suspend call comes from the USB stack or from the system and act + * in consequence. + * + * - stop the notification endpoint polling + */ +static +int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) +{ + int result = 0; + struct device *dev = &iface->dev; + struct i2400mu *i2400mu = usb_get_intfdata(iface); +#ifdef CONFIG_PM + struct usb_device *usb_dev = i2400mu->usb_dev; +#endif + struct i2400m *i2400m = &i2400mu->i2400m; + + d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); + if (i2400m->updown == 0) + goto no_firmware; + d_printf(1, dev, "fw up, requesting standby\n"); + atomic_dec(&i2400mu->do_autopm); + result = i2400m_cmd_enter_powersave(i2400m); + atomic_inc(&i2400mu->do_autopm); +#ifdef CONFIG_PM + if (result < 0 && usb_dev->auto_pm == 0) { + /* System suspend, can't fail */ + dev_err(dev, "failed to suspend, will reset on resume\n"); + result = 0; + } +#endif + if (result < 0) + goto error_enter_powersave; + i2400mu_notification_release(i2400mu); + d_printf(1, dev, "fw up, got standby\n"); +error_enter_powersave: +no_firmware: + d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", + iface, pm_msg.event, result); + return result; +} + + +static +int i2400mu_resume(struct usb_interface *iface) +{ + int ret = 0; + struct device *dev = &iface->dev; + struct i2400mu *i2400mu = usb_get_intfdata(iface); + struct i2400m *i2400m = &i2400mu->i2400m; + + d_fnstart(3, dev, "(iface %p)\n", iface); + if (i2400m->updown == 0) { + d_printf(1, dev, "fw was down, no resume neeed\n"); + goto out; + } + d_printf(1, dev, "fw was up, resuming\n"); + i2400mu_notification_setup(i2400mu); + /* USB has flow control, so we don't need to give it time to + * come back; otherwise, we'd use something like a get-state + * command... */ +out: + d_fnend(3, dev, "(iface %p) = %d\n", iface, ret); + return ret; +} + + +static +struct usb_device_id i2400mu_id_table[] = { + { USB_DEVICE(0x8086, 0x0181) }, + { USB_DEVICE(0x8086, 0x1403) }, + { USB_DEVICE(0x8086, 0x1405) }, + { USB_DEVICE(0x8086, 0x0180) }, + { USB_DEVICE(0x8086, 0x0182) }, + { USB_DEVICE(0x8086, 0x1406) }, + { USB_DEVICE(0x8086, 0x1403) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, i2400mu_id_table); + + +static +struct usb_driver i2400mu_driver = { + .name = KBUILD_MODNAME, + .suspend = i2400mu_suspend, + .resume = i2400mu_resume, + .probe = i2400mu_probe, + .disconnect = i2400mu_disconnect, + .id_table = i2400mu_id_table, + .supports_autosuspend = 1, +}; + +static +int __init i2400mu_driver_init(void) +{ + return usb_register(&i2400mu_driver); +} +module_init(i2400mu_driver_init); + + +static +void __exit i2400mu_driver_exit(void) +{ + flush_scheduled_work(); /* for the stuff we schedule from sysfs.c */ + usb_deregister(&i2400mu_driver); +} +module_exit(i2400mu_driver_exit); + +MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); +MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(I2400MU_FW_FILE_NAME); diff --git a/drivers/net/wireless/ath5k/dma.c b/drivers/net/wireless/ath5k/dma.c index 7e2b1a67e5da..b65b4feb2d28 100644 --- a/drivers/net/wireless/ath5k/dma.c +++ b/drivers/net/wireless/ath5k/dma.c @@ -594,7 +594,7 @@ int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) * XXX: BMISS interrupts may occur after association. * I found this on 5210 code but it needs testing. If this is * true we should disable them before assoc and re-enable them - * after a successfull assoc + some jiffies. + * after a successful assoc + some jiffies. interrupt_mask &= ~AR5K_INT_BMISS; */ } diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 9caa96a13586..a611ad857983 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -287,7 +287,7 @@ static void zd_op_stop(struct ieee80211_hw *hw) * @skb - a sk-buffer * @flags: extra flags to set in the TX status info * @ackssi: ACK signal strength - * @success - True for successfull transmission of the frame + * @success - True for successful transmission of the frame * * This information calls ieee80211_tx_status_irqsafe() if required by the * control information. It copies the control information into the status diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 761635be9104..cd6184ee08ee 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1105,6 +1105,16 @@ static void xennet_uninit(struct net_device *dev) gnttab_free_grant_references(np->gref_rx_head); } +static const struct net_device_ops xennet_netdev_ops = { + .ndo_open = xennet_open, + .ndo_uninit = xennet_uninit, + .ndo_stop = xennet_close, + .ndo_start_xmit = xennet_start_xmit, + .ndo_change_mtu = xennet_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + static struct net_device * __devinit xennet_create_dev(struct xenbus_device *dev) { int i, err; @@ -1161,12 +1171,9 @@ static struct net_device * __devinit xennet_create_dev(struct xenbus_device *dev goto exit_free_tx; } - netdev->open = xennet_open; - netdev->hard_start_xmit = xennet_start_xmit; - netdev->stop = xennet_close; + netdev->netdev_ops = &xennet_netdev_ops; + netif_napi_add(netdev, &np->napi, xennet_poll, 64); - netdev->uninit = xennet_uninit; - netdev->change_mtu = xennet_change_mtu; netdev->features = NETIF_F_IP_CSUM; SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops); diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 65e8294a9e29..9da5a4b81133 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -1,11 +1,12 @@ /** * @file buffer_sync.c * - * @remark Copyright 2002 OProfile authors + * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> * @author Barry Kasindorf + * @author Robert Richter <robert.richter@amd.com> * * This is the core of the buffer management. Each * CPU buffer is processed and entered into the @@ -315,88 +316,73 @@ static void add_trace_begin(void) add_event_entry(TRACE_BEGIN_CODE); } -#ifdef CONFIG_OPROFILE_IBS - -#define IBS_FETCH_CODE_SIZE 2 -#define IBS_OP_CODE_SIZE 5 - -/* - * Add IBS fetch and op entries to event buffer - */ -static void add_ibs_begin(int cpu, int code, struct mm_struct *mm) +static void add_data(struct op_entry *entry, struct mm_struct *mm) { - unsigned long rip; - int i, count; - unsigned long ibs_cookie = 0; + unsigned long code, pc, val; + unsigned long cookie; off_t offset; - struct op_sample *sample; - - sample = cpu_buffer_read_entry(cpu); - if (!sample) - goto Error; - rip = sample->eip; -#ifdef __LP64__ - rip += sample->event << 32; -#endif + if (!op_cpu_buffer_get_data(entry, &code)) + return; + if (!op_cpu_buffer_get_data(entry, &pc)) + return; + if (!op_cpu_buffer_get_size(entry)) + return; if (mm) { - ibs_cookie = lookup_dcookie(mm, rip, &offset); + cookie = lookup_dcookie(mm, pc, &offset); - if (ibs_cookie == NO_COOKIE) - offset = rip; - if (ibs_cookie == INVALID_COOKIE) { + if (cookie == NO_COOKIE) + offset = pc; + if (cookie == INVALID_COOKIE) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); - offset = rip; + offset = pc; } - if (ibs_cookie != last_cookie) { - add_cookie_switch(ibs_cookie); - last_cookie = ibs_cookie; + if (cookie != last_cookie) { + add_cookie_switch(cookie); + last_cookie = cookie; } } else - offset = rip; + offset = pc; add_event_entry(ESCAPE_CODE); add_event_entry(code); add_event_entry(offset); /* Offset from Dcookie */ - /* we send the Dcookie offset, but send the raw Linear Add also*/ - add_event_entry(sample->eip); - add_event_entry(sample->event); - - if (code == IBS_FETCH_CODE) - count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ - else - count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ - - for (i = 0; i < count; i++) { - sample = cpu_buffer_read_entry(cpu); - if (!sample) - goto Error; - add_event_entry(sample->eip); - add_event_entry(sample->event); - } - - return; - -Error: - return; + while (op_cpu_buffer_get_data(entry, &val)) + add_event_entry(val); } -#endif - -static void add_sample_entry(unsigned long offset, unsigned long event) +static inline void add_sample_entry(unsigned long offset, unsigned long event) { add_event_entry(offset); add_event_entry(event); } -static int add_us_sample(struct mm_struct *mm, struct op_sample *s) +/* + * Add a sample to the global event buffer. If possible the + * sample is converted into a persistent dentry/offset pair + * for later lookup from userspace. Return 0 on failure. + */ +static int +add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) { unsigned long cookie; off_t offset; + if (in_kernel) { + add_sample_entry(s->eip, s->event); + return 1; + } + + /* add userspace sample */ + + if (!mm) { + atomic_inc(&oprofile_stats.sample_lost_no_mm); + return 0; + } + cookie = lookup_dcookie(mm, s->eip, &offset); if (cookie == INVALID_COOKIE) { @@ -415,25 +401,6 @@ static int add_us_sample(struct mm_struct *mm, struct op_sample *s) } -/* Add a sample to the global event buffer. If possible the - * sample is converted into a persistent dentry/offset pair - * for later lookup from userspace. - */ -static int -add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) -{ - if (in_kernel) { - add_sample_entry(s->eip, s->event); - return 1; - } else if (mm) { - return add_us_sample(mm, s); - } else { - atomic_inc(&oprofile_stats.sample_lost_no_mm); - } - return 0; -} - - static void release_mm(struct mm_struct *mm) { if (!mm) @@ -526,66 +493,69 @@ void sync_buffer(int cpu) { struct mm_struct *mm = NULL; struct mm_struct *oldmm; + unsigned long val; struct task_struct *new; unsigned long cookie = 0; int in_kernel = 1; sync_buffer_state state = sb_buffer_start; unsigned int i; unsigned long available; + unsigned long flags; + struct op_entry entry; + struct op_sample *sample; mutex_lock(&buffer_mutex); add_cpu_switch(cpu); - cpu_buffer_reset(cpu); - available = cpu_buffer_entries(cpu); + op_cpu_buffer_reset(cpu); + available = op_cpu_buffer_entries(cpu); for (i = 0; i < available; ++i) { - struct op_sample *s = cpu_buffer_read_entry(cpu); - if (!s) + sample = op_cpu_buffer_read_entry(&entry, cpu); + if (!sample) break; - if (is_code(s->eip)) { - switch (s->event) { - case 0: - case CPU_IS_KERNEL: + if (is_code(sample->eip)) { + flags = sample->event; + if (flags & TRACE_BEGIN) { + state = sb_bt_start; + add_trace_begin(); + } + if (flags & KERNEL_CTX_SWITCH) { /* kernel/userspace switch */ - in_kernel = s->event; + in_kernel = flags & IS_KERNEL; if (state == sb_buffer_start) state = sb_sample_start; - add_kernel_ctx_switch(s->event); - break; - case CPU_TRACE_BEGIN: - state = sb_bt_start; - add_trace_begin(); - break; -#ifdef CONFIG_OPROFILE_IBS - case IBS_FETCH_BEGIN: - state = sb_bt_start; - add_ibs_begin(cpu, IBS_FETCH_CODE, mm); - break; - case IBS_OP_BEGIN: - state = sb_bt_start; - add_ibs_begin(cpu, IBS_OP_CODE, mm); - break; -#endif - default: + add_kernel_ctx_switch(flags & IS_KERNEL); + } + if (flags & USER_CTX_SWITCH + && op_cpu_buffer_get_data(&entry, &val)) { /* userspace context switch */ + new = (struct task_struct *)val; oldmm = mm; - new = (struct task_struct *)s->event; release_mm(oldmm); mm = take_tasks_mm(new); if (mm != oldmm) cookie = get_exec_dcookie(mm); add_user_ctx_switch(new, cookie); - break; - } - } else if (state >= sb_bt_start && - !add_sample(mm, s, in_kernel)) { - if (state == sb_bt_start) { - state = sb_bt_ignore; - atomic_inc(&oprofile_stats.bt_lost_no_mapping); } + if (op_cpu_buffer_get_size(&entry)) + add_data(&entry, mm); + continue; + } + + if (state < sb_bt_start) + /* ignore sample */ + continue; + + if (add_sample(mm, sample, in_kernel)) + continue; + + /* ignore backtraces if failed to add a sample */ + if (state == sb_bt_start) { + state = sb_bt_ignore; + atomic_inc(&oprofile_stats.bt_lost_no_mapping); } } release_mm(mm); diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 61090969158f..2e03b6d796d3 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -1,11 +1,12 @@ /** * @file cpu_buffer.c * - * @remark Copyright 2002 OProfile authors + * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> * @author Barry Kasindorf <barry.kasindorf@amd.com> + * @author Robert Richter <robert.richter@amd.com> * * Each CPU has a local buffer that stores PC value/event * pairs. We also log context switches when we notice them. @@ -45,8 +46,8 @@ * can be changed to a single buffer solution when the ring buffer * access is implemented as non-locking atomic code. */ -struct ring_buffer *op_ring_buffer_read; -struct ring_buffer *op_ring_buffer_write; +static struct ring_buffer *op_ring_buffer_read; +static struct ring_buffer *op_ring_buffer_write; DEFINE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer); static void wq_sync_buffer(struct work_struct *work); @@ -54,19 +55,9 @@ static void wq_sync_buffer(struct work_struct *work); #define DEFAULT_TIMER_EXPIRE (HZ / 10) static int work_enabled; -void free_cpu_buffers(void) -{ - if (op_ring_buffer_read) - ring_buffer_free(op_ring_buffer_read); - op_ring_buffer_read = NULL; - if (op_ring_buffer_write) - ring_buffer_free(op_ring_buffer_write); - op_ring_buffer_write = NULL; -} - unsigned long oprofile_get_cpu_buffer_size(void) { - return fs_cpu_buffer_size; + return oprofile_cpu_buffer_size; } void oprofile_cpu_buffer_inc_smpl_lost(void) @@ -77,11 +68,21 @@ void oprofile_cpu_buffer_inc_smpl_lost(void) cpu_buf->sample_lost_overflow++; } +void free_cpu_buffers(void) +{ + if (op_ring_buffer_read) + ring_buffer_free(op_ring_buffer_read); + op_ring_buffer_read = NULL; + if (op_ring_buffer_write) + ring_buffer_free(op_ring_buffer_write); + op_ring_buffer_write = NULL; +} + int alloc_cpu_buffers(void) { int i; - unsigned long buffer_size = fs_cpu_buffer_size; + unsigned long buffer_size = oprofile_cpu_buffer_size; op_ring_buffer_read = ring_buffer_alloc(buffer_size, OP_BUFFER_FLAGS); if (!op_ring_buffer_read) @@ -97,8 +98,6 @@ int alloc_cpu_buffers(void) b->last_is_kernel = -1; b->tracing = 0; b->buffer_size = buffer_size; - b->tail_pos = 0; - b->head_pos = 0; b->sample_received = 0; b->sample_lost_overflow = 0; b->backtrace_aborted = 0; @@ -145,47 +144,156 @@ void end_cpu_work(void) flush_scheduled_work(); } -static inline int -add_sample(struct oprofile_cpu_buffer *cpu_buf, - unsigned long pc, unsigned long event) +/* + * This function prepares the cpu buffer to write a sample. + * + * Struct op_entry is used during operations on the ring buffer while + * struct op_sample contains the data that is stored in the ring + * buffer. Struct entry can be uninitialized. The function reserves a + * data array that is specified by size. Use + * op_cpu_buffer_write_commit() after preparing the sample. In case of + * errors a null pointer is returned, otherwise the pointer to the + * sample. + * + */ +struct op_sample +*op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size) +{ + entry->event = ring_buffer_lock_reserve + (op_ring_buffer_write, sizeof(struct op_sample) + + size * sizeof(entry->sample->data[0]), &entry->irq_flags); + if (entry->event) + entry->sample = ring_buffer_event_data(entry->event); + else + entry->sample = NULL; + + if (!entry->sample) + return NULL; + + entry->size = size; + entry->data = entry->sample->data; + + return entry->sample; +} + +int op_cpu_buffer_write_commit(struct op_entry *entry) +{ + return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event, + entry->irq_flags); +} + +struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu) +{ + struct ring_buffer_event *e; + e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL); + if (e) + goto event; + if (ring_buffer_swap_cpu(op_ring_buffer_read, + op_ring_buffer_write, + cpu)) + return NULL; + e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL); + if (e) + goto event; + return NULL; + +event: + entry->event = e; + entry->sample = ring_buffer_event_data(e); + entry->size = (ring_buffer_event_length(e) - sizeof(struct op_sample)) + / sizeof(entry->sample->data[0]); + entry->data = entry->sample->data; + return entry->sample; +} + +unsigned long op_cpu_buffer_entries(int cpu) +{ + return ring_buffer_entries_cpu(op_ring_buffer_read, cpu) + + ring_buffer_entries_cpu(op_ring_buffer_write, cpu); +} + +static int +op_add_code(struct oprofile_cpu_buffer *cpu_buf, unsigned long backtrace, + int is_kernel, struct task_struct *task) { struct op_entry entry; - int ret; + struct op_sample *sample; + unsigned long flags; + int size; + + flags = 0; + + if (backtrace) + flags |= TRACE_BEGIN; + + /* notice a switch from user->kernel or vice versa */ + is_kernel = !!is_kernel; + if (cpu_buf->last_is_kernel != is_kernel) { + cpu_buf->last_is_kernel = is_kernel; + flags |= KERNEL_CTX_SWITCH; + if (is_kernel) + flags |= IS_KERNEL; + } + + /* notice a task switch */ + if (cpu_buf->last_task != task) { + cpu_buf->last_task = task; + flags |= USER_CTX_SWITCH; + } + + if (!flags) + /* nothing to do */ + return 0; + + if (flags & USER_CTX_SWITCH) + size = 1; + else + size = 0; + + sample = op_cpu_buffer_write_reserve(&entry, size); + if (!sample) + return -ENOMEM; - ret = cpu_buffer_write_entry(&entry); - if (ret) - return ret; + sample->eip = ESCAPE_CODE; + sample->event = flags; - entry.sample->eip = pc; - entry.sample->event = event; + if (size) + op_cpu_buffer_add_data(&entry, (unsigned long)task); - ret = cpu_buffer_write_commit(&entry); - if (ret) - return ret; + op_cpu_buffer_write_commit(&entry); return 0; } static inline int -add_code(struct oprofile_cpu_buffer *buffer, unsigned long value) +op_add_sample(struct oprofile_cpu_buffer *cpu_buf, + unsigned long pc, unsigned long event) { - return add_sample(buffer, ESCAPE_CODE, value); + struct op_entry entry; + struct op_sample *sample; + + sample = op_cpu_buffer_write_reserve(&entry, 0); + if (!sample) + return -ENOMEM; + + sample->eip = pc; + sample->event = event; + + return op_cpu_buffer_write_commit(&entry); } -/* This must be safe from any context. It's safe writing here - * because of the head/tail separation of the writer and reader - * of the CPU buffer. +/* + * This must be safe from any context. * * is_kernel is needed because on some architectures you cannot * tell if you are in kernel or user space simply by looking at * pc. We tag this in the buffer by generating kernel enter/exit * events whenever is_kernel changes */ -static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, - int is_kernel, unsigned long event) +static int +log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, + unsigned long backtrace, int is_kernel, unsigned long event) { - struct task_struct *task; - cpu_buf->sample_received++; if (pc == ESCAPE_CODE) { @@ -193,25 +301,10 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, return 0; } - is_kernel = !!is_kernel; - - task = current; - - /* notice a switch from user->kernel or vice versa */ - if (cpu_buf->last_is_kernel != is_kernel) { - cpu_buf->last_is_kernel = is_kernel; - if (add_code(cpu_buf, is_kernel)) - goto fail; - } - - /* notice a task switch */ - if (cpu_buf->last_task != task) { - cpu_buf->last_task = task; - if (add_code(cpu_buf, (unsigned long)task)) - goto fail; - } + if (op_add_code(cpu_buf, backtrace, is_kernel, current)) + goto fail; - if (add_sample(cpu_buf, pc, event)) + if (op_add_sample(cpu_buf, pc, event)) goto fail; return 1; @@ -221,109 +314,102 @@ fail: return 0; } -static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) +static inline void oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) { - add_code(cpu_buf, CPU_TRACE_BEGIN); cpu_buf->tracing = 1; - return 1; } -static void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf) +static inline void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf) { cpu_buf->tracing = 0; } -void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, - unsigned long event, int is_kernel) +static inline void +__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); - - if (!backtrace_depth) { - log_sample(cpu_buf, pc, is_kernel, event); - return; - } - - if (!oprofile_begin_trace(cpu_buf)) - return; + unsigned long backtrace = oprofile_backtrace_depth; /* * if log_sample() fail we can't backtrace since we lost the * source of this event */ - if (log_sample(cpu_buf, pc, is_kernel, event)) - oprofile_ops.backtrace(regs, backtrace_depth); + if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event)) + /* failed */ + return; + + if (!backtrace) + return; + + oprofile_begin_trace(cpu_buf); + oprofile_ops.backtrace(regs, backtrace); oprofile_end_trace(cpu_buf); } +void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, + unsigned long event, int is_kernel) +{ + __oprofile_add_ext_sample(pc, regs, event, is_kernel); +} + void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) { int is_kernel = !user_mode(regs); unsigned long pc = profile_pc(regs); - oprofile_add_ext_sample(pc, regs, event, is_kernel); + __oprofile_add_ext_sample(pc, regs, event, is_kernel); } -#ifdef CONFIG_OPROFILE_IBS - -#define MAX_IBS_SAMPLE_SIZE 14 - -void oprofile_add_ibs_sample(struct pt_regs * const regs, - unsigned int * const ibs_sample, int ibs_code) +/* + * Add samples with data to the ring buffer. + * + * Use oprofile_add_data(&entry, val) to add data and + * oprofile_write_commit(&entry) to commit the sample. + */ +void +oprofile_write_reserve(struct op_entry *entry, struct pt_regs * const regs, + unsigned long pc, int code, int size) { + struct op_sample *sample; int is_kernel = !user_mode(regs); struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); - struct task_struct *task; - int fail = 0; cpu_buf->sample_received++; - /* notice a switch from user->kernel or vice versa */ - if (cpu_buf->last_is_kernel != is_kernel) { - if (add_code(cpu_buf, is_kernel)) - goto fail; - cpu_buf->last_is_kernel = is_kernel; - } - - /* notice a task switch */ - if (!is_kernel) { - task = current; - if (cpu_buf->last_task != task) { - if (add_code(cpu_buf, (unsigned long)task)) - goto fail; - cpu_buf->last_task = task; - } - } - - fail = fail || add_code(cpu_buf, ibs_code); - fail = fail || add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]); - fail = fail || add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]); - fail = fail || add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]); - - if (ibs_code == IBS_OP_BEGIN) { - fail = fail || add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]); - fail = fail || add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]); - fail = fail || add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]); - } + /* no backtraces for samples with data */ + if (op_add_code(cpu_buf, 0, is_kernel, current)) + goto fail; - if (fail) + sample = op_cpu_buffer_write_reserve(entry, size + 2); + if (!sample) goto fail; + sample->eip = ESCAPE_CODE; + sample->event = 0; /* no flags */ - if (backtrace_depth) - oprofile_ops.backtrace(regs, backtrace_depth); + op_cpu_buffer_add_data(entry, code); + op_cpu_buffer_add_data(entry, pc); return; fail: cpu_buf->sample_lost_overflow++; - return; } -#endif +int oprofile_add_data(struct op_entry *entry, unsigned long val) +{ + return op_cpu_buffer_add_data(entry, val); +} + +int oprofile_write_commit(struct op_entry *entry) +{ + return op_cpu_buffer_write_commit(entry); +} void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); - log_sample(cpu_buf, pc, is_kernel, event); + log_sample(cpu_buf, pc, 0, is_kernel, event); } void oprofile_add_trace(unsigned long pc) @@ -340,7 +426,7 @@ void oprofile_add_trace(unsigned long pc) if (pc == ESCAPE_CODE) goto fail; - if (add_sample(cpu_buf, pc, 0)) + if (op_add_sample(cpu_buf, pc, 0)) goto fail; return; diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index aacb0f0bc566..63f81c44846a 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h @@ -1,10 +1,11 @@ /** * @file cpu_buffer.h * - * @remark Copyright 2002 OProfile authors + * @remark Copyright 2002-2009 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> + * @author Robert Richter <robert.richter@amd.com> */ #ifndef OPROFILE_CPU_BUFFER_H @@ -31,17 +32,12 @@ void end_cpu_work(void); struct op_sample { unsigned long eip; unsigned long event; + unsigned long data[0]; }; -struct op_entry { - struct ring_buffer_event *event; - struct op_sample *sample; - unsigned long irq_flags; -}; +struct op_entry; struct oprofile_cpu_buffer { - volatile unsigned long head_pos; - volatile unsigned long tail_pos; unsigned long buffer_size; struct task_struct *last_task; int last_is_kernel; @@ -54,8 +50,6 @@ struct oprofile_cpu_buffer { struct delayed_work work; }; -extern struct ring_buffer *op_ring_buffer_read; -extern struct ring_buffer *op_ring_buffer_write; DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer); /* @@ -64,7 +58,7 @@ DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer); * reset these to invalid values; the next sample collected will * populate the buffer with proper values to initialize the buffer */ -static inline void cpu_buffer_reset(int cpu) +static inline void op_cpu_buffer_reset(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); @@ -72,55 +66,48 @@ static inline void cpu_buffer_reset(int cpu) cpu_buf->last_task = NULL; } -static inline int cpu_buffer_write_entry(struct op_entry *entry) -{ - entry->event = ring_buffer_lock_reserve(op_ring_buffer_write, - sizeof(struct op_sample), - &entry->irq_flags); - if (entry->event) - entry->sample = ring_buffer_event_data(entry->event); - else - entry->sample = NULL; - - if (!entry->sample) - return -ENOMEM; - - return 0; -} +struct op_sample +*op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size); +int op_cpu_buffer_write_commit(struct op_entry *entry); +struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu); +unsigned long op_cpu_buffer_entries(int cpu); -static inline int cpu_buffer_write_commit(struct op_entry *entry) +/* returns the remaining free size of data in the entry */ +static inline +int op_cpu_buffer_add_data(struct op_entry *entry, unsigned long val) { - return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event, - entry->irq_flags); + if (!entry->size) + return 0; + *entry->data = val; + entry->size--; + entry->data++; + return entry->size; } -static inline struct op_sample *cpu_buffer_read_entry(int cpu) +/* returns the size of data in the entry */ +static inline +int op_cpu_buffer_get_size(struct op_entry *entry) { - struct ring_buffer_event *e; - e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL); - if (e) - return ring_buffer_event_data(e); - if (ring_buffer_swap_cpu(op_ring_buffer_read, - op_ring_buffer_write, - cpu)) - return NULL; - e = ring_buffer_consume(op_ring_buffer_read, cpu, NULL); - if (e) - return ring_buffer_event_data(e); - return NULL; + return entry->size; } -/* "acquire" as many cpu buffer slots as we can */ -static inline unsigned long cpu_buffer_entries(int cpu) +/* returns 0 if empty or the size of data including the current value */ +static inline +int op_cpu_buffer_get_data(struct op_entry *entry, unsigned long *val) { - return ring_buffer_entries_cpu(op_ring_buffer_read, cpu) - + ring_buffer_entries_cpu(op_ring_buffer_write, cpu); + int size = entry->size; + if (!size) + return 0; + *val = *entry->data; + entry->size--; + entry->data++; + return size; } -/* transient events for the CPU buffer -> event buffer */ -#define CPU_IS_KERNEL 1 -#define CPU_TRACE_BEGIN 2 -#define IBS_FETCH_BEGIN 3 -#define IBS_OP_BEGIN 4 +/* extra data flags */ +#define KERNEL_CTX_SWITCH (1UL << 0) +#define IS_KERNEL (1UL << 1) +#define TRACE_BEGIN (1UL << 2) +#define USER_CTX_SWITCH (1UL << 3) #endif /* OPROFILE_CPU_BUFFER_H */ diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c index 191a3202cecc..2b7ae366ceb1 100644 --- a/drivers/oprofile/event_buffer.c +++ b/drivers/oprofile/event_buffer.c @@ -73,8 +73,8 @@ int alloc_event_buffer(void) unsigned long flags; spin_lock_irqsave(&oprofilefs_lock, flags); - buffer_size = fs_buffer_size; - buffer_watershed = fs_buffer_watershed; + buffer_size = oprofile_buffer_size; + buffer_watershed = oprofile_buffer_watershed; spin_unlock_irqrestore(&oprofilefs_lock, flags); if (buffer_watershed >= buffer_size) diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index cd375907f26f..3cffce90f82a 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -23,7 +23,7 @@ struct oprofile_operations oprofile_ops; unsigned long oprofile_started; -unsigned long backtrace_depth; +unsigned long oprofile_backtrace_depth; static unsigned long is_setup; static DEFINE_MUTEX(start_mutex); @@ -172,7 +172,7 @@ int oprofile_set_backtrace(unsigned long val) goto out; } - backtrace_depth = val; + oprofile_backtrace_depth = val; out: mutex_unlock(&start_mutex); diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h index 5df0c21a608f..c288d3c24b50 100644 --- a/drivers/oprofile/oprof.h +++ b/drivers/oprofile/oprof.h @@ -21,12 +21,12 @@ void oprofile_stop(void); struct oprofile_operations; -extern unsigned long fs_buffer_size; -extern unsigned long fs_cpu_buffer_size; -extern unsigned long fs_buffer_watershed; +extern unsigned long oprofile_buffer_size; +extern unsigned long oprofile_cpu_buffer_size; +extern unsigned long oprofile_buffer_watershed; extern struct oprofile_operations oprofile_ops; extern unsigned long oprofile_started; -extern unsigned long backtrace_depth; +extern unsigned long oprofile_backtrace_depth; struct super_block; struct dentry; diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c index d8201998b0b7..5d36ffc30dd5 100644 --- a/drivers/oprofile/oprofile_files.c +++ b/drivers/oprofile/oprofile_files.c @@ -14,17 +14,18 @@ #include "oprofile_stats.h" #include "oprof.h" -#define FS_BUFFER_SIZE_DEFAULT 131072 -#define FS_CPU_BUFFER_SIZE_DEFAULT 8192 -#define FS_BUFFER_WATERSHED_DEFAULT 32768 /* FIXME: tune */ +#define BUFFER_SIZE_DEFAULT 131072 +#define CPU_BUFFER_SIZE_DEFAULT 8192 +#define BUFFER_WATERSHED_DEFAULT 32768 /* FIXME: tune */ -unsigned long fs_buffer_size; -unsigned long fs_cpu_buffer_size; -unsigned long fs_buffer_watershed; +unsigned long oprofile_buffer_size; +unsigned long oprofile_cpu_buffer_size; +unsigned long oprofile_buffer_watershed; static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { - return oprofilefs_ulong_to_user(backtrace_depth, buf, count, offset); + return oprofilefs_ulong_to_user(oprofile_backtrace_depth, buf, count, + offset); } @@ -125,16 +126,16 @@ static const struct file_operations dump_fops = { void oprofile_create_files(struct super_block *sb, struct dentry *root) { /* reinitialize default values */ - fs_buffer_size = FS_BUFFER_SIZE_DEFAULT; - fs_cpu_buffer_size = FS_CPU_BUFFER_SIZE_DEFAULT; - fs_buffer_watershed = FS_BUFFER_WATERSHED_DEFAULT; + oprofile_buffer_size = BUFFER_SIZE_DEFAULT; + oprofile_cpu_buffer_size = CPU_BUFFER_SIZE_DEFAULT; + oprofile_buffer_watershed = BUFFER_WATERSHED_DEFAULT; oprofilefs_create_file(sb, root, "enable", &enable_fops); oprofilefs_create_file_perm(sb, root, "dump", &dump_fops, 0666); oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops); - oprofilefs_create_ulong(sb, root, "buffer_size", &fs_buffer_size); - oprofilefs_create_ulong(sb, root, "buffer_watershed", &fs_buffer_watershed); - oprofilefs_create_ulong(sb, root, "cpu_buffer_size", &fs_cpu_buffer_size); + oprofilefs_create_ulong(sb, root, "buffer_size", &oprofile_buffer_size); + oprofilefs_create_ulong(sb, root, "buffer_watershed", &oprofile_buffer_watershed); + oprofilefs_create_ulong(sb, root, "cpu_buffer_size", &oprofile_cpu_buffer_size); oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops); oprofilefs_create_file(sb, root, "backtrace_depth", &depth_fops); oprofilefs_create_file(sb, root, "pointer_size", &pointer_size_fops); diff --git a/drivers/parisc/asp.c b/drivers/parisc/asp.c index 821369135369..7931133526c4 100644 --- a/drivers/parisc/asp.c +++ b/drivers/parisc/asp.c @@ -71,8 +71,7 @@ static void asp_choose_irq(struct parisc_device *dev, void *ctrl) */ #define ASP_INTERRUPT_ADDR 0xf0800000 -int __init -asp_init_chip(struct parisc_device *dev) +static int __init asp_init_chip(struct parisc_device *dev) { struct gsc_irq gsc_irq; int ret; diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index dcc1e9958d2f..cd4dd7ed2c06 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -555,7 +555,7 @@ static u32 hint_lookup[] = { * (Load Coherence Index) instruction. The 8 bits used for the virtual * index are bits 12:19 of the value returned by LCI. */ -void CCIO_INLINE +static void CCIO_INLINE ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, unsigned long hints) { @@ -1578,8 +1578,6 @@ static int __init ccio_probe(struct parisc_device *dev) ioc_count++; - parisc_vmerge_boundary = IOVP_SIZE; - parisc_vmerge_max_size = BITS_PER_LONG * IOVP_SIZE; parisc_has_iommu(); return 0; } diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 3bc54b30c3a1..d539d9df88e7 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -287,7 +287,7 @@ DINO_PORT_OUT(b, 8, 3) DINO_PORT_OUT(w, 16, 2) DINO_PORT_OUT(l, 32, 0) -struct pci_port_ops dino_port_ops = { +static struct pci_port_ops dino_port_ops = { .inb = dino_in8, .inw = dino_in16, .inl = dino_in32, @@ -547,7 +547,7 @@ dino_card_fixup(struct pci_dev *dev) ** The additional "-1" adjusts for skewing the IRQ<->slot. */ dino_cfg_read(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 1, &irq_pin); - dev->irq = (irq_pin + PCI_SLOT(dev->devfn) - 1) % 4 ; + dev->irq = pci_swizzle_interrupt_pin(dev, irq_pin) - 1; /* Shouldn't really need to do this but it's in case someone tries ** to bypass PCI services and look at the card themselves. @@ -672,7 +672,7 @@ dino_fixup_bus(struct pci_bus *bus) dino_cfg_read(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 1, &irq_pin); - irq_pin = (irq_pin + PCI_SLOT(dev->devfn) - 1) % 4 ; + irq_pin = pci_swizzle_interrupt_pin(dev, irq_pin) - 1; printk(KERN_WARNING "Device %s has undefined IRQ, " "setting to %d\n", pci_name(dev), irq_pin); dino_cfg_write(dev->bus, dev->devfn, @@ -690,7 +690,7 @@ dino_fixup_bus(struct pci_bus *bus) } -struct pci_bios_ops dino_bios_ops = { +static struct pci_bios_ops dino_bios_ops = { .init = dino_bios_init, .fixup_bus = dino_fixup_bus }; diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c index 65eee67aa2ae..13856415b432 100644 --- a/drivers/parisc/hppb.c +++ b/drivers/parisc/hppb.c @@ -29,7 +29,7 @@ struct hppb_card { struct hppb_card *next; }; -struct hppb_card hppb_card_head = { +static struct hppb_card hppb_card_head = { .hpa = 0, .next = NULL, }; diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 9dedbbd218c3..0797659ee016 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -519,8 +519,7 @@ iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) ** ** Advantage is it's really easy to implement. */ - intr_pin = ((intr_pin-1)+PCI_SLOT(pcidev->devfn)) % 4; - intr_pin++; /* convert back to INTA-D (1-4) */ + intr_pin = pci_swizzle_interrupt_pin(pcidev, intr_pin); #endif /* PCI_BRIDGE_FUNCS */ /* diff --git a/drivers/parisc/lasi.c b/drivers/parisc/lasi.c index bee510098ce8..e65727ca9fc0 100644 --- a/drivers/parisc/lasi.c +++ b/drivers/parisc/lasi.c @@ -107,7 +107,7 @@ lasi_init_irq(struct gsc_asic *this_lasi) #else -void __init lasi_led_init(unsigned long lasi_hpa) +static void __init lasi_led_init(unsigned long lasi_hpa) { unsigned long datareg; @@ -163,8 +163,7 @@ static void lasi_power_off(void) gsc_writel(0x02, datareg); } -int __init -lasi_init_chip(struct parisc_device *dev) +static int __init lasi_init_chip(struct parisc_device *dev) { extern void (*chassis_power_off)(void); struct gsc_asic *lasi; diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index a28c8946deaa..d8233de8c75d 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -824,7 +824,7 @@ lba_fixup_bus(struct pci_bus *bus) } -struct pci_bios_ops lba_bios_ops = { +static struct pci_bios_ops lba_bios_ops = { .init = lba_bios_init, .fixup_bus = lba_fixup_bus, }; diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index bc73b96346ff..3fac8f81d59d 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -561,7 +561,7 @@ typedef unsigned long space_t; * IOMMU uses little endian for the pdir. */ -void SBA_INLINE +static void SBA_INLINE sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, unsigned long hint) { @@ -1874,7 +1874,7 @@ static struct parisc_device_id sba_tbl[] = { { 0, } }; -int sba_driver_callback(struct parisc_device *); +static int sba_driver_callback(struct parisc_device *); static struct parisc_driver sba_driver = { .name = MODULE_NAME, @@ -1887,8 +1887,7 @@ static struct parisc_driver sba_driver = { ** If so, initialize the chip and tell other partners in crime they ** have work to do. */ -int -sba_driver_callback(struct parisc_device *dev) +static int sba_driver_callback(struct parisc_device *dev) { struct sba_device *sba_dev; u32 func_class; @@ -1979,8 +1978,6 @@ sba_driver_callback(struct parisc_device *dev) proc_create("sba_iommu-bitmap", 0, root, &sba_proc_bitmap_fops); #endif - parisc_vmerge_boundary = IOVP_SIZE; - parisc_vmerge_max_size = IOVP_SIZE * BITS_PER_LONG; parisc_has_iommu(); return 0; } diff --git a/drivers/parisc/wax.c b/drivers/parisc/wax.c index 892a83bbe73d..da9d5ad1353c 100644 --- a/drivers/parisc/wax.c +++ b/drivers/parisc/wax.c @@ -68,8 +68,7 @@ wax_init_irq(struct gsc_asic *wax) // gsc_writel(0xFFFFFFFF, base+0x2000); /* RS232-B on Wax */ } -int __init -wax_init_chip(struct parisc_device *dev) +static int __init wax_init_chip(struct parisc_device *dev) { struct gsc_asic *wax; struct parisc_device *parent; diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index e1ca42591ac4..2a4501dd2515 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -42,6 +42,15 @@ config PCI_DEBUG When in doubt, say N. +config PCI_STUB + tristate "PCI Stub driver" + depends on PCI + help + Say Y or M here if you want be able to reserve a PCI device + when it is going to be assigned to a guest operating system. + + When in doubt, say N. + config HT_IRQ bool "Interrupts on hypertransport devices" default y diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index af3bfe22847b..3d07ce24f6a8 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -53,6 +53,8 @@ obj-$(CONFIG_HOTPLUG) += setup-bus.o obj-$(CONFIG_PCI_SYSCALL) += syscall.o +obj-$(CONFIG_PCI_STUB) += pci-stub.o + ifeq ($(CONFIG_PCI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 39bb96b413ef..381444794778 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -66,6 +66,39 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); EXPORT_SYMBOL(pci_bus_write_config_word); EXPORT_SYMBOL(pci_bus_write_config_dword); + +/** + * pci_read_vpd - Read one entry from Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @buf: pointer to where to store result + * + */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->read(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_read_vpd); + +/** + * pci_write_vpd - Write entry to Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @val: value to write + * + */ +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->write(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_write_vpd); + /* * The following routines are to prevent the user from accessing PCI config * space when it's unsafe to do so. Some devices require this during BIST and @@ -133,125 +166,145 @@ PCI_USER_WRITE_CONFIG(dword, u32) struct pci_vpd_pci22 { struct pci_vpd base; - spinlock_t lock; /* controls access to hardware and the flags */ - u8 cap; + struct mutex lock; + u16 flag; bool busy; - bool flag; /* value of F bit to wait for */ + u8 cap; }; -/* Wait for last operation to complete */ +/* + * Wait for last operation to complete. + * This code has to spin since there is no other notification from the PCI + * hardware. Since the VPD is often implemented by serial attachment to an + * EEPROM, it may take many milliseconds to complete. + */ static int pci_vpd_pci22_wait(struct pci_dev *dev) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); - u16 flag, status; - int wait; + unsigned long timeout = jiffies + HZ/20 + 2; + u16 status; int ret; if (!vpd->busy) return 0; - flag = vpd->flag ? PCI_VPD_ADDR_F : 0; - wait = vpd->flag ? 10 : 1000; /* read: 100 us; write: 10 ms */ for (;;) { - ret = pci_user_read_config_word(dev, - vpd->cap + PCI_VPD_ADDR, + ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, &status); - if (ret < 0) + if (ret) return ret; - if ((status & PCI_VPD_ADDR_F) == flag) { + + if ((status & PCI_VPD_ADDR_F) == vpd->flag) { vpd->busy = false; return 0; } - if (wait-- == 0) + + if (time_after(jiffies, timeout)) return -ETIMEDOUT; - udelay(10); + if (fatal_signal_pending(current)) + return -EINTR; + if (!cond_resched()) + udelay(10); } } -static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, - char *buf) +static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); - u32 val; int ret; - int begin, end, i; + loff_t end = pos + count; + u8 *buf = arg; - if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) + if (pos < 0 || pos > vpd->base.len || end > vpd->base.len) return -EINVAL; - if (size == 0) - return 0; - spin_lock_irq(&vpd->lock); - ret = pci_vpd_pci22_wait(dev); - if (ret < 0) - goto out; - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos & ~3); - if (ret < 0) - goto out; - vpd->busy = true; - vpd->flag = 1; + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; + ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; - ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, - &val); -out: - spin_unlock_irq(&vpd->lock); - if (ret < 0) - return ret; - - /* Convert to bytes */ - begin = pos & 3; - end = min(4, begin + size); - for (i = 0; i < end; ++i) { - if (i >= begin) - *buf++ = val; - val >>= 8; + + while (pos < end) { + u32 val; + unsigned int i, skip; + + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos & ~3); + if (ret < 0) + break; + vpd->busy = true; + vpd->flag = PCI_VPD_ADDR_F; + ret = pci_vpd_pci22_wait(dev); + if (ret < 0) + break; + + ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); + if (ret < 0) + break; + + skip = pos & 3; + for (i = 0; i < sizeof(u32); i++) { + if (i >= skip) { + *buf++ = val; + if (++pos == end) + break; + } + val >>= 8; + } } - return end - begin; +out: + mutex_unlock(&vpd->lock); + return ret ? ret : count; } -static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, - const char *buf) +static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); - u32 val; - int ret; + const u8 *buf = arg; + loff_t end = pos + count; + int ret = 0; - if (pos < 0 || pos > vpd->base.len || pos & 3 || - size > vpd->base.len - pos || size < 4) + if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len) return -EINVAL; - val = (u8) *buf++; - val |= ((u8) *buf++) << 8; - val |= ((u8) *buf++) << 16; - val |= ((u32)(u8) *buf++) << 24; + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; - spin_lock_irq(&vpd->lock); ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; - ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, - val); - if (ret < 0) - goto out; - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos | PCI_VPD_ADDR_F); - if (ret < 0) - goto out; - vpd->busy = true; - vpd->flag = 0; - ret = pci_vpd_pci22_wait(dev); -out: - spin_unlock_irq(&vpd->lock); - if (ret < 0) - return ret; - return 4; + while (pos < end) { + u32 val; + + val = *buf++; + val |= *buf++ << 8; + val |= *buf++ << 16; + val |= *buf++ << 24; + + ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); + if (ret < 0) + break; + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos | PCI_VPD_ADDR_F); + if (ret < 0) + break; + + vpd->busy = true; + vpd->flag = 0; + ret = pci_vpd_pci22_wait(dev); + + pos += sizeof(u32); + } +out: + mutex_unlock(&vpd->lock); + return ret ? ret : count; } static void pci_vpd_pci22_release(struct pci_dev *dev) @@ -259,7 +312,7 @@ static void pci_vpd_pci22_release(struct pci_dev *dev) kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); } -static struct pci_vpd_ops pci_vpd_pci22_ops = { +static const struct pci_vpd_ops pci_vpd_pci22_ops = { .read = pci_vpd_pci22_read, .write = pci_vpd_pci22_write, .release = pci_vpd_pci22_release, @@ -279,7 +332,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) vpd->base.len = PCI_VPD_PCI22_SIZE; vpd->base.ops = &pci_vpd_pci22_ops; - spin_lock_init(&vpd->lock); + mutex_init(&vpd->lock); vpd->cap = cap; vpd->busy = false; dev->vpd = &vpd->base; @@ -287,6 +340,29 @@ int pci_vpd_pci22_init(struct pci_dev *dev) } /** + * pci_vpd_truncate - Set available Vital Product Data size + * @dev: pci device struct + * @size: available memory in bytes + * + * Adjust size of available VPD area. + */ +int pci_vpd_truncate(struct pci_dev *dev, size_t size) +{ + if (!dev->vpd) + return -EINVAL; + + /* limited by the access method */ + if (size > dev->vpd->len) + return -EINVAL; + + dev->vpd->len = size; + dev->vpd->attr->size = size; + + return 0; +} +EXPORT_SYMBOL(pci_vpd_truncate); + +/** * pci_block_user_cfg_access - Block userspace PCI config reads/writes * @dev: pci device struct * diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 999cc4088b59..52b54f053be0 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -71,7 +71,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, } /** - * add a single device + * pci_bus_add_device - add a single device * @dev: device to add * * This adds a single pci device to the global @@ -91,6 +91,37 @@ int pci_bus_add_device(struct pci_dev *dev) } /** + * pci_bus_add_child - add a child bus + * @bus: bus to add + * + * This adds sysfs entries for a single bus + */ +int pci_bus_add_child(struct pci_bus *bus) +{ + int retval; + + if (bus->bridge) + bus->dev.parent = bus->bridge; + + retval = device_register(&bus->dev); + if (retval) + return retval; + + bus->is_added = 1; + + retval = device_create_file(&bus->dev, &dev_attr_cpuaffinity); + if (retval) + return retval; + + retval = device_create_file(&bus->dev, &dev_attr_cpulistaffinity); + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(bus); + + return retval; +} + +/** * pci_bus_add_devices - insert newly discovered PCI devices * @bus: bus to check for new devices * @@ -105,7 +136,7 @@ int pci_bus_add_device(struct pci_dev *dev) void pci_bus_add_devices(struct pci_bus *bus) { struct pci_dev *dev; - struct pci_bus *child_bus; + struct pci_bus *child; int retval; list_for_each_entry(dev, &bus->devices, bus_list) { @@ -120,45 +151,29 @@ void pci_bus_add_devices(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { BUG_ON(!dev->is_added); + child = dev->subordinate; /* * If there is an unattached subordinate bus, attach * it and then scan for unattached PCI devices. */ - if (dev->subordinate) { - if (list_empty(&dev->subordinate->node)) { - down_write(&pci_bus_sem); - list_add_tail(&dev->subordinate->node, - &dev->bus->children); - up_write(&pci_bus_sem); - } - pci_bus_add_devices(dev->subordinate); - - /* register the bus with sysfs as the parent is now - * properly registered. */ - child_bus = dev->subordinate; - if (child_bus->is_added) - continue; - child_bus->dev.parent = child_bus->bridge; - retval = device_register(&child_bus->dev); - if (retval) - dev_err(&dev->dev, "Error registering pci_bus," - " continuing...\n"); - else { - child_bus->is_added = 1; - retval = device_create_file(&child_bus->dev, - &dev_attr_cpuaffinity); - } - if (retval) - dev_err(&dev->dev, "Error creating cpuaffinity" - " file, continuing...\n"); - - retval = device_create_file(&child_bus->dev, - &dev_attr_cpulistaffinity); - if (retval) - dev_err(&dev->dev, - "Error creating cpulistaffinity" - " file, continuing...\n"); + if (!child) + continue; + if (list_empty(&child->node)) { + down_write(&pci_bus_sem); + list_add_tail(&child->node, &dev->bus->children); + up_write(&pci_bus_sem); } + pci_bus_add_devices(child); + + /* + * register the bus with sysfs as the parent is now + * properly registered. + */ + if (child->is_added) + continue; + retval = pci_bus_add_child(child); + if (retval) + dev_err(&dev->dev, "Error adding bus, continuing\n"); } } diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 9bdbe1a6688f..e31fb91652ce 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -55,6 +55,9 @@ pciehp-objs := pciehp_core.o \ pciehp_ctrl.o \ pciehp_pci.o \ pciehp_hpc.o +ifdef CONFIG_ACPI +pciehp-objs += pciehp_acpi.o +endif shpchp-objs := shpchp_core.o \ shpchp_ctrl.o \ diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index e17ef54f0efc..1c1141801060 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -33,7 +33,6 @@ #include <linux/pci-acpi.h> #include <acpi/acpi.h> #include <acpi/acpi_bus.h> -#include <acpi/actypes.h> #define MY_NAME "acpi_pcihp" @@ -501,5 +500,74 @@ int acpi_root_bridge(acpi_handle handle) } EXPORT_SYMBOL_GPL(acpi_root_bridge); + +static int is_ejectable(acpi_handle handle) +{ + acpi_status status; + acpi_handle tmp; + unsigned long long removable; + status = acpi_get_handle(handle, "_ADR", &tmp); + if (ACPI_FAILURE(status)) + return 0; + status = acpi_get_handle(handle, "_EJ0", &tmp); + if (ACPI_SUCCESS(status)) + return 1; + status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); + if (ACPI_SUCCESS(status) && removable) + return 1; + return 0; +} + +/** + * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot + * @pbus: the PCI bus of the PCI slot corresponding to 'handle' + * @handle: ACPI handle to check + * + * Return 1 if handle is ejectable PCI slot, 0 otherwise. + */ +int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle) +{ + acpi_handle bridge_handle, parent_handle; + + if (!(bridge_handle = acpi_pci_get_bridge_handle(pbus))) + return 0; + if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle)))) + return 0; + if (bridge_handle != parent_handle) + return 0; + return is_ejectable(handle); +} +EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable); + +static acpi_status +check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int *found = (int *)context; + if (is_ejectable(handle)) { + *found = 1; + return AE_CTRL_TERMINATE; + } + return AE_OK; +} + +/** + * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots + * @pbus - PCI bus to scan + * + * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise. + */ +int acpi_pci_detect_ejectable(struct pci_bus *pbus) +{ + acpi_handle handle; + int found = 0; + + if (!(handle = acpi_pci_get_bridge_handle(pbus))) + return 0; + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, + check_hotplug, (void *)&found, NULL); + return found; +} +EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable); + module_param(debug_acpi, bool, 0644); MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not"); diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 9bcb6cbd5aa9..4fc168b70095 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -44,7 +44,7 @@ do { \ if (acpiphp_debug) \ printk(KERN_DEBUG "%s: " format, \ - MY_NAME , ## arg); \ + MY_NAME , ## arg); \ } while (0) #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3affc6472e65..f09b1010d477 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -46,6 +46,7 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> +#include <linux/pci-acpi.h> #include <linux/mutex.h> #include "../pci.h" @@ -62,61 +63,6 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus); static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus); static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context); - -/* - * initialization & terminatation routines - */ - -/** - * is_ejectable - determine if a slot is ejectable - * @handle: handle to acpi namespace - * - * Ejectable slot should satisfy at least these conditions: - * - * 1. has _ADR method - * 2. has _EJ0 method - * - * optionally - * - * 1. has _STA method - * 2. has _PS0 method - * 3. has _PS3 method - * 4. .. - */ -static int is_ejectable(acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - - status = acpi_get_handle(handle, "_ADR", &tmp); - if (ACPI_FAILURE(status)) { - return 0; - } - - status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) { - return 0; - } - - return 1; -} - - -/* callback routine to check for the existence of ejectable slots */ -static acpi_status -is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *count = (int *)context; - - if (is_ejectable(handle)) { - (*count)++; - /* only one ejectable slot is enough */ - return AE_CTRL_TERMINATE; - } else { - return AE_OK; - } -} - /* callback routine to check for the existence of a pci dock device */ static acpi_status is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) @@ -131,9 +77,6 @@ is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) } } - - - /* * the _DCK method can do funny things... and sometimes not * hah-hah funny. @@ -160,9 +103,9 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val, if (((buses >> 8) & 0xff) != bus->secondary) { buses = (buses & 0xff000000) - | ((unsigned int)(bus->primary) << 0) - | ((unsigned int)(bus->secondary) << 8) - | ((unsigned int)(bus->subordinate) << 16); + | ((unsigned int)(bus->primary) << 0) + | ((unsigned int)(bus->secondary) << 8) + | ((unsigned int)(bus->subordinate) << 16); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); } return NOTIFY_OK; @@ -184,17 +127,12 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_status status = AE_OK; unsigned long long adr, sun; int device, function, retval; + struct pci_bus *pbus = bridge->pci_bus; - status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); - - if (ACPI_FAILURE(status)) - return AE_OK; - - status = acpi_get_handle(handle, "_EJ0", &tmp); - - if (ACPI_FAILURE(status) && !(is_dock_device(handle))) + if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) return AE_OK; + acpi_evaluate_integer(handle, "_ADR", NULL, &adr); device = (adr >> 16) & 0xffff; function = adr & 0xffff; @@ -205,7 +143,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) INIT_LIST_HEAD(&newfunc->sibling); newfunc->handle = handle; newfunc->function = function; - if (ACPI_SUCCESS(status)) + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp))) newfunc->flags = FUNC_HAS_EJ0; if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp))) @@ -256,8 +195,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) bridge->nr_slots++; dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", - slot->sun, pci_domain_nr(bridge->pci_bus), - bridge->pci_bus->number, slot->device); + slot->sun, pci_domain_nr(pbus), pbus->number, device); retval = acpiphp_register_hotplug_slot(slot); if (retval) { if (retval == -EBUSY) @@ -274,8 +212,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) list_add_tail(&newfunc->sibling, &slot->funcs); /* associate corresponding pci_dev */ - newfunc->pci_dev = pci_get_slot(bridge->pci_bus, - PCI_DEVFN(device, function)); + newfunc->pci_dev = pci_get_slot(pbus, PCI_DEVFN(device, function)); if (newfunc->pci_dev) { slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); } @@ -324,27 +261,15 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) /* see if it's worth looking at this bridge */ -static int detect_ejectable_slots(acpi_handle *bridge_handle) +static int detect_ejectable_slots(struct pci_bus *pbus) { - acpi_status status; - int count; - - count = 0; - - /* only check slots defined directly below bridge object */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, - is_ejectable_slot, (void *)&count, NULL); - - /* - * we also need to add this bridge if there is a dock bridge or - * other pci device on a dock station (removable) - */ - if (!count) - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, - (u32)1, is_pci_dock_device, (void *)&count, - NULL); - - return count; + int found = acpi_pci_detect_ejectable(pbus); + if (!found) { + acpi_handle bridge_handle = acpi_pci_get_bridge_handle(pbus); + acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, + is_pci_dock_device, (void *)&found, NULL); + } + return found; } @@ -554,7 +479,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) goto out; /* check if this bridge has ejectable slots */ - if ((detect_ejectable_slots(handle) > 0)) { + if ((detect_ejectable_slots(dev->subordinate) > 0)) { dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); add_p2p_bridge(handle, dev); } @@ -615,7 +540,7 @@ static int add_bridge(acpi_handle handle) } /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(handle) > 0) { + if (detect_ejectable_slots(pci_bus) > 0) { dbg("found PCI host-bus bridge with hot-pluggable slots\n"); add_host_bridge(handle, pci_bus); } diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 881fdd2b7313..5befa7e379b7 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -271,7 +271,7 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context) dbg("%s: generationg bus event\n", __func__); acpi_bus_generate_proc_event(note->device, note->event, detail); acpi_bus_generate_netlink_event(note->device->pnp.device_class, - note->device->dev.bus_id, + dev_name(¬e->device->dev), note->event, detail); } else note->event = event; diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index a60a25290995..cc227a8c4b11 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1954,7 +1954,7 @@ void cpqhp_pushbutton_thread(unsigned long slot) return ; } - if (func != NULL && ctrl != NULL) { + if (ctrl != NULL) { if (cpqhp_process_SI(ctrl, func) != 0) { amber_LED_on(ctrl, hp_slot); green_LED_off(ctrl, hp_slot); @@ -2604,7 +2604,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func for (cloop = 0; cloop < 4; cloop++) { if (irqs.valid_INT & (0x01 << cloop)) { rc = cpqhp_set_irq(func->bus, func->device, - 0x0A + cloop, irqs.interrupt[cloop]); + cloop + 1, irqs.interrupt[cloop]); if (rc) goto free_and_out; } @@ -2945,7 +2945,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func } if (!behind_bridge) { - rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); + rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ); if (rc) return 1; } else { diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index df146be9d2e9..6c0ed0fcb8ee 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -171,7 +171,7 @@ int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) fakebus->number = bus_num; dbg("%s: dev %d, bus %d, pin %d, num %d\n", __func__, dev_num, bus_num, int_pin, irq_num); - rc = pcibios_set_irq_routing(fakedev, int_pin - 0x0a, irq_num); + rc = pcibios_set_irq_routing(fakedev, int_pin - 1, irq_num); kfree(fakedev); kfree(fakebus); dbg("%s: rc %d\n", __func__, rc); diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index 3a2637a00934..b0e7de9e536d 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -324,6 +324,7 @@ static int disable_slot(struct hotplug_slot *slot) if (test_and_set_bit(0, &dslot->removed)) { dbg("Slot already scheduled for removal\n"); + pci_dev_put(dev); return -ENODEV; } diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index b2801a7ee37f..db85284ffb62 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -217,14 +217,25 @@ struct hpc_ops { #ifdef CONFIG_ACPI #include <acpi/acpi.h> #include <acpi/acpi_bus.h> -#include <acpi/actypes.h> #include <linux/pci-acpi.h> +extern void __init pciehp_acpi_slot_detection_init(void); +extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev); + +static inline void pciehp_firmware_init(void) +{ + pciehp_acpi_slot_detection_init(); +} + static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) { + int retval; u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); - return acpi_get_hp_hw_control_from_firmware(dev, flags); + retval = acpi_get_hp_hw_control_from_firmware(dev, flags); + if (retval) + return retval; + return pciehp_acpi_slot_detection_check(dev); } static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, @@ -235,6 +246,7 @@ static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, return 0; } #else +#define pciehp_firmware_init() do {} while (0) #define pciehp_get_hp_hw_control_from_firmware(dev) 0 #define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV) #endif /* CONFIG_ACPI */ diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c new file mode 100644 index 000000000000..438d795f9fe3 --- /dev/null +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -0,0 +1,141 @@ +/* + * ACPI related functions for PCI Express Hot Plug driver. + * + * Copyright (C) 2008 Kenji Kaneshige + * Copyright (C) 2008 Fujitsu Limited. + * + * 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. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. 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 <linux/acpi.h> +#include <linux/pci.h> +#include <linux/pci_hotplug.h> +#include "pciehp.h" + +#define PCIEHP_DETECT_PCIE (0) +#define PCIEHP_DETECT_ACPI (1) +#define PCIEHP_DETECT_AUTO (2) +#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO + +static int slot_detection_mode; +static char *pciehp_detect_mode; +module_param(pciehp_detect_mode, charp, 0444); +MODULE_PARM_DESC(pciehp_detect_mode, + "Slot detection mode: pcie, acpi, auto\n" + " pcie - Use PCIe based slot detection\n" + " acpi - Use ACPI for slot detection\n" + " auto(default) - Auto select mode. Use acpi option if duplicate\n" + " slot ids are found. Otherwise, use pcie option\n"); + +int pciehp_acpi_slot_detection_check(struct pci_dev *dev) +{ + if (slot_detection_mode != PCIEHP_DETECT_ACPI) + return 0; + if (acpi_pci_detect_ejectable(dev->subordinate)) + return 0; + return -ENODEV; +} + +static int __init parse_detect_mode(void) +{ + if (!pciehp_detect_mode) + return PCIEHP_DETECT_DEFAULT; + if (!strcmp(pciehp_detect_mode, "pcie")) + return PCIEHP_DETECT_PCIE; + if (!strcmp(pciehp_detect_mode, "acpi")) + return PCIEHP_DETECT_ACPI; + if (!strcmp(pciehp_detect_mode, "auto")) + return PCIEHP_DETECT_AUTO; + warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", + pciehp_detect_mode); + return PCIEHP_DETECT_DEFAULT; +} + +static struct pcie_port_service_id __initdata port_pci_ids[] = { + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .port_type = PCIE_ANY_PORT, + .service_type = PCIE_PORT_SERVICE_HP, + .driver_data = 0, + }, { /* end: all zeroes */ } +}; + +static int __initdata dup_slot_id; +static int __initdata acpi_slot_detected; +static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); + +/* Dummy driver for dumplicate name detection */ +static int __init dummy_probe(struct pcie_device *dev, + const struct pcie_port_service_id *id) +{ + int pos; + u32 slot_cap; + struct slot *slot, *tmp; + struct pci_dev *pdev = dev->port; + struct pci_bus *pbus = pdev->subordinate; + if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL))) + return -ENOMEM; + /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ + if (pciehp_get_hp_hw_control_from_firmware(pdev)) + return -ENODEV; + if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP))) + return -ENODEV; + pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap); + slot->number = slot_cap >> 19; + list_for_each_entry(tmp, &dummy_slots, slot_list) { + if (tmp->number == slot->number) + dup_slot_id++; + } + list_add_tail(&slot->slot_list, &dummy_slots); + if (!acpi_slot_detected && acpi_pci_detect_ejectable(pbus)) + acpi_slot_detected = 1; + return -ENODEV; /* dummy driver always returns error */ +} + +static struct pcie_port_service_driver __initdata dummy_driver = { + .name = "pciehp_dummy", + .id_table = port_pci_ids, + .probe = dummy_probe, +}; + +static int __init select_detection_mode(void) +{ + struct slot *slot, *tmp; + pcie_port_service_register(&dummy_driver); + pcie_port_service_unregister(&dummy_driver); + list_for_each_entry_safe(slot, tmp, &dummy_slots, slot_list) { + list_del(&slot->slot_list); + kfree(slot); + } + if (acpi_slot_detected && dup_slot_id) + return PCIEHP_DETECT_ACPI; + return PCIEHP_DETECT_PCIE; +} + +void __init pciehp_acpi_slot_detection_init(void) +{ + slot_detection_mode = parse_detect_mode(); + if (slot_detection_mode != PCIEHP_DETECT_AUTO) + goto out; + slot_detection_mode = select_detection_mode(); +out: + if (slot_detection_mode == PCIEHP_DETECT_ACPI) + info("Using ACPI for slot detection.\n"); +} diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 39cf248d24e3..5482d4ed8256 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -522,6 +522,7 @@ static int __init pcied_init(void) { int retval = 0; + pciehp_firmware_init(); retval = pcie_port_service_register(&hpdriver_portdrv); dbg("pcie_port_service_register = %d\n", retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index fead63c6b49e..ff4034502d24 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -178,15 +178,14 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) "Issue of Slot Power Off command failed\n"); return; } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); } - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); - if (PWR_LED(ctrl)) pslot->hpc_ops->green_led_off(pslot); @@ -286,15 +285,14 @@ static int remove_board(struct slot *p_slot) "Issue of Slot Disable command failed\n"); return retval; } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); } - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); - if (PWR_LED(ctrl)) /* turn off Green LED */ p_slot->hpc_ops->green_led_off(p_slot); diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b643ca13e4f1..71a8012886b0 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -42,42 +42,6 @@ static atomic_t pciehp_num_controllers = ATOMIC_INIT(0); -struct ctrl_reg { - u8 cap_id; - u8 nxt_ptr; - u16 cap_reg; - u32 dev_cap; - u16 dev_ctrl; - u16 dev_status; - u32 lnk_cap; - u16 lnk_ctrl; - u16 lnk_status; - u32 slot_cap; - u16 slot_ctrl; - u16 slot_status; - u16 root_ctrl; - u16 rsvp; - u32 root_status; -} __attribute__ ((packed)); - -/* offsets to the controller registers based on the above structure layout */ -enum ctrl_offsets { - PCIECAPID = offsetof(struct ctrl_reg, cap_id), - NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr), - CAPREG = offsetof(struct ctrl_reg, cap_reg), - DEVCAP = offsetof(struct ctrl_reg, dev_cap), - DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl), - DEVSTATUS = offsetof(struct ctrl_reg, dev_status), - LNKCAP = offsetof(struct ctrl_reg, lnk_cap), - LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl), - LNKSTATUS = offsetof(struct ctrl_reg, lnk_status), - SLOTCAP = offsetof(struct ctrl_reg, slot_cap), - SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl), - SLOTSTATUS = offsetof(struct ctrl_reg, slot_status), - ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl), - ROOTSTATUS = offsetof(struct ctrl_reg, root_status), -}; - static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value) { struct pci_dev *dev = ctrl->pci_dev; @@ -102,95 +66,9 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) return pci_write_config_dword(dev, ctrl->cap_base + reg, value); } -/* Field definitions in PCI Express Capabilities Register */ -#define CAP_VER 0x000F -#define DEV_PORT_TYPE 0x00F0 -#define SLOT_IMPL 0x0100 -#define MSG_NUM 0x3E00 - -/* Device or Port Type */ -#define NAT_ENDPT 0x00 -#define LEG_ENDPT 0x01 -#define ROOT_PORT 0x04 -#define UP_STREAM 0x05 -#define DN_STREAM 0x06 -#define PCIE_PCI_BRDG 0x07 -#define PCI_PCIE_BRDG 0x10 - -/* Field definitions in Device Capabilities Register */ -#define DATTN_BUTTN_PRSN 0x1000 -#define DATTN_LED_PRSN 0x2000 -#define DPWR_LED_PRSN 0x4000 - -/* Field definitions in Link Capabilities Register */ -#define MAX_LNK_SPEED 0x000F -#define MAX_LNK_WIDTH 0x03F0 -#define LINK_ACTIVE_REPORTING 0x00100000 - -/* Link Width Encoding */ -#define LNK_X1 0x01 -#define LNK_X2 0x02 -#define LNK_X4 0x04 -#define LNK_X8 0x08 -#define LNK_X12 0x0C -#define LNK_X16 0x10 -#define LNK_X32 0x20 - -/*Field definitions of Link Status Register */ -#define LNK_SPEED 0x000F -#define NEG_LINK_WD 0x03F0 -#define LNK_TRN_ERR 0x0400 -#define LNK_TRN 0x0800 -#define SLOT_CLK_CONF 0x1000 -#define LINK_ACTIVE 0x2000 - -/* Field definitions in Slot Capabilities Register */ -#define ATTN_BUTTN_PRSN 0x00000001 -#define PWR_CTRL_PRSN 0x00000002 -#define MRL_SENS_PRSN 0x00000004 -#define ATTN_LED_PRSN 0x00000008 -#define PWR_LED_PRSN 0x00000010 -#define HP_SUPR_RM_SUP 0x00000020 -#define HP_CAP 0x00000040 -#define SLOT_PWR_VALUE 0x000003F8 -#define SLOT_PWR_LIMIT 0x00000C00 -#define PSN 0xFFF80000 /* PSN: Physical Slot Number */ - -/* Field definitions in Slot Control Register */ -#define ATTN_BUTTN_ENABLE 0x0001 -#define PWR_FAULT_DETECT_ENABLE 0x0002 -#define MRL_DETECT_ENABLE 0x0004 -#define PRSN_DETECT_ENABLE 0x0008 -#define CMD_CMPL_INTR_ENABLE 0x0010 -#define HP_INTR_ENABLE 0x0020 -#define ATTN_LED_CTRL 0x00C0 -#define PWR_LED_CTRL 0x0300 -#define PWR_CTRL 0x0400 -#define EMI_CTRL 0x0800 - -/* Attention indicator and Power indicator states */ -#define LED_ON 0x01 -#define LED_BLINK 0x10 -#define LED_OFF 0x11 - /* Power Control Command */ #define POWER_ON 0 -#define POWER_OFF 0x0400 - -/* EMI Status defines */ -#define EMI_DISENGAGED 0 -#define EMI_ENGAGED 1 - -/* Field definitions in Slot Status Register */ -#define ATTN_BUTTN_PRESSED 0x0001 -#define PWR_FAULT_DETECTED 0x0002 -#define MRL_SENS_CHANGED 0x0004 -#define PRSN_DETECT_CHANGED 0x0008 -#define CMD_COMPLETED 0x0010 -#define MRL_STATE 0x0020 -#define PRSN_STATE 0x0040 -#define EMI_STATE 0x0080 -#define EMI_STATUS_BIT 7 +#define POWER_OFF PCI_EXP_SLTCTL_PCC static irqreturn_t pcie_isr(int irq, void *dev_id); static void start_int_poll_timer(struct controller *ctrl, int sec); @@ -253,22 +131,20 @@ static inline void pciehp_free_irq(struct controller *ctrl) static int pcie_poll_cmd(struct controller *ctrl) { u16 slot_status; - int timeout = 1000; + int err, timeout = 1000; - if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) { - if (slot_status & CMD_COMPLETED) { - pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED); - return 1; - } + err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { + pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + return 1; } while (timeout > 0) { msleep(10); timeout -= 10; - if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) { - if (slot_status & CMD_COMPLETED) { - pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED); - return 1; - } + err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { + pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + return 1; } } return 0; /* timeout */ @@ -302,14 +178,14 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) mutex_lock(&ctrl->ctrl_lock); - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); goto out; } - if (slot_status & CMD_COMPLETED) { + if (slot_status & PCI_EXP_SLTSTA_CC) { if (!ctrl->no_cmd_complete) { /* * After 1 sec and CMD_COMPLETED still not set, just @@ -332,7 +208,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) } } - retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); + retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); goto out; @@ -342,7 +218,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); - retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl); + retval = pciehp_writew(ctrl, PCI_EXP_SLTCTL, slot_ctrl); if (retval) ctrl_err(ctrl, "Cannot write to SLOTCTRL register\n"); @@ -356,8 +232,8 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) * completed interrupt is not enabled, we need to poll * command completed event. */ - if (!(slot_ctrl & HP_INTR_ENABLE) || - !(slot_ctrl & CMD_CMPL_INTR_ENABLE)) + if (!(slot_ctrl & PCI_EXP_SLTCTL_HPIE) || + !(slot_ctrl & PCI_EXP_SLTCTL_CCIE)) poll = 1; pcie_wait_cmd(ctrl, poll); } @@ -370,9 +246,9 @@ static inline int check_link_active(struct controller *ctrl) { u16 link_status; - if (pciehp_readw(ctrl, LNKSTATUS, &link_status)) + if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &link_status)) return 0; - return !!(link_status & LINK_ACTIVE); + return !!(link_status & PCI_EXP_LNKSTA_DLLLA); } static void pcie_wait_link_active(struct controller *ctrl) @@ -412,15 +288,15 @@ static int hpc_check_lnk_status(struct controller *ctrl) } else msleep(1000); - retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); return retval; } ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); - if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) || - !(lnk_status & NEG_LINK_WD)) { + if ((lnk_status & PCI_EXP_LNKSTA_LT) || + !(lnk_status & PCI_EXP_LNKSTA_NLW)) { ctrl_err(ctrl, "Link Training Error occurs \n"); retval = -1; return retval; @@ -436,16 +312,16 @@ static int hpc_get_attention_status(struct slot *slot, u8 *status) u8 atten_led_state; int retval = 0; - retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); + retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); return retval; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_ctrl); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_ctrl); - atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6; + atten_led_state = (slot_ctrl & PCI_EXP_SLTCTL_AIC) >> 6; switch (atten_led_state) { case 0: @@ -475,15 +351,15 @@ static int hpc_get_power_status(struct slot *slot, u8 *status) u8 pwr_state; int retval = 0; - retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); + retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); return retval; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_ctrl); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_ctrl); - pwr_state = (slot_ctrl & PWR_CTRL) >> 10; + pwr_state = (slot_ctrl & PCI_EXP_SLTCTL_PCC) >> 10; switch (pwr_state) { case 0: @@ -504,17 +380,15 @@ static int hpc_get_latch_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } - - *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1; - + *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); return 0; } @@ -522,18 +396,15 @@ static int hpc_get_adapter_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; - u8 card_state; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } - card_state = (u8)((slot_status & PRSN_STATE) >> 6); - *status = (card_state == 1) ? 1 : 0; - + *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); return 0; } @@ -541,32 +412,28 @@ static int hpc_query_power_fault(struct slot *slot) { struct controller *ctrl = slot->ctrl; u16 slot_status; - u8 pwr_fault; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "Cannot check for power fault\n"); return retval; } - pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1); - - return pwr_fault; + return !!(slot_status & PCI_EXP_SLTSTA_PFD); } static int hpc_get_emi_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "Cannot check EMI status\n"); return retval; } - *status = (slot_status & EMI_STATE) >> EMI_STATUS_BIT; - + *status = !!(slot_status & PCI_EXP_SLTSTA_EIS); return retval; } @@ -576,8 +443,8 @@ static int hpc_toggle_emi(struct slot *slot) u16 cmd_mask; int rc; - slot_cmd = EMI_CTRL; - cmd_mask = EMI_CTRL; + slot_cmd = PCI_EXP_SLTCTL_EIC; + cmd_mask = PCI_EXP_SLTCTL_EIC; rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask); slot->last_emi_toggle = get_seconds(); @@ -591,7 +458,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value) u16 cmd_mask; int rc; - cmd_mask = ATTN_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_AIC; switch (value) { case 0 : /* turn off */ slot_cmd = 0x00C0; @@ -607,7 +474,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value) } rc = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); return rc; } @@ -619,10 +486,10 @@ static void hpc_set_green_led_on(struct slot *slot) u16 cmd_mask; slot_cmd = 0x0100; - cmd_mask = PWR_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PIC; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } static void hpc_set_green_led_off(struct slot *slot) @@ -632,10 +499,10 @@ static void hpc_set_green_led_off(struct slot *slot) u16 cmd_mask; slot_cmd = 0x0300; - cmd_mask = PWR_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PIC; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } static void hpc_set_green_led_blink(struct slot *slot) @@ -645,10 +512,10 @@ static void hpc_set_green_led_blink(struct slot *slot) u16 cmd_mask; slot_cmd = 0x0200; - cmd_mask = PWR_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PIC; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } static int hpc_power_on_slot(struct slot * slot) @@ -662,15 +529,15 @@ static int hpc_power_on_slot(struct slot * slot) ctrl_dbg(ctrl, "%s: slot->hp_slot %x\n", __func__, slot->hp_slot); /* Clear sticky power-fault bit from previous power failures */ - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } - slot_status &= PWR_FAULT_DETECTED; + slot_status &= PCI_EXP_SLTSTA_PFD; if (slot_status) { - retval = pciehp_writew(ctrl, SLOTSTATUS, slot_status); + retval = pciehp_writew(ctrl, PCI_EXP_SLTSTA, slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS register\n", @@ -680,13 +547,13 @@ static int hpc_power_on_slot(struct slot * slot) } slot_cmd = POWER_ON; - cmd_mask = PWR_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PCC; /* Enable detection that we turned off at slot power-off time */ if (!pciehp_poll_mode) { - slot_cmd |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); - cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); + slot_cmd |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); + cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); @@ -696,7 +563,7 @@ static int hpc_power_on_slot(struct slot * slot) return -1; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); return retval; } @@ -753,7 +620,7 @@ static int hpc_power_off_slot(struct slot * slot) changed = pcie_mask_bad_dllp(ctrl); slot_cmd = POWER_OFF; - cmd_mask = PWR_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PCC; /* * If we get MRL or presence detect interrupts now, the isr * will notice the sticky power-fault bit too and issue power @@ -762,10 +629,10 @@ static int hpc_power_off_slot(struct slot * slot) * till the slot is powered on again. */ if (!pciehp_poll_mode) { - slot_cmd &= ~(PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); - cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); + slot_cmd &= ~(PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); + cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); @@ -775,7 +642,7 @@ static int hpc_power_off_slot(struct slot * slot) goto out; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); out: if (changed) pcie_unmask_bad_dllp(ctrl); @@ -796,19 +663,19 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) */ intr_loc = 0; do { - if (pciehp_readw(ctrl, SLOTSTATUS, &detected)) { + if (pciehp_readw(ctrl, PCI_EXP_SLTSTA, &detected)) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS\n", __func__); return IRQ_NONE; } - detected &= (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | - MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | - CMD_COMPLETED); + detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_CC); intr_loc |= detected; if (!intr_loc) return IRQ_NONE; - if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) { + if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, detected)) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", __func__); return IRQ_NONE; @@ -818,31 +685,31 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc); /* Check Command Complete Interrupt Pending */ - if (intr_loc & CMD_COMPLETED) { + if (intr_loc & PCI_EXP_SLTSTA_CC) { ctrl->cmd_busy = 0; smp_mb(); wake_up(&ctrl->queue); } - if (!(intr_loc & ~CMD_COMPLETED)) + if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) return IRQ_HANDLED; p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); /* Check MRL Sensor Changed */ - if (intr_loc & MRL_SENS_CHANGED) + if (intr_loc & PCI_EXP_SLTSTA_MRLSC) pciehp_handle_switch_change(p_slot); /* Check Attention Button Pressed */ - if (intr_loc & ATTN_BUTTN_PRESSED) + if (intr_loc & PCI_EXP_SLTSTA_ABP) pciehp_handle_attention_button(p_slot); /* Check Presence Detect Changed */ - if (intr_loc & PRSN_DETECT_CHANGED) + if (intr_loc & PCI_EXP_SLTSTA_PDC) pciehp_handle_presence_change(p_slot); /* Check Power Fault Detected */ - if (intr_loc & PWR_FAULT_DETECTED) + if (intr_loc & PCI_EXP_SLTSTA_PFD) pciehp_handle_power_fault(p_slot); return IRQ_HANDLED; @@ -855,7 +722,7 @@ static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value) u32 lnk_cap; int retval = 0; - retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); + retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); return retval; @@ -884,13 +751,13 @@ static int hpc_get_max_lnk_width(struct slot *slot, u32 lnk_cap; int retval = 0; - retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); + retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); return retval; } - switch ((lnk_cap & 0x03F0) >> 4){ + switch ((lnk_cap & PCI_EXP_LNKSTA_NLW) >> 4){ case 0: lnk_wdth = PCIE_LNK_WIDTH_RESRV; break; @@ -933,14 +800,14 @@ static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value) int retval = 0; u16 lnk_status; - retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n", __func__); return retval; } - switch (lnk_status & 0x0F) { + switch (lnk_status & PCI_EXP_LNKSTA_CLS) { case 1: lnk_speed = PCIE_2PT5GB; break; @@ -963,14 +830,14 @@ static int hpc_get_cur_lnk_width(struct slot *slot, int retval = 0; u16 lnk_status; - retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n", __func__); return retval; } - switch ((lnk_status & 0x03F0) >> 4){ + switch ((lnk_status & PCI_EXP_LNKSTA_NLW) >> 4){ case 0: lnk_wdth = PCIE_LNK_WIDTH_RESRV; break; @@ -1036,18 +903,19 @@ int pcie_enable_notification(struct controller *ctrl) { u16 cmd, mask; - cmd = PRSN_DETECT_ENABLE; + cmd = PCI_EXP_SLTCTL_PDCE; if (ATTN_BUTTN(ctrl)) - cmd |= ATTN_BUTTN_ENABLE; + cmd |= PCI_EXP_SLTCTL_ABPE; if (POWER_CTRL(ctrl)) - cmd |= PWR_FAULT_DETECT_ENABLE; + cmd |= PCI_EXP_SLTCTL_PFDE; if (MRL_SENS(ctrl)) - cmd |= MRL_DETECT_ENABLE; + cmd |= PCI_EXP_SLTCTL_MRLSCE; if (!pciehp_poll_mode) - cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; + cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; - mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | - PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); if (pcie_write_cmd(ctrl, cmd, mask)) { ctrl_err(ctrl, "Cannot enable software notification\n"); @@ -1059,8 +927,9 @@ int pcie_enable_notification(struct controller *ctrl) static void pcie_disable_notification(struct controller *ctrl) { u16 mask; - mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | - PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); if (pcie_write_cmd(ctrl, 0, mask)) ctrl_warn(ctrl, "Cannot disable software notification\n"); } @@ -1157,9 +1026,9 @@ static inline void dbg_ctrl(struct controller *ctrl) EMI(ctrl) ? "yes" : "no"); ctrl_info(ctrl, " Command Completed : %3s\n", NO_CMD_CMPL(ctrl) ? "no" : "yes"); - pciehp_readw(ctrl, SLOTSTATUS, ®16); + pciehp_readw(ctrl, PCI_EXP_SLTSTA, ®16); ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16); - pciehp_readw(ctrl, SLOTCTRL, ®16); + pciehp_readw(ctrl, PCI_EXP_SLTCTL, ®16); ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16); } @@ -1183,7 +1052,7 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl_err(ctrl, "Cannot find PCI Express capability\n"); goto abort_ctrl; } - if (pciehp_readl(ctrl, SLOTCAP, &slot_cap)) { + if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) { ctrl_err(ctrl, "Cannot read SLOTCAP register\n"); goto abort_ctrl; } @@ -1208,17 +1077,17 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->no_cmd_complete = 1; /* Check if Data Link Layer Link Active Reporting is implemented */ - if (pciehp_readl(ctrl, LNKCAP, &link_cap)) { + if (pciehp_readl(ctrl, PCI_EXP_LNKCAP, &link_cap)) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); goto abort_ctrl; } - if (link_cap & LINK_ACTIVE_REPORTING) { + if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { ctrl_dbg(ctrl, "Link Active Reporting supported\n"); ctrl->link_active_reporting = 1; } /* Clear all remaining event bits in Slot Status register */ - if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) + if (pciehp_writew(ctrl, PCI_EXP_SLTSTA, 0x1f)) goto abort_ctrl; /* Disable sotfware notification */ diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c index 6441dfa969a3..de01174aff06 100644 --- a/drivers/pci/irq.c +++ b/drivers/pci/irq.c @@ -15,7 +15,7 @@ static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason) dev_printk(KERN_ERR, &pdev->dev, "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", - parent->dev.bus_id, parent->vendor, parent->device); + dev_name(&parent->dev), parent->vendor, parent->device); dev_printk(KERN_ERR, &pdev->dev, "%s\n", reason); dev_printk(KERN_ERR, &pdev->dev, "Please report to linux-kernel@vger.kernel.org\n"); WARN_ON(1); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 11a51f8ed3b3..b4a90badd0a6 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -776,28 +776,19 @@ void pci_no_msi(void) pci_msi_enable = 0; } -void pci_msi_init_pci_dev(struct pci_dev *dev) -{ - INIT_LIST_HEAD(&dev->msi_list); -} - -#ifdef CONFIG_ACPI -#include <linux/acpi.h> -#include <linux/pci-acpi.h> -static void __devinit msi_acpi_init(void) +/** + * pci_msi_enabled - is MSI enabled? + * + * Returns true if MSI has not been disabled by the command-line option + * pci=nomsi. + **/ +int pci_msi_enabled(void) { - if (acpi_pci_disabled) - return; - pci_osc_support_set(OSC_MSI_SUPPORT); - pcie_osc_support_set(OSC_MSI_SUPPORT); + return pci_msi_enable; } -#else -static inline void msi_acpi_init(void) { } -#endif /* CONFIG_ACPI */ +EXPORT_SYMBOL(pci_msi_enabled); -void __devinit msi_init(void) +void pci_msi_init_pci_dev(struct pci_dev *dev) { - if (!pci_msi_enable) - return; - msi_acpi_init(); + INIT_LIST_HEAD(&dev->msi_list); } diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index ae5ec76dca77..deea8a187eb8 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -13,8 +13,6 @@ #include <linux/module.h> #include <linux/pci-aspm.h> #include <acpi/acpi.h> -#include <acpi/acnamesp.h> -#include <acpi/acresrc.h> #include <acpi/acpi_bus.h> #include <linux/pci-acpi.h> @@ -24,13 +22,14 @@ struct acpi_osc_data { acpi_handle handle; u32 support_set; u32 control_set; + u32 control_query; + int is_queried; struct list_head sibiling; }; static LIST_HEAD(acpi_osc_data_list); struct acpi_osc_args { u32 capbuf[3]; - u32 ctrl_result; }; static DEFINE_MUTEX(pci_acpi_lock); @@ -56,7 +55,7 @@ static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; static acpi_status acpi_run_osc(acpi_handle handle, - struct acpi_osc_args *osc_args) + struct acpi_osc_args *osc_args, u32 *retval) { acpi_status status; struct acpi_object_list input; @@ -112,8 +111,7 @@ static acpi_status acpi_run_osc(acpi_handle handle, goto out_kfree; } out_success: - osc_args->ctrl_result = - *((u32 *)(out_obj->buffer.pointer + 8)); + *retval = *((u32 *)(out_obj->buffer.pointer + 8)); status = AE_OK; out_kfree: @@ -121,11 +119,10 @@ out_kfree: return status; } -static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, - u32 *result) +static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data) { acpi_status status; - u32 support_set; + u32 support_set, result; struct acpi_osc_args osc_args; /* do _OSC query for all possible controls */ @@ -134,56 +131,45 @@ static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set; osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; - status = acpi_run_osc(osc_data->handle, &osc_args); + status = acpi_run_osc(osc_data->handle, &osc_args, &result); if (ACPI_SUCCESS(status)) { osc_data->support_set = support_set; - *result = osc_args.ctrl_result; + osc_data->control_query = result; + osc_data->is_queried = 1; } return status; } -static acpi_status acpi_query_osc(acpi_handle handle, - u32 level, void *context, void **retval) +/* + * pci_acpi_osc_support: Invoke _OSC indicating support for the given feature + * @flags: Bitmask of flags to support + * + * See the ACPI spec for the definition of the flags + */ +int pci_acpi_osc_support(acpi_handle handle, u32 flags) { acpi_status status; - struct acpi_osc_data *osc_data; - u32 flags = (unsigned long)context, dummy; acpi_handle tmp; + struct acpi_osc_data *osc_data; + int rc = 0; status = acpi_get_handle(handle, "_OSC", &tmp); if (ACPI_FAILURE(status)) - return AE_OK; + return -ENOTTY; mutex_lock(&pci_acpi_lock); osc_data = acpi_get_osc_data(handle); if (!osc_data) { printk(KERN_ERR "acpi osc data array is full\n"); + rc = -ENOMEM; goto out; } - __acpi_query_osc(flags, osc_data, &dummy); + __acpi_query_osc(flags, osc_data); out: mutex_unlock(&pci_acpi_lock); - return AE_OK; -} - -/** - * __pci_osc_support_set - register OS support to Firmware - * @flags: OS support bits - * @hid: hardware ID - * - * Update OS support fields and doing a _OSC Query to obtain an update - * from Firmware on supported control bits. - **/ -acpi_status __pci_osc_support_set(u32 flags, const char *hid) -{ - if (!(flags & OSC_SUPPORT_MASKS)) - return AE_TYPE; - - acpi_get_devices(hid, acpi_query_osc, - (void *)(unsigned long)flags, NULL); - return AE_OK; + return rc; } /** @@ -196,7 +182,7 @@ acpi_status __pci_osc_support_set(u32 flags, const char *hid) acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) { acpi_status status; - u32 ctrlset, control_set, result; + u32 control_req, control_set, result; acpi_handle tmp; struct acpi_osc_data *osc_data; struct acpi_osc_args osc_args; @@ -213,28 +199,34 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) goto out; } - ctrlset = (flags & OSC_CONTROL_MASKS); - if (!ctrlset) { + control_req = (flags & OSC_CONTROL_MASKS); + if (!control_req) { status = AE_TYPE; goto out; } - status = __acpi_query_osc(osc_data->support_set, osc_data, &result); - if (ACPI_FAILURE(status)) + /* No need to evaluate _OSC if the control was already granted. */ + if ((osc_data->control_set & control_req) == control_req) goto out; - if ((result & ctrlset) != ctrlset) { + if (!osc_data->is_queried) { + status = __acpi_query_osc(osc_data->support_set, osc_data); + if (ACPI_FAILURE(status)) + goto out; + } + + if ((osc_data->control_query & control_req) != control_req) { status = AE_SUPPORT; goto out; } - control_set = osc_data->control_set | ctrlset; + control_set = osc_data->control_set | control_req; osc_args.capbuf[OSC_QUERY_TYPE] = 0; osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set; osc_args.capbuf[OSC_CONTROL_TYPE] = control_set; - status = acpi_run_osc(handle, &osc_args); + status = acpi_run_osc(handle, &osc_args, &result); if (ACPI_SUCCESS(status)) - osc_data->control_set = control_set; + osc_data->control_set = result; out: mutex_unlock(&pci_acpi_lock); return status; @@ -375,7 +367,7 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) * The string should be the same as root bridge's name * Please look at 'pci_scan_bus_parented' */ - num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus); + num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus); if (num != 2) return -ENODEV; *handle = acpi_get_pci_rootbridge_handle(seg, bus); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 99d867bcf22a..c697f2680856 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -16,6 +16,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/cpu.h> #include "pci.h" /* @@ -48,7 +49,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) subdevice=PCI_ANY_ID, class=0, class_mask=0; unsigned long driver_data=0; int fields=0; - int retval; + int retval=0; fields = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, &subvendor, &subdevice, @@ -58,16 +59,18 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) /* Only accept driver_data values that match an existing id_table entry */ - retval = -EINVAL; - while (ids->vendor || ids->subvendor || ids->class_mask) { - if (driver_data == ids->driver_data) { - retval = 0; - break; + if (ids) { + retval = -EINVAL; + while (ids->vendor || ids->subvendor || ids->class_mask) { + if (driver_data == ids->driver_data) { + retval = 0; + break; + } + ids++; } - ids++; + if (retval) /* No match */ + return retval; } - if (retval) /* No match */ - return retval; dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); if (!dynid) @@ -183,32 +186,43 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, return pci_match_id(drv->id_table, dev); } +struct drv_dev_and_id { + struct pci_driver *drv; + struct pci_dev *dev; + const struct pci_device_id *id; +}; + +static long local_pci_probe(void *_ddi) +{ + struct drv_dev_and_id *ddi = _ddi; + + return ddi->drv->probe(ddi->dev, ddi->id); +} + static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, const struct pci_device_id *id) { - int error; -#ifdef CONFIG_NUMA - /* Execute driver initialization on node where the - device's bus is attached to. This way the driver likely - allocates its local memory on the right node without - any need to change it. */ - struct mempolicy *oldpol; - cpumask_t oldmask = current->cpus_allowed; - int node = dev_to_node(&dev->dev); + int error, node; + struct drv_dev_and_id ddi = { drv, dev, id }; + /* Execute driver initialization on node where the device's + bus is attached to. This way the driver likely allocates + its local memory on the right node without any need to + change it. */ + node = dev_to_node(&dev->dev); if (node >= 0) { + int cpu; node_to_cpumask_ptr(nodecpumask, node); - set_cpus_allowed_ptr(current, nodecpumask); - } - /* And set default memory allocation policy */ - oldpol = current->mempolicy; - current->mempolicy = NULL; /* fall back to system default policy */ -#endif - error = drv->probe(dev, id); -#ifdef CONFIG_NUMA - set_cpus_allowed_ptr(current, &oldmask); - current->mempolicy = oldpol; -#endif + + get_online_cpus(); + cpu = cpumask_any_and(nodecpumask, cpu_online_mask); + if (cpu < nr_cpu_ids) + error = work_on_cpu(cpu, local_pci_probe, &ddi); + else + error = local_pci_probe(&ddi); + put_online_cpus(); + } else + error = local_pci_probe(&ddi); return error; } @@ -300,21 +314,12 @@ static void pci_device_shutdown(struct device *dev) #ifdef CONFIG_PM_SLEEP -static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) -{ - struct pci_driver *drv = pci_dev->driver; - - return drv && (drv->suspend || drv->suspend_late || drv->resume - || drv->resume_early); -} - /* * Default "suspend" method for devices that have no driver provided suspend, - * or not even a driver at all. + * or not even a driver at all (second part). */ -static void pci_default_pm_suspend(struct pci_dev *pci_dev) +static void pci_pm_set_unknown_state(struct pci_dev *pci_dev) { - pci_save_state(pci_dev); /* * mark its power state as "unknown", since we don't know if * e.g. the BIOS will change its device state when we suspend. @@ -325,19 +330,9 @@ static void pci_default_pm_suspend(struct pci_dev *pci_dev) /* * Default "resume" method for devices that have no driver provided resume, - * or not even a driver at all (first part). - */ -static void pci_default_pm_resume_early(struct pci_dev *pci_dev) -{ - /* restore the PCI config space */ - pci_restore_state(pci_dev); -} - -/* - * Default "resume" method for devices that have no driver provided resume, * or not even a driver at all (second part). */ -static int pci_default_pm_resume_late(struct pci_dev *pci_dev) +static int pci_pm_reenable_device(struct pci_dev *pci_dev) { int retval; @@ -363,8 +358,16 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); } else { - pci_default_pm_suspend(pci_dev); + pci_save_state(pci_dev); + /* + * This is for compatibility with existing code with legacy PM + * support. + */ + pci_pm_set_unknown_state(pci_dev); } + + pci_fixup_device(pci_fixup_suspend, pci_dev); + return i; } @@ -381,32 +384,130 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) return i; } +static int pci_legacy_resume_early(struct device *dev) +{ + int error = 0; + struct pci_dev * pci_dev = to_pci_dev(dev); + struct pci_driver * drv = pci_dev->driver; + + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + if (drv && drv->resume_early) + error = drv->resume_early(pci_dev); + return error; +} + static int pci_legacy_resume(struct device *dev) { int error; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; + pci_fixup_device(pci_fixup_resume, pci_dev); + if (drv && drv->resume) { error = drv->resume(pci_dev); } else { - pci_default_pm_resume_early(pci_dev); - error = pci_default_pm_resume_late(pci_dev); + /* restore the PCI config space */ + pci_restore_state(pci_dev); + error = pci_pm_reenable_device(pci_dev); } return error; } -static int pci_legacy_resume_early(struct device *dev) +/* Auxiliary functions used by the new power management framework */ + +static int pci_restore_standard_config(struct pci_dev *pci_dev) { + struct pci_dev *parent = pci_dev->bus->self; int error = 0; - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; - if (drv && drv->resume_early) - error = drv->resume_early(pci_dev); + /* Check if the device's bus is operational */ + if (!parent || parent->current_state == PCI_D0) { + pci_restore_state(pci_dev); + pci_update_current_state(pci_dev, PCI_D0); + } else { + dev_warn(&pci_dev->dev, "unable to restore config, " + "bridge %s in low power state D%d\n", pci_name(parent), + parent->current_state); + pci_dev->current_state = PCI_UNKNOWN; + error = -EAGAIN; + } + return error; } +static bool pci_is_bridge(struct pci_dev *pci_dev) +{ + return !!(pci_dev->subordinate); +} + +static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) +{ + if (pci_restore_standard_config(pci_dev)) + pci_fixup_device(pci_fixup_resume_early, pci_dev); +} + +static int pci_pm_default_resume(struct pci_dev *pci_dev) +{ + /* + * pci_restore_standard_config() should have been called once already, + * but it would have failed if the device's parent bridge had not been + * in power state D0 at that time. Check it and try again if necessary. + */ + if (pci_dev->current_state == PCI_UNKNOWN) { + int error = pci_restore_standard_config(pci_dev); + if (error) + return error; + } + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (!pci_is_bridge(pci_dev)) + pci_enable_wake(pci_dev, PCI_D0, false); + + return pci_pm_reenable_device(pci_dev); +} + +static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) +{ + /* If device is enabled at this point, disable it */ + pci_disable_enabled_device(pci_dev); + /* + * Save state with interrupts enabled, because in principle the bus the + * device is on may be put into a low power state after this code runs. + */ + pci_save_state(pci_dev); +} + +static void pci_pm_default_suspend(struct pci_dev *pci_dev) +{ + pci_pm_default_suspend_generic(pci_dev); + + if (!pci_is_bridge(pci_dev)) + pci_prepare_to_sleep(pci_dev); + + pci_fixup_device(pci_fixup_suspend, pci_dev); +} + +static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) +{ + struct pci_driver *drv = pci_dev->driver; + bool ret = drv && (drv->suspend || drv->suspend_late || drv->resume + || drv->resume_early); + + /* + * Legacy PM support is used by default, so warn if the new framework is + * supported as well. Drivers are supposed to support either the + * former, or the latter, but not both at the same time. + */ + WARN_ON(ret && drv->driver.pm); + + return ret; +} + +/* New power management framework */ + static int pci_pm_prepare(struct device *dev) { struct device_driver *drv = dev->driver; @@ -434,15 +535,16 @@ static int pci_pm_suspend(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->suspend) { - error = drv->pm->suspend(dev); - suspend_report_result(drv->pm->suspend, error); - } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_SUSPEND); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend(dev, PMSG_SUSPEND); + + if (drv && drv->pm && drv->pm->suspend) { + error = drv->pm->suspend(dev); + suspend_report_result(drv->pm->suspend, error); } - pci_fixup_device(pci_fixup_suspend, pci_dev); + + if (!error) + pci_pm_default_suspend(pci_dev); return error; } @@ -453,56 +555,50 @@ static int pci_pm_suspend_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->suspend_noirq) { - error = drv->pm->suspend_noirq(dev); - suspend_report_result(drv->pm->suspend_noirq, error); - } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); - } else { - pci_default_pm_suspend(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend_late(dev, PMSG_SUSPEND); + + if (drv && drv->pm && drv->pm->suspend_noirq) { + error = drv->pm->suspend_noirq(dev); + suspend_report_result(drv->pm->suspend_noirq, error); } + if (!error) + pci_pm_set_unknown_state(pci_dev); + return error; } -static int pci_pm_resume(struct device *dev) +static int pci_pm_resume_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume_early(dev); - if (drv && drv->pm) { - if (drv->pm->resume) - error = drv->pm->resume(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_resume(dev); - } else { - error = pci_default_pm_resume_late(pci_dev); - } + pci_pm_default_resume_noirq(pci_dev); + + if (drv && drv->pm && drv->pm->resume_noirq) + error = drv->pm->resume_noirq(dev); return error; } -static int pci_pm_resume_noirq(struct device *dev) +static int pci_pm_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume(dev); - if (drv && drv->pm) { - if (drv->pm->resume_noirq) - error = drv->pm->resume_noirq(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_resume_early(dev); - } else { - pci_default_pm_resume_early(pci_dev); - } + error = pci_pm_default_resume(pci_dev); + + if (!error && drv && drv->pm && drv->pm->resume) + error = drv->pm->resume(dev); return error; } @@ -524,16 +620,17 @@ static int pci_pm_freeze(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->freeze) { - error = drv->pm->freeze(dev); - suspend_report_result(drv->pm->freeze, error); - } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_FREEZE); - pci_fixup_device(pci_fixup_suspend, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend(dev, PMSG_FREEZE); + + if (drv && drv->pm && drv->pm->freeze) { + error = drv->pm->freeze(dev); + suspend_report_result(drv->pm->freeze, error); } + if (!error) + pci_pm_default_suspend_generic(pci_dev); + return error; } @@ -543,50 +640,50 @@ static int pci_pm_freeze_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->freeze_noirq) { - error = drv->pm->freeze_noirq(dev); - suspend_report_result(drv->pm->freeze_noirq, error); - } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend_late(dev, PMSG_FREEZE); - } else { - pci_default_pm_suspend(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend_late(dev, PMSG_FREEZE); + + if (drv && drv->pm && drv->pm->freeze_noirq) { + error = drv->pm->freeze_noirq(dev); + suspend_report_result(drv->pm->freeze_noirq, error); } + if (!error) + pci_pm_set_unknown_state(pci_dev); + return error; } -static int pci_pm_thaw(struct device *dev) +static int pci_pm_thaw_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->thaw) - error = drv->pm->thaw(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); - error = pci_legacy_resume(dev); - } + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume_early(dev); + + pci_update_current_state(pci_dev, PCI_D0); + + if (drv && drv->pm && drv->pm->thaw_noirq) + error = drv->pm->thaw_noirq(dev); return error; } -static int pci_pm_thaw_noirq(struct device *dev) +static int pci_pm_thaw(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->thaw_noirq) - error = drv->pm->thaw_noirq(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); - error = pci_legacy_resume_early(dev); - } + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume(dev); + + pci_pm_reenable_device(pci_dev); + + if (drv && drv->pm && drv->pm->thaw) + error = drv->pm->thaw(dev); return error; } @@ -597,17 +694,17 @@ static int pci_pm_poweroff(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_suspend, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend(dev, PMSG_HIBERNATE); - if (drv && drv->pm) { - if (drv->pm->poweroff) { - error = drv->pm->poweroff(dev); - suspend_report_result(drv->pm->poweroff, error); - } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_HIBERNATE); + if (drv && drv->pm && drv->pm->poweroff) { + error = drv->pm->poweroff(dev); + suspend_report_result(drv->pm->poweroff, error); } + if (!error) + pci_pm_default_suspend(pci_dev); + return error; } @@ -616,54 +713,47 @@ static int pci_pm_poweroff_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->poweroff_noirq) { - error = drv->pm->poweroff_noirq(dev); - suspend_report_result(drv->pm->poweroff_noirq, error); - } - } else if (pci_has_legacy_pm_support(to_pci_dev(dev))) { - error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE); + if (pci_has_legacy_pm_support(to_pci_dev(dev))) + return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); + + if (drv && drv->pm && drv->pm->poweroff_noirq) { + error = drv->pm->poweroff_noirq(dev); + suspend_report_result(drv->pm->poweroff_noirq, error); } return error; } -static int pci_pm_restore(struct device *dev) +static int pci_pm_restore_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; int error = 0; - if (drv && drv->pm) { - if (drv->pm->restore) - error = drv->pm->restore(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_resume(dev); - } else { - error = pci_default_pm_resume_late(pci_dev); - } - pci_fixup_device(pci_fixup_resume, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume_early(dev); + + pci_pm_default_resume_noirq(pci_dev); + + if (drv && drv->pm && drv->pm->restore_noirq) + error = drv->pm->restore_noirq(dev); return error; } -static int pci_pm_restore_noirq(struct device *dev) +static int pci_pm_restore(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume(dev); - if (drv && drv->pm) { - if (drv->pm->restore_noirq) - error = drv->pm->restore_noirq(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_resume_early(dev); - } else { - pci_default_pm_resume_early(pci_dev); - } - pci_fixup_device(pci_fixup_resume_early, pci_dev); + error = pci_pm_default_resume(pci_dev); + + if (!error && drv && drv->pm && drv->pm->restore) + error = drv->pm->restore(dev); return error; } diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c new file mode 100644 index 000000000000..74fbec0bf6cb --- /dev/null +++ b/drivers/pci/pci-stub.c @@ -0,0 +1,47 @@ +/* pci-stub - simple stub driver to reserve a pci device + * + * Copyright (C) 2008 Red Hat, Inc. + * Author: + * Chris Wright + * + * This work is licensed under the terms of the GNU GPL, version 2. + * + * Usage is simple, allocate a new id to the stub driver and bind the + * device to it. For example: + * + * # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind + * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver + * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub + */ + +#include <linux/module.h> +#include <linux/pci.h> + +static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static struct pci_driver stub_driver = { + .name = "pci-stub", + .id_table = NULL, /* only dynamic id's */ + .probe = pci_stub_probe, +}; + +static int __init pci_stub_init(void) +{ + return pci_register_driver(&stub_driver); +} + +static void __exit pci_stub_exit(void) +{ + pci_unregister_driver(&stub_driver); +} + +module_init(pci_stub_init); +module_exit(pci_stub_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chris Wright <chrisw@sous-sol.org>"); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c88485860a0a..db7ec14fa719 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -58,23 +58,24 @@ static ssize_t broken_parity_status_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - ssize_t consumed = -EINVAL; + unsigned long val; - if ((count > 0) && (*buf == '0' || *buf == '1')) { - pdev->broken_parity_status = *buf == '1' ? 1 : 0; - consumed = count; - } - return consumed; + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + pdev->broken_parity_status = !!val; + + return count; } static ssize_t local_cpus_show(struct device *dev, struct device_attribute *attr, char *buf) { - cpumask_t mask; + const struct cpumask *mask; int len; - mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); - len = cpumask_scnprintf(buf, PAGE_SIZE-2, &mask); + mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); + len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); buf[len++] = '\n'; buf[len] = '\0'; return len; @@ -84,11 +85,11 @@ static ssize_t local_cpus_show(struct device *dev, static ssize_t local_cpulist_show(struct device *dev, struct device_attribute *attr, char *buf) { - cpumask_t mask; + const struct cpumask *mask; int len; - mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); - len = cpulist_scnprintf(buf, PAGE_SIZE-2, &mask); + mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); + len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask); buf[len++] = '\n'; buf[len] = '\0'; return len; @@ -101,11 +102,13 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf) struct pci_dev * pci_dev = to_pci_dev(dev); char * str = buf; int i; - int max = 7; + int max; resource_size_t start, end; if (pci_dev->subordinate) max = DEVICE_COUNT_RESOURCE; + else + max = PCI_BRIDGE_RESOURCES; for (i = 0; i < max; i++) { struct resource *res = &pci_dev->resource[i]; @@ -133,19 +136,23 @@ static ssize_t is_enabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t result = -EINVAL; struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + ssize_t result = strict_strtoul(buf, 0, &val); + + if (result < 0) + return result; /* this can crash the machine when done on the "wrong" device */ if (!capable(CAP_SYS_ADMIN)) - return count; + return -EPERM; - if (*buf == '0') { + if (!val) { if (atomic_read(&pdev->enable_cnt) != 0) pci_disable_device(pdev); else result = -EIO; - } else if (*buf == '1') + } else result = pci_enable_device(pdev); return result < 0 ? result : count; @@ -185,25 +192,28 @@ msi_bus_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; /* bad things may happen if the no_msi flag is changed * while some drivers are loaded */ if (!capable(CAP_SYS_ADMIN)) - return count; + return -EPERM; + /* Maybe pci devices without subordinate busses shouldn't even have this + * attribute in the first place? */ if (!pdev->subordinate) return count; - if (*buf == '0') { - pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; - dev_warn(&pdev->dev, "forced subordinate bus to not support MSI," - " bad things could happen.\n"); - } + /* Is the flag going to change, or keep the value it already had? */ + if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^ + !!val) { + pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI; - if (*buf == '1') { - pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; - dev_warn(&pdev->dev, "forced subordinate bus to support MSI," - " bad things could happen.\n"); + dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI," + " bad things could happen\n", val ? "" : " not"); } return count; @@ -361,55 +371,33 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, } static ssize_t -pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +read_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device, kobj)); - int end; - int ret; if (off > bin_attr->size) count = 0; else if (count > bin_attr->size - off) count = bin_attr->size - off; - end = off + count; - - while (off < end) { - ret = dev->vpd->ops->read(dev, off, end - off, buf); - if (ret < 0) - return ret; - buf += ret; - off += ret; - } - return count; + return pci_read_vpd(dev, off, count, buf); } static ssize_t -pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) +write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device, kobj)); - int end; - int ret; if (off > bin_attr->size) count = 0; else if (count > bin_attr->size - off) count = bin_attr->size - off; - end = off + count; - - while (off < end) { - ret = dev->vpd->ops->write(dev, off, end - off, buf); - if (ret < 0) - return ret; - buf += ret; - off += ret; - } - return count; + return pci_write_vpd(dev, off, count, buf); } #ifdef HAVE_PCI_LEGACY @@ -569,7 +557,7 @@ void pci_remove_legacy_files(struct pci_bus *b) #ifdef HAVE_PCI_MMAP -static int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) +int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) { unsigned long nr, start, size; @@ -620,6 +608,9 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, vma->vm_pgoff += start >> PAGE_SHIFT; mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start)) + return -EINVAL; + return pci_mmap_page_range(pdev, vma, mmap_type, write_combine); } @@ -832,8 +823,8 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) attr->size = dev->vpd->len; attr->attr.name = "vpd"; attr->attr.mode = S_IRUSR | S_IWUSR; - attr->read = pci_read_vpd; - attr->write = pci_write_vpd; + attr->read = read_vpd_attr; + attr->write = write_vpd_attr; retval = sysfs_create_bin_file(&dev->dev.kobj, attr); if (retval) { kfree(dev->vpd->attr); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 061d1ee0046a..c12f6c790698 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -56,6 +56,22 @@ unsigned char pci_bus_max_busnr(struct pci_bus* bus) } EXPORT_SYMBOL_GPL(pci_bus_max_busnr); +#ifdef CONFIG_HAS_IOMEM +void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) +{ + /* + * Make sure the BAR is actually a memory resource, not an IO resource + */ + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { + WARN_ON(1); + return NULL; + } + return ioremap_nocache(pci_resource_start(pdev, bar), + pci_resource_len(pdev, bar)); +} +EXPORT_SYMBOL_GPL(pci_ioremap_bar); +#endif + #if 0 /** * pci_max_busnr - returns maximum PCI bus number @@ -360,25 +376,10 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) static void pci_restore_bars(struct pci_dev *dev) { - int i, numres; - - switch (dev->hdr_type) { - case PCI_HEADER_TYPE_NORMAL: - numres = 6; - break; - case PCI_HEADER_TYPE_BRIDGE: - numres = 2; - break; - case PCI_HEADER_TYPE_CARDBUS: - numres = 1; - break; - default: - /* Should never get here, but just in case... */ - return; - } + int i; - for (i = 0; i < numres; i ++) - pci_update_resource(dev, &dev->resource[i], i); + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) + pci_update_resource(dev, i); } static struct pci_platform_pm_ops *pci_platform_pm; @@ -524,14 +525,17 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) * pci_update_current_state - Read PCI power state of given device from its * PCI PM registers and cache it * @dev: PCI device to handle. + * @state: State to cache in case the device doesn't have the PM capability */ -static void pci_update_current_state(struct pci_dev *dev) +void pci_update_current_state(struct pci_dev *dev, pci_power_t state) { if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); + } else { + dev->current_state = state; } } @@ -574,7 +578,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ int ret = platform_pci_set_power_state(dev, PCI_D0); if (!ret) - pci_update_current_state(dev); + pci_update_current_state(dev, PCI_D0); } /* This device is quirked not to be put into D3, so don't put it in D3 */ @@ -587,7 +591,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* Allow the platform to finalize the transition */ int ret = platform_pci_set_power_state(dev, state); if (!ret) { - pci_update_current_state(dev); + pci_update_current_state(dev, state); error = 0; } } @@ -640,19 +644,14 @@ static int pci_save_pcie_state(struct pci_dev *dev) int pos, i = 0; struct pci_cap_saved_state *save_state; u16 *cap; - int found = 0; pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (pos <= 0) return 0; save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); - if (!save_state) - save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); - else - found = 1; if (!save_state) { - dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n"); + dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); return -ENOMEM; } cap = (u16 *)&save_state->data[0]; @@ -661,9 +660,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); - save_state->cap_nr = PCI_CAP_ID_EXP; - if (!found) - pci_add_saved_cap(dev, save_state); + return 0; } @@ -688,30 +685,21 @@ static void pci_restore_pcie_state(struct pci_dev *dev) static int pci_save_pcix_state(struct pci_dev *dev) { - int pos, i = 0; + int pos; struct pci_cap_saved_state *save_state; - u16 *cap; - int found = 0; pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); if (pos <= 0) return 0; save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); - if (!save_state) - save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); - else - found = 1; if (!save_state) { - dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n"); + dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); return -ENOMEM; } - cap = (u16 *)&save_state->data[0]; - pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]); - save_state->cap_nr = PCI_CAP_ID_PCIX; - if (!found) - pci_add_saved_cap(dev, save_state); + pci_read_config_word(dev, pos + PCI_X_CMD, (u16 *)save_state->data); + return 0; } @@ -982,6 +970,32 @@ void pcim_pin_device(struct pci_dev *pdev) */ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} +static void do_pci_disable_device(struct pci_dev *dev) +{ + u16 pci_command; + + pci_read_config_word(dev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_MASTER) { + pci_command &= ~PCI_COMMAND_MASTER; + pci_write_config_word(dev, PCI_COMMAND, pci_command); + } + + pcibios_disable_device(dev); +} + +/** + * pci_disable_enabled_device - Disable device without updating enable_cnt + * @dev: PCI device to disable + * + * NOTE: This function is a backend of PCI power management routines and is + * not supposed to be called drivers. + */ +void pci_disable_enabled_device(struct pci_dev *dev) +{ + if (atomic_read(&dev->enable_cnt)) + do_pci_disable_device(dev); +} + /** * pci_disable_device - Disable PCI device after use * @dev: PCI device to be disabled @@ -996,7 +1010,6 @@ void pci_disable_device(struct pci_dev *dev) { struct pci_devres *dr; - u16 pci_command; dr = find_pci_dr(dev); if (dr) @@ -1005,14 +1018,9 @@ pci_disable_device(struct pci_dev *dev) if (atomic_sub_return(1, &dev->enable_cnt) != 0) return; - pci_read_config_word(dev, PCI_COMMAND, &pci_command); - if (pci_command & PCI_COMMAND_MASTER) { - pci_command &= ~PCI_COMMAND_MASTER; - pci_write_config_word(dev, PCI_COMMAND, pci_command); - } - dev->is_busmaster = 0; + do_pci_disable_device(dev); - pcibios_disable_device(dev); + dev->is_busmaster = 0; } /** @@ -1107,7 +1115,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) int error = 0; bool pme_done = false; - if (!device_may_wakeup(&dev->dev)) + if (enable && !device_may_wakeup(&dev->dev)) return -EINVAL; /* @@ -1252,14 +1260,15 @@ void pci_pm_init(struct pci_dev *dev) /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); if (!pm) - return; + goto Exit; + /* Check device's ability to generate PME# */ pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", pmc & PCI_PM_CAP_VER_MASK); - return; + goto Exit; } dev->pm_cap = pm; @@ -1298,6 +1307,74 @@ void pci_pm_init(struct pci_dev *dev) } else { dev->pme_support = 0; } + + Exit: + pci_update_current_state(dev, PCI_D0); +} + +/** + * platform_pci_wakeup_init - init platform wakeup if present + * @dev: PCI device + * + * Some devices don't have PCI PM caps but can still generate wakeup + * events through platform methods (like ACPI events). If @dev supports + * platform wakeup events, set the device flag to indicate as much. This + * may be redundant if the device also supports PCI PM caps, but double + * initialization should be safe in that case. + */ +void platform_pci_wakeup_init(struct pci_dev *dev) +{ + if (!platform_pci_can_wakeup(dev)) + return; + + device_set_wakeup_capable(&dev->dev, true); + device_set_wakeup_enable(&dev->dev, false); + platform_pci_sleep_wake(dev, false); +} + +/** + * pci_add_save_buffer - allocate buffer for saving given capability registers + * @dev: the PCI device + * @cap: the capability to allocate the buffer for + * @size: requested size of the buffer + */ +static int pci_add_cap_save_buffer( + struct pci_dev *dev, char cap, unsigned int size) +{ + int pos; + struct pci_cap_saved_state *save_state; + + pos = pci_find_capability(dev, cap); + if (pos <= 0) + return 0; + + save_state = kzalloc(sizeof(*save_state) + size, GFP_KERNEL); + if (!save_state) + return -ENOMEM; + + save_state->cap_nr = cap; + pci_add_saved_cap(dev, save_state); + + return 0; +} + +/** + * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities + * @dev: the PCI device + */ +void pci_allocate_cap_save_buffers(struct pci_dev *dev) +{ + int error; + + error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP, 4 * sizeof(u16)); + if (error) + dev_err(&dev->dev, + "unable to preallocate PCI Express save buffer\n"); + + error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_PCIX, sizeof(u16)); + if (error) + dev_err(&dev->dev, + "unable to preallocate PCI-X save buffer\n"); } /** @@ -1337,6 +1414,20 @@ void pci_enable_ari(struct pci_dev *dev) bridge->ari_enabled = 1; } +/** + * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge + * @dev: the PCI device + * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) + * + * Perform INTx swizzling for a device behind one level of bridge. This is + * required by section 9.1 of the PCI-to-PCI bridge specification for devices + * behind bridges on add-in cards. + */ +u8 pci_swizzle_interrupt_pin(struct pci_dev *dev, u8 pin) +{ + return (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; +} + int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) { @@ -1345,9 +1436,9 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) pin = dev->pin; if (!pin) return -1; - pin--; + while (dev->bus->self) { - pin = (pin + PCI_SLOT(dev->devfn)) % 4; + pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } *bridge = dev; @@ -1355,6 +1446,26 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) } /** + * pci_common_swizzle - swizzle INTx all the way to root bridge + * @dev: the PCI device + * @pinp: pointer to the INTx pin value (1=INTA, 2=INTB, 3=INTD, 4=INTD) + * + * Perform INTx swizzling for a device. This traverses through all PCI-to-PCI + * bridges all the way up to a PCI root bus. + */ +u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) +{ + u8 pin = *pinp; + + while (dev->bus->self) { + pin = pci_swizzle_interrupt_pin(dev, pin); + dev = dev->bus->self; + } + *pinp = pin; + return PCI_SLOT(dev->devfn); +} + +/** * pci_release_region - Release a PCI bar * @pdev: PCI device whose resources were previously reserved by pci_request_region * @bar: BAR to release @@ -1395,7 +1506,8 @@ void pci_release_region(struct pci_dev *pdev, int bar) * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. */ -int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) +static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_name, + int exclusive) { struct pci_devres *dr; @@ -1408,8 +1520,9 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) goto err_out; } else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { - if (!request_mem_region(pci_resource_start(pdev, bar), - pci_resource_len(pdev, bar), res_name)) + if (!__request_mem_region(pci_resource_start(pdev, bar), + pci_resource_len(pdev, bar), res_name, + exclusive)) goto err_out; } @@ -1428,6 +1541,47 @@ err_out: } /** + * pci_request_region - Reserved PCI I/O and memory resource + * @pdev: PCI device whose resources are to be reserved + * @bar: BAR to be reserved + * @res_name: Name to be associated with resource. + * + * Mark the PCI region associated with PCI device @pdev BR @bar as + * being reserved by owner @res_name. Do not access any + * address inside the PCI regions unless this call returns + * successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) +{ + return __pci_request_region(pdev, bar, res_name, 0); +} + +/** + * pci_request_region_exclusive - Reserved PCI I/O and memory resource + * @pdev: PCI device whose resources are to be reserved + * @bar: BAR to be reserved + * @res_name: Name to be associated with resource. + * + * Mark the PCI region associated with PCI device @pdev BR @bar as + * being reserved by owner @res_name. Do not access any + * address inside the PCI regions unless this call returns + * successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + * + * The key difference that _exclusive makes it that userspace is + * explicitly not allowed to map the resource via /dev/mem or + * sysfs. + */ +int pci_request_region_exclusive(struct pci_dev *pdev, int bar, const char *res_name) +{ + return __pci_request_region(pdev, bar, res_name, IORESOURCE_EXCLUSIVE); +} +/** * pci_release_selected_regions - Release selected PCI I/O and memory resources * @pdev: PCI device whose resources were previously reserved * @bars: Bitmask of BARs to be released @@ -1444,20 +1598,14 @@ void pci_release_selected_regions(struct pci_dev *pdev, int bars) pci_release_region(pdev, i); } -/** - * pci_request_selected_regions - Reserve selected PCI I/O and memory resources - * @pdev: PCI device whose resources are to be reserved - * @bars: Bitmask of BARs to be requested - * @res_name: Name to be associated with resource - */ -int pci_request_selected_regions(struct pci_dev *pdev, int bars, - const char *res_name) +int __pci_request_selected_regions(struct pci_dev *pdev, int bars, + const char *res_name, int excl) { int i; for (i = 0; i < 6; i++) if (bars & (1 << i)) - if(pci_request_region(pdev, i, res_name)) + if (__pci_request_region(pdev, i, res_name, excl)) goto err_out; return 0; @@ -1469,6 +1617,26 @@ err_out: return -EBUSY; } + +/** + * pci_request_selected_regions - Reserve selected PCI I/O and memory resources + * @pdev: PCI device whose resources are to be reserved + * @bars: Bitmask of BARs to be requested + * @res_name: Name to be associated with resource + */ +int pci_request_selected_regions(struct pci_dev *pdev, int bars, + const char *res_name) +{ + return __pci_request_selected_regions(pdev, bars, res_name, 0); +} + +int pci_request_selected_regions_exclusive(struct pci_dev *pdev, + int bars, const char *res_name) +{ + return __pci_request_selected_regions(pdev, bars, res_name, + IORESOURCE_EXCLUSIVE); +} + /** * pci_release_regions - Release reserved PCI I/O and memory resources * @pdev: PCI device whose resources were previously reserved by pci_request_regions @@ -1502,27 +1670,66 @@ int pci_request_regions(struct pci_dev *pdev, const char *res_name) } /** + * pci_request_regions_exclusive - Reserved PCI I/O and memory resources + * @pdev: PCI device whose resources are to be reserved + * @res_name: Name to be associated with resource. + * + * Mark all PCI regions associated with PCI device @pdev as + * being reserved by owner @res_name. Do not access any + * address inside the PCI regions unless this call returns + * successfully. + * + * pci_request_regions_exclusive() will mark the region so that + * /dev/mem and the sysfs MMIO access will not be allowed. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) +{ + return pci_request_selected_regions_exclusive(pdev, + ((1 << 6) - 1), res_name); +} + +static void __pci_set_master(struct pci_dev *dev, bool enable) +{ + u16 old_cmd, cmd; + + pci_read_config_word(dev, PCI_COMMAND, &old_cmd); + if (enable) + cmd = old_cmd | PCI_COMMAND_MASTER; + else + cmd = old_cmd & ~PCI_COMMAND_MASTER; + if (cmd != old_cmd) { + dev_dbg(&dev->dev, "%s bus mastering\n", + enable ? "enabling" : "disabling"); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + dev->is_busmaster = enable; +} + +/** * pci_set_master - enables bus-mastering for device dev * @dev: the PCI device to enable * * Enables bus-mastering on the device and calls pcibios_set_master() * to do the needed arch specific settings. */ -void -pci_set_master(struct pci_dev *dev) +void pci_set_master(struct pci_dev *dev) { - u16 cmd; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - if (! (cmd & PCI_COMMAND_MASTER)) { - dev_dbg(&dev->dev, "enabling bus mastering\n"); - cmd |= PCI_COMMAND_MASTER; - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - dev->is_busmaster = 1; + __pci_set_master(dev, true); pcibios_set_master(dev); } +/** + * pci_clear_master - disables bus-mastering for device dev + * @dev: the PCI device to disable + */ +void pci_clear_master(struct pci_dev *dev) +{ + __pci_set_master(dev, false); +} + #ifdef PCI_DISABLE_MWI int pci_set_mwi(struct pci_dev *dev) { @@ -1751,24 +1958,7 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) EXPORT_SYMBOL(pci_set_dma_seg_boundary); #endif -/** - * pci_execute_reset_function() - Reset a PCI device function - * @dev: Device function to reset - * - * Some devices allow an individual function to be reset without affecting - * other functions in the same device. The PCI device must be responsive - * to PCI config space in order to use this function. - * - * The device function is presumed to be unused when this function is called. - * Resetting the device will make the contents of PCI configuration space - * random, so any caller of this must be prepared to reinitialise the - * device including MSI, bus mastering, BARs, decoding IO and memory spaces, - * etc. - * - * Returns 0 if the device function was successfully reset or -ENOTTY if the - * device doesn't support resetting a single function. - */ -int pci_execute_reset_function(struct pci_dev *dev) +static int __pcie_flr(struct pci_dev *dev, int probe) { u16 status; u32 cap; @@ -1780,6 +1970,9 @@ int pci_execute_reset_function(struct pci_dev *dev) if (!(cap & PCI_EXP_DEVCAP_FLR)) return -ENOTTY; + if (probe) + return 0; + pci_block_user_cfg_access(dev); /* Wait for Transaction Pending bit clean */ @@ -1802,6 +1995,80 @@ int pci_execute_reset_function(struct pci_dev *dev) pci_unblock_user_cfg_access(dev); return 0; } + +static int __pci_af_flr(struct pci_dev *dev, int probe) +{ + int cappos = pci_find_capability(dev, PCI_CAP_ID_AF); + u8 status; + u8 cap; + + if (!cappos) + return -ENOTTY; + pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap); + if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) + return -ENOTTY; + + if (probe) + return 0; + + pci_block_user_cfg_access(dev); + + /* Wait for Transaction Pending bit clean */ + msleep(100); + pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); + if (status & PCI_AF_STATUS_TP) { + dev_info(&dev->dev, "Busy after 100ms while trying to" + " reset; sleeping for 1 second\n"); + ssleep(1); + pci_read_config_byte(dev, + cappos + PCI_AF_STATUS, &status); + if (status & PCI_AF_STATUS_TP) + dev_info(&dev->dev, "Still busy after 1s; " + "proceeding with reset anyway\n"); + } + pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); + mdelay(100); + + pci_unblock_user_cfg_access(dev); + return 0; +} + +static int __pci_reset_function(struct pci_dev *pdev, int probe) +{ + int res; + + res = __pcie_flr(pdev, probe); + if (res != -ENOTTY) + return res; + + res = __pci_af_flr(pdev, probe); + if (res != -ENOTTY) + return res; + + return res; +} + +/** + * pci_execute_reset_function() - Reset a PCI device function + * @dev: Device function to reset + * + * Some devices allow an individual function to be reset without affecting + * other functions in the same device. The PCI device must be responsive + * to PCI config space in order to use this function. + * + * The device function is presumed to be unused when this function is called. + * Resetting the device will make the contents of PCI configuration space + * random, so any caller of this must be prepared to reinitialise the + * device including MSI, bus mastering, BARs, decoding IO and memory spaces, + * etc. + * + * Returns 0 if the device function was successfully reset or -ENOTTY if the + * device doesn't support resetting a single function. + */ +int pci_execute_reset_function(struct pci_dev *dev) +{ + return __pci_reset_function(dev, 0); +} EXPORT_SYMBOL_GPL(pci_execute_reset_function); /** @@ -1822,15 +2089,10 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function); */ int pci_reset_function(struct pci_dev *dev) { - u32 cap; - int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); - int r; + int r = __pci_reset_function(dev, 1); - if (!exppos) - return -ENOTTY; - pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); - if (!(cap & PCI_EXP_DEVCAP_FLR)) - return -ENOTTY; + if (r < 0) + return r; if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) disable_irq(dev->irq); @@ -2022,6 +2284,28 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags) return bars; } +/** + * pci_resource_bar - get position of the BAR associated with a resource + * @dev: the PCI device + * @resno: the resource number + * @type: the BAR type to be filled in + * + * Returns BAR position in config space, or 0 if the BAR is invalid. + */ +int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) +{ + if (resno < PCI_ROM_RESOURCE) { + *type = pci_bar_unknown; + return PCI_BASE_ADDRESS_0 + 4 * resno; + } else if (resno == PCI_ROM_RESOURCE) { + *type = pci_bar_mem32; + return dev->rom_base_reg; + } + + dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno); + return 0; +} + static void __devinit pci_no_domains(void) { #ifdef CONFIG_PCI_DOMAINS @@ -2029,6 +2313,19 @@ static void __devinit pci_no_domains(void) #endif } +/** + * pci_ext_cfg_enabled - can we access extended PCI config space? + * @dev: The PCI device of the root bridge. + * + * Returns 1 if we can access PCI extended config space (offsets + * greater than 0xff). This is the default implementation. Architecture + * implementations can override this. + */ +int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) +{ + return 1; +} + static int __devinit pci_init(void) { struct pci_dev *dev = NULL; @@ -2037,8 +2334,6 @@ static int __devinit pci_init(void) pci_fixup_device(pci_fixup_final, dev); } - msi_init(); - return 0; } @@ -2083,11 +2378,15 @@ EXPORT_SYMBOL(pci_find_capability); EXPORT_SYMBOL(pci_bus_find_capability); EXPORT_SYMBOL(pci_release_regions); EXPORT_SYMBOL(pci_request_regions); +EXPORT_SYMBOL(pci_request_regions_exclusive); EXPORT_SYMBOL(pci_release_region); EXPORT_SYMBOL(pci_request_region); +EXPORT_SYMBOL(pci_request_region_exclusive); EXPORT_SYMBOL(pci_release_selected_regions); EXPORT_SYMBOL(pci_request_selected_regions); +EXPORT_SYMBOL(pci_request_selected_regions_exclusive); EXPORT_SYMBOL(pci_set_master); +EXPORT_SYMBOL(pci_clear_master); EXPORT_SYMBOL(pci_set_mwi); EXPORT_SYMBOL(pci_try_set_mwi); EXPORT_SYMBOL(pci_clear_mwi); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9de87e9f98f5..1351bb4addde 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -10,6 +10,10 @@ extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env); extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev); extern void pci_cleanup_rom(struct pci_dev *dev); +#ifdef HAVE_PCI_MMAP +extern int pci_mmap_fits(struct pci_dev *pdev, int resno, + struct vm_area_struct *vma); +#endif /** * Firmware PM callbacks @@ -40,7 +44,11 @@ struct pci_platform_pm_ops { }; extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); +extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); +extern void pci_disable_enabled_device(struct pci_dev *dev); extern void pci_pm_init(struct pci_dev *dev); +extern void platform_pci_wakeup_init(struct pci_dev *dev); +extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); @@ -50,14 +58,14 @@ extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { - int (*read)(struct pci_dev *dev, int pos, int size, char *buf); - int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); + ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); + ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); void (*release)(struct pci_dev *dev); }; struct pci_vpd { unsigned int len; - struct pci_vpd_ops *ops; + const struct pci_vpd_ops *ops; struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; @@ -98,11 +106,9 @@ extern unsigned int pci_pm_d3_delay; #ifdef CONFIG_PCI_MSI void pci_no_msi(void); extern void pci_msi_init_pci_dev(struct pci_dev *dev); -extern void __devinit msi_init(void); #else static inline void pci_no_msi(void) { } static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } -static inline void msi_init(void) { } #endif #ifdef CONFIG_PCIEAER @@ -159,16 +165,28 @@ struct pci_slot_attribute { }; #define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr) +enum pci_bar_type { + pci_bar_unknown, /* Standard PCI BAR probe */ + pci_bar_io, /* An io port BAR */ + pci_bar_mem32, /* A 32-bit memory BAR */ + pci_bar_mem64, /* A 64-bit memory BAR */ +}; + +extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, + struct resource *res, unsigned int reg); +extern int pci_resource_bar(struct pci_dev *dev, int resno, + enum pci_bar_type *type); +extern int pci_bus_add_child(struct pci_bus *bus); extern void pci_enable_ari(struct pci_dev *dev); /** * pci_ari_enabled - query ARI forwarding status - * @dev: the PCI device + * @bus: the PCI bus * * Returns 1 if ARI forwarding is enabled, or 0 if not enabled; */ -static inline int pci_ari_enabled(struct pci_dev *dev) +static inline int pci_ari_enabled(struct pci_bus *bus) { - return dev->ari_enabled; + return bus->self && bus->self->ari_enabled; } #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 6dd7b13e9808..ebce26c37049 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -38,7 +38,6 @@ int aer_osc_setup(struct pcie_device *pciedev) handle = acpi_find_root_bridge_handle(pdev); if (handle) { - pcie_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT); status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 3933d4f30e8c..0fc29ae80df8 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -233,7 +233,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) if (info->flags & AER_TLP_HEADER_VALID_FLAG) { unsigned char *tlp = (unsigned char *) &info->tlp; - printk("%sTLB Header:\n", loglevel); + printk("%sTLP Header:\n", loglevel); printk("%s%02x%02x%02x%02x %02x%02x%02x%02x" " %02x%02x%02x%02x %02x%02x%02x%02x\n", loglevel, diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 9aad608bcf3f..586b6f75910d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> +#include <linux/delay.h> #include <linux/pci-aspm.h> #include "../pci.h" @@ -33,6 +34,11 @@ struct endpoint_state { struct pcie_link_state { struct list_head sibiling; struct pci_dev *pdev; + bool downstream_has_switch; + + struct pcie_link_state *parent; + struct list_head children; + struct list_head link; /* ASPM state */ unsigned int support_state; @@ -70,6 +76,8 @@ static const char *policy_str[] = { [POLICY_POWERSAVE] = "powersave" }; +#define LINK_RETRAIN_TIMEOUT HZ + static int policy_to_aspm_state(struct pci_dev *pdev) { struct pcie_link_state *link_state = pdev->link_state; @@ -125,7 +133,7 @@ static void pcie_set_clock_pm(struct pci_dev *pdev, int enable) link_state->clk_pm_enabled = !!enable; } -static void pcie_check_clock_pm(struct pci_dev *pdev) +static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist) { int pos; u32 reg32; @@ -149,10 +157,26 @@ static void pcie_check_clock_pm(struct pci_dev *pdev) if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN)) enabled = 0; } - link_state->clk_pm_capable = capable; link_state->clk_pm_enabled = enabled; link_state->bios_clk_state = enabled; - pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); + if (!blacklist) { + link_state->clk_pm_capable = capable; + pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); + } else { + link_state->clk_pm_capable = 0; + pcie_set_clock_pm(pdev, 0); + } +} + +static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev) +{ + struct pci_dev *child_dev; + + list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { + if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) + return true; + } + return false; } /* @@ -217,16 +241,18 @@ static void pcie_aspm_configure_common_clock(struct pci_dev *pdev) pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); /* Wait for link training end */ - /* break out after waiting for 1 second */ + /* break out after waiting for timeout */ start_jiffies = jiffies; - while ((jiffies - start_jiffies) < HZ) { + for (;;) { pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, ®16); if (!(reg16 & PCI_EXP_LNKSTA_LT)) break; - cpu_relax(); + if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) + break; + msleep(1); } /* training failed -> recover */ - if ((jiffies - start_jiffies) >= HZ) { + if (reg16 & PCI_EXP_LNKSTA_LT) { dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure" " common clock\n"); i = 0; @@ -419,9 +445,9 @@ static unsigned int pcie_aspm_check_state(struct pci_dev *pdev, { struct pci_dev *child_dev; - /* If no child, disable the link */ + /* If no child, ignore the link */ if (list_empty(&pdev->subordinate->devices)) - return 0; + return state; list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { /* @@ -462,6 +488,9 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state) int valid = 1; struct pcie_link_state *link_state = pdev->link_state; + /* If no child, disable the link */ + if (list_empty(&pdev->subordinate->devices)) + state = 0; /* * if the downstream component has pci bridge function, don't do ASPM * now @@ -493,20 +522,52 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state) link_state->enabled_state = state; } +static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link) +{ + struct pcie_link_state *root_port_link = link; + while (root_port_link->parent) + root_port_link = root_port_link->parent; + return root_port_link; +} + +/* check the whole hierarchy, and configure each link in the hierarchy */ static void __pcie_aspm_configure_link_state(struct pci_dev *pdev, unsigned int state) { struct pcie_link_state *link_state = pdev->link_state; + struct pcie_link_state *root_port_link = get_root_port_link(link_state); + struct pcie_link_state *leaf; - if (link_state->support_state == 0) - return; state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; - /* state 0 means disabling aspm */ - state = pcie_aspm_check_state(pdev, state); + /* check all links who have specific root port link */ + list_for_each_entry(leaf, &link_list, sibiling) { + if (!list_empty(&leaf->children) || + get_root_port_link(leaf) != root_port_link) + continue; + state = pcie_aspm_check_state(leaf->pdev, state); + } + /* check root port link too in case it hasn't children */ + state = pcie_aspm_check_state(root_port_link->pdev, state); + if (link_state->enabled_state == state) return; - __pcie_aspm_config_link(pdev, state); + + /* + * we must change the hierarchy. See comments in + * __pcie_aspm_config_link for the order + **/ + if (state & PCIE_LINK_STATE_L1) { + list_for_each_entry(leaf, &link_list, sibiling) { + if (get_root_port_link(leaf) == root_port_link) + __pcie_aspm_config_link(leaf->pdev, state); + } + } else { + list_for_each_entry_reverse(leaf, &link_list, sibiling) { + if (get_root_port_link(leaf) == root_port_link) + __pcie_aspm_config_link(leaf->pdev, state); + } + } } /* @@ -570,6 +631,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) unsigned int state; struct pcie_link_state *link_state; int error = 0; + int blacklist; if (aspm_disabled || !pdev->is_pcie || pdev->link_state) return; @@ -580,29 +642,58 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) if (list_empty(&pdev->subordinate->devices)) goto out; - if (pcie_aspm_sanity_check(pdev)) - goto out; + blacklist = !!pcie_aspm_sanity_check(pdev); mutex_lock(&aspm_lock); link_state = kzalloc(sizeof(*link_state), GFP_KERNEL); if (!link_state) goto unlock_out; - pdev->link_state = link_state; - pcie_aspm_configure_common_clock(pdev); + link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev); + INIT_LIST_HEAD(&link_state->children); + INIT_LIST_HEAD(&link_state->link); + if (pdev->bus->self) {/* this is a switch */ + struct pcie_link_state *parent_link_state; - pcie_aspm_cap_init(pdev); + parent_link_state = pdev->bus->parent->self->link_state; + if (!parent_link_state) { + kfree(link_state); + goto unlock_out; + } + list_add(&link_state->link, &parent_link_state->children); + link_state->parent = parent_link_state; + } - /* config link state to avoid BIOS error */ - state = pcie_aspm_check_state(pdev, policy_to_aspm_state(pdev)); - __pcie_aspm_config_link(pdev, state); + pdev->link_state = link_state; - pcie_check_clock_pm(pdev); + if (!blacklist) { + pcie_aspm_configure_common_clock(pdev); + pcie_aspm_cap_init(pdev); + } else { + link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; + link_state->bios_aspm_state = 0; + /* Set support state to 0, so we will disable ASPM later */ + link_state->support_state = 0; + } link_state->pdev = pdev; list_add(&link_state->sibiling, &link_list); + if (link_state->downstream_has_switch) { + /* + * If link has switch, delay the link config. The leaf link + * initialization will config the whole hierarchy. but we must + * make sure BIOS doesn't set unsupported link state + **/ + state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state); + __pcie_aspm_config_link(pdev, state); + } else + __pcie_aspm_configure_link_state(pdev, + policy_to_aspm_state(pdev)); + + pcie_check_clock_pm(pdev, blacklist); + unlock_out: if (error) free_link_state(pdev); @@ -635,6 +726,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) /* All functions are removed, so just disable ASPM for the link */ __pcie_aspm_config_one_dev(parent, 0); list_del(&link_state->sibiling); + list_del(&link_state->link); /* Clock PM is for endpoint device */ free_link_state(parent); @@ -857,24 +949,15 @@ void pcie_no_aspm(void) aspm_disabled = 1; } -#ifdef CONFIG_ACPI -#include <acpi/acpi_bus.h> -#include <linux/pci-acpi.h> -static void pcie_aspm_platform_init(void) -{ - pcie_osc_support_set(OSC_ACTIVE_STATE_PWR_SUPPORT| - OSC_CLOCK_PWR_CAPABILITY_SUPPORT); -} -#else -static inline void pcie_aspm_platform_init(void) { } -#endif - -static int __init pcie_aspm_init(void) +/** + * pcie_aspm_enabled - is PCIe ASPM enabled? + * + * Returns true if ASPM has not been disabled by the command-line option + * pcie_aspm=off. + **/ +int pcie_aspm_enabled(void) { - if (aspm_disabled) - return 0; - pcie_aspm_platform_init(); - return 0; + return !aspm_disabled; } +EXPORT_SYMBOL(pcie_aspm_enabled); -fs_initcall(pcie_aspm_init); diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index 359fe5568df1..eec89b767f9f 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -16,14 +16,10 @@ #include "portdrv.h" static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); -static int pcie_port_bus_suspend(struct device *dev, pm_message_t state); -static int pcie_port_bus_resume(struct device *dev); struct bus_type pcie_port_bus_type = { .name = "pci_express", .match = pcie_port_bus_match, - .suspend = pcie_port_bus_suspend, - .resume = pcie_port_bus_resume, }; EXPORT_SYMBOL_GPL(pcie_port_bus_type); @@ -49,32 +45,12 @@ static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) return 1; } -static int pcie_port_bus_suspend(struct device *dev, pm_message_t state) +int pcie_port_bus_register(void) { - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->suspend) - driver->suspend(pciedev, state); - return 0; + return bus_register(&pcie_port_bus_type); } -static int pcie_port_bus_resume(struct device *dev) +void pcie_port_bus_unregister(void) { - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->resume) - driver->resume(pciedev); - return 0; + bus_unregister(&pcie_port_bus_type); } diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 2e091e014829..8b3f8c18032f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -19,91 +19,15 @@ extern int pcie_mch_quirk; /* MSI-quirk Indicator */ -static int pcie_port_probe_service(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - int status; - - if (!dev || !dev->driver) - return -ENODEV; - - driver = to_service_driver(dev->driver); - if (!driver || !driver->probe) - return -ENODEV; - - pciedev = to_pcie_device(dev); - status = driver->probe(pciedev, driver->id_table); - if (!status) { - dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", - driver->name); - get_device(dev); - } - return status; -} - -static int pcie_port_remove_service(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->remove) { - dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n", - driver->name); - driver->remove(pciedev); - put_device(dev); - } - return 0; -} - -static void pcie_port_shutdown_service(struct device *dev) {} - -static int pcie_port_suspend_service(struct device *dev, pm_message_t state) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->suspend) - driver->suspend(pciedev, state); - return 0; -} - -static int pcie_port_resume_service(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - - if (driver && driver->resume) - driver->resume(pciedev); - return 0; -} - -/* - * release_pcie_device - * - * Being invoked automatically when device is being removed - * in response to device_unregister(dev) call. - * Release all resources being claimed. +/** + * release_pcie_device - free PCI Express port service device structure + * @dev: Port service device to release + * + * Invoked automatically when device is being removed in response to + * device_unregister(dev). Release all resources being claimed. */ static void release_pcie_device(struct device *dev) { - dev_printk(KERN_DEBUG, dev, "free port service\n"); kfree(to_pcie_device(dev)); } @@ -128,7 +52,16 @@ static int is_msi_quirked(struct pci_dev *dev) } return quirk; } - + +/** + * assign_interrupt_mode - choose interrupt mode for PCI Express port services + * (INTx, MSI-X, MSI) and set up vectors + * @dev: PCI Express port to handle + * @vectors: Array of interrupt vectors to populate + * @mask: Bitmask of port capabilities returned by get_port_device_capability() + * + * Return value: Interrupt mode associated with the port + */ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { int i, pos, nvec, status = -EINVAL; @@ -150,7 +83,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) if (pos) { struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}}; - dev_info(&dev->dev, "found MSI-X capability\n"); status = pci_enable_msix(dev, msix_entries, nvec); if (!status) { int j = 0; @@ -165,7 +97,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) if (status) { pos = pci_find_capability(dev, PCI_CAP_ID_MSI); if (pos) { - dev_info(&dev->dev, "found MSI capability\n"); status = pci_enable_msi(dev); if (!status) { interrupt_mode = PCIE_PORT_MSI_MODE; @@ -177,6 +108,16 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) return interrupt_mode; } +/** + * get_port_device_capability - discover capabilities of a PCI Express port + * @dev: PCI Express port to examine + * + * The capabilities are read from the port's PCI Express configuration registers + * as described in PCI Express Base Specification 1.0a sections 7.8.2, 7.8.9 and + * 7.9 - 7.11. + * + * Return value: Bitmask of discovered port capabilities + */ static int get_port_device_capability(struct pci_dev *dev) { int services = 0, pos; @@ -204,6 +145,15 @@ static int get_port_device_capability(struct pci_dev *dev) return services; } +/** + * pcie_device_init - initialize PCI Express port service device + * @dev: Port service device to initialize + * @parent: PCI Express port to associate the service device with + * @port_type: Type of the port + * @service_type: Type of service to associate with the service device + * @irq: Interrupt vector to associate with the service device + * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) + */ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, int port_type, int service_type, int irq, int irq_mode) { @@ -224,11 +174,19 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, device->driver = NULL; device->driver_data = NULL; device->release = release_pcie_device; /* callback to free pcie dev */ - snprintf(device->bus_id, sizeof(device->bus_id), "%s:pcie%02x", + dev_set_name(device, "%s:pcie%02x", pci_name(parent), get_descriptor_id(port_type, service_type)); device->parent = &parent->dev; } +/** + * alloc_pcie_device - allocate PCI Express port service device structure + * @parent: PCI Express port to associate the service device with + * @port_type: Type of the port + * @service_type: Type of service to associate with the service device + * @irq: Interrupt vector to associate with the service device + * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) + */ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, int port_type, int service_type, int irq, int irq_mode) { @@ -239,10 +197,13 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, return NULL; pcie_device_init(parent, device, port_type, service_type, irq,irq_mode); - dev_printk(KERN_DEBUG, &device->device, "allocate port service\n"); return device; } +/** + * pcie_port_device_probe - check if device is a PCI Express port + * @dev: Device to check + */ int pcie_port_device_probe(struct pci_dev *dev) { int pos, type; @@ -260,6 +221,13 @@ int pcie_port_device_probe(struct pci_dev *dev) return -ENODEV; } +/** + * pcie_port_device_register - register PCI Express port + * @dev: PCI Express port to register + * + * Allocate the port extension structure and register services associated with + * the port. + */ int pcie_port_device_register(struct pci_dev *dev) { struct pcie_port_device_ext *p_ext; @@ -323,6 +291,11 @@ static int suspend_iter(struct device *dev, void *data) return 0; } +/** + * pcie_port_device_suspend - suspend port services associated with a PCIe port + * @dev: PCI Express port to handle + * @state: Representation of system power management transition in progress + */ int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) { return device_for_each_child(&dev->dev, &state, suspend_iter); @@ -341,6 +314,10 @@ static int resume_iter(struct device *dev, void *data) return 0; } +/** + * pcie_port_device_suspend - resume port services associated with a PCIe port + * @dev: PCI Express port to handle + */ int pcie_port_device_resume(struct pci_dev *dev) { return device_for_each_child(&dev->dev, NULL, resume_iter); @@ -363,6 +340,13 @@ static int remove_iter(struct device *dev, void *data) return 0; } +/** + * pcie_port_device_remove - unregister PCI Express port service devices + * @dev: PCI Express port the service devices to unregister are associated with + * + * Remove PCI Express port service devices associated with given port and + * disable MSI-X or MSI for the port. + */ void pcie_port_device_remove(struct pci_dev *dev) { struct device *device; @@ -386,16 +370,80 @@ void pcie_port_device_remove(struct pci_dev *dev) pci_disable_msi(dev); } -int pcie_port_bus_register(void) +/** + * pcie_port_probe_service - probe driver for given PCI Express port service + * @dev: PCI Express port service device to probe against + * + * If PCI Express port service driver is registered with + * pcie_port_service_register(), this function will be called by the driver core + * whenever match is found between the driver and a port service device. + */ +static int pcie_port_probe_service(struct device *dev) { - return bus_register(&pcie_port_bus_type); + struct pcie_device *pciedev; + struct pcie_port_service_driver *driver; + int status; + + if (!dev || !dev->driver) + return -ENODEV; + + driver = to_service_driver(dev->driver); + if (!driver || !driver->probe) + return -ENODEV; + + pciedev = to_pcie_device(dev); + status = driver->probe(pciedev, driver->id_table); + if (!status) { + dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", + driver->name); + get_device(dev); + } + return status; } -void pcie_port_bus_unregister(void) +/** + * pcie_port_remove_service - detach driver from given PCI Express port service + * @dev: PCI Express port service device to handle + * + * If PCI Express port service driver is registered with + * pcie_port_service_register(), this function will be called by the driver core + * when device_unregister() is called for the port service device associated + * with the driver. + */ +static int pcie_port_remove_service(struct device *dev) { - bus_unregister(&pcie_port_bus_type); + struct pcie_device *pciedev; + struct pcie_port_service_driver *driver; + + if (!dev || !dev->driver) + return 0; + + pciedev = to_pcie_device(dev); + driver = to_service_driver(dev->driver); + if (driver && driver->remove) { + dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n", + driver->name); + driver->remove(pciedev); + put_device(dev); + } + return 0; } +/** + * pcie_port_shutdown_service - shut down given PCI Express port service + * @dev: PCI Express port service device to handle + * + * If PCI Express port service driver is registered with + * pcie_port_service_register(), this function will be called by the driver core + * when device_shutdown() is called for the port service device associated + * with the driver. + */ +static void pcie_port_shutdown_service(struct device *dev) {} + +/** + * pcie_port_service_register - register PCI Express port service driver + * @new: PCI Express port service driver to register + */ int pcie_port_service_register(struct pcie_port_service_driver *new) { new->driver.name = (char *)new->name; @@ -403,15 +451,17 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) new->driver.probe = pcie_port_probe_service; new->driver.remove = pcie_port_remove_service; new->driver.shutdown = pcie_port_shutdown_service; - new->driver.suspend = pcie_port_suspend_service; - new->driver.resume = pcie_port_resume_service; return driver_register(&new->driver); } -void pcie_port_service_unregister(struct pcie_port_service_driver *new) +/** + * pcie_port_service_unregister - unregister PCI Express port service driver + * @drv: PCI Express port service driver to unregister + */ +void pcie_port_service_unregister(struct pcie_port_service_driver *drv) { - driver_unregister(&new->driver); + driver_unregister(&drv->driver); } EXPORT_SYMBOL(pcie_port_service_register); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 584422da8d8b..99a914a027f8 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -41,7 +41,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) { int retval; - pci_restore_state(dev); retval = pci_enable_device(dev); if (retval) return retval; @@ -52,11 +51,18 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) #ifdef CONFIG_PM static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) { - int ret = pcie_port_device_suspend(dev, state); + return pcie_port_device_suspend(dev, state); - if (!ret) - ret = pcie_portdrv_save_config(dev); - return ret; +} + +static int pcie_portdrv_suspend_late(struct pci_dev *dev, pm_message_t state) +{ + return pci_save_state(dev); +} + +static int pcie_portdrv_resume_early(struct pci_dev *dev) +{ + return pci_restore_state(dev); } static int pcie_portdrv_resume(struct pci_dev *dev) @@ -66,6 +72,8 @@ static int pcie_portdrv_resume(struct pci_dev *dev) } #else #define pcie_portdrv_suspend NULL +#define pcie_portdrv_suspend_late NULL +#define pcie_portdrv_resume_early NULL #define pcie_portdrv_resume NULL #endif @@ -221,6 +229,7 @@ static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) /* If fatal, restore cfg space for possible link reset at upstream */ if (dev->error_state == pci_channel_io_frozen) { + pci_restore_state(dev); pcie_portdrv_restore_config(dev); pci_enable_pcie_error_reporting(dev); } @@ -283,6 +292,8 @@ static struct pci_driver pcie_portdriver = { .remove = pcie_portdrv_remove, .suspend = pcie_portdrv_suspend, + .suspend_late = pcie_portdrv_suspend_late, + .resume_early = pcie_portdrv_resume_early, .resume = pcie_portdrv_resume, .err_handler = &pcie_portdrv_err_handler, diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5b3f5937ecf5..55ec44a27e89 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -51,12 +51,12 @@ static ssize_t pci_bus_show_cpuaffinity(struct device *dev, char *buf) { int ret; - cpumask_t cpumask; + const struct cpumask *cpumask; - cpumask = pcibus_to_cpumask(to_pci_bus(dev)); + cpumask = cpumask_of_pcibus(to_pci_bus(dev)); ret = type? - cpulist_scnprintf(buf, PAGE_SIZE-2, &cpumask) : - cpumask_scnprintf(buf, PAGE_SIZE-2, &cpumask); + cpulist_scnprintf(buf, PAGE_SIZE-2, cpumask) : + cpumask_scnprintf(buf, PAGE_SIZE-2, cpumask); buf[ret++] = '\n'; buf[ret] = '\0'; return ret; @@ -135,13 +135,6 @@ static u64 pci_size(u64 base, u64 maxbase, u64 mask) return size; } -enum pci_bar_type { - pci_bar_unknown, /* Standard PCI BAR probe */ - pci_bar_io, /* An io port BAR */ - pci_bar_mem32, /* A 32-bit memory BAR */ - pci_bar_mem64, /* A 64-bit memory BAR */ -}; - static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar) { if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { @@ -156,11 +149,16 @@ static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar) return pci_bar_mem32; } -/* - * If the type is not unknown, we assume that the lowest bit is 'enable'. - * Returns 1 if the BAR was 64-bit and 0 if it was 32-bit. +/** + * pci_read_base - read a PCI BAR + * @dev: the PCI device + * @type: type of the BAR + * @res: resource buffer to be filled in + * @pos: BAR position in the config space + * + * Returns 1 if the BAR is 64-bit, or 0 if 32-bit. */ -static int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, +int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int pos) { u32 l, sz, mask; @@ -400,19 +398,17 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, if (!child) return NULL; - child->self = bridge; child->parent = parent; child->ops = parent->ops; child->sysdata = parent->sysdata; child->bus_flags = parent->bus_flags; - child->bridge = get_device(&bridge->dev); /* initialize some portions of the bus device, but don't register it * now as the parent is not properly set up yet. This device will get * registered later in pci_bus_add_devices() */ child->dev.class = &pcibus_class; - sprintf(child->dev.bus_id, "%04x:%02x", pci_domain_nr(child), busnr); + dev_set_name(&child->dev, "%04x:%02x", pci_domain_nr(child), busnr); /* * Set up the primary, secondary and subordinate @@ -422,8 +418,14 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->primary = parent->secondary; child->subordinate = 0xff; + if (!bridge) + return child; + + child->self = bridge; + child->bridge = get_device(&bridge->dev); + /* Set up default resource pointers and names.. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; child->resource[i]->name = child->name; } @@ -958,8 +960,12 @@ static void pci_init_capabilities(struct pci_dev *dev) /* MSI/MSI-X list */ pci_msi_init_pci_dev(dev); + /* Buffers for saving PCIe and PCI-X capabilities */ + pci_allocate_cap_save_buffers(dev); + /* Power Management */ pci_pm_init(dev); + platform_pci_wakeup_init(dev); /* Vital Product Data */ pci_vpd_pci22_init(dev); @@ -1130,7 +1136,7 @@ struct pci_bus * pci_create_bus(struct device *parent, memset(dev, 0, sizeof(*dev)); dev->parent = parent; dev->release = pci_release_bus_bridge_dev; - sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus); + dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); error = device_register(dev); if (error) goto dev_reg_err; @@ -1141,7 +1147,7 @@ struct pci_bus * pci_create_bus(struct device *parent, b->dev.class = &pcibus_class; b->dev.parent = b->bridge; - sprintf(b->dev.bus_id, "%04x:%02x", pci_domain_nr(b), bus); + dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus); error = device_register(&b->dev); if (error) goto class_dev_reg_err; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index e1098c302c45..593bb844b8db 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -252,11 +252,20 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) const struct proc_dir_entry *dp = PDE(inode); struct pci_dev *dev = dp->data; struct pci_filp_private *fpriv = file->private_data; - int ret; + int i, ret; if (!capable(CAP_SYS_RAWIO)) return -EPERM; + /* Make sure the caller is mapping a real resource for this device */ + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + if (pci_mmap_fits(dev, i, vma)) + break; + } + + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + ret = pci_mmap_page_range(dev, vma, fpriv->mmap_state, fpriv->write_combine); @@ -352,15 +361,16 @@ static int show_device(struct seq_file *m, void *v) dev->vendor, dev->device, dev->irq); - /* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */ - for (i=0; i<7; i++) { + + /* only print standard and ROM resources to preserve compatibility */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { resource_size_t start, end; pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); seq_printf(m, "\t%16llx", (unsigned long long)(start | (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); } - for (i=0; i<7; i++) { + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { resource_size_t start, end; pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); seq_printf(m, "\t%16llx", diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ce0985615133..baad093aafe3 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -56,7 +56,7 @@ static void quirk_passive_release(struct pci_dev *dev) while ((d = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) { pci_read_config_byte(d, 0x82, &dlc); if (!(dlc & 1<<1)) { - dev_err(&d->dev, "PIIX3: Enabling Passive Release\n"); + dev_info(&d->dev, "PIIX3: Enabling Passive Release\n"); dlc |= 1<<1; pci_write_config_byte(d, 0x82, dlc); } @@ -449,7 +449,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, quirk_ich4_lpc_acpi); -static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev) +static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) { u32 region; @@ -459,20 +459,95 @@ static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev) pci_read_config_dword(dev, 0x48, ®ion); quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO"); } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich6_lpc_acpi); + +static void __devinit ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) +{ + u32 val; + u32 size, base; + + pci_read_config_dword(dev, reg, &val); + + /* Enabled? */ + if (!(val & 1)) + return; + base = val & 0xfffc; + if (dynsize) { + /* + * This is not correct. It is 16, 32 or 64 bytes depending on + * register D31:F0:ADh bits 5:4. + * + * But this gets us at least _part_ of it. + */ + size = 16; + } else { + size = 128; + } + base &= ~(size-1); + + /* Just print it out for now. We should reserve it after more debugging */ + dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base+size-1); +} + +static void __devinit quirk_ich6_lpc(struct pci_dev *dev) +{ + /* Shared ACPI/GPIO decode with all ICH6+ */ + ich6_lpc_acpi_gpio(dev); + + /* ICH6-specific generic IO decode */ + ich6_lpc_generic_decode(dev, 0x84, "LPC Generic IO decode 1", 0); + ich6_lpc_generic_decode(dev, 0x88, "LPC Generic IO decode 2", 1); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc); + +static void __devinit ich7_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name) +{ + u32 val; + u32 mask, base; + + pci_read_config_dword(dev, reg, &val); + + /* Enabled? */ + if (!(val & 1)) + return; + + /* + * IO base in bits 15:2, mask in bits 23:18, both + * are dword-based + */ + base = val & 0xfffc; + mask = (val >> 16) & 0xfc; + mask |= 3; + + /* Just print it out for now. We should reserve it after more debugging */ + dev_info(&dev->dev, "%s PIO at %04x (mask %04x)\n", name, base, mask); +} + +/* ICH7-10 has the same common LPC generic IO decode registers */ +static void __devinit quirk_ich7_lpc(struct pci_dev *dev) +{ + /* We share the common ACPI/DPIO decode with ICH6 */ + ich6_lpc_acpi_gpio(dev); + + /* And have 4 ICH7+ generic decodes */ + ich7_lpc_generic_decode(dev, 0x84, "ICH7 LPC Generic IO decode 1"); + ich7_lpc_generic_decode(dev, 0x88, "ICH7 LPC Generic IO decode 2"); + ich7_lpc_generic_decode(dev, 0x8c, "ICH7 LPC Generic IO decode 3"); + ich7_lpc_generic_decode(dev, 0x90, "ICH7 LPC Generic IO decode 4"); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_1, quirk_ich7_lpc); /* * VIA ACPI: One IO region pointed to by longword at @@ -2074,11 +2149,12 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ -static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) +static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, + struct pci_fixup *end) { while (f < end) { if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) && - (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { + (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { dev_dbg(&dev->dev, "calling %pF\n", f->hook); f->hook(dev); } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ea979f2bc6db..704608945780 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -536,9 +536,8 @@ static void pci_bus_dump_res(struct pci_bus *bus) if (!res) continue; - printk(KERN_INFO "bus: %02x index %x %s: %pR\n", - bus->number, i, - (res->flags & IORESOURCE_IO) ? "io port" : "mmio", res); + dev_printk(KERN_DEBUG, &bus->dev, "resource %d %s %pR\n", i, + (res->flags & IORESOURCE_IO) ? "io: " : "mem:", res); } } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 2dbd96cce2d8..32e8d88a4619 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -26,11 +26,13 @@ #include "pci.h" -void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) +void pci_update_resource(struct pci_dev *dev, int resno) { struct pci_bus_region region; u32 new, check, mask; int reg; + enum pci_bar_type type; + struct resource *res = dev->resource + resno; /* * Ignore resources for unimplemented BARs and unused resource slots @@ -61,17 +63,13 @@ void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) else mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; - if (resno < 6) { - reg = PCI_BASE_ADDRESS_0 + 4 * resno; - } else if (resno == PCI_ROM_RESOURCE) { + reg = pci_resource_bar(dev, resno, &type); + if (!reg) + return; + if (type != pci_bar_unknown) { if (!(res->flags & IORESOURCE_ROM_ENABLE)) return; new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Hmm, non-standard resource. */ - - return; /* kill uninitialised var warning */ } pci_write_config_dword(dev, reg, new); @@ -134,7 +132,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) align = resource_alignment(res); if (!align) { - dev_err(&dev->dev, "BAR %d: can't allocate resource (bogus " + dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " "alignment) %pR flags %#lx\n", resno, res, res->flags); return -EINVAL; @@ -157,12 +155,12 @@ int pci_assign_resource(struct pci_dev *dev, int resno) } if (ret) { - dev_err(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", + dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); } else { res->flags &= ~IORESOURCE_STARTALIGN; if (resno < PCI_BRIDGE_RESOURCES) - pci_update_resource(dev, res, resno); + pci_update_resource(dev, resno); } return ret; @@ -197,7 +195,7 @@ int pci_assign_resource_fixed(struct pci_dev *dev, int resno) dev_err(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); } else if (resno < PCI_BRIDGE_RESOURCES) { - pci_update_resource(dev, res, resno); + pci_update_resource(dev, resno); } return ret; diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig new file mode 100644 index 000000000000..9652c3fe7f5e --- /dev/null +++ b/drivers/platform/Kconfig @@ -0,0 +1,5 @@ +# drivers/platform/Kconfig + +if X86 +source "drivers/platform/x86/Kconfig" +endif diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile new file mode 100644 index 000000000000..782953ae4c03 --- /dev/null +++ b/drivers/platform/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for linux/drivers/platform +# + +obj-$(CONFIG_X86) += x86/ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig new file mode 100644 index 000000000000..e65448e99b48 --- /dev/null +++ b/drivers/platform/x86/Kconfig @@ -0,0 +1,375 @@ +# +# X86 Platform Specific Drivers +# + +menuconfig X86_PLATFORM_DEVICES + bool "X86 Platform Specific Device Drivers" + default y + ---help--- + Say Y here to get to see options for device drivers for various + x86 platforms, including vendor-specific laptop extension drivers. + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if X86_PLATFORM_DEVICES + +config ACER_WMI + tristate "Acer WMI Laptop Extras (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on ACPI + depends on LEDS_CLASS + depends on NEW_LEDS + depends on BACKLIGHT_CLASS_DEVICE + depends on SERIO_I8042 + depends on RFKILL + select ACPI_WMI + ---help--- + This is a driver for newer Acer (and Wistron) laptops. It adds + wireless radio and bluetooth control, and on some laptops, + exposes the mail LED and LCD backlight. + + For more information about this driver see + <file:Documentation/laptops/acer-wmi.txt> + + If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M + here. + +config ASUS_LAPTOP + tristate "Asus Laptop Extras (EXPERIMENTAL)" + depends on ACPI + depends on EXPERIMENTAL && !ACPI_ASUS + depends on LEDS_CLASS + depends on NEW_LEDS + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is the new Linux driver for Asus laptops. It may also support some + MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate + standard ACPI events that go through /proc/acpi/events. It also adds + support for video output switching, LCD backlight control, Bluetooth and + Wlan control, and most importantly, allows you to blink those fancy LEDs. + + For more information and a userspace daemon for handling the extra + buttons see <http://acpi4asus.sf.net/>. + + If you have an ACPI-compatible ASUS laptop, say Y or M here. + +config FUJITSU_LAPTOP + tristate "Fujitsu Laptop Extras" + depends on ACPI + depends on INPUT + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is a driver for laptops built by Fujitsu: + + * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks + * Possibly other Fujitsu laptop models + * Tested with S6410 and S7020 + + It adds support for LCD brightness control and some hotkeys. + + If you have a Fujitsu laptop, say Y or M here. + +config FUJITSU_LAPTOP_DEBUG + bool "Verbose debug mode for Fujitsu Laptop Extras" + depends on FUJITSU_LAPTOP + default n + ---help--- + Enables extra debug output from the fujitsu extras driver, at the + expense of a slight increase in driver size. + + If you are not sure, say N here. + +config TC1100_WMI + tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)" + depends on !X86_64 + depends on EXPERIMENTAL + depends on ACPI + select ACPI_WMI + ---help--- + This is a driver for the WMI extensions (wireless and bluetooth power + control) of the HP Compaq TC1100 tablet. + +config HP_WMI + tristate "HP WMI extras" + depends on ACPI_WMI + depends on INPUT + depends on RFKILL + help + Say Y here if you want to support WMI-based hotkeys on HP laptops and + to read data from WMI such as docking or ambient light sensor state. + + To compile this driver as a module, choose M here: the module will + be called hp-wmi. + +config MSI_LAPTOP + tristate "MSI Laptop Extras" + depends on ACPI + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is a driver for laptops built by MSI (MICRO-STAR + INTERNATIONAL): + + MSI MegaBook S270 (MS-1013) + Cytron/TCM/Medion/Tchibo MD96100/SAM2000 + + It adds support for Bluetooth, WLAN and LCD brightness control. + + More information about this driver is available at + <http://0pointer.de/lennart/tchibo.html>. + + If you have an MSI S270 laptop, say Y or M here. + +config PANASONIC_LAPTOP + tristate "Panasonic Laptop Extras" + depends on INPUT && ACPI + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This driver adds support for access to backlight control and hotkeys + on Panasonic Let's Note laptops. + + If you have a Panasonic Let's note laptop (such as the R1(N variant), + R2, R3, R5, T2, W2 and Y2 series), say Y. + +config COMPAL_LAPTOP + tristate "Compal Laptop Extras" + depends on ACPI + depends on BACKLIGHT_CLASS_DEVICE + ---help--- + This is a driver for laptops built by Compal: + + Compal FL90/IFL90 + Compal FL91/IFL91 + Compal FL92/JFL92 + Compal FT00/IFT00 + + It adds support for Bluetooth, WLAN and LCD brightness control. + + If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here. + +config SONY_LAPTOP + tristate "Sony Laptop Extras" + depends on ACPI + select BACKLIGHT_CLASS_DEVICE + depends on INPUT + ---help--- + This mini-driver drives the SNC and SPIC devices present in the ACPI + BIOS of the Sony Vaio laptops. + + It gives access to some extra laptop functionalities like Bluetooth, + screen brightness control, Fn keys and allows powering on/off some + devices. + + Read <file:Documentation/laptops/sony-laptop.txt> for more information. + +config SONYPI_COMPAT + bool "Sonypi compatibility" + depends on SONY_LAPTOP + ---help--- + Build the sonypi driver compatibility code into the sony-laptop driver. + +config THINKPAD_ACPI + tristate "ThinkPad ACPI Laptop Extras" + depends on ACPI + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + select HWMON + select NVRAM + select INPUT + select NEW_LEDS + select LEDS_CLASS + select NET + select RFKILL + ---help--- + This is a driver for the IBM and Lenovo ThinkPad laptops. It adds + support for Fn-Fx key combinations, Bluetooth control, video + output switching, ThinkLight control, UltraBay eject and more. + For more information about this driver see + <file:Documentation/laptops/thinkpad-acpi.txt> and + <http://ibm-acpi.sf.net/> . + + This driver was formerly known as ibm-acpi. + + If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. + +config THINKPAD_ACPI_DEBUG + bool "Verbose debug mode" + depends on THINKPAD_ACPI + default n + ---help--- + Enables extra debugging information, at the expense of a slightly + increase in driver size. + + If you are not sure, say N here. + +config THINKPAD_ACPI_DOCK + bool "Legacy Docking Station Support" + depends on THINKPAD_ACPI + depends on ACPI_DOCK=n + default n + ---help--- + Allows the thinkpad_acpi driver to handle docking station events. + This support was made obsolete by the generic ACPI docking station + support (CONFIG_ACPI_DOCK). It will allow locking and removing the + laptop from the docking station, but will not properly connect PCI + devices. + + If you are not sure, say N here. + +config THINKPAD_ACPI_BAY + bool "Legacy Removable Bay Support" + depends on THINKPAD_ACPI + default y + ---help--- + Allows the thinkpad_acpi driver to handle removable bays. It will + electrically disable the device in the bay, and also generate + notifications when the bay lever is ejected or inserted. + + If you are not sure, say Y here. + +config THINKPAD_ACPI_VIDEO + bool "Video output control support" + depends on THINKPAD_ACPI + default y + ---help--- + Allows the thinkpad_acpi driver to provide an interface to control + the various video output ports. + + This feature often won't work well, depending on ThinkPad model, + display state, video output devices in use, whether there is a X + server running, phase of the moon, and the current mood of + Schroedinger's cat. If you can use X.org's RandR to control + your ThinkPad's video output ports instead of this feature, + don't think twice: do it and say N here to save some memory. + + If you are not sure, say Y here. + +config THINKPAD_ACPI_HOTKEY_POLL + bool "Support NVRAM polling for hot keys" + depends on THINKPAD_ACPI + default y + ---help--- + Some thinkpad models benefit from NVRAM polling to detect a few of + the hot key press events. If you know your ThinkPad model does not + need to do NVRAM polling to support any of the hot keys you use, + unselecting this option will save about 1kB of memory. + + ThinkPads T40 and newer, R52 and newer, and X31 and newer are + unlikely to need NVRAM polling in their latest BIOS versions. + + NVRAM polling can detect at most the following keys: ThinkPad/Access + IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute, + Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12). + + If you are not sure, say Y here. The driver enables polling only if + it is strictly necessary to do so. + +config INTEL_MENLOW + tristate "Thermal Management driver for Intel menlow platform" + depends on ACPI_THERMAL + select THERMAL + ---help--- + ACPI thermal management enhancement driver on + Intel Menlow platform. + + If unsure, say N. + +config EEEPC_LAPTOP + tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" + depends on ACPI + depends on EXPERIMENTAL + select BACKLIGHT_CLASS_DEVICE + select HWMON + select RFKILL + ---help--- + This driver supports the Fn-Fx keys on Eee PC laptops. + It also adds the ability to switch camera/wlan on/off. + + If you have an Eee PC laptop, say Y or M here. + + +config ACPI_WMI + tristate "WMI (EXPERIMENTAL)" + depends on ACPI + depends on EXPERIMENTAL + help + This driver adds support for the ACPI-WMI (Windows Management + Instrumentation) mapper device (PNP0C14) found on some systems. + + ACPI-WMI is a proprietary extension to ACPI to expose parts of the + ACPI firmware to userspace - this is done through various vendor + defined methods and data blocks in a PNP0C14 device, which are then + made available for userspace to call. + + The implementation of this in Linux currently only exposes this to + other kernel space drivers. + + This driver is a required dependency to build the firmware specific + drivers needed on many machines, including Acer and HP laptops. + + It is safe to enable this driver even if your DSDT doesn't define + any ACPI-WMI devices. + +config ACPI_ASUS + tristate "ASUS/Medion Laptop Extras" + depends on ACPI + select BACKLIGHT_CLASS_DEVICE + ---help--- + This driver provides support for extra features of ACPI-compatible + ASUS laptops. As some of Medion laptops are made by ASUS, it may also + support some Medion laptops (such as 9675 for example). It makes all + the extra buttons generate standard ACPI events that go through + /proc/acpi/events, and (on some models) adds support for changing the + display brightness and output, switching the LCD backlight on and off, + and most importantly, allows you to blink those fancy LEDs intended + for reporting mail and wireless status. + + Note: display switching code is currently considered EXPERIMENTAL, + toying with these values may even lock your machine. + + All settings are changed via /proc/acpi/asus directory entries. Owner + and group for these entries can be set with asus_uid and asus_gid + parameters. + + More information and a userspace daemon for handling the extra buttons + at <http://sourceforge.net/projects/acpi4asus/>. + + If you have an ACPI-compatible ASUS laptop, say Y or M here. This + driver is still under development, so if your laptop is unsupported or + something works not quite as expected, please use the mailing list + available on the above page (acpi4asus-user@lists.sourceforge.net). + + NOTE: This driver is deprecated and will probably be removed soon, + use asus-laptop instead. + +config ACPI_TOSHIBA + tristate "Toshiba Laptop Extras" + depends on ACPI + depends on INPUT + select INPUT_POLLDEV + select NET + select RFKILL + select BACKLIGHT_CLASS_DEVICE + ---help--- + This driver adds support for access to certain system settings + on "legacy free" Toshiba laptops. These laptops can be recognized by + their lack of a BIOS setup menu and APM support. + + On these machines, all system configuration is handled through the + ACPI. This driver is required for access to controls not covered + by the general ACPI drivers, such as LCD brightness, video output, + etc. + + This driver differs from the non-ACPI Toshiba laptop driver (located + under "Processor type and features") in several aspects. + Configuration is accessed by reading and writing text files in the + /proc tree instead of by program interface to /dev. Furthermore, no + power management functions are exposed, as those are handled by the + general ACPI drivers. + + More information about this driver is available at + <http://memebeam.org/toys/ToshibaAcpiDriver>. + + If you have a legacy free Toshiba laptop (such as the Libretto L1 + series), say Y. +endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile new file mode 100644 index 000000000000..1e9de2ae0de5 --- /dev/null +++ b/drivers/platform/x86/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for linux/drivers/platform/x86 +# x86 Platform-Specific Drivers +# +obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o +obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o +obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o +obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o +obj-$(CONFIG_ACER_WMI) += acer-wmi.o +obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o +obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o +obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o +obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o +obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o +obj-$(CONFIG_ACPI_WMI) += wmi.o +obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o +obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o diff --git a/drivers/misc/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 94c9f911824e..94c9f911824e 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c diff --git a/drivers/misc/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 8fb8b3591048..8fb8b3591048 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c diff --git a/drivers/acpi/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index 1e74988c7b2d..1e74988c7b2d 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c diff --git a/drivers/misc/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 11003bba10d3..11003bba10d3 100644 --- a/drivers/misc/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c diff --git a/drivers/misc/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 02fe2b8b8939..02fe2b8b8939 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index a7dd3e9fb79d..65dc41540c62 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -3,6 +3,7 @@ /* Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> Copyright (C) 2008 Peter Gruber <nokos@gmx.net> + Copyright (C) 2008 Tony Vroon <tony@linx.net> Based on earlier work: Copyright (C) 2003 Shane Spencer <shane@bogomip.com> Adrian Yee <brewt-fujitsu@brewt.org> @@ -65,8 +66,11 @@ #include <linux/kfifo.h> #include <linux/video_output.h> #include <linux/platform_device.h> +#ifdef CONFIG_LEDS_CLASS +#include <linux/leds.h> +#endif -#define FUJITSU_DRIVER_VERSION "0.4.3" +#define FUJITSU_DRIVER_VERSION "0.5.0" #define FUJITSU_LCD_N_LEVELS 8 @@ -83,6 +87,24 @@ #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 +/* FUNC interface - command values */ +#define FUNC_RFKILL 0x1000 +#define FUNC_LEDS 0x1001 +#define FUNC_BUTTONS 0x1002 +#define FUNC_BACKLIGHT 0x1004 + +/* FUNC interface - responses */ +#define UNSUPPORTED_CMD 0x80000000 + +#ifdef CONFIG_LEDS_CLASS +/* FUNC interface - LED control */ +#define FUNC_LED_OFF 0x1 +#define FUNC_LED_ON 0x30001 +#define KEYBOARD_LAMPS 0x100 +#define LOGOLAMP_POWERON 0x2000 +#define LOGOLAMP_ALWAYS 0x4000 +#endif + /* Hotkey details */ #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ #define KEY2_CODE 0x411 @@ -133,7 +155,6 @@ struct fujitsu_t { static struct fujitsu_t *fujitsu; static int use_alt_lcd_levels = -1; -static int disable_brightness_keys = -1; static int disable_brightness_adjust = -1; /* Device used to access other hotkeys on the laptop */ @@ -145,8 +166,9 @@ struct fujitsu_hotkey_t { struct platform_device *pf_device; struct kfifo *fifo; spinlock_t fifo_lock; - - unsigned int irb; /* info about the pressed buttons */ + int rfkill_state; + int logolamp_registered; + int kblamps_registered; }; static struct fujitsu_hotkey_t *fujitsu_hotkey; @@ -154,12 +176,139 @@ static struct fujitsu_hotkey_t *fujitsu_hotkey; static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, void *data); +#ifdef CONFIG_LEDS_CLASS +static enum led_brightness logolamp_get(struct led_classdev *cdev); +static void logolamp_set(struct led_classdev *cdev, + enum led_brightness brightness); + +struct led_classdev logolamp_led = { + .name = "fujitsu::logolamp", + .brightness_get = logolamp_get, + .brightness_set = logolamp_set +}; + +static enum led_brightness kblamps_get(struct led_classdev *cdev); +static void kblamps_set(struct led_classdev *cdev, + enum led_brightness brightness); + +struct led_classdev kblamps_led = { + .name = "fujitsu::kblamps", + .brightness_get = kblamps_get, + .brightness_set = kblamps_set +}; +#endif + #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG static u32 dbg_level = 0x03; #endif static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data); +/* Fujitsu ACPI interface function */ + +static int call_fext_func(int cmd, int arg0, int arg1, int arg2) +{ + acpi_status status = AE_OK; + union acpi_object params[4] = { + { .type = ACPI_TYPE_INTEGER }, + { .type = ACPI_TYPE_INTEGER }, + { .type = ACPI_TYPE_INTEGER }, + { .type = ACPI_TYPE_INTEGER } + }; + struct acpi_object_list arg_list = { 4, ¶ms[0] }; + struct acpi_buffer output; + union acpi_object out_obj; + acpi_handle handle = NULL; + + status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle); + if (ACPI_FAILURE(status)) { + vdbg_printk(FUJLAPTOP_DBG_ERROR, + "FUNC interface is not present\n"); + return -ENODEV; + } + + params[0].integer.value = cmd; + params[1].integer.value = arg0; + params[2].integer.value = arg1; + params[3].integer.value = arg2; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; + + status = acpi_evaluate_object(handle, NULL, &arg_list, &output); + if (ACPI_FAILURE(status)) { + vdbg_printk(FUJLAPTOP_DBG_WARN, + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", + cmd, arg0, arg1, arg2); + return -ENODEV; + } + + if (out_obj.type != ACPI_TYPE_INTEGER) { + vdbg_printk(FUJLAPTOP_DBG_WARN, + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not " + "return an integer\n", + cmd, arg0, arg1, arg2); + return -ENODEV; + } + + vdbg_printk(FUJLAPTOP_DBG_TRACE, + "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", + cmd, arg0, arg1, arg2, (int)out_obj.integer.value); + return out_obj.integer.value; +} + +#ifdef CONFIG_LEDS_CLASS +/* LED class callbacks */ + +static void logolamp_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + if (brightness >= LED_FULL) { + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); + } else if (brightness >= LED_HALF) { + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); + } else { + call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); + } +} + +static void kblamps_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + if (brightness >= LED_FULL) + call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); + else + call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); +} + +static enum led_brightness logolamp_get(struct led_classdev *cdev) +{ + enum led_brightness brightness = LED_OFF; + int poweron, always; + + poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); + if (poweron == FUNC_LED_ON) { + brightness = LED_HALF; + always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); + if (always == FUNC_LED_ON) + brightness = LED_FULL; + } + return brightness; +} + +static enum led_brightness kblamps_get(struct led_classdev *cdev) +{ + enum led_brightness brightness = LED_OFF; + + if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) + brightness = LED_FULL; + + return brightness; +} +#endif + /* Hardware access for LCD brightness control */ static int set_lcd_level(int level) @@ -263,44 +412,34 @@ static int get_max_brightness(void) return fujitsu->max_brightness; } -static int get_lcd_level_alt(void) -{ - unsigned long long state = 0; - acpi_status status = AE_OK; - - vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n"); - - status = - acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state); - if (status < 0) - return status; - - fujitsu->brightness_level = state & 0x0fffffff; - - if (state & 0x80000000) - fujitsu->brightness_changed = 1; - else - fujitsu->brightness_changed = 0; - - return fujitsu->brightness_level; -} - /* Backlight device stuff */ static int bl_get_brightness(struct backlight_device *b) { - if (use_alt_lcd_levels) - return get_lcd_level_alt(); - else - return get_lcd_level(); + return get_lcd_level(); } static int bl_update_status(struct backlight_device *b) { + int ret; + if (b->props.power == 4) + ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); + else + ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); + if (ret != 0) + vdbg_printk(FUJLAPTOP_DBG_ERROR, + "Unable to adjust backlight power, error code %i\n", + ret); + if (use_alt_lcd_levels) - return set_lcd_level_alt(b->props.brightness); + ret = set_lcd_level_alt(b->props.brightness); else - return set_lcd_level(b->props.brightness); + ret = set_lcd_level(b->props.brightness); + if (ret != 0) + vdbg_printk(FUJLAPTOP_DBG_ERROR, + "Unable to adjust LCD brightness, error code %i\n", + ret); + return ret; } static struct backlight_ops fujitsubl_ops = { @@ -344,10 +483,7 @@ static ssize_t show_lcd_level(struct device *dev, int ret; - if (use_alt_lcd_levels) - ret = get_lcd_level_alt(); - else - ret = get_lcd_level(); + ret = get_lcd_level(); if (ret < 0) return ret; @@ -372,52 +508,71 @@ static ssize_t store_lcd_level(struct device *dev, if (ret < 0) return ret; - if (use_alt_lcd_levels) - ret = get_lcd_level_alt(); - else - ret = get_lcd_level(); + ret = get_lcd_level(); if (ret < 0) return ret; return count; } -/* Hardware access for hotkey device */ - -static int get_irb(void) +static ssize_t +ignore_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { - unsigned long long state = 0; - acpi_status status = AE_OK; - - vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n"); - - status = - acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL, - &state); - if (status < 0) - return status; + return count; +} - fujitsu_hotkey->irb = state; +static ssize_t +show_lid_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + return sprintf(buf, "unknown\n"); + if (fujitsu_hotkey->rfkill_state & 0x100) + return sprintf(buf, "open\n"); + else + return sprintf(buf, "closed\n"); +} - return fujitsu_hotkey->irb; +static ssize_t +show_dock_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + return sprintf(buf, "unknown\n"); + if (fujitsu_hotkey->rfkill_state & 0x200) + return sprintf(buf, "docked\n"); + else + return sprintf(buf, "undocked\n"); } static ssize_t -ignore_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +show_radios_state(struct device *dev, + struct device_attribute *attr, char *buf) { - return count; + if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) + return sprintf(buf, "unknown\n"); + if (fujitsu_hotkey->rfkill_state & 0x20) + return sprintf(buf, "on\n"); + else + return sprintf(buf, "killed\n"); } static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, ignore_store); static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); +static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store); +static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store); +static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store); static struct attribute *fujitsupf_attributes[] = { &dev_attr_brightness_changed.attr, &dev_attr_max_brightness.attr, &dev_attr_lcd_level.attr, + &dev_attr_lid.attr, + &dev_attr_dock.attr, + &dev_attr_radios.attr, NULL }; @@ -435,24 +590,16 @@ static struct platform_driver fujitsupf_driver = { static void dmi_check_cb_common(const struct dmi_system_id *id) { acpi_handle handle; - int have_blnf; printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", id->ident); - have_blnf = ACPI_SUCCESS - (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle)); if (use_alt_lcd_levels == -1) { - vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n"); - use_alt_lcd_levels = 1; - } - if (disable_brightness_keys == -1) { - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "auto-detecting disable_keys\n"); - disable_brightness_keys = have_blnf ? 1 : 0; - } - if (disable_brightness_adjust == -1) { - vdbg_printk(FUJLAPTOP_DBG_TRACE, - "auto-detecting disable_adjust\n"); - disable_brightness_adjust = have_blnf ? 0 : 1; + if (ACPI_SUCCESS(acpi_get_handle(NULL, + "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) + use_alt_lcd_levels = 1; + else + use_alt_lcd_levels = 0; + vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as " + "%i\n", use_alt_lcd_levels); } } @@ -581,19 +728,14 @@ static int acpi_fujitsu_add(struct acpi_device *device) /* do config (detect defaults) */ use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; - disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0; disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; vdbg_printk(FUJLAPTOP_DBG_INFO, - "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n", - use_alt_lcd_levels, disable_brightness_keys, - disable_brightness_adjust); + "config: [alt interface: %d], [adjust disable: %d]\n", + use_alt_lcd_levels, disable_brightness_adjust); if (get_max_brightness() <= 0) fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; - if (use_alt_lcd_levels) - get_lcd_level_alt(); - else - get_lcd_level(); + get_lcd_level(); return result; @@ -644,43 +786,23 @@ static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) case ACPI_FUJITSU_NOTIFY_CODE1: keycode = 0; oldb = fujitsu->brightness_level; - get_lcd_level(); /* the alt version always yields changed */ + get_lcd_level(); newb = fujitsu->brightness_level; vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i (%i)]\n", oldb, newb, fujitsu->brightness_changed); - if (oldb == newb && fujitsu->brightness_changed) { - keycode = 0; - if (disable_brightness_keys != 1) { - if (oldb == 0) { - acpi_bus_generate_proc_event - (fujitsu->dev, - ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, - 0); - keycode = KEY_BRIGHTNESSDOWN; - } else if (oldb == - (fujitsu->max_brightness) - 1) { - acpi_bus_generate_proc_event - (fujitsu->dev, - ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, - 0); - keycode = KEY_BRIGHTNESSUP; - } - } - } else if (oldb < newb) { + if (oldb < newb) { if (disable_brightness_adjust != 1) { if (use_alt_lcd_levels) set_lcd_level_alt(newb); else set_lcd_level(newb); } - if (disable_brightness_keys != 1) { - acpi_bus_generate_proc_event(fujitsu->dev, - ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); - keycode = KEY_BRIGHTNESSUP; - } + acpi_bus_generate_proc_event(fujitsu->dev, + ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); + keycode = KEY_BRIGHTNESSUP; } else if (oldb > newb) { if (disable_brightness_adjust != 1) { if (use_alt_lcd_levels) @@ -688,13 +810,9 @@ static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) else set_lcd_level(newb); } - if (disable_brightness_keys != 1) { - acpi_bus_generate_proc_event(fujitsu->dev, - ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); - keycode = KEY_BRIGHTNESSDOWN; - } - } else { - keycode = KEY_UNKNOWN; + acpi_bus_generate_proc_event(fujitsu->dev, + ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); + keycode = KEY_BRIGHTNESSDOWN; } break; default: @@ -771,7 +889,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) input->id.bustype = BUS_HOST; input->id.product = 0x06; input->dev.parent = &device->dev; - input->evbit[0] = BIT(EV_KEY); + + set_bit(EV_KEY, input->evbit); set_bit(fujitsu->keycode1, input->keybit); set_bit(fujitsu->keycode2, input->keybit); set_bit(fujitsu->keycode3, input->keybit); @@ -803,10 +922,44 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) printk(KERN_ERR "_INI Method failed\n"); } - i = 0; /* Discard hotkey ringbuffer */ - while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ; + i = 0; + while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 + && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) + ; /* No action, result is discarded */ vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); + fujitsu_hotkey->rfkill_state = + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); + + /* Suspect this is a keymap of the application panel, print it */ + printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", + call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); + + #ifdef CONFIG_LEDS_CLASS + if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { + result = led_classdev_register(&fujitsu->pf_device->dev, + &logolamp_led); + if (result == 0) { + fujitsu_hotkey->logolamp_registered = 1; + } else { + printk(KERN_ERR "fujitsu-laptop: Could not register " + "LED handler for logo lamp, error %i\n", result); + } + } + + if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && + (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { + result = led_classdev_register(&fujitsu->pf_device->dev, + &kblamps_led); + if (result == 0) { + fujitsu_hotkey->kblamps_registered = 1; + } else { + printk(KERN_ERR "fujitsu-laptop: Could not register " + "LED handler for keyboard lamps, error %i\n", result); + } + } + #endif + return result; end: @@ -852,16 +1005,15 @@ static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, input = fujitsu_hotkey->input; - vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n"); + fujitsu_hotkey->rfkill_state = + call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); switch (event) { case ACPI_FUJITSU_NOTIFY_CODE1: i = 0; - while ((irb = get_irb()) != 0 - && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { - vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n", - irb); - + while ((irb = + call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 + && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { switch (irb & 0x4ff) { case KEY1_CODE: keycode = fujitsu->keycode1; @@ -1035,6 +1187,15 @@ static int __init fujitsu_init(void) goto fail_hotkey1; } + /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ + + if (!acpi_video_backlight_support()) { + if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) + fujitsu->bl_device->props.power = 4; + else + fujitsu->bl_device->props.power = 0; + } + printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION " successfully loaded.\n"); @@ -1074,6 +1235,14 @@ fail_acpi: static void __exit fujitsu_cleanup(void) { + #ifdef CONFIG_LEDS_CLASS + if (fujitsu_hotkey->logolamp_registered != 0) + led_classdev_unregister(&logolamp_led); + + if (fujitsu_hotkey->kblamps_registered != 0) + led_classdev_unregister(&kblamps_led); + #endif + sysfs_remove_group(&fujitsu->pf_device->dev.kobj, &fujitsupf_attribute_group); platform_device_unregister(fujitsu->pf_device); @@ -1098,9 +1267,6 @@ module_exit(fujitsu_cleanup); module_param(use_alt_lcd_levels, uint, 0644); MODULE_PARM_DESC(use_alt_lcd_levels, "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); -module_param(disable_brightness_keys, uint, 0644); -MODULE_PARM_DESC(disable_brightness_keys, - "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device)."); module_param(disable_brightness_adjust, uint, 0644); MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG @@ -1108,12 +1274,13 @@ module_param_named(debug, dbg_level, uint, 0644); MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); #endif -MODULE_AUTHOR("Jonathan Woithe, Peter Gruber"); +MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); MODULE_DESCRIPTION("Fujitsu laptop extras support"); MODULE_VERSION(FUJITSU_DRIVER_VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); +MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); static struct pnp_device_id pnp_ids[] = { diff --git a/drivers/misc/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 4b7c24c519c3..7c789f0a94d7 100644 --- a/drivers/misc/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -428,7 +428,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) wifi_rfkill->state = hp_wmi_wifi_state(); wifi_rfkill->toggle_radio = hp_wmi_wifi_set; wifi_rfkill->user_claim_unsupported = 1; - rfkill_register(wifi_rfkill); + err = rfkill_register(wifi_rfkill); + if (err) + goto add_sysfs_error; } if (wireless & 0x2) { @@ -438,7 +440,8 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) bluetooth_rfkill->state = hp_wmi_bluetooth_state(); bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; bluetooth_rfkill->user_claim_unsupported = 1; - rfkill_register(bluetooth_rfkill); + err = rfkill_register(bluetooth_rfkill); + goto register_bluetooth_error; } if (wireless & 0x4) { @@ -447,10 +450,16 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) wwan_rfkill->state = hp_wmi_wwan_state(); wwan_rfkill->toggle_radio = hp_wmi_wwan_set; wwan_rfkill->user_claim_unsupported = 1; - rfkill_register(wwan_rfkill); + err = rfkill_register(wwan_rfkill); + if (err) + goto register_wwan_err; } return 0; +register_wwan_err: + rfkill_unregister(bluetooth_rfkill); +register_bluetooth_error: + rfkill_unregister(wifi_rfkill); add_sysfs_error: cleanup_sysfs(device); return err; diff --git a/drivers/misc/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 27b7662955bb..27b7662955bb 100644 --- a/drivers/misc/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c diff --git a/drivers/misc/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 759763d18e4c..759763d18e4c 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c diff --git a/drivers/misc/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 4a1bc64485d5..f30db367c82e 100644 --- a/drivers/misc/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -241,8 +241,6 @@ static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val) }; acpi_status status = AE_OK; - ACPI_FUNCTION_TRACE("acpi_pcc_write_sset"); - status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET, ¶ms, NULL); @@ -254,8 +252,6 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device) unsigned long long s; acpi_status status; - ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty"); - status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY, NULL, &s); if (ACPI_SUCCESS(status)) @@ -274,8 +270,6 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) union acpi_object *hkey = NULL; int i; - ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata"); - status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0, &buffer); if (ACPI_FAILURE(status)) { @@ -501,8 +495,6 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) int key_code, hkey_num; unsigned long long result; - ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput"); - rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, NULL, &result); if (!ACPI_SUCCESS(rc)) { @@ -538,8 +530,6 @@ static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data) { struct pcc_acpi *pcc = (struct pcc_acpi *) data; - ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify"); - switch (event) { case HKEY_NOTIFY: acpi_pcc_generate_keyinput(pcc); @@ -554,8 +544,6 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) { int i, rc; - ACPI_FUNCTION_TRACE("acpi_pcc_init_input"); - pcc->input_dev = input_allocate_device(); if (!pcc->input_dev) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, @@ -597,8 +585,6 @@ static int acpi_pcc_hotkey_resume(struct acpi_device *device) struct pcc_acpi *pcc = acpi_driver_data(device); acpi_status status = AE_OK; - ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume"); - if (device == NULL || pcc == NULL) return -EINVAL; @@ -616,8 +602,6 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) struct pcc_acpi *pcc; int num_sifr, result; - ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add"); - if (!device) return -EINVAL; @@ -714,8 +698,6 @@ static int __init acpi_pcc_init(void) { int result = 0; - ACPI_FUNCTION_TRACE("acpi_pcc_init"); - if (acpi_disabled) return -ENODEV; @@ -733,8 +715,6 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) { struct pcc_acpi *pcc = acpi_driver_data(device); - ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove"); - if (!device || !pcc) return -EINVAL; @@ -757,8 +737,6 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) static void __exit acpi_pcc_exit(void) { - ACPI_FUNCTION_TRACE("acpi_pcc_exit"); - acpi_bus_unregister_driver(&acpi_pcc_driver); } diff --git a/drivers/misc/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 571b211608d1..537959d07148 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -935,14 +935,17 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) static acpi_status sony_walk_callback(acpi_handle handle, u32 level, void *context, void **return_value) { - struct acpi_namespace_node *node; - union acpi_operand_object *operand; + struct acpi_device_info *info; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - node = (struct acpi_namespace_node *)handle; - operand = (union acpi_operand_object *)node->object; + if (ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) { + info = buffer.pointer; - printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii, - (u32) operand->method.param_count); + printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", + (char *)&info->name, info->param_count); + + kfree(buffer.pointer); + } return AE_OK; } diff --git a/drivers/misc/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index f25e4c974dcf..b4a4aa9ee482 100644 --- a/drivers/misc/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -30,7 +30,6 @@ #include <linux/init.h> #include <linux/types.h> #include <acpi/acpi.h> -#include <acpi/actypes.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> #include <linux/platform_device.h> diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 899766e16fa8..3478453eba7a 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -76,7 +76,6 @@ #include <linux/workqueue.h> #include <acpi/acpi_drivers.h> -#include <acpi/acnamesp.h> #include <linux/pci_ids.h> diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 40e60fc2e596..40e60fc2e596 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c diff --git a/drivers/acpi/wmi.c b/drivers/platform/x86/wmi.c index 8a8b377712c9..8a8b377712c9 100644 --- a/drivers/acpi/wmi.c +++ b/drivers/platform/x86/wmi.c diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 383e47c392a4..2834846a185d 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -23,7 +23,6 @@ #include <linux/pnp.h> #include <linux/mod_devicetable.h> #include <acpi/acpi_bus.h> -#include <acpi/actypes.h> #include "../base.h" #include "pnpacpi.h" diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 02a774424e8d..f511a406fcaa 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -29,7 +29,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); -/** +/* * struct regulator_dev * * Voltage / Current regulator class device. One for each regulator. @@ -56,7 +56,7 @@ struct regulator_dev { void *reg_data; /* regulator_dev data */ }; -/** +/* * struct regulator_map * * Used to provide symbolic supply names to devices. @@ -79,7 +79,7 @@ struct regulator { int uA_load; int min_uV; int max_uV; - int enabled; /* client has called enabled */ + int enabled; /* count of client enables */ char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; @@ -174,6 +174,16 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, /* operating mode constraint check */ static int regulator_check_mode(struct regulator_dev *rdev, int mode) { + switch (mode) { + case REGULATOR_MODE_FAST: + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_IDLE: + case REGULATOR_MODE_STANDBY: + break; + default: + return -EINVAL; + } + if (!rdev->constraints) { printk(KERN_ERR "%s: no constraints for %s\n", __func__, rdev->desc->name); @@ -232,6 +242,7 @@ static ssize_t regulator_uV_show(struct device *dev, return ret; } +static DEVICE_ATTR(microvolts, 0444, regulator_uV_show, NULL); static ssize_t regulator_uA_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -240,6 +251,7 @@ static ssize_t regulator_uA_show(struct device *dev, return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev)); } +static DEVICE_ATTR(microamps, 0444, regulator_uA_show, NULL); static ssize_t regulator_name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -257,12 +269,8 @@ static ssize_t regulator_name_show(struct device *dev, return sprintf(buf, "%s\n", name); } -static ssize_t regulator_opmode_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t regulator_print_opmode(char *buf, int mode) { - struct regulator_dev *rdev = dev_get_drvdata(dev); - int mode = _regulator_get_mode(rdev); - switch (mode) { case REGULATOR_MODE_FAST: return sprintf(buf, "fast\n"); @@ -276,12 +284,17 @@ static ssize_t regulator_opmode_show(struct device *dev, return sprintf(buf, "unknown\n"); } -static ssize_t regulator_state_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t regulator_opmode_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - int state = _regulator_is_enabled(rdev); + return regulator_print_opmode(buf, _regulator_get_mode(rdev)); +} +static DEVICE_ATTR(opmode, 0444, regulator_opmode_show, NULL); + +static ssize_t regulator_print_state(char *buf, int state) +{ if (state > 0) return sprintf(buf, "enabled\n"); else if (state == 0) @@ -290,6 +303,15 @@ static ssize_t regulator_state_show(struct device *dev, return sprintf(buf, "unknown\n"); } +static ssize_t regulator_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return regulator_print_state(buf, _regulator_is_enabled(rdev)); +} +static DEVICE_ATTR(state, 0444, regulator_state_show, NULL); + static ssize_t regulator_min_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -300,6 +322,7 @@ static ssize_t regulator_min_uA_show(struct device *dev, return sprintf(buf, "%d\n", rdev->constraints->min_uA); } +static DEVICE_ATTR(min_microamps, 0444, regulator_min_uA_show, NULL); static ssize_t regulator_max_uA_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -311,6 +334,7 @@ static ssize_t regulator_max_uA_show(struct device *dev, return sprintf(buf, "%d\n", rdev->constraints->max_uA); } +static DEVICE_ATTR(max_microamps, 0444, regulator_max_uA_show, NULL); static ssize_t regulator_min_uV_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -322,6 +346,7 @@ static ssize_t regulator_min_uV_show(struct device *dev, return sprintf(buf, "%d\n", rdev->constraints->min_uV); } +static DEVICE_ATTR(min_microvolts, 0444, regulator_min_uV_show, NULL); static ssize_t regulator_max_uV_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -333,6 +358,7 @@ static ssize_t regulator_max_uV_show(struct device *dev, return sprintf(buf, "%d\n", rdev->constraints->max_uV); } +static DEVICE_ATTR(max_microvolts, 0444, regulator_max_uV_show, NULL); static ssize_t regulator_total_uA_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -347,6 +373,7 @@ static ssize_t regulator_total_uA_show(struct device *dev, mutex_unlock(&rdev->mutex); return sprintf(buf, "%d\n", uA); } +static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL); static ssize_t regulator_num_users_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -374,153 +401,106 @@ static ssize_t regulator_suspend_mem_uV_show(struct device *dev, { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); return sprintf(buf, "%d\n", rdev->constraints->state_mem.uV); } +static DEVICE_ATTR(suspend_mem_microvolts, 0444, + regulator_suspend_mem_uV_show, NULL); static ssize_t regulator_suspend_disk_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); return sprintf(buf, "%d\n", rdev->constraints->state_disk.uV); } +static DEVICE_ATTR(suspend_disk_microvolts, 0444, + regulator_suspend_disk_uV_show, NULL); static ssize_t regulator_suspend_standby_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); return sprintf(buf, "%d\n", rdev->constraints->state_standby.uV); } - -static ssize_t suspend_opmode_show(struct regulator_dev *rdev, - unsigned int mode, char *buf) -{ - switch (mode) { - case REGULATOR_MODE_FAST: - return sprintf(buf, "fast\n"); - case REGULATOR_MODE_NORMAL: - return sprintf(buf, "normal\n"); - case REGULATOR_MODE_IDLE: - return sprintf(buf, "idle\n"); - case REGULATOR_MODE_STANDBY: - return sprintf(buf, "standby\n"); - } - return sprintf(buf, "unknown\n"); -} +static DEVICE_ATTR(suspend_standby_microvolts, 0444, + regulator_suspend_standby_uV_show, NULL); static ssize_t regulator_suspend_mem_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); - return suspend_opmode_show(rdev, - rdev->constraints->state_mem.mode, buf); + return regulator_print_opmode(buf, + rdev->constraints->state_mem.mode); } +static DEVICE_ATTR(suspend_mem_mode, 0444, + regulator_suspend_mem_mode_show, NULL); static ssize_t regulator_suspend_disk_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); - return suspend_opmode_show(rdev, - rdev->constraints->state_disk.mode, buf); + return regulator_print_opmode(buf, + rdev->constraints->state_disk.mode); } +static DEVICE_ATTR(suspend_disk_mode, 0444, + regulator_suspend_disk_mode_show, NULL); static ssize_t regulator_suspend_standby_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); - return suspend_opmode_show(rdev, - rdev->constraints->state_standby.mode, buf); + return regulator_print_opmode(buf, + rdev->constraints->state_standby.mode); } +static DEVICE_ATTR(suspend_standby_mode, 0444, + regulator_suspend_standby_mode_show, NULL); static ssize_t regulator_suspend_mem_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); - - if (rdev->constraints->state_mem.enabled) - return sprintf(buf, "enabled\n"); - else - return sprintf(buf, "disabled\n"); + return regulator_print_state(buf, + rdev->constraints->state_mem.enabled); } +static DEVICE_ATTR(suspend_mem_state, 0444, + regulator_suspend_mem_state_show, NULL); static ssize_t regulator_suspend_disk_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); - - if (rdev->constraints->state_disk.enabled) - return sprintf(buf, "enabled\n"); - else - return sprintf(buf, "disabled\n"); + return regulator_print_state(buf, + rdev->constraints->state_disk.enabled); } +static DEVICE_ATTR(suspend_disk_state, 0444, + regulator_suspend_disk_state_show, NULL); static ssize_t regulator_suspend_standby_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); - if (!rdev->constraints) - return sprintf(buf, "not defined\n"); - - if (rdev->constraints->state_standby.enabled) - return sprintf(buf, "enabled\n"); - else - return sprintf(buf, "disabled\n"); + return regulator_print_state(buf, + rdev->constraints->state_standby.enabled); } +static DEVICE_ATTR(suspend_standby_state, 0444, + regulator_suspend_standby_state_show, NULL); + +/* + * These are the only attributes are present for all regulators. + * Other attributes are a function of regulator functionality. + */ static struct device_attribute regulator_dev_attrs[] = { __ATTR(name, 0444, regulator_name_show, NULL), - __ATTR(microvolts, 0444, regulator_uV_show, NULL), - __ATTR(microamps, 0444, regulator_uA_show, NULL), - __ATTR(opmode, 0444, regulator_opmode_show, NULL), - __ATTR(state, 0444, regulator_state_show, NULL), - __ATTR(min_microvolts, 0444, regulator_min_uV_show, NULL), - __ATTR(min_microamps, 0444, regulator_min_uA_show, NULL), - __ATTR(max_microvolts, 0444, regulator_max_uV_show, NULL), - __ATTR(max_microamps, 0444, regulator_max_uA_show, NULL), - __ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL), __ATTR(num_users, 0444, regulator_num_users_show, NULL), __ATTR(type, 0444, regulator_type_show, NULL), - __ATTR(suspend_mem_microvolts, 0444, - regulator_suspend_mem_uV_show, NULL), - __ATTR(suspend_disk_microvolts, 0444, - regulator_suspend_disk_uV_show, NULL), - __ATTR(suspend_standby_microvolts, 0444, - regulator_suspend_standby_uV_show, NULL), - __ATTR(suspend_mem_mode, 0444, - regulator_suspend_mem_mode_show, NULL), - __ATTR(suspend_disk_mode, 0444, - regulator_suspend_disk_mode_show, NULL), - __ATTR(suspend_standby_mode, 0444, - regulator_suspend_standby_mode_show, NULL), - __ATTR(suspend_mem_state, 0444, - regulator_suspend_mem_state_show, NULL), - __ATTR(suspend_disk_state, 0444, - regulator_suspend_disk_state_show, NULL), - __ATTR(suspend_standby_state, 0444, - regulator_suspend_standby_state_show, NULL), __ATTR_NULL, }; @@ -675,7 +655,8 @@ static void print_constraints(struct regulator_dev *rdev) /** * set_machine_constraints - sets regulator constraints - * @regulator: regulator source + * @rdev: regulator source + * @constraints: constraints to apply * * Allows platform initialisation code to define and constrain * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: @@ -750,8 +731,8 @@ out: /** * set_supply - set regulator supply regulator - * @regulator: regulator name - * @supply: supply regulator name + * @rdev: regulator name + * @supply_rdev: supply regulator name * * Called by platform initialisation code to set the supply regulator for this * regulator. This ensures that a regulators supply will also be enabled by the @@ -778,9 +759,9 @@ out: /** * set_consumer_device_supply: Bind a regulator to a symbolic supply - * @regulator: regulator source - * @dev: device the supply applies to - * @supply: symbolic name for supply + * @rdev: regulator source + * @consumer_dev: device the supply applies to + * @supply: symbolic name for supply * * Allows platform initialisation code to map physical regulator * sources to symbolic names for supplies for use by devices. Devices @@ -795,6 +776,20 @@ static int set_consumer_device_supply(struct regulator_dev *rdev, if (supply == NULL) return -EINVAL; + list_for_each_entry(node, ®ulator_map_list, list) { + if (consumer_dev != node->dev) + continue; + if (strcmp(node->supply, supply) != 0) + continue; + + dev_dbg(consumer_dev, "%s/%s is '%s' supply; fail %s/%s\n", + dev_name(&node->regulator->dev), + node->regulator->desc->name, + supply, + dev_name(&rdev->dev), rdev->desc->name); + return -EBUSY; + } + node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); if (node == NULL) return -ENOMEM; @@ -963,16 +958,13 @@ void regulator_put(struct regulator *regulator) if (regulator == NULL || IS_ERR(regulator)) return; - if (regulator->enabled) { - printk(KERN_WARNING "Releasing supply %s while enabled\n", - regulator->supply_name); - WARN_ON(regulator->enabled); - regulator_disable(regulator); - } - mutex_lock(®ulator_list_mutex); rdev = regulator->rdev; + if (WARN(regulator->enabled, "Releasing supply %s while enabled\n", + regulator->supply_name)) + _regulator_disable(rdev); + /* remove any sysfs entries */ if (regulator->dev) { sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); @@ -1034,29 +1026,26 @@ static int _regulator_enable(struct regulator_dev *rdev) * regulator_enable - enable regulator output * @regulator: regulator source * - * Enable the regulator output at the predefined voltage or current value. + * Request that the regulator be enabled with the regulator output at + * the predefined voltage or current value. Calls to regulator_enable() + * must be balanced with calls to regulator_disable(). + * * NOTE: the output value can be set by other drivers, boot loader or may be * hardwired in the regulator. - * NOTE: calls to regulator_enable() must be balanced with calls to - * regulator_disable(). */ int regulator_enable(struct regulator *regulator) { - int ret; - - if (regulator->enabled) { - printk(KERN_CRIT "Regulator %s already enabled\n", - regulator->supply_name); - WARN_ON(regulator->enabled); - return 0; - } + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; - mutex_lock(®ulator->rdev->mutex); - regulator->enabled = 1; - ret = _regulator_enable(regulator->rdev); - if (ret != 0) - regulator->enabled = 0; - mutex_unlock(®ulator->rdev->mutex); + mutex_lock(&rdev->mutex); + if (regulator->enabled == 0) + ret = _regulator_enable(rdev); + else if (regulator->enabled < 0) + ret = -EIO; + if (ret == 0) + regulator->enabled++; + mutex_unlock(&rdev->mutex); return ret; } EXPORT_SYMBOL_GPL(regulator_enable); @@ -1100,27 +1089,31 @@ static int _regulator_disable(struct regulator_dev *rdev) * regulator_disable - disable regulator output * @regulator: regulator source * - * Disable the regulator output voltage or current. - * NOTE: this will only disable the regulator output if no other consumer - * devices have it enabled. - * NOTE: calls to regulator_enable() must be balanced with calls to + * Disable the regulator output voltage or current. Calls to + * regulator_enable() must be balanced with calls to * regulator_disable(). + * + * NOTE: this will only disable the regulator output if no other consumer + * devices have it enabled, the regulator device supports disabling and + * machine constraints permit this operation. */ int regulator_disable(struct regulator *regulator) { - int ret; - - if (!regulator->enabled) { - printk(KERN_ERR "%s: not in use by this consumer\n", - __func__); - return 0; - } + struct regulator_dev *rdev = regulator->rdev; + int ret = 0; - mutex_lock(®ulator->rdev->mutex); - regulator->enabled = 0; - regulator->uA_load = 0; - ret = _regulator_disable(regulator->rdev); - mutex_unlock(®ulator->rdev->mutex); + mutex_lock(&rdev->mutex); + if (regulator->enabled == 1) { + ret = _regulator_disable(rdev); + if (ret == 0) + regulator->uA_load = 0; + } else if (WARN(regulator->enabled <= 0, + "unbalanced disables for supply %s\n", + regulator->supply_name)) + ret = -EIO; + if (ret == 0) + regulator->enabled--; + mutex_unlock(&rdev->mutex); return ret; } EXPORT_SYMBOL_GPL(regulator_disable); @@ -1196,7 +1189,13 @@ out: * regulator_is_enabled - is the regulator output enabled * @regulator: regulator source * - * Returns zero for disabled otherwise return number of enable requests. + * Returns positive if the regulator driver backing the source/client + * has requested that the device be enabled, zero if it hasn't, else a + * negative errno code. + * + * Note that the device backing this regulator handle can have multiple + * users, so it might be enabled even if regulator_enable() was never + * called for this particular source. */ int regulator_is_enabled(struct regulator *regulator) { @@ -1219,7 +1218,7 @@ EXPORT_SYMBOL_GPL(regulator_is_enabled); * * NOTE: If the regulator is shared between several devices then the lowest * request voltage that meets the system constraints will be used. - * NOTE: Regulator system constraints must be set for this regulator before + * Regulator system constraints must be set for this regulator before * calling this function otherwise this call will fail. */ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) @@ -1493,7 +1492,8 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, output_uV, total_uA_load); - if (ret <= 0) { + ret = regulator_check_mode(rdev, mode); + if (ret < 0) { printk(KERN_ERR "%s: failed to get optimum mode for %s @" " %d uA %d -> %d uV\n", __func__, rdev->desc->name, total_uA_load, input_uV, output_uV); @@ -1501,7 +1501,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) } ret = rdev->desc->ops->set_mode(rdev, mode); - if (ret <= 0) { + if (ret < 0) { printk(KERN_ERR "%s: failed to set optimum mode %x for %s\n", __func__, mode, rdev->desc->name); goto out; @@ -1516,7 +1516,7 @@ EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); /** * regulator_register_notifier - register regulator event notifier * @regulator: regulator source - * @notifier_block: notifier block + * @nb: notifier block * * Register notifier block to receive regulator events. */ @@ -1531,7 +1531,7 @@ EXPORT_SYMBOL_GPL(regulator_register_notifier); /** * regulator_unregister_notifier - unregister regulator event notifier * @regulator: regulator source - * @notifier_block: notifier block + * @nb: notifier block * * Unregister regulator event notifier block. */ @@ -1697,9 +1697,9 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); /** * regulator_notifier_call_chain - call regulator event notifier - * @regulator: regulator source + * @rdev: regulator source * @event: notifier block - * @data: + * @data: callback-specific data. * * Called by regulator drivers to notify clients a regulator event has * occurred. We also notify regulator clients downstream. @@ -1713,10 +1713,122 @@ int regulator_notifier_call_chain(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); +/* + * To avoid cluttering sysfs (and memory) with useless state, only + * create attributes that can be meaningfully displayed. + */ +static int add_regulator_attributes(struct regulator_dev *rdev) +{ + struct device *dev = &rdev->dev; + struct regulator_ops *ops = rdev->desc->ops; + int status = 0; + + /* some attributes need specific methods to be displayed */ + if (ops->get_voltage) { + status = device_create_file(dev, &dev_attr_microvolts); + if (status < 0) + return status; + } + if (ops->get_current_limit) { + status = device_create_file(dev, &dev_attr_microamps); + if (status < 0) + return status; + } + if (ops->get_mode) { + status = device_create_file(dev, &dev_attr_opmode); + if (status < 0) + return status; + } + if (ops->is_enabled) { + status = device_create_file(dev, &dev_attr_state); + if (status < 0) + return status; + } + + /* some attributes are type-specific */ + if (rdev->desc->type == REGULATOR_CURRENT) { + status = device_create_file(dev, &dev_attr_requested_microamps); + if (status < 0) + return status; + } + + /* all the other attributes exist to support constraints; + * don't show them if there are no constraints, or if the + * relevant supporting methods are missing. + */ + if (!rdev->constraints) + return status; + + /* constraints need specific supporting methods */ + if (ops->set_voltage) { + status = device_create_file(dev, &dev_attr_min_microvolts); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_max_microvolts); + if (status < 0) + return status; + } + if (ops->set_current_limit) { + status = device_create_file(dev, &dev_attr_min_microamps); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_max_microamps); + if (status < 0) + return status; + } + + /* suspend mode constraints need multiple supporting methods */ + if (!(ops->set_suspend_enable && ops->set_suspend_disable)) + return status; + + status = device_create_file(dev, &dev_attr_suspend_standby_state); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_suspend_mem_state); + if (status < 0) + return status; + status = device_create_file(dev, &dev_attr_suspend_disk_state); + if (status < 0) + return status; + + if (ops->set_suspend_voltage) { + status = device_create_file(dev, + &dev_attr_suspend_standby_microvolts); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_mem_microvolts); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_disk_microvolts); + if (status < 0) + return status; + } + + if (ops->set_suspend_mode) { + status = device_create_file(dev, + &dev_attr_suspend_standby_mode); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_mem_mode); + if (status < 0) + return status; + status = device_create_file(dev, + &dev_attr_suspend_disk_mode); + if (status < 0) + return status; + } + + return status; +} + /** * regulator_register - register regulator - * @regulator: regulator source - * @reg_data: private regulator data + * @regulator_desc: regulator to register + * @dev: struct device for the regulator + * @driver_data: private regulator data * * Called by regulator drivers to register a regulator. * Returns 0 on success. @@ -1761,45 +1873,37 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, /* preform any regulator specific init */ if (init_data->regulator_init) { ret = init_data->regulator_init(rdev->reg_data); - if (ret < 0) { - kfree(rdev); - rdev = ERR_PTR(ret); - goto out; - } - } - - /* set regulator constraints */ - ret = set_machine_constraints(rdev, &init_data->constraints); - if (ret < 0) { - kfree(rdev); - rdev = ERR_PTR(ret); - goto out; + if (ret < 0) + goto clean; } /* register with sysfs */ rdev->dev.class = ®ulator_class; rdev->dev.parent = dev; - snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id), - "regulator.%d", atomic_inc_return(®ulator_no) - 1); + dev_set_name(&rdev->dev, "regulator.%d", + atomic_inc_return(®ulator_no) - 1); ret = device_register(&rdev->dev); - if (ret != 0) { - kfree(rdev); - rdev = ERR_PTR(ret); - goto out; - } + if (ret != 0) + goto clean; dev_set_drvdata(&rdev->dev, rdev); + /* set regulator constraints */ + ret = set_machine_constraints(rdev, &init_data->constraints); + if (ret < 0) + goto scrub; + + /* add attributes supported by this regulator */ + ret = add_regulator_attributes(rdev); + if (ret < 0) + goto scrub; + /* set supply regulator if it exists */ if (init_data->supply_regulator_dev) { ret = set_supply(rdev, dev_get_drvdata(init_data->supply_regulator_dev)); - if (ret < 0) { - device_unregister(&rdev->dev); - kfree(rdev); - rdev = ERR_PTR(ret); - goto out; - } + if (ret < 0) + goto scrub; } /* add consumers devices */ @@ -1811,10 +1915,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, for (--i; i >= 0; i--) unset_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev); - device_unregister(&rdev->dev); - kfree(rdev); - rdev = ERR_PTR(ret); - goto out; + goto scrub; } } @@ -1822,12 +1923,19 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, out: mutex_unlock(®ulator_list_mutex); return rdev; + +scrub: + device_unregister(&rdev->dev); +clean: + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; } EXPORT_SYMBOL_GPL(regulator_register); /** * regulator_unregister - unregister regulator - * @regulator: regulator source + * @rdev: regulator to unregister * * Called by regulator drivers to unregister a regulator. */ @@ -1846,7 +1954,7 @@ void regulator_unregister(struct regulator_dev *rdev) EXPORT_SYMBOL_GPL(regulator_unregister); /** - * regulator_suspend_prepare: prepare regulators for system wide suspend + * regulator_suspend_prepare - prepare regulators for system wide suspend * @state: system suspend state * * Configure each regulator with it's suspend operating parameters for state. @@ -1882,7 +1990,7 @@ EXPORT_SYMBOL_GPL(regulator_suspend_prepare); /** * rdev_get_drvdata - get rdev regulator driver data - * @regulator: regulator + * @rdev: regulator * * Get rdev regulator driver private data. This call can be used in the * regulator driver context. @@ -1919,7 +2027,7 @@ EXPORT_SYMBOL_GPL(regulator_set_drvdata); /** * regulator_get_id - get regulator ID - * @regulator: regulator + * @rdev: regulator */ int rdev_get_id(struct regulator_dev *rdev) { diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 773b29cec8be..fe77730a7edb 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -102,7 +102,7 @@ static int da903x_set_ldo_voltage(struct regulator_dev *rdev, uint8_t val, mask; if (check_range(info, min_uV, max_uV)) { - pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); return -EINVAL; } @@ -159,7 +159,7 @@ static int da903x_is_enabled(struct regulator_dev *rdev) if (ret) return ret; - return reg_val & (1 << info->enable_bit); + return !!(reg_val & (1 << info->enable_bit)); } /* DA9030 specific operations */ @@ -172,7 +172,7 @@ static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev, int ret; if (check_range(info, min_uV, max_uV)) { - pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); return -EINVAL; } @@ -199,7 +199,7 @@ static int da9030_set_ldo14_voltage(struct regulator_dev *rdev, int thresh; if (check_range(info, min_uV, max_uV)) { - pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); return -EINVAL; } @@ -248,7 +248,7 @@ static int da9034_set_dvc_voltage(struct regulator_dev *rdev, int ret; if (check_range(info, min_uV, max_uV)) { - pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); return -EINVAL; } @@ -273,7 +273,7 @@ static int da9034_set_ldo12_voltage(struct regulator_dev *rdev, uint8_t val, mask; if (check_range(info, min_uV, max_uV)) { - pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV); return -EINVAL; } diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index c68c496b2c49..7aa35248181b 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1412,6 +1412,97 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg, } EXPORT_SYMBOL_GPL(wm8350_register_regulator); +/** + * wm8350_register_led - Register a WM8350 LED output + * + * @param wm8350 The WM8350 device to configure. + * @param lednum LED device index to create. + * @param dcdc The DCDC to use for the LED. + * @param isink The ISINK to use for the LED. + * @param pdata Configuration for the LED. + * + * The WM8350 supports the use of an ISINK together with a DCDC to + * provide a power-efficient LED driver. This function registers the + * regulators and instantiates the platform device for a LED. The + * operating modes for the LED regulators must be configured using + * wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and + * wm8350_dcdc_set_slot() prior to calling this function. + */ +int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink, + struct wm8350_led_platform_data *pdata) +{ + struct wm8350_led *led; + struct platform_device *pdev; + int ret; + + if (lednum > ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) { + dev_err(wm8350->dev, "Invalid LED index %d\n", lednum); + return -ENODEV; + } + + led = &wm8350->pmic.led[lednum]; + + if (led->pdev) { + dev_err(wm8350->dev, "LED %d already allocated\n", lednum); + return -EINVAL; + } + + pdev = platform_device_alloc("wm8350-led", lednum); + if (pdev == NULL) { + dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum); + return -ENOMEM; + } + + led->isink_consumer.dev = &pdev->dev; + led->isink_consumer.supply = "led_isink"; + led->isink_init.num_consumer_supplies = 1; + led->isink_init.consumer_supplies = &led->isink_consumer; + led->isink_init.constraints.min_uA = 0; + led->isink_init.constraints.max_uA = pdata->max_uA; + led->isink_init.constraints.valid_ops_mask = REGULATOR_CHANGE_CURRENT; + led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL; + ret = wm8350_register_regulator(wm8350, isink, &led->isink_init); + if (ret != 0) { + platform_device_put(pdev); + return ret; + } + + led->dcdc_consumer.dev = &pdev->dev; + led->dcdc_consumer.supply = "led_vcc"; + led->dcdc_init.num_consumer_supplies = 1; + led->dcdc_init.consumer_supplies = &led->dcdc_consumer; + led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL; + ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init); + if (ret != 0) { + platform_device_put(pdev); + return ret; + } + + switch (isink) { + case WM8350_ISINK_A: + wm8350->pmic.isink_A_dcdc = dcdc; + break; + case WM8350_ISINK_B: + wm8350->pmic.isink_B_dcdc = dcdc; + break; + } + + pdev->dev.platform_data = pdata; + pdev->dev.parent = wm8350->dev; + ret = platform_device_add(pdev); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to register LED %d: %d\n", + lednum, ret); + platform_device_put(pdev); + return ret; + } + + led->pdev = pdev; + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_register_led); + static struct platform_driver wm8350_regulator_driver = { .probe = wm8350_regulator_probe, .remove = wm8350_regulator_remove, diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 162330b9d1dc..7e5155e88ac7 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -86,13 +86,11 @@ enum ds_type { struct ds1307 { - u8 reg_addr; u8 regs[11]; enum ds_type type; unsigned long flags; #define HAS_NVRAM 0 /* bit 0 == sysfs file active */ #define HAS_ALARM 1 /* bit 1 == irq claimed */ - struct i2c_msg msg[2]; struct i2c_client *client; struct rtc_device *rtc; struct work_struct work; @@ -204,13 +202,9 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) int tmp; /* read the RTC date and time registers all at once */ - ds1307->reg_addr = 0; - ds1307->msg[1].flags = I2C_M_RD; - ds1307->msg[1].len = 7; - - tmp = i2c_transfer(to_i2c_adapter(ds1307->client->dev.parent), - ds1307->msg, 2); - if (tmp != 2) { + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + DS1307_REG_SECS, 7, ds1307->regs); + if (tmp != 7) { dev_err(dev, "%s error %d\n", "read", tmp); return -EIO; } @@ -257,7 +251,6 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) t->tm_hour, t->tm_mday, t->tm_mon, t->tm_year, t->tm_wday); - *buf++ = 0; /* first register addr */ buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec); buf[DS1307_REG_MIN] = bin2bcd(t->tm_min); buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); @@ -282,23 +275,19 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) break; } - ds1307->msg[1].flags = 0; - ds1307->msg[1].len = 8; - dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", "write", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); - result = i2c_transfer(to_i2c_adapter(ds1307->client->dev.parent), - &ds1307->msg[1], 1); - if (result != 1) { - dev_err(dev, "%s error %d\n", "write", tmp); - return -EIO; + result = i2c_smbus_write_i2c_block_data(ds1307->client, 0, 7, buf); + if (result < 0) { + dev_err(dev, "%s error %d\n", "write", result); + return result; } return 0; } -static int ds1307_read_alarm(struct device *dev, struct rtc_wkalrm *t) +static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); @@ -308,13 +297,9 @@ static int ds1307_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* read all ALARM1, ALARM2, and status registers at once */ - ds1307->reg_addr = DS1339_REG_ALARM1_SECS; - ds1307->msg[1].flags = I2C_M_RD; - ds1307->msg[1].len = 9; - - ret = i2c_transfer(to_i2c_adapter(client->dev.parent), - ds1307->msg, 2); - if (ret != 2) { + ret = i2c_smbus_read_i2c_block_data(client, + DS1339_REG_ALARM1_SECS, 9, ds1307->regs); + if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm read", ret); return -EIO; } @@ -353,7 +338,7 @@ static int ds1307_read_alarm(struct device *dev, struct rtc_wkalrm *t) return 0; } -static int ds1307_set_alarm(struct device *dev, struct rtc_wkalrm *t) +static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct i2c_client *client = to_i2c_client(dev); struct ds1307 *ds1307 = i2c_get_clientdata(client); @@ -371,13 +356,9 @@ static int ds1307_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* read current status of both alarms and the chip */ - ds1307->reg_addr = DS1339_REG_ALARM1_SECS; - ds1307->msg[1].flags = I2C_M_RD; - ds1307->msg[1].len = 9; - - ret = i2c_transfer(to_i2c_adapter(client->dev.parent), - ds1307->msg, 2); - if (ret != 2) { + ret = i2c_smbus_read_i2c_block_data(client, + DS1339_REG_ALARM1_SECS, 9, buf); + if (ret != 9) { dev_err(dev, "%s error %d\n", "alarm write", ret); return -EIO; } @@ -392,7 +373,6 @@ static int ds1307_set_alarm(struct device *dev, struct rtc_wkalrm *t) ds1307->regs[6], control, status); /* set ALARM1, using 24 hour and day-of-month modes */ - *buf++ = DS1339_REG_ALARM1_SECS; /* first register addr */ buf[0] = bin2bcd(t->time.tm_sec); buf[1] = bin2bcd(t->time.tm_min); buf[2] = bin2bcd(t->time.tm_hour); @@ -411,14 +391,11 @@ static int ds1307_set_alarm(struct device *dev, struct rtc_wkalrm *t) } buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - ds1307->msg[1].flags = 0; - ds1307->msg[1].len = 10; - - ret = i2c_transfer(to_i2c_adapter(client->dev.parent), - &ds1307->msg[1], 1); - if (ret != 1) { + ret = i2c_smbus_write_i2c_block_data(client, + DS1339_REG_ALARM1_SECS, 9, buf); + if (ret < 0) { dev_err(dev, "can't set alarm time\n"); - return -EIO; + return ret; } return 0; @@ -475,8 +452,8 @@ static int ds1307_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) static const struct rtc_class_ops ds13xx_rtc_ops = { .read_time = ds1307_get_time, .set_time = ds1307_set_time, - .read_alarm = ds1307_read_alarm, - .set_alarm = ds1307_set_alarm, + .read_alarm = ds1337_read_alarm, + .set_alarm = ds1337_set_alarm, .ioctl = ds1307_ioctl, }; @@ -490,7 +467,6 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, { struct i2c_client *client; struct ds1307 *ds1307; - struct i2c_msg msg[2]; int result; client = kobj_to_i2c_client(kobj); @@ -503,24 +479,10 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, if (unlikely(!count)) return count; - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = buf; - - buf[0] = 8 + off; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = count; - msg[1].buf = buf; - - result = i2c_transfer(to_i2c_adapter(client->dev.parent), msg, 2); - if (result != 2) { + result = i2c_smbus_read_i2c_block_data(client, 8 + off, count, buf); + if (result < 0) dev_err(&client->dev, "%s error %d\n", "nvram read", result); - return -EIO; - } - return count; + return result; } static ssize_t @@ -528,8 +490,7 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client; - u8 buffer[NVRAM_SIZE + 1]; - int ret; + int result; client = kobj_to_i2c_client(kobj); @@ -540,11 +501,12 @@ ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, if (unlikely(!count)) return count; - buffer[0] = 8 + off; - memcpy(buffer + 1, buf, count); - - ret = i2c_master_send(client, buffer, count + 1); - return (ret < 0) ? ret : (ret - 1); + result = i2c_smbus_write_i2c_block_data(client, 8 + off, count, buf); + if (result < 0) { + dev_err(&client->dev, "%s error %d\n", "nvram write", result); + return result; + } + return count; } static struct bin_attribute nvram = { @@ -571,9 +533,11 @@ static int __devinit ds1307_probe(struct i2c_client *client, const struct chip_desc *chip = &chips[id->driver_data]; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int want_irq = false; + unsigned char *buf; if (!i2c_check_functionality(adapter, - I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) @@ -581,18 +545,8 @@ static int __devinit ds1307_probe(struct i2c_client *client, ds1307->client = client; i2c_set_clientdata(client, ds1307); - - ds1307->msg[0].addr = client->addr; - ds1307->msg[0].flags = 0; - ds1307->msg[0].len = 1; - ds1307->msg[0].buf = &ds1307->reg_addr; - - ds1307->msg[1].addr = client->addr; - ds1307->msg[1].flags = I2C_M_RD; - ds1307->msg[1].len = sizeof(ds1307->regs); - ds1307->msg[1].buf = ds1307->regs; - ds1307->type = id->driver_data; + buf = ds1307->regs; switch (ds1307->type) { case ds_1337: @@ -602,21 +556,15 @@ static int __devinit ds1307_probe(struct i2c_client *client, INIT_WORK(&ds1307->work, ds1307_work); want_irq = true; } - - ds1307->reg_addr = DS1337_REG_CONTROL; - ds1307->msg[1].len = 2; - /* get registers that the "rtc" read below won't read... */ - tmp = i2c_transfer(adapter, ds1307->msg, 2); + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, + DS1337_REG_CONTROL, 2, buf); if (tmp != 2) { pr_debug("read error %d\n", tmp); err = -EIO; goto exit_free; } - ds1307->reg_addr = 0; - ds1307->msg[1].len = sizeof(ds1307->regs); - /* oscillator off? turn it on, so clock can tick. */ if (ds1307->regs[0] & DS1337_BIT_nEOSC) ds1307->regs[0] &= ~DS1337_BIT_nEOSC; @@ -647,9 +595,8 @@ static int __devinit ds1307_probe(struct i2c_client *client, read_rtc: /* read RTC registers */ - - tmp = i2c_transfer(adapter, ds1307->msg, 2); - if (tmp != 2) { + tmp = i2c_smbus_read_i2c_block_data(ds1307->client, 0, 8, buf); + if (tmp != 8) { pr_debug("read error %d\n", tmp); err = -EIO; goto exit_free; @@ -707,22 +654,6 @@ read_rtc: break; } - tmp = ds1307->regs[DS1307_REG_SECS]; - tmp = bcd2bin(tmp & 0x7f); - if (tmp > 60) - goto exit_bad; - tmp = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); - if (tmp > 60) - goto exit_bad; - - tmp = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f); - if (tmp == 0 || tmp > 31) - goto exit_bad; - - tmp = bcd2bin(ds1307->regs[DS1307_REG_MONTH] & 0x1f); - if (tmp == 0 || tmp > 12) - goto exit_bad; - tmp = ds1307->regs[DS1307_REG_HOUR]; switch (ds1307->type) { case ds_1340: @@ -779,13 +710,6 @@ read_rtc: return 0; -exit_bad: - dev_dbg(&client->dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", - "bogus register", - ds1307->regs[0], ds1307->regs[1], - ds1307->regs[2], ds1307->regs[3], - ds1307->regs[4], ds1307->regs[5], - ds1307->regs[6]); exit_irq: if (ds1307->rtc) rtc_device_unregister(ds1307->rtc); diff --git a/drivers/rtc/rtc-parisc.c b/drivers/rtc/rtc-parisc.c index 346d633655e7..c6bfa6fe1a2a 100644 --- a/drivers/rtc/rtc-parisc.c +++ b/drivers/rtc/rtc-parisc.c @@ -34,7 +34,8 @@ static int parisc_get_time(struct device *dev, struct rtc_time *tm) static int parisc_set_time(struct device *dev, struct rtc_time *tm) { struct parisc_rtc *p = dev_get_drvdata(dev); - unsigned long flags, ret; + unsigned long flags; + int ret; spin_lock_irqsave(&p->lock, flags); ret = set_rtc_time(tm); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 570ae59c1d5e..bd5914994142 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -336,6 +336,9 @@ static int dasd_state_ready_to_online(struct dasd_device * device) { int rc; + struct gendisk *disk; + struct disk_part_iter piter; + struct hd_struct *part; if (device->discipline->ready_to_online) { rc = device->discipline->ready_to_online(device); @@ -343,8 +346,14 @@ dasd_state_ready_to_online(struct dasd_device * device) return rc; } device->state = DASD_STATE_ONLINE; - if (device->block) + if (device->block) { dasd_schedule_block_bh(device->block); + disk = device->block->bdev->bd_disk; + disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); + while ((part = disk_part_iter_next(&piter))) + kobject_uevent(&part_to_dev(part)->kobj, KOBJ_CHANGE); + disk_part_iter_exit(&piter); + } return 0; } @@ -354,6 +363,9 @@ dasd_state_ready_to_online(struct dasd_device * device) static int dasd_state_online_to_ready(struct dasd_device *device) { int rc; + struct gendisk *disk; + struct disk_part_iter piter; + struct hd_struct *part; if (device->discipline->online_to_ready) { rc = device->discipline->online_to_ready(device); @@ -361,6 +373,13 @@ static int dasd_state_online_to_ready(struct dasd_device *device) return rc; } device->state = DASD_STATE_READY; + if (device->block) { + disk = device->block->bdev->bd_disk; + disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); + while ((part = disk_part_iter_next(&piter))) + kobject_uevent(&part_to_dev(part)->kobj, KOBJ_CHANGE); + disk_part_iter_exit(&piter); + } return 0; } diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index b8f9c00633f3..d82aad5224f0 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -2621,7 +2621,7 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } } - /* double-check if current erp/cqr was successfull */ + /* double-check if current erp/cqr was successful */ if ((cqr->irb.scsw.cmd.cstat == 0x00) && (cqr->irb.scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) { diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 2ef25731d197..300e28a531f8 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -206,6 +206,8 @@ dasd_feature_list(char *str, char **endp) features |= DASD_FEATURE_USEDIAG; else if (len == 6 && !strncmp(str, "erplog", 6)) features |= DASD_FEATURE_ERPLOG; + else if (len == 8 && !strncmp(str, "failfast", 8)) + features |= DASD_FEATURE_FAILFAST; else { MESSAGE(KERN_WARNING, "unsupported feature: %*s, " @@ -667,6 +669,51 @@ dasd_device_from_cdev(struct ccw_device *cdev) */ /* + * failfast controls the behaviour, if no path is available + */ +static ssize_t dasd_ff_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dasd_devmap *devmap; + int ff_flag; + + devmap = dasd_find_busid(dev->bus_id); + if (!IS_ERR(devmap)) + ff_flag = (devmap->features & DASD_FEATURE_FAILFAST) != 0; + else + ff_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_FAILFAST) != 0; + return snprintf(buf, PAGE_SIZE, ff_flag ? "1\n" : "0\n"); +} + +static ssize_t dasd_ff_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + int val; + char *endp; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + + val = simple_strtoul(buf, &endp, 0); + if (((endp + 1) < (buf + count)) || (val > 1)) + return -EINVAL; + + spin_lock(&dasd_devmap_lock); + if (val) + devmap->features |= DASD_FEATURE_FAILFAST; + else + devmap->features &= ~DASD_FEATURE_FAILFAST; + if (devmap->device) + devmap->device->features = devmap->features; + spin_unlock(&dasd_devmap_lock); + return count; +} + +static DEVICE_ATTR(failfast, 0644, dasd_ff_show, dasd_ff_store); + +/* * readonly controls the readonly status of a dasd */ static ssize_t @@ -1020,6 +1067,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_use_diag.attr, &dev_attr_eer_enabled.attr, &dev_attr_erplog.attr, + &dev_attr_failfast.attr, NULL, }; diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 7844461a995b..ef2a56952054 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -544,7 +544,8 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, } cqr->retries = DIAG_MAX_RETRIES; cqr->buildclk = get_clock(); - if (blk_noretry_request(req)) + if (blk_noretry_request(req) || + block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->startdev = memdev; cqr->memdev = memdev; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index bd2c52e20762..bdb87998f364 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1700,7 +1700,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, recid++; } } - if (blk_noretry_request(req)) + if (blk_noretry_request(req) || + block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->startdev = startdev; cqr->memdev = startdev; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 7d442aeff3d1..f1d176021694 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -355,7 +355,8 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, recid++; } } - if (blk_noretry_request(req)) + if (blk_noretry_request(req) || + block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->startdev = memdev; cqr->memdev = memdev; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 05a14536c369..4a39084d9c95 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -199,7 +199,7 @@ struct dasd_ccw_req { #define DASD_CQR_ERROR 0x82 /* request is completed with error */ #define DASD_CQR_CLEAR_PENDING 0x83 /* request is clear pending */ #define DASD_CQR_CLEARED 0x84 /* request was cleared */ -#define DASD_CQR_SUCCESS 0x85 /* request was successfull */ +#define DASD_CQR_SUCCESS 0x85 /* request was successful */ /* per dasd_ccw_req flags */ diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 643033890e34..0769ced52dbd 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -100,7 +100,7 @@ comment "S/390 tape interface support" config S390_TAPE_BLOCK bool "Support for tape block devices" - depends on S390_TAPE + depends on S390_TAPE && BLOCK help Select this option if you want to access your channel-attached tape devices using the block device interface. This interface is similar diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index 4005c44a404c..71605a179d65 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -801,7 +801,7 @@ tape_3590_done(struct tape_device *device, struct tape_request *request) static inline int tape_3590_erp_succeded(struct tape_device *device, struct tape_request *request) { - DBF_EVENT(3, "Error Recovery successfull for %s\n", + DBF_EVENT(3, "Error Recovery successful for %s\n", tape_op_verbose[request->op]); return tape_3590_done(device, request); } diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 06b71823f399..659f8a791656 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -379,7 +379,7 @@ int cio_commit_config(struct subchannel *sch) if (ccode < 0) /* -EIO if msch gets a program check. */ return ccode; switch (ccode) { - case 0: /* successfull */ + case 0: /* successful */ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib)) return -ENODEV; diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index f8a3b6967f69..da7afb04e71f 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -169,6 +169,8 @@ static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev) q->nr); debugfs_queues[i] = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR, debugfs_root, q, &debugfs_fops); + if (IS_ERR(debugfs_queues[i])) + debugfs_queues[i] = NULL; } void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 744f928a59ea..10cb0f8726e5 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -114,7 +114,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq) * @count: count of buffers to examine * @auto_ack: automatically acknowledge buffers * - * Returns the number of successfull extracted equal buffer states. + * Returns the number of successfully extracted equal buffer states. * Stops processing if a state is different from the last buffers state. */ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 591a2b3ae4cb..c4f1b046c3b1 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -916,6 +916,21 @@ static struct ethtool_ops qeth_l2_osn_ops = { .get_drvinfo = qeth_core_get_drvinfo, }; +static struct net_device_ops qeth_l2_netdev_ops = { + .ndo_open = qeth_l2_open, + .ndo_stop = qeth_l2_stop, + .ndo_get_stats = qeth_get_stats, + .ndo_start_xmit = qeth_l2_hard_start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_multicast_list = qeth_l2_set_multicast_list, + .ndo_do_ioctl = qeth_l2_do_ioctl, + .ndo_set_mac_address = qeth_l2_set_mac_address, + .ndo_change_mtu = qeth_change_mtu, + .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid, + .ndo_tx_timeout = qeth_tx_timeout, +}; + static int qeth_l2_setup_netdev(struct qeth_card *card) { switch (card->info.type) { @@ -937,19 +952,9 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) return -ENODEV; card->dev->ml_priv = card; - card->dev->tx_timeout = &qeth_tx_timeout; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; - card->dev->open = qeth_l2_open; - card->dev->stop = qeth_l2_stop; - card->dev->hard_start_xmit = qeth_l2_hard_start_xmit; - card->dev->do_ioctl = qeth_l2_do_ioctl; - card->dev->get_stats = qeth_get_stats; - card->dev->change_mtu = qeth_change_mtu; - card->dev->set_multicast_list = qeth_l2_set_multicast_list; - card->dev->vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid; - card->dev->vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid; - card->dev->set_mac_address = qeth_l2_set_mac_address; card->dev->mtu = card->info.initial_mtu; + card->dev->netdev_ops = &qeth_l2_netdev_ops; if (card->info.type != QETH_CARD_TYPE_OSN) SET_ETHTOOL_OPS(card->dev, &qeth_l2_ethtool_ops); else diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 4693ee4e7b98..68d623ab7e6e 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1829,28 +1829,6 @@ static void qeth_l3_vlan_rx_register(struct net_device *dev, static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { - struct net_device *vlandev; - struct qeth_card *card = dev->ml_priv; - struct in_device *in_dev; - - if (card->info.type == QETH_CARD_TYPE_IQD) - return; - - vlandev = vlan_group_get_device(card->vlangrp, vid); - vlandev->neigh_setup = qeth_l3_neigh_setup; - - in_dev = in_dev_get(vlandev); -#ifdef CONFIG_SYSCTL - neigh_sysctl_unregister(in_dev->arp_parms); -#endif - neigh_parms_release(&arp_tbl, in_dev->arp_parms); - - in_dev->arp_parms = neigh_parms_alloc(vlandev, &arp_tbl); -#ifdef CONFIG_SYSCTL - neigh_sysctl_register(vlandev, in_dev->arp_parms, NET_IPV4, - NET_IPV4_NEIGH, "ipv4", NULL, NULL); -#endif - in_dev_put(in_dev); return; } @@ -2916,6 +2894,21 @@ qeth_l3_neigh_setup(struct net_device *dev, struct neigh_parms *np) return 0; } +static struct net_device_ops qeth_l3_netdev_ops = { + .ndo_open = qeth_l3_open, + .ndo_stop = qeth_l3_stop, + .ndo_get_stats = qeth_get_stats, + .ndo_start_xmit = qeth_l3_hard_start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_multicast_list = qeth_l3_set_multicast_list, + .ndo_do_ioctl = qeth_l3_do_ioctl, + .ndo_change_mtu = qeth_change_mtu, + .ndo_vlan_rx_register = qeth_l3_vlan_rx_register, + .ndo_vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid, + .ndo_tx_timeout = qeth_tx_timeout, +}; + static int qeth_l3_setup_netdev(struct qeth_card *card) { if (card->info.type == QETH_CARD_TYPE_OSAE) { @@ -2930,7 +2923,8 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) card->dev = alloc_etherdev(0); if (!card->dev) return -ENODEV; - card->dev->neigh_setup = qeth_l3_neigh_setup; + qeth_l3_netdev_ops.ndo_neigh_setup = + qeth_l3_neigh_setup; /*IPv6 address autoconfiguration stuff*/ qeth_l3_get_unique_id(card); @@ -2947,21 +2941,10 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) } else return -ENODEV; - card->dev->hard_start_xmit = qeth_l3_hard_start_xmit; card->dev->ml_priv = card; - card->dev->tx_timeout = &qeth_tx_timeout; card->dev->watchdog_timeo = QETH_TX_TIMEOUT; - card->dev->open = qeth_l3_open; - card->dev->stop = qeth_l3_stop; - card->dev->do_ioctl = qeth_l3_do_ioctl; - card->dev->get_stats = qeth_get_stats; - card->dev->change_mtu = qeth_change_mtu; - card->dev->set_multicast_list = qeth_l3_set_multicast_list; - card->dev->vlan_rx_register = qeth_l3_vlan_rx_register; - card->dev->vlan_rx_add_vid = qeth_l3_vlan_rx_add_vid; - card->dev->vlan_rx_kill_vid = qeth_l3_vlan_rx_kill_vid; card->dev->mtu = card->info.initial_mtu; - card->dev->set_mac_address = NULL; + card->dev->netdev_ops = &qeth_l3_netdev_ops; SET_ETHTOOL_OPS(card->dev, &qeth_l3_ethtool_ops); card->dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index 2550af4ae432..4431578d8c45 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -214,7 +214,7 @@ static int __devinit d7s_probe(struct of_device *op, writeb(regs, p->regs); - printk(KERN_INFO PFX "7-Segment Display%s at [%s:0x%lx] %s\n", + printk(KERN_INFO PFX "7-Segment Display%s at [%s:0x%llx] %s\n", op->node->full_name, (regs & D7S_FLIP) ? " (FLIPPED)" : "", op->resource[0].start, diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index b7322976d2b7..256c7bec7bd7 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -884,6 +884,7 @@ config SCSI_IBMVSCSI tristate "IBM Virtual SCSI support" depends on PPC_PSERIES || PPC_ISERIES select SCSI_SRP_ATTRS + select VIOPATH if PPC_ISERIES help This is the IBM POWER Virtual SCSI Client diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c index 9e64b21ef637..c889d8458684 100644 --- a/drivers/scsi/NCR_D700.c +++ b/drivers/scsi/NCR_D700.c @@ -318,7 +318,7 @@ NCR_D700_probe(struct device *dev) return -ENOMEM; p->dev = dev; - snprintf(p->name, sizeof(p->name), "D700(%s)", dev->bus_id); + snprintf(p->name, sizeof(p->name), "D700(%s)", dev_name(dev)); if (request_irq(irq, NCR_D700_intr, IRQF_SHARED, p->name, p)) { printk(KERN_ERR "D700: request_irq failed\n"); kfree(p); diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index 07d572feceed..37dd47136fb1 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -169,10 +169,8 @@ int __init a2091_detect(struct scsi_host_template *tpnt) continue; instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata)); - if (instance == NULL) { - release_mem_region(address, 256); - continue; - } + if (instance == NULL) + goto release; instance->base = ZTWO_VADDR(address); instance->irq = IRQ_AMIGA_PORTS; instance->unique_id = z->slotaddr; @@ -183,10 +181,18 @@ int __init a2091_detect(struct scsi_host_template *tpnt) HDATA(instance)->fast = 0; HDATA(instance)->dma_mode = CTRL_DMA; wd33c93_init(instance, regs, dma_setup, dma_stop, WD33C93_FS_8_10); - request_irq(IRQ_AMIGA_PORTS, a2091_intr, IRQF_SHARED, "A2091 SCSI", - instance); + if (request_irq(IRQ_AMIGA_PORTS, a2091_intr, IRQF_SHARED, "A2091 SCSI", + instance)) + goto unregister; DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; num_a2091++; + continue; + +unregister: + scsi_unregister(instance); + wd33c93_release(); +release: + release_mem_region(address, 256); } return num_a2091; diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 2f602720193e..7507d8bc57a1 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -2527,7 +2527,7 @@ static void asc_prt_scsi_host(struct Scsi_Host *s) { struct asc_board *boardp = shost_priv(s); - printk("Scsi_Host at addr 0x%p, device %s\n", s, boardp->dev->bus_id); + printk("Scsi_Host at addr 0x%p, device %s\n", s, dev_name(boardp->dev)); printk(" host_busy %u, host_no %d, last_reset %d,\n", s->host_busy, s->host_no, (unsigned)s->last_reset); diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index d4640ef6d44f..78eb86fc6276 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -189,7 +189,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev) asd_clear_nexus_I_T(dev, NEXUS_PHASE_PRE); /* send a hard reset */ ASD_DPRINTK("sending %s reset to %s\n", - reset_type ? "hard" : "soft", phy->dev.bus_id); + reset_type ? "hard" : "soft", dev_name(&phy->dev)); res = sas_phy_reset(phy, reset_type); if (res == TMF_RESP_FUNC_COMPLETE) { /* wait for the maximum settle time */ diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/drivers/scsi/cxgb3i/cxgb3i_ddp.c index 1a41f04264f7..08f3a09d9233 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_ddp.c +++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.c @@ -11,6 +11,7 @@ */ #include <linux/skbuff.h> +#include <linux/scatterlist.h> /* from cxgb3 LLD */ #include "common.h" diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index ca7363752401..5d1bf7e3d245 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -329,12 +329,16 @@ int __init gvp11_detect(struct scsi_host_template *tpnt) (epc & GVP_SCSICLKMASK) ? WD33C93_FS_8_10 : WD33C93_FS_12_15); - request_irq(IRQ_AMIGA_PORTS, gvp11_intr, IRQF_SHARED, "GVP11 SCSI", - instance); + if (request_irq(IRQ_AMIGA_PORTS, gvp11_intr, IRQF_SHARED, "GVP11 SCSI", + instance)) + goto unregister; DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; num_gvp11++; continue; +unregister: + scsi_unregister(instance); + wd33c93_release(); release: release_mem_region(address, 256); } diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 3fdbb13e80a8..aa670a1d1513 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -388,8 +388,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->dma_boundary = 0xffffffff; device_initialize(&shost->shost_gendev); - snprintf(shost->shost_gendev.bus_id, BUS_ID_SIZE, "host%d", - shost->host_no); + dev_set_name(&shost->shost_gendev, "host%d", shost->host_no); #ifndef CONFIG_SYSFS_DEPRECATED shost->shost_gendev.bus = &scsi_bus_type; #endif @@ -398,8 +397,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) device_initialize(&shost->shost_dev); shost->shost_dev.parent = &shost->shost_gendev; shost->shost_dev.class = &shost_class; - snprintf(shost->shost_dev.bus_id, BUS_ID_SIZE, "host%d", - shost->host_no); + dev_set_name(&shost->shost_dev, "host%d", shost->host_no); shost->shost_dev.groups = scsi_sysfs_shost_attr_groups; shost->ehandler = kthread_run(scsi_error_handler, shost, diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 44f202f33101..ee0739b217b6 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -101,7 +101,7 @@ static const struct { { IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_ESTABLISH, DID_ERROR, 1, 1, "unable to establish" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_FAULT, DID_OK, 1, 0, "transport fault" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_CMD_TIMEOUT, DID_TIME_OUT, 1, 1, "command timeout" }, - { IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_NO_CONNECT, 1, 1, "network down" }, + { IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_TRANSPORT_DISRUPTED, 1, 1, "network down" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_HW_FAILURE, DID_ERROR, 1, 1, "hardware failure" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DOWN_ERR, DID_REQUEUE, 0, 0, "link down" }, { IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DEAD_ERR, DID_ERROR, 0, 0, "link dead" }, @@ -115,11 +115,11 @@ static const struct { { IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" }, { IBMVFC_VIOS_FAILURE, IBMVFC_SW_FAILURE, DID_ERROR, 0, 1, "software failure" }, - { IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ABORT, 0, 1, "invalid parameter" }, - { IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ABORT, 0, 1, "missing parameter" }, + { IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ERROR, 0, 1, "invalid parameter" }, + { IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ERROR, 0, 1, "missing parameter" }, { IBMVFC_VIOS_FAILURE, IBMVFC_HOST_IO_BUS, DID_ERROR, 1, 1, "host I/O bus failure" }, - { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ABORT, 0, 1, "transaction cancelled" }, - { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ABORT, 0, 1, "transaction cancelled implicit" }, + { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ERROR, 0, 1, "transaction cancelled" }, + { IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ERROR, 0, 1, "transaction cancelled implicit" }, { IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" }, { IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" }, { IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" }, @@ -1145,10 +1145,10 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs); strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME); strncpy(login_info->device_name, - vhost->host->shost_gendev.bus_id, IBMVFC_MAX_NAME); + dev_name(&vhost->host->shost_gendev), IBMVFC_MAX_NAME); location = of_get_property(of_node, "ibm,loc-code", NULL); - location = location ? location : vhost->dev->bus_id; + location = location ? location : dev_name(vhost->dev); strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME); } diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 868d35ea01bb..5c541f7850f9 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -89,6 +89,7 @@ static int max_id = 64; static int max_channel = 3; static int init_timeout = 5; static int max_requests = IBMVSCSI_MAX_REQUESTS_DEFAULT; +static int max_events = IBMVSCSI_MAX_REQUESTS_DEFAULT + 2; static struct scsi_transport_template *ibmvscsi_transport_template; @@ -1633,7 +1634,7 @@ static struct scsi_host_template driver_template = { static unsigned long ibmvscsi_get_desired_dma(struct vio_dev *vdev) { /* iu_storage data allocated in initialize_event_pool */ - unsigned long desired_io = max_requests * sizeof(union viosrp_iu); + unsigned long desired_io = max_events * sizeof(union viosrp_iu); /* add io space for sg data */ desired_io += (IBMVSCSI_MAX_SECTORS_DEFAULT * 512 * @@ -1657,7 +1658,6 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) vdev->dev.driver_data = NULL; - driver_template.can_queue = max_requests - 2; host = scsi_host_alloc(&driver_template, sizeof(*hostdata)); if (!host) { dev_err(&vdev->dev, "couldn't allocate host data\n"); @@ -1673,12 +1673,12 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) atomic_set(&hostdata->request_limit, -1); hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT; - rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_requests); + rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_events); if (rc != 0 && rc != H_RESOURCE) { dev_err(&vdev->dev, "couldn't initialize crq. rc=%d\n", rc); goto init_crq_failed; } - if (initialize_event_pool(&hostdata->pool, max_requests, hostdata) != 0) { + if (initialize_event_pool(&hostdata->pool, max_events, hostdata) != 0) { dev_err(&vdev->dev, "couldn't initialize event pool\n"); goto init_pool_failed; } @@ -1730,7 +1730,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) add_host_failed: release_event_pool(&hostdata->pool, hostdata); init_pool_failed: - ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_requests); + ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, max_events); init_crq_failed: scsi_host_put(host); scsi_host_alloc_failed: @@ -1742,7 +1742,7 @@ static int ibmvscsi_remove(struct vio_dev *vdev) struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data; release_event_pool(&hostdata->pool, hostdata); ibmvscsi_ops->release_crq_queue(&hostdata->queue, hostdata, - max_requests); + max_events); srp_remove_host(hostdata->host); scsi_remove_host(hostdata->host); @@ -1779,6 +1779,10 @@ int __init ibmvscsi_module_init(void) { int ret; + /* Ensure we have two requests to do error recovery */ + driver_template.can_queue = max_requests; + max_events = max_requests + 2; + if (firmware_has_feature(FW_FEATURE_ISERIES)) ibmvscsi_ops = &iseriesvscsi_ops; else if (firmware_has_feature(FW_FEATURE_VIO)) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 0edfb1fa63ce..841f460edbc4 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -2184,7 +2184,7 @@ static void ipr_dump_location_data(struct ipr_ioa_cfg *ioa_cfg, sizeof(struct ipr_dump_entry_header); driver_dump->location_entry.hdr.data_type = IPR_DUMP_DATA_TYPE_ASCII; driver_dump->location_entry.hdr.id = IPR_DUMP_LOCATION_ID; - strcpy(driver_dump->location_entry.location, ioa_cfg->pdev->dev.bus_id); + strcpy(driver_dump->location_entry.location, dev_name(&ioa_cfg->pdev->dev)); driver_dump->hdr.num_entries++; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 59459141b437..8f872f816fe4 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1272,7 +1272,7 @@ struct ipr_dump_entry_header { struct ipr_dump_location_entry { struct ipr_dump_entry_header hdr; - u8 location[BUS_ID_SIZE]; + u8 location[20]; }__attribute__((packed)); struct ipr_dump_trace_entry { diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c index 3126824da36d..4a4e6954ec79 100644 --- a/drivers/scsi/lasi700.c +++ b/drivers/scsi/lasi700.c @@ -103,8 +103,7 @@ lasi700_probe(struct parisc_device *dev) hostdata = kzalloc(sizeof(*hostdata), GFP_KERNEL); if (!hostdata) { - printk(KERN_ERR "%s: Failed to allocate host data\n", - dev->dev.bus_id); + dev_printk(KERN_ERR, dev, "Failed to allocate host data\n"); return -ENOMEM; } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 709a6f75ca9d..facc5bfcf7db 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -169,7 +169,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) if (res) { printk("sas: driver on pcidev %s cannot handle " "device %llx, error:%d\n", - sas_ha->dev->bus_id, + dev_name(sas_ha->dev), SAS_ADDR(dev->sas_addr), res); } } diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index bf34a236f946..c17c25030f1c 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c @@ -56,7 +56,7 @@ void sas_dprint_phye(int phyid, enum phy_event pe) void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he) { - SAS_DPRINTK("ha %s: %s event\n", sas_ha->dev->bus_id, + SAS_DPRINTK("ha %s: %s event\n", dev_name(sas_ha->dev), sas_hae_str[he]); } diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 16f93123271e..d110a366c48a 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -199,8 +199,8 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; case SMP_DISCOVER: - req->data_len =- 16; - if (req->data_len < 0) { + req->data_len -= 16; + if ((int)req->data_len < 0) { req->data_len = 0; error = -EINVAL; goto out; @@ -215,8 +215,8 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; case SMP_REPORT_PHY_SATA: - req->data_len =- 16; - if (req->data_len < 0) { + req->data_len -= 16; + if ((int)req->data_len < 0) { req->data_len = 0; error = -EINVAL; goto out; @@ -238,8 +238,8 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; case SMP_PHY_CONTROL: - req->data_len =- 44; - if (req->data_len < 0) { + req->data_len -= 44; + if ((int)req->data_len < 0) { req->data_len = 0; error = -EINVAL; goto out; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 139935a121b4..e6ac59c023f1 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -113,7 +113,7 @@ static void sas_form_port(struct asd_sas_phy *phy) sas_port_add_phy(port->port, phy->phy); SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n", - phy->phy->dev.bus_id,port->port->dev.bus_id, + dev_name(&phy->phy->dev), dev_name(&port->port->dev), port->phy_mask, SAS_ADDR(port->attached_sas_addr)); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4c77038c8f1c..6c867311cef1 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1795,12 +1795,13 @@ lpfc_block_mgmt_io(struct lpfc_hba * phba) int lpfc_online(struct lpfc_hba *phba) { - struct lpfc_vport *vport = phba->pport; + struct lpfc_vport *vport; struct lpfc_vport **vports; int i; if (!phba) return 0; + vport = phba->pport; if (!(vport->fc_flag & FC_OFFLINE_MODE)) return 0; diff --git a/drivers/scsi/mvsas.c b/drivers/scsi/mvsas.c index 1dd70d7a4947..23e5a876bb10 100644 --- a/drivers/scsi/mvsas.c +++ b/drivers/scsi/mvsas.c @@ -2959,7 +2959,7 @@ static int __devinit mvs_hw_init(struct mvs_info *mvi) /* enable auto port detection */ mw32(GBL_PORT_TYPE, MODE_AUTO_DET_EN); - msleep(100); + msleep(1100); /* init and reset phys */ for (i = 0; i < mvi->chip->n_phy; i++) { u32 lo = be32_to_cpu(*(u32 *)&mvi->sas_addr[4]); diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index 165ff884f48e..67cde0138061 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -114,7 +114,7 @@ static int aha152x_probe(struct pcmcia_device *link) link->io.NumPorts1 = 0x20; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index cd53627cc761..c7acef50d5da 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -303,7 +303,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, else if (start == (ha->flt_region_boot * 4) || start == (ha->flt_region_fw * 4)) valid = 1; - else if (IS_QLA25XX(ha) && + else if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && start == (ha->flt_region_vpd_nvram * 4)) valid = 1; if (!valid) { @@ -815,6 +815,21 @@ qla2x00_total_isp_aborts_show(struct device *dev, ha->qla_stats.total_isp_aborts); } +static ssize_t +qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha)) + return snprintf(buf, PAGE_SIZE, "\n"); + + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x (%x)\n", + ha->mpi_version[0], ha->mpi_version[1], ha->mpi_version[2], + ha->mpi_version[3], ha->mpi_capabilities); +} + static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -839,6 +854,7 @@ static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show, NULL); static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show, NULL); +static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -858,6 +874,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_optrom_fcode_version, &dev_attr_optrom_fw_version, &dev_attr_total_isp_aborts, + &dev_attr_mpi_version, NULL, }; @@ -892,6 +909,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) case PORT_SPEED_8GB: speed = FC_PORTSPEED_8GBIT; break; + case PORT_SPEED_10GB: + speed = FC_PORTSPEED_10GBIT; + break; } fc_host_speed(shost) = speed; } @@ -1382,7 +1402,9 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha) fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports; fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count; - if (IS_QLA25XX(ha)) + if (IS_QLA81XX(ha)) + speed = FC_PORTSPEED_10GBIT; + else if (IS_QLA25XX(ha)) speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; else if (IS_QLA24XX_TYPE(ha)) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 1cf77772623b..34760f8d4f17 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -310,6 +310,76 @@ qla2xxx_read_window(struct device_reg_2xxx __iomem *reg, uint32_t count, *buf++ = htons(RD_REG_WORD(dmp_reg++)); } +static inline void * +qla24xx_copy_eft(struct qla_hw_data *ha, void *ptr) +{ + if (!ha->eft) + return ptr; + + memcpy(ptr, ha->eft, ntohl(ha->fw_dump->eft_size)); + return ptr + ntohl(ha->fw_dump->eft_size); +} + +static inline void * +qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) +{ + uint32_t cnt; + uint32_t *iter_reg; + struct qla2xxx_fce_chain *fcec = ptr; + + if (!ha->fce) + return ptr; + + *last_chain = &fcec->type; + fcec->type = __constant_htonl(DUMP_CHAIN_FCE); + fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) + + fce_calc_size(ha->fce_bufs)); + fcec->size = htonl(fce_calc_size(ha->fce_bufs)); + fcec->addr_l = htonl(LSD(ha->fce_dma)); + fcec->addr_h = htonl(MSD(ha->fce_dma)); + + iter_reg = fcec->eregs; + for (cnt = 0; cnt < 8; cnt++) + *iter_reg++ = htonl(ha->fce_mb[cnt]); + + memcpy(iter_reg, ha->fce, ntohl(fcec->size)); + + return iter_reg; +} + +static inline void * +qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) +{ + uint32_t cnt, que_idx; + uint8_t req_cnt, rsp_cnt, que_cnt; + struct qla2xxx_mq_chain *mq = ptr; + struct device_reg_25xxmq __iomem *reg; + + if (!ha->mqenable) + return ptr; + + mq = ptr; + *last_chain = &mq->type; + mq->type = __constant_htonl(DUMP_CHAIN_MQ); + mq->chain_size = __constant_htonl(sizeof(struct qla2xxx_mq_chain)); + + req_cnt = find_first_zero_bit(ha->req_qid_map, ha->max_queues); + rsp_cnt = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues); + que_cnt = req_cnt > rsp_cnt ? req_cnt : rsp_cnt; + mq->count = htonl(que_cnt); + for (cnt = 0; cnt < que_cnt; cnt++) { + reg = (struct device_reg_25xxmq *) ((void *) + ha->mqiobase + cnt * QLA_QUE_PAGE); + que_idx = cnt * 4; + mq->qregs[que_idx] = htonl(RD_REG_DWORD(®->req_q_in)); + mq->qregs[que_idx+1] = htonl(RD_REG_DWORD(®->req_q_out)); + mq->qregs[que_idx+2] = htonl(RD_REG_DWORD(®->rsp_q_in)); + mq->qregs[que_idx+3] = htonl(RD_REG_DWORD(®->rsp_q_out)); + } + + return ptr + sizeof(struct qla2xxx_mq_chain); +} + /** * qla2300_fw_dump() - Dumps binary data from the 2300 firmware. * @ha: HA context @@ -913,8 +983,8 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) goto qla24xx_fw_dump_failed_0; nxt = qla2xxx_copy_queues(ha, nxt); - if (ha->eft) - memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); + + qla24xx_copy_eft(ha, nxt); qla24xx_fw_dump_failed_0: if (rval != QLA_SUCCESS) { @@ -942,19 +1012,14 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) uint32_t risc_address; struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - struct device_reg_25xxmq __iomem *reg25; uint32_t __iomem *dmp_reg; uint32_t *iter_reg; uint16_t __iomem *mbx_reg; unsigned long flags; struct qla25xx_fw_dump *fw; uint32_t ext_mem_cnt; - void *nxt; - struct qla2xxx_fce_chain *fcec; - struct qla2xxx_mq_chain *mq = NULL; - uint32_t qreg_size; - uint8_t req_cnt, rsp_cnt, que_cnt; - uint32_t que_idx; + void *nxt, *nxt_chain; + uint32_t *last_chain = NULL; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); risc_address = ext_mem_cnt = 0; @@ -1001,28 +1066,6 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg)); fw->pcie_regs[3] = htonl(RD_REG_DWORD(®->iobase_window)); - /* Multi queue registers */ - if (ha->mqenable) { - qreg_size = sizeof(struct qla2xxx_mq_chain); - mq = kzalloc(qreg_size, GFP_KERNEL); - if (!mq) - goto qla25xx_fw_dump_failed_0; - req_cnt = find_first_zero_bit(ha->req_qid_map, ha->max_queues); - rsp_cnt = find_first_zero_bit(ha->rsp_qid_map, ha->max_queues); - que_cnt = req_cnt > rsp_cnt ? req_cnt : rsp_cnt; - mq->count = htonl(que_cnt); - mq->chain_size = htonl(qreg_size); - mq->type = __constant_htonl(DUMP_CHAIN_MQ); - for (cnt = 0; cnt < que_cnt; cnt++) { - reg25 = (struct device_reg_25xxmq *) ((void *) - ha->mqiobase + cnt * QLA_QUE_PAGE); - que_idx = cnt * 4; - mq->qregs[que_idx] = htonl(reg25->req_q_in); - mq->qregs[que_idx+1] = htonl(reg25->req_q_out); - mq->qregs[que_idx+2] = htonl(reg25->rsp_q_in); - mq->qregs[que_idx+3] = htonl(reg25->rsp_q_out); - } - } WRT_REG_DWORD(®->iobase_window, 0x00); RD_REG_DWORD(®->iobase_window); @@ -1240,6 +1283,10 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg); qla24xx_read_window(reg, 0x6F00, 16, iter_reg); + /* Multi queue registers */ + nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset, + &last_chain); + rval = qla24xx_soft_reset(ha); if (rval != QLA_SUCCESS) goto qla25xx_fw_dump_failed_0; @@ -1249,39 +1296,341 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) if (rval != QLA_SUCCESS) goto qla25xx_fw_dump_failed_0; - /* Fibre Channel Trace Buffer. */ nxt = qla2xxx_copy_queues(ha, nxt); - if (ha->eft) - memcpy(nxt, ha->eft, ntohl(ha->fw_dump->eft_size)); - /* Fibre Channel Event Buffer. */ - if (!ha->fce) - goto qla25xx_fw_dump_failed_0; + nxt = qla24xx_copy_eft(ha, nxt); + + /* Chain entries -- started with MQ. */ + qla25xx_copy_fce(ha, nxt_chain, &last_chain); + if (last_chain) { + ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); + *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + } - ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); +qla25xx_fw_dump_failed_0: + if (rval != QLA_SUCCESS) { + qla_printk(KERN_WARNING, ha, + "Failed to dump firmware (%x)!!!\n", rval); + ha->fw_dumped = 0; - if (ha->mqenable) { - nxt = nxt + ntohl(ha->fw_dump->eft_size); - memcpy(nxt, mq, qreg_size); - kfree(mq); - fcec = nxt + qreg_size; } else { - fcec = nxt + ntohl(ha->fw_dump->eft_size); + qla_printk(KERN_INFO, ha, + "Firmware dump saved to temp buffer (%ld/%p).\n", + base_vha->host_no, ha->fw_dump); + ha->fw_dumped = 1; } - fcec->type = __constant_htonl(DUMP_CHAIN_FCE | DUMP_CHAIN_LAST); - fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) + - fce_calc_size(ha->fce_bufs)); - fcec->size = htonl(fce_calc_size(ha->fce_bufs)); - fcec->addr_l = htonl(LSD(ha->fce_dma)); - fcec->addr_h = htonl(MSD(ha->fce_dma)); - iter_reg = fcec->eregs; - for (cnt = 0; cnt < 8; cnt++) - *iter_reg++ = htonl(ha->fce_mb[cnt]); +qla25xx_fw_dump_failed: + if (!hardware_locked) + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} - memcpy(iter_reg, ha->fce, ntohl(fcec->size)); +void +qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) +{ + int rval; + uint32_t cnt; + uint32_t risc_address; + struct qla_hw_data *ha = vha->hw; + struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + uint32_t __iomem *dmp_reg; + uint32_t *iter_reg; + uint16_t __iomem *mbx_reg; + unsigned long flags; + struct qla81xx_fw_dump *fw; + uint32_t ext_mem_cnt; + void *nxt, *nxt_chain; + uint32_t *last_chain = NULL; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); -qla25xx_fw_dump_failed_0: + risc_address = ext_mem_cnt = 0; + flags = 0; + + if (!hardware_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (!ha->fw_dump) { + qla_printk(KERN_WARNING, ha, + "No buffer available for dump!!!\n"); + goto qla81xx_fw_dump_failed; + } + + if (ha->fw_dumped) { + qla_printk(KERN_WARNING, ha, + "Firmware has been previously dumped (%p) -- ignoring " + "request...\n", ha->fw_dump); + goto qla81xx_fw_dump_failed; + } + fw = &ha->fw_dump->isp.isp81; + qla2xxx_prep_dump(ha, ha->fw_dump); + + fw->host_status = htonl(RD_REG_DWORD(®->host_status)); + + /* Pause RISC. */ + rval = qla24xx_pause_risc(reg); + if (rval != QLA_SUCCESS) + goto qla81xx_fw_dump_failed_0; + + /* Host/Risc registers. */ + iter_reg = fw->host_risc_reg; + iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg); + qla24xx_read_window(reg, 0x7010, 16, iter_reg); + + /* PCIe registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x7C00); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_window, 0x01); + dmp_reg = ®->iobase_c4; + fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++)); + fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++)); + fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg)); + fw->pcie_regs[3] = htonl(RD_REG_DWORD(®->iobase_window)); + + WRT_REG_DWORD(®->iobase_window, 0x00); + RD_REG_DWORD(®->iobase_window); + + /* Host interface registers. */ + dmp_reg = ®->flash_addr; + for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++) + fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Disable interrupts. */ + WRT_REG_DWORD(®->ictrl, 0); + RD_REG_DWORD(®->ictrl); + + /* Shadow registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_select, 0xB0000000); + fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0100000); + fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0200000); + fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0300000); + fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0400000); + fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0500000); + fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0600000); + fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0700000); + fw->shadow_reg[7] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0800000); + fw->shadow_reg[8] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0900000); + fw->shadow_reg[9] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0A00000); + fw->shadow_reg[10] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + /* RISC I/O register. */ + WRT_REG_DWORD(®->iobase_addr, 0x0010); + fw->risc_io_reg = htonl(RD_REG_DWORD(®->iobase_window)); + + /* Mailbox registers. */ + mbx_reg = ®->mailbox0; + for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++) + fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++)); + + /* Transfer sequence registers. */ + iter_reg = fw->xseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg); + qla24xx_read_window(reg, 0xBF70, 16, iter_reg); + + iter_reg = fw->xseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg); + qla24xx_read_window(reg, 0xBFE0, 16, iter_reg); + + qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg); + + /* Receive sequence registers. */ + iter_reg = fw->rseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg); + qla24xx_read_window(reg, 0xFF70, 16, iter_reg); + + iter_reg = fw->rseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg); + qla24xx_read_window(reg, 0xFFD0, 16, iter_reg); + + qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg); + qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg); + + /* Auxiliary sequence registers. */ + iter_reg = fw->aseq_gp_reg; + iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg); + qla24xx_read_window(reg, 0xB070, 16, iter_reg); + + iter_reg = fw->aseq_0_reg; + iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg); + qla24xx_read_window(reg, 0xB0D0, 16, iter_reg); + + qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg); + qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg); + + /* Command DMA registers. */ + qla24xx_read_window(reg, 0x7100, 16, fw->cmd_dma_reg); + + /* Queues. */ + iter_reg = fw->req0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->resp0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + iter_reg = fw->req1_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg); + dmp_reg = ®->iobase_q; + for (cnt = 0; cnt < 7; cnt++) + *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++)); + + /* Transmit DMA registers. */ + iter_reg = fw->xmt0_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg); + qla24xx_read_window(reg, 0x7610, 16, iter_reg); + + iter_reg = fw->xmt1_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg); + qla24xx_read_window(reg, 0x7630, 16, iter_reg); + + iter_reg = fw->xmt2_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg); + qla24xx_read_window(reg, 0x7650, 16, iter_reg); + + iter_reg = fw->xmt3_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg); + qla24xx_read_window(reg, 0x7670, 16, iter_reg); + + iter_reg = fw->xmt4_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg); + qla24xx_read_window(reg, 0x7690, 16, iter_reg); + + qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg); + + /* Receive DMA registers. */ + iter_reg = fw->rcvt0_data_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg); + qla24xx_read_window(reg, 0x7710, 16, iter_reg); + + iter_reg = fw->rcvt1_data_dma_reg; + iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg); + qla24xx_read_window(reg, 0x7730, 16, iter_reg); + + /* RISC registers. */ + iter_reg = fw->risc_gp_reg; + iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg); + qla24xx_read_window(reg, 0x0F70, 16, iter_reg); + + /* Local memory controller registers. */ + iter_reg = fw->lmc_reg; + iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg); + qla24xx_read_window(reg, 0x3070, 16, iter_reg); + + /* Fibre Protocol Module registers. */ + iter_reg = fw->fpm_hdw_reg; + iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x40C0, 16, iter_reg); + qla24xx_read_window(reg, 0x40D0, 16, iter_reg); + + /* Frame Buffer registers. */ + iter_reg = fw->fb_hdw_reg; + iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg); + iter_reg = qla24xx_read_window(reg, 0x61C0, 16, iter_reg); + qla24xx_read_window(reg, 0x6F00, 16, iter_reg); + + /* Multi queue registers */ + nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset, + &last_chain); + + rval = qla24xx_soft_reset(ha); + if (rval != QLA_SUCCESS) + goto qla81xx_fw_dump_failed_0; + + rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram), + &nxt); + if (rval != QLA_SUCCESS) + goto qla81xx_fw_dump_failed_0; + + nxt = qla2xxx_copy_queues(ha, nxt); + + nxt = qla24xx_copy_eft(ha, nxt); + + /* Chain entries -- started with MQ. */ + qla25xx_copy_fce(ha, nxt_chain, &last_chain); + if (last_chain) { + ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); + *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + } + +qla81xx_fw_dump_failed_0: if (rval != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "Failed to dump firmware (%x)!!!\n", rval); @@ -1294,10 +1643,11 @@ qla25xx_fw_dump_failed_0: ha->fw_dumped = 1; } -qla25xx_fw_dump_failed: +qla81xx_fw_dump_failed: if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); } + /****************************************************************************/ /* Driver Debug Functions. */ /****************************************************************************/ diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index c1794a70a45f..f660dd70b72e 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -251,6 +251,45 @@ struct qla25xx_fw_dump { uint32_t ext_mem[1]; }; +struct qla81xx_fw_dump { + uint32_t host_status; + uint32_t host_risc_reg[32]; + uint32_t pcie_regs[4]; + uint32_t host_reg[32]; + uint32_t shadow_reg[11]; + uint32_t risc_io_reg; + uint16_t mailbox_reg[32]; + uint32_t xseq_gp_reg[128]; + uint32_t xseq_0_reg[48]; + uint32_t xseq_1_reg[16]; + uint32_t rseq_gp_reg[128]; + uint32_t rseq_0_reg[32]; + uint32_t rseq_1_reg[16]; + uint32_t rseq_2_reg[16]; + uint32_t aseq_gp_reg[128]; + uint32_t aseq_0_reg[32]; + uint32_t aseq_1_reg[16]; + uint32_t aseq_2_reg[16]; + uint32_t cmd_dma_reg[16]; + uint32_t req0_dma_reg[15]; + uint32_t resp0_dma_reg[15]; + uint32_t req1_dma_reg[15]; + uint32_t xmt0_dma_reg[32]; + uint32_t xmt1_dma_reg[32]; + uint32_t xmt2_dma_reg[32]; + uint32_t xmt3_dma_reg[32]; + uint32_t xmt4_dma_reg[32]; + uint32_t xmt_data_dma_reg[16]; + uint32_t rcvt0_data_dma_reg[32]; + uint32_t rcvt1_data_dma_reg[32]; + uint32_t risc_gp_reg[128]; + uint32_t lmc_reg[128]; + uint32_t fpm_hdw_reg[224]; + uint32_t fb_hdw_reg[208]; + uint32_t code_ram[0x2000]; + uint32_t ext_mem[1]; +}; + #define EFT_NUM_BUFFERS 4 #define EFT_BYTES_PER_BUFFER 0x4000 #define EFT_SIZE ((EFT_BYTES_PER_BUFFER) * (EFT_NUM_BUFFERS)) @@ -313,5 +352,6 @@ struct qla2xxx_fw_dump { struct qla2300_fw_dump isp23; struct qla24xx_fw_dump isp24; struct qla25xx_fw_dump isp25; + struct qla81xx_fw_dump isp81; } isp; }; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index a29c95204975..023ee77fb027 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -187,7 +187,6 @@ struct req_que; * SCSI Request Block */ typedef struct srb { - struct scsi_qla_host *vha; /* HA the SP is queued on */ struct req_que *que; struct fc_port *fcport; @@ -2136,7 +2135,6 @@ struct qla_msix_entry { /* Work events. */ enum qla_work_type { QLA_EVT_AEN, - QLA_EVT_HWE_LOG, }; @@ -2151,10 +2149,6 @@ struct qla_work_evt { enum fc_host_event_code code; u32 data; } aen; - struct { - uint16_t code; - uint16_t d1, d2, d3; - } hwe; } u; }; @@ -2309,6 +2303,7 @@ struct qla_hw_data { #define PORT_SPEED_2GB 0x01 #define PORT_SPEED_4GB 0x03 #define PORT_SPEED_8GB 0x04 +#define PORT_SPEED_10GB 0x13 uint16_t link_data_rate; /* F/W operating speed */ uint8_t current_topology; @@ -2328,6 +2323,7 @@ struct qla_hw_data { #define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532 #define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432 +#define PCI_DEVICE_ID_QLOGIC_ISP8001 0x8001 uint32_t device_type; #define DT_ISP2100 BIT_0 #define DT_ISP2200 BIT_1 @@ -2342,7 +2338,8 @@ struct qla_hw_data { #define DT_ISP5432 BIT_10 #define DT_ISP2532 BIT_11 #define DT_ISP8432 BIT_12 -#define DT_ISP_LAST (DT_ISP8432 << 1) +#define DT_ISP8001 BIT_13 +#define DT_ISP_LAST (DT_ISP8001 << 1) #define DT_IIDMA BIT_26 #define DT_FWI2 BIT_27 @@ -2364,6 +2361,7 @@ struct qla_hw_data { #define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432) #define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532) #define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432) +#define IS_QLA8001(ha) (DT_MASK(ha) & DT_ISP8001) #define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ IS_QLA6312(ha) || IS_QLA6322(ha)) @@ -2373,8 +2371,11 @@ struct qla_hw_data { #define IS_QLA84XX(ha) (IS_QLA8432(ha)) #define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ IS_QLA84XX(ha)) +#define IS_QLA81XX(ha) (IS_QLA8001(ha)) #define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \ - IS_QLA25XX(ha)) + IS_QLA25XX(ha) || IS_QLA81XX(ha)) +#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && \ + (ha)->flags.msix_enabled) #define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA) #define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2) @@ -2472,6 +2473,9 @@ struct qla_hw_data { uint8_t fw_seriallink_options[4]; uint16_t fw_seriallink_options24[4]; + uint8_t mpi_version[4]; + uint32_t mpi_capabilities; + /* Firmware dump information. */ struct qla2xxx_fw_dump *fw_dump; uint32_t fw_dump_len; @@ -2480,6 +2484,7 @@ struct qla_hw_data { dma_addr_t eft_dma; void *eft; + uint32_t chain_offset; struct dentry *dfs_dir; struct dentry *dfs_fce; dma_addr_t fce_dma; @@ -2489,10 +2494,6 @@ struct qla_hw_data { uint64_t fce_wr, fce_rd; struct mutex fce_mutex; - uint32_t hw_event_start; - uint32_t hw_event_ptr; - uint32_t hw_event_pause_errors; - uint32_t pci_attr; uint16_t chip_revision; @@ -2522,6 +2523,12 @@ struct qla_hw_data { uint8_t fcode_revision[16]; uint32_t fw_revision[4]; + /* Offsets for flash/nvram access (set to ~0 if not used). */ + uint32_t flash_conf_off; + uint32_t flash_data_off; + uint32_t nvram_conf_off; + uint32_t nvram_data_off; + uint32_t fdt_wrt_disable; uint32_t fdt_erase_cmd; uint32_t fdt_block_size; @@ -2533,7 +2540,6 @@ struct qla_hw_data { uint32_t flt_region_boot; uint32_t flt_region_fw; uint32_t flt_region_vpd_nvram; - uint32_t flt_region_hw_event; uint32_t flt_region_npiv_conf; /* Needed for BEACON */ @@ -2737,6 +2743,7 @@ typedef struct scsi_qla_host { #define OPTROM_SIZE_2322 0x100000 #define OPTROM_SIZE_24XX 0x100000 #define OPTROM_SIZE_25XX 0x200000 +#define OPTROM_SIZE_81XX 0x400000 #include "qla_gbl.h" #include "qla_dbg.h" diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 0e366a1b44b3..c66036da7d2b 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -113,7 +113,8 @@ int qla2x00_dfs_setup(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; - if (!IS_QLA25XX(ha)) + + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)) goto out; if (!ha->fce) goto out; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index ee1f1e794c2d..7abb045a0410 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1215,9 +1215,10 @@ struct qla_fdt_layout { struct qla_flt_location { uint8_t sig[4]; - uint32_t start_lo; - uint32_t start_hi; - uint16_t unused; + uint16_t start_lo; + uint16_t start_hi; + uint8_t version; + uint8_t unused[5]; uint16_t checksum; }; @@ -1390,4 +1391,291 @@ struct access_chip_rsp_84xx { uint32_t reserved[12]; }; + +/* 81XX Support **************************************************************/ + +#define MBA_DCBX_START 0x8016 +#define MBA_DCBX_COMPLETE 0x8030 +#define MBA_FCF_CONF_ERR 0x8031 +#define MBA_DCBX_PARAM_UPDATE 0x8032 +#define MBA_IDC_COMPLETE 0x8100 +#define MBA_IDC_NOTIFY 0x8101 +#define MBA_IDC_TIME_EXT 0x8102 + +struct nvram_81xx { + /* NVRAM header. */ + uint8_t id[4]; + uint16_t nvram_version; + uint16_t reserved_0; + + /* Firmware Initialization Control Block. */ + uint16_t version; + uint16_t reserved_1; + uint16_t frame_payload_size; + uint16_t execution_throttle; + uint16_t exchange_count; + uint16_t reserved_2; + + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; + + uint16_t login_retry_count; + uint16_t reserved_3; + uint16_t interrupt_delay_timer; + uint16_t login_timeout; + + uint32_t firmware_options_1; + uint32_t firmware_options_2; + uint32_t firmware_options_3; + + uint16_t reserved_4[4]; + + /* Offset 64. */ + uint8_t enode_mac[6]; + uint16_t reserved_5[5]; + + /* Offset 80. */ + uint16_t reserved_6[24]; + + /* Offset 128. */ + uint16_t reserved_7[64]; + + /* + * BIT 0 = Enable spinup delay + * BIT 1 = Disable BIOS + * BIT 2 = Enable Memory Map BIOS + * BIT 3 = Enable Selectable Boot + * BIT 4 = Disable RISC code load + * BIT 5 = Disable Serdes + * BIT 6 = Opt boot mode + * BIT 7 = Interrupt enable + * + * BIT 8 = EV Control enable + * BIT 9 = Enable lip reset + * BIT 10 = Enable lip full login + * BIT 11 = Enable target reset + * BIT 12 = Stop firmware + * BIT 13 = Enable nodename option + * BIT 14 = Default WWPN valid + * BIT 15 = Enable alternate WWN + * + * BIT 16 = CLP LUN string + * BIT 17 = CLP Target string + * BIT 18 = CLP BIOS enable string + * BIT 19 = CLP Serdes string + * BIT 20 = CLP WWPN string + * BIT 21 = CLP WWNN string + * BIT 22 = + * BIT 23 = + * BIT 24 = Keep WWPN + * BIT 25 = Temp WWPN + * BIT 26-31 = + */ + uint32_t host_p; + + uint8_t alternate_port_name[WWN_SIZE]; + uint8_t alternate_node_name[WWN_SIZE]; + + uint8_t boot_port_name[WWN_SIZE]; + uint16_t boot_lun_number; + uint16_t reserved_8; + + uint8_t alt1_boot_port_name[WWN_SIZE]; + uint16_t alt1_boot_lun_number; + uint16_t reserved_9; + + uint8_t alt2_boot_port_name[WWN_SIZE]; + uint16_t alt2_boot_lun_number; + uint16_t reserved_10; + + uint8_t alt3_boot_port_name[WWN_SIZE]; + uint16_t alt3_boot_lun_number; + uint16_t reserved_11; + + /* + * BIT 0 = Selective Login + * BIT 1 = Alt-Boot Enable + * BIT 2 = Reserved + * BIT 3 = Boot Order List + * BIT 4 = Reserved + * BIT 5 = Selective LUN + * BIT 6 = Reserved + * BIT 7-31 = + */ + uint32_t efi_parameters; + + uint8_t reset_delay; + uint8_t reserved_12; + uint16_t reserved_13; + + uint16_t boot_id_number; + uint16_t reserved_14; + + uint16_t max_luns_per_target; + uint16_t reserved_15; + + uint16_t port_down_retry_count; + uint16_t link_down_timeout; + + /* FCode parameters. */ + uint16_t fcode_parameter; + + uint16_t reserved_16[3]; + + /* Offset 352. */ + uint8_t reserved_17[4]; + uint16_t reserved_18[5]; + uint8_t reserved_19[2]; + uint16_t reserved_20[8]; + + /* Offset 384. */ + uint8_t reserved_21[16]; + uint16_t reserved_22[8]; + + /* Offset 416. */ + uint16_t reserved_23[32]; + + /* Offset 480. */ + uint8_t model_name[16]; + + /* Offset 496. */ + uint16_t feature_mask_l; + uint16_t feature_mask_h; + uint16_t reserved_24[2]; + + uint16_t subsystem_vendor_id; + uint16_t subsystem_device_id; + + uint32_t checksum; +}; + +/* + * ISP Initialization Control Block. + * Little endian except where noted. + */ +#define ICB_VERSION 1 +struct init_cb_81xx { + uint16_t version; + uint16_t reserved_1; + + uint16_t frame_payload_size; + uint16_t execution_throttle; + uint16_t exchange_count; + + uint16_t reserved_2; + + uint8_t port_name[WWN_SIZE]; /* Big endian. */ + uint8_t node_name[WWN_SIZE]; /* Big endian. */ + + uint16_t response_q_inpointer; + uint16_t request_q_outpointer; + + uint16_t login_retry_count; + + uint16_t prio_request_q_outpointer; + + uint16_t response_q_length; + uint16_t request_q_length; + + uint16_t reserved_3; + + uint16_t prio_request_q_length; + + uint32_t request_q_address[2]; + uint32_t response_q_address[2]; + uint32_t prio_request_q_address[2]; + + uint8_t reserved_4[8]; + + uint16_t atio_q_inpointer; + uint16_t atio_q_length; + uint32_t atio_q_address[2]; + + uint16_t interrupt_delay_timer; /* 100us increments. */ + uint16_t login_timeout; + + /* + * BIT 0-3 = Reserved + * BIT 4 = Enable Target Mode + * BIT 5 = Disable Initiator Mode + * BIT 6 = Reserved + * BIT 7 = Reserved + * + * BIT 8-13 = Reserved + * BIT 14 = Node Name Option + * BIT 15-31 = Reserved + */ + uint32_t firmware_options_1; + + /* + * BIT 0 = Operation Mode bit 0 + * BIT 1 = Operation Mode bit 1 + * BIT 2 = Operation Mode bit 2 + * BIT 3 = Operation Mode bit 3 + * BIT 4-7 = Reserved + * + * BIT 8 = Enable Class 2 + * BIT 9 = Enable ACK0 + * BIT 10 = Reserved + * BIT 11 = Enable FC-SP Security + * BIT 12 = FC Tape Enable + * BIT 13 = Reserved + * BIT 14 = Enable Target PRLI Control + * BIT 15-31 = Reserved + */ + uint32_t firmware_options_2; + + /* + * BIT 0-3 = Reserved + * BIT 4 = FCP RSP Payload bit 0 + * BIT 5 = FCP RSP Payload bit 1 + * BIT 6 = Enable Receive Out-of-Order data frame handling + * BIT 7 = Reserved + * + * BIT 8 = Reserved + * BIT 9 = Enable Out-of-Order FCP_XFER_RDY relative offset handling + * BIT 10-16 = Reserved + * BIT 17 = Enable multiple FCFs + * BIT 18-20 = MAC addressing mode + * BIT 21-25 = Ethernet data rate + * BIT 26 = Enable ethernet header rx IOCB for ATIO q + * BIT 27 = Enable ethernet header rx IOCB for response q + * BIT 28 = SPMA selection bit 0 + * BIT 28 = SPMA selection bit 1 + * BIT 30-31 = Reserved + */ + uint32_t firmware_options_3; + + uint8_t reserved_5[8]; + + uint8_t enode_mac[6]; + + uint8_t reserved_6[10]; +}; + +struct mid_init_cb_81xx { + struct init_cb_81xx init_cb; + + uint16_t count; + uint16_t options; + + struct mid_conf_entry_24xx entries[MAX_MULTI_ID_FABRIC]; +}; + +#define FARX_ACCESS_FLASH_CONF_81XX 0x7FFD0000 +#define FARX_ACCESS_FLASH_DATA_81XX 0x7F800000 + +/* 81XX Flash locations -- occupies second 2MB region. */ +#define FA_BOOT_CODE_ADDR_81 0x80000 +#define FA_RISC_CODE_ADDR_81 0xA0000 +#define FA_FW_AREA_ADDR_81 0xC0000 +#define FA_VPD_NVRAM_ADDR_81 0xD0000 +#define FA_FEATURE_ADDR_81 0xD4000 +#define FA_FLASH_DESCR_ADDR_81 0xD8000 +#define FA_FLASH_LAYOUT_ADDR_81 0xD8400 +#define FA_HW_EVENT0_ADDR_81 0xDC000 +#define FA_HW_EVENT1_ADDR_81 0xDC400 +#define FA_NPIV_CONF0_ADDR_81 0xD1000 +#define FA_NPIV_CONF1_ADDR_81 0xD2000 + #endif diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 0011e31205db..ba4913353752 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -28,8 +28,10 @@ extern void qla2x00_reset_adapter(struct scsi_qla_host *); extern void qla24xx_reset_adapter(struct scsi_qla_host *); extern int qla2x00_nvram_config(struct scsi_qla_host *); extern int qla24xx_nvram_config(struct scsi_qla_host *); +extern int qla81xx_nvram_config(struct scsi_qla_host *); extern void qla2x00_update_fw_options(struct scsi_qla_host *); extern void qla24xx_update_fw_options(scsi_qla_host_t *); +extern void qla81xx_update_fw_options(scsi_qla_host_t *); extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *); extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *); @@ -69,8 +71,6 @@ extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum fc_host_event_code, u32); -extern int qla2x00_post_hwe_work(struct scsi_qla_host *, uint16_t , uint16_t, - uint16_t, uint16_t); extern void qla2x00_abort_fcport_cmds(fc_port_t *); extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *, @@ -143,7 +143,7 @@ qla2x00_execute_fw(scsi_qla_host_t *, uint32_t); extern void qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *, - uint16_t *, uint16_t *, uint16_t *, uint32_t *); + uint16_t *, uint16_t *, uint16_t *, uint32_t *, uint8_t *, uint32_t *); extern int qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *); @@ -317,9 +317,6 @@ extern uint8_t *qla25xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *); extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); -extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t, - uint16_t, uint16_t); - extern int qla2xxx_get_flash_info(scsi_qla_host_t *); extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); @@ -332,6 +329,7 @@ extern void qla2100_fw_dump(scsi_qla_host_t *, int); extern void qla2300_fw_dump(scsi_qla_host_t *, int); extern void qla24xx_fw_dump(scsi_qla_host_t *, int); extern void qla25xx_fw_dump(scsi_qla_host_t *, int); +extern void qla81xx_fw_dump(scsi_qla_host_t *, int); extern void qla2x00_dump_regs(scsi_qla_host_t *); extern void qla2x00_dump_buffer(uint8_t *, uint32_t); diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 0a6f72973996..557f58d5bf88 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1535,7 +1535,10 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) eiter = (struct ct_fdmi_port_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED); eiter->len = __constant_cpu_to_be16(4 + 4); - if (IS_QLA25XX(ha)) + if (IS_QLA81XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32( + FDMI_PORT_SPEED_10GB); + else if (IS_QLA25XX(ha)) eiter->a.sup_speed = __constant_cpu_to_be32( FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB| FDMI_PORT_SPEED_4GB|FDMI_PORT_SPEED_8GB); @@ -1575,6 +1578,10 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) eiter->a.cur_speed = __constant_cpu_to_be32(FDMI_PORT_SPEED_8GB); break; + case PORT_SPEED_10GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_10GB); + break; default: eiter->a.cur_speed = __constant_cpu_to_be32(FDMI_PORT_SPEED_UNKNOWN); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 52ed56ecf195..2d4f32b4df5c 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -552,10 +552,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) d2 = RD_REG_DWORD(®->ctrl_status); barrier(); } - if (cnt == 0 || hw_evt) - qla2xxx_hw_event_log(vha, HW_EVENT_RESET_ERR, - RD_REG_WORD(®->mailbox1), RD_REG_WORD(®->mailbox2), - RD_REG_WORD(®->mailbox3)); WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET); RD_REG_DWORD(®->hccr); @@ -574,6 +570,9 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) } spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (IS_NOPOLLING_TYPE(ha)) + ha->isp_ops->enable_intrs(ha); } /** @@ -779,16 +778,19 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) mem_size = (ha->fw_memory_size - 0x11000 + 1) * sizeof(uint16_t); } else if (IS_FWI2_CAPABLE(ha)) { - fixed_size = IS_QLA25XX(ha) ? - offsetof(struct qla25xx_fw_dump, ext_mem) : - offsetof(struct qla24xx_fw_dump, ext_mem); + if (IS_QLA81XX(ha)) + fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem); + else if (IS_QLA25XX(ha)) + fixed_size = offsetof(struct qla25xx_fw_dump, ext_mem); + else + fixed_size = offsetof(struct qla24xx_fw_dump, ext_mem); mem_size = (ha->fw_memory_size - 0x100000 + 1) * sizeof(uint32_t); if (ha->mqenable) mq_size = sizeof(struct qla2xxx_mq_chain); /* Allocate memory for Fibre Channel Event Buffer. */ - if (!IS_QLA25XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)) goto try_eft; tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, @@ -851,7 +853,9 @@ cont_alloc: dump_size = offsetof(struct qla2xxx_fw_dump, isp); dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + - mq_size + eft_size + fce_size; + eft_size; + ha->chain_offset = dump_size; + dump_size += mq_size + fce_size; ha->fw_dump = vmalloc(dump_size); if (!ha->fw_dump) { @@ -987,7 +991,8 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) &ha->fw_major_version, &ha->fw_minor_version, &ha->fw_subminor_version, - &ha->fw_attributes, &ha->fw_memory_size); + &ha->fw_attributes, &ha->fw_memory_size, + ha->mpi_version, &ha->mpi_capabilities); ha->flags.npiv_supported = 0; if (IS_QLA2XXX_MIDTYPE(ha) && (ha->fw_attributes & BIT_2)) { @@ -1665,10 +1670,6 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " "invalid -- WWPN) defaults.\n"); - if (chksum) - qla2xxx_hw_event_log(vha, HW_EVENT_NVRAM_CHKSUM_ERR, 0, - MSW(chksum), LSW(chksum)); - /* * Set default initialization control block. */ @@ -4255,3 +4256,269 @@ qla84xx_init_chip(scsi_qla_host_t *vha) return rval != QLA_SUCCESS || status[0] ? QLA_FUNCTION_FAILED: QLA_SUCCESS; } + +/* 81XX Support **************************************************************/ + +int +qla81xx_nvram_config(scsi_qla_host_t *vha) +{ + int rval; + struct init_cb_81xx *icb; + struct nvram_81xx *nv; + uint32_t *dptr; + uint8_t *dptr1, *dptr2; + uint32_t chksum; + uint16_t cnt; + struct qla_hw_data *ha = vha->hw; + + rval = QLA_SUCCESS; + icb = (struct init_cb_81xx *)ha->init_cb; + nv = ha->nvram; + + /* Determine NVRAM starting address. */ + ha->nvram_size = sizeof(struct nvram_81xx); + ha->nvram_base = FA_NVRAM_FUNC0_ADDR; + ha->vpd_size = FA_NVRAM_VPD_SIZE; + ha->vpd_base = FA_NVRAM_VPD0_ADDR; + if (PCI_FUNC(ha->pdev->devfn) & 1) { + ha->nvram_base = FA_NVRAM_FUNC1_ADDR; + ha->vpd_base = FA_NVRAM_VPD1_ADDR; + } + + /* Get VPD data into cache */ + ha->vpd = ha->nvram + VPD_OFFSET; + ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd, + ha->nvram_base - FA_NVRAM_FUNC0_ADDR, FA_NVRAM_VPD_SIZE * 4); + + /* Get NVRAM data into cache and calculate checksum. */ + dptr = (uint32_t *)nv; + ha->isp_ops->read_nvram(vha, (uint8_t *)dptr, ha->nvram_base, + ha->nvram_size); + for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++) + chksum += le32_to_cpu(*dptr++); + + DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no)); + DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size)); + + /* Bad NVRAM data, set defaults parameters. */ + if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' + || nv->id[3] != ' ' || + nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) { + /* Reset NVRAM data. */ + qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " + "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], + le16_to_cpu(nv->nvram_version)); + qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " + "invalid -- WWPN) defaults.\n"); + + /* + * Set default initialization control block. + */ + memset(nv, 0, ha->nvram_size); + nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION); + nv->version = __constant_cpu_to_le16(ICB_VERSION); + nv->frame_payload_size = __constant_cpu_to_le16(2048); + nv->execution_throttle = __constant_cpu_to_le16(0xFFFF); + nv->exchange_count = __constant_cpu_to_le16(0); + nv->port_name[0] = 0x21; + nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn); + nv->port_name[2] = 0x00; + nv->port_name[3] = 0xe0; + nv->port_name[4] = 0x8b; + nv->port_name[5] = 0x1c; + nv->port_name[6] = 0x55; + nv->port_name[7] = 0x86; + nv->node_name[0] = 0x20; + nv->node_name[1] = 0x00; + nv->node_name[2] = 0x00; + nv->node_name[3] = 0xe0; + nv->node_name[4] = 0x8b; + nv->node_name[5] = 0x1c; + nv->node_name[6] = 0x55; + nv->node_name[7] = 0x86; + nv->login_retry_count = __constant_cpu_to_le16(8); + nv->interrupt_delay_timer = __constant_cpu_to_le16(0); + nv->login_timeout = __constant_cpu_to_le16(0); + nv->firmware_options_1 = + __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); + nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4); + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13); + nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10); + nv->efi_parameters = __constant_cpu_to_le32(0); + nv->reset_delay = 5; + nv->max_luns_per_target = __constant_cpu_to_le16(128); + nv->port_down_retry_count = __constant_cpu_to_le16(30); + nv->link_down_timeout = __constant_cpu_to_le16(30); + nv->enode_mac[0] = 0x01; + nv->enode_mac[1] = 0x02; + nv->enode_mac[2] = 0x03; + nv->enode_mac[3] = 0x04; + nv->enode_mac[4] = 0x05; + nv->enode_mac[5] = 0x06 + PCI_FUNC(ha->pdev->devfn); + + rval = 1; + } + + /* Reset Initialization control block */ + memset(icb, 0, sizeof(struct init_cb_81xx)); + + /* Copy 1st segment. */ + dptr1 = (uint8_t *)icb; + dptr2 = (uint8_t *)&nv->version; + cnt = (uint8_t *)&icb->response_q_inpointer - (uint8_t *)&icb->version; + while (cnt--) + *dptr1++ = *dptr2++; + + icb->login_retry_count = nv->login_retry_count; + + /* Copy 2nd segment. */ + dptr1 = (uint8_t *)&icb->interrupt_delay_timer; + dptr2 = (uint8_t *)&nv->interrupt_delay_timer; + cnt = (uint8_t *)&icb->reserved_5 - + (uint8_t *)&icb->interrupt_delay_timer; + while (cnt--) + *dptr1++ = *dptr2++; + + memcpy(icb->enode_mac, nv->enode_mac, sizeof(icb->enode_mac)); + /* Some boards (with valid NVRAMs) still have NULL enode_mac!! */ + if (!memcmp(icb->enode_mac, "\0\0\0\0\0\0", sizeof(icb->enode_mac))) { + icb->enode_mac[0] = 0x01; + icb->enode_mac[1] = 0x02; + icb->enode_mac[2] = 0x03; + icb->enode_mac[3] = 0x04; + icb->enode_mac[4] = 0x05; + icb->enode_mac[5] = 0x06 + PCI_FUNC(ha->pdev->devfn); + } + + /* + * Setup driver NVRAM options. + */ + qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name), + "QLE81XX"); + + /* Use alternate WWN? */ + if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { + memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); + memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); + } + + /* Prepare nodename */ + if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) { + /* + * Firmware will apply the following mask if the nodename was + * not provided. + */ + memcpy(icb->node_name, icb->port_name, WWN_SIZE); + icb->node_name[0] &= 0xF0; + } + + /* Set host adapter parameters. */ + ha->flags.disable_risc_code_load = 0; + ha->flags.enable_lip_reset = 0; + ha->flags.enable_lip_full_login = + le32_to_cpu(nv->host_p) & BIT_10 ? 1: 0; + ha->flags.enable_target_reset = + le32_to_cpu(nv->host_p) & BIT_11 ? 1: 0; + ha->flags.enable_led_scheme = 0; + ha->flags.disable_serdes = le32_to_cpu(nv->host_p) & BIT_5 ? 1: 0; + + ha->operating_mode = (le32_to_cpu(icb->firmware_options_2) & + (BIT_6 | BIT_5 | BIT_4)) >> 4; + + /* save HBA serial number */ + ha->serial0 = icb->port_name[5]; + ha->serial1 = icb->port_name[6]; + ha->serial2 = icb->port_name[7]; + memcpy(vha->node_name, icb->node_name, WWN_SIZE); + memcpy(vha->port_name, icb->port_name, WWN_SIZE); + + icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); + + ha->retry_count = le16_to_cpu(nv->login_retry_count); + + /* Set minimum login_timeout to 4 seconds. */ + if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout) + nv->login_timeout = cpu_to_le16(ql2xlogintimeout); + if (le16_to_cpu(nv->login_timeout) < 4) + nv->login_timeout = __constant_cpu_to_le16(4); + ha->login_timeout = le16_to_cpu(nv->login_timeout); + icb->login_timeout = nv->login_timeout; + + /* Set minimum RATOV to 100 tenths of a second. */ + ha->r_a_tov = 100; + + ha->loop_reset_delay = nv->reset_delay; + + /* Link Down Timeout = 0: + * + * When Port Down timer expires we will start returning + * I/O's to OS with "DID_NO_CONNECT". + * + * Link Down Timeout != 0: + * + * The driver waits for the link to come up after link down + * before returning I/Os to OS with "DID_NO_CONNECT". + */ + if (le16_to_cpu(nv->link_down_timeout) == 0) { + ha->loop_down_abort_time = + (LOOP_DOWN_TIME - LOOP_DOWN_TIMEOUT); + } else { + ha->link_down_timeout = le16_to_cpu(nv->link_down_timeout); + ha->loop_down_abort_time = + (LOOP_DOWN_TIME - ha->link_down_timeout); + } + + /* Need enough time to try and get the port back. */ + ha->port_down_retry_count = le16_to_cpu(nv->port_down_retry_count); + if (qlport_down_retry) + ha->port_down_retry_count = qlport_down_retry; + + /* Set login_retry_count */ + ha->login_retry_count = le16_to_cpu(nv->login_retry_count); + if (ha->port_down_retry_count == + le16_to_cpu(nv->port_down_retry_count) && + ha->port_down_retry_count > 3) + ha->login_retry_count = ha->port_down_retry_count; + else if (ha->port_down_retry_count > (int)ha->login_retry_count) + ha->login_retry_count = ha->port_down_retry_count; + if (ql2xloginretrycount) + ha->login_retry_count = ql2xloginretrycount; + + /* Enable ZIO. */ + if (!vha->flags.init_done) { + ha->zio_mode = le32_to_cpu(icb->firmware_options_2) & + (BIT_3 | BIT_2 | BIT_1 | BIT_0); + ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ? + le16_to_cpu(icb->interrupt_delay_timer): 2; + } + icb->firmware_options_2 &= __constant_cpu_to_le32( + ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); + vha->flags.process_response_queue = 0; + if (ha->zio_mode != QLA_ZIO_DISABLED) { + ha->zio_mode = QLA_ZIO_MODE_6; + + DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay " + "(%d us).\n", vha->host_no, ha->zio_mode, + ha->zio_timer * 100)); + qla_printk(KERN_INFO, ha, + "ZIO mode %d enabled; timer delay (%d us).\n", + ha->zio_mode, ha->zio_timer * 100); + + icb->firmware_options_2 |= cpu_to_le32( + (uint32_t)ha->zio_mode); + icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer); + vha->flags.process_response_queue = 1; + } + + if (rval) { + DEBUG2_3(printk(KERN_WARNING + "scsi(%ld): NVRAM configuration failed!\n", vha->host_no)); + } + return (rval); +} + +void +qla81xx_update_fw_options(scsi_qla_host_t *ha) +{ +} diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 5bedc9d05942..2258152b1f41 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -173,7 +173,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, return; } - vha = sp->vha; + vha = sp->fcport->vha; req = sp->que; cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); @@ -234,7 +234,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, return; } - vha = sp->vha; + vha = sp->fcport->vha; req = sp->que; cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); @@ -294,7 +294,7 @@ qla2x00_start_scsi(srb_t *sp) /* Setup device pointers. */ ret = 0; - vha = sp->vha; + vha = sp->fcport->vha; ha = vha->hw; reg = &ha->iobase->isp; cmd = sp->cmd; @@ -353,7 +353,6 @@ qla2x00_start_scsi(srb_t *sp) /* Build command packet */ req->current_outstanding_cmd = handle; req->outstanding_cmds[handle] = sp; - sp->vha = vha; sp->que = req; sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; @@ -656,7 +655,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, return; } - vha = sp->vha; + vha = sp->fcport->vha; req = sp->que; /* Set transfer direction */ @@ -723,7 +722,7 @@ qla24xx_start_scsi(srb_t *sp) struct req_que *req = NULL; struct rsp_que *rsp = NULL; struct scsi_cmnd *cmd = sp->cmd; - struct scsi_qla_host *vha = sp->vha; + struct scsi_qla_host *vha = sp->fcport->vha; struct qla_hw_data *ha = vha->hw; uint16_t que_id; @@ -791,7 +790,6 @@ qla24xx_start_scsi(srb_t *sp) /* Build command packet. */ req->current_outstanding_cmd = handle; req->outstanding_cmds[handle] = sp; - sp->vha = vha; sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index d5fb79a88001..789fc576f222 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -275,7 +275,7 @@ void qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) { #define LS_UNKNOWN 2 - static char *link_speeds[5] = { "1", "2", "?", "4", "8" }; + static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" }; char *link_speed; uint16_t handle_cnt; uint16_t cnt; @@ -288,6 +288,8 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) /* Setup to process RIO completion. */ handle_cnt = 0; + if (IS_QLA81XX(ha)) + goto skip_rio; switch (mb[0]) { case MBA_SCSI_COMPLETION: handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1])); @@ -339,7 +341,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) default: break; } - +skip_rio: switch (mb[0]) { case MBA_SCSI_COMPLETION: /* Fast Post */ if (!vha->flags.online) @@ -362,7 +364,6 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n", mb[1], mb[2], mb[3]); - qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]); ha->isp_ops->fw_dump(vha, 1); if (IS_FWI2_CAPABLE(ha)) { @@ -387,7 +388,6 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) vha->host_no)); qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n"); - qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; @@ -396,7 +396,6 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) vha->host_no)); qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n"); - qla2x00_post_hwe_work(vha, mb[0], mb[1], mb[2], mb[3]); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; @@ -436,6 +435,8 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) link_speed = link_speeds[LS_UNKNOWN]; if (mb[1] < 5) link_speed = link_speeds[mb[1]]; + else if (mb[1] == 0x13) + link_speed = link_speeds[5]; ha->link_data_rate = mb[1]; } @@ -495,12 +496,17 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) qla2x00_post_aen_work(vha, FCH_EVT_LIPRESET, mb[1]); break; + /* case MBA_DCBX_COMPLETE: */ case MBA_POINT_TO_POINT: /* Point-to-Point */ if (IS_QLA2100(ha)) break; - DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE received.\n", - vha->host_no)); + if (IS_QLA81XX(ha)) + DEBUG2(printk("scsi(%ld): DCBX Completed -- %04x %04x " + "%04x\n", vha->host_no, mb[1], mb[2], mb[3])); + else + DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE " + "received.\n", vha->host_no)); /* * Until there's a transition from loop down to loop up, treat @@ -641,10 +647,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) /* case MBA_RIO_RESPONSE: */ case MBA_ZIO_RESPONSE: - DEBUG2(printk("scsi(%ld): [R|Z]IO update completion.\n", - vha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): [R|Z]IO update completion.\n", + DEBUG3(printk("scsi(%ld): [R|Z]IO update completion.\n", vha->host_no)); if (IS_FWI2_CAPABLE(ha)) @@ -698,6 +701,35 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) } spin_unlock_irqrestore(&ha->cs84xx->access_lock, flags); break; + case MBA_DCBX_START: + DEBUG2(printk("scsi(%ld): DCBX Started -- %04x %04x %04x\n", + vha->host_no, mb[1], mb[2], mb[3])); + break; + case MBA_DCBX_PARAM_UPDATE: + DEBUG2(printk("scsi(%ld): DCBX Parameters Updated -- " + "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); + break; + case MBA_FCF_CONF_ERR: + DEBUG2(printk("scsi(%ld): FCF Configuration Error -- " + "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); + break; + case MBA_IDC_COMPLETE: + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " + "Complete -- %04x %04x %04x\n", vha->host_no, mb[1], mb[2], + mb[3])); + break; + case MBA_IDC_NOTIFY: + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " + "Request Notification -- %04x %04x %04x\n", vha->host_no, + mb[1], mb[2], mb[3])); + /**** Mailbox registers 4 - 7 valid!!! */ + break; + case MBA_IDC_TIME_EXT: + DEBUG2(printk("scsi(%ld): Inter-Driver Commucation " + "Time Extension -- %04x %04x %04x\n", vha->host_no, mb[1], + mb[2], mb[3])); + /**** Mailbox registers 4 - 7 valid!!! */ + break; } if (!vha->vp_idx && ha->num_vhosts) @@ -1510,7 +1542,7 @@ qla2xxx_check_risc_status(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (!IS_QLA25XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)) return; rval = QLA_SUCCESS; @@ -1590,12 +1622,6 @@ qla24xx_intr_handler(int irq, void *dev_id) if (pci_channel_offline(ha->pdev)) break; - if (ha->hw_event_pause_errors == 0) - qla2x00_post_hwe_work(vha, HW_EVENT_PARITY_ERR, - 0, MSW(stat), LSW(stat)); - else if (ha->hw_event_pause_errors < 0xffffffff) - ha->hw_event_pause_errors++; - hccr = RD_REG_DWORD(®->hccr); qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " @@ -1740,12 +1766,6 @@ qla24xx_msix_default(int irq, void *dev_id) if (pci_channel_offline(ha->pdev)) break; - if (ha->hw_event_pause_errors == 0) - qla2x00_post_hwe_work(vha, HW_EVENT_PARITY_ERR, - 0, MSW(stat), LSW(stat)); - else if (ha->hw_event_pause_errors < 0xffffffff) - ha->hw_event_pause_errors++; - hccr = RD_REG_DWORD(®->hccr); qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " @@ -1944,7 +1964,8 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) device_reg_t __iomem *reg = ha->iobase; /* If possible, enable MSI-X. */ - if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha)) + if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && + !IS_QLA8432(ha) && !IS_QLA8001(ha)) goto skip_msix; if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX || @@ -1979,7 +2000,8 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) "MSI-X: Falling back-to INTa mode -- %d.\n", ret); skip_msix: - if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha)) + if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && + !IS_QLA8001(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); @@ -2000,6 +2022,12 @@ skip_msi: ha->flags.inta_enabled = 1; clear_risc_ints: + /* + * FIXME: Noted that 8014s were being dropped during NK testing. + * Timing deltas during MSI-X/INTa transitions? + */ + if (IS_QLA81XX(ha)) + goto fail; spin_lock_irq(&ha->hardware_lock); if (IS_FWI2_CAPABLE(ha)) { WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_HOST_INT); @@ -2044,7 +2072,7 @@ qla2x00_get_rsp_host(struct rsp_que *rsp) if (pkt && pkt->handle < MAX_OUTSTANDING_COMMANDS) { sp = req->outstanding_cmds[pkt->handle]; if (sp) - vha = sp->vha; + vha = sp->fcport->vha; } } if (!vha) diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index a99976f5fabd..db4df45234a5 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -123,8 +123,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) /* Wait for mbx cmd completion until timeout */ - if (!abort_active && io_lock_on) { - + if ((!abort_active && io_lock_on) || IS_NOPOLLING_TYPE(ha)) { set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); if (IS_FWI2_CAPABLE(ha)) @@ -218,7 +217,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) /* Clean up */ ha->mcp = NULL; - if (abort_active || !io_lock_on) { + if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) { DEBUG11(printk("%s(%ld): checking for additional resp " "interrupt.\n", __func__, base_vha->host_no)); @@ -412,7 +411,8 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) */ void qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor, - uint16_t *subminor, uint16_t *attributes, uint32_t *memory) + uint16_t *subminor, uint16_t *attributes, uint32_t *memory, uint8_t *mpi, + uint32_t *mpi_caps) { int rval; mbx_cmd_t mc; @@ -423,6 +423,8 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor, mcp->mb[0] = MBC_GET_FIRMWARE_VERSION; mcp->out_mb = MBX_0; mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_QLA81XX(vha->hw)) + mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10; mcp->flags = 0; mcp->tov = MBX_TOV_SECONDS; rval = qla2x00_mailbox_command(vha, mcp); @@ -436,6 +438,13 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor, *memory = 0x1FFFF; /* Defaults to 128KB. */ else *memory = (mcp->mb[5] << 16) | mcp->mb[4]; + if (IS_QLA81XX(vha->hw)) { + mpi[0] = mcp->mb[10] >> 8; + mpi[1] = mcp->mb[10] & 0xff; + mpi[2] = mcp->mb[11] >> 8; + mpi[3] = mcp->mb[11] & 0xff; + *mpi_caps = (mcp->mb[12] << 16) | mcp->mb[13]; + } if (rval != QLA_SUCCESS) { /*EMPTY*/ @@ -568,7 +577,6 @@ int qla2x00_mbx_reg_test(scsi_qla_host_t *vha) { int rval; - struct qla_hw_data *ha = vha->hw; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; @@ -595,14 +603,6 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha) if (mcp->mb[5] != 0xA5A5 || mcp->mb[6] != 0x5A5A || mcp->mb[7] != 0x2525) rval = QLA_FUNCTION_FAILED; - if (rval == QLA_FUNCTION_FAILED) { - struct device_reg_24xx __iomem *reg = - &ha->iobase->isp24; - - qla2xxx_hw_event_log(vha, HW_EVENT_ISP_ERR, 0, - LSW(RD_REG_DWORD(®->hccr)), - LSW(RD_REG_DWORD(®->istatus))); - } } if (rval != QLA_SUCCESS) { @@ -1363,7 +1363,13 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - if (IS_FWI2_CAPABLE(vha->hw)) { + if (IS_QLA81XX(vha->hw)) { + /* Logout across all FCFs. */ + mcp->mb[0] = MBC_LIP_FULL_LOGIN; + mcp->mb[1] = BIT_1; + mcp->mb[2] = 0; + mcp->out_mb = MBX_2|MBX_1|MBX_0; + } else if (IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[1] = BIT_6; mcp->mb[2] = 0; @@ -1853,6 +1859,9 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + if (IS_QLA81XX(vha->hw)) + return QLA_SUCCESS; + DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n", vha->host_no)); @@ -2512,7 +2521,7 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA25XX(vha->hw)) + if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); @@ -3155,7 +3164,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp, mcp->mb[7] = LSW(MSD(rsp->dma)); mcp->mb[5] = rsp->length; mcp->mb[11] = rsp->vp_idx; - mcp->mb[14] = rsp->msix->vector; + mcp->mb[14] = rsp->msix->entry; mcp->mb[13] = rsp->rid; reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) + diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 386ffeae5b5a..886323130fcc 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -614,8 +614,10 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->vp_idx = vp_idx; req->qos = qos; - if (ha->rsp_q_map[rsp_que]) + if (ha->rsp_q_map[rsp_que]) { req->rsp = ha->rsp_q_map[rsp_que]; + req->rsp->req = req; + } /* Use alternate PCI bus number */ if (MSB(req->rid)) options |= BIT_4; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 8ea927788b3f..4a71f522f925 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -404,26 +404,9 @@ static char * qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str) { struct qla_hw_data *ha = vha->hw; - sprintf(str, "%d.%02d.%02d ", ha->fw_major_version, - ha->fw_minor_version, - ha->fw_subminor_version); - if (ha->fw_attributes & BIT_0) - strcat(str, "[Class 2] "); - if (ha->fw_attributes & BIT_1) - strcat(str, "[IP] "); - if (ha->fw_attributes & BIT_2) - strcat(str, "[Multi-ID] "); - if (ha->fw_attributes & BIT_3) - strcat(str, "[SB-2] "); - if (ha->fw_attributes & BIT_4) - strcat(str, "[T10 CRC] "); - if (ha->fw_attributes & BIT_5) - strcat(str, "[VI] "); - if (ha->fw_attributes & BIT_10) - strcat(str, "[84XX] "); - if (ha->fw_attributes & BIT_13) - strcat(str, "[Experimental]"); + sprintf(str, "%d.%02d.%02d (%x)", ha->fw_major_version, + ha->fw_minor_version, ha->fw_subminor_version, ha->fw_attributes); return str; } @@ -438,7 +421,6 @@ qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport, if (!sp) return sp; - sp->vha = vha; sp->fcport = fcport; sp->cmd = cmd; sp->que = ha->req_q_map[0]; @@ -1182,7 +1164,7 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) continue; for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { sp = req->outstanding_cmds[cnt]; - if (sp && sp->vha == vha) { + if (sp && sp->fcport->vha == vha) { req->outstanding_cmds[cnt] = NULL; sp->cmd->result = res; qla2x00_sp_compl(ha, sp); @@ -1329,6 +1311,8 @@ qla24xx_disable_intrs(struct qla_hw_data *ha) unsigned long flags = 0; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + if (IS_NOPOLLING_TYPE(ha)) + return; spin_lock_irqsave(&ha->hardware_lock, flags); ha->interrupts_on = 0; WRT_REG_DWORD(®->ictrl, 0); @@ -1488,6 +1472,44 @@ static struct isp_operations qla25xx_isp_ops = { .rd_req_reg = qla24xx_rd_req_reg, }; +static struct isp_operations qla81xx_isp_ops = { + .pci_config = qla25xx_pci_config, + .reset_chip = qla24xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla24xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla81xx_nvram_config, + .update_fw_options = qla81xx_update_fw_options, + .load_risc = qla24xx_load_risc, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla24xx_intr_handler, + .enable_intrs = qla24xx_enable_intrs, + .disable_intrs = qla24xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .target_reset = qla24xx_abort_target, + .lun_reset = qla24xx_lun_reset, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = qla25xx_read_nvram_data, + .write_nvram = qla25xx_write_nvram_data, + .fw_dump = qla81xx_fw_dump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla24xx_beacon_blink, + .read_optrom = qla25xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla24xx_start_scsi, + .wrt_req_reg = qla24xx_wrt_req_reg, + .wrt_rsp_reg = qla24xx_wrt_rsp_reg, + .rd_req_reg = qla24xx_rd_req_reg, +}; + static inline void qla2x00_set_isp_flags(struct qla_hw_data *ha) { @@ -1567,6 +1589,13 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; + case PCI_DEVICE_ID_QLOGIC_ISP8001: + ha->device_type |= DT_ISP8001; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; } } @@ -1629,7 +1658,7 @@ skip_pio: /* Determine queue resources */ ha->max_queues = 1; - if (ql2xmaxqueues <= 1 || !IS_QLA25XX(ha)) + if (ql2xmaxqueues <= 1 || (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))) goto mqiobase_exit; ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3), pci_resource_len(ha->pdev, 3)); @@ -1706,7 +1735,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532) { + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001) { bars = pci_select_bars(pdev, IORESOURCE_MEM); sht = &qla24xx_driver_template; mem_only = 1; @@ -1760,6 +1790,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) rsp_length = RESPONSE_ENTRY_CNT_2100; ha->max_loop_id = SNS_LAST_LOOP_ID_2100; ha->gid_list_info_size = 4; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA2200(ha)) { ha->mbx_count = MAILBOX_REGISTER_COUNT; @@ -1767,6 +1801,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) rsp_length = RESPONSE_ENTRY_CNT_2100; ha->max_loop_id = SNS_LAST_LOOP_ID_2100; ha->gid_list_info_size = 4; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA23XX(ha)) { ha->mbx_count = MAILBOX_REGISTER_COUNT; @@ -1776,6 +1814,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->gid_list_info_size = 6; if (IS_QLA2322(ha) || IS_QLA6322(ha)) ha->optrom_size = OPTROM_SIZE_2322; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; ha->isp_ops = &qla2300_isp_ops; } else if (IS_QLA24XX_TYPE(ha)) { ha->mbx_count = MAILBOX_REGISTER_COUNT; @@ -1787,6 +1829,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->optrom_size = OPTROM_SIZE_24XX; ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX; ha->isp_ops = &qla24xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA; + ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; + ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; } else if (IS_QLA25XX(ha)) { ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_24XX; @@ -1797,6 +1843,23 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->optrom_size = OPTROM_SIZE_25XX; ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; ha->isp_ops = &qla25xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA; + ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; + ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; + } else if (IS_QLA81XX(ha)) { + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_24XX; + rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_81xx); + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_81XX; + ha->isp_ops = &qla81xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; } mutex_init(&ha->vport_lock); @@ -2458,23 +2521,6 @@ qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code, return qla2x00_post_work(vha, e, 1); } -int -qla2x00_post_hwe_work(struct scsi_qla_host *vha, uint16_t code, uint16_t d1, - uint16_t d2, uint16_t d3) -{ - struct qla_work_evt *e; - - e = qla2x00_alloc_work(vha, QLA_EVT_HWE_LOG, 1); - if (!e) - return QLA_FUNCTION_FAILED; - - e->u.hwe.code = code; - e->u.hwe.d1 = d1; - e->u.hwe.d2 = d2; - e->u.hwe.d3 = d3; - return qla2x00_post_work(vha, e, 1); -} - static void qla2x00_do_work(struct scsi_qla_host *vha) { @@ -2492,10 +2538,6 @@ qla2x00_do_work(struct scsi_qla_host *vha) fc_host_post_event(vha->host, fc_get_event_number(), e->u.aen.code, e->u.aen.data); break; - case QLA_EVT_HWE_LOG: - qla2xxx_hw_event_log(vha, e->u.hwe.code, e->u.hwe.d1, - e->u.hwe.d2, e->u.hwe.d3); - break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); @@ -2914,13 +2956,14 @@ qla2x00_timer(scsi_qla_host_t *vha) /* Firmware interface routines. */ -#define FW_BLOBS 6 +#define FW_BLOBS 7 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 #define FW_ISP2322 3 #define FW_ISP24XX 4 #define FW_ISP25XX 5 +#define FW_ISP81XX 6 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" @@ -2928,6 +2971,7 @@ qla2x00_timer(scsi_qla_host_t *vha) #define FW_FILE_ISP2322 "ql2322_fw.bin" #define FW_FILE_ISP24XX "ql2400_fw.bin" #define FW_FILE_ISP25XX "ql2500_fw.bin" +#define FW_FILE_ISP81XX "ql8100_fw.bin" static DEFINE_MUTEX(qla_fw_lock); @@ -2938,6 +2982,7 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, }, { .name = FW_FILE_ISP24XX, }, { .name = FW_FILE_ISP25XX, }, + { .name = FW_FILE_ISP81XX, }, }; struct fw_blob * @@ -2959,6 +3004,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) blob = &qla_fw_blobs[FW_ISP24XX]; } else if (IS_QLA25XX(ha)) { blob = &qla_fw_blobs[FW_ISP25XX]; + } else if (IS_QLA81XX(ha)) { + blob = &qla_fw_blobs[FW_ISP81XX]; } mutex_lock(&qla_fw_lock); @@ -3112,6 +3159,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); @@ -3200,3 +3248,4 @@ MODULE_FIRMWARE(FW_FILE_ISP2300); MODULE_FIRMWARE(FW_FILE_ISP2322); MODULE_FIRMWARE(FW_FILE_ISP24XX); MODULE_FIRMWARE(FW_FILE_ISP25XX); +MODULE_FIRMWARE(FW_FILE_ISP81XX); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index c538ee1b1a31..303f8ee11f25 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -425,27 +425,27 @@ qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat) #define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4) static inline uint32_t -flash_conf_to_access_addr(uint32_t faddr) +flash_conf_addr(struct qla_hw_data *ha, uint32_t faddr) { - return FARX_ACCESS_FLASH_CONF | faddr; + return ha->flash_conf_off | faddr; } static inline uint32_t -flash_data_to_access_addr(uint32_t faddr) +flash_data_addr(struct qla_hw_data *ha, uint32_t faddr) { - return FARX_ACCESS_FLASH_DATA | faddr; + return ha->flash_data_off | faddr; } static inline uint32_t -nvram_conf_to_access_addr(uint32_t naddr) +nvram_conf_addr(struct qla_hw_data *ha, uint32_t naddr) { - return FARX_ACCESS_NVRAM_CONF | naddr; + return ha->nvram_conf_off | naddr; } static inline uint32_t -nvram_data_to_access_addr(uint32_t naddr) +nvram_data_addr(struct qla_hw_data *ha, uint32_t naddr) { - return FARX_ACCESS_NVRAM_DATA | naddr; + return ha->nvram_data_off | naddr; } static uint32_t @@ -481,10 +481,12 @@ qla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { uint32_t i; + struct qla_hw_data *ha = vha->hw; + /* Dword reads to flash. */ for (i = 0; i < dwords; i++, faddr++) - dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(vha->hw, - flash_data_to_access_addr(faddr))); + dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, + flash_data_addr(ha, faddr))); return dwptr; } @@ -518,7 +520,7 @@ qla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id, { uint32_t ids; - ids = qla24xx_read_flash_dword(ha, flash_data_to_access_addr(0xd03ab)); + ids = qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x03ab)); *man_id = LSB(ids); *flash_id = MSB(ids); @@ -530,8 +532,7 @@ qla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id, * Example: ATMEL 0x00 01 45 1F * Extract MFG and Dev ID from last two bytes. */ - ids = qla24xx_read_flash_dword(ha, - flash_data_to_access_addr(0xd009f)); + ids = qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x009f)); *man_id = LSB(ids); *flash_id = MSB(ids); } @@ -555,9 +556,13 @@ qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start) /* Begin with sane defaults. */ loc = locations[0]; - *start = IS_QLA24XX_TYPE(ha) ? FA_FLASH_LAYOUT_ADDR_24: - FA_FLASH_LAYOUT_ADDR; - + *start = 0; + if (IS_QLA24XX_TYPE(ha)) + *start = FA_FLASH_LAYOUT_ADDR_24; + else if (IS_QLA25XX(ha)) + *start = FA_FLASH_LAYOUT_ADDR; + else if (IS_QLA81XX(ha)) + *start = FA_FLASH_LAYOUT_ADDR_81; /* Begin with first PCI expansion ROM header. */ buf = (uint8_t *)req->ring; dcode = (uint32_t *)req->ring; @@ -618,6 +623,22 @@ static void qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) { const char *loc, *locations[] = { "DEF", "FLT" }; + const uint32_t def_fw[] = + { FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 }; + const uint32_t def_boot[] = + { FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR_81 }; + const uint32_t def_vpd_nvram[] = + { FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR_81 }; + const uint32_t def_fdt[] = + { FA_FLASH_DESCR_ADDR_24, FA_FLASH_DESCR_ADDR, + FA_FLASH_DESCR_ADDR_81 }; + const uint32_t def_npiv_conf0[] = + { FA_NPIV_CONF0_ADDR_24, FA_NPIV_CONF0_ADDR, + FA_NPIV_CONF0_ADDR_81 }; + const uint32_t def_npiv_conf1[] = + { FA_NPIV_CONF1_ADDR_24, FA_NPIV_CONF1_ADDR, + FA_NPIV_CONF1_ADDR_81 }; + uint32_t def; uint16_t *wptr; uint16_t cnt, chksum; uint32_t start; @@ -676,20 +697,12 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) case FLT_REG_FDT: ha->flt_region_fdt = start; break; - case FLT_REG_HW_EVENT_0: - if (!PCI_FUNC(ha->pdev->devfn)) - ha->flt_region_hw_event = start; - break; - case FLT_REG_HW_EVENT_1: - if (PCI_FUNC(ha->pdev->devfn)) - ha->flt_region_hw_event = start; - break; case FLT_REG_NPIV_CONF_0: - if (!PCI_FUNC(ha->pdev->devfn)) + if (!(PCI_FUNC(ha->pdev->devfn) & 1)) ha->flt_region_npiv_conf = start; break; case FLT_REG_NPIV_CONF_1: - if (PCI_FUNC(ha->pdev->devfn)) + if (PCI_FUNC(ha->pdev->devfn) & 1) ha->flt_region_npiv_conf = start; break; } @@ -699,22 +712,24 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) no_flash_data: /* Use hardcoded defaults. */ loc = locations[0]; - ha->flt_region_fw = FA_RISC_CODE_ADDR; - ha->flt_region_boot = FA_BOOT_CODE_ADDR; - ha->flt_region_vpd_nvram = FA_VPD_NVRAM_ADDR; - ha->flt_region_fdt = IS_QLA24XX_TYPE(ha) ? FA_FLASH_DESCR_ADDR_24: - FA_FLASH_DESCR_ADDR; - ha->flt_region_hw_event = !PCI_FUNC(ha->pdev->devfn) ? - FA_HW_EVENT0_ADDR: FA_HW_EVENT1_ADDR; - ha->flt_region_npiv_conf = !PCI_FUNC(ha->pdev->devfn) ? - (IS_QLA24XX_TYPE(ha) ? FA_NPIV_CONF0_ADDR_24: FA_NPIV_CONF0_ADDR): - (IS_QLA24XX_TYPE(ha) ? FA_NPIV_CONF1_ADDR_24: FA_NPIV_CONF1_ADDR); + def = 0; + if (IS_QLA24XX_TYPE(ha)) + def = 0; + else if (IS_QLA25XX(ha)) + def = 1; + else if (IS_QLA81XX(ha)) + def = 2; + ha->flt_region_fw = def_fw[def]; + ha->flt_region_boot = def_boot[def]; + ha->flt_region_vpd_nvram = def_vpd_nvram[def]; + ha->flt_region_fdt = def_fdt[def]; + ha->flt_region_npiv_conf = !(PCI_FUNC(ha->pdev->devfn) & 1) ? + def_npiv_conf0[def]: def_npiv_conf1[def]; done: DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x " - "vpd_nvram=0x%x fdt=0x%x flt=0x%x hwe=0x%x npiv=0x%x.\n", loc, + "vpd_nvram=0x%x fdt=0x%x flt=0x%x npiv=0x%x.\n", loc, ha->flt_region_boot, ha->flt_region_fw, ha->flt_region_vpd_nvram, - ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_hw_event, - ha->flt_region_npiv_conf)); + ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_npiv_conf)); } static void @@ -757,14 +772,14 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) mid = le16_to_cpu(fdt->man_id); fid = le16_to_cpu(fdt->id); ha->fdt_wrt_disable = fdt->wrt_disable_bits; - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0300 | fdt->erase_cmd); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0300 | fdt->erase_cmd); ha->fdt_block_size = le32_to_cpu(fdt->block_size); if (fdt->unprotect_sec_cmd) { - ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0300 | + ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0300 | fdt->unprotect_sec_cmd); ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ? - flash_conf_to_access_addr(0x0300 | fdt->protect_sec_cmd): - flash_conf_to_access_addr(0x0336); + flash_conf_addr(ha, 0x0300 | fdt->protect_sec_cmd): + flash_conf_addr(ha, 0x0336); } goto done; no_flash_data: @@ -773,7 +788,7 @@ no_flash_data: mid = man_id; fid = flash_id; ha->fdt_wrt_disable = 0x9c; - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x03d8); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x03d8); switch (man_id) { case 0xbf: /* STT flash. */ if (flash_id == 0x8e) @@ -782,16 +797,16 @@ no_flash_data: ha->fdt_block_size = FLASH_BLK_SIZE_32K; if (flash_id == 0x80) - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0352); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0352); break; case 0x13: /* ST M25P80. */ ha->fdt_block_size = FLASH_BLK_SIZE_64K; break; case 0x1f: /* Atmel 26DF081A. */ ha->fdt_block_size = FLASH_BLK_SIZE_4K; - ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0320); - ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0339); - ha->fdt_protect_sec_cmd = flash_conf_to_access_addr(0x0336); + ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0320); + ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0339); + ha->fdt_protect_sec_cmd = flash_conf_addr(ha, 0x0336); break; default: /* Default to 64 kb sector size. */ @@ -813,7 +828,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) uint32_t flt_addr; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA81XX(ha)) return QLA_SUCCESS; ret = qla2xxx_find_flt_start(vha, &flt_addr); @@ -838,7 +853,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) struct qla_npiv_entry *entry; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA81XX(ha)) return; ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, @@ -930,9 +945,9 @@ qla24xx_unprotect_flash(struct qla_hw_data *ha) return; /* Disable flash write-protection. */ - qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); + qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0); /* Some flash parts need an additional zero-write to clear bits.*/ - qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); + qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0); } static void @@ -945,11 +960,10 @@ qla24xx_protect_flash(struct qla_hw_data *ha) goto skip_wrt_protect; /* Enable flash write-protection and wait for completion. */ - qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), + qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), ha->fdt_wrt_disable); for (cnt = 300; cnt && - qla24xx_read_flash_dword(ha, - flash_conf_to_access_addr(0x005)) & BIT_0; + qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x005)) & BIT_0; cnt--) { udelay(10); } @@ -977,7 +991,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, ret = QLA_SUCCESS; /* Prepare burst-capable write on supported ISPs. */ - if (IS_QLA25XX(ha) && !(faddr & 0xfff) && + if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && !(faddr & 0xfff) && dwords > OPTROM_BURST_DWORDS) { optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, &optrom_dma, GFP_KERNEL); @@ -989,7 +1003,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, } rest_addr = (ha->fdt_block_size >> 2) - 1; - sec_mask = 0x80000 - (ha->fdt_block_size >> 2); + sec_mask = (ha->optrom_size >> 2) - (ha->fdt_block_size >> 2); qla24xx_unprotect_flash(ha); @@ -1024,13 +1038,13 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, *s = cpu_to_le32(*d); ret = qla2x00_load_ram(vha, optrom_dma, - flash_data_to_access_addr(faddr), + flash_data_addr(ha, faddr), OPTROM_BURST_DWORDS); if (ret != QLA_SUCCESS) { qla_printk(KERN_WARNING, ha, "Unable to burst-write optrom segment " "(%x/%x/%llx).\n", ret, - flash_data_to_access_addr(faddr), + flash_data_addr(ha, faddr), (unsigned long long)optrom_dma); qla_printk(KERN_WARNING, ha, "Reverting to slow-write.\n"); @@ -1047,7 +1061,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, } ret = qla24xx_write_flash_dword(ha, - flash_data_to_access_addr(faddr), cpu_to_le32(*dwptr)); + flash_data_addr(ha, faddr), cpu_to_le32(*dwptr)); if (ret != QLA_SUCCESS) { DEBUG9(printk("%s(%ld) Unable to program flash " "address=%x data=%x.\n", __func__, @@ -1098,12 +1112,13 @@ qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, { uint32_t i; uint32_t *dwptr; + struct qla_hw_data *ha = vha->hw; /* Dword reads to flash. */ dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++) - dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(vha->hw, - nvram_data_to_access_addr(naddr))); + dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, + nvram_data_addr(ha, naddr))); return buf; } @@ -1160,17 +1175,14 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ /* Disable NVRAM write-protection. */ - qla24xx_write_flash_dword(ha, nvram_conf_to_access_addr(0x101), - 0); - qla24xx_write_flash_dword(ha, nvram_conf_to_access_addr(0x101), - 0); + qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0); + qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0); /* Dword writes to flash. */ dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++, dwptr++) { ret = qla24xx_write_flash_dword(ha, - nvram_data_to_access_addr(naddr), - cpu_to_le32(*dwptr)); + nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr)); if (ret != QLA_SUCCESS) { DEBUG9(qla_printk("Unable to program nvram address=%x " "data=%x.\n", naddr, *dwptr)); @@ -1179,8 +1191,7 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, } /* Enable NVRAM write-protection. */ - qla24xx_write_flash_dword(ha, nvram_conf_to_access_addr(0x101), - 0x8c); + qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0x8c); /* Disable flash write. */ WRT_REG_DWORD(®->ctrl_status, @@ -1202,8 +1213,7 @@ qla25xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++) dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, - flash_data_to_access_addr(ha->flt_region_vpd_nvram | - naddr))); + flash_data_addr(ha, ha->flt_region_vpd_nvram | naddr))); return buf; } @@ -2246,12 +2256,12 @@ qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, burst = left; rval = qla2x00_dump_ram(vha, optrom_dma, - flash_data_to_access_addr(faddr), burst); + flash_data_addr(ha, faddr), burst); if (rval) { qla_printk(KERN_WARNING, ha, "Unable to burst-read optrom segment " "(%x/%x/%llx).\n", rval, - flash_data_to_access_addr(faddr), + flash_data_addr(ha, faddr), (unsigned long long)optrom_dma); qla_printk(KERN_WARNING, ha, "Reverting to slow-read.\n"); @@ -2648,108 +2658,3 @@ qla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size) return 0; } - -static int -qla2xxx_hw_event_store(scsi_qla_host_t *vha, uint32_t *fdata) -{ - uint32_t d[2], faddr; - struct qla_hw_data *ha = vha->hw; - - /* Locate first empty entry. */ - for (;;) { - if (ha->hw_event_ptr >= - ha->flt_region_hw_event + FA_HW_EVENT_SIZE) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Log Full!\n")); - return QLA_MEMORY_ALLOC_FAILED; - } - - qla24xx_read_flash_data(vha, d, ha->hw_event_ptr, 2); - faddr = flash_data_to_access_addr(ha->hw_event_ptr); - ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; - if (d[0] == __constant_cpu_to_le32(0xffffffff) && - d[1] == __constant_cpu_to_le32(0xffffffff)) { - qla24xx_unprotect_flash(ha); - - qla24xx_write_flash_dword(ha, faddr++, - cpu_to_le32(jiffies)); - qla24xx_write_flash_dword(ha, faddr++, 0); - qla24xx_write_flash_dword(ha, faddr++, *fdata++); - qla24xx_write_flash_dword(ha, faddr++, *fdata); - - qla24xx_protect_flash(ha); - break; - } - } - return QLA_SUCCESS; -} - -int -qla2xxx_hw_event_log(scsi_qla_host_t *vha, uint16_t code, uint16_t d1, - uint16_t d2, uint16_t d3) -{ -#define QMARK(a, b, c, d) \ - cpu_to_le32(LSB(a) << 24 | LSB(b) << 16 | LSB(c) << 8 | LSB(d)) - struct qla_hw_data *ha = vha->hw; - int rval; - uint32_t marker[2], fdata[4]; - - if (ha->flt_region_hw_event == 0) - return QLA_FUNCTION_FAILED; - - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- code=%x, d1=%x, d2=%x, d3=%x.\n", code, d1, d2, d3)); - - /* If marker not already found, locate or write. */ - if (!ha->flags.hw_event_marker_found) { - /* Create marker. */ - marker[0] = QMARK('L', ha->fw_major_version, - ha->fw_minor_version, ha->fw_subminor_version); - marker[1] = QMARK(QLA_DRIVER_MAJOR_VER, QLA_DRIVER_MINOR_VER, - QLA_DRIVER_PATCH_VER, QLA_DRIVER_BETA_VER); - - /* Locate marker. */ - ha->hw_event_ptr = ha->flt_region_hw_event; - for (;;) { - qla24xx_read_flash_data(vha, fdata, ha->hw_event_ptr, - 4); - if (fdata[0] == __constant_cpu_to_le32(0xffffffff) && - fdata[1] == __constant_cpu_to_le32(0xffffffff)) - break; - ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; - if (ha->hw_event_ptr >= - ha->flt_region_hw_event + FA_HW_EVENT_SIZE) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Log Full!\n")); - return QLA_MEMORY_ALLOC_FAILED; - } - if (fdata[2] == marker[0] && fdata[3] == marker[1]) { - ha->flags.hw_event_marker_found = 1; - break; - } - } - /* No marker, write it. */ - if (!ha->flags.hw_event_marker_found) { - rval = qla2xxx_hw_event_store(vha, marker); - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Failed marker write=%x.!\n", - rval)); - return rval; - } - ha->flags.hw_event_marker_found = 1; - } - } - - /* Store error. */ - fdata[0] = cpu_to_le32(code << 16 | d1); - fdata[1] = cpu_to_le32(d2 << 16 | d3); - rval = qla2xxx_hw_event_store(vha, fdata); - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "HW event -- Failed error write=%x.!\n", - rval)); - } - - return rval; -} diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index be22f3a09f8d..808bab6ef06b 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,9 +7,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.03-k1" +#define QLA2XXX_VERSION "8.03.00-k1" #define QLA_DRIVER_MAJOR_VER 8 -#define QLA_DRIVER_MINOR_VER 2 -#define QLA_DRIVER_PATCH_VER 3 +#define QLA_DRIVER_MINOR_VER 3 +#define QLA_DRIVER_PATCH_VER 0 #define QLA_DRIVER_BETA_VER 0 diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c index 913a931176ef..8e5c169b03fb 100644 --- a/drivers/scsi/raid_class.c +++ b/drivers/scsi/raid_class.c @@ -237,8 +237,7 @@ int raid_component_add(struct raid_template *r,struct device *raid_dev, rc->dev.parent = get_device(component_dev); rc->num = rd->component_count++; - snprintf(rc->dev.bus_id, sizeof(rc->dev.bus_id), - "component-%d", rc->num); + dev_set_name(&rc->dev, "component-%d", rc->num); list_add_tail(&rc->node, &rd->component_list); rc->dev.class = &raid_class.class; err = device_add(&rc->dev); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index f8b79d401d58..42e72a2c1f98 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -651,10 +651,6 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) unsigned long timeout; int rtn = 0; - /* - * We will use a queued command if possible, otherwise we will - * emulate the queuing and calling of completion function ourselves. - */ atomic_inc(&cmd->device->iorequest_cnt); /* check if the device is still usable */ diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 27c633f55794..6eebd0bbe8a8 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -2508,7 +2508,7 @@ static void pseudo_0_release(struct device *dev) } static struct device pseudo_primary = { - .bus_id = "pseudo_0", + .init_name = "pseudo_0", .release = pseudo_0_release, }; @@ -2680,7 +2680,7 @@ static int sdebug_add_adapter(void) sdbg_host->dev.bus = &pseudo_lld_bus; sdbg_host->dev.parent = &pseudo_primary; sdbg_host->dev.release = &sdebug_release_adapter; - sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host); + dev_set_name(&sdbg_host->dev, "adapter%d", scsi_debug_add_host); error = device_register(&sdbg_host->dev); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index d86ebea9350a..ad6a1370761e 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -124,34 +124,22 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) enum blk_eh_timer_return scsi_times_out(struct request *req) { struct scsi_cmnd *scmd = req->special; - enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED; scsi_log_completion(scmd, TIMEOUT_ERROR); if (scmd->device->host->transportt->eh_timed_out) - eh_timed_out = scmd->device->host->transportt->eh_timed_out; + rtn = scmd->device->host->transportt->eh_timed_out(scmd); else if (scmd->device->host->hostt->eh_timed_out) - eh_timed_out = scmd->device->host->hostt->eh_timed_out; - else - eh_timed_out = NULL; + rtn = scmd->device->host->hostt->eh_timed_out(scmd); - if (eh_timed_out) { - rtn = eh_timed_out(scmd); - switch (rtn) { - case BLK_EH_NOT_HANDLED: - break; - default: - return rtn; - } - } - - if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { + if (unlikely(rtn == BLK_EH_NOT_HANDLED && + !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { scmd->result |= DID_TIME_OUT << 16; - return BLK_EH_HANDLED; + rtn = BLK_EH_HANDLED; } - return BLK_EH_NOT_HANDLED; + return rtn; } /** diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 2ae4f8fc5831..b98f763931c5 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -167,10 +167,17 @@ EXPORT_SYMBOL(scsi_set_medium_removal); static int scsi_ioctl_get_pci(struct scsi_device *sdev, void __user *arg) { struct device *dev = scsi_get_device(sdev->host); + const char *name; if (!dev) return -ENXIO; - return copy_to_user(arg, dev->bus_id, sizeof(dev->bus_id))? -EFAULT: 0; + + name = dev_name(dev); + + /* compatibility with old ioctl which only returned + * 20 characters */ + return copy_to_user(arg, name, min(strlen(name), (size_t)20)) + ? -EFAULT: 0; } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index f2f51e0333eb..940dc32ff0dc 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -91,26 +91,19 @@ static void scsi_unprep_request(struct request *req) scsi_put_command(cmd); } -/* - * Function: scsi_queue_insert() - * - * Purpose: Insert a command in the midlevel queue. - * - * Arguments: cmd - command that we are adding to queue. - * reason - why we are inserting command to queue. - * - * Lock status: Assumed that lock is not held upon entry. - * - * Returns: Nothing. - * - * Notes: We do this for one of two cases. Either the host is busy - * and it cannot accept any more commands for the time being, - * or the device returned QUEUE_FULL and can accept no more - * commands. - * Notes: This could be called either from an interrupt context or a - * normal process context. +/** + * __scsi_queue_insert - private queue insertion + * @cmd: The SCSI command being requeued + * @reason: The reason for the requeue + * @unbusy: Whether the queue should be unbusied + * + * This is a private queue insertion. The public interface + * scsi_queue_insert() always assumes the queue should be unbusied + * because it's always called before the completion. This function is + * for a requeue after completion, which should only occur in this + * file. */ -int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) +static int __scsi_queue_insert(struct scsi_cmnd *cmd, int reason, int unbusy) { struct Scsi_Host *host = cmd->device->host; struct scsi_device *device = cmd->device; @@ -150,7 +143,8 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) * Decrement the counters, since these commands are no longer * active on the host/device. */ - scsi_device_unbusy(device); + if (unbusy) + scsi_device_unbusy(device); /* * Requeue this command. It will go before all other commands @@ -172,6 +166,29 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) return 0; } +/* + * Function: scsi_queue_insert() + * + * Purpose: Insert a command in the midlevel queue. + * + * Arguments: cmd - command that we are adding to queue. + * reason - why we are inserting command to queue. + * + * Lock status: Assumed that lock is not held upon entry. + * + * Returns: Nothing. + * + * Notes: We do this for one of two cases. Either the host is busy + * and it cannot accept any more commands for the time being, + * or the device returned QUEUE_FULL and can accept no more + * commands. + * Notes: This could be called either from an interrupt context or a + * normal process context. + */ +int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) +{ + return __scsi_queue_insert(cmd, reason, 1); +} /** * scsi_execute - insert request and wait for the result * @sdev: scsi device @@ -684,6 +701,8 @@ void scsi_run_host_queues(struct Scsi_Host *shost) scsi_run_queue(sdev->request_queue); } +static void __scsi_release_buffers(struct scsi_cmnd *, int); + /* * Function: scsi_end_request() * @@ -732,6 +751,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int error, * leftovers in the front of the * queue, and goose the queue again. */ + scsi_release_buffers(cmd); scsi_requeue_command(q, cmd); cmd = NULL; } @@ -743,6 +763,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int error, * This will goose the queue request function at the end, so we don't * need to worry about launching another command. */ + __scsi_release_buffers(cmd, 0); scsi_next_command(cmd); return NULL; } @@ -798,6 +819,26 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb) __sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, scsi_sg_free); } +static void __scsi_release_buffers(struct scsi_cmnd *cmd, int do_bidi_check) +{ + + if (cmd->sdb.table.nents) + scsi_free_sgtable(&cmd->sdb); + + memset(&cmd->sdb, 0, sizeof(cmd->sdb)); + + if (do_bidi_check && scsi_bidi_cmnd(cmd)) { + struct scsi_data_buffer *bidi_sdb = + cmd->request->next_rq->special; + scsi_free_sgtable(bidi_sdb); + kmem_cache_free(scsi_sdb_cache, bidi_sdb); + cmd->request->next_rq->special = NULL; + } + + if (scsi_prot_sg_count(cmd)) + scsi_free_sgtable(cmd->prot_sdb); +} + /* * Function: scsi_release_buffers() * @@ -817,21 +858,7 @@ static void scsi_free_sgtable(struct scsi_data_buffer *sdb) */ void scsi_release_buffers(struct scsi_cmnd *cmd) { - if (cmd->sdb.table.nents) - scsi_free_sgtable(&cmd->sdb); - - memset(&cmd->sdb, 0, sizeof(cmd->sdb)); - - if (scsi_bidi_cmnd(cmd)) { - struct scsi_data_buffer *bidi_sdb = - cmd->request->next_rq->special; - scsi_free_sgtable(bidi_sdb); - kmem_cache_free(scsi_sdb_cache, bidi_sdb); - cmd->request->next_rq->special = NULL; - } - - if (scsi_prot_sg_count(cmd)) - scsi_free_sgtable(cmd->prot_sdb); + __scsi_release_buffers(cmd, 1); } EXPORT_SYMBOL(scsi_release_buffers); @@ -945,7 +972,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) } BUG_ON(blk_bidi_rq(req)); /* bidi not support for !blk_pc_request yet */ - scsi_release_buffers(cmd); /* * Next deal with any sectors which we were able to correctly @@ -963,6 +989,8 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) return; this_count = blk_rq_bytes(req); + error = -EIO; + if (host_byte(result) == DID_RESET) { /* Third party bus reset or reset for error recovery * reasons. Just retry the command and see what @@ -1004,13 +1032,18 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) /* This will issue a new 6-byte command. */ cmd->device->use_10_for_rw = 0; action = ACTION_REPREP; + } else if (sshdr.asc == 0x10) /* DIX */ { + description = "Host Data Integrity Failure"; + action = ACTION_FAIL; + error = -EILSEQ; } else action = ACTION_FAIL; break; case ABORTED_COMMAND: if (sshdr.asc == 0x10) { /* DIF */ + description = "Target Data Integrity Failure"; action = ACTION_FAIL; - description = "Data Integrity Failure"; + error = -EILSEQ; } else action = ACTION_RETRY; break; @@ -1029,6 +1062,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) case 0x09: /* self test in progress */ action = ACTION_DELAYED_RETRY; break; + default: + description = "Device not ready"; + action = ACTION_FAIL; + break; } } else { description = "Device not ready"; @@ -1052,9 +1089,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) switch (action) { case ACTION_FAIL: /* Give up and fail the remainder of the request */ + scsi_release_buffers(cmd); if (!(req->cmd_flags & REQ_QUIET)) { if (description) - scmd_printk(KERN_INFO, cmd, "%s", + scmd_printk(KERN_INFO, cmd, "%s\n", description); scsi_print_result(cmd); if (driver_byte(result) & DRIVER_SENSE) @@ -1067,15 +1105,16 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) /* Unprep the request and put it back at the head of the queue. * A new command will be prepared and issued. */ + scsi_release_buffers(cmd); scsi_requeue_command(q, cmd); break; case ACTION_RETRY: /* Retry the same command immediately */ - scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY); + __scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY, 0); break; case ACTION_DELAYED_RETRY: /* Retry the same command after a delay */ - scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); + __scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY, 0); break; } } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 18486b51668d..66505bb79410 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -32,6 +32,7 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/spinlock.h> +#include <linux/async.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -179,6 +180,8 @@ int scsi_complete_async_scans(void) spin_unlock(&async_scan_lock); kfree(data); + /* Synchronize async operations globally */ + async_synchronize_full(); return 0; } @@ -411,8 +414,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, device_initialize(dev); starget->reap_ref = 1; dev->parent = get_device(parent); - sprintf(dev->bus_id, "target%d:%d:%d", - shost->host_no, channel, id); + dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); #ifndef CONFIG_SYSFS_DEPRECATED dev->bus = &scsi_bus_type; #endif @@ -1021,7 +1023,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, if (rescan || !scsi_device_created(sdev)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: device exists on %s\n", - sdev->sdev_gendev.bus_id)); + dev_name(&sdev->sdev_gendev))); if (sdevp) *sdevp = sdev; else @@ -1160,7 +1162,7 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of" - "%s\n", starget->dev.bus_id)); + "%s\n", dev_name(&starget->dev))); max_dev_lun = min(max_scsi_luns, shost->max_lun); /* diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 93c28f30bbd7..da63802cbf9d 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1079,16 +1079,14 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) device_initialize(&sdev->sdev_gendev); sdev->sdev_gendev.bus = &scsi_bus_type; sdev->sdev_gendev.type = &scsi_dev_type; - sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", - sdev->host->host_no, sdev->channel, sdev->id, - sdev->lun); - + dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%d", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); + device_initialize(&sdev->sdev_dev); sdev->sdev_dev.parent = &sdev->sdev_gendev; sdev->sdev_dev.class = &sdev_class; - snprintf(sdev->sdev_dev.bus_id, BUS_ID_SIZE, - "%d:%d:%d:%d", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); + dev_set_name(&sdev->sdev_dev, "%d:%d:%d:%d", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); sdev->scsi_level = starget->scsi_level; transport_setup_device(&sdev->sdev_gendev); spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 062304de4854..5f77417ed585 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -2407,8 +2407,12 @@ fc_rport_final_delete(struct work_struct *work) /* * Notify the driver that the rport is now dead. The LLDD will * also guarantee that any communication to the rport is terminated + * + * Avoid this call if we already called it when we preserved the + * rport for the binding. */ - if (i->f->dev_loss_tmo_callbk) + if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) && + (i->f->dev_loss_tmo_callbk)) i->f->dev_loss_tmo_callbk(rport); transport_remove_device(dev); @@ -2486,8 +2490,8 @@ fc_rport_create(struct Scsi_Host *shost, int channel, device_initialize(dev); /* takes self reference */ dev->parent = get_device(&shost->shost_gendev); /* parent reference */ dev->release = fc_rport_dev_release; - sprintf(dev->bus_id, "rport-%d:%d-%d", - shost->host_no, channel, rport->number); + dev_set_name(dev, "rport-%d:%d-%d", + shost->host_no, channel, rport->number); transport_setup_device(dev); error = device_add(dev); @@ -2647,7 +2651,8 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, spin_lock_irqsave(shost->host_lock, flags); rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT | - FC_RPORT_DEVLOSS_PENDING); + FC_RPORT_DEVLOSS_PENDING | + FC_RPORT_DEVLOSS_CALLBK_DONE); /* if target, initiate a scan */ if (rport->scsi_target_id != -1) { @@ -2944,6 +2949,7 @@ fc_timeout_deleted_rport(struct work_struct *work) struct fc_rport *rport = container_of(work, struct fc_rport, dev_loss_work.work); struct Scsi_Host *shost = rport_to_shost(rport); + struct fc_internal *i = to_fc_internal(shost->transportt); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsigned long flags; @@ -3011,6 +3017,7 @@ fc_timeout_deleted_rport(struct work_struct *work) rport->roles = FC_PORT_ROLE_UNKNOWN; rport->port_state = FC_PORTSTATE_NOTPRESENT; rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT; + rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE; /* * Pre-emptively kill I/O rather than waiting for the work queue @@ -3046,8 +3053,18 @@ fc_timeout_deleted_rport(struct work_struct *work) * all attached scsi devices. */ fc_queue_work(shost, &rport->stgt_delete_work); + + /* + * Notify the driver that the rport is now dead. The LLDD will + * also guarantee that any communication to the rport is terminated + * + * Note: we set the CALLBK_DONE flag above to correspond + */ + if (i->f->dev_loss_tmo_callbk) + i->f->dev_loss_tmo_callbk(rport); } + /** * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a disconnected SCSI target. * @work: rport to terminate io on. @@ -3164,8 +3181,8 @@ fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, device_initialize(dev); /* takes self reference */ dev->parent = get_device(pdev); /* takes parent reference */ dev->release = fc_vport_dev_release; - sprintf(dev->bus_id, "vport-%d:%d-%d", - shost->host_no, channel, vport->number); + dev_set_name(dev, "vport-%d:%d-%d", + shost->host_no, channel, vport->number); transport_setup_device(dev); error = device_add(dev); @@ -3188,19 +3205,19 @@ fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, */ if (pdev != &shost->shost_gendev) { error = sysfs_create_link(&shost->shost_gendev.kobj, - &dev->kobj, dev->bus_id); + &dev->kobj, dev_name(dev)); if (error) printk(KERN_ERR "%s: Cannot create vport symlinks for " "%s, err=%d\n", - __func__, dev->bus_id, error); + __func__, dev_name(dev), error); } spin_lock_irqsave(shost->host_lock, flags); vport->flags &= ~FC_VPORT_CREATING; spin_unlock_irqrestore(shost->host_lock, flags); dev_printk(KERN_NOTICE, pdev, - "%s created via shost%d channel %d\n", dev->bus_id, + "%s created via shost%d channel %d\n", dev_name(dev), shost->host_no, channel); *ret_vport = vport; @@ -3297,7 +3314,7 @@ fc_vport_terminate(struct fc_vport *vport) return stat; if (dev->parent != &shost->shost_gendev) - sysfs_remove_link(&shost->shost_gendev.kobj, dev->bus_id); + sysfs_remove_link(&shost->shost_gendev.kobj, dev_name(dev)); transport_remove_device(dev); device_del(dev); transport_destroy_device(dev); @@ -3329,7 +3346,7 @@ fc_vport_sched_delete(struct work_struct *work) dev_printk(KERN_ERR, vport->dev.parent, "%s: %s could not be deleted created via " "shost%d channel %d - error %d\n", __func__, - vport->dev.bus_id, vport->shost->host_no, + dev_name(&vport->dev), vport->shost->host_no, vport->channel, stat); } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 4a803ebaf508..75c9297694cb 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -187,8 +187,7 @@ iscsi_create_endpoint(int dd_size) ep->id = id; ep->dev.class = &iscsi_endpoint_class; - snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%llu", - (unsigned long long) id); + dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id); err = device_register(&ep->dev); if (err) goto free_ep; @@ -724,8 +723,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) } session->target_id = id; - snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", - session->sid); + dev_set_name(&session->dev, "session%u", session->sid); err = device_add(&session->dev); if (err) { iscsi_cls_session_printk(KERN_ERR, session, @@ -898,8 +896,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) if (!get_device(&session->dev)) goto free_conn; - snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", - session->sid, cid); + dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid); conn->dev.parent = &session->dev; conn->dev.release = iscsi_conn_release; err = device_register(&conn->dev); @@ -1816,7 +1813,7 @@ iscsi_register_transport(struct iscsi_transport *tt) priv->t.create_work_queue = 1; priv->dev.class = &iscsi_transport_class; - snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name); + dev_set_name(&priv->dev, "%s", tt->name); err = device_register(&priv->dev); if (err) goto free_priv; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 366609386be1..50988cbf7b2d 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -207,7 +207,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) struct request_queue *q; int error; struct device *dev; - char namebuf[BUS_ID_SIZE]; + char namebuf[20]; const char *name; void (*release)(struct device *); @@ -219,7 +219,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) if (rphy) { q = blk_init_queue(sas_non_host_smp_request, NULL); dev = &rphy->dev; - name = dev->bus_id; + name = dev_name(dev); release = NULL; } else { q = blk_init_queue(sas_host_smp_request, NULL); @@ -629,10 +629,10 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) INIT_LIST_HEAD(&phy->port_siblings); if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); - sprintf(phy->dev.bus_id, "phy-%d:%d:%d", shost->host_no, + dev_set_name(&phy->dev, "phy-%d:%d:%d", shost->host_no, rphy->scsi_target_id, number); } else - sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); + dev_set_name(&phy->dev, "phy-%d:%d", shost->host_no, number); transport_setup_device(&phy->dev); @@ -770,7 +770,7 @@ static void sas_port_create_link(struct sas_port *port, int res; res = sysfs_create_link(&port->dev.kobj, &phy->dev.kobj, - phy->dev.bus_id); + dev_name(&phy->dev)); if (res) goto err; res = sysfs_create_link(&phy->dev.kobj, &port->dev.kobj, "port"); @@ -785,7 +785,7 @@ err: static void sas_port_delete_link(struct sas_port *port, struct sas_phy *phy) { - sysfs_remove_link(&port->dev.kobj, phy->dev.bus_id); + sysfs_remove_link(&port->dev.kobj, dev_name(&phy->dev)); sysfs_remove_link(&phy->dev.kobj, "port"); } @@ -821,11 +821,11 @@ struct sas_port *sas_port_alloc(struct device *parent, int port_id) if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); - sprintf(port->dev.bus_id, "port-%d:%d:%d", shost->host_no, - rphy->scsi_target_id, port->port_identifier); + dev_set_name(&port->dev, "port-%d:%d:%d", shost->host_no, + rphy->scsi_target_id, port->port_identifier); } else - sprintf(port->dev.bus_id, "port-%d:%d", shost->host_no, - port->port_identifier); + dev_set_name(&port->dev, "port-%d:%d", shost->host_no, + port->port_identifier); transport_setup_device(&port->dev); @@ -935,7 +935,7 @@ void sas_port_delete(struct sas_port *port) if (port->is_backlink) { struct device *parent = port->dev.parent; - sysfs_remove_link(&port->dev.kobj, parent->bus_id); + sysfs_remove_link(&port->dev.kobj, dev_name(parent)); port->is_backlink = 0; } @@ -984,7 +984,8 @@ void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy) /* If this trips, you added a phy that was already * part of a different port */ if (unlikely(tmp != phy)) { - dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", phy->dev.bus_id); + dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", + dev_name(&phy->dev)); BUG(); } } else { @@ -1023,7 +1024,7 @@ void sas_port_mark_backlink(struct sas_port *port) return; port->is_backlink = 1; res = sysfs_create_link(&port->dev.kobj, &parent->kobj, - parent->bus_id); + dev_name(parent)); if (res) goto err; return; @@ -1367,11 +1368,12 @@ struct sas_rphy *sas_end_device_alloc(struct sas_port *parent) rdev->rphy.dev.release = sas_end_device_release; if (scsi_is_sas_expander_device(parent->dev.parent)) { struct sas_rphy *rphy = dev_to_rphy(parent->dev.parent); - sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d:%d", - shost->host_no, rphy->scsi_target_id, parent->port_identifier); + dev_set_name(&rdev->rphy.dev, "end_device-%d:%d:%d", + shost->host_no, rphy->scsi_target_id, + parent->port_identifier); } else - sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d", - shost->host_no, parent->port_identifier); + dev_set_name(&rdev->rphy.dev, "end_device-%d:%d", + shost->host_no, parent->port_identifier); rdev->rphy.identify.device_type = SAS_END_DEVICE; sas_rphy_initialize(&rdev->rphy); transport_setup_device(&rdev->rphy.dev); @@ -1411,8 +1413,8 @@ struct sas_rphy *sas_expander_alloc(struct sas_port *parent, mutex_lock(&sas_host->lock); rdev->rphy.scsi_target_id = sas_host->next_expander_id++; mutex_unlock(&sas_host->lock); - sprintf(rdev->rphy.dev.bus_id, "expander-%d:%d", - shost->host_no, rdev->rphy.scsi_target_id); + dev_set_name(&rdev->rphy.dev, "expander-%d:%d", + shost->host_no, rdev->rphy.scsi_target_id); rdev->rphy.identify.device_type = type; sas_rphy_initialize(&rdev->rphy); transport_setup_device(&rdev->rphy.dev); @@ -1445,7 +1447,7 @@ int sas_rphy_add(struct sas_rphy *rphy) transport_add_device(&rphy->dev); transport_configure_device(&rphy->dev); if (sas_bsg_initialize(shost, rphy)) - printk("fail to a bsg device %s\n", rphy->dev.bus_id); + printk("fail to a bsg device %s\n", dev_name(&rphy->dev)); mutex_lock(&sas_host->lock); diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index 8a7af951d98a..21a045e0559f 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -212,7 +212,7 @@ struct srp_rport *srp_rport_add(struct Scsi_Host *shost, rport->roles = ids->roles; id = atomic_inc_return(&to_srp_host_attrs(shost)->next_port_id); - sprintf(rport->dev.bus_id, "port-%d:%d", shost->host_no, id); + dev_set_name(&rport->dev, "port-%d:%d", shost->host_no, id); transport_setup_device(&rport->dev); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 62b28d58e65e..d57566b8be0a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -48,6 +48,7 @@ #include <linux/delay.h> #include <linux/mutex.h> #include <linux/string_helpers.h> +#include <linux/async.h> #include <asm/uaccess.h> #include <scsi/scsi.h> @@ -1802,6 +1803,71 @@ static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen) return 0; } +/* + * The asynchronous part of sd_probe + */ +static void sd_probe_async(void *data, async_cookie_t cookie) +{ + struct scsi_disk *sdkp = data; + struct scsi_device *sdp; + struct gendisk *gd; + u32 index; + struct device *dev; + + sdp = sdkp->device; + gd = sdkp->disk; + index = sdkp->index; + dev = &sdp->sdev_gendev; + + if (!sdp->request_queue->rq_timeout) { + if (sdp->type != TYPE_MOD) + blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT); + else + blk_queue_rq_timeout(sdp->request_queue, + SD_MOD_TIMEOUT); + } + + device_initialize(&sdkp->dev); + sdkp->dev.parent = &sdp->sdev_gendev; + sdkp->dev.class = &sd_disk_class; + dev_set_name(&sdkp->dev, dev_name(&sdp->sdev_gendev)); + + if (device_add(&sdkp->dev)) + goto out_free_index; + + get_device(&sdp->sdev_gendev); + + if (index < SD_MAX_DISKS) { + gd->major = sd_major((index & 0xf0) >> 4); + gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); + gd->minors = SD_MINORS; + } + gd->fops = &sd_fops; + gd->private_data = &sdkp->driver; + gd->queue = sdkp->device->request_queue; + + sd_revalidate_disk(gd); + + blk_queue_prep_rq(sdp->request_queue, sd_prep_fn); + + gd->driverfs_dev = &sdp->sdev_gendev; + gd->flags = GENHD_FL_EXT_DEVT | GENHD_FL_DRIVERFS; + if (sdp->removable) + gd->flags |= GENHD_FL_REMOVABLE; + + dev_set_drvdata(dev, sdkp); + add_disk(gd); + sd_dif_config_host(sdkp); + + sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", + sdp->removable ? "removable " : ""); + + return; + + out_free_index: + ida_remove(&sd_index_ida, index); +} + /** * sd_probe - called during driver initialization and whenever a * new scsi device is attached to the system. It is called once @@ -1865,48 +1931,7 @@ static int sd_probe(struct device *dev) sdkp->openers = 0; sdkp->previous_state = 1; - if (!sdp->request_queue->rq_timeout) { - if (sdp->type != TYPE_MOD) - blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT); - else - blk_queue_rq_timeout(sdp->request_queue, - SD_MOD_TIMEOUT); - } - - device_initialize(&sdkp->dev); - sdkp->dev.parent = &sdp->sdev_gendev; - sdkp->dev.class = &sd_disk_class; - strncpy(sdkp->dev.bus_id, sdp->sdev_gendev.bus_id, BUS_ID_SIZE); - - if (device_add(&sdkp->dev)) - goto out_free_index; - - get_device(&sdp->sdev_gendev); - - if (index < SD_MAX_DISKS) { - gd->major = sd_major((index & 0xf0) >> 4); - gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); - gd->minors = SD_MINORS; - } - gd->fops = &sd_fops; - gd->private_data = &sdkp->driver; - gd->queue = sdkp->device->request_queue; - - sd_revalidate_disk(gd); - - blk_queue_prep_rq(sdp->request_queue, sd_prep_fn); - - gd->driverfs_dev = &sdp->sdev_gendev; - gd->flags = GENHD_FL_EXT_DEVT | GENHD_FL_DRIVERFS; - if (sdp->removable) - gd->flags |= GENHD_FL_REMOVABLE; - - dev_set_drvdata(dev, sdkp); - add_disk(gd); - sd_dif_config_host(sdkp); - - sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", - sdp->removable ? "removable " : ""); + async_schedule(sd_probe_async, sdkp); return 0; diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c index 3ebb1f289490..184dff492797 100644 --- a/drivers/scsi/sd_dif.c +++ b/drivers/scsi/sd_dif.c @@ -142,7 +142,7 @@ static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix) static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors) { struct sd_dif_tuple *sdt = prot; - char *tag = tag_buf; + u8 *tag = tag_buf; unsigned int i, j; for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { @@ -154,7 +154,7 @@ static void sd_dif_type1_set_tag(void *prot, void *tag_buf, unsigned int sectors static void sd_dif_type1_get_tag(void *prot, void *tag_buf, unsigned int sectors) { struct sd_dif_tuple *sdt = prot; - char *tag = tag_buf; + u8 *tag = tag_buf; unsigned int i, j; for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { @@ -256,7 +256,7 @@ static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix) static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors) { struct sd_dif_tuple *sdt = prot; - char *tag = tag_buf; + u8 *tag = tag_buf; unsigned int i, j; for (i = 0, j = 0 ; i < sectors ; i++, j += 6, sdt++) { @@ -269,7 +269,7 @@ static void sd_dif_type3_set_tag(void *prot, void *tag_buf, unsigned int sectors static void sd_dif_type3_get_tag(void *prot, void *tag_buf, unsigned int sectors) { struct sd_dif_tuple *sdt = prot; - char *tag = tag_buf; + u8 *tag = tag_buf; unsigned int i, j; for (i = 0, j = 0 ; i < sectors ; i++, j += 2, sdt++) { @@ -374,7 +374,10 @@ void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsig else csum_convert = 0; + BUG_ON(dif && (scmd->cmnd[0] == READ_6 || scmd->cmnd[0] == WRITE_6)); + switch (scmd->cmnd[0]) { + case READ_6: case READ_10: case READ_12: case READ_16: @@ -390,6 +393,7 @@ void sd_dif_op(struct scsi_cmnd *scmd, unsigned int dif, unsigned int dix, unsig break; + case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: @@ -475,8 +479,9 @@ int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_s error: kunmap_atomic(sdt, KM_USER0); - sd_printk(KERN_ERR, sdkp, "%s: virt %u, phys %u, ref %u\n", - __func__, virt, phys, be32_to_cpu(sdt->ref_tag)); + sd_printk(KERN_ERR, sdkp, "%s: virt %u, phys %u, ref %u, app %4x\n", + __func__, virt, phys, be32_to_cpu(sdt->ref_tag), + be16_to_cpu(sdt->app_tag)); return -EIO; } diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 7f0df29f3a64..e946e05db7f7 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -526,7 +526,7 @@ static int ses_intf_add(struct device *cdev, if (!scomp) goto err_free; - edev = enclosure_register(cdev->parent, sdev->sdev_gendev.bus_id, + edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev), components, &ses_enclosure_callbacks); if (IS_ERR(edev)) { err = PTR_ERR(edev); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 5103855242ae..8f0bd3f7a59f 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1669,6 +1669,8 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) md->pages = req_schp->pages; md->page_order = req_schp->page_order; md->nr_entries = req_schp->k_use_sg; + md->offset = 0; + md->null_mapped = hp->dxferp ? 0 : 1; } if (iov_count) diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index 31fe6051c799..0807b260268b 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -297,7 +297,7 @@ out: return err; } -static void __exit sgiwd93_remove(struct platform_device *pdev) +static int __exit sgiwd93_remove(struct platform_device *pdev) { struct Scsi_Host *host = platform_get_drvdata(pdev); struct ip22_hostdata *hdata = (struct ip22_hostdata *) host->hostdata; @@ -307,6 +307,7 @@ static void __exit sgiwd93_remove(struct platform_device *pdev) free_irq(pd->irq, host); dma_free_noncoherent(&pdev->dev, HPC_DMA_SIZE, hdata->cpu, hdata->dma); scsi_host_put(host); + return 0; } static struct platform_driver sgiwd93_driver = { diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c index d63d229e2323..6dc8b846c112 100644 --- a/drivers/scsi/sim710.c +++ b/drivers/scsi/sim710.c @@ -102,7 +102,7 @@ sim710_probe_common(struct device *dev, unsigned long base_addr, struct NCR_700_Host_Parameters *hostdata = kzalloc(sizeof(struct NCR_700_Host_Parameters), GFP_KERNEL); - printk(KERN_NOTICE "sim710: %s\n", dev->bus_id); + printk(KERN_NOTICE "sim710: %s\n", dev_name(dev)); printk(KERN_NOTICE "sim710: irq = %d, clock = %d, base = 0x%lx, scsi_id = %d\n", irq, clock, base_addr, scsi_id); @@ -305,7 +305,7 @@ sim710_eisa_probe(struct device *dev) scsi_id = ffs(val) - 1; if(scsi_id > 7 || (val & ~(1<<scsi_id)) != 0) { - printk(KERN_ERR "sim710.c, EISA card %s has incorrect scsi_id, setting to 7\n", dev->bus_id); + printk(KERN_ERR "sim710.c, EISA card %s has incorrect scsi_id, setting to 7\n", dev_name(dev)); scsi_id = 7; } } else { diff --git a/drivers/scsi/sni_53c710.c b/drivers/scsi/sni_53c710.c index 2bbef4c45a0d..77f0b2cdaa94 100644 --- a/drivers/scsi/sni_53c710.c +++ b/drivers/scsi/sni_53c710.c @@ -78,8 +78,7 @@ static int __init snirm710_probe(struct platform_device *dev) base = res->start; hostdata = kzalloc(sizeof(*hostdata), GFP_KERNEL); if (!hostdata) { - printk(KERN_ERR "%s: Failed to allocate host data\n", - dev->dev.bus_id); + dev_printk(KERN_ERR, dev, "Failed to allocate host data\n"); return -ENOMEM; } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 7f3f317ee6ca..c6f19ee8f2cb 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support */ -static const char *verstr = "20080504"; +static const char *verstr = "20081215"; #include <linux/module.h> @@ -182,18 +182,16 @@ static struct scsi_tape **scsi_tapes = NULL; static int modes_defined; -static struct st_buffer *new_tape_buffer(int, int, int); static int enlarge_buffer(struct st_buffer *, int, int); static void clear_buffer(struct st_buffer *); static void normalize_buffer(struct st_buffer *); static int append_to_buffer(const char __user *, struct st_buffer *, int); static int from_buffer(struct st_buffer *, char __user *, int); static void move_buffer_data(struct st_buffer *, int); -static void buf_to_sg(struct st_buffer *, unsigned int); -static int sgl_map_user_pages(struct scatterlist *, const unsigned int, +static int sgl_map_user_pages(struct st_buffer *, const unsigned int, unsigned long, size_t, int); -static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int); +static int sgl_unmap_user_pages(struct st_buffer *, const unsigned int, int); static int st_probe(struct device *); static int st_remove(struct device *); @@ -435,22 +433,6 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) return (-EIO); } - -/* Wakeup from interrupt */ -static void st_sleep_done(void *data, char *sense, int result, int resid) -{ - struct st_request *SRpnt = data; - struct scsi_tape *STp = SRpnt->stp; - - memcpy(SRpnt->sense, sense, SCSI_SENSE_BUFFERSIZE); - (STp->buffer)->cmdstat.midlevel_result = SRpnt->result = result; - (STp->buffer)->cmdstat.residual = resid; - DEB( STp->write_pending = 0; ) - - if (SRpnt->waiting) - complete(SRpnt->waiting); -} - static struct st_request *st_allocate_request(struct scsi_tape *stp) { struct st_request *streq; @@ -475,6 +457,63 @@ static void st_release_request(struct st_request *streq) kfree(streq); } +static void st_scsi_execute_end(struct request *req, int uptodate) +{ + struct st_request *SRpnt = req->end_io_data; + struct scsi_tape *STp = SRpnt->stp; + + STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors; + STp->buffer->cmdstat.residual = req->data_len; + + if (SRpnt->waiting) + complete(SRpnt->waiting); + + blk_rq_unmap_user(SRpnt->bio); + __blk_put_request(req->q, req); +} + +static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd, + int data_direction, void *buffer, unsigned bufflen, + int timeout, int retries) +{ + struct request *req; + struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data; + int err = 0; + int write = (data_direction == DMA_TO_DEVICE); + + req = blk_get_request(SRpnt->stp->device->request_queue, write, + GFP_KERNEL); + if (!req) + return DRIVER_ERROR << 24; + + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_flags |= REQ_QUIET; + + mdata->null_mapped = 1; + + if (bufflen) { + err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen, + GFP_KERNEL); + if (err) { + blk_put_request(req); + return DRIVER_ERROR << 24; + } + } + + SRpnt->bio = req->bio; + req->cmd_len = COMMAND_SIZE(cmd[0]); + memset(req->cmd, 0, BLK_MAX_CDB); + memcpy(req->cmd, cmd, req->cmd_len); + req->sense = SRpnt->sense; + req->sense_len = 0; + req->timeout = timeout; + req->retries = retries; + req->end_io_data = SRpnt; + + blk_execute_rq_nowait(req->q, NULL, req, 1, st_scsi_execute_end); + return 0; +} + /* Do the scsi command. Waits until command performed if do_wait is true. Otherwise write_behind_check() is used to check that the command has finished. */ @@ -483,6 +522,8 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd int bytes, int direction, int timeout, int retries, int do_wait) { struct completion *waiting; + struct rq_map_data *mdata = &STp->buffer->map_data; + int ret; /* if async, make sure there's no command outstanding */ if (!do_wait && ((STp->buffer)->last_SRpnt)) { @@ -510,21 +551,27 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd init_completion(waiting); SRpnt->waiting = waiting; - if (!STp->buffer->do_dio) - buf_to_sg(STp->buffer, bytes); + if (STp->buffer->do_dio) { + mdata->nr_entries = STp->buffer->sg_segs; + mdata->pages = STp->buffer->mapped_pages; + } else { + mdata->nr_entries = + DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order); + STp->buffer->map_data.pages = STp->buffer->reserved_pages; + STp->buffer->map_data.offset = 0; + } memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); STp->buffer->cmdstat.have_sense = 0; STp->buffer->syscall_result = 0; - if (scsi_execute_async(STp->device, cmd, COMMAND_SIZE(cmd[0]), direction, - &((STp->buffer)->sg[0]), bytes, (STp->buffer)->sg_segs, - timeout, retries, SRpnt, st_sleep_done, GFP_KERNEL)) { + ret = st_scsi_execute(SRpnt, cmd, direction, NULL, bytes, timeout, + retries); + if (ret) { /* could not allocate the buffer or request was too large */ (STp->buffer)->syscall_result = (-EBUSY); (STp->buffer)->last_SRpnt = NULL; - } - else if (do_wait) { + } else if (do_wait) { wait_for_completion(waiting); SRpnt->waiting = NULL; (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); @@ -533,28 +580,6 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd return SRpnt; } -static int st_scsi_kern_execute(struct st_request *streq, - const unsigned char *cmd, int data_direction, - void *buffer, unsigned bufflen, int timeout, - int retries) -{ - struct scsi_tape *stp = streq->stp; - int ret, resid; - - stp->buffer->cmdstat.have_sense = 0; - memcpy(streq->cmd, cmd, sizeof(streq->cmd)); - - ret = scsi_execute(stp->device, cmd, data_direction, buffer, bufflen, - streq->sense, timeout, retries, 0, &resid); - if (driver_byte(ret) & DRIVER_ERROR) - return -EBUSY; - - stp->buffer->cmdstat.midlevel_result = streq->result = ret; - stp->buffer->cmdstat.residual = resid; - stp->buffer->syscall_result = st_chk_result(stp, streq); - - return 0; -} /* Handle the write-behind checking (waits for completion). Returns -ENOSPC if write has been correct but EOM early warning reached, -EIO if write ended in @@ -627,7 +652,6 @@ static int cross_eof(struct scsi_tape * STp, int forward) { struct st_request *SRpnt; unsigned char cmd[MAX_COMMAND_SIZE]; - int ret; cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ @@ -641,26 +665,20 @@ static int cross_eof(struct scsi_tape * STp, int forward) DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n", tape_name(STp), forward ? "forward" : "backward")); - SRpnt = st_allocate_request(STp); + SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, + STp->device->request_queue->rq_timeout, + MAX_RETRIES, 1); if (!SRpnt) - return STp->buffer->syscall_result; - - ret = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0, - STp->device->request_queue->rq_timeout, - MAX_RETRIES); - if (ret) - goto out; + return (STp->buffer)->syscall_result; - ret = STp->buffer->syscall_result; + st_release_request(SRpnt); + SRpnt = NULL; if ((STp->buffer)->cmdstat.midlevel_result != 0) printk(KERN_ERR "%s: Stepping over filemark %s failed.\n", tape_name(STp), forward ? "forward" : "backward"); -out: - st_release_request(SRpnt); - - return ret; + return (STp->buffer)->syscall_result; } @@ -881,24 +899,21 @@ static int test_ready(struct scsi_tape *STp, int do_wait) int attentions, waits, max_wait, scode; int retval = CHKRES_READY, new_session = 0; unsigned char cmd[MAX_COMMAND_SIZE]; - struct st_request *SRpnt; + struct st_request *SRpnt = NULL; struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; - SRpnt = st_allocate_request(STp); - if (!SRpnt) - return STp->buffer->syscall_result; - max_wait = do_wait ? ST_BLOCK_SECONDS : 0; for (attentions=waits=0; ; ) { memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = TEST_UNIT_READY; + SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, + STp->long_timeout, MAX_READY_RETRIES, 1); - retval = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0, - STp->long_timeout, - MAX_READY_RETRIES); - if (retval) + if (!SRpnt) { + retval = (STp->buffer)->syscall_result; break; + } if (cmdstatp->have_sense) { @@ -942,8 +957,8 @@ static int test_ready(struct scsi_tape *STp, int do_wait) break; } - st_release_request(SRpnt); - + if (SRpnt != NULL) + st_release_request(SRpnt); return retval; } @@ -1020,24 +1035,17 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) } } - SRpnt = st_allocate_request(STp); - if (!SRpnt) { - retval = STp->buffer->syscall_result; - goto err_out; - } - if (STp->omit_blklims) STp->min_block = STp->max_block = (-1); else { memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = READ_BLOCK_LIMITS; - retval = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE, - STp->buffer->b_data, 6, - STp->device->request_queue->rq_timeout, - MAX_READY_RETRIES); - if (retval) { - st_release_request(SRpnt); + SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE, + STp->device->request_queue->rq_timeout, + MAX_READY_RETRIES, 1); + if (!SRpnt) { + retval = (STp->buffer)->syscall_result; goto err_out; } @@ -1061,12 +1069,11 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) cmd[0] = MODE_SENSE; cmd[4] = 12; - retval = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE, - STp->buffer->b_data, 12, - STp->device->request_queue->rq_timeout, - MAX_READY_RETRIES); - if (retval) { - st_release_request(SRpnt); + SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE, + STp->device->request_queue->rq_timeout, + MAX_READY_RETRIES, 1); + if (!SRpnt) { + retval = (STp->buffer)->syscall_result; goto err_out; } @@ -1296,17 +1303,11 @@ static int st_flush(struct file *filp, fl_owner_t id) cmd[0] = WRITE_FILEMARKS; cmd[4] = 1 + STp->two_fm; - SRpnt = st_allocate_request(STp); + SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, + STp->device->request_queue->rq_timeout, + MAX_WRITE_RETRIES, 1); if (!SRpnt) { - result = STp->buffer->syscall_result; - goto out; - } - - result = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0, - STp->device->request_queue->rq_timeout, - MAX_WRITE_RETRIES); - if (result) { - st_release_request(SRpnt); + result = (STp->buffer)->syscall_result; goto out; } @@ -1471,8 +1472,8 @@ static int setup_buffering(struct scsi_tape *STp, const char __user *buf, if (i && ((unsigned long)buf & queue_dma_alignment( STp->device->request_queue)) == 0) { - i = sgl_map_user_pages(&(STbp->sg[0]), STbp->use_sg, - (unsigned long)buf, count, (is_read ? READ : WRITE)); + i = sgl_map_user_pages(STbp, STbp->use_sg, (unsigned long)buf, + count, (is_read ? READ : WRITE)); if (i > 0) { STbp->do_dio = i; STbp->buffer_bytes = 0; /* can be used as transfer counter */ @@ -1480,7 +1481,6 @@ static int setup_buffering(struct scsi_tape *STp, const char __user *buf, else STbp->do_dio = 0; /* fall back to buffering with any error */ STbp->sg_segs = STbp->do_dio; - STbp->frp_sg_current = 0; DEB( if (STbp->do_dio) { STp->nbr_dio++; @@ -1526,7 +1526,7 @@ static void release_buffering(struct scsi_tape *STp, int is_read) STbp = STp->buffer; if (STbp->do_dio) { - sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, is_read); + sgl_unmap_user_pages(STbp, STbp->do_dio, is_read); STbp->do_dio = 0; STbp->sg_segs = 0; } @@ -2372,7 +2372,6 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs) { unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt; - int ret; memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SENSE; @@ -2381,17 +2380,14 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs) cmd[2] = page; cmd[4] = 255; - SRpnt = st_allocate_request(STp); - if (!SRpnt) - return STp->buffer->syscall_result; + SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_FROM_DEVICE, + STp->device->request_queue->rq_timeout, 0, 1); + if (SRpnt == NULL) + return (STp->buffer)->syscall_result; - ret = st_scsi_kern_execute(SRpnt, cmd, DMA_FROM_DEVICE, - STp->buffer->b_data, cmd[4], - STp->device->request_queue->rq_timeout, - MAX_RETRIES); st_release_request(SRpnt); - return ret ? : STp->buffer->syscall_result; + return STp->buffer->syscall_result; } @@ -2399,9 +2395,10 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs) in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */ static int write_mode_page(struct scsi_tape *STp, int page, int slow) { - int pgo, timeout, ret = 0; + int pgo; unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt; + int timeout; memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SELECT; @@ -2415,21 +2412,16 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow) (STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP; (STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR; - SRpnt = st_allocate_request(STp); - if (!SRpnt) - return ret; - - timeout = slow ? STp->long_timeout : - STp->device->request_queue->rq_timeout; - - ret = st_scsi_kern_execute(SRpnt, cmd, DMA_TO_DEVICE, - STp->buffer->b_data, cmd[4], timeout, 0); - if (!ret) - ret = STp->buffer->syscall_result; + timeout = slow ? + STp->long_timeout : STp->device->request_queue->rq_timeout; + SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_TO_DEVICE, + timeout, 0, 1); + if (SRpnt == NULL) + return (STp->buffer)->syscall_result; st_release_request(SRpnt); - return ret; + return STp->buffer->syscall_result; } @@ -2547,16 +2539,13 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod printk(ST_DEB_MSG "%s: Loading tape.\n", name); ); - SRpnt = st_allocate_request(STp); + SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, + timeout, MAX_RETRIES, 1); if (!SRpnt) - return STp->buffer->syscall_result; - - retval = st_scsi_kern_execute(SRpnt, cmd, DMA_NONE, NULL, 0, timeout, - MAX_RETRIES); - if (retval) - goto out; + return (STp->buffer)->syscall_result; retval = (STp->buffer)->syscall_result; + st_release_request(SRpnt); if (!retval) { /* SCSI command successful */ @@ -2575,8 +2564,6 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod STps = &(STp->ps[STp->partition]); STps->drv_file = STps->drv_block = (-1); } -out: - st_release_request(SRpnt); return retval; } @@ -2852,15 +2839,12 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon return (-ENOSYS); } - SRpnt = st_allocate_request(STp); + SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction, + timeout, MAX_RETRIES, 1); if (!SRpnt) return (STp->buffer)->syscall_result; - ioctl_result = st_scsi_kern_execute(SRpnt, cmd, direction, - STp->buffer->b_data, datalen, - timeout, MAX_RETRIES); - if (!ioctl_result) - ioctl_result = (STp->buffer)->syscall_result; + ioctl_result = (STp->buffer)->syscall_result; if (!ioctl_result) { /* SCSI command successful */ st_release_request(SRpnt); @@ -3022,17 +3006,11 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti if (!logical && !STp->scsi2_logical) scmd[1] = 1; } - - SRpnt = st_allocate_request(STp); + SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE, + STp->device->request_queue->rq_timeout, + MAX_READY_RETRIES, 1); if (!SRpnt) - return STp->buffer->syscall_result; - - result = st_scsi_kern_execute(SRpnt, scmd, DMA_FROM_DEVICE, - STp->buffer->b_data, 20, - STp->device->request_queue->rq_timeout, - MAX_READY_RETRIES); - if (result) - goto out; + return (STp->buffer)->syscall_result; if ((STp->buffer)->syscall_result != 0 || (STp->device->scsi_level >= SCSI_2 && @@ -3060,7 +3038,6 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti DEBC(printk(ST_DEB_MSG "%s: Got tape pos. blk %d part %d.\n", name, *block, *partition)); } -out: st_release_request(SRpnt); SRpnt = NULL; @@ -3135,14 +3112,10 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition timeout = STp->device->request_queue->rq_timeout; } - SRpnt = st_allocate_request(STp); + SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE, + timeout, MAX_READY_RETRIES, 1); if (!SRpnt) - return STp->buffer->syscall_result; - - result = st_scsi_kern_execute(SRpnt, scmd, DMA_NONE, NULL, 0, - timeout, MAX_READY_RETRIES); - if (result) - goto out; + return (STp->buffer)->syscall_result; STps->drv_block = STps->drv_file = (-1); STps->eof = ST_NOEOF; @@ -3167,7 +3140,7 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition STps->drv_block = STps->drv_file = 0; result = 0; } -out: + st_release_request(SRpnt); SRpnt = NULL; @@ -3696,38 +3669,34 @@ static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a /* Try to allocate a new tape buffer. Calling function must not hold dev_arr_lock. */ -static struct st_buffer * - new_tape_buffer(int from_initialization, int need_dma, int max_sg) +static struct st_buffer *new_tape_buffer(int need_dma, int max_sg) { - int i, got = 0; - gfp_t priority; struct st_buffer *tb; - if (from_initialization) - priority = GFP_ATOMIC; - else - priority = GFP_KERNEL; - - i = sizeof(struct st_buffer) + (max_sg - 1) * sizeof(struct scatterlist) + - max_sg * sizeof(struct st_buf_fragment); - tb = kzalloc(i, priority); + tb = kzalloc(sizeof(struct st_buffer), GFP_ATOMIC); if (!tb) { printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n"); return NULL; } - tb->frp_segs = tb->orig_frp_segs = 0; + tb->frp_segs = 0; tb->use_sg = max_sg; - tb->frp = (struct st_buf_fragment *)(&(tb->sg[0]) + max_sg); - tb->dma = need_dma; - tb->buffer_size = got; - sg_init_table(tb->sg, max_sg); + tb->buffer_size = 0; + + tb->reserved_pages = kzalloc(max_sg * sizeof(struct page *), + GFP_ATOMIC); + if (!tb->reserved_pages) { + kfree(tb); + return NULL; + } return tb; } /* Try to allocate enough space in the tape buffer */ +#define ST_MAX_ORDER 6 + static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dma) { int segs, nbr, max_segs, b_size, order, got; @@ -3747,33 +3716,45 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm priority = GFP_KERNEL | __GFP_NOWARN; if (need_dma) priority |= GFP_DMA; - for (b_size = PAGE_SIZE, order=0; order <= 6 && - b_size < new_size - STbuffer->buffer_size; - order++, b_size *= 2) - ; /* empty */ + + if (STbuffer->cleared) + priority |= __GFP_ZERO; + + if (STbuffer->frp_segs) { + order = STbuffer->map_data.page_order; + b_size = PAGE_SIZE << order; + } else { + for (b_size = PAGE_SIZE, order = 0; + order < ST_MAX_ORDER && b_size < new_size; + order++, b_size *= 2) + ; /* empty */ + } + if (max_segs * (PAGE_SIZE << order) < new_size) { + if (order == ST_MAX_ORDER) + return 0; + normalize_buffer(STbuffer); + return enlarge_buffer(STbuffer, new_size, need_dma); + } for (segs = STbuffer->frp_segs, got = STbuffer->buffer_size; segs < max_segs && got < new_size;) { - STbuffer->frp[segs].page = alloc_pages(priority, order); - if (STbuffer->frp[segs].page == NULL) { - if (new_size - got <= (max_segs - segs) * b_size / 2) { - b_size /= 2; /* Large enough for the rest of the buffers */ - order--; - continue; - } + struct page *page; + + page = alloc_pages(priority, order); + if (!page) { DEB(STbuffer->buffer_size = got); normalize_buffer(STbuffer); return 0; } - STbuffer->frp[segs].length = b_size; + STbuffer->frp_segs += 1; got += b_size; STbuffer->buffer_size = got; - if (STbuffer->cleared) - memset(page_address(STbuffer->frp[segs].page), 0, b_size); + STbuffer->reserved_pages[segs] = page; segs++; } - STbuffer->b_data = page_address(STbuffer->frp[0].page); + STbuffer->b_data = page_address(STbuffer->reserved_pages[0]); + STbuffer->map_data.page_order = order; return 1; } @@ -3785,7 +3766,8 @@ static void clear_buffer(struct st_buffer * st_bp) int i; for (i=0; i < st_bp->frp_segs; i++) - memset(page_address(st_bp->frp[i].page), 0, st_bp->frp[i].length); + memset(page_address(st_bp->reserved_pages[i]), 0, + PAGE_SIZE << st_bp->map_data.page_order); st_bp->cleared = 1; } @@ -3793,16 +3775,16 @@ static void clear_buffer(struct st_buffer * st_bp) /* Release the extra buffer */ static void normalize_buffer(struct st_buffer * STbuffer) { - int i, order; + int i, order = STbuffer->map_data.page_order; - for (i = STbuffer->orig_frp_segs; i < STbuffer->frp_segs; i++) { - order = get_order(STbuffer->frp[i].length); - __free_pages(STbuffer->frp[i].page, order); - STbuffer->buffer_size -= STbuffer->frp[i].length; + for (i = 0; i < STbuffer->frp_segs; i++) { + __free_pages(STbuffer->reserved_pages[i], order); + STbuffer->buffer_size -= (PAGE_SIZE << order); } - STbuffer->frp_segs = STbuffer->orig_frp_segs; - STbuffer->frp_sg_current = 0; + STbuffer->frp_segs = 0; STbuffer->sg_segs = 0; + STbuffer->map_data.page_order = 0; + STbuffer->map_data.offset = 0; } @@ -3811,18 +3793,19 @@ static void normalize_buffer(struct st_buffer * STbuffer) static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count) { int i, cnt, res, offset; + int length = PAGE_SIZE << st_bp->map_data.page_order; for (i = 0, offset = st_bp->buffer_bytes; - i < st_bp->frp_segs && offset >= st_bp->frp[i].length; i++) - offset -= st_bp->frp[i].length; + i < st_bp->frp_segs && offset >= length; i++) + offset -= length; if (i == st_bp->frp_segs) { /* Should never happen */ printk(KERN_WARNING "st: append_to_buffer offset overflow.\n"); return (-EIO); } for (; i < st_bp->frp_segs && do_count > 0; i++) { - cnt = st_bp->frp[i].length - offset < do_count ? - st_bp->frp[i].length - offset : do_count; - res = copy_from_user(page_address(st_bp->frp[i].page) + offset, ubp, cnt); + struct page *page = st_bp->reserved_pages[i]; + cnt = length - offset < do_count ? length - offset : do_count; + res = copy_from_user(page_address(page) + offset, ubp, cnt); if (res) return (-EFAULT); do_count -= cnt; @@ -3842,18 +3825,19 @@ static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, in static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count) { int i, cnt, res, offset; + int length = PAGE_SIZE << st_bp->map_data.page_order; for (i = 0, offset = st_bp->read_pointer; - i < st_bp->frp_segs && offset >= st_bp->frp[i].length; i++) - offset -= st_bp->frp[i].length; + i < st_bp->frp_segs && offset >= length; i++) + offset -= length; if (i == st_bp->frp_segs) { /* Should never happen */ printk(KERN_WARNING "st: from_buffer offset overflow.\n"); return (-EIO); } for (; i < st_bp->frp_segs && do_count > 0; i++) { - cnt = st_bp->frp[i].length - offset < do_count ? - st_bp->frp[i].length - offset : do_count; - res = copy_to_user(ubp, page_address(st_bp->frp[i].page) + offset, cnt); + struct page *page = st_bp->reserved_pages[i]; + cnt = length - offset < do_count ? length - offset : do_count; + res = copy_to_user(ubp, page_address(page) + offset, cnt); if (res) return (-EFAULT); do_count -= cnt; @@ -3874,6 +3858,7 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset) { int src_seg, dst_seg, src_offset = 0, dst_offset; int count, total; + int length = PAGE_SIZE << st_bp->map_data.page_order; if (offset == 0) return; @@ -3881,24 +3866,26 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset) total=st_bp->buffer_bytes - offset; for (src_seg=0; src_seg < st_bp->frp_segs; src_seg++) { src_offset = offset; - if (src_offset < st_bp->frp[src_seg].length) + if (src_offset < length) break; - offset -= st_bp->frp[src_seg].length; + offset -= length; } st_bp->buffer_bytes = st_bp->read_pointer = total; for (dst_seg=dst_offset=0; total > 0; ) { - count = min(st_bp->frp[dst_seg].length - dst_offset, - st_bp->frp[src_seg].length - src_offset); - memmove(page_address(st_bp->frp[dst_seg].page) + dst_offset, - page_address(st_bp->frp[src_seg].page) + src_offset, count); + struct page *dpage = st_bp->reserved_pages[dst_seg]; + struct page *spage = st_bp->reserved_pages[src_seg]; + + count = min(length - dst_offset, length - src_offset); + memmove(page_address(dpage) + dst_offset, + page_address(spage) + src_offset, count); src_offset += count; - if (src_offset >= st_bp->frp[src_seg].length) { + if (src_offset >= length) { src_seg++; src_offset = 0; } dst_offset += count; - if (dst_offset >= st_bp->frp[dst_seg].length) { + if (dst_offset >= length) { dst_seg++; dst_offset = 0; } @@ -3906,32 +3893,6 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset) } } - -/* Fill the s/g list up to the length required for this transfer */ -static void buf_to_sg(struct st_buffer *STbp, unsigned int length) -{ - int i; - unsigned int count; - struct scatterlist *sg; - struct st_buf_fragment *frp; - - if (length == STbp->frp_sg_current) - return; /* work already done */ - - sg = &(STbp->sg[0]); - frp = STbp->frp; - for (i=count=0; count < length; i++) { - if (length - count > frp[i].length) - sg_set_page(&sg[i], frp[i].page, frp[i].length, 0); - else - sg_set_page(&sg[i], frp[i].page, length - count, 0); - count += sg[i].length; - } - STbp->sg_segs = i; - STbp->frp_sg_current = length; -} - - /* Validate the options from command line or module parameters */ static void validate_options(void) { @@ -4026,7 +3987,7 @@ static int st_probe(struct device *dev) SDp->request_queue->max_phys_segments); if (st_max_sg_segs < i) i = st_max_sg_segs; - buffer = new_tape_buffer(1, (SDp->host)->unchecked_isa_dma, i); + buffer = new_tape_buffer((SDp->host)->unchecked_isa_dma, i); if (buffer == NULL) { printk(KERN_ERR "st: Can't allocate new tape buffer. Device not attached.\n"); @@ -4280,8 +4241,8 @@ static void scsi_tape_release(struct kref *kref) tpnt->device = NULL; if (tpnt->buffer) { - tpnt->buffer->orig_frp_segs = 0; normalize_buffer(tpnt->buffer); + kfree(tpnt->buffer->reserved_pages); kfree(tpnt->buffer); } @@ -4567,14 +4528,16 @@ out: } /* The following functions may be useful for a larger audience. */ -static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, - unsigned long uaddr, size_t count, int rw) +static int sgl_map_user_pages(struct st_buffer *STbp, + const unsigned int max_pages, unsigned long uaddr, + size_t count, int rw) { unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; unsigned long start = uaddr >> PAGE_SHIFT; const int nr_pages = end - start; int res, i, j; struct page **pages; + struct rq_map_data *mdata = &STbp->map_data; /* User attempted Overflow! */ if ((uaddr + count) < uaddr) @@ -4616,24 +4579,11 @@ static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pa flush_dcache_page(pages[i]); } - /* Populate the scatter/gather list */ - sg_set_page(&sgl[0], pages[0], 0, uaddr & ~PAGE_MASK); - if (nr_pages > 1) { - sgl[0].length = PAGE_SIZE - sgl[0].offset; - count -= sgl[0].length; - for (i=1; i < nr_pages ; i++) { - sg_set_page(&sgl[i], pages[i], - count < PAGE_SIZE ? count : PAGE_SIZE, 0);; - count -= PAGE_SIZE; - } - } - else { - sgl[0].length = count; - } + mdata->offset = uaddr & ~PAGE_MASK; + mdata->page_order = 0; + STbp->mapped_pages = pages; - kfree(pages); return nr_pages; - out_unmap: if (res > 0) { for (j=0; j < res; j++) @@ -4646,13 +4596,13 @@ static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pa /* And unmap them... */ -static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages, - int dirtied) +static int sgl_unmap_user_pages(struct st_buffer *STbp, + const unsigned int nr_pages, int dirtied) { int i; for (i=0; i < nr_pages; i++) { - struct page *page = sg_page(&sgl[i]); + struct page *page = STbp->mapped_pages[i]; if (dirtied) SetPageDirty(page); @@ -4661,6 +4611,8 @@ static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_p */ page_cache_release(page); } + kfree(STbp->mapped_pages); + STbp->mapped_pages = NULL; return 0; } diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index b92712f95931..544dc6b1f548 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -29,6 +29,7 @@ struct st_request { int result; struct scsi_tape *stp; struct completion *waiting; + struct bio *bio; }; /* The tape buffer descriptor. */ @@ -44,20 +45,13 @@ struct st_buffer { int syscall_result; struct st_request *last_SRpnt; struct st_cmdstatus cmdstat; + struct page **reserved_pages; + struct page **mapped_pages; + struct rq_map_data map_data; unsigned char *b_data; unsigned short use_sg; /* zero or max number of s/g segments for this adapter */ unsigned short sg_segs; /* number of segments in s/g list */ - unsigned short orig_frp_segs; /* number of segments allocated at first try */ unsigned short frp_segs; /* number of buffer segments */ - unsigned int frp_sg_current; /* driver buffer length currently in s/g list */ - struct st_buf_fragment *frp; /* the allocated buffer fragment list */ - struct scatterlist sg[1]; /* MUST BE last item */ -}; - -/* The tape buffer fragment descriptor */ -struct st_buf_fragment { - struct page *page; - unsigned int length; }; /* The tape mode definition */ diff --git a/drivers/scsi/zalon.c b/drivers/scsi/zalon.c index 3c4a300494a4..a8d61a62522e 100644 --- a/drivers/scsi/zalon.c +++ b/drivers/scsi/zalon.c @@ -137,8 +137,8 @@ zalon_probe(struct parisc_device *dev) goto fail; if (request_irq(dev->irq, ncr53c8xx_intr, IRQF_SHARED, "zalon", host)) { - printk(KERN_ERR "%s: irq problem with %d, detaching\n ", - dev->dev.bus_id, dev->irq); + dev_printk(KERN_ERR, dev, "irq problem with %d, detaching\n ", + dev->irq); goto fail; } diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index b695ab3142d8..3e525e38a5d9 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -457,7 +457,7 @@ config SERIAL_SAMSUNG config SERIAL_SAMSUNG_UARTS int - depends on SERIAL_SAMSUNG + depends on ARM && PLAT_S3C default 2 if ARCH_S3C2400 default 4 if ARCH_S3C64XX || CPU_S3C2443 default 3 @@ -1320,13 +1320,30 @@ config SERIAL_NETX_CONSOLE config SERIAL_OF_PLATFORM tristate "Serial port on Open Firmware platform bus" depends on PPC_OF - depends on SERIAL_8250 + depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL help If you have a PowerPC based system that has serial ports on a platform specific bus, you should enable this option. Currently, only 8250 compatible ports are supported, but others can easily be added. +config SERIAL_OF_PLATFORM_NWPSERIAL + tristate "NWP serial port driver" + depends on PPC_OF && PPC_DCR + select SERIAL_OF_PLATFORM + select SERIAL_CORE_CONSOLE + select SERIAL_CORE + help + This driver supports the cell network processor nwp serial + device. + +config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE + bool "Console on NWP serial port" + depends on SERIAL_OF_PLATFORM_NWPSERIAL=y + select SERIAL_CORE_CONSOLE + help + Support for Console on the NWP serial ports. + config SERIAL_QE tristate "Freescale QUICC Engine serial port support" depends on QUICC_ENGINE diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index dfe775ac45b2..8844c0a03929 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o +obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c new file mode 100644 index 000000000000..32f3eaf0d262 --- /dev/null +++ b/drivers/serial/nwpserial.c @@ -0,0 +1,475 @@ +/* + * Serial Port driver for a NWP uart device + * + * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/irqreturn.h> +#include <linux/mutex.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/nwpserial.h> +#include <asm/prom.h> +#include <asm/dcr.h> + +#define NWPSERIAL_NR 2 + +#define NWPSERIAL_STATUS_RXVALID 0x1 +#define NWPSERIAL_STATUS_TXFULL 0x2 + +struct nwpserial_port { + struct uart_port port; + dcr_host_t dcr_host; + unsigned int ier; + unsigned int mcr; +}; + +static DEFINE_MUTEX(nwpserial_mutex); +static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; + +static void wait_for_bits(struct nwpserial_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = dcr_read(up->dcr_host, UART_LSR); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & bits) != bits); +} + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static void nwpserial_console_putchar(struct uart_port *port, int c) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void +nwpserial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct nwpserial_port *up = &nwpserial_ports[co->index]; + unsigned long flags; + int locked = 1; + + if (oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); + + /* save and disable interrupt */ + up->ier = dcr_read(up->dcr_host, UART_IER); + dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); + + uart_console_write(&up->port, s, count, nwpserial_console_putchar); + + /* wait for transmitter to become emtpy */ + while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) + cpu_relax(); + + /* restore interrupt state */ + dcr_write(up->dcr_host, UART_IER, up->ier); + + if (locked) + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver nwpserial_reg; +static struct console nwpserial_console = { + .name = "ttySQ", + .write = nwpserial_console_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &nwpserial_reg, +}; +#define NWPSERIAL_CONSOLE (&nwpserial_console) +#else +#define NWPSERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ + +/**************************************************************************/ + +static int nwpserial_request_port(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_release_port(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_NWPSERIAL; +} + +static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) +{ + struct nwpserial_port *up = dev_id; + struct tty_struct *tty = up->port.info->port.tty; + irqreturn_t ret; + unsigned int iir; + unsigned char ch; + + spin_lock(&up->port.lock); + + /* check if the uart was the interrupt source. */ + iir = dcr_read(up->dcr_host, UART_IIR); + if (!iir) { + ret = IRQ_NONE; + goto out; + } + + do { + up->port.icount.rx++; + ch = dcr_read(up->dcr_host, UART_RX); + if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } while (dcr_read(up->dcr_host, UART_RX) & UART_LSR_DR); + + tty_flip_buffer_push(tty); + ret = IRQ_HANDLED; + +out: + spin_unlock(&up->port.lock); + return ret; +} + +static int nwpserial_startup(struct uart_port *port) +{ + struct nwpserial_port *up; + int err; + + up = container_of(port, struct nwpserial_port, port); + + /* disable flow control by default */ + up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; + dcr_write(up->dcr_host, UART_MCR, up->mcr); + + /* register interrupt handler */ + err = request_irq(up->port.irq, nwpserial_interrupt, + IRQF_SHARED, "nwpserial", up); + if (err) + return err; + + /* enable interrupts */ + up->ier = UART_IER_RDI; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* enable receiving */ + up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; + + return 0; +} + +static void nwpserial_shutdown(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + /* disable receiving */ + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* disable interrupts from this port */ + up->ier = 0; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* free irq */ + free_irq(up->port.irq, port); +} + +static int nwpserial_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char *nwpserial_type(struct uart_port *port) +{ + return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; +} + +static void nwpserial_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID + | NWPSERIAL_STATUS_TXFULL; + + up->port.ignore_status_mask = 0; + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* Copy back the old hardware settings */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void nwpserial_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static void nwpserial_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_stop_rx(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* don't forward any more data (like !CREAD) */ + up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; +} + +static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) +{ + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void nwpserial_start_tx(struct uart_port *port) +{ + struct nwpserial_port *up; + struct circ_buf *xmit; + up = container_of(port, struct nwpserial_port, port); + xmit = &up->port.info->xmit; + + if (port->x_char) { + nwpserial_putchar(up, up->port.x_char); + port->x_char = 0; + } + + while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { + nwpserial_putchar(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + } +} + +static unsigned int nwpserial_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* N/A */ +} + +static void nwpserial_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + +static unsigned int nwpserial_tx_empty(struct uart_port *port) +{ + struct nwpserial_port *up; + unsigned long flags; + int ret; + up = container_of(port, struct nwpserial_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + ret = dcr_read(up->dcr_host, UART_LSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +} + +static struct uart_ops nwpserial_pops = { + .tx_empty = nwpserial_tx_empty, + .set_mctrl = nwpserial_set_mctrl, + .get_mctrl = nwpserial_get_mctrl, + .stop_tx = nwpserial_stop_tx, + .start_tx = nwpserial_start_tx, + .stop_rx = nwpserial_stop_rx, + .enable_ms = nwpserial_enable_ms, + .break_ctl = nwpserial_break_ctl, + .startup = nwpserial_startup, + .shutdown = nwpserial_shutdown, + .set_termios = nwpserial_set_termios, + .type = nwpserial_type, + .release_port = nwpserial_release_port, + .request_port = nwpserial_request_port, + .config_port = nwpserial_config_port, + .verify_port = nwpserial_verify_port, +}; + +static struct uart_driver nwpserial_reg = { + .owner = THIS_MODULE, + .driver_name = "nwpserial", + .dev_name = "ttySQ", + .major = TTY_MAJOR, + .minor = 68, + .nr = NWPSERIAL_NR, + .cons = NWPSERIAL_CONSOLE, +}; + +int nwpserial_register_port(struct uart_port *port) +{ + struct nwpserial_port *up = NULL; + int ret = -1; + int i; + static int first = 1; + int dcr_len; + int dcr_base; + struct device_node *dn; + + mutex_lock(&nwpserial_mutex); + + dn = to_of_device(port->dev)->node; + if (dn == NULL) + goto out; + + /* get dcr base. */ + dcr_base = dcr_resource_start(dn, 0); + + /* find matching entry */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.iobase == dcr_base) { + up = &nwpserial_ports[i]; + break; + } + + /* we didn't find a mtching entry, search for a free port */ + if (up == NULL) + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN && + nwpserial_ports[i].port.iobase == 0) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) { + ret = -EBUSY; + goto out; + } + + if (first) + uart_register_driver(&nwpserial_reg); + first = 0; + + up->port.membase = port->membase; + up->port.irq = port->irq; + up->port.uartclk = port->uartclk; + up->port.fifosize = port->fifosize; + up->port.regshift = port->regshift; + up->port.iotype = port->iotype; + up->port.flags = port->flags; + up->port.mapbase = port->mapbase; + up->port.private_data = port->private_data; + + if (port->dev) + up->port.dev = port->dev; + + if (up->port.iobase != dcr_base) { + up->port.ops = &nwpserial_pops; + up->port.fifosize = 16; + + spin_lock_init(&up->port.lock); + + up->port.iobase = dcr_base; + dcr_len = dcr_resource_len(dn, 0); + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); + goto out; + } + } + + ret = uart_add_one_port(&nwpserial_reg, &up->port); + if (ret == 0) + ret = up->port.line; + +out: + mutex_unlock(&nwpserial_mutex); + + return ret; +} +EXPORT_SYMBOL(nwpserial_register_port); + +void nwpserial_unregister_port(int line) +{ + struct nwpserial_port *up = &nwpserial_ports[line]; + mutex_lock(&nwpserial_mutex); + uart_remove_one_port(&nwpserial_reg, &up->port); + + up->port.type = PORT_UNKNOWN; + + mutex_unlock(&nwpserial_mutex); +} +EXPORT_SYMBOL(nwpserial_unregister_port); + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static int __init nwpserial_console_init(void) +{ + struct nwpserial_port *up = NULL; + struct device_node *dn; + const char *name; + int dcr_base; + int dcr_len; + int i; + + /* search for a free port */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) + return -1; + + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) + return -1; + + dn = of_find_node_by_path(name); + if (!dn) + return -1; + + spin_lock_init(&up->port.lock); + up->port.ops = &nwpserial_pops; + up->port.type = PORT_NWPSERIAL; + up->port.fifosize = 16; + + dcr_base = dcr_resource_start(dn, 0); + dcr_len = dcr_resource_len(dn, 0); + up->port.iobase = dcr_base; + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk("Cannot map DCR resources for SERIAL"); + return -1; + } + register_console(&nwpserial_console); + return 0; +} +console_initcall(nwpserial_console_init); +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 8fa0ff561e9f..a821e3a3d664 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -14,6 +14,7 @@ #include <linux/serial_core.h> #include <linux/serial_8250.h> #include <linux/of_platform.h> +#include <linux/nwpserial.h> #include <asm/prom.h> @@ -99,9 +100,16 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, goto out; switch (port_type) { +#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: ret = serial8250_register_port(&port); break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + ret = nwpserial_register_port(&port); + break; +#endif default: /* need to add code for these */ case PORT_UNKNOWN: @@ -129,9 +137,16 @@ static int of_platform_serial_remove(struct of_device *ofdev) { struct of_serial_info *info = ofdev->dev.driver_data; switch (info->type) { +#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: serial8250_unregister_port(info->line); break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + nwpserial_unregister_port(info->line); + break; +#endif default: /* need to add code for these */ break; @@ -148,6 +163,10 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + { .type = "serial", .compatible = "ibm,qpace-nwp-serial", + .data = (void *)PORT_NWPSERIAL, }, +#endif { .type = "serial", .data = (void *)PORT_UNKNOWN, }, { /* end of list */ }, }; diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index 06372b227bb2..36a93b95e3f2 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -557,7 +557,7 @@ unsigned int comedi_buf_munge(comedi_async * async, unsigned int num_bytes) block_size = num_bytes - count; if (block_size < 0) { rt_printk("%s: %s: bug! block_size is negative\n", - __FILE__, __FUNCTION__); + __FILE__, __func__); break; } if ((int)(async->munge_ptr + block_size - diff --git a/drivers/staging/epl/Edrv8139.c b/drivers/staging/epl/Edrv8139.c index 88ab4a4f1023..296354aaa9c7 100644 --- a/drivers/staging/epl/Edrv8139.c +++ b/drivers/staging/epl/Edrv8139.c @@ -391,19 +391,19 @@ tEplKernel EdrvInit(tEdrvInitParam * pEdrvInitParam_p) // register PCI driver iResult = pci_register_driver(&EdrvDriver); if (iResult != 0) { - printk("%s pci_register_driver failed with %d\n", __FUNCTION__, + printk("%s pci_register_driver failed with %d\n", __func__, iResult); Ret = kEplNoResource; goto Exit; } if (EdrvInstance_l.m_pPciDev == NULL) { - printk("%s m_pPciDev=NULL\n", __FUNCTION__); + printk("%s m_pPciDev=NULL\n", __func__); Ret = kEplNoResource; goto Exit; } // read MAC address from controller - printk("%s local MAC = ", __FUNCTION__); + printk("%s local MAC = ", __func__); for (iResult = 0; iResult < 6; iResult++) { pEdrvInitParam_p->m_abMyMacAddr[iResult] = EDRV_REGB_READ((EDRV_REGDW_IDR0 + iResult)); @@ -434,7 +434,7 @@ tEplKernel EdrvShutdown(void) { // unregister PCI driver - printk("%s calling pci_unregister_driver()\n", __FUNCTION__); + printk("%s calling pci_unregister_driver()\n", __func__); pci_unregister_driver(&EdrvDriver); return kEplSuccessful; @@ -621,7 +621,7 @@ tEplKernel EdrvSendTxMsg(tEdrvTxBuffer * pBuffer_p) EDRV_REGDW_READ((EDRV_REGDW_TSD0 + (EdrvInstance_l.m_uiCurTxDesc * sizeof(DWORD)))); - printk("%s InvOp TSD%u = 0x%08lX", __FUNCTION__, + printk("%s InvOp TSD%u = 0x%08lX", __func__, EdrvInstance_l.m_uiCurTxDesc, dwTemp); printk(" Cmd = 0x%02X\n", (WORD) EDRV_REGB_READ(EDRV_REGB_COMMAND)); @@ -646,7 +646,7 @@ tEplKernel EdrvSendTxMsg(tEdrvTxBuffer * pBuffer_p) dwTemp = EDRV_REGDW_READ((EDRV_REGDW_TSAD0 + (EdrvInstance_l.m_uiCurTxDesc * sizeof(DWORD)))); -// printk("%s TSAD%u = 0x%08lX", __FUNCTION__, EdrvInstance_l.m_uiCurTxDesc, dwTemp); +// printk("%s TSAD%u = 0x%08lX", __func__, EdrvInstance_l.m_uiCurTxDesc, dwTemp); // start transmission EDRV_REGDW_WRITE((EDRV_REGDW_TSD0 + @@ -786,7 +786,7 @@ static int TgtEthIsr(int nIrqNum_p, void *ppDevInstData_p, if (EdrvInstance_l.m_pbTxBuf == NULL) { printk("%s Tx buffers currently not allocated\n", - __FUNCTION__); + __func__); goto Exit; } // read transmit status @@ -842,7 +842,7 @@ static int TgtEthIsr(int nIrqNum_p, void *ppDevInstData_p, if (EdrvInstance_l.m_pbRxBuf == NULL) { printk("%s Rx buffers currently not allocated\n", - __FUNCTION__); + __func__); goto Exit; } // read current offset in receive buffer @@ -944,7 +944,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) DWORD dwTemp; if (EdrvInstance_l.m_pPciDev != NULL) { // Edrv is already connected to a PCI device - printk("%s device %s discarded\n", __FUNCTION__, + printk("%s device %s discarded\n", __func__, pci_name(pPciDev)); iResult = -ENODEV; goto Exit; @@ -953,7 +953,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) if (pPciDev->revision >= 0x20) { printk ("%s device %s is an enhanced 8139C+ version, which is not supported\n", - __FUNCTION__, pci_name(pPciDev)); + __func__, pci_name(pPciDev)); iResult = -ENODEV; goto Exit; } @@ -961,7 +961,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) EdrvInstance_l.m_pPciDev = pPciDev; // enable device - printk("%s enable device\n", __FUNCTION__); + printk("%s enable device\n", __func__); iResult = pci_enable_device(pPciDev); if (iResult != 0) { goto Exit; @@ -972,13 +972,13 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) goto Exit; } - printk("%s request regions\n", __FUNCTION__); + printk("%s request regions\n", __func__); iResult = pci_request_regions(pPciDev, DRV_NAME); if (iResult != 0) { goto Exit; } - printk("%s ioremap\n", __FUNCTION__); + printk("%s ioremap\n", __func__); EdrvInstance_l.m_pIoAddr = ioremap(pci_resource_start(pPciDev, 1), pci_resource_len(pPciDev, 1)); @@ -987,11 +987,11 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) goto Exit; } // enable PCI busmaster - printk("%s enable busmaster\n", __FUNCTION__); + printk("%s enable busmaster\n", __func__); pci_set_master(pPciDev); // reset controller - printk("%s reset controller\n", __FUNCTION__); + printk("%s reset controller\n", __func__); EDRV_REGB_WRITE(EDRV_REGB_COMMAND, EDRV_REGB_COMMAND_RST); // wait until reset has finished @@ -1008,20 +1008,20 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) dwTemp = EDRV_REGDW_READ(EDRV_REGDW_TCR); if (((dwTemp & EDRV_REGDW_TCR_VER_MASK) != EDRV_REGDW_TCR_VER_C) && ((dwTemp & EDRV_REGDW_TCR_VER_MASK) != EDRV_REGDW_TCR_VER_D)) { // unsupported chip - printk("%s Unsupported chip! TCR = 0x%08lX\n", __FUNCTION__, + printk("%s Unsupported chip! TCR = 0x%08lX\n", __func__, dwTemp); iResult = -ENODEV; goto Exit; } // disable interrupts - printk("%s disable interrupts\n", __FUNCTION__); + printk("%s disable interrupts\n", __func__); EDRV_REGW_WRITE(EDRV_REGW_INT_MASK, 0); // acknowledge all pending interrupts EDRV_REGW_WRITE(EDRV_REGW_INT_STATUS, EDRV_REGW_READ(EDRV_REGW_INT_STATUS)); // install interrupt handler - printk("%s install interrupt handler\n", __FUNCTION__); + printk("%s install interrupt handler\n", __func__); iResult = request_irq(pPciDev->irq, TgtEthIsr, IRQF_SHARED, DRV_NAME /*pPciDev->dev.name */ , pPciDev); @@ -1031,16 +1031,16 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) /* // unlock configuration registers - printk("%s unlock configuration registers\n", __FUNCTION__); + printk("%s unlock configuration registers\n", __func__); EDRV_REGB_WRITE(EDRV_REGB_CMD9346, EDRV_REGB_CMD9346_UNLOCK); // check if user specified a MAC address - printk("%s check specified MAC address\n", __FUNCTION__); + printk("%s check specified MAC address\n", __func__); for (iResult = 0; iResult < 6; iResult++) { if (EdrvInstance_l.m_InitParam.m_abMyMacAddr[iResult] != 0) { - printk("%s set local MAC address\n", __FUNCTION__); + printk("%s set local MAC address\n", __func__); // write this MAC address to controller EDRV_REGDW_WRITE(EDRV_REGDW_IDR0, le32_to_cpu(*((DWORD*)&EdrvInstance_l.m_InitParam.m_abMyMacAddr[0]))); @@ -1059,7 +1059,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) */ // allocate buffers - printk("%s allocate buffers\n", __FUNCTION__); + printk("%s allocate buffers\n", __func__); EdrvInstance_l.m_pbTxBuf = pci_alloc_consistent(pPciDev, EDRV_TX_BUFFER_SIZE, &EdrvInstance_l.m_pTxBufDma); @@ -1076,7 +1076,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) goto Exit; } // reset pointers for Tx buffers - printk("%s reset pointers fo Tx buffers\n", __FUNCTION__); + printk("%s reset pointers fo Tx buffers\n", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_TSAD0, 0); dwTemp = EDRV_REGDW_READ(EDRV_REGDW_TSAD0); EDRV_REGDW_WRITE(EDRV_REGDW_TSAD1, 0); @@ -1090,11 +1090,11 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) (WORD) EDRV_REGB_READ(EDRV_REGB_COMMAND)); // set pointer for receive buffer in controller - printk("%s set pointer to Rx buffer\n", __FUNCTION__); + printk("%s set pointer to Rx buffer\n", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_RBSTART, EdrvInstance_l.m_pRxBufDma); // enable transmitter and receiver - printk("%s enable Tx and Rx", __FUNCTION__); + printk("%s enable Tx and Rx", __func__); EDRV_REGB_WRITE(EDRV_REGB_COMMAND, (EDRV_REGB_COMMAND_RE | EDRV_REGB_COMMAND_TE)); printk(" Command = 0x%02X\n", @@ -1104,12 +1104,12 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) EDRV_REGDW_WRITE(EDRV_REGDW_MPC, 0); // set transmit configuration register - printk("%s set Tx conf register", __FUNCTION__); + printk("%s set Tx conf register", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_TCR, EDRV_REGDW_TCR_DEF); printk(" = 0x%08X\n", EDRV_REGDW_READ(EDRV_REGDW_TCR)); // set receive configuration register - printk("%s set Rx conf register", __FUNCTION__); + printk("%s set Rx conf register", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_RCR, EDRV_REGDW_RCR_DEF); printk(" = 0x%08X\n", EDRV_REGDW_READ(EDRV_REGDW_RCR)); @@ -1121,7 +1121,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) /* // enable transmitter and receiver - printk("%s enable Tx and Rx", __FUNCTION__); + printk("%s enable Tx and Rx", __func__); EDRV_REGB_WRITE(EDRV_REGB_COMMAND, (EDRV_REGB_COMMAND_RE | EDRV_REGB_COMMAND_TE)); printk(" Command = 0x%02X\n", (WORD) EDRV_REGB_READ(EDRV_REGB_COMMAND)); */ @@ -1129,11 +1129,11 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) EDRV_REGW_WRITE(EDRV_REGW_MULINT, 0); // enable interrupts - printk("%s enable interrupts\n", __FUNCTION__); + printk("%s enable interrupts\n", __func__); EDRV_REGW_WRITE(EDRV_REGW_INT_MASK, EDRV_REGW_INT_MASK_DEF); Exit: - printk("%s finished with %d\n", __FUNCTION__, iResult); + printk("%s finished with %d\n", __func__, iResult); return iResult; } diff --git a/drivers/staging/epl/EplSdoAsySequ.c b/drivers/staging/epl/EplSdoAsySequ.c index 991c6be880c0..6b6a9975d78b 100644 --- a/drivers/staging/epl/EplSdoAsySequ.c +++ b/drivers/staging/epl/EplSdoAsySequ.c @@ -876,7 +876,7 @@ static tEplKernel EplSdoAsySeqProcess(unsigned int uiHandle_p, { /* PRINTF3("%s scon=%u rcon=%u\n", - __FUNCTION__, + __func__, pRecFrame_p->m_le_bSendSeqNumCon, pRecFrame_p->m_le_bRecSeqNumCon); */ diff --git a/drivers/staging/frontier/alphatrack.c b/drivers/staging/frontier/alphatrack.c index 61d7c5df87af..6136e3f8762d 100644 --- a/drivers/staging/frontier/alphatrack.c +++ b/drivers/staging/frontier/alphatrack.c @@ -239,7 +239,7 @@ static void usb_alphatrack_interrupt_in_callback(struct urb *urb) goto exit; } else { dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); goto resubmit; /* maybe we can recover */ } } @@ -261,7 +261,7 @@ static void usb_alphatrack_interrupt_in_callback(struct urb *urb) if(dev->offline > 0 && dev->interrupt_in_buffer[1] != 0xff) { dev->offline = 0; } if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; } #endif - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); next_ring_head = (dev->ring_head+1) % ring_buffer_size; if (next_ring_head != dev->ring_tail) { @@ -305,7 +305,7 @@ static void usb_alphatrack_interrupt_out_callback(struct urb *urb) urb->status == -ESHUTDOWN)) dbg_info(&dev->intf->dev, "%s - nonzero write interrupt status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); atomic_dec(&dev->writes_pending); dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); @@ -330,7 +330,7 @@ static int usb_alphatrack_open(struct inode *inode, struct file *file) if (!interface) { err("%s - error, can't find device for minor %d\n", - __FUNCTION__, subminor); + __func__, subminor); retval = -ENODEV; goto unlock_disconnect_exit; } @@ -514,7 +514,7 @@ static ssize_t usb_alphatrack_read(struct file *file, char __user *buffer, size_ } dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; c+=INPUT_CMD_SIZE; - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); } retval = c; @@ -573,7 +573,7 @@ static ssize_t usb_alphatrack_write(struct file *file, const char __user *buffer if (bytes_to_write < count) dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); - dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write); + dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; diff --git a/drivers/staging/frontier/tranzport.c b/drivers/staging/frontier/tranzport.c index 275faa762388..79abb6b16f74 100644 --- a/drivers/staging/frontier/tranzport.c +++ b/drivers/staging/frontier/tranzport.c @@ -335,7 +335,7 @@ static void usb_tranzport_interrupt_in_callback(struct urb *urb) goto exit; } else { dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); goto resubmit; /* maybe we can recover */ } } @@ -345,7 +345,7 @@ static void usb_tranzport_interrupt_in_callback(struct urb *urb) "Urb length was %d bytes!! Do something intelligent \n", urb->actual_length); } else { dbg_info(&dev->intf->dev, "%s: received: %02x%02x%02x%02x%02x%02x%02x%02x\n", - __FUNCTION__, dev->interrupt_in_buffer[0],dev->interrupt_in_buffer[1],dev->interrupt_in_buffer[2],dev->interrupt_in_buffer[3],dev->interrupt_in_buffer[4],dev->interrupt_in_buffer[5],dev->interrupt_in_buffer[6],dev->interrupt_in_buffer[7]); + __func__, dev->interrupt_in_buffer[0],dev->interrupt_in_buffer[1],dev->interrupt_in_buffer[2],dev->interrupt_in_buffer[3],dev->interrupt_in_buffer[4],dev->interrupt_in_buffer[5],dev->interrupt_in_buffer[6],dev->interrupt_in_buffer[7]); #if SUPPRESS_EXTRA_OFFLINE_EVENTS if(dev->offline == 2 && dev->interrupt_in_buffer[1] == 0xff) { goto resubmit; } if(dev->offline == 1 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 2; goto resubmit; } @@ -355,7 +355,7 @@ static void usb_tranzport_interrupt_in_callback(struct urb *urb) if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; } #endif - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); next_ring_head = (dev->ring_head+1) % ring_buffer_size; @@ -399,7 +399,7 @@ static void usb_tranzport_interrupt_out_callback(struct urb *urb) urb->status == -ESHUTDOWN)) dbg_info(&dev->intf->dev, "%s - nonzero write interrupt status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); @@ -424,7 +424,7 @@ static int usb_tranzport_open(struct inode *inode, struct file *file) if (!interface) { err("%s - error, can't find device for minor %d\n", - __FUNCTION__, subminor); + __func__, subminor); retval = -ENODEV; goto unlock_disconnect_exit; } @@ -613,7 +613,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t } dbg_info(&dev->intf->dev, "%s: copying to userspace: %02x%02x%02x%02x%02x%02x%02x%02x\n", - __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); + __func__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); #if BUFFERED_READS c = 0; @@ -632,7 +632,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t // FIXME the math is wrong for going in reverse, actually, as the midi spec doesn't allow signed chars dbg_info(&dev->intf->dev, "%s: trying to compress: %02x%02x%02x%02x%02x %02x %02x %02x\n", - __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); + __func__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); if(((*dev->ring_buffer)[dev->ring_tail].cmd[6] != 0 && @@ -645,7 +645,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t ((*dev->ring_buffer)[dev->ring_tail].cmd[5] == (*dev->ring_buffer)[next_tail].cmd[5])) { dbg_info(&dev->intf->dev, "%s: should compress: %02x%02x%02x%02x%02x%02x%02x%02x\n", - __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); + __func__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); newwheel += oldwheel; if(oldwheel > 0 && !(newwheel > 0)) { @@ -673,7 +673,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; c+=8; - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); } retval = c; @@ -684,7 +684,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t } dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); retval = 8; #endif /* BUFFERED_READS */ @@ -743,7 +743,7 @@ static ssize_t usb_tranzport_write(struct file *file, const char __user *buffer, if (bytes_to_write < count) dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); - dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write); + dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c index 4f7237a03ad1..868edb65e7bf 100644 --- a/drivers/staging/go7007/go7007-v4l2.c +++ b/drivers/staging/go7007/go7007-v4l2.c @@ -1712,8 +1712,7 @@ static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) page = alloc_page(GFP_USER | __GFP_DMA32); if (!page) return VM_FAULT_OOM; - clear_user_page(page_address(page), (unsigned long)vmf->virtual_address, - page); + clear_user_highpage(page, (unsigned long)vmf->virtual_address); vmf->page = page; return 0; } diff --git a/drivers/staging/meilhaus/me0600_ext_irq.c b/drivers/staging/meilhaus/me0600_ext_irq.c index a449ab200940..eba18adecb72 100644 --- a/drivers/staging/meilhaus/me0600_ext_irq.c +++ b/drivers/staging/meilhaus/me0600_ext_irq.c @@ -360,7 +360,7 @@ static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs) if (instance->lintno > 1) { PERROR_CRITICAL ("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n", - __FUNCTION__, instance->lintno, inl(instance->intcsr)); + __func__, instance->lintno, inl(instance->intcsr)); return IRQ_NONE; } @@ -384,7 +384,7 @@ static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs) } else { PINFO ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, status); + jiffies, __func__, status); ret = IRQ_NONE; } spin_unlock(instance->intcsr_lock); diff --git a/drivers/staging/meilhaus/me1400_ext_irq.c b/drivers/staging/meilhaus/me1400_ext_irq.c index b8c2696bc150..b4df7cc58ab7 100644 --- a/drivers/staging/meilhaus/me1400_ext_irq.c +++ b/drivers/staging/meilhaus/me1400_ext_irq.c @@ -349,7 +349,7 @@ static irqreturn_t me1400_ext_irq_isr(int irq, void *dev_id, (PLX_LOCAL_INT1_STATE | PLX_LOCAL_INT1_EN | PLX_PCI_INT_EN)) { spin_unlock(&instance->subdevice_lock); PINFO("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, status); + jiffies, __func__, status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me1600_ao.c b/drivers/staging/meilhaus/me1600_ao.c index 6f26665b30b7..d127c6b00307 100644 --- a/drivers/staging/meilhaus/me1600_ao.c +++ b/drivers/staging/meilhaus/me1600_ao.c @@ -977,7 +977,7 @@ static void me1600_ao_work_control_task(struct work_struct *work) container_of((void *)work, me1600_ao_subdevice_t, ao_control_task); #endif - PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies, + PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, instance->ao_idx); if (!((instance->ao_regs_shadows)->trigger & instance->ao_idx)) { // Output was triggerd. @@ -1027,7 +1027,7 @@ static void me1600_ao_work_control_task(struct work_struct *work) queue_delayed_work(instance->me1600_workqueue, &instance->ao_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me4600_ai.c b/drivers/staging/meilhaus/me4600_ai.c index 1a0de5dea277..0a8c9d737e90 100644 --- a/drivers/staging/meilhaus/me4600_ai.c +++ b/drivers/staging/meilhaus/me4600_ai.c @@ -2629,11 +2629,11 @@ static irqreturn_t me4600_ai_isr(int irq, void *dev_id, struct pt_regs *regs) if ((irq_status & (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC | ME4600_IRQ_STATUS_BIT_LE)) == ME4600_IRQ_STATUS_BIT_LE) { //This is security check case. LE is unused. This should never ever happend. PINFO ("%ld Shared interrupt. %s(): irq_status_reg=LE_IRQ\n", - jiffies, __FUNCTION__); + jiffies, __func__); } else { PINFO ("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, irq_status); + jiffies, __func__, irq_status); } #endif return IRQ_NONE; @@ -3329,7 +3329,7 @@ static void me4600_ai_work_control_task(struct work_struct *work) instance = container_of((void *)work, me4600_ai_subdevice_t, ai_control_task); #endif - PINFO("<%s: %ld> executed.\n", __FUNCTION__, jiffies); + PINFO("<%s: %ld> executed.\n", __func__, jiffies); status = inl(instance->status_reg); PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, @@ -3428,7 +3428,7 @@ static void me4600_ai_work_control_task(struct work_struct *work) queue_delayed_work(instance->me4600_workqueue, &instance->ai_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me4600_ao.c b/drivers/staging/meilhaus/me4600_ao.c index 2c92e655a81e..e2bec8229abd 100644 --- a/drivers/staging/meilhaus/me4600_ao.c +++ b/drivers/staging/meilhaus/me4600_ao.c @@ -2294,7 +2294,7 @@ static irqreturn_t me4600_ao_isr(int irq, void *dev_id irq_status = inl(instance->irq_status_reg); if (!(irq_status & (ME4600_IRQ_STATUS_BIT_AO_HF << instance->ao_idx))) { PINFO("%ld Shared interrupt. %s(): ID=%d: status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->ao_idx, irq_status); + jiffies, __func__, instance->ao_idx, irq_status); return IRQ_NONE; } @@ -3009,7 +3009,7 @@ static void me4600_ao_work_control_task( instance = container_of((void *)work, me4600_ao_subdevice_t, ao_control_task); #endif - PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies, + PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, instance->ao_idx); status = inl(instance->status_reg); @@ -3316,7 +3316,7 @@ static void me4600_ao_work_control_task( queue_delayed_work(instance->me4600_workqueue, &instance->ao_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me4600_ext_irq.c b/drivers/staging/meilhaus/me4600_ext_irq.c index 8a10dceae32a..adc1e1babf40 100644 --- a/drivers/staging/meilhaus/me4600_ext_irq.c +++ b/drivers/staging/meilhaus/me4600_ext_irq.c @@ -356,7 +356,7 @@ static irqreturn_t me4600_ext_irq_isr(int irq, void *dev_id, irq_status = inl(instance->irq_status_reg); if (!(irq_status & ME4600_IRQ_STATUS_BIT_EX)) { PINFO("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, irq_status); + jiffies, __func__, irq_status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me6000_ao.c b/drivers/staging/meilhaus/me6000_ao.c index 3f5ff6d1b991..94f01231f79a 100644 --- a/drivers/staging/meilhaus/me6000_ao.c +++ b/drivers/staging/meilhaus/me6000_ao.c @@ -863,7 +863,7 @@ static int me6000_ao_io_single_write(me_subdevice_t * subdevice, /// @note When flag 'ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS' is set than output is triggered. ALWAYS! - PINFO("<%s> start mode= 0x%08x %s\n", __FUNCTION__, mode, + PINFO("<%s> start mode= 0x%08x %s\n", __func__, mode, (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) ? "SYNCHRONOUS" : ""); if (instance->fifo & ME6000_AO_HAS_FIFO) { // FIFO - Continous mode @@ -1663,7 +1663,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, status = inl(instance->status_reg); //Start state machine and interrupts - PINFO("<%s:%d> Start state machine.\n", __FUNCTION__, __LINE__); + PINFO("<%s:%d> Start state machine.\n", __func__, __LINE__); ctrl &= ~(ME6000_AO_CTRL_BIT_STOP | ME6000_AO_CTRL_BIT_IMMEDIATE_STOP); if (instance->start_mode == ME6000_AO_EXT_TRIG) { PDEBUG("DIGITAL TRIGGER\n"); @@ -1671,7 +1671,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, } if (!(status & ME6000_AO_STATUS_BIT_HF)) { //More than half! if ((ctrl & ME6000_AO_CTRL_MODE_MASK) == ME6000_AO_MODE_CONTINUOUS) { //Enable IRQ only when hardware_continous is set and FIFO is more than half - PINFO("<%s:%d> Start interrupts.\n", __FUNCTION__, + PINFO("<%s:%d> Start interrupts.\n", __func__, __LINE__); ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ; } @@ -1682,7 +1682,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); //Trigger output - PINFO("<%s> start mode= 0x%x %s\n", __FUNCTION__, instance->start_mode, + PINFO("<%s> start mode= 0x%x %s\n", __func__, instance->start_mode, (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) ? "SYNCHRONOUS" : ""); if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { //Trigger outputs @@ -1777,7 +1777,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, status = inl(instance->status_reg); if (!(status & ME6000_AO_STATUS_BIT_HF)) { //More than half! spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); - PINFO("<%s:%d> Start interrupts.\n", __FUNCTION__, + PINFO("<%s:%d> Start interrupts.\n", __func__, __LINE__); ctrl = inl(instance->ctrl_reg); ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ; @@ -1819,7 +1819,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); PINFO("<%s:%d> Start interrupts.\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); ctrl = inl(instance->ctrl_reg); ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ; outl(ctrl, instance->ctrl_reg); @@ -2346,7 +2346,7 @@ static irqreturn_t me6000_ao_isr(int irq, void *dev_id, struct pt_regs *regs) irq_status = inl(instance->irq_status_reg); if (!(irq_status & (ME6000_IRQ_STATUS_BIT_AO_HF << instance->ao_idx))) { PINFO("%ld Shared interrupt. %s(): ID=%d: status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->ao_idx, irq_status); + jiffies, __func__, instance->ao_idx, irq_status); return IRQ_NONE; } @@ -2861,7 +2861,7 @@ int inline ao_stop_immediately(me6000_ao_subdevice_t * instance) } } - PINFO("<%s> Wait for stop: %d\n", __FUNCTION__, i); + PINFO("<%s> Wait for stop: %d\n", __func__, i); //Still working! set_current_state(TASK_INTERRUPTIBLE); @@ -3132,7 +3132,7 @@ static void me6000_ao_work_control_task( instance = container_of((void *)work, me6000_ao_subdevice_t, ao_control_task); #endif - PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies, + PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, instance->ao_idx); status = inl(instance->status_reg); @@ -3550,7 +3550,7 @@ static void me6000_ao_work_control_task( queue_delayed_work(instance->me6000_workqueue, &instance->ao_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me8100_di.c b/drivers/staging/meilhaus/me8100_di.c index 0f146371b9a0..971727c9e305 100644 --- a/drivers/staging/meilhaus/me8100_di.c +++ b/drivers/staging/meilhaus/me8100_di.c @@ -536,7 +536,7 @@ static irqreturn_t me8100_isr(int irq, void *dev_id, struct pt_regs *regs) PLX_INTCSR_LOCAL_INT1_EN)) { PINFO ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, icsr); + jiffies, __func__, icsr); return IRQ_NONE; } } else if (instance->di_idx == 1) { @@ -547,11 +547,11 @@ static irqreturn_t me8100_isr(int irq, void *dev_id, struct pt_regs *regs) PLX_INTCSR_LOCAL_INT2_EN)) { PINFO ("%ld Shared interrupt. %s(): idx=1 plx:irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, icsr); + jiffies, __func__, icsr); return IRQ_NONE; } } else { - PERROR("%s():Wrong interrupt idx=%d csr=0x%X.\n", __FUNCTION__, + PERROR("%s():Wrong interrupt idx=%d csr=0x%X.\n", __func__, instance->di_idx, icsr); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me8200_di.c b/drivers/staging/meilhaus/me8200_di.c index 0bb4567091cc..27525bc067b6 100644 --- a/drivers/staging/meilhaus/me8200_di.c +++ b/drivers/staging/meilhaus/me8200_di.c @@ -570,7 +570,7 @@ static irqreturn_t me8200_isr(int irq, void *dev_id, struct pt_regs *regs) if (!irq_status) { PINFO ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->di_idx, irq_status); + jiffies, __func__, instance->di_idx, irq_status); return IRQ_NONE; } @@ -658,7 +658,7 @@ static irqreturn_t me8200_isr_EX(int irq, void *dev_id, struct pt_regs *regs) if (!irq_status) { PINFO ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->di_idx, irq_status); + jiffies, __func__, instance->di_idx, irq_status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me8200_do.c b/drivers/staging/meilhaus/me8200_do.c index 5f4ba5dfa860..d2bebd16ff41 100644 --- a/drivers/staging/meilhaus/me8200_do.c +++ b/drivers/staging/meilhaus/me8200_do.c @@ -475,7 +475,7 @@ static irqreturn_t me8200_do_isr(int irq, void *dev_id, struct pt_regs *regs) (ME8200_DO_IRQ_STATUS_BIT_ACTIVE << instance->do_idx))) { PINFO ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->do_idx, irq_status); + jiffies, __func__, instance->do_idx, irq_status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/medebug.h b/drivers/staging/meilhaus/medebug.h index 382d00fe311d..dcfb97c26fd1 100644 --- a/drivers/staging/meilhaus/medebug.h +++ b/drivers/staging/meilhaus/medebug.h @@ -66,21 +66,21 @@ #ifdef MEDEBUG_DEBUG # define PDEBUG(fmt, args...) \ - printk(KERN_DEBUG"ME_DRV D: <%s> " fmt, __FUNCTION__, ##args) + printk(KERN_DEBUG"ME_DRV D: <%s> " fmt, __func__, ##args) #else # define PDEBUG(fmt, args...) #endif #ifdef MEDEBUG_DEBUG_LOCKS # define PDEBUG_LOCKS(fmt, args...) \ - printk(KERN_DEBUG"ME_DRV L: <%s> " fmt, __FUNCTION__, ##args) + printk(KERN_DEBUG"ME_DRV L: <%s> " fmt, __func__, ##args) #else # define PDEBUG_LOCKS(fmt, args...) #endif #ifdef MEDEBUG_DEBUG_REG # define PDEBUG_REG(fmt, args...) \ - printk(KERN_DEBUG"ME_DRV R: <%s:%d> REG:" fmt, __FUNCTION__, __LINE__, ##args) + printk(KERN_DEBUG"ME_DRV R: <%s:%d> REG:" fmt, __func__, __LINE__, ##args) #else # define PDEBUG_REG(fmt, args...) #endif @@ -108,7 +108,7 @@ //This debug is only to detect logical errors! # define PSECURITY(fmt, args...) \ - printk(KERN_CRIT"ME_DRV SECURITY: <%s:%s:%i> " fmt, __FILE__, __FUNCTION__, __LINE__, ##args) + printk(KERN_CRIT"ME_DRV SECURITY: <%s:%s:%i> " fmt, __FILE__, __func__, __LINE__, ##args) //This debug is to keep track in customers' system # define PLOG(fmt, args...) \ printk(KERN_INFO"ME_DRV: " fmt, ##args) @@ -116,7 +116,7 @@ //This debug is to check new parts during development #ifdef MEDEBUG_DEVELOP # define PDEVELOP(fmt, args...) \ - printk(KERN_CRIT"ME_DRV: <%s:%s:%i> " fmt, __FILE__, __FUNCTION__, __LINE__, ##args) + printk(KERN_CRIT"ME_DRV: <%s:%s:%i> " fmt, __FILE__, __func__, __LINE__, ##args) #else # define PDEVELOP(fmt, args...) #endif diff --git a/drivers/staging/otus/80211core/cagg.c b/drivers/staging/otus/80211core/cagg.c index fcfd01a6b36c..4942190747a1 100644 --- a/drivers/staging/otus/80211core/cagg.c +++ b/drivers/staging/otus/80211core/cagg.c @@ -1933,7 +1933,7 @@ u16_t zfAggRx(zdev_t* dev, zbuf_t* buf, struct zsAdditionInfo *addInfo, struct a */ /* zm_msg2_agg(ZM_LV_0, "queue seq = ", seq_no); - * DbgPrint("%s:%s%lxh %s%lxh\n", __FUNCTION__, "queue seq=", seq_no, + * DbgPrint("%s:%s%lxh %s%lxh\n", __func__, "queue seq=", seq_no, * "; seq_start=", tid_rx->seq_start); */ diff --git a/drivers/staging/otus/80211core/pub_zfw.h b/drivers/staging/otus/80211core/pub_zfw.h index 01a227216726..2474bb7536e8 100644 --- a/drivers/staging/otus/80211core/pub_zfw.h +++ b/drivers/staging/otus/80211core/pub_zfw.h @@ -23,7 +23,7 @@ /* Buffer management */ #ifdef ZM_ENABLE_BUFFER_DEBUG extern zbuf_t* zfwBufAllocateWithContext(zdev_t* dev, u16_t len, u8_t *functionName, ULONG line); -#define zfwBufAllocate(dev, len) zfwBufAllocateWithContext(dev, len, (u8_t *)__FUNCTION__, __LINE__) +#define zfwBufAllocate(dev, len) zfwBufAllocateWithContext(dev, len, (u8_t *)__func__, __LINE__) #else extern zbuf_t* zfwBufAllocate(zdev_t* dev, u16_t len); #endif diff --git a/drivers/staging/otus/80211core/struct.h b/drivers/staging/otus/80211core/struct.h index 43631c630a8f..17b5ce37ebb5 100644 --- a/drivers/staging/otus/80211core/struct.h +++ b/drivers/staging/otus/80211core/struct.h @@ -137,7 +137,7 @@ extern const u8_t zg11gRateTbl[8]; #ifdef ZM_ENABLE_BUFFER_TRACE extern void zfwBufTrace(zdev_t* dev, zbuf_t *buf, u8_t *functionName); -#define ZM_BUFFER_TRACE(dev, buf) zfwBufTrace(dev, buf, __FUNCTION__); +#define ZM_BUFFER_TRACE(dev, buf) zfwBufTrace(dev, buf, __func__); #else #define ZM_BUFFER_TRACE(dev, buf) #endif diff --git a/drivers/staging/otus/oal_marc.h b/drivers/staging/otus/oal_marc.h index 438e4bc2e9a6..206111616a03 100644 --- a/drivers/staging/otus/oal_marc.h +++ b/drivers/staging/otus/oal_marc.h @@ -106,14 +106,14 @@ /***** Debug message *****/ #if 0 -#define zm_debug_msg0(msg) printk("%s:%s\n", __FUNCTION__, msg); -#define zm_debug_msg1(msg, val) printk("%s:%s%ld\n", __FUNCTION__, \ +#define zm_debug_msg0(msg) printk("%s:%s\n", __func__, msg); +#define zm_debug_msg1(msg, val) printk("%s:%s%ld\n", __func__, \ msg, (u32_t)val); -#define zm_debug_msg2(msg, val) printk("%s:%s%lxh\n", __FUNCTION__, \ +#define zm_debug_msg2(msg, val) printk("%s:%s%lxh\n", __func__, \ msg, (u32_t)val); -#define zm_debug_msg_s(msg, val) printk("%s:%s%s\n", __FUNCTION__, \ +#define zm_debug_msg_s(msg, val) printk("%s:%s%s\n", __func__, \ msg, val); -#define zm_debug_msg_p(msg, val1, val2) printk("%s:%s%01ld.%02ld\n", __FUNCTION__, \ +#define zm_debug_msg_p(msg, val1, val2) printk("%s:%s%01ld.%02ld\n", __func__, \ msg, (val1/val2), (((val1*100)/val2)%100)); #define zm_dbg(S) printk S #else @@ -127,7 +127,7 @@ #define zm_assert(expr) if(!(expr)) { \ printk( "Atheors Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ + #expr,__FILE__,__func__,__LINE__); \ } #define DbgPrint printk diff --git a/drivers/staging/rt2860/2860_main_dev.c b/drivers/staging/rt2860/2860_main_dev.c index 1e38f2d1f692..08ca81f43cc8 100644 --- a/drivers/staging/rt2860/2860_main_dev.c +++ b/drivers/staging/rt2860/2860_main_dev.c @@ -1202,7 +1202,7 @@ VOID RT28xx_UpdateBeaconToAsic( UCHAR bcn_idx = 0; { - DBGPRINT(RT_DEBUG_ERROR, ("%s() : No valid Interface be found.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s() : No valid Interface be found.\n", __func__)); return; } diff --git a/drivers/staging/rt2860/common/ba_action.c b/drivers/staging/rt2860/common/ba_action.c index 8247aeb73a23..591d1e2158da 100644 --- a/drivers/staging/rt2860/common/ba_action.c +++ b/drivers/staging/rt2860/common/ba_action.c @@ -599,7 +599,7 @@ VOID BAOriSessionAdd( pBAEntry->ORIBATimer.TimerValue = 0; //pFrame->TimeOutValue; - DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __FUNCTION__, pEntry->TXBAbitmap, + DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __func__, pEntry->TXBAbitmap, pBAEntry->BAWinSize, pBAEntry->ORIBATimer.TimerValue)); // SEND BAR ; @@ -673,7 +673,7 @@ BOOLEAN BARecSessionAdd( ba_refresh_reordering_mpdus(pAd, pBAEntry); } - DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __FUNCTION__, pAd->BATable.numAsRecipient, Idx, + DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __func__, pAd->BATable.numAsRecipient, Idx, pFrame->BaParm.BufSize, BAWinSize)); // Start fill in parameters. @@ -915,7 +915,7 @@ VOID BAOriSessionTearDown( return; } - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BAOriEntry[Idx]; DBGPRINT(RT_DEBUG_TRACE,("\t===>Idx = %ld, Wcid=%d.TID=%d, ORI_BA_Status = %d \n", Idx, Wcid, TID, pBAEntry->ORI_BA_Status)); @@ -974,7 +974,7 @@ VOID BARecSessionTearDown( if (Idx == 0) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BARecEntry[Idx]; @@ -1185,7 +1185,7 @@ VOID PeerAddBAReqAction( PULONG ptemp; PMAC_TABLE_ENTRY pMacEntry; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __func__, Elem->Wcid)); //hex_dump("AddBAReq", Elem->Msg, Elem->MsgLen); @@ -1269,7 +1269,7 @@ VOID PeerAddBAReqAction( MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); MlmeFreeMemory(pAd, pOutBuffer); - DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __FUNCTION__, Elem->Wcid, ADDframe.BaParm.TID, + DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __func__, Elem->Wcid, ADDframe.BaParm.TID, ADDframe.BaParm.BufSize)); } @@ -1288,7 +1288,7 @@ VOID PeerAddBARspAction( if (Elem->Wcid >= MAX_LEN_OF_MAC_TABLE) return; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __func__, Elem->Wcid)); //hex_dump("PeerAddBARspAction()", Elem->Msg, Elem->MsgLen); @@ -1329,7 +1329,7 @@ VOID PeerDelBAAction( //PUCHAR pOutBuffer = NULL; PFRAME_DELBA_REQ pDelFrame = NULL; - DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __func__)); //DELBA Request from unknown peer, ignore this. if (PeerDelBAActionSanity(pAd, Elem->Wcid, Elem->Msg, Elem->MsgLen)) { @@ -1366,7 +1366,7 @@ BOOLEAN CntlEnqueueForRecv( TID = (UCHAR)pFrame->BARControl.TID; - DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __func__, Wcid, TID)); //hex_dump("BAR", (PCHAR) pFrame, MsgLen); // Do nothing if the driver is starting halt state. // This might happen when timer already been fired before cancel timer with mlmehalt diff --git a/drivers/staging/rt2860/common/cmm_data.c b/drivers/staging/rt2860/common/cmm_data.c index ac549011ccb9..b67b9eba7229 100644 --- a/drivers/staging/rt2860/common/cmm_data.c +++ b/drivers/staging/rt2860/common/cmm_data.c @@ -2787,7 +2787,7 @@ BOOLEAN MacTableDeleteEntry( } else { - printk("\n%s: Impossible Wcid = %d !!!!!\n", __FUNCTION__, wcid); + printk("\n%s: Impossible Wcid = %d !!!!!\n", __func__, wcid); } } diff --git a/drivers/staging/rt2860/common/cmm_data_2860.c b/drivers/staging/rt2860/common/cmm_data_2860.c index 4f414edd658c..419e50c3fc4c 100644 --- a/drivers/staging/rt2860/common/cmm_data_2860.c +++ b/drivers/staging/rt2860/common/cmm_data_2860.c @@ -1002,7 +1002,7 @@ VOID RT28xxPciStaAsicSleepThenAutoWakeup( AutoWakeupCfg.field.AutoLeadTime = 5; RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word); AsicSendCommandToMcu(pAd, 0x30, 0xff, 0xff, 0x00); // send POWER-SAVE command to MCU. Timeout 40us. - DBGPRINT(RT_DEBUG_TRACE, ("<-- %s, TbttNumToNextWakeUp=%d \n", __FUNCTION__, TbttNumToNextWakeUp)); + DBGPRINT(RT_DEBUG_TRACE, ("<-- %s, TbttNumToNextWakeUp=%d \n", __func__, TbttNumToNextWakeUp)); } OPSTATUS_SET_FLAG(pAd, fOP_STATUS_DOZE); } @@ -1115,7 +1115,7 @@ VOID RT28xxPciMlmeRadioOn( if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF)) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __func__)); if ((pAd->OpMode == OPMODE_AP) || ((pAd->OpMode == OPMODE_STA) && (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)))) @@ -1165,7 +1165,7 @@ VOID RT28xxPciMlmeRadioOFF( if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF)) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __func__)); // Set LED RTMPSetLED(pAd, LED_RADIO_OFF); diff --git a/drivers/staging/rt2860/common/cmm_info.c b/drivers/staging/rt2860/common/cmm_info.c index 0aadf8af633a..dd92ac6eaed2 100644 --- a/drivers/staging/rt2860/common/cmm_info.c +++ b/drivers/staging/rt2860/common/cmm_info.c @@ -2039,7 +2039,7 @@ VOID RTMPIoctlGetMacTable( wrq->u.data.length = sizeof(RT_802_11_MAC_TABLE); if (copy_to_user(wrq->u.data.pointer, &MacTab, wrq->u.data.length)) { - DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __func__)); } msg = (CHAR *) kmalloc(sizeof(CHAR)*(MAX_LEN_OF_MAC_TABLE*MAC_LINE_LEN), MEM_ALLOC_FLAG); diff --git a/drivers/staging/rt2860/common/dfs.c b/drivers/staging/rt2860/common/dfs.c index 23cf1510e2d2..b09bba5cb206 100644 --- a/drivers/staging/rt2860/common/dfs.c +++ b/drivers/staging/rt2860/common/dfs.c @@ -428,7 +428,7 @@ INT Set_ChMovingTime_Proc( pAd->CommonCfg.RadarDetect.ChMovingTime = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.ChMovingTime)); return TRUE; @@ -444,7 +444,7 @@ INT Set_LongPulseRadarTh_Proc( pAd->CommonCfg.RadarDetect.LongPulseRadarTh = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.LongPulseRadarTh)); return TRUE; diff --git a/drivers/staging/rt2860/common/rtmp_init.c b/drivers/staging/rt2860/common/rtmp_init.c index 84edfa52dec3..563f2c5bb856 100644 --- a/drivers/staging/rt2860/common/rtmp_init.c +++ b/drivers/staging/rt2860/common/rtmp_init.c @@ -2542,7 +2542,7 @@ NDIS_STATUS NICLoadFirmware( #ifdef BIN_IN_FILE #define NICLF_DEFAULT_USE() \ flg_default_firm_use = TRUE; \ - printk("%s - Use default firmware!\n", __FUNCTION__); + printk("%s - Use default firmware!\n", __func__); NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PUCHAR src; @@ -2557,7 +2557,7 @@ NDIS_STATUS NICLoadFirmware( BOOLEAN flg_default_firm_use = FALSE; - DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __func__)); /* init */ pFirmwareImage = NULL; @@ -2580,7 +2580,7 @@ NDIS_STATUS NICLoadFirmware( if (pFirmwareImage == NULL) { /* allocate fail, use default firmware array in firmware.h */ - printk("%s - Allocate memory fail!\n", __FUNCTION__); + printk("%s - Allocate memory fail!\n", __func__); NICLF_DEFAULT_USE(); } else @@ -2601,7 +2601,7 @@ NDIS_STATUS NICLoadFirmware( if (IS_ERR(srcf)) { printk("%s - Error %ld opening %s\n", - __FUNCTION__, -PTR_ERR(srcf), src); + __func__, -PTR_ERR(srcf), src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2609,7 +2609,7 @@ NDIS_STATUS NICLoadFirmware( /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - printk("%s - %s does not have a write method\n", __FUNCTION__, src); + printk("%s - %s does not have a write method\n", __func__, src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2623,7 +2623,7 @@ NDIS_STATUS NICLoadFirmware( if (FileLength != MAX_FIRMWARE_IMAGE_SIZE) { printk("%s: error file length (=%d) in RT2860AP.BIN\n", - __FUNCTION__, FileLength); + __func__, FileLength); NICLF_DEFAULT_USE(); break; } @@ -2646,7 +2646,7 @@ NDIS_STATUS NICLoadFirmware( /* CRC fail */ printk("%s: CRC = 0x%02x 0x%02x " "error, should be 0x%02x 0x%02x\n", - __FUNCTION__, + __func__, pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-2], pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-1], (UCHAR)(crc>>8), (UCHAR)(crc)); @@ -2665,7 +2665,7 @@ NDIS_STATUS NICLoadFirmware( ((FIRMWARE_MAJOR_VERSION << 8) + FIRMWARE_MINOR_VERSION)) { - printk("%s: firmware version too old!\n", __FUNCTION__); + printk("%s: firmware version too old!\n", __func__); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2770,7 +2770,7 @@ NDIS_STATUS NICLoadFirmware( } /* End of if */ DBGPRINT(RT_DEBUG_TRACE, - ("<=== %s (status=%d)\n", __FUNCTION__, Status)); + ("<=== %s (status=%d)\n", __func__, Status)); return Status; } /* End of NICLoadFirmware */ diff --git a/drivers/staging/rt2860/common/spectrum.c b/drivers/staging/rt2860/common/spectrum.c index 85e636a607f7..0265a6d1df1a 100644 --- a/drivers/staging/rt2860/common/spectrum.c +++ b/drivers/staging/rt2860/common/spectrum.c @@ -49,7 +49,7 @@ VOID MeasureReqTabInit( if (pAd->CommonCfg.pMeasureReqTab) NdisZeroMemory(pAd->CommonCfg.pMeasureReqTab, sizeof(MEASURE_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __func__)); return; } @@ -77,7 +77,7 @@ static PMEASURE_REQ_ENTRY MeasureReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -114,7 +114,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -175,7 +175,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -210,7 +210,7 @@ static VOID MeasureReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return; } @@ -267,7 +267,7 @@ VOID TpcReqTabInit( if (pAd->CommonCfg.pTpcReqTab) NdisZeroMemory(pAd->CommonCfg.pTpcReqTab, sizeof(TPC_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __func__)); return; } @@ -295,7 +295,7 @@ static PTPC_REQ_ENTRY TpcReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -333,7 +333,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -394,7 +394,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -429,7 +429,7 @@ static VOID TpcReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return; } @@ -782,7 +782,7 @@ VOID EnqueueMeasurementReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -844,7 +844,7 @@ VOID EnqueueMeasurementRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -898,7 +898,7 @@ VOID EnqueueTPCReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -950,7 +950,7 @@ VOID EnqueueTPCRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1003,7 +1003,7 @@ VOID EnqueueChSwAnn( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1596,7 +1596,7 @@ static VOID PeerMeasureReportAction( if ((pMeasureReportInfo = kmalloc(sizeof(MEASURE_RPI_REPORT), GFP_ATOMIC)) == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __FUNCTION__, sizeof(MEASURE_RPI_REPORT))); + DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __func__, sizeof(MEASURE_RPI_REPORT))); return; } @@ -1705,7 +1705,7 @@ static VOID PeerTpcRepAction( { TpcReqDelete(pAd, pEntry->DialogToken); DBGPRINT(RT_DEBUG_TRACE, ("%s: DialogToken=%x, TxPwr=%d, LinkMargin=%d\n", - __FUNCTION__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); + __func__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); } } @@ -1821,7 +1821,7 @@ INT Set_MeasureReq_Proc( MeasureReqType = simple_strtol(thisChar, 0, 16); if (MeasureReqType > 3) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __FUNCTION__, MeasureReqType)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __func__, MeasureReqType)); return TRUE; } break; @@ -1833,10 +1833,10 @@ INT Set_MeasureReq_Proc( ArgIdx++; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __FUNCTION__, Aid, MeasureReqType, MeasureCh)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __func__, Aid, MeasureReqType, MeasureCh)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } @@ -1861,10 +1861,10 @@ INT Set_TpcReq_Proc( Aid = simple_strtol(arg, 0, 16); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __func__, Aid)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } diff --git a/drivers/staging/rt2860/rt_ate.c b/drivers/staging/rt2860/rt_ate.c index 2f07db565c30..f3316ec0b156 100644 --- a/drivers/staging/rt2860/rt_ate.c +++ b/drivers/staging/rt2860/rt_ate.c @@ -291,7 +291,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); } else// 5.5 GHz { @@ -318,7 +318,7 @@ static INT ATETxPwrHandler( R = (ULONG) TxPower; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __FUNCTION__, TxPower, R)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __func__, TxPower, R)); } if (pAd->ate.Channel <= 14) @@ -431,7 +431,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); if (pAd->ate.Channel <= 14) { @@ -2098,7 +2098,7 @@ INT Set_ATE_Load_E2P_Proc( UINT32 FileLength = 0; UINT32 value = simple_strtol(arg, 0, 10); - ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __FUNCTION__, value)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __func__, value)); if (value > 0) { @@ -2122,14 +2122,14 @@ INT Set_ATE_Load_E2P_Proc( if (IS_ERR(srcf)) { - ate_print("%s - Error %ld opening %s\n", __FUNCTION__, -PTR_ERR(srcf), src); + ate_print("%s - Error %ld opening %s\n", __func__, -PTR_ERR(srcf), src); break; } /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - ate_print("%s - %s does not have a read method\n", __FUNCTION__, src); + ate_print("%s - %s does not have a read method\n", __func__, src); break; } @@ -2142,7 +2142,7 @@ INT Set_ATE_Load_E2P_Proc( if (FileLength != EEPROM_SIZE) { ate_print("%s: error file length (=%d) in e2p.bin\n", - __FUNCTION__, FileLength); + __func__, FileLength); break; } else @@ -2174,7 +2174,7 @@ INT Set_ATE_Load_E2P_Proc( current->fsuid = orgfsuid; current->fsgid = orgfsgid; } - ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __FUNCTION__, ret)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __func__, ret)); return ret; @@ -2187,12 +2187,12 @@ INT Set_ATE_Load_E2P_Proc( USHORT WriteEEPROM[(EEPROM_SIZE/2)]; struct iwreq *wrq = (struct iwreq *)arg; - ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __FUNCTION__, wrq->u.data.length)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __func__, wrq->u.data.length)); if (wrq->u.data.length != EEPROM_SIZE) { ate_print("%s: error length (=%d) from host\n", - __FUNCTION__, wrq->u.data.length); + __func__, wrq->u.data.length); return FALSE; } else/* (wrq->u.data.length == EEPROM_SIZE) */ @@ -2211,7 +2211,7 @@ INT Set_ATE_Load_E2P_Proc( } while(FALSE); } - ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __FUNCTION__)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __func__)); return TRUE; @@ -3353,7 +3353,7 @@ static INT ATESetUpFrame( if (pPacket == NULL) { pAd->ate.TxCount = 0; - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s fail to alloc packet space.\n", __FUNCTION__)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s fail to alloc packet space.\n", __func__)); return -1; } pTxRing->Cell[TxIdx].pNextNdisPacket = pPacket; @@ -3646,7 +3646,7 @@ VOID RtmpDoAte( Command_Id = ntohs(pRaCfg->command_id); - ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __FUNCTION__, Command_Id)); + ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __func__, Command_Id)); switch (Command_Id) { @@ -5690,7 +5690,7 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.TxAntennaSel = 2; break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R1 */ @@ -5728,13 +5728,13 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.RxAntennaSel = 3; break; default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R3 */ default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } diff --git a/drivers/staging/rt2860/rt_linux.c b/drivers/staging/rt2860/rt_linux.c index 374c174c88ee..f14500931efb 100644 --- a/drivers/staging/rt2860/rt_linux.c +++ b/drivers/staging/rt2860/rt_linux.c @@ -406,7 +406,7 @@ NDIS_STATUS RTMPAllocateNdisPacket( skb_put(GET_OS_PKT_TYPE(pPacket), HeaderLen+DataLen); RTMP_SET_PACKET_SOURCE(pPacket, PKTSRC_NDIS); -// printk("%s : pPacket = %p, len = %d\n", __FUNCTION__, pPacket, GET_OS_PKT_LEN(pPacket)); +// printk("%s : pPacket = %p, len = %d\n", __func__, pPacket, GET_OS_PKT_LEN(pPacket)); *ppPacket = pPacket; return NDIS_STATUS_SUCCESS; } @@ -773,13 +773,13 @@ VOID RTMPSendWirelessEvent( if (event_table_len == 0) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __FUNCTION__, type)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __func__, type)); return; } if (event >= event_table_len) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __FUNCTION__, event)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __func__, event)); return; } @@ -817,14 +817,14 @@ VOID RTMPSendWirelessEvent( //send wireless event wireless_send_event(pAd->net_dev, IWEVCUSTOM, &wrqu, pBuf); - //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __FUNCTION__, pBuf)); + //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __func__, pBuf)); kfree(pBuf); } else - DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __func__)); #else - DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __func__)); #endif /* WIRELESS_EXT >= 15 */ } @@ -848,13 +848,13 @@ void send_monitor_packets( ASSERT(pRxBlk->pRxPacket); if (pRxBlk->DataSize < 10) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __FUNCTION__, pRxBlk->DataSize)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __func__, pRxBlk->DataSize)); goto err_free_sk_buff; } if (pRxBlk->DataSize + sizeof(wlan_ng_prism2_header) > RX_BUFFER_AGGRESIZE) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __FUNCTION__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __func__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); goto err_free_sk_buff; } @@ -910,7 +910,7 @@ void send_monitor_packets( if (skb_headroom(pOSPkt) < (sizeof(wlan_ng_prism2_header)+ header_len)) { if (pskb_expand_head(pOSPkt, (sizeof(wlan_ng_prism2_header) + header_len), 0, GFP_ATOMIC)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __func__)); goto err_free_sk_buff; } //end if } //end if diff --git a/drivers/staging/rt2860/rt_linux.h b/drivers/staging/rt2860/rt_linux.h index 0cc7cf23d762..0fd58f5109f2 100644 --- a/drivers/staging/rt2860/rt_linux.h +++ b/drivers/staging/rt2860/rt_linux.h @@ -131,7 +131,7 @@ typedef int (*HARD_START_XMIT_FUNC)(struct sk_buff *skb, struct net_device *net_ #define RT_MOD_INC_USE_COUNT() \ if (!try_module_get(THIS_MODULE)) \ { \ - DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __FUNCTION__)); \ + DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __func__)); \ return -1; \ } diff --git a/drivers/staging/rt2860/rt_profile.c b/drivers/staging/rt2860/rt_profile.c index cd7ffc8a6e8a..326a3cb52b9c 100644 --- a/drivers/staging/rt2860/rt_profile.c +++ b/drivers/staging/rt2860/rt_profile.c @@ -1024,7 +1024,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->MlmeAux.SsidLen = pAd->CommonCfg.SsidLen; NdisZeroMemory(pAd->MlmeAux.Ssid, NDIS_802_11_LENGTH_SSID); NdisMoveMemory(pAd->MlmeAux.Ssid, tmpbuf, pAd->MlmeAux.SsidLen); - DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __func__, tmpbuf)); } } } @@ -1043,7 +1043,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.BssType = BSS_INFRA; // Reset Ralink supplicant to not use, it will be set to start when UI set PMK key pAd->StaCfg.WpaState = SS_NOTUSE; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __FUNCTION__, pAd->StaCfg.BssType)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __func__, pAd->StaCfg.BssType)); } } #endif // CONFIG_STA_SUPPORT // @@ -1337,7 +1337,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.PortSecured = WPA_802_1X_PORT_NOT_SECURED; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1363,7 +1363,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.OrigWepStatus = pAd->StaCfg.WepStatus; pAd->StaCfg.bMixCipher = FALSE; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1400,7 +1400,7 @@ NDIS_STATUS RTMPReadParametersHook( else { err = 1; - DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __func__)); } if (err == 0) @@ -1416,7 +1416,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.WpaState = SS_NOTUSE; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __func__, tmpbuf)); } } } diff --git a/drivers/staging/rt2860/rtmp_def.h b/drivers/staging/rt2860/rtmp_def.h index bb6f37b0bdc2..be982147883f 100644 --- a/drivers/staging/rt2860/rtmp_def.h +++ b/drivers/staging/rt2860/rtmp_def.h @@ -333,7 +333,7 @@ /* sanity check for apidx */ #define MBSS_MR_APIDX_SANITY_CHECK(apidx) \ { if (apidx > MAX_MBSSID_NUM) { \ - printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __FUNCTION__, apidx); \ + printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __func__, apidx); \ apidx = MAIN_MBSSID; } } #define VALID_WCID(_wcid) ((_wcid) > 0 && (_wcid) < MAX_LEN_OF_MAC_TABLE ) diff --git a/drivers/staging/rt2860/sta_ioctl.c b/drivers/staging/rt2860/sta_ioctl.c index 9347d11b586e..3ea2b2c4ab0e 100644 --- a/drivers/staging/rt2860/sta_ioctl.c +++ b/drivers/staging/rt2860/sta_ioctl.c @@ -2200,7 +2200,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, } break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2219,7 +2219,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2228,7 +2228,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2257,7 +2257,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2290,7 +2290,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2321,7 +2321,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2341,7 +2341,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2370,12 +2370,12 @@ int rt_ioctl_siwauth(struct net_device *dev, { STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; case IW_AUTH_PRIVACY_INVOKED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2384,7 +2384,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2397,10 +2397,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2508,7 +2508,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2520,15 +2520,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2546,7 +2546,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, NdisMoveMemory(pAdapter->SharedKey[BSS0][keyIdx].Key, ext->key, ext->key_len); break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rt2870/2870_main_dev.c b/drivers/staging/rt2870/2870_main_dev.c index 91da4e2298ac..04c764d95d77 100644 --- a/drivers/staging/rt2870/2870_main_dev.c +++ b/drivers/staging/rt2870/2870_main_dev.c @@ -263,7 +263,7 @@ INT MlmeThread( * This is important in preemption kernels, which transfer the flow * of execution immediately upon a complete(). */ - DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__func__)); pObj->MLMEThr_pid = THREAD_PID_INIT_VALUE; @@ -465,7 +465,7 @@ INT TimerQThread( * This is important in preemption kernels, which transfer the flow * of execution immediately upon a complete(). */ - DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__func__)); pObj->TimerQThr_pid = THREAD_PID_INIT_VALUE; @@ -1258,7 +1258,7 @@ BOOLEAN RT28XXProbePostConfig( if (!(pAd->BulkInEpAddr && pAd->BulkOutEpAddr[0])) { - printk("%s: Could not find both bulk-in and bulk-out endpoints\n", __FUNCTION__); + printk("%s: Could not find both bulk-in and bulk-out endpoints\n", __func__); return FALSE; } diff --git a/drivers/staging/rt2870/common/2870_rtmp_init.c b/drivers/staging/rt2870/common/2870_rtmp_init.c index 5d89a310d1ea..9f5143b3922e 100644 --- a/drivers/staging/rt2870/common/2870_rtmp_init.c +++ b/drivers/staging/rt2870/common/2870_rtmp_init.c @@ -1087,7 +1087,7 @@ PNDIS_PACKET GetPacketFromRxRing( if (pRxWI->MPDUtotalByteCount > ThisFrameLen) { DBGPRINT(RT_DEBUG_ERROR, ("%s():pRxWIMPDUtotalByteCount(%d) large than RxDMALen(%ld)\n", - __FUNCTION__, pRxWI->MPDUtotalByteCount, ThisFrameLen)); + __func__, pRxWI->MPDUtotalByteCount, ThisFrameLen)); goto label_null; } #ifdef RT_BIG_ENDIAN @@ -1098,7 +1098,7 @@ PNDIS_PACKET GetPacketFromRxRing( pSkb = dev_alloc_skb(ThisFrameLen); if (pSkb == NULL) { - DBGPRINT(RT_DEBUG_ERROR,("%s():Cannot Allocate sk buffer for this Bulk-In buffer!\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR,("%s():Cannot Allocate sk buffer for this Bulk-In buffer!\n", __func__)); goto label_null; } diff --git a/drivers/staging/rt2870/common/ba_action.c b/drivers/staging/rt2870/common/ba_action.c index 95addb20bced..d9f738177e2b 100644 --- a/drivers/staging/rt2870/common/ba_action.c +++ b/drivers/staging/rt2870/common/ba_action.c @@ -595,7 +595,7 @@ VOID BAOriSessionAdd( pBAEntry->ORIBATimer.TimerValue = 0; //pFrame->TimeOutValue; - DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __FUNCTION__, pEntry->TXBAbitmap, + DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __func__, pEntry->TXBAbitmap, pBAEntry->BAWinSize, pBAEntry->ORIBATimer.TimerValue)); // SEND BAR ; @@ -669,7 +669,7 @@ BOOLEAN BARecSessionAdd( ba_refresh_reordering_mpdus(pAd, pBAEntry); } - DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __FUNCTION__, pAd->BATable.numAsRecipient, Idx, + DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __func__, pAd->BATable.numAsRecipient, Idx, pFrame->BaParm.BufSize, BAWinSize)); // Start fill in parameters. @@ -911,7 +911,7 @@ VOID BAOriSessionTearDown( return; } - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BAOriEntry[Idx]; DBGPRINT(RT_DEBUG_TRACE,("\t===>Idx = %ld, Wcid=%d.TID=%d, ORI_BA_Status = %d \n", Idx, Wcid, TID, pBAEntry->ORI_BA_Status)); @@ -970,7 +970,7 @@ VOID BARecSessionTearDown( if (Idx == 0) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BARecEntry[Idx]; @@ -1181,7 +1181,7 @@ VOID PeerAddBAReqAction( PULONG ptemp; PMAC_TABLE_ENTRY pMacEntry; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __func__, Elem->Wcid)); //hex_dump("AddBAReq", Elem->Msg, Elem->MsgLen); @@ -1265,7 +1265,7 @@ VOID PeerAddBAReqAction( MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); MlmeFreeMemory(pAd, pOutBuffer); - DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __FUNCTION__, Elem->Wcid, ADDframe.BaParm.TID, + DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __func__, Elem->Wcid, ADDframe.BaParm.TID, ADDframe.BaParm.BufSize)); } @@ -1284,7 +1284,7 @@ VOID PeerAddBARspAction( if (Elem->Wcid >= MAX_LEN_OF_MAC_TABLE) return; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __func__, Elem->Wcid)); //hex_dump("PeerAddBARspAction()", Elem->Msg, Elem->MsgLen); @@ -1325,7 +1325,7 @@ VOID PeerDelBAAction( //PUCHAR pOutBuffer = NULL; PFRAME_DELBA_REQ pDelFrame = NULL; - DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __func__)); //DELBA Request from unknown peer, ignore this. if (PeerDelBAActionSanity(pAd, Elem->Wcid, Elem->Msg, Elem->MsgLen)) { @@ -1362,7 +1362,7 @@ BOOLEAN CntlEnqueueForRecv( TID = (UCHAR)pFrame->BARControl.TID; - DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __func__, Wcid, TID)); //hex_dump("BAR", (PCHAR) pFrame, MsgLen); // Do nothing if the driver is starting halt state. // This might happen when timer already been fired before cancel timer with mlmehalt diff --git a/drivers/staging/rt2870/common/cmm_data.c b/drivers/staging/rt2870/common/cmm_data.c index 4477a8e64a29..fd809abf6df0 100644 --- a/drivers/staging/rt2870/common/cmm_data.c +++ b/drivers/staging/rt2870/common/cmm_data.c @@ -2009,7 +2009,7 @@ BOOLEAN MacTableDeleteEntry( } else { - printk("\n%s: Impossible Wcid = %d !!!!!\n", __FUNCTION__, wcid); + printk("\n%s: Impossible Wcid = %d !!!!!\n", __func__, wcid); } } diff --git a/drivers/staging/rt2870/common/cmm_info.c b/drivers/staging/rt2870/common/cmm_info.c index 40792e162374..47a1b1a73f09 100644 --- a/drivers/staging/rt2870/common/cmm_info.c +++ b/drivers/staging/rt2870/common/cmm_info.c @@ -2331,7 +2331,7 @@ VOID RTMPIoctlGetMacTable( wrq->u.data.length = sizeof(RT_802_11_MAC_TABLE); if (copy_to_user(wrq->u.data.pointer, &MacTab, wrq->u.data.length)) { - DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __func__)); } msg = (CHAR *) kmalloc(sizeof(CHAR)*(MAX_LEN_OF_MAC_TABLE*MAC_LINE_LEN), MEM_ALLOC_FLAG); diff --git a/drivers/staging/rt2870/common/dfs.c b/drivers/staging/rt2870/common/dfs.c index 23cf1510e2d2..b09bba5cb206 100644 --- a/drivers/staging/rt2870/common/dfs.c +++ b/drivers/staging/rt2870/common/dfs.c @@ -428,7 +428,7 @@ INT Set_ChMovingTime_Proc( pAd->CommonCfg.RadarDetect.ChMovingTime = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.ChMovingTime)); return TRUE; @@ -444,7 +444,7 @@ INT Set_LongPulseRadarTh_Proc( pAd->CommonCfg.RadarDetect.LongPulseRadarTh = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.LongPulseRadarTh)); return TRUE; diff --git a/drivers/staging/rt2870/common/rtmp_init.c b/drivers/staging/rt2870/common/rtmp_init.c index 87028fd60bc2..870a00da3dac 100644 --- a/drivers/staging/rt2870/common/rtmp_init.c +++ b/drivers/staging/rt2870/common/rtmp_init.c @@ -2746,7 +2746,7 @@ NDIS_STATUS NICLoadFirmware( #ifdef BIN_IN_FILE #define NICLF_DEFAULT_USE() \ flg_default_firm_use = TRUE; \ - printk("%s - Use default firmware!\n", __FUNCTION__); + printk("%s - Use default firmware!\n", __func__); NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PUCHAR src; @@ -2761,7 +2761,7 @@ NDIS_STATUS NICLoadFirmware( BOOLEAN flg_default_firm_use = FALSE; - DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __func__)); /* init */ pFirmwareImage = NULL; @@ -2784,7 +2784,7 @@ NDIS_STATUS NICLoadFirmware( if (pFirmwareImage == NULL) { /* allocate fail, use default firmware array in firmware.h */ - printk("%s - Allocate memory fail!\n", __FUNCTION__); + printk("%s - Allocate memory fail!\n", __func__); NICLF_DEFAULT_USE(); } else @@ -2805,7 +2805,7 @@ NDIS_STATUS NICLoadFirmware( if (IS_ERR(srcf)) { printk("%s - Error %ld opening %s\n", - __FUNCTION__, -PTR_ERR(srcf), src); + __func__, -PTR_ERR(srcf), src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2813,7 +2813,7 @@ NDIS_STATUS NICLoadFirmware( /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - printk("%s - %s does not have a write method\n", __FUNCTION__, src); + printk("%s - %s does not have a write method\n", __func__, src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2827,7 +2827,7 @@ NDIS_STATUS NICLoadFirmware( if (FileLength != MAX_FIRMWARE_IMAGE_SIZE) { printk("%s: error file length (=%d) in RT2860AP.BIN\n", - __FUNCTION__, FileLength); + __func__, FileLength); NICLF_DEFAULT_USE(); break; } @@ -2850,7 +2850,7 @@ NDIS_STATUS NICLoadFirmware( /* CRC fail */ printk("%s: CRC = 0x%02x 0x%02x " "error, should be 0x%02x 0x%02x\n", - __FUNCTION__, + __func__, pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-2], pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-1], (UCHAR)(crc>>8), (UCHAR)(crc)); @@ -2869,7 +2869,7 @@ NDIS_STATUS NICLoadFirmware( ((FIRMWARE_MAJOR_VERSION << 8) + FIRMWARE_MINOR_VERSION)) { - printk("%s: firmware version too old!\n", __FUNCTION__); + printk("%s: firmware version too old!\n", __func__); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -3024,10 +3024,10 @@ NDIS_STATUS NICLoadFirmware( #if 0 DBGPRINT(RT_DEBUG_TRACE, - ("<=== %s (src=%s, status=%d)\n", __FUNCTION__, src, Status)); + ("<=== %s (src=%s, status=%d)\n", __func__, src, Status)); #else DBGPRINT(RT_DEBUG_TRACE, - ("<=== %s (status=%d)\n", __FUNCTION__, Status)); + ("<=== %s (status=%d)\n", __func__, Status)); #endif return Status; } /* End of NICLoadFirmware */ diff --git a/drivers/staging/rt2870/common/rtusb_bulk.c b/drivers/staging/rt2870/common/rtusb_bulk.c index 3c6ba1b9deb1..c46d9166ccf4 100644 --- a/drivers/staging/rt2870/common/rtusb_bulk.c +++ b/drivers/staging/rt2870/common/rtusb_bulk.c @@ -329,7 +329,7 @@ VOID RTUSBBulkOutDataPacket( if (pTxInfo->QSEL != FIFO_EDCA) { - printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __FUNCTION__, pTxInfo->QSEL); + printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __func__, pTxInfo->QSEL); printk("\tCWPos=%ld, NBPos=%ld, ENBPos=%ld, bCopy=%d!\n", pHTTXContext->CurWritePosition, pHTTXContext->NextBulkOutPosition, pHTTXContext->ENextBulkOutPosition, pHTTXContext->bCopySavePad); hex_dump("Wrong QSel Pkt:", (PUCHAR)&pWirelessPkt[TmpBulkEndPos], (pHTTXContext->CurWritePosition - pHTTXContext->NextBulkOutPosition)); } @@ -942,7 +942,7 @@ VOID RTUSBBulkOutMLMEPacket( pTxInfo = (PTXINFO_STRUC)pMLMEContext->TransferBuffer; if (pTxInfo->QSEL != FIFO_EDCA) { - printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __FUNCTION__, pTxInfo->QSEL); + printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __func__, pTxInfo->QSEL); printk("\tMLME_Index=%d!\n", Index); hex_dump("Wrong QSel Pkt:", (PUCHAR)pMLMEContext->TransferBuffer, pTxInfo->USBDMATxPktLen); } diff --git a/drivers/staging/rt2870/common/spectrum.c b/drivers/staging/rt2870/common/spectrum.c index abba8405184f..43782ce9288f 100644 --- a/drivers/staging/rt2870/common/spectrum.c +++ b/drivers/staging/rt2870/common/spectrum.c @@ -48,7 +48,7 @@ VOID MeasureReqTabInit( if (pAd->CommonCfg.pMeasureReqTab) NdisZeroMemory(pAd->CommonCfg.pMeasureReqTab, sizeof(MEASURE_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __func__)); return; } @@ -76,7 +76,7 @@ static PMEASURE_REQ_ENTRY MeasureReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -113,7 +113,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -174,7 +174,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -209,7 +209,7 @@ static VOID MeasureReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return; } @@ -266,7 +266,7 @@ VOID TpcReqTabInit( if (pAd->CommonCfg.pTpcReqTab) NdisZeroMemory(pAd->CommonCfg.pTpcReqTab, sizeof(TPC_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __func__)); return; } @@ -294,7 +294,7 @@ static PTPC_REQ_ENTRY TpcReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -332,7 +332,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -393,7 +393,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -428,7 +428,7 @@ static VOID TpcReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return; } @@ -781,7 +781,7 @@ VOID EnqueueMeasurementReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -843,7 +843,7 @@ VOID EnqueueMeasurementRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -897,7 +897,7 @@ VOID EnqueueTPCReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -949,7 +949,7 @@ VOID EnqueueTPCRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1002,7 +1002,7 @@ VOID EnqueueChSwAnn( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1595,7 +1595,7 @@ static VOID PeerMeasureReportAction( if ((pMeasureReportInfo = kmalloc(sizeof(MEASURE_RPI_REPORT), GFP_ATOMIC)) == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __FUNCTION__, sizeof(MEASURE_RPI_REPORT))); + DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __func__, sizeof(MEASURE_RPI_REPORT))); return; } @@ -1704,7 +1704,7 @@ static VOID PeerTpcRepAction( { TpcReqDelete(pAd, pEntry->DialogToken); DBGPRINT(RT_DEBUG_TRACE, ("%s: DialogToken=%x, TxPwr=%d, LinkMargin=%d\n", - __FUNCTION__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); + __func__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); } } @@ -1820,7 +1820,7 @@ INT Set_MeasureReq_Proc( MeasureReqType = simple_strtol(thisChar, 0, 16); if (MeasureReqType > 3) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __FUNCTION__, MeasureReqType)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __func__, MeasureReqType)); return TRUE; } break; @@ -1832,10 +1832,10 @@ INT Set_MeasureReq_Proc( ArgIdx++; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __FUNCTION__, Aid, MeasureReqType, MeasureCh)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __func__, Aid, MeasureReqType, MeasureCh)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } @@ -1860,10 +1860,10 @@ INT Set_TpcReq_Proc( Aid = simple_strtol(arg, 0, 16); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __func__, Aid)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } diff --git a/drivers/staging/rt2870/rt_ate.c b/drivers/staging/rt2870/rt_ate.c index e99b3da0b62d..27c763ee9276 100644 --- a/drivers/staging/rt2870/rt_ate.c +++ b/drivers/staging/rt2870/rt_ate.c @@ -314,7 +314,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); } else// 5.5 GHz { @@ -341,7 +341,7 @@ static INT ATETxPwrHandler( R = (ULONG) TxPower; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __FUNCTION__, TxPower, R)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __func__, TxPower, R)); } if (pAd->ate.Channel <= 14) @@ -454,7 +454,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); if (pAd->ate.Channel <= 14) { @@ -2261,7 +2261,7 @@ INT Set_ATE_Load_E2P_Proc( UINT32 FileLength = 0; UINT32 value = simple_strtol(arg, 0, 10); - ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __FUNCTION__, value)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __func__, value)); if (value > 0) { @@ -2285,14 +2285,14 @@ INT Set_ATE_Load_E2P_Proc( if (IS_ERR(srcf)) { - ate_print("%s - Error %ld opening %s\n", __FUNCTION__, -PTR_ERR(srcf), src); + ate_print("%s - Error %ld opening %s\n", __func__, -PTR_ERR(srcf), src); break; } /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - ate_print("%s - %s does not have a read method\n", __FUNCTION__, src); + ate_print("%s - %s does not have a read method\n", __func__, src); break; } @@ -2305,7 +2305,7 @@ INT Set_ATE_Load_E2P_Proc( if (FileLength != EEPROM_SIZE) { ate_print("%s: error file length (=%d) in e2p.bin\n", - __FUNCTION__, FileLength); + __func__, FileLength); break; } else @@ -2337,7 +2337,7 @@ INT Set_ATE_Load_E2P_Proc( current->fsuid = orgfsuid; current->fsgid = orgfsgid; } - ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __FUNCTION__, ret)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __func__, ret)); return ret; @@ -2350,12 +2350,12 @@ INT Set_ATE_Load_E2P_Proc( USHORT WriteEEPROM[(EEPROM_SIZE/2)]; struct iwreq *wrq = (struct iwreq *)arg; - ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __FUNCTION__, wrq->u.data.length)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __func__, wrq->u.data.length)); if (wrq->u.data.length != EEPROM_SIZE) { ate_print("%s: error length (=%d) from host\n", - __FUNCTION__, wrq->u.data.length); + __func__, wrq->u.data.length); return FALSE; } else/* (wrq->u.data.length == EEPROM_SIZE) */ @@ -2374,7 +2374,7 @@ INT Set_ATE_Load_E2P_Proc( } while(FALSE); } - ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __FUNCTION__)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __func__)); return TRUE; @@ -4083,7 +4083,7 @@ VOID RtmpDoAte( Command_Id = ntohs(pRaCfg->command_id); - ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __FUNCTION__, Command_Id)); + ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __func__, Command_Id)); switch (Command_Id) { @@ -6117,7 +6117,7 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.TxAntennaSel = 2; break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R1 */ @@ -6155,13 +6155,13 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.RxAntennaSel = 3; break; default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R3 */ default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } diff --git a/drivers/staging/rt2870/rt_linux.c b/drivers/staging/rt2870/rt_linux.c index f2ea8ebcd042..992e3d16f9b4 100644 --- a/drivers/staging/rt2870/rt_linux.c +++ b/drivers/staging/rt2870/rt_linux.c @@ -404,7 +404,7 @@ NDIS_STATUS RTMPAllocateNdisPacket( skb_put(GET_OS_PKT_TYPE(pPacket), HeaderLen+DataLen); RTMP_SET_PACKET_SOURCE(pPacket, PKTSRC_NDIS); -// printk("%s : pPacket = %p, len = %d\n", __FUNCTION__, pPacket, GET_OS_PKT_LEN(pPacket)); +// printk("%s : pPacket = %p, len = %d\n", __func__, pPacket, GET_OS_PKT_LEN(pPacket)); *ppPacket = pPacket; return NDIS_STATUS_SUCCESS; } @@ -814,13 +814,13 @@ VOID RTMPSendWirelessEvent( if (event_table_len == 0) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __FUNCTION__, type)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __func__, type)); return; } if (event >= event_table_len) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __FUNCTION__, event)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __func__, event)); return; } @@ -858,14 +858,14 @@ VOID RTMPSendWirelessEvent( //send wireless event wireless_send_event(pAd->net_dev, IWEVCUSTOM, &wrqu, pBuf); - //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __FUNCTION__, pBuf)); + //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __func__, pBuf)); kfree(pBuf); } else - DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __func__)); #else - DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __func__)); #endif /* WIRELESS_EXT >= 15 */ } @@ -889,13 +889,13 @@ void send_monitor_packets( ASSERT(pRxBlk->pRxPacket); if (pRxBlk->DataSize < 10) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __FUNCTION__, pRxBlk->DataSize)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __func__, pRxBlk->DataSize)); goto err_free_sk_buff; } if (pRxBlk->DataSize + sizeof(wlan_ng_prism2_header) > RX_BUFFER_AGGRESIZE) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __FUNCTION__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __func__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); goto err_free_sk_buff; } @@ -951,7 +951,7 @@ void send_monitor_packets( if (skb_headroom(pOSPkt) < (sizeof(wlan_ng_prism2_header)+ header_len)) { if (pskb_expand_head(pOSPkt, (sizeof(wlan_ng_prism2_header) + header_len), 0, GFP_ATOMIC)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __func__)); goto err_free_sk_buff; } //end if } //end if diff --git a/drivers/staging/rt2870/rt_linux.h b/drivers/staging/rt2870/rt_linux.h index 814297577261..859f9cef0a19 100644 --- a/drivers/staging/rt2870/rt_linux.h +++ b/drivers/staging/rt2870/rt_linux.h @@ -124,7 +124,7 @@ typedef int (*HARD_START_XMIT_FUNC)(struct sk_buff *skb, struct net_device *net_ #define RT_MOD_INC_USE_COUNT() \ if (!try_module_get(THIS_MODULE)) \ { \ - DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __FUNCTION__)); \ + DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __func__)); \ return -1; \ } diff --git a/drivers/staging/rt2870/rt_main_dev.c b/drivers/staging/rt2870/rt_main_dev.c index b990f8a61641..313ecea0bfa8 100644 --- a/drivers/staging/rt2870/rt_main_dev.c +++ b/drivers/staging/rt2870/rt_main_dev.c @@ -1554,7 +1554,7 @@ int rt28xx_packet_xmit(struct sk_buff *skb) #if 0 // if ((pkt->data[0] & 0x1) == 0) { - //hex_dump(__FUNCTION__, pkt->data, pkt->len); + //hex_dump(__func__, pkt->data, pkt->len); printk("pPacket = %x\n", pPacket); } #endif diff --git a/drivers/staging/rt2870/rt_profile.c b/drivers/staging/rt2870/rt_profile.c index c4474a6428ac..467fea35e4a9 100644 --- a/drivers/staging/rt2870/rt_profile.c +++ b/drivers/staging/rt2870/rt_profile.c @@ -1024,7 +1024,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->MlmeAux.SsidLen = pAd->CommonCfg.SsidLen; NdisZeroMemory(pAd->MlmeAux.Ssid, NDIS_802_11_LENGTH_SSID); NdisMoveMemory(pAd->MlmeAux.Ssid, tmpbuf, pAd->MlmeAux.SsidLen); - DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __func__, tmpbuf)); } } } @@ -1043,7 +1043,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.BssType = BSS_INFRA; // Reset Ralink supplicant to not use, it will be set to start when UI set PMK key pAd->StaCfg.WpaState = SS_NOTUSE; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __FUNCTION__, pAd->StaCfg.BssType)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __func__, pAd->StaCfg.BssType)); } } #endif // CONFIG_STA_SUPPORT // @@ -1341,7 +1341,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.PortSecured = WPA_802_1X_PORT_NOT_SECURED; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1368,7 +1368,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.bMixCipher = FALSE; //RTMPMakeRSNIE(pAd, pAd->StaCfg.AuthMode, pAd->StaCfg.WepStatus, 0); - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1405,7 +1405,7 @@ NDIS_STATUS RTMPReadParametersHook( else { err = 1; - DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __func__)); } if (err == 0) @@ -1436,7 +1436,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.WpaState = SS_NOTUSE; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __func__, tmpbuf)); } } } diff --git a/drivers/staging/rt2870/rtmp_def.h b/drivers/staging/rt2870/rtmp_def.h index 9b86325b4c55..c0f7561a9d9d 100644 --- a/drivers/staging/rt2870/rtmp_def.h +++ b/drivers/staging/rt2870/rtmp_def.h @@ -342,7 +342,7 @@ /* sanity check for apidx */ #define MBSS_MR_APIDX_SANITY_CHECK(apidx) \ { if (apidx > MAX_MBSSID_NUM) { \ - printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __FUNCTION__, apidx); \ + printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __func__, apidx); \ apidx = MAIN_MBSSID; } } #define VALID_WCID(_wcid) ((_wcid) > 0 && (_wcid) < MAX_LEN_OF_MAC_TABLE ) diff --git a/drivers/staging/rt2870/sta_ioctl.c b/drivers/staging/rt2870/sta_ioctl.c index 422f39f9004a..91f0fab11313 100644 --- a/drivers/staging/rt2870/sta_ioctl.c +++ b/drivers/staging/rt2870/sta_ioctl.c @@ -2224,7 +2224,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, wrq->length = strlen(extra) + 1; // 1: size of '\0' break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2252,7 +2252,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2267,7 +2267,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2281,7 +2281,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2314,7 +2314,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2345,7 +2345,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2365,7 +2365,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2395,7 +2395,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; @@ -2408,7 +2408,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.PairCipher = Ndis802_11WEPDisabled; pAdapter->StaCfg.GroupCipher = Ndis802_11WEPDisabled; }*/ - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2418,7 +2418,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2431,10 +2431,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2542,7 +2542,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2554,15 +2554,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2595,7 +2595,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, } break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rt2870/tmp60 b/drivers/staging/rt2870/tmp60 index 992096a248c5..975e44484379 100644 --- a/drivers/staging/rt2870/tmp60 +++ b/drivers/staging/rt2870/tmp60 @@ -2224,7 +2224,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, wrq->length = strlen(extra) + 1; // 1: size of '\0' break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2252,7 +2252,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2267,7 +2267,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2281,7 +2281,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2314,7 +2314,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2345,7 +2345,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2365,7 +2365,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2395,7 +2395,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; @@ -2408,7 +2408,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.PairCipher = Ndis802_11WEPDisabled; pAdapter->StaCfg.GroupCipher = Ndis802_11WEPDisabled; }*/ - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2418,7 +2418,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2431,10 +2431,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2542,7 +2542,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2554,15 +2554,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2580,7 +2580,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, NdisMoveMemory(pAdapter->SharedKey[BSS0][keyIdx].Key, ext->key, ext->key_len); break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rt2870/tmp61 b/drivers/staging/rt2870/tmp61 index 992096a248c5..975e44484379 100644 --- a/drivers/staging/rt2870/tmp61 +++ b/drivers/staging/rt2870/tmp61 @@ -2224,7 +2224,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, wrq->length = strlen(extra) + 1; // 1: size of '\0' break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2252,7 +2252,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2267,7 +2267,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2281,7 +2281,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2314,7 +2314,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2345,7 +2345,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2365,7 +2365,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2395,7 +2395,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; @@ -2408,7 +2408,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.PairCipher = Ndis802_11WEPDisabled; pAdapter->StaCfg.GroupCipher = Ndis802_11WEPDisabled; }*/ - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2418,7 +2418,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2431,10 +2431,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2542,7 +2542,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2554,15 +2554,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2580,7 +2580,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, NdisMoveMemory(pAdapter->SharedKey[BSS0][keyIdx].Key, ext->key, ext->key_len); break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rtl8187se/ieee80211.h b/drivers/staging/rtl8187se/ieee80211.h index bf06abeeaeb8..58336080ad50 100644 --- a/drivers/staging/rtl8187se/ieee80211.h +++ b/drivers/staging/rtl8187se/ieee80211.h @@ -396,7 +396,7 @@ extern u32 ieee80211_debug_level; #define IEEE80211_DEBUG(level, fmt, args...) \ do { if (ieee80211_debug_level & (level)) \ printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_IEEE80211_DEBUG */ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211.h b/drivers/staging/rtl8187se/ieee80211/ieee80211.h index bf06abeeaeb8..58336080ad50 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211.h +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211.h @@ -396,7 +396,7 @@ extern u32 ieee80211_debug_level; #define IEEE80211_DEBUG(level, fmt, args...) \ do { if (ieee80211_debug_level & (level)) \ printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_IEEE80211_DEBUG */ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c index 7d890c328b0e..08add385790f 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c @@ -490,7 +490,7 @@ static char * ieee80211_ccmp_print_stats(char *p, void *priv) void ieee80211_ccmp_null(void) { -// printk("============>%s()\n", __FUNCTION__); +// printk("============>%s()\n", __func__); return; } static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = { diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c index e560b1e11020..de97bbec0da5 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c @@ -984,7 +984,7 @@ void ieee80211_crypto_tkip_exit(void) void ieee80211_tkip_null(void) { -// printk("============>%s()\n", __FUNCTION__); +// printk("============>%s()\n", __func__); return; } diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c index f973daeeeb0f..68a22b32fcf0 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c @@ -380,7 +380,7 @@ void ieee80211_crypto_wep_exit(void) void ieee80211_wep_null(void) { -// printk("============>%s()\n", __FUNCTION__); +// printk("============>%s()\n", __func__); return; } #if 0 diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c index 79ec64959cf6..23519b37538f 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c @@ -1635,7 +1635,7 @@ inline void update_network(struct ieee80211_network *dst, memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); dst->rates_ex_len = src->rates_ex_len; dst->HighestOperaRate= src->HighestOperaRate; - //printk("==========>in %s: src->ssid is %s,chan is %d\n",__FUNCTION__,src->ssid,src->channel); + //printk("==========>in %s: src->ssid is %s,chan is %d\n",__func__,src->ssid,src->channel); //YJ,add,080819,for hidden ap if(src->ssid_len > 0) diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c index fcffee516d51..e5752f615e09 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c @@ -731,7 +731,7 @@ void ieee80211_softmac_scan_wq(struct ieee80211_device *ieee) memcpy(channel_map, GET_DOT11D_INFO(ieee)->channel_map, MAX_CHANNEL_NUMBER+1); #endif // printk("ieee80211_softmac_scan_wq ENABLE_IPS\n"); -// printk("in %s\n",__FUNCTION__); +// printk("in %s\n",__func__); down(&ieee->scan_sem); do{ @@ -1785,7 +1785,7 @@ void ieee80211_associate_step1(struct ieee80211_device *ieee) //If call dev_kfree_skb_any,a warning will ocur.... //KERNEL: assertion (!atomic_read(&skb->users)) failed at net/core/dev.c (1708) //So ... 1204 by lawrence. - //printk("\nIn %s,line %d call kfree skb.",__FUNCTION__,__LINE__); + //printk("\nIn %s,line %d call kfree skb.",__func__,__LINE__); //dev_kfree_skb_any(skb);//edit by thomas } } @@ -2432,7 +2432,7 @@ inline void ieee80211_sta_ps(struct ieee80211_device *ieee) } sleep = ieee80211_sta_ps_sleep(ieee,&th, &tl); -// printk("===>%s,%d[2 wake, 1 sleep, 0 do nothing], ieee->sta_sleep = %d\n",__FUNCTION__, sleep,ieee->sta_sleep); +// printk("===>%s,%d[2 wake, 1 sleep, 0 do nothing], ieee->sta_sleep = %d\n",__func__, sleep,ieee->sta_sleep); /* 2 wake, 1 sleep, 0 do nothing */ if(sleep == 0) goto out; @@ -2510,7 +2510,7 @@ void ieee80211_ps_tx_ack(struct ieee80211_device *ieee, short success) /* Null frame with PS bit set */ if(success){ - // printk("==================> %s::enter sleep state\n",__FUNCTION__); + // printk("==================> %s::enter sleep state\n",__func__); ieee->sta_sleep = 1; ieee->enter_sleep_state(ieee->dev,ieee->ps_th,ieee->ps_tl); } diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c index 43b8aecee9de..93af37e2d31a 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c @@ -31,7 +31,7 @@ int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info { int ret; struct iw_freq *fwrq = & wrqu->freq; -// printk("in %s\n",__FUNCTION__); +// printk("in %s\n",__func__); down(&ieee->wx_sem); if(ieee->iw_mode == IW_MODE_INFRA){ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c index c7d9f4fda413..6aad61e78041 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c @@ -663,7 +663,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, ret = -EINVAL; goto done; } -// printk("8-09-08-9=====>%s, alg name:%s\n",__FUNCTION__, alg); +// printk("8-09-08-9=====>%s, alg name:%s\n",__func__, alg); ops = ieee80211_get_crypto_ops(alg); if (ops == NULL) { @@ -757,7 +757,7 @@ int ieee80211_wx_set_mlme(struct ieee80211_device *ieee, union iwreq_data *wrqu, char *extra) { struct iw_mlme *mlme = (struct iw_mlme *) extra; -// printk("\ndkgadfslkdjgalskdf===============>%s(), cmd:%x\n", __FUNCTION__, mlme->cmd); +// printk("\ndkgadfslkdjgalskdf===============>%s(), cmd:%x\n", __func__, mlme->cmd); #if 1 switch (mlme->cmd) { case IW_MLME_DEAUTH: @@ -830,7 +830,7 @@ int ieee80211_wx_set_auth(struct ieee80211_device *ieee, int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len) { #if 0 - printk("====>%s()\n", __FUNCTION__); + printk("====>%s()\n", __func__); { int i; for (i=0; i<len; i++) @@ -866,7 +866,7 @@ int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len) ieee->wpa_ie = NULL; ieee->wpa_ie_len = 0; } -// printk("<=====out %s()\n", __FUNCTION__); +// printk("<=====out %s()\n", __func__); return 0; diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c index 00f4df49bc0e..94534955e38b 100644 --- a/drivers/staging/rtl8187se/r8180_core.c +++ b/drivers/staging/rtl8187se/r8180_core.c @@ -1407,12 +1407,12 @@ void rtl8180_set_chan(struct net_device *dev,short ch) if((ch > 14) || (ch < 1)) { - printk("In %s: Invalid chnanel %d\n", __FUNCTION__, ch); + printk("In %s: Invalid chnanel %d\n", __func__, ch); return; } priv->chan=ch; - //printk("in %s:channel is %d\n",__FUNCTION__,ch); + //printk("in %s:channel is %d\n",__func__,ch); priv->rf_set_chan(dev,priv->chan); } @@ -3656,7 +3656,7 @@ void rtl8180_link_change(struct net_device *dev) void rtl8180_rq_tx_ack(struct net_device *dev){ struct r8180_priv *priv = ieee80211_priv(dev); -// printk("====================>%s\n",__FUNCTION__); +// printk("====================>%s\n",__func__); write_nic_byte(dev,CONFIG4,read_nic_byte(dev,CONFIG4)|CONFIG4_PWRMGT); priv->ack_tx_to_ieee = 1; } @@ -5448,7 +5448,7 @@ void rtl8180_hw_wakeup_wq(struct net_device *dev) #endif // printk("dev is %d\n",dev); -// printk("&*&(^*(&(&=========>%s()\n", __FUNCTION__); +// printk("&*&(^*(&(&=========>%s()\n", __func__); rtl8180_hw_wakeup(dev); } @@ -6318,12 +6318,12 @@ priv->txnpring)/8); // printk("NumTxOkTotal is %d\n",priv->NumTxOkTotal++); } #endif - // printk("in function %s:curr_retry_count is %d\n",__FUNCTION__,((*head) & (0x000000ff))); + // printk("in function %s:curr_retry_count is %d\n",__func__,((*head) & (0x000000ff))); } if(!error){ priv->NumTxOkBytesTotal += (*(head+3)) & (0x00000fff); } -// printk("in function %s:curr_txokbyte_count is %d\n",__FUNCTION__,(*(head+3)) & (0x00000fff)); +// printk("in function %s:curr_txokbyte_count is %d\n",__func__,(*(head+3)) & (0x00000fff)); *head = *head &~ (1<<31); if((head - begin)/8 == priv->txringcount-1) diff --git a/drivers/staging/rtl8187se/r8180_rtl8225z2.c b/drivers/staging/rtl8187se/r8180_rtl8225z2.c index 90a574d46da0..4136b9429597 100644 --- a/drivers/staging/rtl8187se/r8180_rtl8225z2.c +++ b/drivers/staging/rtl8187se/r8180_rtl8225z2.c @@ -1571,7 +1571,7 @@ void rtl8225z4_rf_sleep(struct net_device *dev) // // Turn off RF power. // - //printk("=========>%s()\n", __FUNCTION__); + //printk("=========>%s()\n", __func__); MgntActSet_RF_State(dev, eRfSleep, RF_CHANGE_BY_PS); //mdelay(2); //FIXME } @@ -1580,7 +1580,7 @@ void rtl8225z4_rf_wakeup(struct net_device *dev) // // Turn on RF power. // - //printk("=========>%s()\n", __FUNCTION__); + //printk("=========>%s()\n", __func__); MgntActSet_RF_State(dev, eRfOn, RF_CHANGE_BY_PS); } #endif diff --git a/drivers/staging/rtl8187se/r8180_wx.c b/drivers/staging/rtl8187se/r8180_wx.c index c77abe502b79..8b3901d87f87 100644 --- a/drivers/staging/rtl8187se/r8180_wx.c +++ b/drivers/staging/rtl8187se/r8180_wx.c @@ -1177,7 +1177,7 @@ static int r8180_wx_set_channelplan(struct net_device *dev, //struct ieee80211_device *ieee = netdev_priv(dev); int *val = (int *)extra; int i; - printk("-----in fun %s\n", __FUNCTION__); + printk("-----in fun %s\n", __func__); if(priv->ieee80211->bHwRadioOff) return 0; @@ -1235,7 +1235,7 @@ static int r8180_wx_set_forcerate(struct net_device *dev, down(&priv->wx_sem); - printk("==============>%s(): forcerate is %d\n",__FUNCTION__,forcerate); + printk("==============>%s(): forcerate is %d\n",__func__,forcerate); if((forcerate == 2) || (forcerate == 4) || (forcerate == 11) || (forcerate == 22) || (forcerate == 12) || (forcerate == 18) || (forcerate == 24) || (forcerate == 36) || (forcerate == 48) || (forcerate == 72) || (forcerate == 96) || (forcerate == 108)) @@ -1260,7 +1260,7 @@ static int r8180_wx_set_enc_ext(struct net_device *dev, { struct r8180_priv *priv = ieee80211_priv(dev); - //printk("===>%s()\n", __FUNCTION__); + //printk("===>%s()\n", __func__); int ret=0; @@ -1277,7 +1277,7 @@ static int r8180_wx_set_auth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra) { - //printk("====>%s()\n", __FUNCTION__); + //printk("====>%s()\n", __func__); struct r8180_priv *priv = ieee80211_priv(dev); int ret=0; @@ -1294,7 +1294,7 @@ static int r8180_wx_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - //printk("====>%s()\n", __FUNCTION__); + //printk("====>%s()\n", __func__); int ret=0; struct r8180_priv *priv = ieee80211_priv(dev); @@ -1315,7 +1315,7 @@ static int r8180_wx_set_gen_ie(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { -// printk("====>%s(), len:%d\n", __FUNCTION__, data->length); +// printk("====>%s(), len:%d\n", __func__, data->length); int ret=0; struct r8180_priv *priv = ieee80211_priv(dev); @@ -1328,7 +1328,7 @@ static int r8180_wx_set_gen_ie(struct net_device *dev, ret = ieee80211_wx_set_gen_ie(priv->ieee80211, extra, data->length); #endif up(&priv->wx_sem); - //printk("<======%s(), ret:%d\n", __FUNCTION__, ret); + //printk("<======%s(), ret:%d\n", __func__, ret); return ret; diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 289d81adfb9c..83babb0a1df7 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -150,4 +150,6 @@ source "drivers/usb/atm/Kconfig" source "drivers/usb/gadget/Kconfig" +source "drivers/usb/otg/Kconfig" + endif # USB_SUPPORT diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d50a99f70aee..00b47ea24f86 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1275,7 +1275,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) struct acm *acm = usb_get_intfdata(intf); int cnt; - if (acm->dev->auto_pm) { + if (message.event & PM_EVENT_AUTO) { int b; spin_lock_irq(&acm->read_lock); diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 5a8ecc045e3f..3771d6e6d0cc 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -764,7 +764,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) mutex_lock(&desc->plock); #ifdef CONFIG_PM - if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) { + if ((message.event & PM_EVENT_AUTO) && + test_bit(WDM_IN_USE, &desc->flags)) { rv = -EBUSY; } else { #endif diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 43a863c5cc43..0f5c05f6f9df 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/kref.h> @@ -482,7 +483,6 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, int retval; int actual; unsigned long int n_bytes; - int n; int remaining; int done; int this_part; @@ -526,11 +526,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf, goto exit; } - n_bytes = 12 + this_part; - if (this_part % 4) - n_bytes += 4 - this_part % 4; - for (n = 12 + this_part; n < n_bytes; n++) - buffer[n] = 0; + n_bytes = roundup(12 + this_part, 4); + memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part)); retval = usb_bulk_msg(data->usb_dev, usb_sndbulkpipe(data->usb_dev, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index aa79280df15d..26fece124e0e 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -981,9 +981,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, return -EINVAL; if (!uurb->buffer) return -EINVAL; - if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || - uurb->signr > SIGRTMAX)) - return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { ifnum = findintfep(ps->dev, uurb->endpoint); @@ -1320,7 +1317,7 @@ static int get_urb32(struct usbdevfs_urb *kurb, if (__get_user(uptr, &uurb->buffer)) return -EFAULT; kurb->buffer = compat_ptr(uptr); - if (__get_user(uptr, &uurb->buffer)) + if (__get_user(uptr, &uurb->usercontext)) return -EFAULT; kurb->usercontext = compat_ptr(uptr); @@ -1401,8 +1398,6 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) if (copy_from_user(&ds, arg, sizeof(ds))) return -EFAULT; - if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX)) - return -EINVAL; ps->discsignr = ds.signr; ps->disccontext = ds.context; return 0; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8c081308b0e2..98760553bc95 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -184,6 +184,20 @@ static int usb_unbind_device(struct device *dev) return 0; } +/* + * Cancel any pending scheduled resets + * + * [see usb_queue_reset_device()] + * + * Called after unconfiguring / when releasing interfaces. See + * comments in __usb_queue_reset_device() regarding + * udev->reset_running. + */ +static void usb_cancel_queued_reset(struct usb_interface *iface) +{ + if (iface->reset_running == 0) + cancel_work_sync(&iface->reset_ws); +} /* called from driver core with dev locked */ static int usb_probe_interface(struct device *dev) @@ -242,6 +256,7 @@ static int usb_probe_interface(struct device *dev) mark_quiesced(intf); intf->needs_remote_wakeup = 0; intf->condition = USB_INTERFACE_UNBOUND; + usb_cancel_queued_reset(intf); } else intf->condition = USB_INTERFACE_BOUND; @@ -272,6 +287,7 @@ static int usb_unbind_interface(struct device *dev) usb_disable_interface(udev, intf); driver->disconnect(intf); + usb_cancel_queued_reset(intf); /* Reset other interface state. * We cannot do a Set-Interface if the device is suspended or @@ -279,9 +295,12 @@ static int usb_unbind_interface(struct device *dev) * altsetting means creating new endpoint device entries). * When either of these happens, defer the Set-Interface. */ - if (intf->cur_altsetting->desc.bAlternateSetting == 0) - ; /* Already in altsetting 0 so skip Set-Interface */ - else if (!error && intf->dev.power.status == DPM_ON) + if (intf->cur_altsetting->desc.bAlternateSetting == 0) { + /* Already in altsetting 0 so skip Set-Interface. + * Just re-enable it without affecting the endpoint toggles. + */ + usb_enable_interface(udev, intf, false); + } else if (!error && intf->dev.power.status == DPM_ON) usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); else @@ -380,8 +399,10 @@ void usb_driver_release_interface(struct usb_driver *driver, if (device_is_registered(dev)) { iface->condition = USB_INTERFACE_UNBINDING; device_release_driver(dev); + } else { + iface->condition = USB_INTERFACE_UNBOUND; + usb_cancel_queued_reset(iface); } - dev->driver = NULL; usb_set_intfdata(iface, NULL); @@ -904,7 +925,7 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) } /* Caller has locked udev's pm_mutex */ -static int usb_resume_device(struct usb_device *udev) +static int usb_resume_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; int status = 0; @@ -922,7 +943,7 @@ static int usb_resume_device(struct usb_device *udev) udev->reset_resume = 1; udriver = to_usb_device_driver(udev->dev.driver); - status = udriver->resume(udev); + status = udriver->resume(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); @@ -942,7 +963,8 @@ static int usb_suspend_interface(struct usb_device *udev, if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf)) goto done; - if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */ + /* This can happen; see usb_driver_release_interface() */ + if (intf->condition == USB_INTERFACE_UNBOUND) goto done; driver = to_usb_driver(intf->dev.driver); @@ -950,7 +972,7 @@ static int usb_suspend_interface(struct usb_device *udev, status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); - else if (!udev->auto_pm) + else if (!(msg.event & PM_EVENT_AUTO)) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { @@ -968,7 +990,7 @@ static int usb_suspend_interface(struct usb_device *udev, /* Caller has locked intf's usb_device's pm_mutex */ static int usb_resume_interface(struct usb_device *udev, - struct usb_interface *intf, int reset_resume) + struct usb_interface *intf, pm_message_t msg, int reset_resume) { struct usb_driver *driver; int status = 0; @@ -1092,7 +1114,7 @@ static int autosuspend_check(struct usb_device *udev, int reschedule) if (reschedule) { if (!timer_pending(&udev->autosuspend.timer)) { queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - round_jiffies_relative(suspend_time - j)); + round_jiffies_up_relative(suspend_time - j)); } return -EAGAIN; } @@ -1119,10 +1141,9 @@ static inline int autosuspend_check(struct usb_device *udev, int reschedule) * all the interfaces which were suspended are resumed so that they remain * in the same state as the device. * - * If an autosuspend is in progress (@udev->auto_pm is set), the routine - * checks first to make sure that neither the device itself or any of its - * active interfaces is in use (pm_usage_cnt is greater than 0). If they - * are, the autosuspend fails. + * If an autosuspend is in progress the routine checks first to make sure + * that neither the device itself or any of its active interfaces is in use + * (pm_usage_cnt is greater than 0). If they are, the autosuspend fails. * * If the suspend succeeds, the routine recursively queues an autosuspend * request for @udev's parent device, thereby propagating the change up @@ -1157,7 +1178,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) udev->do_remote_wakeup = device_may_wakeup(&udev->dev); - if (udev->auto_pm) { + if (msg.event & PM_EVENT_AUTO) { status = autosuspend_check(udev, 0); if (status < 0) goto done; @@ -1177,13 +1198,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { + pm_message_t msg2; + + msg2.event = msg.event ^ (PM_EVENT_SUSPEND | PM_EVENT_RESUME); while (--i >= 0) { intf = udev->actconfig->interface[i]; - usb_resume_interface(udev, intf, 0); + usb_resume_interface(udev, intf, msg2, 0); } /* Try another autosuspend when the interfaces aren't busy */ - if (udev->auto_pm) + if (msg.event & PM_EVENT_AUTO) autosuspend_check(udev, status == -EBUSY); /* If the suspend succeeded then prevent any more URB submissions, @@ -1213,6 +1237,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) /** * usb_resume_both - resume a USB device and its interfaces * @udev: the usb_device to resume + * @msg: Power Management message describing this state transition * * This is the central routine for resuming USB devices. It calls the * the resume method for @udev and then calls the resume methods for all @@ -1238,7 +1263,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * * This routine can run only in process context. */ -static int usb_resume_both(struct usb_device *udev) +static int usb_resume_both(struct usb_device *udev, pm_message_t msg) { int status = 0; int i; @@ -1254,14 +1279,15 @@ static int usb_resume_both(struct usb_device *udev) /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { - if (udev->auto_pm && udev->autoresume_disabled) { + if ((msg.event & PM_EVENT_AUTO) && + udev->autoresume_disabled) { status = -EPERM; goto done; } if (parent) { status = usb_autoresume_device(parent); if (status == 0) { - status = usb_resume_device(udev); + status = usb_resume_device(udev, msg); if (status || udev->state == USB_STATE_NOTATTACHED) { usb_autosuspend_device(parent); @@ -1284,15 +1310,16 @@ static int usb_resume_both(struct usb_device *udev) /* We can't progagate beyond the USB subsystem, * so if a root hub's controller is suspended * then we're stuck. */ - status = usb_resume_device(udev); + status = usb_resume_device(udev, msg); } } else if (udev->reset_resume) - status = usb_resume_device(udev); + status = usb_resume_device(udev, msg); if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; - usb_resume_interface(udev, intf, udev->reset_resume); + usb_resume_interface(udev, intf, msg, + udev->reset_resume); } } @@ -1320,13 +1347,13 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) udev->last_busy = jiffies; if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { if (udev->state == USB_STATE_SUSPENDED) - status = usb_resume_both(udev); + status = usb_resume_both(udev, PMSG_AUTO_RESUME); if (status != 0) udev->pm_usage_cnt -= inc_usage_cnt; else if (inc_usage_cnt) udev->last_busy = jiffies; } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) { - status = usb_suspend_both(udev, PMSG_SUSPEND); + status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); } usb_pm_unlock(udev); return status; @@ -1341,6 +1368,19 @@ void usb_autosuspend_work(struct work_struct *work) usb_autopm_do_device(udev, 0); } +/* usb_autoresume_work - callback routine to autoresume a USB device */ +void usb_autoresume_work(struct work_struct *work) +{ + struct usb_device *udev = + container_of(work, struct usb_device, autoresume); + + /* Wake it up, let the drivers do their thing, and then put it + * back to sleep. + */ + if (usb_autopm_do_device(udev, 1) == 0) + usb_autopm_do_device(udev, -1); +} + /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend @@ -1437,13 +1477,14 @@ static int usb_autopm_do_interface(struct usb_interface *intf, udev->last_busy = jiffies; if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { if (udev->state == USB_STATE_SUSPENDED) - status = usb_resume_both(udev); + status = usb_resume_both(udev, + PMSG_AUTO_RESUME); if (status != 0) intf->pm_usage_cnt -= inc_usage_cnt; else udev->last_busy = jiffies; } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) { - status = usb_suspend_both(udev, PMSG_SUSPEND); + status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND); } } usb_pm_unlock(udev); @@ -1492,6 +1533,45 @@ void usb_autopm_put_interface(struct usb_interface *intf) EXPORT_SYMBOL_GPL(usb_autopm_put_interface); /** + * usb_autopm_put_interface_async - decrement a USB interface's PM-usage counter + * @intf: the usb_interface whose counter should be decremented + * + * This routine does essentially the same thing as + * usb_autopm_put_interface(): it decrements @intf's usage counter and + * queues a delayed autosuspend request if the counter is <= 0. The + * difference is that it does not acquire the device's pm_mutex; + * callers must handle all synchronization issues themselves. + * + * Typically a driver would call this routine during an URB's completion + * handler, if no more URBs were pending. + * + * This routine can run in atomic context. + */ +void usb_autopm_put_interface_async(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int status = 0; + + if (intf->condition == USB_INTERFACE_UNBOUND) { + status = -ENODEV; + } else { + udev->last_busy = jiffies; + --intf->pm_usage_cnt; + if (udev->autosuspend_disabled || udev->autosuspend_delay < 0) + status = -EPERM; + else if (intf->pm_usage_cnt <= 0 && + !timer_pending(&udev->autosuspend.timer)) { + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + round_jiffies_up_relative( + udev->autosuspend_delay)); + } + } + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __func__, status, intf->pm_usage_cnt); +} +EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async); + +/** * usb_autopm_get_interface - increment a USB interface's PM-usage counter * @intf: the usb_interface whose counter should be incremented * @@ -1537,6 +1617,37 @@ int usb_autopm_get_interface(struct usb_interface *intf) EXPORT_SYMBOL_GPL(usb_autopm_get_interface); /** + * usb_autopm_get_interface_async - increment a USB interface's PM-usage counter + * @intf: the usb_interface whose counter should be incremented + * + * This routine does much the same thing as + * usb_autopm_get_interface(): it increments @intf's usage counter and + * queues an autoresume request if the result is > 0. The differences + * are that it does not acquire the device's pm_mutex (callers must + * handle all synchronization issues themselves), and it does not + * autoresume the device directly (it only queues a request). After a + * successful call, the device will generally not yet be resumed. + * + * This routine can run in atomic context. + */ +int usb_autopm_get_interface_async(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int status = 0; + + if (intf->condition == USB_INTERFACE_UNBOUND) + status = -ENODEV; + else if (udev->autoresume_disabled) + status = -EPERM; + else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED) + queue_work(ksuspend_usb_wq, &udev->autoresume); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __func__, status, intf->pm_usage_cnt); + return status; +} +EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async); + +/** * usb_autopm_set_interface - set a USB interface's autosuspend state * @intf: the usb_interface whose state should be set * @@ -1563,6 +1674,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_set_interface); void usb_autosuspend_work(struct work_struct *work) {} +void usb_autoresume_work(struct work_struct *work) +{} + #endif /* CONFIG_USB_SUSPEND */ /** @@ -1595,6 +1709,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) /** * usb_external_resume_device - external resume of a USB device and its interfaces * @udev: the usb_device to resume + * @msg: Power Management message describing this state transition * * This routine handles external resume requests: ones not generated * internally by a USB driver (autoresume) but rather coming from the user @@ -1603,13 +1718,13 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) * * The caller must hold @udev's device lock. */ -int usb_external_resume_device(struct usb_device *udev) +int usb_external_resume_device(struct usb_device *udev, pm_message_t msg) { int status; usb_pm_lock(udev); udev->auto_pm = 0; - status = usb_resume_both(udev); + status = usb_resume_both(udev, msg); udev->last_busy = jiffies; usb_pm_unlock(udev); if (status == 0) @@ -1622,7 +1737,7 @@ int usb_external_resume_device(struct usb_device *udev) return status; } -int usb_suspend(struct device *dev, pm_message_t message) +int usb_suspend(struct device *dev, pm_message_t msg) { struct usb_device *udev; @@ -1641,10 +1756,10 @@ int usb_suspend(struct device *dev, pm_message_t message) } udev->skip_sys_resume = 0; - return usb_external_suspend_device(udev, message); + return usb_external_suspend_device(udev, msg); } -int usb_resume(struct device *dev) +int usb_resume(struct device *dev, pm_message_t msg) { struct usb_device *udev; @@ -1656,7 +1771,7 @@ int usb_resume(struct device *dev) */ if (udev->skip_sys_resume) return 0; - return usb_external_resume_device(udev); + return usb_external_resume_device(udev, msg); } #endif /* CONFIG_PM */ diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 946fae43d622..e1710f260b4f 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -276,7 +276,7 @@ static void ep_device_release(struct device *dev) kfree(ep_dev); } -int usb_create_ep_files(struct device *parent, +int usb_create_ep_devs(struct device *parent, struct usb_host_endpoint *endpoint, struct usb_device *udev) { @@ -340,7 +340,7 @@ exit: return retval; } -void usb_remove_ep_files(struct usb_host_endpoint *endpoint) +void usb_remove_ep_devs(struct usb_host_endpoint *endpoint) { struct ep_device *ep_dev = endpoint->ep_dev; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 7e912f21fd36..30ecac3af15a 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -200,18 +200,18 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) * interfaces manually by doing a bus (or "global") suspend. */ if (!udev->parent) - rc = hcd_bus_suspend(udev); + rc = hcd_bus_suspend(udev, msg); /* Non-root devices don't need to do anything for FREEZE or PRETHAW */ else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) rc = 0; else - rc = usb_port_suspend(udev); + rc = usb_port_suspend(udev, msg); return rc; } -static int generic_resume(struct usb_device *udev) +static int generic_resume(struct usb_device *udev, pm_message_t msg) { int rc; @@ -221,9 +221,9 @@ static int generic_resume(struct usb_device *udev) * interfaces manually by doing a bus (or "global") resume. */ if (!udev->parent) - rc = hcd_bus_resume(udev); + rc = hcd_bus_resume(udev, msg); else - rc = usb_port_resume(udev); + rc = usb_port_resume(udev, msg); return rc; } diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 5b87ae7f0a6a..507741ed4482 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -128,6 +128,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } pci_set_master(dev); + device_set_wakeup_enable(&dev->dev, 1); retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) @@ -191,17 +192,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_remove); /** * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD * @dev: USB Host Controller being suspended - * @message: semantics in flux + * @message: Power Management message describing this state transition * - * Store this function in the HCD's struct pci_driver as suspend(). + * Store this function in the HCD's struct pci_driver as .suspend. */ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) { - struct usb_hcd *hcd; + struct usb_hcd *hcd = pci_get_drvdata(dev); int retval = 0; - int has_pci_pm; - - hcd = pci_get_drvdata(dev); + int wake, w; /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface @@ -212,8 +211,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) * otherwise the swsusp will save (and restore) garbage state. */ if (!(hcd->state == HC_STATE_SUSPENDED || - hcd->state == HC_STATE_HALT)) - return -EBUSY; + hcd->state == HC_STATE_HALT)) { + dev_warn(&dev->dev, "Root hub is not suspended\n"); + retval = -EBUSY; + goto done; + } + + /* We might already be suspended (runtime PM -- not yet written) */ + if (dev->current_state != PCI_D0) + goto done; if (hcd->driver->pci_suspend) { retval = hcd->driver->pci_suspend(hcd, message); @@ -221,49 +227,60 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) if (retval) goto done; } - synchronize_irq(dev->irq); - /* FIXME until the generic PM interfaces change a lot more, this - * can't use PCI D1 and D2 states. For example, the confusion - * between messages and states will need to vanish, and messages - * will need to provide a target system state again. - * - * It'll be important to learn characteristics of the target state, - * especially on embedded hardware where the HCD will often be in - * charge of an external VBUS power supply and one or more clocks. - * Some target system states will leave them active; others won't. - * (With PCI, that's often handled by platform BIOS code.) - */ + synchronize_irq(dev->irq); - /* even when the PCI layer rejects some of the PCI calls - * below, HCs can try global suspend and reduce DMA traffic. - * PM-sensitive HCDs may already have done this. + /* Don't fail on error to enable wakeup. We rely on pci code + * to reject requests the hardware can't implement, rather + * than coding the same thing. */ - has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + wake = (hcd->state == HC_STATE_SUSPENDED && + device_may_wakeup(&dev->dev)); + w = pci_wake_from_d3(dev, wake); + if (w < 0) + wake = w; + dev_dbg(&dev->dev, "wakeup: %d\n", wake); /* Downstream ports from this root hub should already be quiesced, so * there will be no DMA activity. Now we can shut down the upstream * link (except maybe for PME# resume signaling) and enter some PCI * low power state, if the hardware allows. */ - if (hcd->state == HC_STATE_SUSPENDED) { + pci_disable_device(dev); + done: + return retval; +} +EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); - /* no DMA or IRQs except when HC is active */ - if (dev->current_state == PCI_D0) { - pci_save_state(dev); - pci_disable_device(dev); - } +/** + * usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled + * @dev: USB Host Controller being suspended + * @message: Power Management message describing this state transition + * + * Store this function in the HCD's struct pci_driver as .suspend_late. + */ +int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message) +{ + int retval = 0; + int has_pci_pm; - if (message.event == PM_EVENT_FREEZE || - message.event == PM_EVENT_PRETHAW) { - dev_dbg(hcd->self.controller, "--> no state change\n"); - goto done; - } + /* We might already be suspended (runtime PM -- not yet written) */ + if (dev->current_state != PCI_D0) + goto done; - if (!has_pci_pm) { - dev_dbg(hcd->self.controller, "--> PCI D0/legacy\n"); - goto done; - } + pci_save_state(dev); + + /* Don't change state if we don't need to */ + if (message.event == PM_EVENT_FREEZE || + message.event == PM_EVENT_PRETHAW) { + dev_dbg(&dev->dev, "--> no state change\n"); + goto done; + } + + has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!has_pci_pm) { + dev_dbg(&dev->dev, "--> PCI D0 legacy\n"); + } else { /* NOTE: dev->current_state becomes nonzero only here, and * only for devices that support PCI PM. Also, exiting @@ -273,35 +290,16 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) retval = pci_set_power_state(dev, PCI_D3hot); suspend_report_result(pci_set_power_state, retval); if (retval == 0) { - int wake = device_can_wakeup(&hcd->self.root_hub->dev); - - wake = wake && device_may_wakeup(hcd->self.controller); - - dev_dbg(hcd->self.controller, "--> PCI D3%s\n", - wake ? "/wakeup" : ""); - - /* Ignore these return values. We rely on pci code to - * reject requests the hardware can't implement, rather - * than coding the same thing. - */ - (void) pci_enable_wake(dev, PCI_D3hot, wake); - (void) pci_enable_wake(dev, PCI_D3cold, wake); + dev_dbg(&dev->dev, "--> PCI D3\n"); } else { dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n", retval); - (void) usb_hcd_pci_resume(dev); + pci_restore_state(dev); } - - } else if (hcd->state != HC_STATE_HALT) { - dev_dbg(hcd->self.controller, "hcd state %d; not suspended\n", - hcd->state); - WARN_ON(1); - retval = -EINVAL; } -done: - if (retval == 0) { #ifdef CONFIG_PPC_PMAC + if (retval == 0) { /* Disable ASIC clocks for USB */ if (machine_is(powermac)) { struct device_node *of_node; @@ -311,30 +309,24 @@ done: pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); } -#endif } +#endif + done: return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); +EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late); /** - * usb_hcd_pci_resume - power management resume of a PCI-based HCD + * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled * @dev: USB Host Controller being resumed * - * Store this function in the HCD's struct pci_driver as resume(). + * Store this function in the HCD's struct pci_driver as .resume_early. */ -int usb_hcd_pci_resume(struct pci_dev *dev) +int usb_hcd_pci_resume_early(struct pci_dev *dev) { - struct usb_hcd *hcd; - int retval; - - hcd = pci_get_drvdata(dev); - if (hcd->state != HC_STATE_SUSPENDED) { - dev_dbg(hcd->self.controller, - "can't resume, not suspended!\n"); - return 0; - } + int retval = 0; + pci_power_t state = dev->current_state; #ifdef CONFIG_PPC_PMAC /* Reenable ASIC clocks for USB */ @@ -352,7 +344,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev) * calls "standby", "suspend to RAM", and so on). There are also * dirty cases when swsusp fakes a suspend in "shutdown" mode. */ - if (dev->current_state != PCI_D0) { + if (state != PCI_D0) { #ifdef DEBUG int pci_pm; u16 pmcr; @@ -364,8 +356,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev) /* Clean case: power to USB and to HC registers was * maintained; remote wakeup is easy. */ - dev_dbg(hcd->self.controller, "resume from PCI D%d\n", - pmcr); + dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr); } else { /* Clean: HC lost Vcc power, D0 uninitialized * + Vaux may have preserved port and transceiver @@ -376,32 +367,55 @@ int usb_hcd_pci_resume(struct pci_dev *dev) * + after BIOS init * + after Linux init (HCD statically linked) */ - dev_dbg(hcd->self.controller, - "PCI D0, from previous PCI D%d\n", - dev->current_state); + dev_dbg(&dev->dev, "resume from previous PCI D%d\n", + state); } #endif - /* yes, ignore these results too... */ - (void) pci_enable_wake(dev, dev->current_state, 0); - (void) pci_enable_wake(dev, PCI_D3cold, 0); + + retval = pci_set_power_state(dev, PCI_D0); } else { /* Same basic cases: clean (powered/not), dirty */ - dev_dbg(hcd->self.controller, "PCI legacy resume\n"); + dev_dbg(&dev->dev, "PCI legacy resume\n"); + } + + if (retval < 0) + dev_err(&dev->dev, "can't resume: %d\n", retval); + else + pci_restore_state(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early); + +/** + * usb_hcd_pci_resume - power management resume of a PCI-based HCD + * @dev: USB Host Controller being resumed + * + * Store this function in the HCD's struct pci_driver as .resume. + */ +int usb_hcd_pci_resume(struct pci_dev *dev) +{ + struct usb_hcd *hcd; + int retval; + + hcd = pci_get_drvdata(dev); + if (hcd->state != HC_STATE_SUSPENDED) { + dev_dbg(hcd->self.controller, + "can't resume, not suspended!\n"); + return 0; } - /* NOTE: the PCI API itself is asymmetric here. We don't need to - * pci_set_power_state(PCI_D0) since that's part of re-enabling; - * but that won't re-enable bus mastering. Yet pci_disable_device() - * explicitly disables bus mastering... - */ retval = pci_enable_device(dev); if (retval < 0) { - dev_err(hcd->self.controller, - "can't re-enable after resume, %d!\n", retval); + dev_err(&dev->dev, "can't re-enable after resume, %d!\n", + retval); return retval; } + pci_set_master(dev); - pci_restore_state(dev); + + /* yes, ignore this result too... */ + (void) pci_wake_from_d3(dev, 0); clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); @@ -413,7 +427,6 @@ int usb_hcd_pci_resume(struct pci_dev *dev) usb_hc_died(hcd); } } - return retval; } EXPORT_SYMBOL_GPL(usb_hcd_pci_resume); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e1b42626d04d..3c711db55d86 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1010,7 +1010,7 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) spin_lock(&hcd_urb_list_lock); /* Check that the URB isn't being killed */ - if (unlikely(urb->reject)) { + if (unlikely(atomic_read(&urb->reject))) { rc = -EPERM; goto done; } @@ -1340,7 +1340,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) INIT_LIST_HEAD(&urb->urb_list); atomic_dec(&urb->use_count); atomic_dec(&urb->dev->urbnum); - if (urb->reject) + if (atomic_read(&urb->reject)) wake_up(&usb_kill_urb_queue); usb_put_urb(urb); } @@ -1444,7 +1444,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) urb->status = status; urb->complete (urb); atomic_dec (&urb->use_count); - if (unlikely (urb->reject)) + if (unlikely(atomic_read(&urb->reject))) wake_up (&usb_kill_urb_queue); usb_put_urb (urb); } @@ -1573,14 +1573,14 @@ int usb_hcd_get_frame_number (struct usb_device *udev) #ifdef CONFIG_PM -int hcd_bus_suspend(struct usb_device *rhdev) +int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) { struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); int status; int old_state = hcd->state; dev_dbg(&rhdev->dev, "bus %s%s\n", - rhdev->auto_pm ? "auto-" : "", "suspend"); + (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); if (!hcd->driver->bus_suspend) { status = -ENOENT; } else { @@ -1598,14 +1598,14 @@ int hcd_bus_suspend(struct usb_device *rhdev) return status; } -int hcd_bus_resume(struct usb_device *rhdev) +int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) { struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); int status; int old_state = hcd->state; dev_dbg(&rhdev->dev, "usb %s%s\n", - rhdev->auto_pm ? "auto-" : "", "resume"); + (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) @@ -1638,7 +1638,7 @@ static void hcd_resume_work(struct work_struct *work) usb_lock_device(udev); usb_mark_last_busy(udev); - usb_external_resume_device(udev); + usb_external_resume_device(udev, PMSG_REMOTE_RESUME); usb_unlock_device(udev); } @@ -2028,7 +2028,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown); /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_MON) +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) struct usb_mon_operations *mon_ops; @@ -2064,4 +2064,4 @@ void usb_mon_deregister (void) } EXPORT_SYMBOL_GPL (usb_mon_deregister); -#endif /* CONFIG_USB_MON */ +#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */ diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 9465e70f4dd0..572d2cf46e8d 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -16,6 +16,8 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifndef __USB_CORE_HCD_H +#define __USB_CORE_HCD_H #ifdef __KERNEL__ @@ -254,7 +256,9 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev, extern void usb_hcd_pci_remove(struct pci_dev *dev); #ifdef CONFIG_PM -extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t state); +extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); +extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg); +extern int usb_hcd_pci_resume_early(struct pci_dev *dev); extern int usb_hcd_pci_resume(struct pci_dev *dev); #endif /* CONFIG_PM */ @@ -386,8 +390,8 @@ extern int usb_find_interface_driver(struct usb_device *dev, #ifdef CONFIG_PM extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd); extern void usb_root_hub_lost_power(struct usb_device *rhdev); -extern int hcd_bus_suspend(struct usb_device *rhdev); -extern int hcd_bus_resume(struct usb_device *rhdev); +extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg); +extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg); #else static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { @@ -419,7 +423,7 @@ static inline void usbfs_cleanup(void) { } /*-------------------------------------------------------------------------*/ -#if defined(CONFIG_USB_MON) +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) struct usb_mon_operations { void (*urb_submit)(struct usb_bus *bus, struct urb *urb); @@ -461,7 +465,7 @@ static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb, static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb, int status) {} -#endif /* CONFIG_USB_MON */ +#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */ /*-------------------------------------------------------------------------*/ @@ -490,3 +494,5 @@ extern struct rw_semaphore ehci_cf_port_reset_rwsem; extern unsigned long usb_hcds_loaded; #endif /* __KERNEL__ */ + +#endif /* __USB_CORE_HCD_H */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b19cbfcd51da..d5d0e40b1e2d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -107,7 +107,9 @@ MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs"); /* define initial 64-byte descriptor request timeout in milliseconds */ static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT; module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(initial_descriptor_timeout, "initial 64-byte descriptor request timeout in milliseconds (default 5000 - 5.0 seconds)"); +MODULE_PARM_DESC(initial_descriptor_timeout, + "initial 64-byte descriptor request timeout in milliseconds " + "(default 5000 - 5.0 seconds)"); /* * As of 2.6.10 we introduce a new USB device initialization scheme which @@ -1136,8 +1138,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) hdev = interface_to_usbdev(intf); if (hdev->level == MAX_TOPO_LEVEL) { - dev_err(&intf->dev, "Unsupported bus topology: " - "hub nested too deep\n"); + dev_err(&intf->dev, + "Unsupported bus topology: hub nested too deep\n"); return -E2BIG; } @@ -1374,8 +1376,9 @@ static void usb_stop_pm(struct usb_device *udev) usb_autosuspend_device(udev->parent); usb_pm_unlock(udev); - /* Stop any autosuspend requests already submitted */ - cancel_rearming_delayed_work(&udev->autosuspend); + /* Stop any autosuspend or autoresume requests already submitted */ + cancel_delayed_work_sync(&udev->autosuspend); + cancel_work_sync(&udev->autoresume); } #else @@ -1434,17 +1437,12 @@ void usb_disconnect(struct usb_device **pdev) usb_disable_device(udev, 0); usb_hcd_synchronize_unlinks(udev); + usb_remove_ep_devs(&udev->ep0); usb_unlock_device(udev); - /* Remove the device-specific files from sysfs. This must be - * done with udev unlocked, because some of the attribute - * routines try to acquire the device lock. - */ - usb_remove_sysfs_dev_files(udev); - /* Unregister the device. The device driver is responsible - * for removing the device files from usbfs and sysfs and for - * de-configuring the device. + * for de-configuring the device and invoking the remove-device + * notifier chain (used by usbfs and possibly others). */ device_del(&udev->dev); @@ -1476,8 +1474,8 @@ static void announce_device(struct usb_device *udev) dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); - dev_info(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, " - "SerialNumber=%d\n", + dev_info(&udev->dev, + "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", udev->descriptor.iManufacturer, udev->descriptor.iProduct, udev->descriptor.iSerialNumber); @@ -1542,7 +1540,7 @@ static int usb_configure_device_otg(struct usb_device *udev) * customize to match your product. */ dev_info(&udev->dev, - "can't set HNP mode; %d\n", + "can't set HNP mode: %d\n", err); bus->b_hnp_enable = 0; } @@ -1635,6 +1633,10 @@ int usb_new_device(struct usb_device *udev) { int err; + /* Increment the parent's count of unsuspended children */ + if (udev->parent) + usb_autoresume_device(udev->parent); + usb_detect_quirks(udev); /* Determine quirks */ err = usb_configure_device(udev); /* detect & probe dev/intfs */ if (err < 0) @@ -1643,13 +1645,12 @@ int usb_new_device(struct usb_device *udev) udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); - /* Increment the parent's count of unsuspended children */ - if (udev->parent) - usb_autoresume_device(udev->parent); + /* Tell the world! */ + announce_device(udev); /* Register the device. The device driver is responsible - * for adding the device files to sysfs and for configuring - * the device. + * for configuring the device and invoking the add-device + * notifier chain (used by usbfs and possibly others). */ err = device_add(&udev->dev); if (err) { @@ -1657,15 +1658,12 @@ int usb_new_device(struct usb_device *udev) goto fail; } - /* put device-specific files into sysfs */ - usb_create_sysfs_dev_files(udev); - - /* Tell the world! */ - announce_device(udev); + (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); return err; fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); + usb_stop_pm(udev); return err; } @@ -1982,7 +1980,7 @@ static int check_port_resume_type(struct usb_device *udev, * * Returns 0 on success, else negative errno. */ -int usb_port_suspend(struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; @@ -2021,7 +2019,7 @@ int usb_port_suspend(struct usb_device *udev) } else { /* device has up to 10 msec to fully suspend */ dev_dbg(&udev->dev, "usb %ssuspend\n", - udev->auto_pm ? "auto-" : ""); + (msg.event & PM_EVENT_AUTO ? "auto-" : "")); usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } @@ -2045,8 +2043,8 @@ static int finish_port_resume(struct usb_device *udev) u16 devstatus; /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "finish %sresume\n", - udev->reset_resume ? "reset-" : ""); + dev_dbg(&udev->dev, "%s\n", + udev->reset_resume ? "finish reset-resume" : "finish resume"); /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the @@ -2098,8 +2096,9 @@ static int finish_port_resume(struct usb_device *udev) NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) - dev_dbg(&udev->dev, "disable remote " - "wakeup, status %d\n", status); + dev_dbg(&udev->dev, + "disable remote wakeup, status %d\n", + status); } status = 0; } @@ -2140,7 +2139,7 @@ static int finish_port_resume(struct usb_device *udev) * * Returns 0 on success, else negative errno. */ -int usb_port_resume(struct usb_device *udev) +int usb_port_resume(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; @@ -2165,7 +2164,7 @@ int usb_port_resume(struct usb_device *udev) } else { /* drive resume for at least 20 msec */ dev_dbg(&udev->dev, "usb %sresume\n", - udev->auto_pm ? "auto-" : ""); + (msg.event & PM_EVENT_AUTO ? "auto-" : "")); msleep(25); /* Virtual root hubs can trigger on GET_PORT_STATUS to @@ -2206,7 +2205,7 @@ static int remote_wakeup(struct usb_device *udev) if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); usb_mark_last_busy(udev); - status = usb_external_resume_device(udev); + status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME); } return status; } @@ -2215,14 +2214,14 @@ static int remote_wakeup(struct usb_device *udev) /* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */ -int usb_port_suspend(struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { return 0; } /* However we may need to do a reset-resume */ -int usb_port_resume(struct usb_device *udev) +int usb_port_resume(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = hdev_to_hub(udev->parent); int port1 = udev->portnum; @@ -2262,7 +2261,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) udev = hdev->children [port1-1]; if (udev && udev->can_submit) { - if (!hdev->auto_pm) + if (!(msg.event & PM_EVENT_AUTO)) dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); return -EBUSY; @@ -2385,7 +2384,7 @@ void usb_ep0_reinit(struct usb_device *udev) { usb_disable_endpoint(udev, 0 + USB_DIR_IN); usb_disable_endpoint(udev, 0 + USB_DIR_OUT); - usb_enable_endpoint(udev, &udev->ep0); + usb_enable_endpoint(udev, &udev->ep0, true); } EXPORT_SYMBOL_GPL(usb_ep0_reinit); @@ -2582,9 +2581,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } if (r) { - dev_err(&udev->dev, "device descriptor " - "read/%s, error %d\n", - "64", r); + dev_err(&udev->dev, + "device descriptor read/64, error %d\n", + r); retval = -EMSGSIZE; continue; } @@ -2621,9 +2620,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, retval = usb_get_device_descriptor(udev, 8); if (retval < 8) { - dev_err(&udev->dev, "device descriptor " - "read/%s, error %d\n", - "8", retval); + dev_err(&udev->dev, + "device descriptor read/8, error %d\n", + retval); if (retval >= 0) retval = -EMSGSIZE; } else { @@ -2650,8 +2649,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); if (retval < (signed)sizeof(udev->descriptor)) { - dev_err(&udev->dev, "device descriptor read/%s, error %d\n", - "all", retval); + dev_err(&udev->dev, "device descriptor read/all, error %d\n", + retval); if (retval >= 0) retval = -ENOMSG; goto fail; @@ -2719,9 +2718,9 @@ hub_power_remaining (struct usb_hub *hub) else delta = 8; if (delta > hub->mA_per_port) - dev_warn(&udev->dev, "%dmA is over %umA budget " - "for port %d!\n", - delta, hub->mA_per_port, port1); + dev_warn(&udev->dev, + "%dmA is over %umA budget for port %d!\n", + delta, hub->mA_per_port, port1); remaining -= delta; } if (remaining < 0) { @@ -3517,3 +3516,46 @@ int usb_reset_device(struct usb_device *udev) return ret; } EXPORT_SYMBOL_GPL(usb_reset_device); + + +/** + * usb_queue_reset_device - Reset a USB device from an atomic context + * @iface: USB interface belonging to the device to reset + * + * This function can be used to reset a USB device from an atomic + * context, where usb_reset_device() won't work (as it blocks). + * + * Doing a reset via this method is functionally equivalent to calling + * usb_reset_device(), except for the fact that it is delayed to a + * workqueue. This means that any drivers bound to other interfaces + * might be unbound, as well as users from usbfs in user space. + * + * Corner cases: + * + * - Scheduling two resets at the same time from two different drivers + * attached to two different interfaces of the same device is + * possible; depending on how the driver attached to each interface + * handles ->pre_reset(), the second reset might happen or not. + * + * - If a driver is unbound and it had a pending reset, the reset will + * be cancelled. + * + * - This function can be called during .probe() or .disconnect() + * times. On return from .disconnect(), any pending resets will be + * cancelled. + * + * There is no no need to lock/unlock the @reset_ws as schedule_work() + * does its own. + * + * NOTE: We don't do any reference count tracking because it is not + * needed. The lifecycle of the work_struct is tied to the + * usb_interface. Before destroying the interface we cancel the + * work_struct, so the fact that work_struct is queued and or + * running means the interface (and thus, the device) exist and + * are referenced. + */ +void usb_queue_reset_device(struct usb_interface *iface) +{ + schedule_work(&iface->reset_ws); +} +EXPORT_SYMBOL_GPL(usb_queue_reset_device); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 6d1048faf08e..de51667dd64d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -18,6 +18,8 @@ #include "hcd.h" /* for usbcore internals */ #include "usb.h" +static void cancel_async_set_config(struct usb_device *udev); + struct api_context { struct completion done; int status; @@ -139,9 +141,9 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, dr->bRequestType = requesttype; dr->bRequest = request; - dr->wValue = cpu_to_le16p(&value); - dr->wIndex = cpu_to_le16p(&index); - dr->wLength = cpu_to_le16p(&size); + dr->wValue = cpu_to_le16(value); + dr->wIndex = cpu_to_le16(index); + dr->wLength = cpu_to_le16(size); /* dbg("usb_control_msg"); */ @@ -1004,6 +1006,34 @@ int usb_clear_halt(struct usb_device *dev, int pipe) } EXPORT_SYMBOL_GPL(usb_clear_halt); +static int create_intf_ep_devs(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + int i; + + if (intf->ep_devs_created || intf->unregistering) + return 0; + + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + (void) usb_create_ep_devs(&intf->dev, &alt->endpoint[i], udev); + intf->ep_devs_created = 1; + return 0; +} + +static void remove_intf_ep_devs(struct usb_interface *intf) +{ + struct usb_host_interface *alt = intf->cur_altsetting; + int i; + + if (!intf->ep_devs_created) + return; + + for (i = 0; i < alt->desc.bNumEndpoints; ++i) + usb_remove_ep_devs(&alt->endpoint[i]); + intf->ep_devs_created = 0; +} + /** * usb_disable_endpoint -- Disable an endpoint by address * @dev: the device whose endpoint is being disabled @@ -1092,7 +1122,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev_dbg(&dev->dev, "unregistering interface %s\n", dev_name(&interface->dev)); interface->unregistering = 1; - usb_remove_sysfs_intf_files(interface); + remove_intf_ep_devs(interface); device_del(&interface->dev); } @@ -1113,22 +1143,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) * usb_enable_endpoint - Enable an endpoint for USB communications * @dev: the device whose interface is being enabled * @ep: the endpoint + * @reset_toggle: flag to set the endpoint's toggle back to 0 * - * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers. + * Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers. * For control endpoints, both the input and output sides are handled. */ -void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) +void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, + bool reset_toggle) { int epnum = usb_endpoint_num(&ep->desc); int is_out = usb_endpoint_dir_out(&ep->desc); int is_control = usb_endpoint_xfer_control(&ep->desc); if (is_out || is_control) { - usb_settoggle(dev, epnum, 1, 0); + if (reset_toggle) + usb_settoggle(dev, epnum, 1, 0); dev->ep_out[epnum] = ep; } if (!is_out || is_control) { - usb_settoggle(dev, epnum, 0, 0); + if (reset_toggle) + usb_settoggle(dev, epnum, 0, 0); dev->ep_in[epnum] = ep; } ep->enabled = 1; @@ -1138,17 +1172,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep) * usb_enable_interface - Enable all the endpoints for an interface * @dev: the device whose interface is being enabled * @intf: pointer to the interface descriptor + * @reset_toggles: flag to set the endpoints' toggles back to 0 * * Enables all the endpoints for the interface's current altsetting. */ -static void usb_enable_interface(struct usb_device *dev, - struct usb_interface *intf) +void usb_enable_interface(struct usb_device *dev, + struct usb_interface *intf, bool reset_toggles) { struct usb_host_interface *alt = intf->cur_altsetting; int i; for (i = 0; i < alt->desc.bNumEndpoints; ++i) - usb_enable_endpoint(dev, &alt->endpoint[i]); + usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles); } /** @@ -1235,8 +1270,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) */ /* prevent submissions using previous endpoint settings */ - if (iface->cur_altsetting != alt) + if (iface->cur_altsetting != alt) { + remove_intf_ep_devs(iface); usb_remove_sysfs_intf_files(iface); + } usb_disable_interface(dev, iface); iface->cur_altsetting = alt; @@ -1271,10 +1308,11 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * during the SETUP stage - hence EP0 toggles are "don't care" here. * (Likewise, EP0 never "halts" on well designed devices.) */ - usb_enable_interface(dev, iface); - if (device_is_registered(&iface->dev)) + usb_enable_interface(dev, iface, true); + if (device_is_registered(&iface->dev)) { usb_create_sysfs_intf_files(iface); - + create_intf_ep_devs(iface); + } return 0; } EXPORT_SYMBOL_GPL(usb_set_interface); @@ -1334,7 +1372,6 @@ int usb_reset_configuration(struct usb_device *dev) struct usb_interface *intf = config->interface[i]; struct usb_host_interface *alt; - usb_remove_sysfs_intf_files(intf); alt = usb_altnum_to_altsetting(intf, 0); /* No altsetting 0? We'll assume the first altsetting. @@ -1345,10 +1382,16 @@ int usb_reset_configuration(struct usb_device *dev) if (!alt) alt = &intf->altsetting[0]; + if (alt != intf->cur_altsetting) { + remove_intf_ep_devs(intf); + usb_remove_sysfs_intf_files(intf); + } intf->cur_altsetting = alt; - usb_enable_interface(dev, intf); - if (device_is_registered(&intf->dev)) + usb_enable_interface(dev, intf, true); + if (device_is_registered(&intf->dev)) { usb_create_sysfs_intf_files(intf); + create_intf_ep_devs(intf); + } } return 0; } @@ -1441,6 +1484,46 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, return retval; } + +/* + * Internal function to queue a device reset + * + * This is initialized into the workstruct in 'struct + * usb_device->reset_ws' that is launched by + * message.c:usb_set_configuration() when initializing each 'struct + * usb_interface'. + * + * It is safe to get the USB device without reference counts because + * the life cycle of @iface is bound to the life cycle of @udev. Then, + * this function will be ran only if @iface is alive (and before + * freeing it any scheduled instances of it will have been cancelled). + * + * We need to set a flag (usb_dev->reset_running) because when we call + * the reset, the interfaces might be unbound. The current interface + * cannot try to remove the queued work as it would cause a deadlock + * (you cannot remove your work from within your executing + * workqueue). This flag lets it know, so that + * usb_cancel_queued_reset() doesn't try to do it. + * + * See usb_queue_reset_device() for more details + */ +void __usb_queue_reset_device(struct work_struct *ws) +{ + int rc; + struct usb_interface *iface = + container_of(ws, struct usb_interface, reset_ws); + struct usb_device *udev = interface_to_usbdev(iface); + + rc = usb_lock_device_for_reset(udev, iface); + if (rc >= 0) { + iface->reset_running = 1; + usb_reset_device(udev); + iface->reset_running = 0; + usb_unlock_device(udev); + } +} + + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1560,6 +1643,9 @@ free_interfaces: if (dev->state != USB_STATE_ADDRESS) usb_disable_device(dev, 1); /* Skip ep0 */ + /* Get rid of pending async Set-Config requests for this device */ + cancel_async_set_config(dev); + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); @@ -1604,13 +1690,14 @@ free_interfaces: alt = &intf->altsetting[0]; intf->cur_altsetting = alt; - usb_enable_interface(dev, intf); + usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; intf->dev.dma_mask = dev->dev.dma_mask; + INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); device_initialize(&intf->dev); mark_quiesced(intf); dev_set_name(&intf->dev, "%d-%s:%d.%d", @@ -1641,17 +1728,21 @@ free_interfaces: dev_name(&intf->dev), ret); continue; } - usb_create_sysfs_intf_files(intf); + create_intf_ep_devs(intf); } usb_autosuspend_device(dev); return 0; } +static LIST_HEAD(set_config_list); +static DEFINE_SPINLOCK(set_config_lock); + struct set_config_request { struct usb_device *udev; int config; struct work_struct work; + struct list_head node; }; /* Worker routine for usb_driver_set_configuration() */ @@ -1659,14 +1750,35 @@ static void driver_set_config_work(struct work_struct *work) { struct set_config_request *req = container_of(work, struct set_config_request, work); + struct usb_device *udev = req->udev; + + usb_lock_device(udev); + spin_lock(&set_config_lock); + list_del(&req->node); + spin_unlock(&set_config_lock); - usb_lock_device(req->udev); - usb_set_configuration(req->udev, req->config); - usb_unlock_device(req->udev); - usb_put_dev(req->udev); + if (req->config >= -1) /* Is req still valid? */ + usb_set_configuration(udev, req->config); + usb_unlock_device(udev); + usb_put_dev(udev); kfree(req); } +/* Cancel pending Set-Config requests for a device whose configuration + * was just changed + */ +static void cancel_async_set_config(struct usb_device *udev) +{ + struct set_config_request *req; + + spin_lock(&set_config_lock); + list_for_each_entry(req, &set_config_list, node) { + if (req->udev == udev) + req->config = -999; /* Mark as cancelled */ + } + spin_unlock(&set_config_lock); +} + /** * usb_driver_set_configuration - Provide a way for drivers to change device configurations * @udev: the device whose configuration is being updated @@ -1698,6 +1810,10 @@ int usb_driver_set_configuration(struct usb_device *udev, int config) req->config = config; INIT_WORK(&req->work, driver_set_config_work); + spin_lock(&set_config_lock); + list_add(&req->node, &set_config_list); + spin_unlock(&set_config_lock); + usb_get_dev(udev); schedule_work(&req->work); return 0; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 4fb65fdc9dc3..4cc2456ef3be 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -359,19 +359,19 @@ set_level(struct device *dev, struct device_attribute *attr, strncmp(buf, on_string, len) == 0) { udev->autosuspend_disabled = 1; udev->autoresume_disabled = 0; - rc = usb_external_resume_device(udev); + rc = usb_external_resume_device(udev, PMSG_USER_RESUME); } else if (len == sizeof auto_string - 1 && strncmp(buf, auto_string, len) == 0) { udev->autosuspend_disabled = 0; udev->autoresume_disabled = 0; - rc = usb_external_resume_device(udev); + rc = usb_external_resume_device(udev, PMSG_USER_RESUME); } else if (len == sizeof suspend_string - 1 && strncmp(buf, suspend_string, len) == 0) { udev->autosuspend_disabled = 0; udev->autoresume_disabled = 1; - rc = usb_external_suspend_device(udev, PMSG_SUSPEND); + rc = usb_external_suspend_device(udev, PMSG_USER_SUSPEND); } else rc = -EINVAL; @@ -629,9 +629,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) struct device *dev = &udev->dev; int retval; - /* Unforunately these attributes cannot be created before - * the uevent is broadcast. - */ retval = device_create_bin_file(dev, &dev_bin_attr_descriptors); if (retval) goto error; @@ -643,11 +640,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) retval = add_power_attributes(dev); if (retval) goto error; - - retval = usb_create_ep_files(dev, &udev->ep0, udev); - if (retval) - goto error; - return 0; + return retval; error: usb_remove_sysfs_dev_files(udev); return retval; @@ -657,7 +650,6 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) { struct device *dev = &udev->dev; - usb_remove_ep_files(&udev->ep0); remove_power_attributes(dev); remove_persist_attributes(dev); device_remove_bin_file(dev, &dev_bin_attr_descriptors); @@ -812,28 +804,6 @@ struct attribute_group *usb_interface_groups[] = { NULL }; -static inline void usb_create_intf_ep_files(struct usb_interface *intf, - struct usb_device *udev) -{ - struct usb_host_interface *iface_desc; - int i; - - iface_desc = intf->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) - usb_create_ep_files(&intf->dev, &iface_desc->endpoint[i], - udev); -} - -static inline void usb_remove_intf_ep_files(struct usb_interface *intf) -{ - struct usb_host_interface *iface_desc; - int i; - - iface_desc = intf->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) - usb_remove_ep_files(&iface_desc->endpoint[i]); -} - int usb_create_sysfs_intf_files(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); @@ -843,26 +813,19 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf) if (intf->sysfs_files_created || intf->unregistering) return 0; - /* The interface string may be present in some altsettings - * and missing in others. Hence its attribute cannot be created - * before the uevent is broadcast. - */ if (alt->string == NULL) alt->string = usb_cache_string(udev, alt->desc.iInterface); if (alt->string) retval = device_create_file(&intf->dev, &dev_attr_interface); - usb_create_intf_ep_files(intf, udev); intf->sysfs_files_created = 1; return 0; } void usb_remove_sysfs_intf_files(struct usb_interface *intf) { - struct device *dev = &intf->dev; - if (!intf->sysfs_files_created) return; - usb_remove_intf_ep_files(intf); - device_remove_file(dev, &dev_attr_interface); + + device_remove_file(&intf->dev, &dev_attr_interface); intf->sysfs_files_created = 0; } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 1f68af9db3f7..58bc5e3c2560 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -10,7 +10,6 @@ #define to_urb(d) container_of(d, struct urb, kref) -static DEFINE_SPINLOCK(usb_reject_lock); static void urb_destroy(struct kref *kref) { @@ -131,9 +130,7 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) urb->anchor = anchor; if (unlikely(anchor->poisoned)) { - spin_lock(&usb_reject_lock); - urb->reject++; - spin_unlock(&usb_reject_lock); + atomic_inc(&urb->reject); } spin_unlock_irqrestore(&anchor->lock, flags); @@ -565,16 +562,12 @@ void usb_kill_urb(struct urb *urb) might_sleep(); if (!(urb && urb->dev && urb->ep)) return; - spin_lock_irq(&usb_reject_lock); - ++urb->reject; - spin_unlock_irq(&usb_reject_lock); + atomic_inc(&urb->reject); usb_hcd_unlink_urb(urb, -ENOENT); wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); - spin_lock_irq(&usb_reject_lock); - --urb->reject; - spin_unlock_irq(&usb_reject_lock); + atomic_dec(&urb->reject); } EXPORT_SYMBOL_GPL(usb_kill_urb); @@ -606,9 +599,7 @@ void usb_poison_urb(struct urb *urb) might_sleep(); if (!(urb && urb->dev && urb->ep)) return; - spin_lock_irq(&usb_reject_lock); - ++urb->reject; - spin_unlock_irq(&usb_reject_lock); + atomic_inc(&urb->reject); usb_hcd_unlink_urb(urb, -ENOENT); wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); @@ -617,14 +608,10 @@ EXPORT_SYMBOL_GPL(usb_poison_urb); void usb_unpoison_urb(struct urb *urb) { - unsigned long flags; - if (!urb) return; - spin_lock_irqsave(&usb_reject_lock, flags); - --urb->reject; - spin_unlock_irqrestore(&usb_reject_lock, flags); + atomic_dec(&urb->reject); } EXPORT_SYMBOL_GPL(usb_unpoison_urb); @@ -692,6 +679,26 @@ void usb_poison_anchored_urbs(struct usb_anchor *anchor) EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs); /** + * usb_unpoison_anchored_urbs - let an anchor be used successfully again + * @anchor: anchor the requests are bound to + * + * Reverses the effect of usb_poison_anchored_urbs + * the anchor can be used normally after it returns + */ +void usb_unpoison_anchored_urbs(struct usb_anchor *anchor) +{ + unsigned long flags; + struct urb *lazarus; + + spin_lock_irqsave(&anchor->lock, flags); + list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) { + usb_unpoison_urb(lazarus); + } + anchor->poisoned = 0; + spin_unlock_irqrestore(&anchor->lock, flags); +} +EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs); +/** * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse * @anchor: anchor the requests are bound to * diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 399e15fc5052..dcfc072630c1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -253,7 +253,7 @@ static int usb_dev_prepare(struct device *dev) static void usb_dev_complete(struct device *dev) { /* Currently used only for rebinding interfaces */ - usb_resume(dev); /* Implement eventually? */ + usb_resume(dev, PMSG_RESUME); /* Message event is meaningless */ } static int usb_dev_suspend(struct device *dev) @@ -263,7 +263,7 @@ static int usb_dev_suspend(struct device *dev) static int usb_dev_resume(struct device *dev) { - return usb_resume(dev); + return usb_resume(dev, PMSG_RESUME); } static int usb_dev_freeze(struct device *dev) @@ -273,7 +273,7 @@ static int usb_dev_freeze(struct device *dev) static int usb_dev_thaw(struct device *dev) { - return usb_resume(dev); + return usb_resume(dev, PMSG_THAW); } static int usb_dev_poweroff(struct device *dev) @@ -283,7 +283,7 @@ static int usb_dev_poweroff(struct device *dev) static int usb_dev_restore(struct device *dev) { - return usb_resume(dev); + return usb_resume(dev, PMSG_RESTORE); } static struct dev_pm_ops usb_device_pm_ops = { @@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; /* ep0 maxpacket comes later, from device descriptor */ - usb_enable_endpoint(dev, &dev->ep0); + usb_enable_endpoint(dev, &dev->ep0, true); dev->can_submit = 1; /* Save readable and stable topology id, distinguishing devices @@ -402,6 +402,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, #ifdef CONFIG_PM mutex_init(&dev->pm_mutex); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); + INIT_WORK(&dev->autoresume, usb_autoresume_work); dev->autosuspend_delay = usb_autosuspend_delay * HZ; dev->connect_time = jiffies; dev->active_duration = -jiffies; @@ -513,10 +514,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf); * disconnect; in some drivers (such as usb-storage) the disconnect() * or suspend() method will block waiting for a device reset to complete. * - * Returns a negative error code for failure, otherwise 1 or 0 to indicate - * that the device will or will not have to be unlocked. (0 can be - * returned when an interface is given and is BINDING, because in that - * case the driver already owns the device lock.) + * Returns a negative error code for failure, otherwise 0. */ int usb_lock_device_for_reset(struct usb_device *udev, const struct usb_interface *iface) @@ -527,16 +525,9 @@ int usb_lock_device_for_reset(struct usb_device *udev, return -ENODEV; if (udev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - if (iface) { - switch (iface->condition) { - case USB_INTERFACE_BINDING: - return 0; - case USB_INTERFACE_BOUND: - break; - default: - return -EINTR; - } - } + if (iface && (iface->condition == USB_INTERFACE_UNBINDING || + iface->condition == USB_INTERFACE_UNBOUND)) + return -EINTR; while (usb_trylock_device(udev) != 0) { @@ -550,10 +541,11 @@ int usb_lock_device_for_reset(struct usb_device *udev, return -ENODEV; if (udev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; - if (iface && iface->condition != USB_INTERFACE_BOUND) + if (iface && (iface->condition == USB_INTERFACE_UNBINDING || + iface->condition == USB_INTERFACE_UNBOUND)) return -EINTR; } - return 1; + return 0; } EXPORT_SYMBOL_GPL(usb_lock_device_for_reset); @@ -962,8 +954,12 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in, } EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg); -/* format to disable USB on kernel command line is: nousb */ -__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444); +/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */ +#ifdef MODULE +module_param(nousb, bool, 0444); +#else +core_param(nousb, nousb, bool, 0444); +#endif /* * for external read access to <nousb> @@ -975,6 +971,37 @@ int usb_disabled(void) EXPORT_SYMBOL_GPL(usb_disabled); /* + * Notifications of device and interface registration + */ +static int usb_bus_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct device *dev = data; + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + if (dev->type == &usb_device_type) + (void) usb_create_sysfs_dev_files(to_usb_device(dev)); + else if (dev->type == &usb_if_device_type) + (void) usb_create_sysfs_intf_files( + to_usb_interface(dev)); + break; + + case BUS_NOTIFY_DEL_DEVICE: + if (dev->type == &usb_device_type) + usb_remove_sysfs_dev_files(to_usb_device(dev)); + else if (dev->type == &usb_if_device_type) + usb_remove_sysfs_intf_files(to_usb_interface(dev)); + break; + } + return 0; +} + +static struct notifier_block usb_bus_nb = { + .notifier_call = usb_bus_notify, +}; + +/* * Init */ static int __init usb_init(void) @@ -991,6 +1018,9 @@ static int __init usb_init(void) retval = bus_register(&usb_bus_type); if (retval) goto bus_register_failed; + retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb); + if (retval) + goto bus_notifier_failed; retval = usb_host_init(); if (retval) goto host_init_failed; @@ -1025,6 +1055,8 @@ driver_register_failed: major_init_failed: usb_host_cleanup(); host_init_failed: + bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); +bus_notifier_failed: bus_unregister(&usb_bus_type); bus_register_failed: ksuspend_usb_cleanup(); @@ -1048,6 +1080,7 @@ static void __exit usb_exit(void) usb_devio_cleanup(); usb_hub_cleanup(); usb_host_cleanup(); + bus_unregister_notifier(&usb_bus_type, &usb_bus_nb); bus_unregister(&usb_bus_type); ksuspend_usb_cleanup(); } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 9a1a45ac3add..386177867a8a 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,16 +1,20 @@ +#include <linux/pm.h> + /* Functions local to drivers/usb/core/ */ extern int usb_create_sysfs_dev_files(struct usb_device *dev); extern void usb_remove_sysfs_dev_files(struct usb_device *dev); extern int usb_create_sysfs_intf_files(struct usb_interface *intf); extern void usb_remove_sysfs_intf_files(struct usb_interface *intf); -extern int usb_create_ep_files(struct device *parent, +extern int usb_create_ep_devs(struct device *parent, struct usb_host_endpoint *endpoint, struct usb_device *udev); -extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint); +extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint); extern void usb_enable_endpoint(struct usb_device *dev, - struct usb_host_endpoint *ep); + struct usb_host_endpoint *ep, bool reset_toggle); +extern void usb_enable_interface(struct usb_device *dev, + struct usb_interface *intf, bool reset_toggles); extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); extern void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf); @@ -42,14 +46,16 @@ extern void usb_host_cleanup(void); #ifdef CONFIG_PM extern int usb_suspend(struct device *dev, pm_message_t msg); -extern int usb_resume(struct device *dev); +extern int usb_resume(struct device *dev, pm_message_t msg); extern void usb_autosuspend_work(struct work_struct *work); -extern int usb_port_suspend(struct usb_device *dev); -extern int usb_port_resume(struct usb_device *dev); +extern void usb_autoresume_work(struct work_struct *work); +extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg); +extern int usb_port_resume(struct usb_device *dev, pm_message_t msg); extern int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg); -extern int usb_external_resume_device(struct usb_device *udev); +extern int usb_external_resume_device(struct usb_device *udev, + pm_message_t msg); static inline void usb_pm_lock(struct usb_device *udev) { @@ -63,12 +69,12 @@ static inline void usb_pm_unlock(struct usb_device *udev) #else -static inline int usb_port_suspend(struct usb_device *udev) +static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { return 0; } -static inline int usb_port_resume(struct usb_device *udev) +static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg) { return 0; } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index dd4cd5a51370..3219d137340a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -297,13 +297,34 @@ config USB_S3C2410_DEBUG # musb builds in ../musb along with host support config USB_GADGET_MUSB_HDRC - boolean "Inventra HDRC USB Peripheral (TI, ...)" + boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)" depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) select USB_GADGET_DUALSPEED select USB_GADGET_SELECTED help This OTG-capable silicon IP is used in dual designs including - the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. + the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin + +config USB_GADGET_IMX + boolean "Freescale IMX USB Peripheral Controller" + depends on ARCH_MX1 + help + Freescale's IMX series include an integrated full speed + USB 1.1 device controller. The controller in the IMX series + is register-compatible. + + It has Six fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "imx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_IMX + tristate + depends on USB_GADGET_IMX + default USB_GADGET + select USB_GADGET_SELECTED config USB_GADGET_M66592 boolean "Renesas M66592 USB Peripheral Controller" @@ -377,6 +398,24 @@ config USB_FSL_QE default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_CI13XXX + boolean "MIPS USB CI13xxx" + depends on PCI + select USB_GADGET_DUALSPEED + help + MIPS USB IP core family device controller + Currently it only supports IP part number CI13412 + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "ci13xxx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_CI13XXX + tristate + depends on USB_GADGET_CI13XXX + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_NET2280 boolean "NetChip 228x" depends on PCI diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index bd4041b47dce..39a51d746cb7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o +obj-$(CONFIG_USB_IMX) += imx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o @@ -19,6 +20,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o +obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c new file mode 100644 index 000000000000..bebf911c7e5f --- /dev/null +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -0,0 +1,2830 @@ +/* + * ci13xxx_udc.c - MIPS USB IP core family device controller + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + */ + +/* + * Description: MIPS USB IP core family device controller + * Currently it only supports IP part number CI13412 + * + * This driver is composed of several blocks: + * - HW: hardware interface + * - DBG: debug facilities (optional) + * - UTIL: utilities + * - ISR: interrupts handling + * - ENDPT: endpoint operations (Gadget API) + * - GADGET: gadget operations (Gadget API) + * - BUS: bus glue code, bus abstraction layer + * - PCI: PCI core interface and PCI resources (interrupts, memory...) + * + * Compile Options + * - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities + * - STALL_IN: non-empty bulk-in pipes cannot be halted + * if defined mass storage compliance succeeds but with warnings + * => case 4: Hi > Dn + * => case 5: Hi > Di + * => case 8: Hi <> Do + * if undefined usbtest 13 fails + * - TRACE: enable function tracing (depends on DEBUG) + * + * Main Features + * - Chapter 9 & Mass Storage Compliance with Gadget File Storage + * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined) + * - Normal & LPM support + * + * USBTEST Report + * - OK: 0-12, 13 (STALL_IN defined) & 14 + * - Not Supported: 15 & 16 (ISO) + * + * TODO List + * - OTG + * - Isochronous & Interrupt Traffic + * - Handle requests which spawns into several TDs + * - GET_STATUS(device) - always reports 0 + * - Gadget API (majority of optional features) + * - Suspend & Remote Wakeup + */ +#include <linux/device.h> +#include <linux/dmapool.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "ci13xxx_udc.h" + + +/****************************************************************************** + * DEFINE + *****************************************************************************/ +/* ctrl register bank access */ +static DEFINE_SPINLOCK(udc_lock); + +/* driver name */ +#define UDC_DRIVER_NAME "ci13xxx_udc" + +/* control endpoint description */ +static const struct usb_endpoint_descriptor +ctrl_endpt_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(CTRL_PAYLOAD_MAX), +}; + +/* UDC descriptor */ +static struct ci13xxx *_udc; + +/* Interrupt statistics */ +#define ISR_MASK 0x1F +static struct { + u32 test; + u32 ui; + u32 uei; + u32 pci; + u32 uri; + u32 sli; + u32 none; + struct { + u32 cnt; + u32 buf[ISR_MASK+1]; + u32 idx; + } hndl; +} isr_statistics; + +/** + * ffs_nr: find first (least significant) bit set + * @x: the word to search + * + * This function returns bit number (instead of position) + */ +static int ffs_nr(u32 x) +{ + int n = ffs(x); + + return n ? n-1 : 32; +} + +/****************************************************************************** + * HW block + *****************************************************************************/ +/* register bank descriptor */ +static struct { + unsigned lpm; /* is LPM? */ + void __iomem *abs; /* bus map offset */ + void __iomem *cap; /* bus map offset + CAP offset + CAP data */ + size_t size; /* bank size */ +} hw_bank; + +/* UDC register map */ +#define ABS_CAPLENGTH (0x100UL) +#define ABS_HCCPARAMS (0x108UL) +#define ABS_DCCPARAMS (0x124UL) +#define ABS_TESTMODE (hw_bank.lpm ? 0x0FCUL : 0x138UL) +/* offset to CAPLENTGH (addr + data) */ +#define CAP_USBCMD (0x000UL) +#define CAP_USBSTS (0x004UL) +#define CAP_USBINTR (0x008UL) +#define CAP_DEVICEADDR (0x014UL) +#define CAP_ENDPTLISTADDR (0x018UL) +#define CAP_PORTSC (0x044UL) +#define CAP_DEVLC (0x0B4UL) +#define CAP_USBMODE (hw_bank.lpm ? 0x0C8UL : 0x068UL) +#define CAP_ENDPTSETUPSTAT (hw_bank.lpm ? 0x0D8UL : 0x06CUL) +#define CAP_ENDPTPRIME (hw_bank.lpm ? 0x0DCUL : 0x070UL) +#define CAP_ENDPTFLUSH (hw_bank.lpm ? 0x0E0UL : 0x074UL) +#define CAP_ENDPTSTAT (hw_bank.lpm ? 0x0E4UL : 0x078UL) +#define CAP_ENDPTCOMPLETE (hw_bank.lpm ? 0x0E8UL : 0x07CUL) +#define CAP_ENDPTCTRL (hw_bank.lpm ? 0x0ECUL : 0x080UL) +#define CAP_LAST (hw_bank.lpm ? 0x12CUL : 0x0C0UL) + +/* maximum number of enpoints: valid only after hw_device_reset() */ +static unsigned hw_ep_max; + +/** + * hw_ep_bit: calculates the bit number + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns bit number + */ +static inline int hw_ep_bit(int num, int dir) +{ + return num + (dir ? 16 : 0); +} + +/** + * hw_aread: reads from register bitfield + * @addr: address relative to bus map + * @mask: bitfield mask + * + * This function returns register bitfield data + */ +static u32 hw_aread(u32 addr, u32 mask) +{ + return ioread32(addr + hw_bank.abs) & mask; +} + +/** + * hw_awrite: writes to register bitfield + * @addr: address relative to bus map + * @mask: bitfield mask + * @data: new data + */ +static void hw_awrite(u32 addr, u32 mask, u32 data) +{ + iowrite32(hw_aread(addr, ~mask) | (data & mask), + addr + hw_bank.abs); +} + +/** + * hw_cread: reads from register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * + * This function returns register bitfield data + */ +static u32 hw_cread(u32 addr, u32 mask) +{ + return ioread32(addr + hw_bank.cap) & mask; +} + +/** + * hw_cwrite: writes to register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * @data: new data + */ +static void hw_cwrite(u32 addr, u32 mask, u32 data) +{ + iowrite32(hw_cread(addr, ~mask) | (data & mask), + addr + hw_bank.cap); +} + +/** + * hw_ctest_and_clear: tests & clears register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * + * This function returns register bitfield data + */ +static u32 hw_ctest_and_clear(u32 addr, u32 mask) +{ + u32 reg = hw_cread(addr, mask); + + iowrite32(reg, addr + hw_bank.cap); + return reg; +} + +/** + * hw_ctest_and_write: tests & writes register bitfield + * @addr: address relative to CAP offset plus content + * @mask: bitfield mask + * @data: new data + * + * This function returns register bitfield data + */ +static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data) +{ + u32 reg = hw_cread(addr, ~0); + + iowrite32((reg & ~mask) | (data & mask), addr + hw_bank.cap); + return (reg & mask) >> ffs_nr(mask); +} + +/** + * hw_device_reset: resets chip (execute without interruption) + * @base: register base address + * + * This function returns an error code + */ +static int hw_device_reset(void __iomem *base) +{ + u32 reg; + + /* bank is a module variable */ + hw_bank.abs = base; + + hw_bank.cap = hw_bank.abs; + hw_bank.cap += ABS_CAPLENGTH; + hw_bank.cap += ioread8(hw_bank.cap); + + reg = hw_aread(ABS_HCCPARAMS, HCCPARAMS_LEN) >> ffs_nr(HCCPARAMS_LEN); + hw_bank.lpm = reg; + hw_bank.size = hw_bank.cap - hw_bank.abs; + hw_bank.size += CAP_LAST; + hw_bank.size /= sizeof(u32); + + /* should flush & stop before reset */ + hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); + hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); + + hw_cwrite(CAP_USBCMD, USBCMD_RST, USBCMD_RST); + while (hw_cread(CAP_USBCMD, USBCMD_RST)) + udelay(10); /* not RTOS friendly */ + + /* USBMODE should be configured step by step */ + hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); + hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE); + hw_cwrite(CAP_USBMODE, USBMODE_SLOM, USBMODE_SLOM); /* HW >= 2.3 */ + + if (hw_cread(CAP_USBMODE, USBMODE_CM) != USBMODE_CM_DEVICE) { + pr_err("cannot enter in device mode"); + pr_err("lpm = %i", hw_bank.lpm); + return -ENODEV; + } + + reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN); + if (reg == 0 || reg > ENDPT_MAX) + return -ENODEV; + + hw_ep_max = reg; /* cache hw ENDPT_MAX */ + + /* setup lock mode ? */ + + /* ENDPTSETUPSTAT is '0' by default */ + + /* HCSPARAMS.bf.ppc SHOULD BE zero for device */ + + return 0; +} + +/** + * hw_device_state: enables/disables interrupts & starts/stops device (execute + * without interruption) + * @dma: 0 => disable, !0 => enable and set dma engine + * + * This function returns an error code + */ +static int hw_device_state(u32 dma) +{ + if (dma) { + hw_cwrite(CAP_ENDPTLISTADDR, ~0, dma); + /* interrupt, error, port change, reset, sleep/suspend */ + hw_cwrite(CAP_USBINTR, ~0, + USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); + hw_cwrite(CAP_USBCMD, USBCMD_RS, USBCMD_RS); + } else { + hw_cwrite(CAP_USBCMD, USBCMD_RS, 0); + hw_cwrite(CAP_USBINTR, ~0, 0); + } + return 0; +} + +/** + * hw_ep_flush: flush endpoint fifo (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns an error code + */ +static int hw_ep_flush(int num, int dir) +{ + int n = hw_ep_bit(num, dir); + + do { + /* flush any pending transfer */ + hw_cwrite(CAP_ENDPTFLUSH, BIT(n), BIT(n)); + while (hw_cread(CAP_ENDPTFLUSH, BIT(n))) + cpu_relax(); + } while (hw_cread(CAP_ENDPTSTAT, BIT(n))); + + return 0; +} + +/** + * hw_ep_disable: disables endpoint (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns an error code + */ +static int hw_ep_disable(int num, int dir) +{ + hw_ep_flush(num, dir); + hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), + dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0); + return 0; +} + +/** + * hw_ep_enable: enables endpoint (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * @type: endpoint type + * + * This function returns an error code + */ +static int hw_ep_enable(int num, int dir, int type) +{ + u32 mask, data; + + if (dir) { + mask = ENDPTCTRL_TXT; /* type */ + data = type << ffs_nr(mask); + + mask |= ENDPTCTRL_TXS; /* unstall */ + mask |= ENDPTCTRL_TXR; /* reset data toggle */ + data |= ENDPTCTRL_TXR; + mask |= ENDPTCTRL_TXE; /* enable */ + data |= ENDPTCTRL_TXE; + } else { + mask = ENDPTCTRL_RXT; /* type */ + data = type << ffs_nr(mask); + + mask |= ENDPTCTRL_RXS; /* unstall */ + mask |= ENDPTCTRL_RXR; /* reset data toggle */ + data |= ENDPTCTRL_RXR; + mask |= ENDPTCTRL_RXE; /* enable */ + data |= ENDPTCTRL_RXE; + } + hw_cwrite(CAP_ENDPTCTRL + num * sizeof(u32), mask, data); + return 0; +} + +/** + * hw_ep_get_halt: return endpoint halt status + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns 1 if endpoint halted + */ +static int hw_ep_get_halt(int num, int dir) +{ + u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; + + return hw_cread(CAP_ENDPTCTRL + num * sizeof(u32), mask) ? 1 : 0; +} + +/** + * hw_ep_is_primed: test if endpoint is primed (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * + * This function returns true if endpoint primed + */ +static int hw_ep_is_primed(int num, int dir) +{ + u32 reg = hw_cread(CAP_ENDPTPRIME, ~0) | hw_cread(CAP_ENDPTSTAT, ~0); + + return test_bit(hw_ep_bit(num, dir), (void *)®); +} + +/** + * hw_test_and_clear_setup_status: test & clear setup status (execute without + * interruption) + * @n: bit number (endpoint) + * + * This function returns setup status + */ +static int hw_test_and_clear_setup_status(int n) +{ + return hw_ctest_and_clear(CAP_ENDPTSETUPSTAT, BIT(n)); +} + +/** + * hw_ep_prime: primes endpoint (execute without interruption) + * @num: endpoint number + * @dir: endpoint direction + * @is_ctrl: true if control endpoint + * + * This function returns an error code + */ +static int hw_ep_prime(int num, int dir, int is_ctrl) +{ + int n = hw_ep_bit(num, dir); + + /* the caller should flush first */ + if (hw_ep_is_primed(num, dir)) + return -EBUSY; + + if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num))) + return -EAGAIN; + + hw_cwrite(CAP_ENDPTPRIME, BIT(n), BIT(n)); + + while (hw_cread(CAP_ENDPTPRIME, BIT(n))) + cpu_relax(); + if (is_ctrl && dir == RX && hw_cread(CAP_ENDPTSETUPSTAT, BIT(num))) + return -EAGAIN; + + /* status shoult be tested according with manual but it doesn't work */ + return 0; +} + +/** + * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute + * without interruption) + * @num: endpoint number + * @dir: endpoint direction + * @value: true => stall, false => unstall + * + * This function returns an error code + */ +static int hw_ep_set_halt(int num, int dir, int value) +{ + if (value != 0 && value != 1) + return -EINVAL; + + do { + u32 addr = CAP_ENDPTCTRL + num * sizeof(u32); + u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS; + u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR; + + /* data toggle - reserved for EP0 but it's in ESS */ + hw_cwrite(addr, mask_xs|mask_xr, value ? mask_xs : mask_xr); + + } while (value != hw_ep_get_halt(num, dir)); + + return 0; +} + +/** + * hw_intr_clear: disables interrupt & clears interrupt status (execute without + * interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_clear(int n) +{ + if (n >= REG_BITS) + return -EINVAL; + + hw_cwrite(CAP_USBINTR, BIT(n), 0); + hw_cwrite(CAP_USBSTS, BIT(n), BIT(n)); + return 0; +} + +/** + * hw_intr_force: enables interrupt & forces interrupt status (execute without + * interruption) + * @n: interrupt bit + * + * This function returns an error code + */ +static int hw_intr_force(int n) +{ + if (n >= REG_BITS) + return -EINVAL; + + hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE); + hw_cwrite(CAP_USBINTR, BIT(n), BIT(n)); + hw_cwrite(CAP_USBSTS, BIT(n), BIT(n)); + hw_awrite(ABS_TESTMODE, TESTMODE_FORCE, 0); + return 0; +} + +/** + * hw_is_port_high_speed: test if port is high speed + * + * This function returns true if high speed port + */ +static int hw_port_is_high_speed(void) +{ + return hw_bank.lpm ? hw_cread(CAP_DEVLC, DEVLC_PSPD) : + hw_cread(CAP_PORTSC, PORTSC_HSP); +} + +/** + * hw_port_test_get: reads port test mode value + * + * This function returns port test mode value + */ +static u8 hw_port_test_get(void) +{ + return hw_cread(CAP_PORTSC, PORTSC_PTC) >> ffs_nr(PORTSC_PTC); +} + +/** + * hw_port_test_set: writes port test mode (execute without interruption) + * @mode: new value + * + * This function returns an error code + */ +static int hw_port_test_set(u8 mode) +{ + const u8 TEST_MODE_MAX = 7; + + if (mode > TEST_MODE_MAX) + return -EINVAL; + + hw_cwrite(CAP_PORTSC, PORTSC_PTC, mode << ffs_nr(PORTSC_PTC)); + return 0; +} + +/** + * hw_read_intr_enable: returns interrupt enable register + * + * This function returns register data + */ +static u32 hw_read_intr_enable(void) +{ + return hw_cread(CAP_USBINTR, ~0); +} + +/** + * hw_read_intr_status: returns interrupt status register + * + * This function returns register data + */ +static u32 hw_read_intr_status(void) +{ + return hw_cread(CAP_USBSTS, ~0); +} + +/** + * hw_register_read: reads all device registers (execute without interruption) + * @buf: destination buffer + * @size: buffer size + * + * This function returns number of registers read + */ +static size_t hw_register_read(u32 *buf, size_t size) +{ + unsigned i; + + if (size > hw_bank.size) + size = hw_bank.size; + + for (i = 0; i < size; i++) + buf[i] = hw_aread(i * sizeof(u32), ~0); + + return size; +} + +/** + * hw_register_write: writes to register + * @addr: register address + * @data: register value + * + * This function returns an error code + */ +static int hw_register_write(u16 addr, u32 data) +{ + /* align */ + addr /= sizeof(u32); + + if (addr >= hw_bank.size) + return -EINVAL; + + /* align */ + addr *= sizeof(u32); + + hw_awrite(addr, ~0, data); + return 0; +} + +/** + * hw_test_and_clear_complete: test & clear complete status (execute without + * interruption) + * @n: bit number (endpoint) + * + * This function returns complete status + */ +static int hw_test_and_clear_complete(int n) +{ + return hw_ctest_and_clear(CAP_ENDPTCOMPLETE, BIT(n)); +} + +/** + * hw_test_and_clear_intr_active: test & clear active interrupts (execute + * without interruption) + * + * This function returns active interrutps + */ +static u32 hw_test_and_clear_intr_active(void) +{ + u32 reg = hw_read_intr_status() & hw_read_intr_enable(); + + hw_cwrite(CAP_USBSTS, ~0, reg); + return reg; +} + +/** + * hw_test_and_clear_setup_guard: test & clear setup guard (execute without + * interruption) + * + * This function returns guard value + */ +static int hw_test_and_clear_setup_guard(void) +{ + return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, 0); +} + +/** + * hw_test_and_set_setup_guard: test & set setup guard (execute without + * interruption) + * + * This function returns guard value + */ +static int hw_test_and_set_setup_guard(void) +{ + return hw_ctest_and_write(CAP_USBCMD, USBCMD_SUTW, USBCMD_SUTW); +} + +/** + * hw_usb_set_address: configures USB address (execute without interruption) + * @value: new USB address + * + * This function returns an error code + */ +static int hw_usb_set_address(u8 value) +{ + /* advance */ + hw_cwrite(CAP_DEVICEADDR, DEVICEADDR_USBADR | DEVICEADDR_USBADRA, + value << ffs_nr(DEVICEADDR_USBADR) | DEVICEADDR_USBADRA); + return 0; +} + +/** + * hw_usb_reset: restart device after a bus reset (execute without + * interruption) + * + * This function returns an error code + */ +static int hw_usb_reset(void) +{ + hw_usb_set_address(0); + + /* ESS flushes only at end?!? */ + hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0); /* flush all EPs */ + + /* clear setup token semaphores */ + hw_cwrite(CAP_ENDPTSETUPSTAT, 0, 0); /* writes its content */ + + /* clear complete status */ + hw_cwrite(CAP_ENDPTCOMPLETE, 0, 0); /* writes its content */ + + /* wait until all bits cleared */ + while (hw_cread(CAP_ENDPTPRIME, ~0)) + udelay(10); /* not RTOS friendly */ + + /* reset all endpoints ? */ + + /* reset internal status and wait for further instructions + no need to verify the port reset status (ESS does it) */ + + return 0; +} + +/****************************************************************************** + * DBG block + *****************************************************************************/ +/** + * show_device: prints information about device capabilities and status + * + * Check "device.h" for details + */ +static ssize_t show_device(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget *gadget = &udc->gadget; + int n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n", + gadget->speed); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_dualspeed = %d\n", + gadget->is_dualspeed); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n", + gadget->is_otg); + n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n", + gadget->is_a_peripheral); + n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n", + gadget->b_hnp_enable); + n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n", + gadget->a_hnp_support); + n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n", + gadget->a_alt_hnp_support); + n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n", + (gadget->name ? gadget->name : "")); + + return n; +} +static DEVICE_ATTR(device, S_IRUSR, show_device, NULL); + +/** + * show_driver: prints information about attached gadget (if any) + * + * Check "device.h" for details + */ +static ssize_t show_driver(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + struct usb_gadget_driver *driver = udc->driver; + int n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + if (driver == NULL) + return scnprintf(buf, PAGE_SIZE, + "There is no gadget attached!\n"); + + n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n", + (driver->function ? driver->function : "")); + n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n", + driver->speed); + + return n; +} +static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL); + +/* Maximum event message length */ +#define DBG_DATA_MSG 64UL + +/* Maximum event messages */ +#define DBG_DATA_MAX 128UL + +/* Event buffer descriptor */ +static struct { + char (buf[DBG_DATA_MAX])[DBG_DATA_MSG]; /* buffer */ + unsigned idx; /* index */ + unsigned tty; /* print to console? */ + rwlock_t lck; /* lock */ +} dbg_data = { + .idx = 0, + .tty = 0, + .lck = __RW_LOCK_UNLOCKED(lck) +}; + +/** + * dbg_dec: decrements debug event index + * @idx: buffer index + */ +static void dbg_dec(unsigned *idx) +{ + *idx = (*idx - 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_inc: increments debug event index + * @idx: buffer index + */ +static void dbg_inc(unsigned *idx) +{ + *idx = (*idx + 1) & (DBG_DATA_MAX-1); +} + +/** + * dbg_print: prints the common part of the event + * @addr: endpoint address + * @name: event name + * @status: status + * @extra: extra information + */ +static void dbg_print(u8 addr, const char *name, int status, const char *extra) +{ + struct timeval tval; + unsigned int stamp; + unsigned long flags; + + write_lock_irqsave(&dbg_data.lck, flags); + + do_gettimeofday(&tval); + stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s */ + stamp = stamp * 1000000 + tval.tv_usec; + + scnprintf(dbg_data.buf[dbg_data.idx], DBG_DATA_MSG, + "%04X\t» %02X %-7.7s %4i «\t%s\n", + stamp, addr, name, status, extra); + + dbg_inc(&dbg_data.idx); + + write_unlock_irqrestore(&dbg_data.lck, flags); + + if (dbg_data.tty != 0) + pr_notice("%04X\t» %02X %-7.7s %4i «\t%s\n", + stamp, addr, name, status, extra); +} + +/** + * dbg_done: prints a DONE event + * @addr: endpoint address + * @td: transfer descriptor + * @status: status + */ +static void dbg_done(u8 addr, const u32 token, int status) +{ + char msg[DBG_DATA_MSG]; + + scnprintf(msg, sizeof(msg), "%d %02X", + (int)(token & TD_TOTAL_BYTES) >> ffs_nr(TD_TOTAL_BYTES), + (int)(token & TD_STATUS) >> ffs_nr(TD_STATUS)); + dbg_print(addr, "DONE", status, msg); +} + +/** + * dbg_event: prints a generic event + * @addr: endpoint address + * @name: event name + * @status: status + */ +static void dbg_event(u8 addr, const char *name, int status) +{ + if (name != NULL) + dbg_print(addr, name, status, ""); +} + +/* + * dbg_queue: prints a QUEUE event + * @addr: endpoint address + * @req: USB request + * @status: status + */ +static void dbg_queue(u8 addr, const struct usb_request *req, int status) +{ + char msg[DBG_DATA_MSG]; + + if (req != NULL) { + scnprintf(msg, sizeof(msg), + "%d %d", !req->no_interrupt, req->length); + dbg_print(addr, "QUEUE", status, msg); + } +} + +/** + * dbg_setup: prints a SETUP event + * @addr: endpoint address + * @req: setup request + */ +static void dbg_setup(u8 addr, const struct usb_ctrlrequest *req) +{ + char msg[DBG_DATA_MSG]; + + if (req != NULL) { + scnprintf(msg, sizeof(msg), + "%02X %02X %04X %04X %d", req->bRequestType, + req->bRequest, le16_to_cpu(req->wValue), + le16_to_cpu(req->wIndex), le16_to_cpu(req->wLength)); + dbg_print(addr, "SETUP", 0, msg); + } +} + +/** + * show_events: displays the event buffer + * + * Check "device.h" for details + */ +static ssize_t show_events(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned long flags; + unsigned i, j, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + read_lock_irqsave(&dbg_data.lck, flags); + + i = dbg_data.idx; + for (dbg_dec(&i); i != dbg_data.idx; dbg_dec(&i)) { + n += strlen(dbg_data.buf[i]); + if (n >= PAGE_SIZE) { + n -= strlen(dbg_data.buf[i]); + break; + } + } + for (j = 0, dbg_inc(&i); j < n; dbg_inc(&i)) + j += scnprintf(buf + j, PAGE_SIZE - j, + "%s", dbg_data.buf[i]); + + read_unlock_irqrestore(&dbg_data.lck, flags); + + return n; +} + +/** + * store_events: configure if events are going to be also printed to console + * + * Check "device.h" for details + */ +static ssize_t store_events(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned tty; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u", &tty) != 1 || tty > 1) { + dev_err(dev, "<1|0>: enable|disable console log\n"); + goto done; + } + + dbg_data.tty = tty; + dev_info(dev, "tty = %u", dbg_data.tty); + + done: + return count; +} +static DEVICE_ATTR(events, S_IRUSR | S_IWUSR, show_events, store_events); + +/** + * show_inters: interrupt status, enable status and historic + * + * Check "device.h" for details + */ +static ssize_t show_inters(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + u32 intr; + unsigned i, j, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + + n += scnprintf(buf + n, PAGE_SIZE - n, + "status = %08x\n", hw_read_intr_status()); + n += scnprintf(buf + n, PAGE_SIZE - n, + "enable = %08x\n", hw_read_intr_enable()); + + n += scnprintf(buf + n, PAGE_SIZE - n, "*test = %d\n", + isr_statistics.test); + n += scnprintf(buf + n, PAGE_SIZE - n, "» ui = %d\n", + isr_statistics.ui); + n += scnprintf(buf + n, PAGE_SIZE - n, "» uei = %d\n", + isr_statistics.uei); + n += scnprintf(buf + n, PAGE_SIZE - n, "» pci = %d\n", + isr_statistics.pci); + n += scnprintf(buf + n, PAGE_SIZE - n, "» uri = %d\n", + isr_statistics.uri); + n += scnprintf(buf + n, PAGE_SIZE - n, "» sli = %d\n", + isr_statistics.sli); + n += scnprintf(buf + n, PAGE_SIZE - n, "*none = %d\n", + isr_statistics.none); + n += scnprintf(buf + n, PAGE_SIZE - n, "*hndl = %d\n", + isr_statistics.hndl.cnt); + + for (i = isr_statistics.hndl.idx, j = 0; j <= ISR_MASK; j++, i++) { + i &= ISR_MASK; + intr = isr_statistics.hndl.buf[i]; + + if (USBi_UI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "ui "); + intr &= ~USBi_UI; + if (USBi_UEI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "uei "); + intr &= ~USBi_UEI; + if (USBi_PCI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "pci "); + intr &= ~USBi_PCI; + if (USBi_URI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "uri "); + intr &= ~USBi_URI; + if (USBi_SLI & intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "sli "); + intr &= ~USBi_SLI; + if (intr) + n += scnprintf(buf + n, PAGE_SIZE - n, "??? "); + if (isr_statistics.hndl.buf[i]) + n += scnprintf(buf + n, PAGE_SIZE - n, "\n"); + } + + spin_unlock_irqrestore(udc->lock, flags); + + return n; +} + +/** + * store_inters: enable & force or disable an individual interrutps + * (to be used for test purposes only) + * + * Check "device.h" for details + */ +static ssize_t store_inters(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned en, bit; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u %u", &en, &bit) != 2 || en > 1) { + dev_err(dev, "<1|0> <bit>: enable|disable interrupt"); + goto done; + } + + spin_lock_irqsave(udc->lock, flags); + if (en) { + if (hw_intr_force(bit)) + dev_err(dev, "invalid bit number\n"); + else + isr_statistics.test++; + } else { + if (hw_intr_clear(bit)) + dev_err(dev, "invalid bit number\n"); + } + spin_unlock_irqrestore(udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(inters, S_IRUSR | S_IWUSR, show_inters, store_inters); + +/** + * show_port_test: reads port test mode + * + * Check "device.h" for details + */ +static ssize_t show_port_test(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned mode; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + mode = hw_port_test_get(); + spin_unlock_irqrestore(udc->lock, flags); + + return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode); +} + +/** + * store_port_test: writes port test mode + * + * Check "device.h" for details + */ +static ssize_t store_port_test(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned mode; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%u", &mode) != 1) { + dev_err(dev, "<mode>: set port test mode"); + goto done; + } + + spin_lock_irqsave(udc->lock, flags); + if (hw_port_test_set(mode)) + dev_err(dev, "invalid mode\n"); + spin_unlock_irqrestore(udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR, + show_port_test, store_port_test); + +/** + * show_qheads: DMA contents of all queue heads + * + * Check "device.h" for details + */ +static ssize_t show_qheads(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + unsigned i, j, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: RX=%08X TX=%08X\n", + i, (u32)mEp->qh[RX].dma, (u32)mEp->qh[TX].dma); + for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) { + n += scnprintf(buf + n, PAGE_SIZE - n, + " %04X: %08X %08X\n", j, + *((u32 *)mEp->qh[RX].ptr + j), + *((u32 *)mEp->qh[TX].ptr + j)); + } + } + spin_unlock_irqrestore(udc->lock, flags); + + return n; +} +static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL); + +/** + * show_registers: dumps all registers + * + * Check "device.h" for details + */ +static ssize_t show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + u32 dump[512]; + unsigned i, k, n = 0; + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + k = hw_register_read(dump, sizeof(dump)/sizeof(u32)); + spin_unlock_irqrestore(udc->lock, flags); + + for (i = 0; i < k; i++) { + n += scnprintf(buf + n, PAGE_SIZE - n, + "reg[0x%04X] = 0x%08X\n", + i * (unsigned)sizeof(u32), dump[i]); + } + + return n; +} + +/** + * store_registers: writes value to register address + * + * Check "device.h" for details + */ +static ssize_t store_registers(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long addr, data, flags; + + dbg_trace("[%s] %p, %d\n", __func__, buf, count); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + goto done; + } + + if (sscanf(buf, "%li %li", &addr, &data) != 2) { + dev_err(dev, "<addr> <data>: write data to register address"); + goto done; + } + + spin_lock_irqsave(udc->lock, flags); + if (hw_register_write(addr, data)) + dev_err(dev, "invalid address range\n"); + spin_unlock_irqrestore(udc->lock, flags); + + done: + return count; +} +static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR, + show_registers, store_registers); + +/** + * show_requests: DMA contents of all requests currently queued (all endpts) + * + * Check "device.h" for details + */ +static ssize_t show_requests(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ci13xxx *udc = container_of(dev, struct ci13xxx, gadget.dev); + unsigned long flags; + struct list_head *ptr = NULL; + struct ci13xxx_req *req = NULL; + unsigned i, j, k, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32); + + dbg_trace("[%s] %p\n", __func__, buf); + if (attr == NULL || buf == NULL) { + dev_err(dev, "[%s] EINVAL\n", __func__); + return 0; + } + + spin_lock_irqsave(udc->lock, flags); + for (i = 0; i < hw_ep_max; i++) + for (k = RX; k <= TX; k++) + list_for_each(ptr, &udc->ci13xxx_ep[i].qh[k].queue) + { + req = list_entry(ptr, + struct ci13xxx_req, queue); + + n += scnprintf(buf + n, PAGE_SIZE - n, + "EP=%02i: TD=%08X %s\n", + i, (u32)req->dma, + ((k == RX) ? "RX" : "TX")); + + for (j = 0; j < qSize; j++) + n += scnprintf(buf + n, PAGE_SIZE - n, + " %04X: %08X\n", j, + *((u32 *)req->ptr + j)); + } + spin_unlock_irqrestore(udc->lock, flags); + + return n; +} +static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL); + +/** + * dbg_create_files: initializes the attribute interface + * @dev: device + * + * This function returns an error code + */ +__maybe_unused static int dbg_create_files(struct device *dev) +{ + int retval = 0; + + if (dev == NULL) + return -EINVAL; + retval = device_create_file(dev, &dev_attr_device); + if (retval) + goto done; + retval = device_create_file(dev, &dev_attr_driver); + if (retval) + goto rm_device; + retval = device_create_file(dev, &dev_attr_events); + if (retval) + goto rm_driver; + retval = device_create_file(dev, &dev_attr_inters); + if (retval) + goto rm_events; + retval = device_create_file(dev, &dev_attr_port_test); + if (retval) + goto rm_inters; + retval = device_create_file(dev, &dev_attr_qheads); + if (retval) + goto rm_port_test; + retval = device_create_file(dev, &dev_attr_registers); + if (retval) + goto rm_qheads; + retval = device_create_file(dev, &dev_attr_requests); + if (retval) + goto rm_registers; + return 0; + + rm_registers: + device_remove_file(dev, &dev_attr_registers); + rm_qheads: + device_remove_file(dev, &dev_attr_qheads); + rm_port_test: + device_remove_file(dev, &dev_attr_port_test); + rm_inters: + device_remove_file(dev, &dev_attr_inters); + rm_events: + device_remove_file(dev, &dev_attr_events); + rm_driver: + device_remove_file(dev, &dev_attr_driver); + rm_device: + device_remove_file(dev, &dev_attr_device); + done: + return retval; +} + +/** + * dbg_remove_files: destroys the attribute interface + * @dev: device + * + * This function returns an error code + */ +__maybe_unused static int dbg_remove_files(struct device *dev) +{ + if (dev == NULL) + return -EINVAL; + device_remove_file(dev, &dev_attr_requests); + device_remove_file(dev, &dev_attr_registers); + device_remove_file(dev, &dev_attr_qheads); + device_remove_file(dev, &dev_attr_port_test); + device_remove_file(dev, &dev_attr_inters); + device_remove_file(dev, &dev_attr_events); + device_remove_file(dev, &dev_attr_driver); + device_remove_file(dev, &dev_attr_device); + return 0; +} + +/****************************************************************************** + * UTIL block + *****************************************************************************/ +/** + * _usb_addr: calculates endpoint address from direction & number + * @ep: endpoint + */ +static inline u8 _usb_addr(struct ci13xxx_ep *ep) +{ + return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num; +} + +/** + * _hardware_queue: configures a request at hardware level + * @gadget: gadget + * @mEp: endpoint + * + * This function returns an error code + */ +static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) +{ + unsigned i; + + trace("%p, %p", mEp, mReq); + + /* don't queue twice */ + if (mReq->req.status == -EALREADY) + return -EALREADY; + + if (hw_ep_is_primed(mEp->num, mEp->dir)) + return -EBUSY; + + mReq->req.status = -EALREADY; + + if (mReq->req.length && !mReq->req.dma) { + mReq->req.dma = \ + dma_map_single(mEp->device, mReq->req.buf, + mReq->req.length, mEp->dir ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (mReq->req.dma == 0) + return -ENOMEM; + + mReq->map = 1; + } + + /* + * TD configuration + * TODO - handle requests which spawns into several TDs + */ + memset(mReq->ptr, 0, sizeof(*mReq->ptr)); + mReq->ptr->next |= TD_TERMINATE; + mReq->ptr->token = mReq->req.length << ffs_nr(TD_TOTAL_BYTES); + mReq->ptr->token &= TD_TOTAL_BYTES; + mReq->ptr->token |= TD_IOC; + mReq->ptr->token |= TD_STATUS_ACTIVE; + mReq->ptr->page[0] = mReq->req.dma; + for (i = 1; i < 5; i++) + mReq->ptr->page[i] = + (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK; + + /* + * QH configuration + * At this point it's guaranteed exclusive access to qhead + * (endpt is not primed) so it's no need to use tripwire + */ + mEp->qh[mEp->dir].ptr->td.next = mReq->dma; /* TERMINATE = 0 */ + mEp->qh[mEp->dir].ptr->td.token &= ~TD_STATUS; /* clear status */ + if (mReq->req.zero == 0) + mEp->qh[mEp->dir].ptr->cap |= QH_ZLT; + else + mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; + + wmb(); /* synchronize before ep prime */ + + return hw_ep_prime(mEp->num, mEp->dir, + mEp->type == USB_ENDPOINT_XFER_CONTROL); +} + +/** + * _hardware_dequeue: handles a request at hardware level + * @gadget: gadget + * @mEp: endpoint + * + * This function returns an error code + */ +static int _hardware_dequeue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) +{ + trace("%p, %p", mEp, mReq); + + if (mReq->req.status != -EALREADY) + return -EINVAL; + + if (hw_ep_is_primed(mEp->num, mEp->dir)) + hw_ep_flush(mEp->num, mEp->dir); + + mReq->req.status = 0; + + if (mReq->map) { + dma_unmap_single(mEp->device, mReq->req.dma, mReq->req.length, + mEp->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + mReq->req.dma = 0; + mReq->map = 0; + } + + mReq->req.status = mReq->ptr->token & TD_STATUS; + if ((TD_STATUS_ACTIVE & mReq->req.status) != 0) + mReq->req.status = -ECONNRESET; + else if ((TD_STATUS_HALTED & mReq->req.status) != 0) + mReq->req.status = -1; + else if ((TD_STATUS_DT_ERR & mReq->req.status) != 0) + mReq->req.status = -1; + else if ((TD_STATUS_TR_ERR & mReq->req.status) != 0) + mReq->req.status = -1; + + mReq->req.actual = mReq->ptr->token & TD_TOTAL_BYTES; + mReq->req.actual >>= ffs_nr(TD_TOTAL_BYTES); + mReq->req.actual = mReq->req.length - mReq->req.actual; + mReq->req.actual = mReq->req.status ? 0 : mReq->req.actual; + + return mReq->req.actual; +} + +/** + * _ep_nuke: dequeues all endpoint requests + * @mEp: endpoint + * + * This function returns an error code + * Caller must hold lock + */ +static int _ep_nuke(struct ci13xxx_ep *mEp) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + trace("%p", mEp); + + if (mEp == NULL) + return -EINVAL; + + hw_ep_flush(mEp->num, mEp->dir); + + while (!list_empty(&mEp->qh[mEp->dir].queue)) { + + /* pop oldest request */ + struct ci13xxx_req *mReq = \ + list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + list_del_init(&mReq->queue); + mReq->req.status = -ESHUTDOWN; + + if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + spin_unlock(mEp->lock); + mReq->req.complete(&mEp->ep, &mReq->req); + spin_lock(mEp->lock); + } + } + return 0; +} + +/** + * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts + * @gadget: gadget + * + * This function returns an error code + * Caller must hold lock + */ +static int _gadget_stop_activity(struct usb_gadget *gadget) +__releases(udc->lock) +__acquires(udc->lock) +{ + struct usb_ep *ep; + struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget); + struct ci13xxx_ep *mEp = container_of(gadget->ep0, + struct ci13xxx_ep, ep); + + trace("%p", gadget); + + if (gadget == NULL) + return -EINVAL; + + spin_unlock(udc->lock); + + /* flush all endpoints */ + gadget_for_each_ep(ep, gadget) { + usb_ep_fifo_flush(ep); + } + usb_ep_fifo_flush(gadget->ep0); + + udc->driver->disconnect(gadget); + + /* make sure to disable all endpoints */ + gadget_for_each_ep(ep, gadget) { + usb_ep_disable(ep); + } + usb_ep_disable(gadget->ep0); + + if (mEp->status != NULL) { + usb_ep_free_request(gadget->ep0, mEp->status); + mEp->status = NULL; + } + + spin_lock(udc->lock); + + return 0; +} + +/****************************************************************************** + * ISR block + *****************************************************************************/ +/** + * isr_reset_handler: USB reset interrupt handler + * @udc: UDC device + * + * This function resets USB engine after a bus reset occurred + */ +static void isr_reset_handler(struct ci13xxx *udc) +__releases(udc->lock) +__acquires(udc->lock) +{ + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[0]; + int retval; + + trace("%p", udc); + + if (udc == NULL) { + err("EINVAL"); + return; + } + + dbg_event(0xFF, "BUS RST", 0); + + retval = _gadget_stop_activity(&udc->gadget); + if (retval) + goto done; + + retval = hw_usb_reset(); + if (retval) + goto done; + + spin_unlock(udc->lock); + retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc); + if (!retval) { + mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL); + if (mEp->status == NULL) { + usb_ep_disable(&mEp->ep); + retval = -ENOMEM; + } + } + spin_lock(udc->lock); + + done: + if (retval) + err("error: %i", retval); +} + +/** + * isr_get_status_complete: get_status request complete function + * @ep: endpoint + * @req: request handled + * + * Caller must release lock + */ +static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) +{ + trace("%p, %p", ep, req); + + if (ep == NULL || req == NULL) { + err("EINVAL"); + return; + } + + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +/** + * isr_get_status_response: get_status request response + * @ep: endpoint + * @setup: setup request packet + * + * This function returns an error code + */ +static int isr_get_status_response(struct ci13xxx_ep *mEp, + struct usb_ctrlrequest *setup) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + struct usb_request *req = NULL; + gfp_t gfp_flags = GFP_ATOMIC; + int dir, num, retval; + + trace("%p, %p", mEp, setup); + + if (mEp == NULL || setup == NULL) + return -EINVAL; + + spin_unlock(mEp->lock); + req = usb_ep_alloc_request(&mEp->ep, gfp_flags); + spin_lock(mEp->lock); + if (req == NULL) + return -ENOMEM; + + req->complete = isr_get_status_complete; + req->length = 2; + req->buf = kzalloc(req->length, gfp_flags); + if (req->buf == NULL) { + retval = -ENOMEM; + goto err_free_req; + } + + if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* TODO: D1 - Remote Wakeup; D0 - Self Powered */ + retval = 0; + } else if ((setup->bRequestType & USB_RECIP_MASK) \ + == USB_RECIP_ENDPOINT) { + dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ? + TX : RX; + num = le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK; + *((u16 *)req->buf) = hw_ep_get_halt(num, dir); + } + /* else do nothing; reserved for future use */ + + spin_unlock(mEp->lock); + retval = usb_ep_queue(&mEp->ep, req, gfp_flags); + spin_lock(mEp->lock); + if (retval) + goto err_free_buf; + + return 0; + + err_free_buf: + kfree(req->buf); + err_free_req: + spin_unlock(mEp->lock); + usb_ep_free_request(&mEp->ep, req); + spin_lock(mEp->lock); + return retval; +} + +/** + * isr_setup_status_phase: queues the status phase of a setup transation + * @mEp: endpoint + * + * This function returns an error code + */ +static int isr_setup_status_phase(struct ci13xxx_ep *mEp) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + int retval; + + trace("%p", mEp); + + /* mEp is always valid & configured */ + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + mEp->status->no_interrupt = 1; + + spin_unlock(mEp->lock); + retval = usb_ep_queue(&mEp->ep, mEp->status, GFP_ATOMIC); + spin_lock(mEp->lock); + + return retval; +} + +/** + * isr_tr_complete_low: transaction complete low level handler + * @mEp: endpoint + * + * This function returns an error code + * Caller must hold lock + */ +static int isr_tr_complete_low(struct ci13xxx_ep *mEp) +__releases(mEp->lock) +__acquires(mEp->lock) +{ + struct ci13xxx_req *mReq; + int retval; + + trace("%p", mEp); + + if (list_empty(&mEp->qh[mEp->dir].queue)) + return -EINVAL; + + /* pop oldest request */ + mReq = list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + list_del_init(&mReq->queue); + + retval = _hardware_dequeue(mEp, mReq); + if (retval < 0) { + dbg_event(_usb_addr(mEp), "DONE", retval); + goto done; + } + + dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); + + if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + spin_unlock(mEp->lock); + mReq->req.complete(&mEp->ep, &mReq->req); + spin_lock(mEp->lock); + } + + if (!list_empty(&mEp->qh[mEp->dir].queue)) { + mReq = list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + _hardware_enqueue(mEp, mReq); + } + + done: + return retval; +} + +/** + * isr_tr_complete_handler: transaction complete interrupt handler + * @udc: UDC descriptor + * + * This function handles traffic events + */ +static void isr_tr_complete_handler(struct ci13xxx *udc) +__releases(udc->lock) +__acquires(udc->lock) +{ + unsigned i; + + trace("%p", udc); + + if (udc == NULL) { + err("EINVAL"); + return; + } + + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + int type, num, err = -EINVAL; + struct usb_ctrlrequest req; + + + if (mEp->desc == NULL) + continue; /* not configured */ + + if ((mEp->dir == RX && hw_test_and_clear_complete(i)) || + (mEp->dir == TX && hw_test_and_clear_complete(i + 16))) { + err = isr_tr_complete_low(mEp); + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { + if (err > 0) /* needs status phase */ + err = isr_setup_status_phase(mEp); + if (err < 0) { + dbg_event(_usb_addr(mEp), + "ERROR", err); + spin_unlock(udc->lock); + if (usb_ep_set_halt(&mEp->ep)) + err("error: ep_set_halt"); + spin_lock(udc->lock); + } + } + } + + if (mEp->type != USB_ENDPOINT_XFER_CONTROL || + !hw_test_and_clear_setup_status(i)) + continue; + + if (i != 0) { + warn("ctrl traffic received at endpoint"); + continue; + } + + /* read_setup_packet */ + do { + hw_test_and_set_setup_guard(); + memcpy(&req, &mEp->qh[RX].ptr->setup, sizeof(req)); + } while (!hw_test_and_clear_setup_guard()); + + type = req.bRequestType; + + mEp->dir = (type & USB_DIR_IN) ? TX : RX; + + dbg_setup(_usb_addr(mEp), &req); + + switch (req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) + goto delegate; + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + num &= USB_ENDPOINT_NUMBER_MASK; + if (!udc->ci13xxx_ep[num].wedge) { + spin_unlock(udc->lock); + err = usb_ep_clear_halt( + &udc->ci13xxx_ep[num].ep); + spin_lock(udc->lock); + if (err) + break; + } + err = isr_setup_status_phase(mEp); + break; + case USB_REQ_GET_STATUS: + if (type != (USB_DIR_IN|USB_RECIP_DEVICE) && + type != (USB_DIR_IN|USB_RECIP_ENDPOINT) && + type != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 2 || + le16_to_cpu(req.wValue) != 0) + break; + err = isr_get_status_response(mEp, &req); + break; + case USB_REQ_SET_ADDRESS: + if (type != (USB_DIR_OUT|USB_RECIP_DEVICE)) + goto delegate; + if (le16_to_cpu(req.wLength) != 0 || + le16_to_cpu(req.wIndex) != 0) + break; + err = hw_usb_set_address((u8)le16_to_cpu(req.wValue)); + if (err) + break; + err = isr_setup_status_phase(mEp); + break; + case USB_REQ_SET_FEATURE: + if (type != (USB_DIR_OUT|USB_RECIP_ENDPOINT) && + le16_to_cpu(req.wValue) != USB_ENDPOINT_HALT) + goto delegate; + if (req.wLength != 0) + break; + num = le16_to_cpu(req.wIndex); + num &= USB_ENDPOINT_NUMBER_MASK; + + spin_unlock(udc->lock); + err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); + spin_lock(udc->lock); + if (err) + break; + err = isr_setup_status_phase(mEp); + break; + default: +delegate: + if (req.wLength == 0) /* no data phase */ + mEp->dir = TX; + + spin_unlock(udc->lock); + err = udc->driver->setup(&udc->gadget, &req); + spin_lock(udc->lock); + break; + } + + if (err < 0) { + dbg_event(_usb_addr(mEp), "ERROR", err); + + spin_unlock(udc->lock); + if (usb_ep_set_halt(&mEp->ep)) + err("error: ep_set_halt"); + spin_lock(udc->lock); + } + } +} + +/****************************************************************************** + * ENDPT block + *****************************************************************************/ +/** + * ep_enable: configure endpoint, making it usable + * + * Check usb_ep_enable() at "usb_gadget.h" for details + */ +static int ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + int direction, retval = 0; + unsigned long flags; + + trace("%p, %p", ep, desc); + + if (ep == NULL || desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + /* only internal SW should enable ctrl endpts */ + + mEp->desc = desc; + + if (!list_empty(&mEp->qh[mEp->dir].queue)) + warn("enabling a non-empty endpoint!"); + + mEp->dir = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? TX : RX; + mEp->num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + mEp->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); + + direction = mEp->dir; + do { + dbg_event(_usb_addr(mEp), "ENABLE", 0); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->qh[mEp->dir].ptr->cap |= QH_IOS; + else if (mEp->type == USB_ENDPOINT_XFER_ISOC) + mEp->qh[mEp->dir].ptr->cap &= ~QH_MULT; + else + mEp->qh[mEp->dir].ptr->cap &= ~QH_ZLT; + + mEp->qh[mEp->dir].ptr->cap |= + (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; + mEp->qh[mEp->dir].ptr->td.next |= TD_TERMINATE; /* needed? */ + + retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + } while (mEp->dir != direction); + + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_disable: endpoint is no longer usable + * + * Check usb_ep_disable() at "usb_gadget.h" for details + */ +static int ep_disable(struct usb_ep *ep) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + int direction, retval = 0; + unsigned long flags; + + trace("%p", ep); + + if (ep == NULL) + return -EINVAL; + else if (mEp->desc == NULL) + return -EBUSY; + + spin_lock_irqsave(mEp->lock, flags); + + /* only internal SW should disable ctrl endpts */ + + direction = mEp->dir; + do { + dbg_event(_usb_addr(mEp), "DISABLE", 0); + + retval |= _ep_nuke(mEp); + retval |= hw_ep_disable(mEp->num, mEp->dir); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + } while (mEp->dir != direction); + + mEp->desc = NULL; + + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_alloc_request: allocate a request object to use with this endpoint + * + * Check usb_ep_alloc_request() at "usb_gadget.h" for details + */ +static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = NULL; + unsigned long flags; + + trace("%p, %i", ep, gfp_flags); + + if (ep == NULL) { + err("EINVAL"); + return NULL; + } + + spin_lock_irqsave(mEp->lock, flags); + + mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags); + if (mReq != NULL) { + INIT_LIST_HEAD(&mReq->queue); + + mReq->ptr = dma_pool_alloc(mEp->td_pool, gfp_flags, + &mReq->dma); + if (mReq->ptr == NULL) { + kfree(mReq); + mReq = NULL; + } + } + + dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL); + + spin_unlock_irqrestore(mEp->lock, flags); + + return (mReq == NULL) ? NULL : &mReq->req; +} + +/** + * ep_free_request: frees a request object + * + * Check usb_ep_free_request() at "usb_gadget.h" for details + */ +static void ep_free_request(struct usb_ep *ep, struct usb_request *req) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + unsigned long flags; + + trace("%p, %p", ep, req); + + if (ep == NULL || req == NULL) { + err("EINVAL"); + return; + } else if (!list_empty(&mReq->queue)) { + err("EBUSY"); + return; + } + + spin_lock_irqsave(mEp->lock, flags); + + if (mReq->ptr) + dma_pool_free(mEp->td_pool, mReq->ptr, mReq->dma); + kfree(mReq); + + dbg_event(_usb_addr(mEp), "FREE", 0); + + spin_unlock_irqrestore(mEp->lock, flags); +} + +/** + * ep_queue: queues (submits) an I/O request to an endpoint + * + * Check usb_ep_queue()* at usb_gadget.h" for details + */ +static int ep_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t __maybe_unused gfp_flags) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + int retval = 0; + unsigned long flags; + + trace("%p, %p, %X", ep, req, gfp_flags); + + if (ep == NULL || req == NULL || mEp->desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL && + !list_empty(&mEp->qh[mEp->dir].queue)) { + _ep_nuke(mEp); + retval = -EOVERFLOW; + warn("endpoint ctrl %X nuked", _usb_addr(mEp)); + } + + /* first nuke then test link, e.g. previous status has not sent */ + if (!list_empty(&mReq->queue)) { + retval = -EBUSY; + err("request already in queue"); + goto done; + } + + if (req->length > (4 * PAGE_SIZE)) { + req->length = (4 * PAGE_SIZE); + retval = -EMSGSIZE; + warn("request length truncated"); + } + + dbg_queue(_usb_addr(mEp), req, retval); + + /* push request */ + mReq->req.status = -EINPROGRESS; + mReq->req.actual = 0; + list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); + + retval = _hardware_enqueue(mEp, mReq); + if (retval == -EALREADY || retval == -EBUSY) { + dbg_event(_usb_addr(mEp), "QUEUE", retval); + retval = 0; + } + + done: + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint + * + * Check usb_ep_dequeue() at "usb_gadget.h" for details + */ +static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + struct ci13xxx_req *mReq = container_of(req, struct ci13xxx_req, req); + unsigned long flags; + + trace("%p, %p", ep, req); + + if (ep == NULL || req == NULL || mEp->desc == NULL || + list_empty(&mReq->queue) || list_empty(&mEp->qh[mEp->dir].queue)) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + dbg_event(_usb_addr(mEp), "DEQUEUE", 0); + + if (mReq->req.status == -EALREADY) + _hardware_dequeue(mEp, mReq); + + /* pop request */ + list_del_init(&mReq->queue); + req->status = -ECONNRESET; + + if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + spin_unlock(mEp->lock); + mReq->req.complete(&mEp->ep, &mReq->req); + spin_lock(mEp->lock); + } + + spin_unlock_irqrestore(mEp->lock, flags); + return 0; +} + +/** + * ep_set_halt: sets the endpoint halt feature + * + * Check usb_ep_set_halt() at "usb_gadget.h" for details + */ +static int ep_set_halt(struct usb_ep *ep, int value) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + int direction, retval = 0; + unsigned long flags; + + trace("%p, %i", ep, value); + + if (ep == NULL || mEp->desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + +#ifndef STALL_IN + /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ + if (value && mEp->type == USB_ENDPOINT_XFER_BULK && mEp->dir == TX && + !list_empty(&mEp->qh[mEp->dir].queue)) { + spin_unlock_irqrestore(mEp->lock, flags); + return -EAGAIN; + } +#endif + + direction = mEp->dir; + do { + dbg_event(_usb_addr(mEp), "HALT", value); + retval |= hw_ep_set_halt(mEp->num, mEp->dir, value); + + if (!value) + mEp->wedge = 0; + + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) + mEp->dir = (mEp->dir == TX) ? RX : TX; + + } while (mEp->dir != direction); + + spin_unlock_irqrestore(mEp->lock, flags); + return retval; +} + +/** + * ep_set_wedge: sets the halt feature and ignores clear requests + * + * Check usb_ep_set_wedge() at "usb_gadget.h" for details + */ +static int ep_set_wedge(struct usb_ep *ep) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + unsigned long flags; + + trace("%p", ep); + + if (ep == NULL || mEp->desc == NULL) + return -EINVAL; + + spin_lock_irqsave(mEp->lock, flags); + + dbg_event(_usb_addr(mEp), "WEDGE", 0); + mEp->wedge = 1; + + spin_unlock_irqrestore(mEp->lock, flags); + + return usb_ep_set_halt(ep); +} + +/** + * ep_fifo_flush: flushes contents of a fifo + * + * Check usb_ep_fifo_flush() at "usb_gadget.h" for details + */ +static void ep_fifo_flush(struct usb_ep *ep) +{ + struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep); + unsigned long flags; + + trace("%p", ep); + + if (ep == NULL) { + err("%02X: -EINVAL", _usb_addr(mEp)); + return; + } + + spin_lock_irqsave(mEp->lock, flags); + + dbg_event(_usb_addr(mEp), "FFLUSH", 0); + hw_ep_flush(mEp->num, mEp->dir); + + spin_unlock_irqrestore(mEp->lock, flags); +} + +/** + * Endpoint-specific part of the API to the USB controller hardware + * Check "usb_gadget.h" for details + */ +static const struct usb_ep_ops usb_ep_ops = { + .enable = ep_enable, + .disable = ep_disable, + .alloc_request = ep_alloc_request, + .free_request = ep_free_request, + .queue = ep_queue, + .dequeue = ep_dequeue, + .set_halt = ep_set_halt, + .set_wedge = ep_set_wedge, + .fifo_flush = ep_fifo_flush, +}; + +/****************************************************************************** + * GADGET block + *****************************************************************************/ +/** + * Device operations part of the API to the USB controller hardware, + * which don't involve endpoints (or i/o) + * Check "usb_gadget.h" for details + */ +static const struct usb_gadget_ops usb_gadget_ops; + +/** + * usb_gadget_register_driver: register a gadget driver + * + * Check usb_gadget_register_driver() at "usb_gadget.h" for details + * Interrupts are enabled here + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct ci13xxx *udc = _udc; + unsigned long i, k, flags; + int retval = -ENOMEM; + + trace("%p", driver); + + if (driver == NULL || + driver->bind == NULL || + driver->unbind == NULL || + driver->setup == NULL || + driver->disconnect == NULL || + driver->suspend == NULL || + driver->resume == NULL) + return -EINVAL; + else if (udc == NULL) + return -ENODEV; + else if (udc->driver != NULL) + return -EBUSY; + + /* alloc resources */ + udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev, + sizeof(struct ci13xxx_qh), + 64, PAGE_SIZE); + if (udc->qh_pool == NULL) + return -ENOMEM; + + udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev, + sizeof(struct ci13xxx_td), + 64, PAGE_SIZE); + if (udc->td_pool == NULL) { + dma_pool_destroy(udc->qh_pool); + udc->qh_pool = NULL; + return -ENOMEM; + } + + spin_lock_irqsave(udc->lock, flags); + + info("hw_ep_max = %d", hw_ep_max); + + udc->driver = driver; + udc->gadget.ops = NULL; + udc->gadget.dev.driver = NULL; + + retval = 0; + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + + scnprintf(mEp->name, sizeof(mEp->name), "ep%i", (int)i); + + mEp->lock = udc->lock; + mEp->device = &udc->gadget.dev; + mEp->td_pool = udc->td_pool; + + mEp->ep.name = mEp->name; + mEp->ep.ops = &usb_ep_ops; + mEp->ep.maxpacket = CTRL_PAYLOAD_MAX; + + /* this allocation cannot be random */ + for (k = RX; k <= TX; k++) { + INIT_LIST_HEAD(&mEp->qh[k].queue); + mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool, + GFP_KERNEL, + &mEp->qh[k].dma); + if (mEp->qh[k].ptr == NULL) + retval = -ENOMEM; + else + memset(mEp->qh[k].ptr, 0, + sizeof(*mEp->qh[k].ptr)); + } + if (i == 0) + udc->gadget.ep0 = &mEp->ep; + else + list_add_tail(&mEp->ep.ep_list, &udc->gadget.ep_list); + } + if (retval) + goto done; + + /* bind gadget */ + driver->driver.bus = NULL; + udc->gadget.ops = &usb_gadget_ops; + udc->gadget.dev.driver = &driver->driver; + + spin_unlock_irqrestore(udc->lock, flags); + retval = driver->bind(&udc->gadget); /* MAY SLEEP */ + spin_lock_irqsave(udc->lock, flags); + + if (retval) { + udc->gadget.ops = NULL; + udc->gadget.dev.driver = NULL; + goto done; + } + + retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma); + + done: + spin_unlock_irqrestore(udc->lock, flags); + if (retval) + usb_gadget_unregister_driver(driver); + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +/** + * usb_gadget_unregister_driver: unregister a gadget driver + * + * Check usb_gadget_unregister_driver() at "usb_gadget.h" for details + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct ci13xxx *udc = _udc; + unsigned long i, k, flags; + + trace("%p", driver); + + if (driver == NULL || + driver->bind == NULL || + driver->unbind == NULL || + driver->setup == NULL || + driver->disconnect == NULL || + driver->suspend == NULL || + driver->resume == NULL || + driver != udc->driver) + return -EINVAL; + + spin_lock_irqsave(udc->lock, flags); + + hw_device_state(0); + + /* unbind gadget */ + if (udc->gadget.ops != NULL) { + _gadget_stop_activity(&udc->gadget); + + spin_unlock_irqrestore(udc->lock, flags); + driver->unbind(&udc->gadget); /* MAY SLEEP */ + spin_lock_irqsave(udc->lock, flags); + + udc->gadget.ops = NULL; + udc->gadget.dev.driver = NULL; + } + + /* free resources */ + for (i = 0; i < hw_ep_max; i++) { + struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; + + if (i == 0) + udc->gadget.ep0 = NULL; + else if (!list_empty(&mEp->ep.ep_list)) + list_del_init(&mEp->ep.ep_list); + + for (k = RX; k <= TX; k++) + if (mEp->qh[k].ptr != NULL) + dma_pool_free(udc->qh_pool, + mEp->qh[k].ptr, mEp->qh[k].dma); + } + + udc->driver = NULL; + + spin_unlock_irqrestore(udc->lock, flags); + + if (udc->td_pool != NULL) { + dma_pool_destroy(udc->td_pool); + udc->td_pool = NULL; + } + if (udc->qh_pool != NULL) { + dma_pool_destroy(udc->qh_pool); + udc->qh_pool = NULL; + } + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/****************************************************************************** + * BUS block + *****************************************************************************/ +/** + * udc_irq: global interrupt handler + * + * This function returns IRQ_HANDLED if the IRQ has been handled + * It locks access to registers + */ +static irqreturn_t udc_irq(void) +{ + struct ci13xxx *udc = _udc; + irqreturn_t retval; + u32 intr; + + trace(); + + if (udc == NULL) { + err("ENODEV"); + return IRQ_HANDLED; + } + + spin_lock(udc->lock); + intr = hw_test_and_clear_intr_active(); + if (intr) { + isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr; + isr_statistics.hndl.idx &= ISR_MASK; + isr_statistics.hndl.cnt++; + + /* order defines priority - do NOT change it */ + if (USBi_URI & intr) { + isr_statistics.uri++; + isr_reset_handler(udc); + } + if (USBi_PCI & intr) { + isr_statistics.pci++; + udc->gadget.speed = hw_port_is_high_speed() ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + if (USBi_UEI & intr) + isr_statistics.uei++; + if (USBi_UI & intr) { + isr_statistics.ui++; + isr_tr_complete_handler(udc); + } + if (USBi_SLI & intr) + isr_statistics.sli++; + retval = IRQ_HANDLED; + } else { + isr_statistics.none++; + retval = IRQ_NONE; + } + spin_unlock(udc->lock); + + return retval; +} + +/** + * udc_release: driver release function + * @dev: device + * + * Currently does nothing + */ +static void udc_release(struct device *dev) +{ + trace("%p", dev); + + if (dev == NULL) + err("EINVAL"); +} + +/** + * udc_probe: parent probe must call this to initialize UDC + * @dev: parent device + * @regs: registers base address + * @name: driver name + * + * This function returns an error code + * No interrupts active, the IRQ has not been requested yet + * Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask + */ +static int udc_probe(struct device *dev, void __iomem *regs, const char *name) +{ + struct ci13xxx *udc; + int retval = 0; + + trace("%p, %p, %p", dev, regs, name); + + if (dev == NULL || regs == NULL || name == NULL) + return -EINVAL; + + udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL); + if (udc == NULL) + return -ENOMEM; + + udc->lock = &udc_lock; + + retval = hw_device_reset(regs); + if (retval) + goto done; + + udc->gadget.ops = NULL; + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.is_dualspeed = 1; + udc->gadget.is_otg = 0; + udc->gadget.name = name; + + INIT_LIST_HEAD(&udc->gadget.ep_list); + udc->gadget.ep0 = NULL; + + strcpy(udc->gadget.dev.bus_id, "gadget"); + udc->gadget.dev.dma_mask = dev->dma_mask; + udc->gadget.dev.parent = dev; + udc->gadget.dev.release = udc_release; + + retval = device_register(&udc->gadget.dev); + if (retval) + goto done; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + retval = dbg_create_files(&udc->gadget.dev); +#endif + if (retval) { + device_unregister(&udc->gadget.dev); + goto done; + } + + _udc = udc; + return retval; + + done: + err("error = %i", retval); + kfree(udc); + _udc = NULL; + return retval; +} + +/** + * udc_remove: parent remove must call this to remove UDC + * + * No interrupts active, the IRQ has been released + */ +static void udc_remove(void) +{ + struct ci13xxx *udc = _udc; + + if (udc == NULL) { + err("EINVAL"); + return; + } + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + dbg_remove_files(&udc->gadget.dev); +#endif + device_unregister(&udc->gadget.dev); + + kfree(udc); + _udc = NULL; +} + +/****************************************************************************** + * PCI block + *****************************************************************************/ +/** + * ci13xxx_pci_irq: interrut handler + * @irq: irq number + * @pdev: USB Device Controller interrupt source + * + * This function returns IRQ_HANDLED if the IRQ has been handled + * This is an ISR don't trace, use attribute interface instead + */ +static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev) +{ + if (irq == 0) { + dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!"); + return IRQ_HANDLED; + } + return udc_irq(); +} + +/** + * ci13xxx_pci_probe: PCI probe + * @pdev: USB device controller being probed + * @id: PCI hotplug ID connecting controller to UDC framework + * + * This function returns an error code + * Allocates basic PCI resources for this USB device controller, and then + * invokes the udc_probe() method to start the UDC associated with it + */ +static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *regs = NULL; + int retval = 0; + + if (id == NULL) + return -EINVAL; + + retval = pci_enable_device(pdev); + if (retval) + goto done; + + if (!pdev->irq) { + dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!"); + retval = -ENODEV; + goto disable_device; + } + + retval = pci_request_regions(pdev, UDC_DRIVER_NAME); + if (retval) + goto disable_device; + + /* BAR 0 holds all the registers */ + regs = pci_iomap(pdev, 0, 0); + if (!regs) { + dev_err(&pdev->dev, "Error mapping memory!"); + retval = -EFAULT; + goto release_regions; + } + pci_set_drvdata(pdev, (__force void *)regs); + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME); + if (retval) + goto iounmap; + + /* our device does not have MSI capability */ + + retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED, + UDC_DRIVER_NAME, pdev); + if (retval) + goto gadget_remove; + + return 0; + + gadget_remove: + udc_remove(); + iounmap: + pci_iounmap(pdev, regs); + release_regions: + pci_release_regions(pdev); + disable_device: + pci_disable_device(pdev); + done: + return retval; +} + +/** + * ci13xxx_pci_remove: PCI remove + * @pdev: USB Device Controller being removed + * + * Reverses the effect of ci13xxx_pci_probe(), + * first invoking the udc_remove() and then releases + * all PCI resources allocated for this USB device controller + */ +static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev) +{ + free_irq(pdev->irq, pdev); + udc_remove(); + pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev)); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +/** + * PCI device table + * PCI device structure + * + * Check "pci.h" for details + */ +static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = { + { PCI_DEVICE(0x153F, 0x1004) }, + { PCI_DEVICE(0x153F, 0x1006) }, + { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table); + +static struct pci_driver ci13xxx_pci_driver = { + .name = UDC_DRIVER_NAME, + .id_table = ci13xxx_pci_id_table, + .probe = ci13xxx_pci_probe, + .remove = __devexit_p(ci13xxx_pci_remove), +}; + +/** + * ci13xxx_pci_init: module init + * + * Driver load + */ +static int __init ci13xxx_pci_init(void) +{ + return pci_register_driver(&ci13xxx_pci_driver); +} +module_init(ci13xxx_pci_init); + +/** + * ci13xxx_pci_exit: module exit + * + * Driver unload + */ +static void __exit ci13xxx_pci_exit(void) +{ + pci_unregister_driver(&ci13xxx_pci_driver); +} +module_exit(ci13xxx_pci_exit); + +MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>"); +MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("June 2008"); diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h new file mode 100644 index 000000000000..4026e9cede34 --- /dev/null +++ b/drivers/usb/gadget/ci13xxx_udc.h @@ -0,0 +1,195 @@ +/* + * ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core + * + * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. + * + * Author: David Lopo + * + * 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. + * + * Description: MIPS USB IP core family device controller + * Structures, registers and logging macros + */ + +#ifndef _CI13XXX_h_ +#define _CI13XXX_h_ + +/****************************************************************************** + * DEFINE + *****************************************************************************/ +#define ENDPT_MAX (16) +#define CTRL_PAYLOAD_MAX (64) +#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */ +#define TX (1) /* similar to USB_DIR_IN but can be used as an index */ + +/****************************************************************************** + * STRUCTURES + *****************************************************************************/ +/* DMA layout of transfer descriptors */ +struct ci13xxx_td { + /* 0 */ + u32 next; +#define TD_TERMINATE BIT(0) + /* 1 */ + u32 token; +#define TD_STATUS (0x00FFUL << 0) +#define TD_STATUS_TR_ERR BIT(3) +#define TD_STATUS_DT_ERR BIT(5) +#define TD_STATUS_HALTED BIT(6) +#define TD_STATUS_ACTIVE BIT(7) +#define TD_MULTO (0x0003UL << 10) +#define TD_IOC BIT(15) +#define TD_TOTAL_BYTES (0x7FFFUL << 16) + /* 2 */ + u32 page[5]; +#define TD_CURR_OFFSET (0x0FFFUL << 0) +#define TD_FRAME_NUM (0x07FFUL << 0) +#define TD_RESERVED_MASK (0x0FFFUL << 0) +} __attribute__ ((packed)); + +/* DMA layout of queue heads */ +struct ci13xxx_qh { + /* 0 */ + u32 cap; +#define QH_IOS BIT(15) +#define QH_MAX_PKT (0x07FFUL << 16) +#define QH_ZLT BIT(29) +#define QH_MULT (0x0003UL << 30) + /* 1 */ + u32 curr; + /* 2 - 8 */ + struct ci13xxx_td td; + /* 9 */ + u32 RESERVED; + struct usb_ctrlrequest setup; +} __attribute__ ((packed)); + +/* Extension of usb_request */ +struct ci13xxx_req { + struct usb_request req; + unsigned map; + struct list_head queue; + struct ci13xxx_td *ptr; + dma_addr_t dma; +}; + +/* Extension of usb_ep */ +struct ci13xxx_ep { + struct usb_ep ep; + const struct usb_endpoint_descriptor *desc; + u8 dir; + u8 num; + u8 type; + char name[16]; + struct { + struct list_head queue; + struct ci13xxx_qh *ptr; + dma_addr_t dma; + } qh[2]; + struct usb_request *status; + int wedge; + + /* global resources */ + spinlock_t *lock; + struct device *device; + struct dma_pool *td_pool; +}; + +/* CI13XXX UDC descriptor & global resources */ +struct ci13xxx { + spinlock_t *lock; /* ctrl register bank access */ + + struct dma_pool *qh_pool; /* DMA pool for queue heads */ + struct dma_pool *td_pool; /* DMA pool for transfer descs */ + + struct usb_gadget gadget; /* USB slave device */ + struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */ + + struct usb_gadget_driver *driver; /* 3rd party gadget driver */ +}; + +/****************************************************************************** + * REGISTERS + *****************************************************************************/ +/* register size */ +#define REG_BITS (32) + +/* HCCPARAMS */ +#define HCCPARAMS_LEN BIT(17) + +/* DCCPARAMS */ +#define DCCPARAMS_DEN (0x1F << 0) +#define DCCPARAMS_DC BIT(7) + +/* TESTMODE */ +#define TESTMODE_FORCE BIT(0) + +/* USBCMD */ +#define USBCMD_RS BIT(0) +#define USBCMD_RST BIT(1) +#define USBCMD_SUTW BIT(13) + +/* USBSTS & USBINTR */ +#define USBi_UI BIT(0) +#define USBi_UEI BIT(1) +#define USBi_PCI BIT(2) +#define USBi_URI BIT(6) +#define USBi_SLI BIT(8) + +/* DEVICEADDR */ +#define DEVICEADDR_USBADRA BIT(24) +#define DEVICEADDR_USBADR (0x7FUL << 25) + +/* PORTSC */ +#define PORTSC_SUSP BIT(7) +#define PORTSC_HSP BIT(9) +#define PORTSC_PTC (0x0FUL << 16) + +/* DEVLC */ +#define DEVLC_PSPD (0x03UL << 25) +#define DEVLC_PSPD_HS (0x02UL << 25) + +/* USBMODE */ +#define USBMODE_CM (0x03UL << 0) +#define USBMODE_CM_IDLE (0x00UL << 0) +#define USBMODE_CM_DEVICE (0x02UL << 0) +#define USBMODE_CM_HOST (0x03UL << 0) +#define USBMODE_SLOM BIT(3) + +/* ENDPTCTRL */ +#define ENDPTCTRL_RXS BIT(0) +#define ENDPTCTRL_RXT (0x03UL << 2) +#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */ +#define ENDPTCTRL_RXE BIT(7) +#define ENDPTCTRL_TXS BIT(16) +#define ENDPTCTRL_TXT (0x03UL << 18) +#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */ +#define ENDPTCTRL_TXE BIT(23) + +/****************************************************************************** + * LOGGING + *****************************************************************************/ +#define ci13xxx_printk(level, format, args...) \ +do { \ + if (_udc == NULL) \ + printk(level "[%s] " format "\n", __func__, ## args); \ + else \ + dev_printk(level, _udc->gadget.dev.parent, \ + "[%s] " format "\n", __func__, ## args); \ +} while (0) + +#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args) +#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args) +#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args) + +#ifdef TRACE +#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args) +#define dbg_trace(format, args...) dev_dbg(dev, format, ##args) +#else +#define trace(format, args...) do {} while (0) +#define dbg_trace(format, args...) do {} while (0) +#endif + +#endif /* _CI13XXX_h_ */ diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 9462e30192d8..a36b1175b18d 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -161,7 +161,7 @@ ep_matches ( /* report address */ desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { - u8 num = simple_strtol (&ep->name [2], NULL, 10); + u8 num = simple_strtoul (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; #ifdef MANY_ENDPOINTS } else if (desc->bEndpointAddress & USB_DIR_IN) { diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index d8fc9b32fe36..c0916c7b217e 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -279,6 +279,13 @@ static int pn_net_mtu(struct net_device *dev, int new_mtu) return err; } +static const struct net_device_ops pn_netdev_ops = { + .ndo_open = pn_net_open, + .ndo_stop = pn_net_close, + .ndo_start_xmit = pn_net_xmit, + .ndo_change_mtu = pn_net_mtu, +}; + static void pn_net_setup(struct net_device *dev) { dev->features = 0; @@ -290,12 +297,9 @@ static void pn_net_setup(struct net_device *dev) dev->addr_len = 1; dev->tx_queue_len = 1; + dev->netdev_ops = &pn_netdev_ops; dev->destructor = free_netdev; dev->header_ops = &phonet_header_ops; - dev->open = pn_net_open; - dev->stop = pn_net_close; - dev->hard_start_xmit = pn_net_xmit; /* mandatory */ - dev->change_mtu = pn_net_mtu; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 2e71368f45b4..b10fa31cc915 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -1,7 +1,7 @@ /* * file_storage.c -- File-backed USB Storage Gadget, for USB development * - * Copyright (C) 2003-2007 Alan Stern + * Copyright (C) 2003-2008 Alan Stern * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,17 @@ /* * The File-backed Storage Gadget acts as a USB Mass Storage device, - * appearing to the host as a disk drive. In addition to providing an - * example of a genuinely useful gadget driver for a USB device, it also - * illustrates a technique of double-buffering for increased throughput. - * Last but not least, it gives an easy way to probe the behavior of the - * Mass Storage drivers in a USB host. + * appearing to the host as a disk drive or as a CD-ROM drive. In addition + * to providing an example of a genuinely useful gadget driver for a USB + * device, it also illustrates a technique of double-buffering for increased + * throughput. Last but not least, it gives an easy way to probe the + * behavior of the Mass Storage drivers in a USB host. * * Backing storage is provided by a regular file or a block device, specified * by the "file" module parameter. Access can be limited to read-only by - * setting the optional "ro" module parameter. The gadget will indicate that - * it has removable media if the optional "removable" module parameter is set. + * setting the optional "ro" module parameter. (For CD-ROM emulation, + * access is always read-only.) The gadget will indicate that it has + * removable media if the optional "removable" module parameter is set. * * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected @@ -64,7 +65,12 @@ * The default number of LUNs is taken from the number of "file" elements; * it is 1 if "file" is not given. If "removable" is not set then a backing * file must be specified for each LUN. If it is set, then an unspecified - * or empty backing filename means the LUN's medium is not loaded. + * or empty backing filename means the LUN's medium is not loaded. Ideally + * each LUN would be settable independently as a disk drive or a CD-ROM + * drive, but currently all LUNs have to be the same type. The CD-ROM + * emulation includes a single data track and no audio tracks; hence there + * need be only one backing file per LUN. Note also that the CD-ROM block + * length is set to 512 rather than the more common value 2048. * * Requirements are modest; only a bulk-in and a bulk-out endpoint are * needed (an interrupt-out endpoint is also needed for CBI). The memory @@ -91,6 +97,8 @@ * USB device controller (usually true), * boolean to permit the driver to halt * bulk endpoints + * cdrom Default false, boolean for whether to emulate + * a CD-ROM drive * transport=XXX Default BBB, transport name (CB, CBI, or BBB) * protocol=YYY Default SCSI, protocol name (RBC, 8020 or * ATAPI, QIC, UFI, 8070, or SCSI; @@ -103,15 +111,16 @@ * PAGE_CACHE_SIZE) * * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", - * "removable", "luns", and "stall" options are available; default values - * are used for everything else. + * "removable", "luns", "stall", and "cdrom" options are available; default + * values are used for everything else. * * The pathnames of the backing files and the ro settings are available in * the attribute files "file" and "ro" in the lun<n> subdirectory of the * gadget's sysfs directory. If the "removable" option is set, writing to * these files will simulate ejecting/loading the medium (writing an empty * line means eject) and adjusting a write-enable tab. Changes to the ro - * setting are not allowed when the medium is loaded. + * setting are not allowed when the medium is loaded or if CD-ROM emulation + * is being used. * * This gadget driver is heavily based on "Gadget Zero" by David Brownell. * The driver's SCSI command interface was based on the "Information @@ -261,7 +270,7 @@ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "7 August 2007" +#define DRIVER_VERSION "20 November 2008" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -341,6 +350,7 @@ static struct { int removable; int can_stall; + int cdrom; char *transport_parm; char *protocol_parm; @@ -359,6 +369,7 @@ static struct { .protocol_parm = "SCSI", .removable = 0, .can_stall = 1, + .cdrom = 0, .vendor = DRIVER_VENDOR_ID, .product = DRIVER_PRODUCT_ID, .release = 0xffff, // Use controller chip type @@ -382,6 +393,9 @@ MODULE_PARM_DESC(removable, "true to simulate removable media"); module_param_named(stall, mod_data.can_stall, bool, S_IRUGO); MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); +module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO); +MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk"); + /* In the non-TEST version, only the module parameters listed above * are available. */ @@ -411,6 +425,10 @@ MODULE_PARM_DESC(buflen, "I/O buffer size"); /*-------------------------------------------------------------------------*/ +/* SCSI device types */ +#define TYPE_DISK 0x00 +#define TYPE_CDROM 0x05 + /* USB protocol value = the transport method */ #define USB_PR_CBI 0x00 // Control/Bulk/Interrupt #define USB_PR_CB 0x01 // Control/Bulk w/o interrupt @@ -487,6 +505,8 @@ struct interrupt_data { #define SC_READ_12 0xa8 #define SC_READ_CAPACITY 0x25 #define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 #define SC_RELEASE 0x17 #define SC_REQUEST_SENSE 0x03 #define SC_RESERVE 0x16 @@ -2006,23 +2026,28 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) u8 *buf = (u8 *) bh->buf; static char vendor_id[] = "Linux "; - static char product_id[] = "File-Stor Gadget"; + static char product_disk_id[] = "File-Stor Gadget"; + static char product_cdrom_id[] = "File-CD Gadget "; if (!fsg->curlun) { // Unsupported LUNs are okay fsg->bad_lun_okay = 1; memset(buf, 0, 36); buf[0] = 0x7f; // Unsupported, no device-type + buf[4] = 31; // Additional length return 36; } - memset(buf, 0, 8); // Non-removable, direct-access device + memset(buf, 0, 8); + buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK); if (mod_data.removable) buf[1] = 0x80; buf[2] = 2; // ANSI SCSI level 2 buf[3] = 2; // SCSI-2 INQUIRY data format buf[4] = 31; // Additional length // No special options - sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id, + sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, + (mod_data.cdrom ? product_cdrom_id : + product_disk_id), mod_data.release); return 36; } @@ -2101,6 +2126,75 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) } +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_be32(dest, addr); + } +} + +static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct lun *curlun = fsg->curlun; + int msf = fsg->cmnd[1] & 0x02; + u32 lba = get_be32(&fsg->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct lun *curlun = fsg->curlun; + int msf = fsg->cmnd[1] & 0x02; + int start_track = fsg->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; +} + + static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct lun *curlun = fsg->curlun; @@ -2848,6 +2942,26 @@ static int do_scsi_command(struct fsg_dev *fsg) reply = do_read_capacity(fsg, bh); break; + case SC_READ_HEADER: + if (!mod_data.cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER")) == 0) + reply = do_read_header(fsg, bh); + break; + + case SC_READ_TOC: + if (!mod_data.cdrom) + goto unknown_cmnd; + fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); + if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC")) == 0) + reply = do_read_toc(fsg, bh); + break; + case SC_READ_FORMAT_CAPACITIES: fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, @@ -2933,6 +3047,7 @@ static int do_scsi_command(struct fsg_dev *fsg) // Fall through default: + unknown_cmnd: fsg->data_size_from_cmnd = 0; sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); if ((reply = check_command(fsg, fsg->cmnd_size, @@ -3498,6 +3613,7 @@ static int open_backing_file(struct lun *curlun, const char *filename) struct inode *inode = NULL; loff_t size; loff_t num_sectors; + loff_t min_sectors; /* R/W if we can, R/O if we must */ ro = curlun->ro; @@ -3541,8 +3657,19 @@ static int open_backing_file(struct lun *curlun, const char *filename) rc = (int) size; goto out; } - num_sectors = size >> 9; // File size in 512-byte sectors - if (num_sectors == 0) { + num_sectors = size >> 9; // File size in 512-byte blocks + min_sectors = 1; + if (mod_data.cdrom) { + num_sectors &= ~3; // Reduce to a multiple of 2048 + min_sectors = 300*4; // Smallest track is 300 frames + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { LINFO(curlun, "file too small: %s\n", filename); rc = -ETOOSMALL; goto out; @@ -3845,9 +3972,12 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; if (mod_data.removable) { // Enable the store_xxx attributes - dev_attr_ro.attr.mode = dev_attr_file.attr.mode = 0644; - dev_attr_ro.store = store_ro; + dev_attr_file.attr.mode = 0644; dev_attr_file.store = store_file; + if (!mod_data.cdrom) { + dev_attr_ro.attr.mode = 0644; + dev_attr_ro.store = store_ro; + } } /* Find out how many LUNs there should be */ @@ -3872,6 +4002,8 @@ static int __init fsg_bind(struct usb_gadget *gadget) for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; curlun->ro = mod_data.ro[i]; + if (mod_data.cdrom) + curlun->ro = 1; curlun->dev.release = lun_release; curlun->dev.parent = &gadget->dev; curlun->dev.driver = &fsg_driver.driver; @@ -4031,9 +4163,9 @@ static int __init fsg_bind(struct usb_gadget *gadget) mod_data.protocol_name, mod_data.protocol_type); DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", mod_data.vendor, mod_data.product, mod_data.release); - DBG(fsg, "removable=%d, stall=%d, buflen=%u\n", + DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n", mod_data.removable, mod_data.can_stall, - mod_data.buflen); + mod_data.cdrom, mod_data.buflen); DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task)); set_bit(REGISTERED, &fsg->atomic_bitflags); @@ -4050,6 +4182,7 @@ out: fsg->state = FSG_STATE_TERMINATED; // The thread is dead fsg_unbind(gadget); close_all_backing_files(fsg); + complete(&fsg->thread_notifier); return rc; } diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index f40272565098..d6c5bcd40064 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -26,6 +26,7 @@ #include <linux/ioport.h> #include <linux/types.h> #include <linux/errno.h> +#include <linux/err.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -370,6 +371,9 @@ static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num) /* alloc multi-ram for BD rings and set the ep parameters */ tmp_addr = cpm_muram_alloc(sizeof(struct qe_bd) * (bdring_len + USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD); + if (IS_ERR_VALUE(tmp_addr)) + return -ENOMEM; + out_be16(&epparam->rbase, (u16)tmp_addr); out_be16(&epparam->tbase, (u16)(tmp_addr + (sizeof(struct qe_bd) * bdring_len))); @@ -689,7 +693,7 @@ en_done2: en_done1: spin_unlock_irqrestore(&udc->lock, flags); en_done: - dev_dbg(udc->dev, "failed to initialize %s\n", ep->ep.name); + dev_err(udc->dev, "failed to initialize %s\n", ep->ep.name); return -ENODEV; } @@ -2408,6 +2412,8 @@ static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev) tmp_addr = cpm_muram_alloc((USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para)), USB_EP_PARA_ALIGNMENT); + if (IS_ERR_VALUE(tmp_addr)) + goto cleanup; for (i = 0; i < USB_MAX_ENDPOINTS; i++) { out_be16(&usbpram->epptr[i], (u16)tmp_addr); @@ -2513,7 +2519,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, /* Initialize the udc structure including QH member and other member */ udc_controller = qe_udc_config(ofdev); if (!udc_controller) { - dev_dbg(&ofdev->dev, "udc_controll is NULL\n"); + dev_err(&ofdev->dev, "failed to initialize\n"); return -ENOMEM; } @@ -2568,7 +2574,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev, /* create a buf for ZLP send, need to remain zeroed */ udc_controller->nullbuf = kzalloc(256, GFP_KERNEL); if (udc_controller->nullbuf == NULL) { - dev_dbg(udc_controller->dev, "cannot alloc nullbuf\n"); + dev_err(udc_controller->dev, "cannot alloc nullbuf\n"); ret = -ENOMEM; goto err3; } diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 4e3107dd2f34..ec6d439a2aa5 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -110,7 +110,6 @@ #define gadget_is_at91(g) 0 #endif -/* status unclear */ #ifdef CONFIG_USB_GADGET_IMX #define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) #else @@ -158,6 +157,11 @@ #define gadget_is_fsl_qe(g) 0 #endif +#ifdef CONFIG_USB_GADGET_CI13XXX +#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name)) +#else +#define gadget_is_ci13xxx(g) 0 +#endif // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 @@ -225,6 +229,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x21; else if (gadget_is_fsl_qe(gadget)) return 0x22; + else if (gadget_is_ci13xxx(gadget)) + return 0x23; return -ENOENT; } diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 60aa04847b18..63419c4d503c 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -1349,7 +1349,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) int retval; if (!driver - || driver->speed != USB_SPEED_FULL + || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->disconnect || !driver->setup) diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c new file mode 100644 index 000000000000..cde8fdf15d5b --- /dev/null +++ b/drivers/usb/gadget/imx_udc.c @@ -0,0 +1,1516 @@ +/* + * driver/usb/gadget/imx_udc.c + * + * Copyright (C) 2005 Mike Lee(eemike@gmail.com) + * Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/delay.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <mach/usb.h> +#include <mach/hardware.h> + +#include "imx_udc.h" + +static const char driver_name[] = "imx_udc"; +static const char ep0name[] = "ep0"; + +void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb, + enum ep0_state stat); + +/******************************************************************************* + * IMX UDC hardware related functions + ******************************************************************************* + */ + +void imx_udc_enable(struct imx_udc_struct *imx_usb) +{ + int temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, imx_usb->base + USB_CTRL); + imx_usb->gadget.speed = USB_SPEED_FULL; +} + +void imx_udc_disable(struct imx_udc_struct *imx_usb) +{ + int temp = __raw_readl(imx_usb->base + USB_CTRL); + + __raw_writel(temp & ~(CTRL_FE_ENA | CTRL_AFE_ENA), + imx_usb->base + USB_CTRL); + + ep0_chg_stat(__func__, imx_usb, EP0_IDLE); + imx_usb->gadget.speed = USB_SPEED_UNKNOWN; +} + +void imx_udc_reset(struct imx_udc_struct *imx_usb) +{ + int temp = __raw_readl(imx_usb->base + USB_ENAB); + + /* set RST bit */ + __raw_writel(temp | ENAB_RST, imx_usb->base + USB_ENAB); + + /* wait RST bit to clear */ + do {} while (__raw_readl(imx_usb->base + USB_ENAB) & ENAB_RST); + + /* wait CFG bit to assert */ + do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); + + /* udc module is now ready */ +} + +void imx_udc_config(struct imx_udc_struct *imx_usb) +{ + u8 ep_conf[5]; + u8 i, j, cfg; + struct imx_ep_struct *imx_ep; + + /* wait CFG bit to assert */ + do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); + + /* Download the endpoint buffer for endpoint 0. */ + for (j = 0; j < 5; j++) { + i = (j == 2 ? imx_usb->imx_ep[0].fifosize : 0x00); + __raw_writeb(i, imx_usb->base + USB_DDAT); + do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_BSY); + } + + /* Download the endpoint buffers for endpoints 1-5. + * We specify two configurations, one interface + */ + for (cfg = 1; cfg < 3; cfg++) { + for (i = 1; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + /* EP no | Config no */ + ep_conf[0] = (i << 4) | (cfg << 2); + /* Type | Direction */ + ep_conf[1] = (imx_ep->bmAttributes << 3) | + (EP_DIR(imx_ep) << 2); + /* Max packet size */ + ep_conf[2] = imx_ep->fifosize; + /* TRXTYP */ + ep_conf[3] = 0xC0; + /* FIFO no */ + ep_conf[4] = i; + + D_INI(imx_usb->dev, + "<%s> ep%d_conf[%d]:" + "[%02x-%02x-%02x-%02x-%02x]\n", + __func__, i, cfg, + ep_conf[0], ep_conf[1], ep_conf[2], + ep_conf[3], ep_conf[4]); + + for (j = 0; j < 5; j++) { + __raw_writeb(ep_conf[j], + imx_usb->base + USB_DDAT); + do {} while (__raw_readl(imx_usb->base + USB_DADR) + & DADR_BSY); + } + } + } + + /* wait CFG bit to clear */ + do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG); +} + +void imx_udc_init_irq(struct imx_udc_struct *imx_usb) +{ + int i; + + /* Mask and clear all irqs */ + __raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK); + __raw_writel(0xFFFFFFFF, imx_usb->base + USB_INTR); + for (i = 0; i < IMX_USB_NB_EP; i++) { + __raw_writel(0x1FF, imx_usb->base + USB_EP_MASK(i)); + __raw_writel(0x1FF, imx_usb->base + USB_EP_INTR(i)); + } + + /* Enable USB irqs */ + __raw_writel(INTR_MSOF | INTR_FRAME_MATCH, imx_usb->base + USB_MASK); + + /* Enable EP0 irqs */ + __raw_writel(0x1FF & ~(EPINTR_DEVREQ | EPINTR_MDEVREQ | EPINTR_EOT + | EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL), + imx_usb->base + USB_EP_MASK(0)); +} + +void imx_udc_init_ep(struct imx_udc_struct *imx_usb) +{ + int i, max, temp; + struct imx_ep_struct *imx_ep; + for (i = 0; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + switch (imx_ep->fifosize) { + case 8: + max = 0; + break; + case 16: + max = 1; + break; + case 32: + max = 2; + break; + case 64: + max = 3; + break; + default: + max = 1; + break; + } + temp = (EP_DIR(imx_ep) << 7) | (max << 5) + | (imx_ep->bmAttributes << 3); + __raw_writel(temp, imx_usb->base + USB_EP_STAT(i)); + __raw_writel(temp | EPSTAT_FLUSH, imx_usb->base + USB_EP_STAT(i)); + D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i, + __raw_readl(imx_usb->base + USB_EP_STAT(i))); + } +} + +void imx_udc_init_fifo(struct imx_udc_struct *imx_usb) +{ + int i, temp; + struct imx_ep_struct *imx_ep; + for (i = 0; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + + /* Fifo control */ + temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000; + __raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i)); + D_INI(imx_usb->dev, "<%s> ep%d_fctrl %08x\n", __func__, i, + __raw_readl(imx_usb->base + USB_EP_FCTRL(i))); + + /* Fifo alarm */ + temp = (i ? imx_ep->fifosize / 2 : 0); + __raw_writel(temp, imx_usb->base + USB_EP_FALRM(i)); + D_INI(imx_usb->dev, "<%s> ep%d_falrm %08x\n", __func__, i, + __raw_readl(imx_usb->base + USB_EP_FALRM(i))); + } +} + +static void imx_udc_init(struct imx_udc_struct *imx_usb) +{ + /* Reset UDC */ + imx_udc_reset(imx_usb); + + /* Download config to enpoint buffer */ + imx_udc_config(imx_usb); + + /* Setup interrups */ + imx_udc_init_irq(imx_usb); + + /* Setup endpoints */ + imx_udc_init_ep(imx_usb); + + /* Setup fifos */ + imx_udc_init_fifo(imx_usb); +} + +void imx_ep_irq_enable(struct imx_ep_struct *imx_ep) +{ + + int i = EP_NO(imx_ep); + + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); + __raw_writel(0x1FF & ~(EPINTR_EOT | EPINTR_EOF), + imx_ep->imx_usb->base + USB_EP_MASK(i)); +} + +void imx_ep_irq_disable(struct imx_ep_struct *imx_ep) +{ + + int i = EP_NO(imx_ep); + + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); + __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); +} + +int imx_ep_empty(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + + return __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) + & FSTAT_EMPTY; +} + +unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + + return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) + & EPSTAT_BCOUNT) >> 16; +} + +void imx_flush(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + + int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + __raw_writel(temp | EPSTAT_FLUSH, + imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); +} + +void imx_ep_stall(struct imx_ep_struct *imx_ep) +{ + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + int temp, i; + + D_ERR(imx_usb->dev, "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name); + + imx_flush(imx_ep); + + /* Special care for ep0 */ + if (EP_NO(imx_ep)) { + temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, imx_usb->base + USB_CTRL); + do { } while (__raw_readl(imx_usb->base + USB_CTRL) & CTRL_CMDOVER); + temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL); + } + else { + temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + __raw_writel(temp | EPSTAT_STALL, + imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + + for (i = 0; i < 100; i ++) { + temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + if (!temp & EPSTAT_STALL) + break; + udelay(20); + } + if (i == 50) + D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n", + __func__, imx_ep->ep.name); + } +} + +static int imx_udc_get_frame(struct usb_gadget *_gadget) +{ + struct imx_udc_struct *imx_usb = container_of(_gadget, + struct imx_udc_struct, gadget); + + return __raw_readl(imx_usb->base + USB_FRAME) & 0x7FF; +} + +static int imx_udc_wakeup(struct usb_gadget *_gadget) +{ + return 0; +} + +/******************************************************************************* + * USB request control functions + ******************************************************************************* + */ + +static void ep_add_request(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + if (unlikely(!req)) + return; + + req->in_use = 1; + list_add_tail(&req->queue, &imx_ep->queue); +} + +static void ep_del_request(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + if (unlikely(!req)) + return; + + list_del_init(&req->queue); + req->in_use = 0; +} + +static void done(struct imx_ep_struct *imx_ep, struct imx_request *req, int status) +{ + ep_del_request(imx_ep, req); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + D_ERR(imx_ep->imx_usb->dev, + "<%s> complete %s req %p stat %d len %u/%u\n", __func__, + imx_ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + req->req.complete(&imx_ep->ep, &req->req); +} + +static void nuke(struct imx_ep_struct *imx_ep, int status) +{ + struct imx_request *req; + + while (!list_empty(&imx_ep->queue)) { + req = list_entry(imx_ep->queue.next, struct imx_request, queue); + done(imx_ep, req, status); + } +} + +/******************************************************************************* + * Data tansfer over USB functions + ******************************************************************************* + */ +static int read_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + u8 *buf; + int bytes_ep, bufferspace, count, i; + + bytes_ep = imx_fifo_bcount(imx_ep); + bufferspace = req->req.length - req->req.actual; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + if (unlikely(imx_ep_empty(imx_ep))) + count = 0; /* zlp */ + else + count = min(bytes_ep, bufferspace); + + for (i = count; i > 0; i--) + *buf++ = __raw_readb(imx_ep->imx_usb->base + + USB_EP_FDAT0(EP_NO(imx_ep))); + req->req.actual += count; + + return count; +} + +static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + u8 *buf; + int length, count, temp; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize); + + if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) { + D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n", + __func__, imx_ep->ep.name); + return -1; + } + + req->req.actual += length; + count = length; + + if (!count && req->req.zero) { /* zlp */ + temp = __raw_readl(imx_ep->imx_usb->base + + USB_EP_STAT(EP_NO(imx_ep))); + __raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base + + USB_EP_STAT(EP_NO(imx_ep))); + D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__); + return 0; + } + + while (count--) { + if (count == 0) { /* last byte */ + temp = __raw_readl(imx_ep->imx_usb->base + + USB_EP_FCTRL(EP_NO(imx_ep))); + __raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base + + USB_EP_FCTRL(EP_NO(imx_ep))); + } + __raw_writeb(*buf++, + imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep))); + } + + return length; +} + +static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + int bytes = 0, + count, + completed = 0; + + while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) + & FSTAT_FR) { + count = read_packet(imx_ep, req); + bytes += count; + + completed = (count != imx_ep->fifosize); + if (completed || req->req.actual == req->req.length) { + completed = 1; + break; + } + } + + if (completed || !req->req.length) { + done(imx_ep, req, 0); + D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", + __func__, imx_ep->ep.name, req, + completed ? "completed" : "not completed"); + if (!EP_NO(imx_ep)) + ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); + } + + D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes); + + return completed; +} + +static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) +{ + int bytes = 0, + count, + completed = 0; + + while (!completed) { + count = write_packet(imx_ep, req); + if (count < 0) + break; /* busy */ + bytes += count; + + /* last packet "must be" short (or a zlp) */ + completed = (count != imx_ep->fifosize); + + if (unlikely(completed)) { + done(imx_ep, req, 0); + D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", + __func__, imx_ep->ep.name, req, + completed ? "completed" : "not completed"); + if (!EP_NO(imx_ep)) + ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); + } + } + + D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes); + + return completed; +} + +/******************************************************************************* + * Endpoint handlers + ******************************************************************************* + */ +static int handle_ep(struct imx_ep_struct *imx_ep) +{ + struct imx_request *req; + int completed = 0; + + do { + if (!list_empty(&imx_ep->queue)) + req = list_entry(imx_ep->queue.next, + struct imx_request, queue); + else { + D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n", + __func__, imx_ep->ep.name); + return 0; + } + + if (EP_DIR(imx_ep)) /* to host */ + completed = write_fifo(imx_ep, req); + else /* to device */ + completed = read_fifo(imx_ep, req); + + dump_ep_stat(__func__, imx_ep); + + } while (completed); + + return 0; +} + +static int handle_ep0(struct imx_ep_struct *imx_ep) +{ + struct imx_request *req = NULL; + int ret = 0; + + if (!list_empty(&imx_ep->queue)) + req = list_entry(imx_ep->queue.next, struct imx_request, queue); + + if (req) { + switch (imx_ep->imx_usb->ep0state) { + + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */ + write_fifo(imx_ep, req); + break; + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */ + read_fifo(imx_ep, req); + break; + default: + D_EP0(imx_ep->imx_usb->dev, + "<%s> ep0 i/o, odd state %d\n", + __func__, imx_ep->imx_usb->ep0state); + ep_del_request(imx_ep, req); + ret = -EL2HLT; + break; + } + } + + return ret; +} + +static void handle_ep0_devreq(struct imx_udc_struct *imx_usb) +{ + struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; + union { + struct usb_ctrlrequest r; + u8 raw[8]; + u32 word[2]; + } u; + int temp, i; + + nuke(imx_ep, -EPROTO); + + /* read SETUP packet */ + for (i = 0; i < 2; i++) { + if (imx_ep_empty(imx_ep)) { + D_ERR(imx_usb->dev, + "<%s> no setup packet received\n", __func__); + goto stall; + } + u.word[i] = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep))); + } + + temp = imx_ep_empty(imx_ep); + while (!imx_ep_empty(imx_ep)) { + i = __raw_readl(imx_usb->base + USB_EP_FDAT(EP_NO(imx_ep))); + D_ERR(imx_usb->dev, + "<%s> wrong to have extra bytes for setup : 0x%08x\n", + __func__, i); + } + if (!temp) + goto stall; + + le16_to_cpus(&u.r.wValue); + le16_to_cpus(&u.r.wIndex); + le16_to_cpus(&u.r.wLength); + + D_REQ(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n", + __func__, u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + + if (imx_usb->set_config) { + /* NACK the host by using CMDOVER */ + temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); + + D_ERR(imx_usb->dev, + "<%s> set config req is pending, NACK the host\n", + __func__); + return; + } + + if (u.r.bRequestType & USB_DIR_IN) + ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE); + else + ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE); + + i = imx_usb->driver->setup(&imx_usb->gadget, &u.r); + if (i < 0) { + D_ERR(imx_usb->dev, "<%s> device setup error %d\n", + __func__, i); + goto stall; + } + + return; +stall: + D_ERR(imx_usb->dev, "<%s> protocol STALL\n", __func__); + imx_ep_stall(imx_ep); + ep0_chg_stat(__func__, imx_usb, EP0_STALL); + return; +} + +/******************************************************************************* + * USB gadget callback functions + ******************************************************************************* + */ + +static int imx_ep_enable(struct usb_ep *usb_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct imx_ep_struct *imx_ep = container_of(usb_ep, + struct imx_ep_struct, ep); + struct imx_udc_struct *imx_usb = imx_ep->imx_usb; + unsigned long flags; + + if (!usb_ep + || !desc + || !EP_NO(imx_ep) + || desc->bDescriptorType != USB_DT_ENDPOINT + || imx_ep->bEndpointAddress != desc->bEndpointAddress) { + D_ERR(imx_usb->dev, + "<%s> bad ep or descriptor\n", __func__); + return -EINVAL; + } + + if (imx_ep->bmAttributes != desc->bmAttributes) { + D_ERR(imx_usb->dev, + "<%s> %s type mismatch\n", __func__, usb_ep->name); + return -EINVAL; + } + + if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) { + D_ERR(imx_usb->dev, + "<%s> bad %s maxpacket\n", __func__, usb_ep->name); + return -ERANGE; + } + + if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { + D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); + return -ESHUTDOWN; + } + + local_irq_save(flags); + + imx_ep->stopped = 0; + imx_flush(imx_ep); + imx_ep_irq_enable(imx_ep); + + local_irq_restore(flags); + + D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name); + return 0; +} + +static int imx_ep_disable(struct usb_ep *usb_ep) +{ + struct imx_ep_struct *imx_ep = container_of(usb_ep, + struct imx_ep_struct, ep); + unsigned long flags; + + if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { + D_ERR(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n", + __func__, usb_ep ? imx_ep->ep.name : NULL); + return -EINVAL; + } + + local_irq_save(flags); + + imx_ep->stopped = 1; + nuke(imx_ep, -ESHUTDOWN); + imx_flush(imx_ep); + imx_ep_irq_disable(imx_ep); + + local_irq_restore(flags); + + D_EPX(imx_ep->imx_usb->dev, + "<%s> DISABLED %s\n", __func__, usb_ep->name); + return 0; +} + +static struct usb_request *imx_ep_alloc_request + (struct usb_ep *usb_ep, gfp_t gfp_flags) +{ + struct imx_request *req; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req || !usb_ep) + return 0; + + INIT_LIST_HEAD(&req->queue); + req->in_use = 0; + + return &req->req; +} + +static void imx_ep_free_request + (struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + struct imx_request *req; + + req = container_of(usb_req, struct imx_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static int imx_ep_queue + (struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) +{ + struct imx_ep_struct *imx_ep; + struct imx_udc_struct *imx_usb; + struct imx_request *req; + unsigned long flags; + int ret = 0; + + imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); + imx_usb = imx_ep->imx_usb; + req = container_of(usb_req, struct imx_request, req); + + /* + Special care on IMX udc. + Ignore enqueue when after set configuration from the + host. This assume all gadget drivers reply set + configuration with the next ep0 req enqueue. + */ + if (imx_usb->set_config && !EP_NO(imx_ep)) { + imx_usb->set_config = 0; + D_EPX(imx_usb->dev, + "<%s> gadget reply set config\n", __func__); + return 0; + } + + if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) { + D_ERR(imx_usb->dev, "<%s> bad params\n", __func__); + return -EINVAL; + } + + if (unlikely(!usb_ep || !imx_ep)) { + D_ERR(imx_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { + D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); + return -ESHUTDOWN; + } + + local_irq_save(flags); + + /* Debug */ + D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n", + __func__, EP_NO(imx_ep), + ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE) + || (EP_NO(imx_ep) && EP_DIR(imx_ep))) ? "IN" : "OUT", usb_req->length); + dump_req(__func__, imx_ep, usb_req); + + if (imx_ep->stopped) { + usb_req->status = -ESHUTDOWN; + ret = -ESHUTDOWN; + goto out; + } + + if (req->in_use) { + D_ERR(imx_usb->dev, + "<%s> refusing to queue req %p (already queued)\n", + __func__, req); + goto out; + } + + usb_req->status = -EINPROGRESS; + usb_req->actual = 0; + + ep_add_request(imx_ep, req); + + if (!EP_NO(imx_ep)) + ret = handle_ep0(imx_ep); + else + ret = handle_ep(imx_ep); +out: + local_irq_restore(flags); + return ret; +} + +static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + struct imx_request *req; + unsigned long flags; + + if (unlikely(!usb_ep || !EP_NO(imx_ep))) { + D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + local_irq_save(flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &imx_ep->queue, queue) { + if (&req->req == usb_req) + break; + } + if (&req->req != usb_req) { + local_irq_restore(flags); + return -EINVAL; + } + + done(imx_ep, req, -ECONNRESET); + + local_irq_restore(flags); + return 0; +} + +static int imx_ep_set_halt(struct usb_ep *usb_ep, int value) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + unsigned long flags; + + if (unlikely(!usb_ep || !EP_NO(imx_ep))) { + D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + local_irq_save(flags); + + if ((imx_ep->bEndpointAddress & USB_DIR_IN) + && !list_empty(&imx_ep->queue)) { + local_irq_restore(flags); + return -EAGAIN; + } + + imx_ep_stall(imx_ep); + + local_irq_restore(flags); + + D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); + return 0; +} + +static int imx_ep_fifo_status(struct usb_ep *usb_ep) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + + if (!usb_ep) { + D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + return -ENODEV; + } + + if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN) + return 0; + else + return imx_fifo_bcount(imx_ep); +} + +static void imx_ep_fifo_flush(struct usb_ep *usb_ep) +{ + struct imx_ep_struct *imx_ep = container_of + (usb_ep, struct imx_ep_struct, ep); + unsigned long flags; + + local_irq_save(flags); + + if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { + D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); + local_irq_restore(flags); + return; + } + + /* toggle and halt bits stay unchanged */ + imx_flush(imx_ep); + + local_irq_restore(flags); +} + +static struct usb_ep_ops imx_ep_ops = { + .enable = imx_ep_enable, + .disable = imx_ep_disable, + + .alloc_request = imx_ep_alloc_request, + .free_request = imx_ep_free_request, + + .queue = imx_ep_queue, + .dequeue = imx_ep_dequeue, + + .set_halt = imx_ep_set_halt, + .fifo_status = imx_ep_fifo_status, + .fifo_flush = imx_ep_fifo_flush, +}; + +/******************************************************************************* + * USB endpoint control functions + ******************************************************************************* + */ + +void ep0_chg_stat(const char *label, + struct imx_udc_struct *imx_usb, enum ep0_state stat) +{ + D_EP0(imx_usb->dev, "<%s> from %15s to %15s\n", + label, state_name[imx_usb->ep0state], state_name[stat]); + + if (imx_usb->ep0state == stat) + return; + + imx_usb->ep0state = stat; +} + +static void usb_init_data(struct imx_udc_struct *imx_usb) +{ + struct imx_ep_struct *imx_ep; + u8 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD(&imx_usb->gadget.ep_list); + INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list); + ep0_chg_stat(__func__, imx_usb, EP0_IDLE); + + /* basic endpoint records init */ + for (i = 0; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + + if (i) { + list_add_tail(&imx_ep->ep.ep_list, + &imx_usb->gadget.ep_list); + imx_ep->stopped = 1; + } else + imx_ep->stopped = 0; + + INIT_LIST_HEAD(&imx_ep->queue); + } +} + +static void udc_stop_activity(struct imx_udc_struct *imx_usb, + struct usb_gadget_driver *driver) +{ + struct imx_ep_struct *imx_ep; + int i; + + if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 1; i < IMX_USB_NB_EP; i++) { + imx_ep = &imx_usb->imx_ep[i]; + imx_flush(imx_ep); + imx_ep->stopped = 1; + imx_ep_irq_disable(imx_ep); + nuke(imx_ep, -ESHUTDOWN); + } + + imx_usb->cfg = 0; + imx_usb->intf = 0; + imx_usb->alt = 0; + + if (driver) + driver->disconnect(&imx_usb->gadget); +} + +/******************************************************************************* + * Interrupt handlers + ******************************************************************************* + */ + +static irqreturn_t imx_udc_irq(int irq, void *dev) +{ + struct imx_udc_struct *imx_usb = dev; + struct usb_ctrlrequest u; + int temp, cfg, intf, alt; + int intr = __raw_readl(imx_usb->base + USB_INTR); + + if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START + | INTR_RESET_STOP | INTR_CFG_CHG)) { + dump_intr(__func__, intr, imx_usb->dev); + dump_usb_stat(__func__, imx_usb); + } + + if (!imx_usb->driver) { + /*imx_udc_disable(imx_usb);*/ + goto end_irq; + } + + if (intr & INTR_WAKEUP) { + if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN + && imx_usb->driver && imx_usb->driver->resume) + imx_usb->driver->resume(&imx_usb->gadget); + imx_usb->set_config = 0; + imx_usb->gadget.speed = USB_SPEED_FULL; + } + + if (intr & INTR_SUSPEND) { + if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN + && imx_usb->driver && imx_usb->driver->suspend) + imx_usb->driver->suspend(&imx_usb->gadget); + imx_usb->set_config = 0; + imx_usb->gadget.speed = USB_SPEED_UNKNOWN; + } + + if (intr & INTR_RESET_START) { + __raw_writel(intr, imx_usb->base + USB_INTR); + udc_stop_activity(imx_usb, imx_usb->driver); + imx_usb->set_config = 0; + imx_usb->gadget.speed = USB_SPEED_UNKNOWN; + } + + if (intr & INTR_RESET_STOP) + imx_usb->gadget.speed = USB_SPEED_FULL; + + if (intr & INTR_CFG_CHG) { + __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR); + temp = __raw_readl(imx_usb->base + USB_STAT); + cfg = (temp & STAT_CFG) >> 5; + intf = (temp & STAT_INTF) >> 3; + alt = temp & STAT_ALTSET; + + D_REQ(imx_usb->dev, + "<%s> orig config C=%d, I=%d, A=%d / " + "req config C=%d, I=%d, A=%d\n", + __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, + cfg, intf, alt); + + if (cfg != 1 && cfg != 2) + goto end_irq; + + imx_usb->set_config = 0; + + /* Config setup */ + if (imx_usb->cfg != cfg) { + D_REQ(imx_usb->dev, "<%s> Change config start\n",__func__); + u.bRequest = USB_REQ_SET_CONFIGURATION; + u.bRequestType = USB_DIR_OUT | + USB_TYPE_STANDARD | + USB_RECIP_DEVICE; + u.wValue = cfg; + u.wIndex = 0; + u.wLength = 0; + imx_usb->cfg = cfg; + imx_usb->set_config = 1; + imx_usb->driver->setup(&imx_usb->gadget, &u); + imx_usb->set_config = 0; + D_REQ(imx_usb->dev, "<%s> Change config done\n",__func__); + + } + if (imx_usb->intf != intf || imx_usb->alt != alt) { + D_REQ(imx_usb->dev, "<%s> Change interface start\n",__func__); + u.bRequest = USB_REQ_SET_INTERFACE; + u.bRequestType = USB_DIR_OUT | + USB_TYPE_STANDARD | + USB_RECIP_INTERFACE; + u.wValue = alt; + u.wIndex = intf; + u.wLength = 0; + imx_usb->intf = intf; + imx_usb->alt = alt; + imx_usb->set_config = 1; + imx_usb->driver->setup(&imx_usb->gadget, &u); + imx_usb->set_config = 0; + D_REQ(imx_usb->dev, "<%s> Change interface done\n",__func__); + } + } + + if (intr & INTR_SOF) { + if (imx_usb->ep0state == EP0_IDLE) { + temp = __raw_readl(imx_usb->base + USB_CTRL); + __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); + } + } + +end_irq: + __raw_writel(intr, imx_usb->base + USB_INTR); + return IRQ_HANDLED; +} + +static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev) +{ + struct imx_udc_struct *imx_usb = dev; + int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0)); + + dump_ep_intr(__func__, 0, intr, imx_usb->dev); + + if (!imx_usb->driver) { + __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); + return IRQ_HANDLED; + } + + /* DEVREQ IRQ has highest priority */ + if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ)) + handle_ep0_devreq(imx_usb); + /* Seem i.MX is missing EOF interrupt sometimes. + * Therefore we monitor both EOF and FIFO_EMPTY interrups + * when transmiting, and both EOF and FIFO_FULL when + * receiving data. + */ + else if (intr & (EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL)) + handle_ep0(&imx_usb->imx_ep[0]); + + __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); + + return IRQ_HANDLED; +} + +static irqreturn_t imx_udc_bulk_irq(int irq, void *dev) +{ + struct imx_udc_struct *imx_usb = dev; + struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0]; + int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + + dump_ep_intr(__func__, irq - USBD_INT0, intr, imx_usb->dev); + + if (!imx_usb->driver) { + __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + return IRQ_HANDLED; + } + + handle_ep(imx_ep); + + __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + + return IRQ_HANDLED; +} + +irq_handler_t intr_handler(int i) +{ + switch (i) { + case 0: + return imx_udc_ctrl_irq; + case 1: + case 2: + case 3: + case 4: + case 5: + return imx_udc_bulk_irq; + default: + return imx_udc_irq; + } +} + +/******************************************************************************* + * Static defined IMX UDC structure + ******************************************************************************* + */ + +static const struct usb_gadget_ops imx_udc_ops = { + .get_frame = imx_udc_get_frame, + .wakeup = imx_udc_wakeup, +}; + +static struct imx_udc_struct controller = { + .gadget = { + .ops = &imx_udc_ops, + .ep0 = &controller.imx_ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + }, + }, + + .imx_ep[0] = { + .ep = { + .name = ep0name, + .ops = &imx_ep_ops, + .maxpacket = 32, + }, + .imx_usb = &controller, + .fifosize = 32, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }, + .imx_ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &imx_ep_ops, + .maxpacket = 64, + }, + .imx_usb = &controller, + .fifosize = 64, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .imx_ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &imx_ep_ops, + .maxpacket = 64, + }, + .imx_usb = &controller, + .fifosize = 64, + .bEndpointAddress = USB_DIR_OUT | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .imx_ep[3] = { + .ep = { + .name = "ep3out-bulk", + .ops = &imx_ep_ops, + .maxpacket = 32, + }, + .imx_usb = &controller, + .fifosize = 32, + .bEndpointAddress = USB_DIR_OUT | 3, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .imx_ep[4] = { + .ep = { + .name = "ep4in-int", + .ops = &imx_ep_ops, + .maxpacket = 32, + }, + .imx_usb = &controller, + .fifosize = 32, + .bEndpointAddress = USB_DIR_IN | 4, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, + .imx_ep[5] = { + .ep = { + .name = "ep5out-int", + .ops = &imx_ep_ops, + .maxpacket = 32, + }, + .imx_usb = &controller, + .fifosize = 32, + .bEndpointAddress = USB_DIR_OUT | 5, + .bmAttributes = USB_ENDPOINT_XFER_INT, + }, +}; + +/******************************************************************************* + * USB gadged driver functions + ******************************************************************************* + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct imx_udc_struct *imx_usb = &controller; + int retval; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!imx_usb) + return -ENODEV; + if (imx_usb->driver) + return -EBUSY; + + /* first hook up the driver ... */ + imx_usb->driver = driver; + imx_usb->gadget.dev.driver = &driver->driver; + + retval = device_add(&imx_usb->gadget.dev); + if (retval) + goto fail; + retval = driver->bind(&imx_usb->gadget); + if (retval) { + D_ERR(imx_usb->dev, "<%s> bind to driver %s --> error %d\n", + __func__, driver->driver.name, retval); + device_del(&imx_usb->gadget.dev); + + goto fail; + } + + D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n", + __func__, driver->driver.name); + + imx_udc_enable(imx_usb); + + return 0; +fail: + imx_usb->driver = NULL; + imx_usb->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct imx_udc_struct *imx_usb = &controller; + + if (!imx_usb) + return -ENODEV; + if (!driver || driver != imx_usb->driver || !driver->unbind) + return -EINVAL; + + udc_stop_activity(imx_usb, driver); + imx_udc_disable(imx_usb); + + driver->unbind(&imx_usb->gadget); + imx_usb->gadget.dev.driver = NULL; + imx_usb->driver = NULL; + + device_del(&imx_usb->gadget.dev); + + D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n", + __func__, driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/******************************************************************************* + * Module functions + ******************************************************************************* + */ + +static int __init imx_udc_probe(struct platform_device *pdev) +{ + struct imx_udc_struct *imx_usb = &controller; + struct resource *res; + struct imxusb_platform_data *pdata; + struct clk *clk; + void __iomem *base; + int ret = 0; + int i, res_size; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "driver needs platform data\n"); + return -ENODEV; + } + + res_size = res->end - res->start + 1; + if (!request_mem_region(res->start, res_size, res->name)) { + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", + res_size, res->start); + return -ENOMEM; + } + + if (pdata->init) { + ret = pdata->init(&pdev->dev); + if (ret) + goto fail0; + } + + base = ioremap(res->start, res_size); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -EIO; + goto fail1; + } + + clk = clk_get(NULL, "usbd_clk"); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(&pdev->dev, "can't get USB clock\n"); + goto fail2; + } + clk_enable(clk); + + if (clk_get_rate(clk) != 48000000) { + D_INI(&pdev->dev, + "Bad USB clock (%d Hz), changing to 48000000 Hz\n", + (int)clk_get_rate(clk)); + if (clk_set_rate(clk, 48000000)) { + dev_err(&pdev->dev, + "Unable to set correct USB clock (48MHz)\n"); + ret = -EIO; + goto fail3; + } + } + + for (i = 0; i < IMX_USB_NB_EP + 1; i++) { + imx_usb->usbd_int[i] = platform_get_irq(pdev, i); + if (imx_usb->usbd_int[i] < 0) { + dev_err(&pdev->dev, "can't get irq number\n"); + ret = -ENODEV; + goto fail3; + } + } + + for (i = 0; i < IMX_USB_NB_EP + 1; i++) { + ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), + IRQF_DISABLED, driver_name, imx_usb); + if (ret) { + dev_err(&pdev->dev, "can't get irq %i, err %d\n", + imx_usb->usbd_int[i], ret); + for (--i; i >= 0; i--) + free_irq(imx_usb->usbd_int[i], imx_usb); + goto fail3; + } + } + + imx_usb->res = res; + imx_usb->base = base; + imx_usb->clk = clk; + imx_usb->dev = &pdev->dev; + + device_initialize(&imx_usb->gadget.dev); + + imx_usb->gadget.dev.parent = &pdev->dev; + imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; + + platform_set_drvdata(pdev, imx_usb); + + usb_init_data(imx_usb); + imx_udc_init(imx_usb); + + return 0; + +fail3: + clk_put(clk); + clk_disable(clk); +fail2: + iounmap(base); +fail1: + if (pdata->exit) + pdata->exit(&pdev->dev); +fail0: + release_mem_region(res->start, res_size); + return ret; +} + +static int __exit imx_udc_remove(struct platform_device *pdev) +{ + struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev); + struct imxusb_platform_data *pdata = pdev->dev.platform_data; + int i; + + imx_udc_disable(imx_usb); + + for (i = 0; i < IMX_USB_NB_EP + 1; i++) + free_irq(imx_usb->usbd_int[i], imx_usb); + + clk_put(imx_usb->clk); + clk_disable(imx_usb->clk); + iounmap(imx_usb->base); + + release_mem_region(imx_usb->res->start, + imx_usb->res->end - imx_usb->res->start + 1); + + if (pdata->exit) + pdata->exit(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM +#define imx_udc_suspend NULL +#define imx_udc_resume NULL +#else +#define imx_udc_suspend NULL +#define imx_udc_resume NULL +#endif + +/*----------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, + .remove = __exit_p(imx_udc_remove), + .suspend = imx_udc_suspend, + .resume = imx_udc_resume, +}; + +static int __init udc_init(void) +{ + return platform_driver_probe(&udc_driver, imx_udc_probe); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION("IMX USB Device Controller driver"); +MODULE_AUTHOR("Darius Augulis <augulis.darius@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx_udc"); diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h new file mode 100644 index 000000000000..850076937d8d --- /dev/null +++ b/drivers/usb/gadget/imx_udc.h @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2005 Mike Lee(eemike@gmail.com) + * + * This udc driver is now under testing and code is based on pxa2xx_udc.h + * Please use it with your own risk! + * + * 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 __LINUX_USB_GADGET_IMX_H +#define __LINUX_USB_GADGET_IMX_H + +#include <linux/types.h> + +/* Helper macros */ +#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */ +#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) +#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/ +#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0) +#define IMX_USB_NB_EP 6 + +/* Driver structures */ +struct imx_request { + struct usb_request req; + struct list_head queue; + unsigned int in_use; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_CONFIG, + EP0_STALL, +}; + +struct imx_ep_struct { + struct usb_ep ep; + struct imx_udc_struct *imx_usb; + struct list_head queue; + unsigned char stopped; + unsigned char fifosize; + unsigned char bEndpointAddress; + unsigned char bmAttributes; +}; + +struct imx_udc_struct { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + struct imx_ep_struct imx_ep[IMX_USB_NB_EP]; + struct clk *clk; + enum ep0_state ep0state; + struct resource *res; + void __iomem *base; + unsigned char set_config; + int cfg, + intf, + alt, + usbd_int[7]; +}; + +/* USB registers */ +#define USB_FRAME (0x00) /* USB frame */ +#define USB_SPEC (0x04) /* USB Spec */ +#define USB_STAT (0x08) /* USB Status */ +#define USB_CTRL (0x0C) /* USB Control */ +#define USB_DADR (0x10) /* USB Desc RAM addr */ +#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */ +#define USB_INTR (0x18) /* USB interrupt */ +#define USB_MASK (0x1C) /* USB Mask */ +#define USB_ENAB (0x24) /* USB Enable */ +#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */ +#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */ +#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */ +#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */ +#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */ +#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */ +#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last read frame pointer */ +#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last write frame pointer */ +#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */ +#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */ +#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */ +/* USB Control Register Bit Fields.*/ +#define CTRL_CMDOVER (1<<6) /* UDC status */ +#define CTRL_CMDERROR (1<<5) /* UDC status */ +#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */ +#define CTRL_UDC_RST (1<<2) /* UDC reset */ +#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */ +#define CTRL_RESUME (1<<0) /* UDC resume */ +/* USB Status Register Bit Fields.*/ +#define STAT_RST (1<<8) +#define STAT_SUSP (1<<7) +#define STAT_CFG (3<<5) +#define STAT_INTF (3<<3) +#define STAT_ALTSET (7<<0) +/* USB Interrupt Status/Mask Registers Bit fields */ +#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */ +#define INTR_MSOF (1<<7) /* Missed Start of Frame */ +#define INTR_SOF (1<<6) /* Start of Frame */ +#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */ +#define INTR_RESET_START (1<<4) /* Reset Signaling start */ +#define INTR_RESUME (1<<3) /* Suspend to resume */ +#define INTR_SUSPEND (1<<2) /* Active to suspend */ +#define INTR_FRAME_MATCH (1<<1) /* Frame matched */ +#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */ +/* USB Enable Register Bit Fields.*/ +#define ENAB_RST (1<<31) /* Reset USB modules */ +#define ENAB_ENAB (1<<30) /* Enable USB modules*/ +#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */ +#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */ +#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */ +/* USB Descriptor Ram Address Register bit fields */ +#define DADR_CFG (1<<31) /* Configuration */ +#define DADR_BSY (1<<30) /* Busy status */ +#define DADR_DADR (0x1FF) /* Descriptor Ram Address */ +/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */ +#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */ +/* USB Endpoint Status Register bit fields */ +#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */ +#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */ +#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */ +#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */ +#define EPSTAT_TYP (3<<3) /* Endpoint type */ +#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */ +#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */ +#define EPSTAT_STALL (1<<0) /* Force stall */ +/* USB Endpoint FIFO Status Register bit fields */ +#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */ +#define FSTAT_ERR (1<<22) /* FIFO error */ +#define FSTAT_UF (1<<21) /* FIFO underflow */ +#define FSTAT_OF (1<<20) /* FIFO overflow */ +#define FSTAT_FR (1<<19) /* FIFO frame ready */ +#define FSTAT_FULL (1<<18) /* FIFO full */ +#define FSTAT_ALRM (1<<17) /* FIFO alarm */ +#define FSTAT_EMPTY (1<<16) /* FIFO empty */ +/* USB Endpoint FIFO Control Register bit fields */ +#define FCTRL_WFR (1<<29) /* Write frame end */ +/* USB Endpoint Interrupt Status Regsiter bit fields */ +#define EPINTR_FIFO_FULL (1<<8) /* fifo full */ +#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */ +#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */ +#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */ +#define EPINTR_FIFO_LOW (1<<4) /* fifo low */ +#define EPINTR_MDEVREQ (1<<3) /* multi Device request */ +#define EPINTR_EOT (1<<2) /* fifo end of transfer */ +#define EPINTR_DEVREQ (1<<1) /* Device request */ +#define EPINTR_EOF (1<<0) /* fifo end of frame */ + +/* Debug macros */ +#ifdef DEBUG + +/* #define DEBUG_REQ */ +/* #define DEBUG_TRX */ +/* #define DEBUG_INIT */ +/* #define DEBUG_EP0 */ +/* #define DEBUG_EPX */ +/* #define DEBUG_IRQ */ +/* #define DEBUG_EPIRQ */ +/* #define DEBUG_DUMP */ +#define DEBUG_ERR + +#ifdef DEBUG_REQ + #define D_REQ(dev, args...) dev_dbg(dev, ## args) +#else + #define D_REQ(dev, args...) do {} while (0) +#endif /* DEBUG_REQ */ + +#ifdef DEBUG_TRX + #define D_TRX(dev, args...) dev_dbg(dev, ## args) +#else + #define D_TRX(dev, args...) do {} while (0) +#endif /* DEBUG_TRX */ + +#ifdef DEBUG_INIT + #define D_INI(dev, args...) dev_dbg(dev, ## args) +#else + #define D_INI(dev, args...) do {} while (0) +#endif /* DEBUG_INIT */ + +#ifdef DEBUG_EP0 + static const char *state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", + "EP0_OUT_DATA_PHASE", + "EP0_CONFIG", + "EP0_STALL" + }; + #define D_EP0(dev, args...) dev_dbg(dev, ## args) +#else + #define D_EP0(dev, args...) do {} while (0) +#endif /* DEBUG_EP0 */ + +#ifdef DEBUG_EPX + #define D_EPX(dev, args...) dev_dbg(dev, ## args) +#else + #define D_EPX(dev, args...) do {} while (0) +#endif /* DEBUG_EP0 */ + +#ifdef DEBUG_IRQ + static void dump_intr(const char *label, int irqreg, struct device *dev) + { + dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, + (irqreg & INTR_WAKEUP) ? " wake" : "", + (irqreg & INTR_MSOF) ? " msof" : "", + (irqreg & INTR_SOF) ? " sof" : "", + (irqreg & INTR_RESUME) ? " resume" : "", + (irqreg & INTR_SUSPEND) ? " suspend" : "", + (irqreg & INTR_RESET_STOP) ? " noreset" : "", + (irqreg & INTR_RESET_START) ? " reset" : "", + (irqreg & INTR_FRAME_MATCH) ? " fmatch" : "", + (irqreg & INTR_CFG_CHG) ? " config" : ""); + } +#else + #define dump_intr(x, y, z) do {} while (0) +#endif /* DEBUG_IRQ */ + +#ifdef DEBUG_EPIRQ + static void dump_ep_intr(const char *label, int nr, int irqreg, struct device *dev) + { + dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr, + (irqreg & EPINTR_FIFO_FULL) ? " full" : "", + (irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "", + (irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "", + (irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "", + (irqreg & EPINTR_FIFO_LOW) ? " flow" : "", + (irqreg & EPINTR_MDEVREQ) ? " mreq" : "", + (irqreg & EPINTR_EOF) ? " eof" : "", + (irqreg & EPINTR_DEVREQ) ? " devreq" : "", + (irqreg & EPINTR_EOT) ? " eot" : ""); + } +#else + #define dump_ep_intr(x, y, z, i) do {} while (0) +#endif /* DEBUG_IRQ */ + +#ifdef DEBUG_DUMP + static void dump_usb_stat(const char *label, struct imx_udc_struct *imx_usb) + { + int temp = __raw_readl(imx_usb->base + USB_STAT); + + dev_dbg(imx_usb->dev, + "<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label, + (temp & STAT_RST) ? " reset" : "", + (temp & STAT_SUSP) ? " suspend" : "", + (temp & STAT_CFG) >> 5, + (temp & STAT_INTF) >> 3, + (temp & STAT_ALTSET)); + } + + static void dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep) + { + int temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); + + dev_dbg(imx_ep->imx_usb->dev, + "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep), + (temp & EPINTR_FIFO_FULL) ? " full" : "", + (temp & EPINTR_FIFO_EMPTY) ? " fempty" : "", + (temp & EPINTR_FIFO_ERROR) ? " ferr" : "", + (temp & EPINTR_FIFO_HIGH) ? " fhigh" : "", + (temp & EPINTR_FIFO_LOW) ? " flow" : "", + (temp & EPINTR_MDEVREQ) ? " mreq" : "", + (temp & EPINTR_EOF) ? " eof" : "", + (temp & EPINTR_DEVREQ) ? " devreq" : "", + (temp & EPINTR_EOT) ? " eot" : ""); + + temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); + + dev_dbg(imx_ep->imx_usb->dev, + "<%s> EP%d_STAT=[%s%s bcount=%d]\n", label, EP_NO(imx_ep), + (temp & EPSTAT_SIP) ? " sip" : "", + (temp & EPSTAT_STALL) ? " stall" : "", + (temp & EPSTAT_BCOUNT) >> 16); + + temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))); + + dev_dbg(imx_ep->imx_usb->dev, + "<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep), + (temp & FSTAT_ERR) ? " ferr" : "", + (temp & FSTAT_UF) ? " funder" : "", + (temp & FSTAT_OF) ? " fover" : "", + (temp & FSTAT_FR) ? " fready" : "", + (temp & FSTAT_FULL) ? " ffull" : "", + (temp & FSTAT_ALRM) ? " falarm" : "", + (temp & FSTAT_EMPTY) ? " fempty" : ""); + } + + static void dump_req(const char *label, struct imx_ep_struct *imx_ep, struct usb_request *req) + { + int i; + + if (!req || !req->buf) { + dev_dbg(imx_ep->imx_usb->dev, "<%s> req or req buf is free\n", label); + return; + } + + if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE) + || (EP_NO(imx_ep) && EP_DIR(imx_ep))) { + + dev_dbg(imx_ep->imx_usb->dev, "<%s> request dump <", label); + for (i = 0; i < req->length; i++) + printk("%02x-", *((u8 *)req->buf + i)); + printk(">\n"); + } + } + +#else + #define dump_ep_stat(x, y) do {} while (0) + #define dump_usb_stat(x, y) do {} while (0) + #define dump_req(x, y, z) do {} while (0) +#endif /* DEBUG_DUMP */ + +#ifdef DEBUG_ERR + #define D_ERR(dev, args...) dev_dbg(dev, ## args) +#else + #define D_ERR(dev, args...) do {} while (0) +#endif + +#else + #define D_REQ(dev, args...) do {} while (0) + #define D_TRX(dev, args...) do {} while (0) + #define D_INI(dev, args...) do {} while (0) + #define D_EP0(dev, args...) do {} while (0) + #define D_EPX(dev, args...) do {} while (0) + #define dump_ep_intr(x, y, z, i) do {} while (0) + #define dump_intr(x, y, z) do {} while (0) + #define dump_ep_stat(x, y) do {} while (0) + #define dump_usb_stat(x, y) do {} while (0) + #define dump_req(x, y, z) do {} while (0) + #define D_ERR(dev, args...) do {} while (0) +#endif /* DEBUG */ + +#endif /* __LINUX_USB_GADGET_IMX_H */ diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 3a8879ec2061..43dcf9e1af6b 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1546,8 +1546,6 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } -#define resource_len(r) (((r)->end - (r)->start) + 1) - static int __init m66592_probe(struct platform_device *pdev) { struct resource *res; @@ -1560,11 +1558,10 @@ static int __init m66592_probe(struct platform_device *pdev) int ret = 0; int i; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (char *)udc_name); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - pr_err("platform_get_resource_byname error.\n"); + pr_err("platform_get_resource error.\n"); goto clean_up; } @@ -1575,7 +1572,7 @@ static int __init m66592_probe(struct platform_device *pdev) goto clean_up; } - reg = ioremap(res->start, resource_len(res)); + reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; pr_err("ioremap error.\n"); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 8ae70de2c37d..12c6d83b218c 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -669,7 +669,7 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ wmb (); - td->dmacount = cpu_to_le32p (&dmacount); + td->dmacount = cpu_to_le32(dmacount); } static const u32 dmactl_default = diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 34e9e393f929..57d9641c6bf8 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -3006,7 +3006,7 @@ cleanup1: cleanup0: if (xceiv) - put_device(xceiv->dev); + otg_put_transceiver(xceiv); if (cpu_is_omap16xx() || cpu_is_omap24xx()) { clk_disable(hhc_clk); @@ -3034,7 +3034,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev) pullup_disable(udc); if (udc->transceiver) { - put_device(udc->transceiver->dev); + otg_put_transceiver(udc->transceiver); udc->transceiver = NULL; } omap_writew(0, UDC_SYSCON1); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 697a0ca349bf..9b36205c5759 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2198,7 +2198,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) udc_disable(dev); udc_reinit(dev); - dev->vbus = is_vbus_present(); + dev->vbus = !!is_vbus_present(); /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa25x_udc_irq, diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 65110d02a206..990f40f988d4 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -430,7 +430,6 @@ static void pio_irq_enable(struct pxa_ep *ep) /** * pio_irq_disable - Disables irq generation for one endpoint * @ep: udc endpoint - * @index: endpoint number */ static void pio_irq_disable(struct pxa_ep *ep) { @@ -586,7 +585,6 @@ static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in) * inc_ep_stats_bytes - Update ep stats counts * @ep: physical endpoint * @count: bytes transfered on endpoint - * @req: usb request * @is_in: ep direction (USB_DIR_IN or 0) */ static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in) diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index c7e255636803..9a2b8920532d 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -36,6 +36,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/gpio.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -51,7 +52,6 @@ #include <mach/irqs.h> #include <mach/hardware.h> -#include <mach/regs-gpio.h> #include <plat/regs-udc.h> #include <plat/udc.h> @@ -1510,11 +1510,7 @@ static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev) dprintk(DEBUG_NORMAL, "%s()\n", __func__); - /* some cpus cannot read from an line configured to IRQ! */ - s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_INPUT); - value = s3c2410_gpio_getpin(udc_info->vbus_pin); - s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_SFN2); - + value = gpio_get_value(udc_info->vbus_pin) ? 1 : 0; if (udc_info->vbus_pin_inverted) value = !value; @@ -1802,7 +1798,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) struct s3c2410_udc *udc = &memory; struct device *dev = &pdev->dev; int retval; - unsigned int irq; + int irq; dev_dbg(dev, "%s()\n", __func__); @@ -1861,7 +1857,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, - IRQF_DISABLED, gadget_name, udc); + IRQF_DISABLED, gadget_name, udc); if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); @@ -1872,17 +1868,28 @@ static int s3c2410_udc_probe(struct platform_device *pdev) dev_dbg(dev, "got irq %i\n", IRQ_USBD); if (udc_info && udc_info->vbus_pin > 0) { - irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + retval = gpio_request(udc_info->vbus_pin, "udc vbus"); + if (retval < 0) { + dev_err(dev, "cannot claim vbus pin\n"); + goto err_int; + } + + irq = gpio_to_irq(udc_info->vbus_pin); + if (irq < 0) { + dev_err(dev, "no irq for gpio vbus pin\n"); + goto err_gpio_claim; + } + retval = request_irq(irq, s3c2410_udc_vbus_irq, IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, gadget_name, udc); if (retval != 0) { - dev_err(dev, "can't get vbus irq %i, err %d\n", + dev_err(dev, "can't get vbus irq %d, err %d\n", irq, retval); retval = -EBUSY; - goto err_int; + goto err_gpio_claim; } dev_dbg(dev, "got irq %i\n", irq); @@ -1902,6 +1909,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev) return 0; +err_gpio_claim: + if (udc_info && udc_info->vbus_pin > 0) + gpio_free(udc_info->vbus_pin); err_int: free_irq(IRQ_USBD, udc); err_map: @@ -1927,7 +1937,7 @@ static int s3c2410_udc_remove(struct platform_device *pdev) debugfs_remove(udc->regs_info); if (udc_info && udc_info->vbus_pin > 0) { - irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + irq = gpio_to_irq(udc_info->vbus_pin); free_irq(irq, udc); } diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index d9739d52f8f5..96d65ca06ecd 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -716,6 +716,14 @@ static int __init get_ether_addr(const char *str, u8 *dev_addr) static struct eth_dev *the_dev; +static const struct net_device_ops eth_netdev_ops = { + .ndo_open = eth_open, + .ndo_stop = eth_stop, + .ndo_start_xmit = eth_start_xmit, + .ndo_change_mtu = ueth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; /** * gether_setup - initialize one ethernet-over-usb link @@ -764,12 +772,8 @@ int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]) if (ethaddr) memcpy(ethaddr, dev->host_mac, ETH_ALEN); - net->change_mtu = ueth_change_mtu; - net->hard_start_xmit = eth_start_xmit; - net->open = eth_open; - net->stop = eth_stop; - /* watchdog_timeo, tx_timeout ... */ - /* set_multicast_list */ + net->netdev_ops = ð_netdev_ops; + SET_ETHTOOL_OPS(net, &ops); /* two kinds of host-initiated state changes: diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index f3a75a929e0a..2b476b6b3d4d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -96,6 +96,19 @@ config USB_EHCI_HCD_PPC_OF Enables support for the USB controller present on the PowerPC OpenFirmware platform bus. +config USB_OXU210HP_HCD + tristate "OXU210HP HCD support" + depends on USB + ---help--- + The OXU210HP is an USB host/OTG/device controller. Enable this + option if your board has this chip. If unsure, say N. + + This driver does not support isochronous transfers and doesn't + implement OTG nor USB device controllers. + + To compile this driver as a module, choose M here: the + module will be called oxu210hp-hcd. + config USB_ISP116X_HCD tristate "ISP116X HCD support" depends on USB diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 23be22224044..e5f3f20787e4 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci/ obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o +obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 0cb53ca8d343..7f4ace73d44a 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -455,9 +455,7 @@ static void qh_lines ( (scratch >> 16) & 0x7fff, scratch, td->urb); - if (temp < 0) - temp = 0; - else if (size < temp) + if (size < temp) temp = size; size -= temp; next += temp; @@ -466,9 +464,7 @@ static void qh_lines ( } temp = snprintf (next, size, "\n"); - if (temp < 0) - temp = 0; - else if (size < temp) + if (size < temp) temp = size; size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 218f9660d7ee..97a53a48a3d8 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -194,6 +194,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) u32 temp; u32 power_okay; int i; + u8 resume_needed = 0; if (time_before (jiffies, ehci->next_statechange)) msleep(5); @@ -228,7 +229,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /* Some controller/firmware combinations need a delay during which * they set up the port statuses. See Bugzilla #8190. */ - mdelay(8); + spin_unlock_irq(&ehci->lock); + msleep(8); + spin_lock_irq(&ehci->lock); /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); @@ -236,12 +239,21 @@ static int ehci_bus_resume (struct usb_hcd *hcd) temp = ehci_readl(ehci, &ehci->regs->port_status [i]); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(i, &ehci->bus_suspended) && - (temp & PORT_SUSPEND)) + (temp & PORT_SUSPEND)) { temp |= PORT_RESUME; + resume_needed = 1; + } ehci_writel(ehci, temp, &ehci->regs->port_status [i]); } + + /* msleep for 20ms only if code is trying to resume port */ + if (resume_needed) { + spin_unlock_irq(&ehci->lock); + msleep(20); + spin_lock_irq(&ehci->lock); + } + i = HCS_N_PORTS (ehci->hcs_params); - mdelay (20); while (i--) { temp = ehci_readl(ehci, &ehci->regs->port_status [i]); if (test_bit(i, &ehci->bus_suspended) && @@ -422,8 +434,15 @@ static int check_reset_complete ( port_status &= ~PORT_RWC_BITS; ehci_writel(ehci, port_status, status_reg); - } else + /* ensure 440EPX ohci controller state is operational */ + if (ehci->has_amcc_usb23) + set_ohci_hcfs(ehci, 1); + } else { ehci_dbg (ehci, "port %d high speed\n", index + 1); + /* ensure 440EPx ohci controller state is suspended */ + if (ehci->has_amcc_usb23) + set_ohci_hcfs(ehci, 0); + } return port_status; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 36864f958444..bdc6e86e1f8b 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -219,15 +219,19 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); - /* Workaround current PCI init glitch: wakeup bits aren't - * being set from PCI PM capability. + /* Keep this around for a while just in case some EHCI + * implementation uses legacy PCI PM support. This test + * can be removed on 17 Dec 2009 if the dev_warn() hasn't + * been triggered by then. */ if (!device_can_wakeup(&pdev->dev)) { u16 port_wake; pci_read_config_word(pdev, 0x62, &port_wake); - if (port_wake & 0x0001) + if (port_wake & 0x0001) { + dev_warn(&pdev->dev, "Enabling legacy PCI PM\n"); device_init_wakeup(&pdev->dev, 1); + } } #ifdef CONFIG_USB_SUSPEND @@ -428,6 +432,8 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, + .suspend_late = usb_hcd_pci_suspend_late, + .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif .shutdown = usb_hcd_pci_shutdown, diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index b018deed2e8f..ef732b704f53 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -107,11 +107,13 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dn = op->node; struct usb_hcd *hcd; - struct ehci_hcd *ehci; + struct ehci_hcd *ehci = NULL; struct resource res; int irq; int rv; + struct device_node *np; + if (usb_disabled()) return -ENODEV; @@ -149,6 +151,20 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) } ehci = hcd_to_ehci(hcd); + np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); + if (np != NULL) { + /* claim we really affected by usb23 erratum */ + if (!of_address_to_resource(np, 0, &res)) + ehci->ohci_hcctrl_reg = ioremap(res.start + + OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN); + else + pr_debug(__FILE__ ": no ohci offset in fdt\n"); + if (!ehci->ohci_hcctrl_reg) { + pr_debug(__FILE__ ": ioremap for ohci hcctrl failed\n"); + } else { + ehci->has_amcc_usb23 = 1; + } + } if (of_get_property(dn, "big-endian", NULL)) { ehci->big_endian_mmio = 1; @@ -181,6 +197,9 @@ err_ioremap: irq_dispose_mapping(irq); err_irq: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + if (ehci->has_amcc_usb23) + iounmap(ehci->ohci_hcctrl_reg); err_rmr: usb_put_hcd(hcd); @@ -191,6 +210,11 @@ err_rmr: static int ehci_hcd_ppc_of_remove(struct of_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + struct device_node *np; + struct resource res; + dev_set_drvdata(&op->dev, NULL); dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); @@ -201,6 +225,25 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op) irq_dispose_mapping(hcd->irq); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + /* use request_mem_region to test if the ohci driver is loaded. if so + * ensure the ohci core is operational. + */ + if (ehci->has_amcc_usb23) { + np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx"); + if (np != NULL) { + if (!of_address_to_resource(np, 0, &res)) + if (!request_mem_region(res.start, + 0x4, hcd_name)) + set_ohci_hcfs(ehci, 1); + else + release_mem_region(res.start, 0x4); + else + pr_debug(__FILE__ ": no ohci offset in fdt\n"); + of_node_put(np); + } + + iounmap(ehci->ohci_hcctrl_reg); + } usb_put_hcd(hcd); return 0; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index c7d4b5a06bdb..fb7054ccf4fc 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -120,6 +120,16 @@ struct ehci_hcd { /* one per controller */ unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; + unsigned has_amcc_usb23:1; + + /* required for usb32 quirk */ + #define OHCI_CTRL_HCFS (3 << 6) + #define OHCI_USB_OPER (2 << 6) + #define OHCI_USB_SUSPEND (3 << 6) + + #define OHCI_HCCTRL_OFFSET 0x4 + #define OHCI_HCCTRL_LEN 0x4 + __hc32 *ohci_hcctrl_reg; u8 sbrn; /* packed release number */ @@ -636,6 +646,30 @@ static inline void ehci_writel(const struct ehci_hcd *ehci, #endif } +/* + * On certain ppc-44x SoC there is a HW issue, that could only worked around with + * explicit suspend/operate of OHCI. This function hereby makes sense only on that arch. + * Other common bits are dependant on has_amcc_usb23 quirk flag. + */ +#ifdef CONFIG_44x +static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational) +{ + u32 hc_control; + + hc_control = (readl_be(ehci->ohci_hcctrl_reg) & ~OHCI_CTRL_HCFS); + if (operational) + hc_control |= OHCI_USB_OPER; + else + hc_control |= OHCI_USB_SUSPEND; + + writel_be(hc_control, ehci->ohci_hcctrl_reg); + (void) readl_be(ehci->ohci_hcctrl_reg); +} +#else +static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational) +{ } +#endif + /*-------------------------------------------------------------------------*/ /* diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 8017f1cf78e2..b899f1a59c26 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -435,14 +435,13 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) /* * PORT 1 Control register of the ISP1760 is the OTG control - * register on ISP1761. + * register on ISP1761. Since there is no OTG or device controller + * support in this driver, we use port 1 as a "normal" USB host port on + * both chips. */ - if (!(priv->devflags & ISP1760_FLAG_ISP1761) && - !(priv->devflags & ISP1760_FLAG_PORT1_DIS)) { - isp1760_writel(PORT1_POWER | PORT1_INIT2, - hcd->regs + HC_PORT1_CTRL); - mdelay(10); - } + isp1760_writel(PORT1_POWER | PORT1_INIT2, + hcd->regs + HC_PORT1_CTRL); + mdelay(10); priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS); diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 4377277667d9..a9daea587962 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -135,7 +135,6 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, * indicate the most "atypical" case, so that a devflags of 0 is * a sane default configuration. */ -#define ISP1760_FLAG_PORT1_DIS 0x00000001 /* Port 1 disabled */ #define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ #define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */ #define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index b87ca7cf4b37..4cf7ca428b33 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -60,9 +60,6 @@ static int of_isp1760_probe(struct of_device *dev, if (of_device_is_compatible(dp, "nxp,usb-isp1761")) devflags |= ISP1760_FLAG_ISP1761; - if (of_get_property(dp, "port1-disable", NULL) != NULL) - devflags |= ISP1760_FLAG_PORT1_DIS; - /* Some systems wire up only 16 of the 32 data lines */ prop = of_get_property(dp, "bus-width", NULL); if (prop && *prop == 16) @@ -129,23 +126,23 @@ static struct of_platform_driver isp1760_of_driver = { #endif #ifdef CONFIG_PCI -static u32 nxp_pci_io_base; -static u32 iolength; -static u32 pci_mem_phy0; -static u32 length; -static u8 __iomem *chip_addr; -static u8 __iomem *iobase; - static int __devinit isp1761_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { u8 latency, limit; __u32 reg_data; int retry_count; - int length; - int status = 1; struct usb_hcd *hcd; unsigned int devflags = 0; + int ret_status = 0; + + resource_size_t pci_mem_phy0; + resource_size_t memlength; + + u8 __iomem *chip_addr; + u8 __iomem *iobase; + resource_size_t nxp_pci_io_base; + resource_size_t iolength; if (usb_disabled()) return -ENODEV; @@ -168,26 +165,30 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, iobase = ioremap_nocache(nxp_pci_io_base, iolength); if (!iobase) { printk(KERN_ERR "ioremap #1\n"); - release_mem_region(nxp_pci_io_base, iolength); - return -ENOMEM; + ret_status = -ENOMEM; + goto cleanup1; } /* Grab the PLX PCI shared memory of the ISP 1761 we need */ pci_mem_phy0 = pci_resource_start(dev, 3); - length = pci_resource_len(dev, 3); - - if (length < 0xffff) { - printk(KERN_ERR "memory length for this resource is less than " - "required\n"); - release_mem_region(nxp_pci_io_base, iolength); - iounmap(iobase); - return -ENOMEM; + memlength = pci_resource_len(dev, 3); + if (memlength < 0xffff) { + printk(KERN_ERR "memory length for this resource is wrong\n"); + ret_status = -ENOMEM; + goto cleanup2; } - if (!request_mem_region(pci_mem_phy0, length, "ISP-PCI")) { + if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) { printk(KERN_ERR "host controller already in use\n"); - release_mem_region(nxp_pci_io_base, iolength); - iounmap(iobase); - return -EBUSY; + ret_status = -EBUSY; + goto cleanup2; + } + + /* map available memory */ + chip_addr = ioremap_nocache(pci_mem_phy0,memlength); + if (!chip_addr) { + printk(KERN_ERR "Error ioremap failed\n"); + ret_status = -ENOMEM; + goto cleanup3; } /* bad pci latencies can contribute to overruns */ @@ -210,39 +211,54 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, * */ writel(0xface, chip_addr + HC_SCRATCH_REG); udelay(100); - reg_data = readl(chip_addr + HC_SCRATCH_REG); + reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff; retry_count--; } + iounmap(chip_addr); + /* Host Controller presence is detected by writing to scratch register * and reading back and checking the contents are same or not */ if (reg_data != 0xFACE) { dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data); - goto clean; + ret_status = -ENOMEM; + goto cleanup3; } pci_set_master(dev); - status = readl(iobase + 0x68); - status |= 0x900; - writel(status, iobase + 0x68); + /* configure PLX PCI chip to pass interrupts */ +#define PLX_INT_CSR_REG 0x68 + reg_data = readl(iobase + PLX_INT_CSR_REG); + reg_data |= 0x900; + writel(reg_data, iobase + PLX_INT_CSR_REG); dev->dev.dma_mask = NULL; - hcd = isp1760_register(pci_mem_phy0, length, dev->irq, + hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), devflags); - if (!IS_ERR(hcd)) { - pci_set_drvdata(dev, hcd); - return 0; + if (IS_ERR(hcd)) { + ret_status = -ENODEV; + goto cleanup3; } -clean: - status = -ENODEV; + + /* done with PLX IO access */ + iounmap(iobase); + release_mem_region(nxp_pci_io_base, iolength); + + pci_set_drvdata(dev, hcd); + return 0; + +cleanup3: + release_mem_region(pci_mem_phy0, memlength); +cleanup2: iounmap(iobase); - release_mem_region(pci_mem_phy0, length); +cleanup1: release_mem_region(nxp_pci_io_base, iolength); - return status; + return ret_status; } + static void isp1761_pci_remove(struct pci_dev *dev) { struct usb_hcd *hcd; @@ -255,12 +271,6 @@ static void isp1761_pci_remove(struct pci_dev *dev) usb_put_hcd(hcd); pci_disable_device(dev); - - iounmap(iobase); - iounmap(chip_addr); - - release_mem_region(nxp_pci_io_base, iolength); - release_mem_region(pci_mem_phy0, length); } static void isp1761_pci_shutdown(struct pci_dev *dev) @@ -268,12 +278,16 @@ static void isp1761_pci_shutdown(struct pci_dev *dev) printk(KERN_ERR "ips1761_pci_shutdown\n"); } -static const struct pci_device_id isp1760_plx [] = { { - /* handle any USB 2.0 EHCI controller */ - PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_OTHER << 8) | (0x06 << 16)), ~0), - .driver_data = 0, -}, -{ /* end: all zeroes */ } +static const struct pci_device_id isp1760_plx [] = { + { + .class = PCI_CLASS_BRIDGE_OTHER << 8, + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, + .device = 0x5406, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = 0x9054, + }, + { } }; MODULE_DEVICE_TABLE(pci, isp1760_plx); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 8aa3f4556a32..65a9609f4ad6 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -589,13 +589,15 @@ static int ohci_run (struct ohci_hcd *ohci) /* also: power/overcurrent flags in roothub.a */ } - /* Reset USB nearly "by the book". RemoteWakeupConnected was - * saved if boot firmware (BIOS/SMM/...) told us it's connected, - * or if bus glue did the same (e.g. for PCI add-in cards with - * PCI PM support). + /* Reset USB nearly "by the book". RemoteWakeupConnected has + * to be checked in case boot firmware (BIOS/SMM/...) has set up + * wakeup in a way the bus isn't aware of (e.g., legacy PCI PM). + * If the bus glue detected wakeup capability then it should + * already be enabled. Either way, if wakeup should be enabled + * but isn't, we'll enable it now. */ if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 - && !device_may_wakeup(hcd->self.controller)) + && !device_can_wakeup(hcd->self.controller)) device_init_wakeup(hcd->self.controller, 1); switch (ohci->hc_control & OHCI_CTRL_HCFS) { diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index a9c2ae36c7ad..8b28ae7865ba 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -355,9 +355,9 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd) /* RWC may not be set for add-in PCI cards, since boot * firmware probably ignored them. This transfers PCI - * PM wakeup capabilities (once the PCI layer is fixed). + * PM wakeup capabilities. */ - if (device_may_wakeup(&pdev->dev)) + if (device_can_wakeup(&pdev->dev)) ohci->hc_control |= OHCI_CTRL_RWC; } #endif /* CONFIG_PM */ @@ -487,6 +487,8 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, + .suspend_late = usb_hcd_pci_suspend_late, + .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index e306ca6aef3d..100bf3d8437c 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -106,65 +106,34 @@ extern int ocpi_enable(void); static struct clk *usb_clk; -static int isp1301_probe(struct i2c_adapter *adap); -static int isp1301_detach(struct i2c_client *client); - static const unsigned short normal_i2c[] = { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END }; -static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END }; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_i2c, - .probe = dummy_i2c_addrlist, - .ignore = dummy_i2c_addrlist, -}; - -struct i2c_driver isp1301_driver = { - .driver = { - .name = "isp1301_pnx", - }, - .attach_adapter = isp1301_probe, - .detach_client = isp1301_detach, -}; -static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind) +static int isp1301_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *c; - int err; - - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - - strlcpy(c->name, "isp1301_pnx", I2C_NAME_SIZE); - c->flags = 0; - c->addr = addr; - c->adapter = adap; - c->driver = &isp1301_driver; - - err = i2c_attach_client(c); - if (err) { - kfree(c); - return err; - } - - isp1301_i2c_client = c; - return 0; } -static int isp1301_probe(struct i2c_adapter *adap) +static int isp1301_remove(struct i2c_client *client) { - return i2c_probe(adap, &addr_data, isp1301_attach); -} - -static int isp1301_detach(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(isp1301_i2c_client); return 0; } +const struct i2c_device_id isp1301_id[] = { + { "isp1301_pnx", 0 }, + { } +}; + +struct i2c_driver isp1301_driver = { + .driver = { + .name = "isp1301_pnx", + }, + .probe = isp1301_probe, + .remove = isp1301_remove, + .id_table = isp1301_id, +}; + static void i2c_write(u8 buf, u8 subaddr) { char tmpbuf[2]; @@ -328,6 +297,8 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) struct usb_hcd *hcd = 0; struct ohci_hcd *ohci; const struct hc_driver *driver = &ohci_pnx4008_hc_driver; + struct i2c_adapter *i2c_adap; + struct i2c_board_info i2c_info; int ret = 0, irq; @@ -351,9 +322,20 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) ret = i2c_add_driver(&isp1301_driver); if (ret < 0) { - err("failed to connect I2C to ISP1301 USB Transceiver"); + err("failed to add ISP1301 driver"); goto out; } + i2c_adap = i2c_get_adapter(2); + memset(&i2c_info, 0, sizeof(struct i2c_board_info)); + strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE); + isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, + normal_i2c); + i2c_put_adapter(i2c_adap); + if (!isp1301_i2c_client) { + err("failed to connect I2C to ISP1301 USB Transceiver"); + ret = -ENODEV; + goto out_i2c_driver; + } isp1301_configure(); @@ -429,6 +411,9 @@ out3: out2: clk_put(usb_clk); out1: + i2c_unregister_client(isp1301_i2c_client); + isp1301_i2c_client = NULL; +out_i2c_driver: i2c_del_driver(&isp1301_driver); out: return ret; @@ -445,6 +430,8 @@ static int usb_hcd_pnx4008_remove(struct platform_device *pdev) pnx4008_unset_usb_bits(); clk_disable(usb_clk); clk_put(usb_clk); + i2c_unregister_client(isp1301_i2c_client); + isp1301_i2c_client = NULL; i2c_del_driver(&isp1301_driver); platform_set_drvdata(pdev, NULL); diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 7ac53264ead3..68a301710297 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -91,6 +91,7 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) int rv; int is_bigendian; + struct device_node *np; if (usb_disabled()) return -ENODEV; @@ -147,6 +148,30 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) if (rv == 0) return 0; + /* by now, 440epx is known to show usb_23 erratum */ + np = of_find_compatible_node(NULL, NULL, "ibm,usb-ehci-440epx"); + + /* Work around - At this point ohci_run has executed, the + * controller is running, everything, the root ports, etc., is + * set up. If the ehci driver is loaded, put the ohci core in + * the suspended state. The ehci driver will bring it out of + * suspended state when / if a non-high speed USB device is + * attached to the USB Host port. If the ehci driver is not + * loaded, do nothing. request_mem_region is used to test if + * the ehci driver is loaded. + */ + if (np != NULL) { + if (!of_address_to_resource(np, 0, &res)) { + if (!request_mem_region(res.start, 0x4, hcd_name)) { + writel_be((readl_be(&ohci->regs->control) | + OHCI_USB_SUSPEND), &ohci->regs->control); + (void) readl_be(&ohci->regs->control); + } else + release_mem_region(res.start, 0x4); + } else + pr_debug(__FILE__ ": cannot get ehci offset from fdt\n"); + } + iounmap(hcd->regs); err_ioremap: irq_dispose_mapping(irq); diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index f9f134af0bd1..8dabe8e31d8c 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -201,7 +201,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) if (!cell) return -EINVAL; - hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev->dev.bus_id); + hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev)); if (!hcd) { ret = -ENOMEM; goto err_usb_create_hcd; diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c new file mode 100644 index 000000000000..75548f7c716b --- /dev/null +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -0,0 +1,3985 @@ +/* + * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008 Eurotech S.p.A. <info@eurtech.it> + * + * This code is *strongly* based on EHCI-HCD code by David Brownell since + * the chip is a quasi-EHCI compatible. + * + * 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 <linux/module.h> +#include <linux/pci.h> +#include <linux/dmapool.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/usb.h> +#include <linux/moduleparam.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> + +#include "../core/hcd.h" + +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> + +#include <linux/irq.h> +#include <linux/platform_device.h> + +#include "oxu210hp.h" + +#define DRIVER_VERSION "0.0.50" + +/* + * Main defines + */ + +#define oxu_dbg(oxu, fmt, args...) \ + dev_dbg(oxu_to_hcd(oxu)->self.controller , fmt , ## args) +#define oxu_err(oxu, fmt, args...) \ + dev_err(oxu_to_hcd(oxu)->self.controller , fmt , ## args) +#define oxu_info(oxu, fmt, args...) \ + dev_info(oxu_to_hcd(oxu)->self.controller , fmt , ## args) + +static inline struct usb_hcd *oxu_to_hcd(struct oxu_hcd *oxu) +{ + return container_of((void *) oxu, struct usb_hcd, hcd_priv); +} + +static inline struct oxu_hcd *hcd_to_oxu(struct usb_hcd *hcd) +{ + return (struct oxu_hcd *) (hcd->hcd_priv); +} + +/* + * Debug stuff + */ + +#undef OXU_URB_TRACE +#undef OXU_VERBOSE_DEBUG + +#ifdef OXU_VERBOSE_DEBUG +#define oxu_vdbg oxu_dbg +#else +#define oxu_vdbg(oxu, fmt, args...) /* Nop */ +#endif + +#ifdef DEBUG + +static int __attribute__((__unused__)) +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ + return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +} + +static int __attribute__((__unused__)) +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ + return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", + label, label[0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : "" + ); +} + +static const char *const fls_strings[] = + { "1024", "512", "256", "??" }; + +static int dbg_command_buf(char *buf, unsigned len, + const char *label, u32 command) +{ + return scnprintf(buf, len, + "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", + label, label[0] ? " " : "", command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT(command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings[(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +} + +static int dbg_port_buf(char *buf, unsigned len, const char *label, + int port, u32 status) +{ + char *sig; + + /* signaling state */ + switch (status & (3 << 10)) { + case 0 << 10: + sig = "se0"; + break; + case 1 << 10: + sig = "k"; /* low speed */ + break; + case 2 << 10: + sig = "j"; + break; + default: + sig = "?"; + break; + } + + return scnprintf(buf, len, + "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", port, status, + (status & PORT_POWER) ? " POWER" : "", + (status & PORT_OWNER) ? " OWNER" : "", + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_OCC) ? " OCC" : "", + (status & PORT_OC) ? " OC" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : "" + ); +} + +#else + +static inline int __attribute__((__unused__)) +dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) +{ return 0; } + +static inline int __attribute__((__unused__)) +dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) +{ return 0; } + +#endif /* DEBUG */ + +/* functions have the "wrong" filename when they're output... */ +#define dbg_status(oxu, label, status) { \ + char _buf[80]; \ + dbg_status_buf(_buf, sizeof _buf, label, status); \ + oxu_dbg(oxu, "%s\n", _buf); \ +} + +#define dbg_cmd(oxu, label, command) { \ + char _buf[80]; \ + dbg_command_buf(_buf, sizeof _buf, label, command); \ + oxu_dbg(oxu, "%s\n", _buf); \ +} + +#define dbg_port(oxu, label, port, status) { \ + char _buf[80]; \ + dbg_port_buf(_buf, sizeof _buf, label, port, status); \ + oxu_dbg(oxu, "%s\n", _buf); \ +} + +/* + * Module parameters + */ + +/* Initial IRQ latency: faster than hw default */ +static int log2_irq_thresh; /* 0 to 6 */ +module_param(log2_irq_thresh, int, S_IRUGO); +MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); + +/* Initial park setting: slower than hw default */ +static unsigned park; +module_param(park, uint, S_IRUGO); +MODULE_PARM_DESC(park, "park setting; 1-3 back-to-back async packets"); + +/* For flakey hardware, ignore overcurrent indicators */ +static int ignore_oc; +module_param(ignore_oc, bool, S_IRUGO); +MODULE_PARM_DESC(ignore_oc, "ignore bogus hardware overcurrent indications"); + + +static void ehci_work(struct oxu_hcd *oxu); +static int oxu_hub_control(struct usb_hcd *hcd, + u16 typeReq, u16 wValue, u16 wIndex, + char *buf, u16 wLength); + +/* + * Local functions + */ + +/* Low level read/write registers functions */ +static inline u32 oxu_readl(void *base, u32 reg) +{ + return readl(base + reg); +} + +static inline void oxu_writel(void *base, u32 reg, u32 val) +{ + writel(val, base + reg); +} + +static inline void timer_action_done(struct oxu_hcd *oxu, + enum ehci_timer_action action) +{ + clear_bit(action, &oxu->actions); +} + +static inline void timer_action(struct oxu_hcd *oxu, + enum ehci_timer_action action) +{ + if (!test_and_set_bit(action, &oxu->actions)) { + unsigned long t; + + switch (action) { + case TIMER_IAA_WATCHDOG: + t = EHCI_IAA_JIFFIES; + break; + case TIMER_IO_WATCHDOG: + t = EHCI_IO_JIFFIES; + break; + case TIMER_ASYNC_OFF: + t = EHCI_ASYNC_JIFFIES; + break; + case TIMER_ASYNC_SHRINK: + default: + t = EHCI_SHRINK_JIFFIES; + break; + } + t += jiffies; + /* all timings except IAA watchdog can be overridden. + * async queue SHRINK often precedes IAA. while it's ready + * to go OFF neither can matter, and afterwards the IO + * watchdog stops unless there's still periodic traffic. + */ + if (action != TIMER_IAA_WATCHDOG + && t > oxu->watchdog.expires + && timer_pending(&oxu->watchdog)) + return; + mod_timer(&oxu->watchdog, t); + } +} + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake(struct oxu_hcd *oxu, void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result; + + do { + result = readl(ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay(1); + usec--; + } while (usec > 0); + return -ETIMEDOUT; +} + +/* Force HC to halt state from unknown (EHCI spec section 2.3) */ +static int ehci_halt(struct oxu_hcd *oxu) +{ + u32 temp = readl(&oxu->regs->status); + + /* disable any irqs left enabled by previous code */ + writel(0, &oxu->regs->intr_enable); + + if ((temp & STS_HALT) != 0) + return 0; + + temp = readl(&oxu->regs->command); + temp &= ~CMD_RUN; + writel(temp, &oxu->regs->command); + return handshake(oxu, &oxu->regs->status, + STS_HALT, STS_HALT, 16 * 125); +} + +/* Put TDI/ARC silicon into EHCI mode */ +static void tdi_reset(struct oxu_hcd *oxu) +{ + u32 __iomem *reg_ptr; + u32 tmp; + + reg_ptr = (u32 __iomem *)(((u8 __iomem *)oxu->regs) + 0x68); + tmp = readl(reg_ptr); + tmp |= 0x3; + writel(tmp, reg_ptr); +} + +/* Reset a non-running (STS_HALT == 1) controller */ +static int ehci_reset(struct oxu_hcd *oxu) +{ + int retval; + u32 command = readl(&oxu->regs->command); + + command |= CMD_RESET; + dbg_cmd(oxu, "reset", command); + writel(command, &oxu->regs->command); + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + oxu->next_statechange = jiffies; + retval = handshake(oxu, &oxu->regs->command, + CMD_RESET, 0, 250 * 1000); + + if (retval) + return retval; + + tdi_reset(oxu); + + return retval; +} + +/* Idle the controller (from running) */ +static void ehci_quiesce(struct oxu_hcd *oxu) +{ + u32 temp; + +#ifdef DEBUG + if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + BUG(); +#endif + + /* wait for any schedule enables/disables to take effect */ + temp = readl(&oxu->regs->command) << 10; + temp &= STS_ASS | STS_PSS; + if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS, + temp, 16 * 125) != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return; + } + + /* then disable anything that's still active */ + temp = readl(&oxu->regs->command); + temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); + writel(temp, &oxu->regs->command); + + /* hardware can take 16 microframes to turn off ... */ + if (handshake(oxu, &oxu->regs->status, STS_ASS | STS_PSS, + 0, 16 * 125) != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return; + } +} + +static int check_reset_complete(struct oxu_hcd *oxu, int index, + u32 __iomem *status_reg, int port_status) +{ + if (!(port_status & PORT_CONNECT)) { + oxu->reset_done[index] = 0; + return port_status; + } + + /* if reset finished and it's still not enabled -- handoff */ + if (!(port_status & PORT_PE)) { + oxu_dbg(oxu, "Failed to enable port %d on root hub TT\n", + index+1); + return port_status; + } else + oxu_dbg(oxu, "port %d high speed\n", index + 1); + + return port_status; +} + +static void ehci_hub_descriptor(struct oxu_hcd *oxu, + struct usb_hub_descriptor *desc) +{ + int ports = HCS_N_PORTS(oxu->hcs_params); + u16 temp; + + desc->bDescriptorType = 0x29; + desc->bPwrOn2PwrGood = 10; /* oxu 1.0, 2.3.9 says 20ms max */ + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = ports; + temp = 1 + (ports / 8); + desc->bDescLength = 7 + 2 * temp; + + /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ + memset(&desc->bitmap[0], 0, temp); + memset(&desc->bitmap[temp], 0xff, temp); + + temp = 0x0008; /* per-port overcurrent reporting */ + if (HCS_PPC(oxu->hcs_params)) + temp |= 0x0001; /* per-port power control */ + else + temp |= 0x0002; /* no power switching */ + desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp); +} + + +/* Allocate an OXU210HP on-chip memory data buffer + * + * An on-chip memory data buffer is required for each OXU210HP USB transfer. + * Each transfer descriptor has one or more on-chip memory data buffers. + * + * Data buffers are allocated from a fix sized pool of data blocks. + * To minimise fragmentation and give reasonable memory utlisation, + * data buffers are allocated with sizes the power of 2 multiples of + * the block size, starting on an address a multiple of the allocated size. + * + * FIXME: callers of this function require a buffer to be allocated for + * len=0. This is a waste of on-chip memory and should be fix. Then this + * function should be changed to not allocate a buffer for len=0. + */ +static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) +{ + int n_blocks; /* minium blocks needed to hold len */ + int a_blocks; /* blocks allocated */ + int i, j; + + /* Don't allocte bigger than supported */ + if (len > BUFFER_SIZE * BUFFER_NUM) { + oxu_err(oxu, "buffer too big (%d)\n", len); + return -ENOMEM; + } + + spin_lock(&oxu->mem_lock); + + /* Number of blocks needed to hold len */ + n_blocks = (len + BUFFER_SIZE - 1) / BUFFER_SIZE; + + /* Round the number of blocks up to the power of 2 */ + for (a_blocks = 1; a_blocks < n_blocks; a_blocks <<= 1) + ; + + /* Find a suitable available data buffer */ + for (i = 0; i < BUFFER_NUM; + i += max(a_blocks, (int)oxu->db_used[i])) { + + /* Check all the required blocks are available */ + for (j = 0; j < a_blocks; j++) + if (oxu->db_used[i + j]) + break; + + if (j != a_blocks) + continue; + + /* Allocate blocks found! */ + qtd->buffer = (void *) &oxu->mem->db_pool[i]; + qtd->buffer_dma = virt_to_phys(qtd->buffer); + + qtd->qtd_buffer_len = BUFFER_SIZE * a_blocks; + oxu->db_used[i] = a_blocks; + + spin_unlock(&oxu->mem_lock); + + return 0; + } + + /* Failed */ + + spin_unlock(&oxu->mem_lock); + + return -ENOMEM; +} + +static void oxu_buf_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd) +{ + int index; + + spin_lock(&oxu->mem_lock); + + index = (qtd->buffer - (void *) &oxu->mem->db_pool[0]) + / BUFFER_SIZE; + oxu->db_used[index] = 0; + qtd->qtd_buffer_len = 0; + qtd->buffer_dma = 0; + qtd->buffer = NULL; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static inline void ehci_qtd_init(struct ehci_qtd *qtd, dma_addr_t dma) +{ + memset(qtd, 0, sizeof *qtd); + qtd->qtd_dma = dma; + qtd->hw_token = cpu_to_le32(QTD_STS_HALT); + qtd->hw_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END; + INIT_LIST_HEAD(&qtd->qtd_list); +} + +static inline void oxu_qtd_free(struct oxu_hcd *oxu, struct ehci_qtd *qtd) +{ + int index; + + if (qtd->buffer) + oxu_buf_free(oxu, qtd); + + spin_lock(&oxu->mem_lock); + + index = qtd - &oxu->mem->qtd_pool[0]; + oxu->qtd_used[index] = 0; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static struct ehci_qtd *ehci_qtd_alloc(struct oxu_hcd *oxu) +{ + int i; + struct ehci_qtd *qtd = NULL; + + spin_lock(&oxu->mem_lock); + + for (i = 0; i < QTD_NUM; i++) + if (!oxu->qtd_used[i]) + break; + + if (i < QTD_NUM) { + qtd = (struct ehci_qtd *) &oxu->mem->qtd_pool[i]; + memset(qtd, 0, sizeof *qtd); + + qtd->hw_token = cpu_to_le32(QTD_STS_HALT); + qtd->hw_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END; + INIT_LIST_HEAD(&qtd->qtd_list); + + qtd->qtd_dma = virt_to_phys(qtd); + + oxu->qtd_used[i] = 1; + } + + spin_unlock(&oxu->mem_lock); + + return qtd; +} + +static void oxu_qh_free(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + int index; + + spin_lock(&oxu->mem_lock); + + index = qh - &oxu->mem->qh_pool[0]; + oxu->qh_used[index] = 0; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static void qh_destroy(struct kref *kref) +{ + struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref); + struct oxu_hcd *oxu = qh->oxu; + + /* clean qtds first, and know this is not linked */ + if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) { + oxu_dbg(oxu, "unused qh not empty!\n"); + BUG(); + } + if (qh->dummy) + oxu_qtd_free(oxu, qh->dummy); + oxu_qh_free(oxu, qh); +} + +static struct ehci_qh *oxu_qh_alloc(struct oxu_hcd *oxu) +{ + int i; + struct ehci_qh *qh = NULL; + + spin_lock(&oxu->mem_lock); + + for (i = 0; i < QHEAD_NUM; i++) + if (!oxu->qh_used[i]) + break; + + if (i < QHEAD_NUM) { + qh = (struct ehci_qh *) &oxu->mem->qh_pool[i]; + memset(qh, 0, sizeof *qh); + + kref_init(&qh->kref); + qh->oxu = oxu; + qh->qh_dma = virt_to_phys(qh); + INIT_LIST_HEAD(&qh->qtd_list); + + /* dummy td enables safe urb queuing */ + qh->dummy = ehci_qtd_alloc(oxu); + if (qh->dummy == NULL) { + oxu_dbg(oxu, "no dummy td\n"); + oxu->qh_used[i] = 0; + + return NULL; + } + + oxu->qh_used[i] = 1; + } + + spin_unlock(&oxu->mem_lock); + + return qh; +} + +/* to share a qh (cpu threads, or hc) */ +static inline struct ehci_qh *qh_get(struct ehci_qh *qh) +{ + kref_get(&qh->kref); + return qh; +} + +static inline void qh_put(struct ehci_qh *qh) +{ + kref_put(&qh->kref, qh_destroy); +} + +static void oxu_murb_free(struct oxu_hcd *oxu, struct oxu_murb *murb) +{ + int index; + + spin_lock(&oxu->mem_lock); + + index = murb - &oxu->murb_pool[0]; + oxu->murb_used[index] = 0; + + spin_unlock(&oxu->mem_lock); + + return; +} + +static struct oxu_murb *oxu_murb_alloc(struct oxu_hcd *oxu) + +{ + int i; + struct oxu_murb *murb = NULL; + + spin_lock(&oxu->mem_lock); + + for (i = 0; i < MURB_NUM; i++) + if (!oxu->murb_used[i]) + break; + + if (i < MURB_NUM) { + murb = &(oxu->murb_pool)[i]; + + oxu->murb_used[i] = 1; + } + + spin_unlock(&oxu->mem_lock); + + return murb; +} + +/* The queue heads and transfer descriptors are managed from pools tied + * to each of the "per device" structures. + * This is the initialisation and cleanup code. + */ +static void ehci_mem_cleanup(struct oxu_hcd *oxu) +{ + kfree(oxu->murb_pool); + oxu->murb_pool = NULL; + + if (oxu->async) + qh_put(oxu->async); + oxu->async = NULL; + + del_timer(&oxu->urb_timer); + + oxu->periodic = NULL; + + /* shadow periodic table */ + kfree(oxu->pshadow); + oxu->pshadow = NULL; +} + +/* Remember to add cleanup code (above) if you add anything here. + */ +static int ehci_mem_init(struct oxu_hcd *oxu, gfp_t flags) +{ + int i; + + for (i = 0; i < oxu->periodic_size; i++) + oxu->mem->frame_list[i] = EHCI_LIST_END; + for (i = 0; i < QHEAD_NUM; i++) + oxu->qh_used[i] = 0; + for (i = 0; i < QTD_NUM; i++) + oxu->qtd_used[i] = 0; + + oxu->murb_pool = kcalloc(MURB_NUM, sizeof(struct oxu_murb), flags); + if (!oxu->murb_pool) + goto fail; + + for (i = 0; i < MURB_NUM; i++) + oxu->murb_used[i] = 0; + + oxu->async = oxu_qh_alloc(oxu); + if (!oxu->async) + goto fail; + + oxu->periodic = (__le32 *) &oxu->mem->frame_list; + oxu->periodic_dma = virt_to_phys(oxu->periodic); + + for (i = 0; i < oxu->periodic_size; i++) + oxu->periodic[i] = EHCI_LIST_END; + + /* software shadow of hardware table */ + oxu->pshadow = kcalloc(oxu->periodic_size, sizeof(void *), flags); + if (oxu->pshadow != NULL) + return 0; + +fail: + oxu_dbg(oxu, "couldn't init memory\n"); + ehci_mem_cleanup(oxu); + return -ENOMEM; +} + +/* Fill a qtd, returning how much of the buffer we were able to queue up. + */ +static int qtd_fill(struct ehci_qtd *qtd, dma_addr_t buf, size_t len, + int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_le32((u32)addr); + qtd->hw_buf_hi[0] = cpu_to_le32((u32)(addr >> 32)); + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely(len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_le32((u32)addr); + qtd->hw_buf_hi[i] = cpu_to_le32((u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_le32((count << 16) | token); + qtd->length = count; + + return count; +} + +static inline void qh_update(struct oxu_hcd *oxu, + struct ehci_qh *qh, struct ehci_qtd *qtd) +{ + /* writes to an active overlay are unsafe */ + BUG_ON(qh->qh_state != QH_STATE_IDLE); + + qh->hw_qtd_next = QTD_NEXT(qtd->qtd_dma); + qh->hw_alt_next = EHCI_LIST_END; + + /* Except for control endpoints, we make hardware maintain data + * toggle (like OHCI) ... here (re)initialize the toggle in the QH, + * and set the pseudo-toggle in udev. Only usb_clear_halt() will + * ever clear it. + */ + if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) { + unsigned is_out, epnum; + + is_out = !(qtd->hw_token & cpu_to_le32(1 << 8)); + epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f; + if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) { + qh->hw_token &= ~__constant_cpu_to_le32(QTD_TOGGLE); + usb_settoggle(qh->dev, epnum, is_out, 1); + } + } + + /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ + wmb(); + qh->hw_token &= __constant_cpu_to_le32(QTD_TOGGLE | QTD_STS_PING); +} + +/* If it weren't for a common silicon quirk (writing the dummy into the qh + * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault + * recovery (including urb dequeue) would need software changes to a QH... + */ +static void qh_refresh(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + + if (list_empty(&qh->qtd_list)) + qtd = qh->dummy; + else { + qtd = list_entry(qh->qtd_list.next, + struct ehci_qtd, qtd_list); + /* first qtd may already be partially processed */ + if (cpu_to_le32(qtd->qtd_dma) == qh->hw_current) + qtd = NULL; + } + + if (qtd) + qh_update(oxu, qh, qtd); +} + +static void qtd_copy_status(struct oxu_hcd *oxu, struct urb *urb, + size_t length, u32 token) +{ + /* count IN/OUT bytes, not SETUP (even short packets) */ + if (likely(QTD_PID(token) != 2)) + urb->actual_length += length - QTD_LENGTH(token); + + /* don't modify error codes */ + if (unlikely(urb->status != -EINPROGRESS)) + return; + + /* force cleanup after short read; not always an error */ + if (unlikely(IS_SHORT_READ(token))) + urb->status = -EREMOTEIO; + + /* serious "can't proceed" faults reported by the hardware */ + if (token & QTD_STS_HALT) { + if (token & QTD_STS_BABBLE) { + /* FIXME "must" disable babbling device's port too */ + urb->status = -EOVERFLOW; + } else if (token & QTD_STS_MMF) { + /* fs/ls interrupt xfer missed the complete-split */ + urb->status = -EPROTO; + } else if (token & QTD_STS_DBE) { + urb->status = (QTD_PID(token) == 1) /* IN ? */ + ? -ENOSR /* hc couldn't read data */ + : -ECOMM; /* hc couldn't write data */ + } else if (token & QTD_STS_XACT) { + /* timeout, bad crc, wrong PID, etc; retried */ + if (QTD_CERR(token)) + urb->status = -EPIPE; + else { + oxu_dbg(oxu, "devpath %s ep%d%s 3strikes\n", + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + urb->status = -EPROTO; + } + /* CERR nonzero + no errors + halt --> stall */ + } else if (QTD_CERR(token)) + urb->status = -EPIPE; + else /* unknown */ + urb->status = -EPROTO; + + oxu_vdbg(oxu, "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + token, urb->status); + } +} + +static void ehci_urb_done(struct oxu_hcd *oxu, struct urb *urb) +__releases(oxu->lock) +__acquires(oxu->lock) +{ + if (likely(urb->hcpriv != NULL)) { + struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; + + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw_info2 & __constant_cpu_to_le32(QH_SMASK)) != 0) { + + /* ... update hc-wide periodic stats (for usbfs) */ + oxu_to_hcd(oxu)->self.bandwidth_int_reqs--; + } + qh_put(qh); + } + + urb->hcpriv = NULL; + switch (urb->status) { + case -EINPROGRESS: /* success */ + urb->status = 0; + default: /* fault */ + break; + case -EREMOTEIO: /* fault or normal */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + urb->status = 0; + break; + case -ECONNRESET: /* canceled */ + case -ENOENT: + break; + } + +#ifdef OXU_URB_TRACE + oxu_dbg(oxu, "%s %s urb %p ep%d%s status %d len %d/%d\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->status, + urb->actual_length, urb->transfer_buffer_length); +#endif + + /* complete() can reenter this HCD */ + spin_unlock(&oxu->lock); + usb_hcd_giveback_urb(oxu_to_hcd(oxu), urb, urb->status); + spin_lock(&oxu->lock); +} + +static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh); +static void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh); + +static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh); +static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh); + +#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT) + +/* Process and free completed qtds for a qh, returning URBs to drivers. + * Chases up to qh->hw_current. Returns number of completions called, + * indicating how much "real" work we did. + */ +static unsigned qh_completions(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + struct ehci_qtd *last = NULL, *end = qh->dummy; + struct list_head *entry, *tmp; + int stopped; + unsigned count = 0; + int do_status = 0; + u8 state; + struct oxu_murb *murb = NULL; + + if (unlikely(list_empty(&qh->qtd_list))) + return count; + + /* completions (or tasks on other cpus) must never clobber HALT + * till we've gone through and cleaned everything up, even when + * they add urbs to this qh's queue or mark them for unlinking. + * + * NOTE: unlinking expects to be done in queue order. + */ + state = qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + stopped = (state == QH_STATE_IDLE); + + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe(entry, tmp, &qh->qtd_list) { + struct ehci_qtd *qtd; + struct urb *urb; + u32 token = 0; + + qtd = list_entry(entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + + /* Clean up any state from previous QTD ...*/ + if (last) { + if (likely(last->urb != urb)) { + if (last->urb->complete == NULL) { + murb = (struct oxu_murb *) last->urb; + last->urb = murb->main; + if (murb->last) { + ehci_urb_done(oxu, last->urb); + count++; + } + oxu_murb_free(oxu, murb); + } else { + ehci_urb_done(oxu, last->urb); + count++; + } + } + oxu_qtd_free(oxu, last); + last = NULL; + } + + /* ignore urbs submitted during completions we reported */ + if (qtd == end) + break; + + /* hardware copies qtd out of qh overlay */ + rmb(); + token = le32_to_cpu(qtd->hw_token); + + /* always clean up qtds the hc de-activated */ + if ((token & QTD_STS_ACTIVE) == 0) { + + if ((token & QTD_STS_HALT) != 0) { + stopped = 1; + + /* magic dummy for some short reads; qh won't advance. + * that silicon quirk can kick in with this dummy too. + */ + } else if (IS_SHORT_READ(token) && + !(qtd->hw_alt_next & EHCI_LIST_END)) { + stopped = 1; + goto halt; + } + + /* stop scanning when we reach qtds the hc is using */ + } else if (likely(!stopped && + HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) { + break; + + } else { + stopped = 1; + + if (unlikely(!HC_IS_RUNNING(oxu_to_hcd(oxu)->state))) + urb->status = -ESHUTDOWN; + + /* ignore active urbs unless some previous qtd + * for the urb faulted (including short read) or + * its urb was canceled. we may patch qh or qtds. + */ + if (likely(urb->status == -EINPROGRESS)) + continue; + + /* issue status after short control reads */ + if (unlikely(do_status != 0) + && QTD_PID(token) == 0 /* OUT */) { + do_status = 0; + continue; + } + + /* token in overlay may be most current */ + if (state == QH_STATE_IDLE + && cpu_to_le32(qtd->qtd_dma) + == qh->hw_current) + token = le32_to_cpu(qh->hw_token); + + /* force halt for unlinked or blocked qh, so we'll + * patch the qh later and so that completions can't + * activate it while we "know" it's stopped. + */ + if ((HALT_BIT & qh->hw_token) == 0) { +halt: + qh->hw_token |= HALT_BIT; + wmb(); + } + } + + /* Remove it from the queue */ + qtd_copy_status(oxu, urb->complete ? + urb : ((struct oxu_murb *) urb)->main, + qtd->length, token); + if ((usb_pipein(qtd->urb->pipe)) && + (NULL != qtd->transfer_buffer)) + memcpy(qtd->transfer_buffer, qtd->buffer, qtd->length); + do_status = (urb->status == -EREMOTEIO) + && usb_pipecontrol(urb->pipe); + + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry(qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } + list_del(&qtd->qtd_list); + last = qtd; + } + + /* last urb's completion might still need calling */ + if (likely(last != NULL)) { + if (last->urb->complete == NULL) { + murb = (struct oxu_murb *) last->urb; + last->urb = murb->main; + if (murb->last) { + ehci_urb_done(oxu, last->urb); + count++; + } + oxu_murb_free(oxu, murb); + } else { + ehci_urb_done(oxu, last->urb); + count++; + } + oxu_qtd_free(oxu, last); + } + + /* restore original state; caller must unlink or relink */ + qh->qh_state = state; + + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(oxu, qh); + break; + case QH_STATE_LINKED: + /* should be rare for periodic transfers, + * except maybe high bandwidth ... + */ + if ((__constant_cpu_to_le32(QH_SMASK) + & qh->hw_info2) != 0) { + intr_deschedule(oxu, qh); + (void) qh_schedule(oxu, qh); + } else + unlink_async(oxu, qh); + break; + /* otherwise, unlink already started */ + } + } + + return count; +} + +/* High bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +/* ... and packet size, for any kind of endpoint descriptor */ +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* Reverse of qh_urb_transaction: free a list of TDs. + * used for cleanup after errors, before HC sees an URB's TDs. + */ +static void qtd_list_free(struct oxu_hcd *oxu, + struct urb *urb, struct list_head *qtd_list) +{ + struct list_head *entry, *temp; + + list_for_each_safe(entry, temp, qtd_list) { + struct ehci_qtd *qtd; + + qtd = list_entry(entry, struct ehci_qtd, qtd_list); + list_del(&qtd->qtd_list); + oxu_qtd_free(oxu, qtd); + } +} + +/* Create a list of filled qtds for this URB; won't link into qh. + */ +static struct list_head *qh_urb_transaction(struct oxu_hcd *oxu, + struct urb *urb, + struct list_head *head, + gfp_t flags) +{ + struct ehci_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + void *transfer_buf = NULL; + int ret; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + return NULL; + list_add_tail(&qtd->qtd_list, head); + qtd->urb = urb; + + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = usb_pipein(urb->pipe); + if (!urb->transfer_buffer && urb->transfer_buffer_length && is_input) + urb->transfer_buffer = phys_to_virt(urb->transfer_dma); + + if (usb_pipecontrol(urb->pipe)) { + /* SETUP pid */ + ret = oxu_buf_alloc(oxu, qtd, sizeof(struct usb_ctrlrequest)); + if (ret) + goto cleanup; + + qtd_fill(qtd, qtd->buffer_dma, sizeof(struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); + memcpy(qtd->buffer, qtd->urb->setup_packet, + sizeof(struct usb_ctrlrequest)); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * Data transfer stage: buffer setup + */ + + ret = oxu_buf_alloc(oxu, qtd, len); + if (ret) + goto cleanup; + + buf = qtd->buffer_dma; + transfer_buf = urb->transfer_buffer; + + if (!is_input) + memcpy(qtd->buffer, qtd->urb->transfer_buffer, len); + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill(qtd, buf, len, token, maxpacket); + qtd->transfer_buffer = transfer_buf; + len -= this_qtd_len; + buf += this_qtd_len; + transfer_buf += this_qtd_len; + if (is_input) + qtd->hw_alt_next = oxu->async->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely(len <= 0)) + break; + + qtd_prev = qtd; + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + goto cleanup; + if (likely(len > 0)) { + ret = oxu_buf_alloc(oxu, qtd, len); + if (ret) + goto cleanup; + } + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + } + + /* unless the bulk/interrupt caller wants a chance to clean + * up after short reads, hc should advance qh past this urb + */ + if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 + || usb_pipecontrol(urb->pipe))) + qtd->hw_alt_next = EHCI_LIST_END; + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + if (likely(urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (usb_pipecontrol(urb->pipe)) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if (usb_pipebulk(urb->pipe) + && (urb->transfer_flags & URB_ZERO_PACKET) + && !(urb->transfer_buffer_length % maxpacket)) { + one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = ehci_qtd_alloc(oxu); + if (unlikely(!qtd)) + goto cleanup; + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT(qtd->qtd_dma); + list_add_tail(&qtd->qtd_list, head); + + /* never any data in such packets */ + qtd_fill(qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + qtd->hw_token |= __constant_cpu_to_le32(QTD_IOC); + return head; + +cleanup: + qtd_list_free(oxu, urb, head); + return NULL; +} + +/* Each QH holds a qtd list; a QH is used for everything except iso. + * + * For interrupt urbs, the scheduler must set the microframe scheduling + * mask(s) each time the QH gets scheduled. For highspeed, that's + * just one microframe in the s-mask. For split interrupt transactions + * there are additional complications: c-mask, maybe FSTNs. + */ +static struct ehci_qh *qh_make(struct oxu_hcd *oxu, + struct urb *urb, gfp_t flags) +{ + struct ehci_qh *qh = oxu_qh_alloc(oxu); + u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; + + if (!qh) + return qh; + + /* + * init endpoint/device data for this QH + */ + info1 |= usb_pipeendpoint(urb->pipe) << 8; + info1 |= usb_pipedevice(urb->pipe) << 0; + + is_input = usb_pipein(urb->pipe); + type = usb_pipetype(urb->pipe); + maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input); + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + qh->period = urb->interval >> 3; + if (qh->period == 0 && urb->interval != 1) { + /* NOTE interval 2 or 4 uframes could work. + * But interval 1 scheduling is simpler, and + * includes high bandwidth. + */ + dbg("intr period %d uframes, NYET!", + urb->interval); + goto done; + } + } else { + struct usb_tt *tt = urb->dev->tt; + int think_time; + + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { /* SPLIT, gap, CSPLIT+DATA */ + qh->c_usecs = qh->usecs + HS_USECS(0); + qh->usecs = HS_USECS(1); + } else { /* SPLIT+DATA, gap, CSPLIT */ + qh->usecs += HS_USECS(1); + qh->c_usecs = HS_USECS(0); + } + + think_time = tt ? tt->think_time : 0; + qh->tt_usecs = NS_TO_US(think_time + + usb_calc_bus_time(urb->dev->speed, + is_input, 0, max_packet(maxp))); + qh->period = urb->interval; + } + } + + /* support for tt scheduling, and access to toggles */ + qh->dev = urb->dev; + + /* using TT? */ + switch (urb->dev->speed) { + case USB_SPEED_LOW: + info1 |= (1 << 12); /* EPS "low" */ + /* FALL THROUGH */ + + case USB_SPEED_FULL: + /* EPS 0 means "full" */ + if (type != PIPE_INTERRUPT) + info1 |= (EHCI_TUNE_RL_TT << 28); + if (type == PIPE_CONTROL) { + info1 |= (1 << 27); /* for TT */ + info1 |= 1 << 14; /* toggle from qtd */ + } + info1 |= maxp << 16; + + info2 |= (EHCI_TUNE_MULT_TT << 30); + info2 |= urb->dev->ttport << 23; + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + + break; + + case USB_SPEED_HIGH: /* no TT involved */ + info1 |= (2 << 12); /* EPS "high" */ + if (type == PIPE_CONTROL) { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else if (type == PIPE_BULK) { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 512 << 16; /* usb2 fixed maxpacket */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet(maxp) << 16; + info2 |= hb_mult(maxp) << 30; + } + break; + default: + dbg("bogus dev %p speed %d", urb->dev, urb->dev->speed); +done: + qh_put(qh); + return NULL; + } + + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ + + /* init as live, toggle clear, advance to dummy */ + qh->qh_state = QH_STATE_IDLE; + qh->hw_info1 = cpu_to_le32(info1); + qh->hw_info2 = cpu_to_le32(info2); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1); + qh_refresh(oxu, qh); + return qh; +} + +/* Move qh (and its qtds) onto async queue; maybe enable queue. + */ +static void qh_link_async(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + __le32 dma = QH_NEXT(qh->qh_dma); + struct ehci_qh *head; + + /* (re)start the async schedule? */ + head = oxu->async; + timer_action_done(oxu, TIMER_ASYNC_OFF); + if (!head->qh_next.qh) { + u32 cmd = readl(&oxu->regs->command); + + if (!(cmd & CMD_ASE)) { + /* in case a clear of CMD_ASE didn't take yet */ + (void)handshake(oxu, &oxu->regs->status, + STS_ASS, 0, 150); + cmd |= CMD_ASE | CMD_RUN; + writel(cmd, &oxu->regs->command); + oxu_to_hcd(oxu)->state = HC_STATE_RUNNING; + /* posted write need not be known to HC yet ... */ + } + } + + /* clear halt and/or toggle; and maybe recover from silicon quirk */ + if (qh->qh_state == QH_STATE_IDLE) + qh_refresh(oxu, qh); + + /* splice right after start */ + qh->qh_next = head->qh_next; + qh->hw_next = head->hw_next; + wmb(); + + head->qh_next.qh = qh; + head->hw_next = dma; + + qh->qh_state = QH_STATE_LINKED; + /* qtd completions reported later by interrupt */ +} + +#define QH_ADDR_MASK __constant_cpu_to_le32(0x7f) + +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct ehci_qh *qh_append_tds(struct oxu_hcd *oxu, + struct urb *urb, struct list_head *qtd_list, + int epnum, void **ptr) +{ + struct ehci_qh *qh = NULL; + + qh = (struct ehci_qh *) *ptr; + if (unlikely(qh == NULL)) { + /* can't sleep here, we have oxu->lock... */ + qh = qh_make(oxu, urb, GFP_ATOMIC); + *ptr = qh; + } + if (likely(qh != NULL)) { + struct ehci_qtd *qtd; + + if (unlikely(list_empty(qtd_list))) + qtd = NULL; + else + qtd = list_entry(qtd_list->next, struct ehci_qtd, + qtd_list); + + /* control qh may need patching ... */ + if (unlikely(epnum == 0)) { + + /* usb_reset_device() briefly reverts to address 0 */ + if (usb_pipedevice(urb->pipe) == 0) + qh->hw_info1 &= ~QH_ADDR_MASK; + } + + /* just one way to queue requests: swap with the dummy qtd. + * only hc or qh_refresh() ever modify the overlay. + */ + if (likely(qtd != NULL)) { + struct ehci_qtd *dummy; + dma_addr_t dma; + __le32 token; + + /* to avoid racing the HC, use the dummy td instead of + * the first td of our list (becomes new dummy). both + * tds stay deactivated until we're done, when the + * HC is allowed to fetch the old dummy (4.10.2). + */ + token = qtd->hw_token; + qtd->hw_token = HALT_BIT; + wmb(); + dummy = qh->dummy; + + dma = dummy->qtd_dma; + *dummy = *qtd; + dummy->qtd_dma = dma; + + list_del(&qtd->qtd_list); + list_add(&dummy->qtd_list, qtd_list); + list_splice(qtd_list, qh->qtd_list.prev); + + ehci_qtd_init(qtd, qtd->qtd_dma); + qh->dummy = qtd; + + /* hc must see the new dummy at list end */ + dma = qtd->qtd_dma; + qtd = list_entry(qh->qtd_list.prev, + struct ehci_qtd, qtd_list); + qtd->hw_next = QTD_NEXT(dma); + + /* let the hc process these next qtds */ + dummy->hw_token = (token & ~(0x80)); + wmb(); + dummy->hw_token = token; + + urb->hcpriv = qh_get(qh); + } + } + return qh; +} + +static int submit_async(struct oxu_hcd *oxu, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags) +{ + struct ehci_qtd *qtd; + int epnum; + unsigned long flags; + struct ehci_qh *qh = NULL; + int rc = 0; + + qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list); + epnum = urb->ep->desc.bEndpointAddress; + +#ifdef OXU_URB_TRACE + oxu_dbg(oxu, "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", + __func__, urb->dev->devpath, urb, + epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", + urb->transfer_buffer_length, + qtd, urb->ep->hcpriv); +#endif + + spin_lock_irqsave(&oxu->lock, flags); + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &oxu_to_hcd(oxu)->flags))) { + rc = -ESHUTDOWN; + goto done; + } + + qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv); + if (unlikely(qh == NULL)) { + rc = -ENOMEM; + goto done; + } + + /* Control/bulk operations through TTs don't need scheduling, + * the HC and TT handle it when the TT has a buffer ready. + */ + if (likely(qh->qh_state == QH_STATE_IDLE)) + qh_link_async(oxu, qh_get(qh)); +done: + spin_unlock_irqrestore(&oxu->lock, flags); + if (unlikely(qh == NULL)) + qtd_list_free(oxu, urb, qtd_list); + return rc; +} + +/* The async qh for the qtds being reclaimed are now unlinked from the HC */ + +static void end_unlink_async(struct oxu_hcd *oxu) +{ + struct ehci_qh *qh = oxu->reclaim; + struct ehci_qh *next; + + timer_action_done(oxu, TIMER_IAA_WATCHDOG); + + qh->qh_state = QH_STATE_IDLE; + qh->qh_next.qh = NULL; + qh_put(qh); /* refcount from reclaim */ + + /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ + next = qh->reclaim; + oxu->reclaim = next; + oxu->reclaim_ready = 0; + qh->reclaim = NULL; + + qh_completions(oxu, qh); + + if (!list_empty(&qh->qtd_list) + && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + qh_link_async(oxu, qh); + else { + qh_put(qh); /* refcount from async list */ + + /* it's not free to turn the async schedule on/off; leave it + * active but idle for a while once it empties. + */ + if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) + && oxu->async->qh_next.qh == NULL) + timer_action(oxu, TIMER_ASYNC_OFF); + } + + if (next) { + oxu->reclaim = NULL; + start_unlink_async(oxu, next); + } +} + +/* makes sure the async qh will become idle */ +/* caller must own oxu->lock */ + +static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + int cmd = readl(&oxu->regs->command); + struct ehci_qh *prev; + +#ifdef DEBUG + assert_spin_locked(&oxu->lock); + if (oxu->reclaim || (qh->qh_state != QH_STATE_LINKED + && qh->qh_state != QH_STATE_UNLINK_WAIT)) + BUG(); +#endif + + /* stop async schedule right now? */ + if (unlikely(qh == oxu->async)) { + /* can't get here without STS_ASS set */ + if (oxu_to_hcd(oxu)->state != HC_STATE_HALT + && !oxu->reclaim) { + /* ... and CMD_IAAD clear */ + writel(cmd & ~CMD_ASE, &oxu->regs->command); + wmb(); + /* handshake later, if we need to */ + timer_action_done(oxu, TIMER_ASYNC_OFF); + } + return; + } + + qh->qh_state = QH_STATE_UNLINK; + oxu->reclaim = qh = qh_get(qh); + + prev = oxu->async; + while (prev->qh_next.qh != qh) + prev = prev->qh_next.qh; + + prev->hw_next = qh->hw_next; + prev->qh_next = qh->qh_next; + wmb(); + + if (unlikely(oxu_to_hcd(oxu)->state == HC_STATE_HALT)) { + /* if (unlikely(qh->reclaim != 0)) + * this will recurse, probably not much + */ + end_unlink_async(oxu); + return; + } + + oxu->reclaim_ready = 0; + cmd |= CMD_IAAD; + writel(cmd, &oxu->regs->command); + (void) readl(&oxu->regs->command); + timer_action(oxu, TIMER_IAA_WATCHDOG); +} + +static void scan_async(struct oxu_hcd *oxu) +{ + struct ehci_qh *qh; + enum ehci_timer_action action = TIMER_IO_WATCHDOG; + + if (!++(oxu->stamp)) + oxu->stamp++; + timer_action_done(oxu, TIMER_ASYNC_SHRINK); +rescan: + qh = oxu->async->qh_next.qh; + if (likely(qh != NULL)) { + do { + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list) + && qh->stamp != oxu->stamp) { + int temp; + + /* unlinks could happen here; completion + * reporting drops the lock. rescan using + * the latest schedule, but don't rescan + * qhs we already finished (no looping). + */ + qh = qh_get(qh); + qh->stamp = oxu->stamp; + temp = qh_completions(oxu, qh); + qh_put(qh); + if (temp != 0) + goto rescan; + } + + /* unlink idle entries, reducing HC PCI usage as well + * as HCD schedule-scanning costs. delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + * (plus, avoids some kind of re-activation race.) + */ + if (list_empty(&qh->qtd_list)) { + if (qh->stamp == oxu->stamp) + action = TIMER_ASYNC_SHRINK; + else if (!oxu->reclaim + && qh->qh_state == QH_STATE_LINKED) + start_unlink_async(oxu, qh); + } + + qh = qh->qh_next.qh; + } while (qh); + } + if (action == TIMER_ASYNC_SHRINK) + timer_action(oxu, TIMER_ASYNC_SHRINK); +} + +/* + * periodic_next_shadow - return "next" pointer on shadow list + * @periodic: host pointer to qh/itd/sitd + * @tag: hardware tag for type of this record + */ +static union ehci_shadow *periodic_next_shadow(union ehci_shadow *periodic, + __le32 tag) +{ + switch (tag) { + default: + case Q_TYPE_QH: + return &periodic->qh->qh_next; + } +} + +/* caller must hold oxu->lock */ +static void periodic_unlink(struct oxu_hcd *oxu, unsigned frame, void *ptr) +{ + union ehci_shadow *prev_p = &oxu->pshadow[frame]; + __le32 *hw_p = &oxu->periodic[frame]; + union ehci_shadow here = *prev_p; + + /* find predecessor of "ptr"; hw and shadow lists are in sync */ + while (here.ptr && here.ptr != ptr) { + prev_p = periodic_next_shadow(prev_p, Q_NEXT_TYPE(*hw_p)); + hw_p = here.hw_next; + here = *prev_p; + } + /* an interrupt entry (at list end) could have been shared */ + if (!here.ptr) + return; + + /* update shadow and hardware lists ... the old "next" pointers + * from ptr may still be in use, the caller updates them. + */ + *prev_p = *periodic_next_shadow(&here, Q_NEXT_TYPE(*hw_p)); + *hw_p = *here.hw_next; +} + +/* how many of the uframe's 125 usecs are allocated? */ +static unsigned short periodic_usecs(struct oxu_hcd *oxu, + unsigned frame, unsigned uframe) +{ + __le32 *hw_p = &oxu->periodic[frame]; + union ehci_shadow *q = &oxu->pshadow[frame]; + unsigned usecs = 0; + + while (q->ptr) { + switch (Q_NEXT_TYPE(*hw_p)) { + case Q_TYPE_QH: + default: + /* is it in the S-mask? */ + if (q->qh->hw_info2 & cpu_to_le32(1 << uframe)) + usecs += q->qh->usecs; + /* ... or C-mask? */ + if (q->qh->hw_info2 & cpu_to_le32(1 << (8 + uframe))) + usecs += q->qh->c_usecs; + hw_p = &q->qh->hw_next; + q = &q->qh->qh_next; + break; + } + } +#ifdef DEBUG + if (usecs > 100) + oxu_err(oxu, "uframe %d sched overrun: %d usecs\n", + frame * 8 + uframe, usecs); +#endif + return usecs; +} + +static int enable_periodic(struct oxu_hcd *oxu) +{ + u32 cmd; + int status; + + /* did clearing PSE did take effect yet? + * takes effect only at frame boundaries... + */ + status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125); + if (status != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return status; + } + + cmd = readl(&oxu->regs->command) | CMD_PSE; + writel(cmd, &oxu->regs->command); + /* posted write ... PSS happens later */ + oxu_to_hcd(oxu)->state = HC_STATE_RUNNING; + + /* make sure ehci_work scans these */ + oxu->next_uframe = readl(&oxu->regs->frame_index) + % (oxu->periodic_size << 3); + return 0; +} + +static int disable_periodic(struct oxu_hcd *oxu) +{ + u32 cmd; + int status; + + /* did setting PSE not take effect yet? + * takes effect only at frame boundaries... + */ + status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125); + if (status != 0) { + oxu_to_hcd(oxu)->state = HC_STATE_HALT; + return status; + } + + cmd = readl(&oxu->regs->command) & ~CMD_PSE; + writel(cmd, &oxu->regs->command); + /* posted write ... */ + + oxu->next_uframe = -1; + return 0; +} + +/* periodic schedule slots have iso tds (normal or split) first, then a + * sparse tree for active interrupt transfers. + * + * this just links in a qh; caller guarantees uframe masks are set right. + * no FSTN support (yet; oxu 0.96+) + */ +static int qh_link_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + unsigned i; + unsigned period = qh->period; + + dev_dbg(&qh->dev->dev, + "link qh%d-%04x/%p start %d [%d/%d us]\n", + period, le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* high bandwidth, or otherwise every microframe */ + if (period == 0) + period = 1; + + for (i = qh->start; i < oxu->periodic_size; i += period) { + union ehci_shadow *prev = &oxu->pshadow[i]; + __le32 *hw_p = &oxu->periodic[i]; + union ehci_shadow here = *prev; + __le32 type = 0; + + /* skip the iso nodes at list head */ + while (here.ptr) { + type = Q_NEXT_TYPE(*hw_p); + if (type == Q_TYPE_QH) + break; + prev = periodic_next_shadow(prev, type); + hw_p = &here.qh->hw_next; + here = *prev; + } + + /* sorting each branch by period (slow-->fast) + * enables sharing interior tree nodes + */ + while (here.ptr && qh != here.qh) { + if (qh->period > here.qh->period) + break; + prev = &here.qh->qh_next; + hw_p = &here.qh->hw_next; + here = *prev; + } + /* link in this qh, unless some earlier pass did that */ + if (qh != here.qh) { + qh->qh_next = here; + if (here.qh) + qh->hw_next = *hw_p; + wmb(); + prev->qh = qh; + *hw_p = QH_NEXT(qh->qh_dma); + } + } + qh->qh_state = QH_STATE_LINKED; + qh_get(qh); + + /* update per-qh bandwidth for usbfs */ + oxu_to_hcd(oxu)->self.bandwidth_allocated += qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + /* maybe enable periodic schedule processing */ + if (!oxu->periodic_sched++) + return enable_periodic(oxu); + + return 0; +} + +static void qh_unlink_periodic(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + unsigned i; + unsigned period; + + /* FIXME: + * IF this isn't high speed + * and this qh is active in the current uframe + * (and overlay token SplitXstate is false?) + * THEN + * qh->hw_info1 |= __constant_cpu_to_le32(1 << 7 "ignore"); + */ + + /* high bandwidth, or otherwise part of every microframe */ + period = qh->period; + if (period == 0) + period = 1; + + for (i = qh->start; i < oxu->periodic_size; i += period) + periodic_unlink(oxu, i, qh); + + /* update per-qh bandwidth for usbfs */ + oxu_to_hcd(oxu)->self.bandwidth_allocated -= qh->period + ? ((qh->usecs + qh->c_usecs) / qh->period) + : (qh->usecs * 8); + + dev_dbg(&qh->dev->dev, + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, + le32_to_cpup(&qh->hw_info2) & (QH_CMASK | QH_SMASK), + qh, qh->start, qh->usecs, qh->c_usecs); + + /* qh->qh_next still "live" to HC */ + qh->qh_state = QH_STATE_UNLINK; + qh->qh_next.ptr = NULL; + qh_put(qh); + + /* maybe turn off periodic schedule */ + oxu->periodic_sched--; + if (!oxu->periodic_sched) + (void) disable_periodic(oxu); +} + +static void intr_deschedule(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + unsigned wait; + + qh_unlink_periodic(oxu, qh); + + /* simple/paranoid: always delay, expecting the HC needs to read + * qh->hw_next or finish a writeback after SPLIT/CSPLIT ... and + * expect khubd to clean up after any CSPLITs we won't issue. + * active high speed queues may need bigger delays... + */ + if (list_empty(&qh->qtd_list) + || (__constant_cpu_to_le32(QH_CMASK) & qh->hw_info2) != 0) + wait = 2; + else + wait = 55; /* worst case: 3 * 1024 */ + + udelay(wait); + qh->qh_state = QH_STATE_IDLE; + qh->hw_next = EHCI_LIST_END; + wmb(); +} + +static int check_period(struct oxu_hcd *oxu, + unsigned frame, unsigned uframe, + unsigned period, unsigned usecs) +{ + int claimed; + + /* complete split running into next frame? + * given FSTN support, we could sometimes check... + */ + if (uframe >= 8) + return 0; + + /* + * 80% periodic == 100 usec/uframe available + * convert "usecs we need" to "max already claimed" + */ + usecs = 100 - usecs; + + /* we "know" 2 and 4 uframe intervals were rejected; so + * for period 0, check _every_ microframe in the schedule. + */ + if (unlikely(period == 0)) { + do { + for (uframe = 0; uframe < 7; uframe++) { + claimed = periodic_usecs(oxu, frame, uframe); + if (claimed > usecs) + return 0; + } + } while ((frame += 1) < oxu->periodic_size); + + /* just check the specified uframe, at that period */ + } else { + do { + claimed = periodic_usecs(oxu, frame, uframe); + if (claimed > usecs) + return 0; + } while ((frame += period) < oxu->periodic_size); + } + + return 1; +} + +static int check_intr_schedule(struct oxu_hcd *oxu, + unsigned frame, unsigned uframe, + const struct ehci_qh *qh, __le32 *c_maskp) +{ + int retval = -ENOSPC; + + if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ + goto done; + + if (!check_period(oxu, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = 0; + goto done; + } + +done: + return retval; +} + +/* "first fit" scheduling policy used the first time through, + * or when the previous schedule slot can't be re-used. + */ +static int qh_schedule(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + int status; + unsigned uframe; + __le32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + + qh_refresh(oxu, qh); + qh->hw_next = EHCI_LIST_END; + frame = qh->start; + + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs(le32_to_cpup(&qh->hw_info2) & QH_SMASK); + status = check_intr_schedule(oxu, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; + status = -ENOSPC; + } + + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + /* "normal" case, uframing flexible except with splits */ + if (qh->period) { + frame = qh->period - 1; + do { + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule(oxu, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } while (status && frame--); + + /* qh->period == 0 means every uframe */ + } else { + frame = 0; + status = check_intr_schedule(oxu, 0, 0, qh, &c_mask); + } + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); + qh->hw_info2 |= qh->period + ? cpu_to_le32(1 << uframe) + : __constant_cpu_to_le32(QH_SMASK); + qh->hw_info2 |= c_mask; + } else + oxu_dbg(oxu, "reused qh %p schedule\n", qh); + + /* stuff into the periodic schedule */ + status = qh_link_periodic(oxu, qh); +done: + return status; +} + +static int intr_submit(struct oxu_hcd *oxu, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags) +{ + unsigned epnum; + unsigned long flags; + struct ehci_qh *qh; + int status = 0; + struct list_head empty; + + /* get endpoint and transfer/schedule data */ + epnum = urb->ep->desc.bEndpointAddress; + + spin_lock_irqsave(&oxu->lock, flags); + + if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, + &oxu_to_hcd(oxu)->flags))) { + status = -ESHUTDOWN; + goto done; + } + + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD(&empty); + qh = qh_append_tds(oxu, urb, &empty, epnum, &urb->ep->hcpriv); + if (qh == NULL) { + status = -ENOMEM; + goto done; + } + if (qh->qh_state == QH_STATE_IDLE) { + status = qh_schedule(oxu, qh); + if (status != 0) + goto done; + } + + /* then queue the urb's tds to the qh */ + qh = qh_append_tds(oxu, urb, qtd_list, epnum, &urb->ep->hcpriv); + BUG_ON(qh == NULL); + + /* ... update usbfs periodic stats */ + oxu_to_hcd(oxu)->self.bandwidth_int_reqs++; + +done: + spin_unlock_irqrestore(&oxu->lock, flags); + if (status) + qtd_list_free(oxu, urb, qtd_list); + + return status; +} + +static inline int itd_submit(struct oxu_hcd *oxu, struct urb *urb, + gfp_t mem_flags) +{ + oxu_dbg(oxu, "iso support is missing!\n"); + return -ENOSYS; +} + +static inline int sitd_submit(struct oxu_hcd *oxu, struct urb *urb, + gfp_t mem_flags) +{ + oxu_dbg(oxu, "split iso support is missing!\n"); + return -ENOSYS; +} + +static void scan_periodic(struct oxu_hcd *oxu) +{ + unsigned frame, clock, now_uframe, mod; + unsigned modified; + + mod = oxu->periodic_size << 3; + + /* + * When running, scan from last scan point up to "now" + * else clean up by scanning everything that's left. + * Touches as few pages as possible: cache-friendly. + */ + now_uframe = oxu->next_uframe; + if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + clock = readl(&oxu->regs->frame_index); + else + clock = now_uframe + mod - 1; + clock %= mod; + + for (;;) { + union ehci_shadow q, *q_p; + __le32 type, *hw_p; + unsigned uframes; + + /* don't scan past the live uframe */ + frame = now_uframe >> 3; + if (frame == (clock >> 3)) + uframes = now_uframe & 0x07; + else { + /* safe to scan the whole frame at once */ + now_uframe |= 0x07; + uframes = 8; + } + +restart: + /* scan each element in frame's queue for completions */ + q_p = &oxu->pshadow[frame]; + hw_p = &oxu->periodic[frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE(*hw_p); + modified = 0; + + while (q.ptr != NULL) { + union ehci_shadow temp; + int live; + + live = HC_IS_RUNNING(oxu_to_hcd(oxu)->state); + switch (type) { + case Q_TYPE_QH: + /* handle any completions */ + temp.qh = qh_get(q.qh); + type = Q_NEXT_TYPE(q.qh->hw_next); + q = q.qh->qh_next; + modified = qh_completions(oxu, temp.qh); + if (unlikely(list_empty(&temp.qh->qtd_list))) + intr_deschedule(oxu, temp.qh); + qh_put(temp.qh); + break; + default: + dbg("corrupt type %d frame %d shadow %p", + type, frame, q.ptr); + q.ptr = NULL; + } + + /* assume completion callbacks modify the queue */ + if (unlikely(modified)) + goto restart; + } + + /* Stop when we catch up to the HC */ + + /* FIXME: this assumes we won't get lapped when + * latencies climb; that should be rare, but... + * detect it, and just go all the way around. + * FLR might help detect this case, so long as latencies + * don't exceed periodic_size msec (default 1.024 sec). + */ + + /* FIXME: likewise assumes HC doesn't halt mid-scan */ + + if (now_uframe == clock) { + unsigned now; + + if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) + break; + oxu->next_uframe = now_uframe; + now = readl(&oxu->regs->frame_index) % mod; + if (now_uframe == now) + break; + + /* rescan the rest of this frame, then ... */ + clock = now; + } else { + now_uframe++; + now_uframe %= mod; + } + } +} + +/* On some systems, leaving remote wakeup enabled prevents system shutdown. + * The firmware seems to think that powering off is a wakeup event! + * This routine turns off remote wakeup and everything else, on all ports. + */ +static void ehci_turn_off_all_ports(struct oxu_hcd *oxu) +{ + int port = HCS_N_PORTS(oxu->hcs_params); + + while (port--) + writel(PORT_RWC_BITS, &oxu->regs->port_status[port]); +} + +static void ehci_port_power(struct oxu_hcd *oxu, int is_on) +{ + unsigned port; + + if (!HCS_PPC(oxu->hcs_params)) + return; + + oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down"); + for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) + (void) oxu_hub_control(oxu_to_hcd(oxu), + is_on ? SetPortFeature : ClearPortFeature, + USB_PORT_FEAT_POWER, + port--, NULL, 0); + msleep(20); +} + +/* Called from some interrupts, timers, and so on. + * It calls driver completion functions, after dropping oxu->lock. + */ +static void ehci_work(struct oxu_hcd *oxu) +{ + timer_action_done(oxu, TIMER_IO_WATCHDOG); + if (oxu->reclaim_ready) + end_unlink_async(oxu); + + /* another CPU may drop oxu->lock during a schedule scan while + * it reports urb completions. this flag guards against bogus + * attempts at re-entrant schedule scanning. + */ + if (oxu->scanning) + return; + oxu->scanning = 1; + scan_async(oxu); + if (oxu->next_uframe != -1) + scan_periodic(oxu); + oxu->scanning = 0; + + /* the IO watchdog guards against hardware or driver bugs that + * misplace IRQs, and should let us run completely without IRQs. + * such lossage has been observed on both VT6202 and VT8235. + */ + if (HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && + (oxu->async->qh_next.ptr != NULL || + oxu->periodic_sched != 0)) + timer_action(oxu, TIMER_IO_WATCHDOG); +} + +static void unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh) +{ + /* if we need to use IAA and it's busy, defer */ + if (qh->qh_state == QH_STATE_LINKED + && oxu->reclaim + && HC_IS_RUNNING(oxu_to_hcd(oxu)->state)) { + struct ehci_qh *last; + + for (last = oxu->reclaim; + last->reclaim; + last = last->reclaim) + continue; + qh->qh_state = QH_STATE_UNLINK_WAIT; + last->reclaim = qh; + + /* bypass IAA if the hc can't care */ + } else if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state) && oxu->reclaim) + end_unlink_async(oxu); + + /* something else might have unlinked the qh by now */ + if (qh->qh_state == QH_STATE_LINKED) + start_unlink_async(oxu, qh); +} + +/* + * USB host controller methods + */ + +static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 status, pcd_status = 0; + int bh; + + spin_lock(&oxu->lock); + + status = readl(&oxu->regs->status); + + /* e.g. cardbus physical eject */ + if (status == ~(u32) 0) { + oxu_dbg(oxu, "device removed\n"); + goto dead; + } + + status &= INTR_MASK; + if (!status) { /* irq sharing? */ + spin_unlock(&oxu->lock); + return IRQ_NONE; + } + + /* clear (just) interrupts */ + writel(status, &oxu->regs->status); + readl(&oxu->regs->command); /* unblock posted write */ + bh = 0; + +#ifdef OXU_VERBOSE_DEBUG + /* unrequested/ignored: Frame List Rollover */ + dbg_status(oxu, "irq", status); +#endif + + /* INT, ERR, and IAA interrupt rates can be throttled */ + + /* normal [4.15.1.2] or error [4.15.1.1] completion */ + if (likely((status & (STS_INT|STS_ERR)) != 0)) + bh = 1; + + /* complete the unlinking of some qh [4.15.2.3] */ + if (status & STS_IAA) { + oxu->reclaim_ready = 1; + bh = 1; + } + + /* remote wakeup [4.3.1] */ + if (status & STS_PCD) { + unsigned i = HCS_N_PORTS(oxu->hcs_params); + pcd_status = status; + + /* resume root hub? */ + if (!(readl(&oxu->regs->command) & CMD_RUN)) + usb_hcd_resume_root_hub(hcd); + + while (i--) { + int pstatus = readl(&oxu->regs->port_status[i]); + + if (pstatus & PORT_OWNER) + continue; + if (!(pstatus & PORT_RESUME) + || oxu->reset_done[i] != 0) + continue; + + /* start 20 msec resume signaling from this port, + * and make khubd collect PORT_STAT_C_SUSPEND to + * stop that signaling. + */ + oxu->reset_done[i] = jiffies + msecs_to_jiffies(20); + oxu_dbg(oxu, "port %d remote wakeup\n", i + 1); + mod_timer(&hcd->rh_timer, oxu->reset_done[i]); + } + } + + /* PCI errors [4.15.2.4] */ + if (unlikely((status & STS_FATAL) != 0)) { + /* bogus "fatal" IRQs appear on some chips... why? */ + status = readl(&oxu->regs->status); + dbg_cmd(oxu, "fatal", readl(&oxu->regs->command)); + dbg_status(oxu, "fatal", status); + if (status & STS_HALT) { + oxu_err(oxu, "fatal error\n"); +dead: + ehci_reset(oxu); + writel(0, &oxu->regs->configured_flag); + /* generic layer kills/unlinks all urbs, then + * uses oxu_stop to clean up the rest + */ + bh = 1; + } + } + + if (bh) + ehci_work(oxu); + spin_unlock(&oxu->lock); + if (pcd_status & STS_PCD) + usb_hcd_poll_rh_status(hcd); + return IRQ_HANDLED; +} + +static irqreturn_t oxu_irq(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int ret = IRQ_HANDLED; + + u32 status = oxu_readl(hcd->regs, OXU_CHIPIRQSTATUS); + u32 enable = oxu_readl(hcd->regs, OXU_CHIPIRQEN_SET); + + /* Disable all interrupt */ + oxu_writel(hcd->regs, OXU_CHIPIRQEN_CLR, enable); + + if ((oxu->is_otg && (status & OXU_USBOTGI)) || + (!oxu->is_otg && (status & OXU_USBSPHI))) + oxu210_hcd_irq(hcd); + else + ret = IRQ_NONE; + + /* Enable all interrupt back */ + oxu_writel(hcd->regs, OXU_CHIPIRQEN_SET, enable); + + return ret; +} + +static void oxu_watchdog(unsigned long param) +{ + struct oxu_hcd *oxu = (struct oxu_hcd *) param; + unsigned long flags; + + spin_lock_irqsave(&oxu->lock, flags); + + /* lost IAA irqs wedge things badly; seen with a vt8235 */ + if (oxu->reclaim) { + u32 status = readl(&oxu->regs->status); + if (status & STS_IAA) { + oxu_vdbg(oxu, "lost IAA\n"); + writel(STS_IAA, &oxu->regs->status); + oxu->reclaim_ready = 1; + } + } + + /* stop async processing after it's idled a bit */ + if (test_bit(TIMER_ASYNC_OFF, &oxu->actions)) + start_unlink_async(oxu, oxu->async); + + /* oxu could run by timer, without IRQs ... */ + ehci_work(oxu); + + spin_unlock_irqrestore(&oxu->lock, flags); +} + +/* One-time init, only for memory state. + */ +static int oxu_hcd_init(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 temp; + int retval; + u32 hcc_params; + + spin_lock_init(&oxu->lock); + + init_timer(&oxu->watchdog); + oxu->watchdog.function = oxu_watchdog; + oxu->watchdog.data = (unsigned long) oxu; + + /* + * hw default: 1K periodic list heads, one per frame. + * periodic_size can shrink by USBCMD update if hcc_params allows. + */ + oxu->periodic_size = DEFAULT_I_TDPS; + retval = ehci_mem_init(oxu, GFP_KERNEL); + if (retval < 0) + return retval; + + /* controllers may cache some of the periodic schedule ... */ + hcc_params = readl(&oxu->caps->hcc_params); + if (HCC_ISOC_CACHE(hcc_params)) /* full frame cache */ + oxu->i_thresh = 8; + else /* N microframes cached */ + oxu->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); + + oxu->reclaim = NULL; + oxu->reclaim_ready = 0; + oxu->next_uframe = -1; + + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + oxu->async->qh_next.qh = NULL; + oxu->async->hw_next = QH_NEXT(oxu->async->qh_dma); + oxu->async->hw_info1 = cpu_to_le32(QH_HEAD); + oxu->async->hw_token = cpu_to_le32(QTD_STS_HALT); + oxu->async->hw_qtd_next = EHCI_LIST_END; + oxu->async->qh_state = QH_STATE_LINKED; + oxu->async->hw_alt_next = QTD_NEXT(oxu->async->dummy->qtd_dma); + + /* clear interrupt enables, set irq latency */ + if (log2_irq_thresh < 0 || log2_irq_thresh > 6) + log2_irq_thresh = 0; + temp = 1 << (16 + log2_irq_thresh); + if (HCC_CANPARK(hcc_params)) { + /* HW default park == 3, on hardware that supports it (like + * NVidia and ALI silicon), maximizes throughput on the async + * schedule by avoiding QH fetches between transfers. + * + * With fast usb storage devices and NForce2, "park" seems to + * make problems: throughput reduction (!), data errors... + */ + if (park) { + park = min(park, (unsigned) 3); + temp |= CMD_PARK; + temp |= park << 8; + } + oxu_dbg(oxu, "park %d\n", park); + } + if (HCC_PGM_FRAMELISTLEN(hcc_params)) { + /* periodic schedule size can be smaller than default */ + temp &= ~(3 << 2); + temp |= (EHCI_TUNE_FLS << 2); + } + oxu->command = temp; + + return 0; +} + +/* Called during probe() after chip reset completes. + */ +static int oxu_reset(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int ret; + + spin_lock_init(&oxu->mem_lock); + INIT_LIST_HEAD(&oxu->urb_list); + oxu->urb_len = 0; + + /* FIMXE */ + hcd->self.controller->dma_mask = 0UL; + + if (oxu->is_otg) { + oxu->caps = hcd->regs + OXU_OTG_CAP_OFFSET; + oxu->regs = hcd->regs + OXU_OTG_CAP_OFFSET + \ + HC_LENGTH(readl(&oxu->caps->hc_capbase)); + + oxu->mem = hcd->regs + OXU_SPH_MEM; + } else { + oxu->caps = hcd->regs + OXU_SPH_CAP_OFFSET; + oxu->regs = hcd->regs + OXU_SPH_CAP_OFFSET + \ + HC_LENGTH(readl(&oxu->caps->hc_capbase)); + + oxu->mem = hcd->regs + OXU_OTG_MEM; + } + + oxu->hcs_params = readl(&oxu->caps->hcs_params); + oxu->sbrn = 0x20; + + ret = oxu_hcd_init(hcd); + if (ret) + return ret; + + return 0; +} + +static int oxu_run(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int retval; + u32 temp, hcc_params; + + hcd->uses_new_polling = 1; + hcd->poll_rh = 0; + + /* EHCI spec section 4.1 */ + retval = ehci_reset(oxu); + if (retval != 0) { + ehci_mem_cleanup(oxu); + return retval; + } + writel(oxu->periodic_dma, &oxu->regs->frame_list); + writel((u32) oxu->async->qh_dma, &oxu->regs->async_next); + + /* hcc_params controls whether oxu->regs->segment must (!!!) + * be used; it constrains QH/ITD/SITD and QTD locations. + * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: the dma mask is visible through dma_supported(), so + * drivers can pass this info along ... like NETIF_F_HIGHDMA, + * Scsi_Host.highmem_io, and so forth. It's readonly to all + * host side drivers though. + */ + hcc_params = readl(&oxu->caps->hcc_params); + if (HCC_64BIT_ADDR(hcc_params)) + writel(0, &oxu->regs->segment); + + oxu->command &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | + CMD_ASE | CMD_RESET); + oxu->command |= CMD_RUN; + writel(oxu->command, &oxu->regs->command); + dbg_cmd(oxu, "init", oxu->command); + + /* + * Start, enabling full USB 2.0 functionality ... usb 1.1 devices + * are explicitly handed to companion controller(s), so no TT is + * involved with the root hub. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) + */ + hcd->state = HC_STATE_RUNNING; + writel(FLAG_CF, &oxu->regs->configured_flag); + readl(&oxu->regs->command); /* unblock posted writes */ + + temp = HC_VERSION(readl(&oxu->caps->hc_capbase)); + oxu_info(oxu, "USB %x.%x started, quasi-EHCI %x.%02x, driver %s%s\n", + ((oxu->sbrn & 0xf0)>>4), (oxu->sbrn & 0x0f), + temp >> 8, temp & 0xff, DRIVER_VERSION, + ignore_oc ? ", overcurrent ignored" : ""); + + writel(INTR_MASK, &oxu->regs->intr_enable); /* Turn On Interrupts */ + + return 0; +} + +static void oxu_stop(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + + /* Turn off port power on all root hub ports. */ + ehci_port_power(oxu, 0); + + /* no more interrupts ... */ + del_timer_sync(&oxu->watchdog); + + spin_lock_irq(&oxu->lock); + if (HC_IS_RUNNING(hcd->state)) + ehci_quiesce(oxu); + + ehci_reset(oxu); + writel(0, &oxu->regs->intr_enable); + spin_unlock_irq(&oxu->lock); + + /* let companion controllers work when we aren't */ + writel(0, &oxu->regs->configured_flag); + + /* root hub is shut down separately (first, when possible) */ + spin_lock_irq(&oxu->lock); + if (oxu->async) + ehci_work(oxu); + spin_unlock_irq(&oxu->lock); + ehci_mem_cleanup(oxu); + + dbg_status(oxu, "oxu_stop completed", readl(&oxu->regs->status)); +} + +/* Kick in for silicon on any bus (not just pci, etc). + * This forcibly disables dma and IRQs, helping kexec and other cases + * where the next system software may expect clean state. + */ +static void oxu_shutdown(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + + (void) ehci_halt(oxu); + ehci_turn_off_all_ports(oxu); + + /* make BIOS/etc use companion controller during reboot */ + writel(0, &oxu->regs->configured_flag); + + /* unblock posted writes */ + readl(&oxu->regs->configured_flag); +} + +/* Non-error returns are a promise to giveback() the urb later + * we drop ownership so next owner (or urb unlink) can get it + * + * urb + dev is in hcd.self.controller.urb_list + * we're queueing TDs onto software and hardware lists + * + * hcd-specific init for hcpriv hasn't been done yet + * + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. + */ +static int __oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + struct list_head qtd_list; + + INIT_LIST_HEAD(&qtd_list); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + default: + if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return submit_async(oxu, urb, &qtd_list, mem_flags); + + case PIPE_INTERRUPT: + if (!qh_urb_transaction(oxu, urb, &qtd_list, mem_flags)) + return -ENOMEM; + return intr_submit(oxu, urb, &qtd_list, mem_flags); + + case PIPE_ISOCHRONOUS: + if (urb->dev->speed == USB_SPEED_HIGH) + return itd_submit(oxu, urb, mem_flags); + else + return sitd_submit(oxu, urb, mem_flags); + } +} + +/* This function is responsible for breaking URBs with big data size + * into smaller size and processing small urbs in sequence. + */ +static int oxu_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int num, rem; + int transfer_buffer_length; + void *transfer_buffer; + struct urb *murb; + int i, ret; + + /* If not bulk pipe just enqueue the URB */ + if (!usb_pipebulk(urb->pipe)) + return __oxu_urb_enqueue(hcd, urb, mem_flags); + + /* Otherwise we should verify the USB transfer buffer size! */ + transfer_buffer = urb->transfer_buffer; + transfer_buffer_length = urb->transfer_buffer_length; + + num = urb->transfer_buffer_length / 4096; + rem = urb->transfer_buffer_length % 4096; + if (rem != 0) + num++; + + /* If URB is smaller than 4096 bytes just enqueue it! */ + if (num == 1) + return __oxu_urb_enqueue(hcd, urb, mem_flags); + + /* Ok, we have more job to do! :) */ + + for (i = 0; i < num - 1; i++) { + /* Get free micro URB poll till a free urb is recieved */ + + do { + murb = (struct urb *) oxu_murb_alloc(oxu); + if (!murb) + schedule(); + } while (!murb); + + /* Coping the urb */ + memcpy(murb, urb, sizeof(struct urb)); + + murb->transfer_buffer_length = 4096; + murb->transfer_buffer = transfer_buffer + i * 4096; + + /* Null pointer for the encodes that this is a micro urb */ + murb->complete = NULL; + + ((struct oxu_murb *) murb)->main = urb; + ((struct oxu_murb *) murb)->last = 0; + + /* This loop is to guarantee urb to be processed when there's + * not enough resources at a particular time by retrying. + */ + do { + ret = __oxu_urb_enqueue(hcd, murb, mem_flags); + if (ret) + schedule(); + } while (ret); + } + + /* Last urb requires special handling */ + + /* Get free micro URB poll till a free urb is recieved */ + do { + murb = (struct urb *) oxu_murb_alloc(oxu); + if (!murb) + schedule(); + } while (!murb); + + /* Coping the urb */ + memcpy(murb, urb, sizeof(struct urb)); + + murb->transfer_buffer_length = rem > 0 ? rem : 4096; + murb->transfer_buffer = transfer_buffer + (num - 1) * 4096; + + /* Null pointer for the encodes that this is a micro urb */ + murb->complete = NULL; + + ((struct oxu_murb *) murb)->main = urb; + ((struct oxu_murb *) murb)->last = 1; + + do { + ret = __oxu_urb_enqueue(hcd, murb, mem_flags); + if (ret) + schedule(); + } while (ret); + + return ret; +} + +/* Remove from hardware lists. + * Completions normally happen asynchronously + */ +static int oxu_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + struct ehci_qh *qh; + unsigned long flags; + + spin_lock_irqsave(&oxu->lock, flags); + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + default: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + unlink_async(oxu, qh); + break; + + case PIPE_INTERRUPT: + qh = (struct ehci_qh *) urb->hcpriv; + if (!qh) + break; + switch (qh->qh_state) { + case QH_STATE_LINKED: + intr_deschedule(oxu, qh); + /* FALL THROUGH */ + case QH_STATE_IDLE: + qh_completions(oxu, qh); + break; + default: + oxu_dbg(oxu, "bogus qh %p state %d\n", + qh, qh->qh_state); + goto done; + } + + /* reschedule QH iff another request is queued */ + if (!list_empty(&qh->qtd_list) + && HC_IS_RUNNING(hcd->state)) { + int status; + + status = qh_schedule(oxu, qh); + spin_unlock_irqrestore(&oxu->lock, flags); + + if (status != 0) { + /* shouldn't happen often, but ... + * FIXME kill those tds' urbs + */ + err("can't reschedule qh %p, err %d", + qh, status); + } + return status; + } + break; + } +done: + spin_unlock_irqrestore(&oxu->lock, flags); + return 0; +} + +/* Bulk qh holds the data toggle */ +static void oxu_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + unsigned long flags; + struct ehci_qh *qh, *tmp; + + /* ASSERT: any requests/urbs are being unlinked */ + /* ASSERT: nobody can be submitting urbs for this any more */ + +rescan: + spin_lock_irqsave(&oxu->lock, flags); + qh = ep->hcpriv; + if (!qh) + goto done; + + /* endpoints can be iso streams. for now, we don't + * accelerate iso completions ... so spin a while. + */ + if (qh->hw_info1 == 0) { + oxu_vdbg(oxu, "iso delay\n"); + goto idle_timeout; + } + + if (!HC_IS_RUNNING(hcd->state)) + qh->qh_state = QH_STATE_IDLE; + switch (qh->qh_state) { + case QH_STATE_LINKED: + for (tmp = oxu->async->qh_next.qh; + tmp && tmp != qh; + tmp = tmp->qh_next.qh) + continue; + /* periodic qh self-unlinks on empty */ + if (!tmp) + goto nogood; + unlink_async(oxu, qh); + /* FALL THROUGH */ + case QH_STATE_UNLINK: /* wait for hw to finish? */ +idle_timeout: + spin_unlock_irqrestore(&oxu->lock, flags); + schedule_timeout_uninterruptible(1); + goto rescan; + case QH_STATE_IDLE: /* fully unlinked */ + if (list_empty(&qh->qtd_list)) { + qh_put(qh); + break; + } + /* else FALL THROUGH */ + default: +nogood: + /* caller was supposed to have unlinked any requests; + * that's not our job. just leak this memory. + */ + oxu_err(oxu, "qh %p (#%02x) state %d%s\n", + qh, ep->desc.bEndpointAddress, qh->qh_state, + list_empty(&qh->qtd_list) ? "" : "(has tds)"); + break; + } + ep->hcpriv = NULL; +done: + spin_unlock_irqrestore(&oxu->lock, flags); + return; +} + +static int oxu_get_frame(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + + return (readl(&oxu->regs->frame_index) >> 3) % + oxu->periodic_size; +} + +/* Build "status change" packet (one or two bytes) from HC registers */ +static int oxu_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 temp, mask, status = 0; + int ports, i, retval = 1; + unsigned long flags; + + /* if !USB_SUSPEND, root hub timers won't get shut down ... */ + if (!HC_IS_RUNNING(hcd->state)) + return 0; + + /* init status to no-changes */ + buf[0] = 0; + ports = HCS_N_PORTS(oxu->hcs_params); + if (ports > 7) { + buf[1] = 0; + retval++; + } + + /* Some boards (mostly VIA?) report bogus overcurrent indications, + * causing massive log spam unless we completely ignore them. It + * may be relevant that VIA VT8235 controlers, where PORT_POWER is + * always set, seem to clear PORT_OCC and PORT_CSC when writing to + * PORT_POWER; that's surprising, but maybe within-spec. + */ + if (!ignore_oc) + mask = PORT_CSC | PORT_PEC | PORT_OCC; + else + mask = PORT_CSC | PORT_PEC; + + /* no hub change reports (bit 0) for now (power, ...) */ + + /* port N changes (bit N)? */ + spin_lock_irqsave(&oxu->lock, flags); + for (i = 0; i < ports; i++) { + temp = readl(&oxu->regs->port_status[i]); + + /* + * Return status information even for ports with OWNER set. + * Otherwise khubd wouldn't see the disconnect event when a + * high-speed device is switched over to the companion + * controller by the user. + */ + + if (!(temp & PORT_CONNECT)) + oxu->reset_done[i] = 0; + if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 && + time_after_eq(jiffies, oxu->reset_done[i]))) { + if (i < 7) + buf[0] |= 1 << (i + 1); + else + buf[1] |= 1 << (i - 7); + status = STS_PCD; + } + } + /* FIXME autosuspend idle root hubs */ + spin_unlock_irqrestore(&oxu->lock, flags); + return status ? retval : 0; +} + +/* Returns the speed of a device attached to a port on the root hub. */ +static inline unsigned int oxu_port_speed(struct oxu_hcd *oxu, + unsigned int portsc) +{ + switch ((portsc >> 26) & 3) { + case 0: + return 0; + case 1: + return 1 << USB_PORT_FEAT_LOWSPEED; + case 2: + default: + return 1 << USB_PORT_FEAT_HIGHSPEED; + } +} + +#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) +static int oxu_hub_control(struct usb_hcd *hcd, u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int ports = HCS_N_PORTS(oxu->hcs_params); + u32 __iomem *status_reg = &oxu->regs->port_status[wIndex - 1]; + u32 temp, status; + unsigned long flags; + int retval = 0; + unsigned selector; + + /* + * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. + * HCS_INDICATOR may say we can change LEDs to off/amber/green. + * (track current state ourselves) ... blink for diagnostics, + * power, "this is the one", etc. EHCI spec supports this. + */ + + spin_lock_irqsave(&oxu->lock, flags); + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = readl(status_reg); + + /* + * Even if OWNER is set, so the port is owned by the + * companion controller, khubd needs to be able to clear + * the port-change status bits (especially + * USB_PORT_FEAT_C_CONNECTION). + */ + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + writel(temp & ~PORT_PE, status_reg); + break; + case USB_PORT_FEAT_C_ENABLE: + writel((temp & ~PORT_RWC_BITS) | PORT_PEC, status_reg); + break; + case USB_PORT_FEAT_SUSPEND: + if (temp & PORT_RESET) + goto error; + if (temp & PORT_SUSPEND) { + if ((temp & PORT_PE) == 0) + goto error; + /* resume signaling for 20 msec */ + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + writel(temp | PORT_RESUME, status_reg); + oxu->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + } + break; + case USB_PORT_FEAT_C_SUSPEND: + /* we auto-clear this feature */ + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(oxu->hcs_params)) + writel(temp & ~(PORT_RWC_BITS | PORT_POWER), + status_reg); + break; + case USB_PORT_FEAT_C_CONNECTION: + writel((temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg); + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + writel((temp & ~PORT_RWC_BITS) | PORT_OCC, status_reg); + break; + case USB_PORT_FEAT_C_RESET: + /* GetPortStatus clears reset */ + break; + default: + goto error; + } + readl(&oxu->regs->command); /* unblock posted write */ + break; + case GetHubDescriptor: + ehci_hub_descriptor(oxu, (struct usb_hub_descriptor *) + buf); + break; + case GetHubStatus: + /* no hub-wide feature/status flags */ + memset(buf, 0, 4); + break; + case GetPortStatus: + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + status = 0; + temp = readl(status_reg); + + /* wPortChange bits */ + if (temp & PORT_CSC) + status |= 1 << USB_PORT_FEAT_C_CONNECTION; + if (temp & PORT_PEC) + status |= 1 << USB_PORT_FEAT_C_ENABLE; + if ((temp & PORT_OCC) && !ignore_oc) + status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + + /* whoever resumes must GetPortStatus to complete it!! */ + if (temp & PORT_RESUME) { + + /* Remote Wakeup received? */ + if (!oxu->reset_done[wIndex]) { + /* resume signaling for 20 msec */ + oxu->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + /* check the port again */ + mod_timer(&oxu_to_hcd(oxu)->rh_timer, + oxu->reset_done[wIndex]); + } + + /* resume completed? */ + else if (time_after_eq(jiffies, + oxu->reset_done[wIndex])) { + status |= 1 << USB_PORT_FEAT_C_SUSPEND; + oxu->reset_done[wIndex] = 0; + + /* stop resume signaling */ + temp = readl(status_reg); + writel(temp & ~(PORT_RWC_BITS | PORT_RESUME), + status_reg); + retval = handshake(oxu, status_reg, + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + oxu_err(oxu, + "port %d resume error %d\n", + wIndex + 1, retval); + goto error; + } + temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); + } + } + + /* whoever resets must GetPortStatus to complete it!! */ + if ((temp & PORT_RESET) + && time_after_eq(jiffies, + oxu->reset_done[wIndex])) { + status |= 1 << USB_PORT_FEAT_C_RESET; + oxu->reset_done[wIndex] = 0; + + /* force reset to complete */ + writel(temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); + /* REVISIT: some hardware needs 550+ usec to clear + * this bit; seems too long to spin routinely... + */ + retval = handshake(oxu, status_reg, + PORT_RESET, 0, 750); + if (retval != 0) { + oxu_err(oxu, "port %d reset error %d\n", + wIndex + 1, retval); + goto error; + } + + /* see what we found out */ + temp = check_reset_complete(oxu, wIndex, status_reg, + readl(status_reg)); + } + + /* transfer dedicated ports to the companion hc */ + if ((temp & PORT_CONNECT) && + test_bit(wIndex, &oxu->companion_ports)) { + temp &= ~PORT_RWC_BITS; + temp |= PORT_OWNER; + writel(temp, status_reg); + oxu_dbg(oxu, "port %d --> companion\n", wIndex + 1); + temp = readl(status_reg); + } + + /* + * Even if OWNER is set, there's no harm letting khubd + * see the wPortStatus values (they should all be 0 except + * for PORT_POWER anyway). + */ + + if (temp & PORT_CONNECT) { + status |= 1 << USB_PORT_FEAT_CONNECTION; + /* status may be from integrated TT */ + status |= oxu_port_speed(oxu, temp); + } + if (temp & PORT_PE) + status |= 1 << USB_PORT_FEAT_ENABLE; + if (temp & (PORT_SUSPEND|PORT_RESUME)) + status |= 1 << USB_PORT_FEAT_SUSPEND; + if (temp & PORT_OC) + status |= 1 << USB_PORT_FEAT_OVER_CURRENT; + if (temp & PORT_RESET) + status |= 1 << USB_PORT_FEAT_RESET; + if (temp & PORT_POWER) + status |= 1 << USB_PORT_FEAT_POWER; + +#ifndef OXU_VERBOSE_DEBUG + if (status & ~0xffff) /* only if wPortChange is interesting */ +#endif + dbg_port(oxu, "GetStatus", wIndex + 1, temp); + put_unaligned(cpu_to_le32(status), (__le32 *) buf); + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + /* no hub-wide feature/status flags */ + break; + default: + goto error; + } + break; + case SetPortFeature: + selector = wIndex >> 8; + wIndex &= 0xff; + if (!wIndex || wIndex > ports) + goto error; + wIndex--; + temp = readl(status_reg); + if (temp & PORT_OWNER) + break; + + temp &= ~PORT_RWC_BITS; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if ((temp & PORT_PE) == 0 + || (temp & PORT_RESET) != 0) + goto error; + if (device_may_wakeup(&hcd->self.root_hub->dev)) + temp |= PORT_WAKE_BITS; + writel(temp | PORT_SUSPEND, status_reg); + break; + case USB_PORT_FEAT_POWER: + if (HCS_PPC(oxu->hcs_params)) + writel(temp | PORT_POWER, status_reg); + break; + case USB_PORT_FEAT_RESET: + if (temp & PORT_RESUME) + goto error; + /* line status bits may report this as low speed, + * which can be fine if this root hub has a + * transaction translator built in. + */ + oxu_vdbg(oxu, "port %d reset\n", wIndex + 1); + temp |= PORT_RESET; + temp &= ~PORT_PE; + + /* + * caller must wait, then call GetPortStatus + * usb 2.0 spec says 50 ms resets on root + */ + oxu->reset_done[wIndex] = jiffies + + msecs_to_jiffies(50); + writel(temp, status_reg); + break; + + /* For downstream facing ports (these): one hub port is put + * into test mode according to USB2 11.24.2.13, then the hub + * must be reset (which for root hub now means rmmod+modprobe, + * or else system reboot). See EHCI 2.3.9 and 4.14 for info + * about the EHCI-specific stuff. + */ + case USB_PORT_FEAT_TEST: + if (!selector || selector > 5) + goto error; + ehci_quiesce(oxu); + ehci_halt(oxu); + temp |= selector << 16; + writel(temp, status_reg); + break; + + default: + goto error; + } + readl(&oxu->regs->command); /* unblock posted writes */ + break; + + default: +error: + /* "stall" on error */ + retval = -EPIPE; + } + spin_unlock_irqrestore(&oxu->lock, flags); + return retval; +} + +#ifdef CONFIG_PM + +static int oxu_bus_suspend(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + int port; + int mask; + + oxu_dbg(oxu, "suspend root hub\n"); + + if (time_before(jiffies, oxu->next_statechange)) + msleep(5); + + port = HCS_N_PORTS(oxu->hcs_params); + spin_lock_irq(&oxu->lock); + + /* stop schedules, clean any completed work */ + if (HC_IS_RUNNING(hcd->state)) { + ehci_quiesce(oxu); + hcd->state = HC_STATE_QUIESCING; + } + oxu->command = readl(&oxu->regs->command); + if (oxu->reclaim) + oxu->reclaim_ready = 1; + ehci_work(oxu); + + /* Unlike other USB host controller types, EHCI doesn't have + * any notion of "global" or bus-wide suspend. The driver has + * to manually suspend all the active unsuspended ports, and + * then manually resume them in the bus_resume() routine. + */ + oxu->bus_suspended = 0; + while (port--) { + u32 __iomem *reg = &oxu->regs->port_status[port]; + u32 t1 = readl(reg) & ~PORT_RWC_BITS; + u32 t2 = t1; + + /* keep track of which ports we suspend */ + if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && + !(t1 & PORT_SUSPEND)) { + t2 |= PORT_SUSPEND; + set_bit(port, &oxu->bus_suspended); + } + + /* enable remote wakeup on all ports */ + if (device_may_wakeup(&hcd->self.root_hub->dev)) + t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; + else + t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); + + if (t1 != t2) { + oxu_vdbg(oxu, "port %d, %08x -> %08x\n", + port + 1, t1, t2); + writel(t2, reg); + } + } + + /* turn off now-idle HC */ + del_timer_sync(&oxu->watchdog); + ehci_halt(oxu); + hcd->state = HC_STATE_SUSPENDED; + + /* allow remote wakeup */ + mask = INTR_MASK; + if (!device_may_wakeup(&hcd->self.root_hub->dev)) + mask &= ~STS_PCD; + writel(mask, &oxu->regs->intr_enable); + readl(&oxu->regs->intr_enable); + + oxu->next_statechange = jiffies + msecs_to_jiffies(10); + spin_unlock_irq(&oxu->lock); + return 0; +} + +/* Caller has locked the root hub, and should reset/reinit on error */ +static int oxu_bus_resume(struct usb_hcd *hcd) +{ + struct oxu_hcd *oxu = hcd_to_oxu(hcd); + u32 temp; + int i; + + if (time_before(jiffies, oxu->next_statechange)) + msleep(5); + spin_lock_irq(&oxu->lock); + + /* Ideally and we've got a real resume here, and no port's power + * was lost. (For PCI, that means Vaux was maintained.) But we + * could instead be restoring a swsusp snapshot -- so that BIOS was + * the last user of the controller, not reset/pm hardware keeping + * state we gave to it. + */ + temp = readl(&oxu->regs->intr_enable); + oxu_dbg(oxu, "resume root hub%s\n", temp ? "" : " after power loss"); + + /* at least some APM implementations will try to deliver + * IRQs right away, so delay them until we're ready. + */ + writel(0, &oxu->regs->intr_enable); + + /* re-init operational registers */ + writel(0, &oxu->regs->segment); + writel(oxu->periodic_dma, &oxu->regs->frame_list); + writel((u32) oxu->async->qh_dma, &oxu->regs->async_next); + + /* restore CMD_RUN, framelist size, and irq threshold */ + writel(oxu->command, &oxu->regs->command); + + /* Some controller/firmware combinations need a delay during which + * they set up the port statuses. See Bugzilla #8190. */ + mdelay(8); + + /* manually resume the ports we suspended during bus_suspend() */ + i = HCS_N_PORTS(oxu->hcs_params); + while (i--) { + temp = readl(&oxu->regs->port_status[i]); + temp &= ~(PORT_RWC_BITS + | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); + if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) { + oxu->reset_done[i] = jiffies + msecs_to_jiffies(20); + temp |= PORT_RESUME; + } + writel(temp, &oxu->regs->port_status[i]); + } + i = HCS_N_PORTS(oxu->hcs_params); + mdelay(20); + while (i--) { + temp = readl(&oxu->regs->port_status[i]); + if (test_bit(i, &oxu->bus_suspended) && (temp & PORT_SUSPEND)) { + temp &= ~(PORT_RWC_BITS | PORT_RESUME); + writel(temp, &oxu->regs->port_status[i]); + oxu_vdbg(oxu, "resumed port %d\n", i + 1); + } + } + (void) readl(&oxu->regs->command); + + /* maybe re-activate the schedule(s) */ + temp = 0; + if (oxu->async->qh_next.qh) + temp |= CMD_ASE; + if (oxu->periodic_sched) + temp |= CMD_PSE; + if (temp) { + oxu->command |= temp; + writel(oxu->command, &oxu->regs->command); + } + + oxu->next_statechange = jiffies + msecs_to_jiffies(5); + hcd->state = HC_STATE_RUNNING; + + /* Now we can safely re-enable irqs */ + writel(INTR_MASK, &oxu->regs->intr_enable); + + spin_unlock_irq(&oxu->lock); + return 0; +} + +#else + +static int oxu_bus_suspend(struct usb_hcd *hcd) +{ + return 0; +} + +static int oxu_bus_resume(struct usb_hcd *hcd) +{ + return 0; +} + +#endif /* CONFIG_PM */ + +static const struct hc_driver oxu_hc_driver = { + .description = "oxu210hp_hcd", + .product_desc = "oxu210hp HCD", + .hcd_priv_size = sizeof(struct oxu_hcd), + + /* + * Generic hardware linkage + */ + .irq = oxu_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * Basic lifecycle operations + */ + .reset = oxu_reset, + .start = oxu_run, + .stop = oxu_stop, + .shutdown = oxu_shutdown, + + /* + * Managing i/o requests and associated device resources + */ + .urb_enqueue = oxu_urb_enqueue, + .urb_dequeue = oxu_urb_dequeue, + .endpoint_disable = oxu_endpoint_disable, + + /* + * Scheduling support + */ + .get_frame_number = oxu_get_frame, + + /* + * Root hub support + */ + .hub_status_data = oxu_hub_status_data, + .hub_control = oxu_hub_control, + .bus_suspend = oxu_bus_suspend, + .bus_resume = oxu_bus_resume, +}; + +/* + * Module stuff + */ + +static void oxu_configuration(struct platform_device *pdev, void *base) +{ + u32 tmp; + + /* Initialize top level registers. + * First write ever + */ + oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D); + oxu_writel(base, OXU_SOFTRESET, OXU_SRESET); + oxu_writel(base, OXU_HOSTIFCONFIG, 0x0000037D); + + tmp = oxu_readl(base, OXU_PIOBURSTREADCTRL); + oxu_writel(base, OXU_PIOBURSTREADCTRL, tmp | 0x0040); + + oxu_writel(base, OXU_ASO, OXU_SPHPOEN | OXU_OVRCCURPUPDEN | + OXU_COMPARATOR | OXU_ASO_OP); + + tmp = oxu_readl(base, OXU_CLKCTRL_SET); + oxu_writel(base, OXU_CLKCTRL_SET, tmp | OXU_SYSCLKEN | OXU_USBOTGCLKEN); + + /* Clear all top interrupt enable */ + oxu_writel(base, OXU_CHIPIRQEN_CLR, 0xff); + + /* Clear all top interrupt status */ + oxu_writel(base, OXU_CHIPIRQSTATUS, 0xff); + + /* Enable all needed top interrupt except OTG SPH core */ + oxu_writel(base, OXU_CHIPIRQEN_SET, OXU_USBSPHLPWUI | OXU_USBOTGLPWUI); +} + +static int oxu_verify_id(struct platform_device *pdev, void *base) +{ + u32 id; + char *bo[] = { + "reserved", + "128-pin LQFP", + "84-pin TFBGA", + "reserved", + }; + + /* Read controller signature register to find a match */ + id = oxu_readl(base, OXU_DEVICEID); + dev_info(&pdev->dev, "device ID %x\n", id); + if ((id & OXU_REV_MASK) != (OXU_REV_2100 << OXU_REV_SHIFT)) + return -1; + + dev_info(&pdev->dev, "found device %x %s (%04x:%04x)\n", + id >> OXU_REV_SHIFT, + bo[(id & OXU_BO_MASK) >> OXU_BO_SHIFT], + (id & OXU_MAJ_REV_MASK) >> OXU_MAJ_REV_SHIFT, + (id & OXU_MIN_REV_MASK) >> OXU_MIN_REV_SHIFT); + + return 0; +} + +static const struct hc_driver oxu_hc_driver; +static struct usb_hcd *oxu_create(struct platform_device *pdev, + unsigned long memstart, unsigned long memlen, + void *base, int irq, int otg) +{ + struct device *dev = &pdev->dev; + + struct usb_hcd *hcd; + struct oxu_hcd *oxu; + int ret; + + /* Set endian mode and host mode */ + oxu_writel(base + (otg ? OXU_OTG_CORE_OFFSET : OXU_SPH_CORE_OFFSET), + OXU_USBMODE, + OXU_CM_HOST_ONLY | OXU_ES_LITTLE | OXU_VBPS); + + hcd = usb_create_hcd(&oxu_hc_driver, dev, + otg ? "oxu210hp_otg" : "oxu210hp_sph"); + if (!hcd) + return ERR_PTR(-ENOMEM); + + hcd->rsrc_start = memstart; + hcd->rsrc_len = memlen; + hcd->regs = base; + hcd->irq = irq; + hcd->state = HC_STATE_HALT; + + oxu = hcd_to_oxu(hcd); + oxu->is_otg = otg; + + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (ret < 0) + return ERR_PTR(ret); + + return hcd; +} + +static int oxu_init(struct platform_device *pdev, + unsigned long memstart, unsigned long memlen, + void *base, int irq) +{ + struct oxu_info *info = platform_get_drvdata(pdev); + struct usb_hcd *hcd; + int ret; + + /* First time configuration at start up */ + oxu_configuration(pdev, base); + + ret = oxu_verify_id(pdev, base); + if (ret) { + dev_err(&pdev->dev, "no devices found!\n"); + return -ENODEV; + } + + /* Create the OTG controller */ + hcd = oxu_create(pdev, memstart, memlen, base, irq, 1); + if (IS_ERR(hcd)) { + dev_err(&pdev->dev, "cannot create OTG controller!\n"); + ret = PTR_ERR(hcd); + goto error_create_otg; + } + info->hcd[0] = hcd; + + /* Create the SPH host controller */ + hcd = oxu_create(pdev, memstart, memlen, base, irq, 0); + if (IS_ERR(hcd)) { + dev_err(&pdev->dev, "cannot create SPH controller!\n"); + ret = PTR_ERR(hcd); + goto error_create_sph; + } + info->hcd[1] = hcd; + + oxu_writel(base, OXU_CHIPIRQEN_SET, + oxu_readl(base, OXU_CHIPIRQEN_SET) | 3); + + return 0; + +error_create_sph: + usb_remove_hcd(info->hcd[0]); + usb_put_hcd(info->hcd[0]); + +error_create_otg: + return ret; +} + +static int oxu_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + void *base; + unsigned long memstart, memlen; + int irq, ret; + struct oxu_info *info; + + if (usb_disabled()) + return -ENODEV; + + /* + * Get the platform resources + */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, + "no IRQ! Check %s setup!\n", dev_name(&pdev->dev)); + return -ENODEV; + } + irq = res->start; + dev_dbg(&pdev->dev, "IRQ resource %d\n", irq); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no registers address! Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + memstart = res->start; + memlen = res->end - res->start + 1; + dev_dbg(&pdev->dev, "MEM resource %lx-%lx\n", memstart, memlen); + if (!request_mem_region(memstart, memlen, + oxu_hc_driver.description)) { + dev_dbg(&pdev->dev, "memory area already in use\n"); + return -EBUSY; + } + + ret = set_irq_type(irq, IRQF_TRIGGER_FALLING); + if (ret) { + dev_err(&pdev->dev, "error setting irq type\n"); + ret = -EFAULT; + goto error_set_irq_type; + } + + base = ioremap(memstart, memlen); + if (!base) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + ret = -EFAULT; + goto error_ioremap; + } + + /* Allocate a driver data struct to hold useful info for both + * SPH & OTG devices + */ + info = kzalloc(sizeof(struct oxu_info), GFP_KERNEL); + if (!info) { + dev_dbg(&pdev->dev, "error allocating memory\n"); + ret = -EFAULT; + goto error_alloc; + } + platform_set_drvdata(pdev, info); + + ret = oxu_init(pdev, memstart, memlen, base, irq); + if (ret < 0) { + dev_dbg(&pdev->dev, "cannot init USB devices\n"); + goto error_init; + } + + dev_info(&pdev->dev, "devices enabled and running\n"); + platform_set_drvdata(pdev, info); + + return 0; + +error_init: + kfree(info); + platform_set_drvdata(pdev, NULL); + +error_alloc: + iounmap(base); + +error_set_irq_type: +error_ioremap: + release_mem_region(memstart, memlen); + + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); + return ret; +} + +static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) +{ + usb_remove_hcd(hcd); + usb_put_hcd(hcd); +} + +static int oxu_drv_remove(struct platform_device *pdev) +{ + struct oxu_info *info = platform_get_drvdata(pdev); + unsigned long memstart = info->hcd[0]->rsrc_start, + memlen = info->hcd[0]->rsrc_len; + void *base = info->hcd[0]->regs; + + oxu_remove(pdev, info->hcd[0]); + oxu_remove(pdev, info->hcd[1]); + + iounmap(base); + release_mem_region(memstart, memlen); + + kfree(info); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void oxu_drv_shutdown(struct platform_device *pdev) +{ + oxu_drv_remove(pdev); +} + +#if 0 +/* FIXME: TODO */ +static int oxu_drv_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} + +static int oxu_drv_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + return 0; +} +#else +#define oxu_drv_suspend NULL +#define oxu_drv_resume NULL +#endif + +static struct platform_driver oxu_driver = { + .probe = oxu_drv_probe, + .remove = oxu_drv_remove, + .shutdown = oxu_drv_shutdown, + .suspend = oxu_drv_suspend, + .resume = oxu_drv_resume, + .driver = { + .name = "oxu210hp-hcd", + .bus = &platform_bus_type + } +}; + +static int __init oxu_module_init(void) +{ + int retval = 0; + + retval = platform_driver_register(&oxu_driver); + if (retval < 0) + return retval; + + return retval; +} + +static void __exit oxu_module_cleanup(void) +{ + platform_driver_unregister(&oxu_driver); +} + +module_init(oxu_module_init); +module_exit(oxu_module_cleanup); + +MODULE_DESCRIPTION("Oxford OXU210HP HCD driver - ver. " DRIVER_VERSION); +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/oxu210hp.h b/drivers/usb/host/oxu210hp.h new file mode 100644 index 000000000000..8910e271cc7d --- /dev/null +++ b/drivers/usb/host/oxu210hp.h @@ -0,0 +1,447 @@ +/* + * Host interface registers + */ + +#define OXU_DEVICEID 0x00 + #define OXU_REV_MASK 0xffff0000 + #define OXU_REV_SHIFT 16 + #define OXU_REV_2100 0x2100 + #define OXU_BO_SHIFT 8 + #define OXU_BO_MASK (0x3 << OXU_BO_SHIFT) + #define OXU_MAJ_REV_SHIFT 4 + #define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT) + #define OXU_MIN_REV_SHIFT 0 + #define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT) +#define OXU_HOSTIFCONFIG 0x04 +#define OXU_SOFTRESET 0x08 + #define OXU_SRESET (1 << 0) + +#define OXU_PIOBURSTREADCTRL 0x0C + +#define OXU_CHIPIRQSTATUS 0x10 +#define OXU_CHIPIRQEN_SET 0x14 +#define OXU_CHIPIRQEN_CLR 0x18 + #define OXU_USBSPHLPWUI 0x00000080 + #define OXU_USBOTGLPWUI 0x00000040 + #define OXU_USBSPHI 0x00000002 + #define OXU_USBOTGI 0x00000001 + +#define OXU_CLKCTRL_SET 0x1C + #define OXU_SYSCLKEN 0x00000008 + #define OXU_USBSPHCLKEN 0x00000002 + #define OXU_USBOTGCLKEN 0x00000001 + +#define OXU_ASO 0x68 + #define OXU_SPHPOEN 0x00000100 + #define OXU_OVRCCURPUPDEN 0x00000800 + #define OXU_ASO_OP (1 << 10) + #define OXU_COMPARATOR 0x000004000 + +#define OXU_USBMODE 0x1A8 + #define OXU_VBPS 0x00000020 + #define OXU_ES_LITTLE 0x00000000 + #define OXU_CM_HOST_ONLY 0x00000003 + +/* + * Proper EHCI structs & defines + */ + +/* Magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +struct oxu_hcd; + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute[8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved[9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status[0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __attribute__ ((packed)); + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __attribute__ ((packed)); + + +#define QTD_NEXT(dma) cpu_to_le32((u32)dma) + +/* + * EHCI Specification 0.95 Section 3.5 + * QTD: describe data transfer components (buffer, direction, ...) + * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". + * + * These are associated only with "QH" (Queue Head) structures, + * used with control, bulk, and interrupt transfers. + */ +struct ehci_qtd { + /* first part defined by EHCI spec */ + __le32 hw_next; /* see EHCI 3.5.1 */ + __le32 hw_alt_next; /* see EHCI 3.5.2 */ + __le32 hw_token; /* see EHCI 3.5.3 */ +#define QTD_TOGGLE (1 << 31) /* data toggle */ +#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + __le32 hw_buf[5]; /* see EHCI 3.5.4 */ + __le32 hw_buf_hi[5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct list_head qtd_list; /* sw qtd list */ + struct urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ + + u32 qtd_buffer_len; + void *buffer; + dma_addr_t buffer_dma; + void *transfer_buffer; + void *transfer_dma; +} __attribute__ ((aligned(32))); + +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK __constant_cpu_to_le32 (~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1) + +/* Type tag from {qh, itd, sitd, fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) + +/* values for that type tag */ +#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union ehci_shadow { + struct ehci_qh *qh; /* Q_TYPE_QH */ + __le32 *hw_next; /* (all types) */ + void *ptr; +}; + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +struct ehci_qh { + /* first part defined by EHCI spec */ + __le32 hw_next; /* see EHCI 3.6.1 */ + __le32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_HEAD 0x00008000 + __le32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 + __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + __le32 hw_qtd_next; + __le32 hw_alt_next; + __le32 hw_token; + __le32 hw_buf[5]; + __le32 hw_buf_hi[5]; + + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + union ehci_shadow qh_next; /* ptr to qh; or periodic */ + struct list_head qtd_list; /* sw qtd list */ + struct ehci_qtd *dummy; + struct ehci_qh *reclaim; /* next to reclaim */ + + struct oxu_hcd *oxu; + struct kref kref; + unsigned stamp; + + u8 qh_state; +#define QH_STATE_LINKED 1 /* HC sees this */ +#define QH_STATE_UNLINK 2 /* HC may still see this */ +#define QH_STATE_IDLE 3 /* HC doesn't see this */ +#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */ +#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + u16 tt_usecs; /* tt downstream bandwidth */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + struct usb_device *dev; /* access to TT */ +} __attribute__ ((aligned(32))); + +/* + * Proper OXU210HP structs + */ + +#define OXU_OTG_CORE_OFFSET 0x00400 +#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100) +#define OXU_SPH_CORE_OFFSET 0x00800 +#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100) + +#define OXU_OTG_MEM 0xE000 +#define OXU_SPH_MEM 0x16000 + +/* Only how many elements & element structure are specifies here. */ +/* 2 host controllers are enabled - total size <= 28 kbytes */ +#define DEFAULT_I_TDPS 1024 +#define QHEAD_NUM 16 +#define QTD_NUM 32 +#define SITD_NUM 8 +#define MURB_NUM 8 + +#define BUFFER_NUM 8 +#define BUFFER_SIZE 512 + +struct oxu_info { + struct usb_hcd *hcd[2]; +}; + +struct oxu_buf { + u8 buffer[BUFFER_SIZE]; +} __attribute__ ((aligned(BUFFER_SIZE))); + +struct oxu_onchip_mem { + struct oxu_buf db_pool[BUFFER_NUM]; + + u32 frame_list[DEFAULT_I_TDPS]; + struct ehci_qh qh_pool[QHEAD_NUM]; + struct ehci_qtd qtd_pool[QTD_NUM]; +} __attribute__ ((aligned(4 << 10))); + +#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ + +struct oxu_murb { + struct urb urb; + struct urb *main; + u8 last; +}; + +struct oxu_hcd { /* one per controller */ + unsigned int is_otg:1; + + u8 qh_used[QHEAD_NUM]; + u8 qtd_used[QTD_NUM]; + u8 db_used[BUFFER_NUM]; + u8 murb_used[MURB_NUM]; + + struct oxu_onchip_mem __iomem *mem; + spinlock_t mem_lock; + + struct timer_list urb_timer; + + struct ehci_caps __iomem *caps; + struct ehci_regs __iomem *regs; + + __u32 hcs_params; /* cached register copy */ + spinlock_t lock; + + /* async schedule support */ + struct ehci_qh *async; + struct ehci_qh *reclaim; + unsigned reclaim_ready:1; + unsigned scanning:1; + + /* periodic schedule support */ + unsigned periodic_size; + __le32 *periodic; /* hw periodic table */ + dma_addr_t periodic_dma; + unsigned i_thresh; /* uframes HC might cache */ + + union ehci_shadow *pshadow; /* mirror hw periodic table */ + int next_uframe; /* scan periodic, start here */ + unsigned periodic_sched; /* periodic activity count */ + + /* per root hub port */ + unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; + /* bit vectors (one bit per port) */ + unsigned long bus_suspended; /* which ports were + * already suspended at the + * start of a bus suspend + */ + unsigned long companion_ports;/* which ports are dedicated + * to the companion controller + */ + + struct timer_list watchdog; + unsigned long actions; + unsigned stamp; + unsigned long next_statechange; + u32 command; + + /* SILICON QUIRKS */ + struct list_head urb_list; /* this is the head to urb + * queue that didn't get enough + * resources + */ + struct oxu_murb *murb_pool; /* murb per split big urb */ + unsigned urb_len; + + u8 sbrn; /* packed release number */ +}; + +#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ +#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ +#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ + +enum ehci_timer_action { + TIMER_IO_WATCHDOG, + TIMER_IAA_WATCHDOG, + TIMER_ASYNC_SHRINK, + TIMER_ASYNC_OFF, +}; + +#include <linux/oxu210hp.h> diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index ae6e70edd745..75b69847918e 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -172,9 +172,9 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) if (!mmio_resource_enabled(pdev, 0)) return; - base = ioremap_nocache(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (base == NULL) return; + base = pci_ioremap_bar(pdev, 0); + if (base == NULL) + return; /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ #ifndef __hppa__ @@ -221,9 +221,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) if (!mmio_resource_enabled(pdev, 0)) return; - base = ioremap_nocache(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (base == NULL) return; + base = pci_ioremap_bar(pdev, 0); + if (base == NULL) + return; cap_length = readb(base); op_reg_base = base + cap_length; @@ -271,7 +271,7 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) /* if boot firmware now owns EHCI, spin till * it hands it over. */ - msec = 5000; + msec = 1000; while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { tried_handoff = 1; msleep(10); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index c21f14e0666a..319041205b57 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2275,7 +2275,6 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev) return 0; } -#define resource_len(r) (((r)->end - (r)->start) + 1) static int __init r8a66597_probe(struct platform_device *pdev) { #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK) @@ -2296,11 +2295,10 @@ static int __init r8a66597_probe(struct platform_device *pdev) goto clean_up; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - (char *)hcd_name); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - dev_err(&pdev->dev, "platform_get_resource_byname error.\n"); + dev_err(&pdev->dev, "platform_get_resource error.\n"); goto clean_up; } @@ -2315,7 +2313,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) irq = ires->start; irq_trigger = ires->flags & IRQF_TRIGGER_MASK; - reg = ioremap(res->start, resource_len(res)); + reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; dev_err(&pdev->dev, "ioremap error.\n"); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index cf5e4cf7ea42..4e221060f58c 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,6 +942,8 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, + .suspend_late = usb_hcd_pci_suspend_late, + .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ }; diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 885867a86de8..4541dfcea88f 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -350,17 +350,16 @@ static int mts_scsi_abort(struct scsi_cmnd *srb) static int mts_scsi_host_reset(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); - int result, rc; + int result; MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); - rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); - if (rc < 0) - return FAILED; - result = usb_reset_device(desc->usb_dev); - if (rc) + result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); + if (result == 0) { + result = usb_reset_device(desc->usb_dev); usb_unlock_device(desc->usb_dev); + } return result ? FAILED : SUCCESS; } diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c index 24e2dc3148a4..c05a85bc5925 100644 --- a/drivers/usb/misc/berry_charge.c +++ b/drivers/usb/misc/berry_charge.c @@ -123,6 +123,11 @@ static int berry_probe(struct usb_interface *intf, { struct usb_device *udev = interface_to_usbdev(intf); + if (udev->bus_mA < 500) { + dbg(&udev->dev, "Not enough power to charge available\n"); + return -ENODEV; + } + dbg(&udev->dev, "Power is set to %dmA\n", udev->actconfig->desc.bMaxPower * 2); diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c index e762beb5f3c6..879a980ca8c4 100644 --- a/drivers/usb/misc/emi26.c +++ b/drivers/usb/misc/emi26.c @@ -160,7 +160,7 @@ static int emi26_load_firmware (struct usb_device *dev) err("%s - error loading firmware: error = %d", __func__, err); goto wraperr; } - } while (i > 0); + } while (rec); /* Assert reset (stop the CPU in the EMI) */ err = emi26_set_reset(dev,1); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 444c69c447be..5f1a19d1497d 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -192,8 +192,6 @@ static struct urb *simple_alloc_urb ( { struct urb *urb; - if (bytes < 0) - return NULL; urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) return urb; diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig index deb9ddffa402..f28f350cd96a 100644 --- a/drivers/usb/mon/Kconfig +++ b/drivers/usb/mon/Kconfig @@ -3,14 +3,13 @@ # config USB_MON - bool "USB Monitor" - depends on USB!=n - default y + tristate "USB Monitor" + depends on USB + default y if USB=y + default m if USB=m help - If you say Y here, a component which captures the USB traffic + If you select this option, a component which captures the USB traffic between peripheral-specific drivers and HC drivers will be built. For more information, see <file:Documentation/usb/usbmon.txt>. - This is somewhat experimental at this time, but it should be safe. - - If unsure, say Y. + If unsure, say Y (if allowed), otherwise M. diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile index 0f76ed5e1617..c6516b566731 100644 --- a/drivers/usb/mon/Makefile +++ b/drivers/usb/mon/Makefile @@ -4,5 +4,4 @@ usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o -# This does not use CONFIG_USB_MON because we want this to use a tristate. -obj-$(CONFIG_USB) += usbmon.o +obj-$(CONFIG_USB_MON) += usbmon.o diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 4b9542bbb35c..5af7379cd9a3 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -11,7 +11,7 @@ config USB_MUSB_HDRC depends on (USB || USB_GADGET) && HAVE_CLK depends on !SUPERH select TWL4030_USB if MACH_OMAP_3430SDP - tristate 'Inventra Highspeed Dual Role Controller (TI, ...)' + tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -22,6 +22,9 @@ config USB_MUSB_HDRC Texas Instruments parts using this IP include DaVinci 644x, OMAP 243x, OMAP 343x, and TUSB 6010. + Analog Devices parts using this IP include Blackfin BF54x, + BF525 and BF527. + If you do not know what this is, please say N. To compile this driver as a module, choose M here; the @@ -33,6 +36,8 @@ config USB_MUSB_SOC default y if ARCH_DAVINCI default y if ARCH_OMAP2430 default y if ARCH_OMAP34XX + default y if (BF54x && !BF544) + default y if (BF52x && !BF522 && !BF523) comment "DaVinci 644x USB support" depends on USB_MUSB_HDRC && ARCH_DAVINCI @@ -43,6 +48,9 @@ comment "OMAP 243x high speed USB support" comment "OMAP 343x high speed USB support" depends on USB_MUSB_HDRC && ARCH_OMAP34XX +comment "Blackfin high speed USB Support" + depends on USB_MUSB_HDRC && (BF54x && !BF544) || (BF52x && !BF522 && !BF523) + config USB_TUSB6010 boolean "TUSB 6010 support" depends on USB_MUSB_HDRC && !USB_MUSB_SOC @@ -142,7 +150,7 @@ config MUSB_PIO_ONLY config USB_INVENTRA_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY - default ARCH_OMAP2430 || ARCH_OMAP34XX + default ARCH_OMAP2430 || ARCH_OMAP34XX || BLACKFIN help Enable DMA transfers using Mentor's engine. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index b6af0d687a73..85710ccc1887 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -22,6 +22,14 @@ ifeq ($(CONFIG_ARCH_OMAP3430),y) musb_hdrc-objs += omap2430.o endif +ifeq ($(CONFIG_BF54x),y) + musb_hdrc-objs += blackfin.o +endif + +ifeq ($(CONFIG_BF52x),y) + musb_hdrc-objs += blackfin.o +endif + ifeq ($(CONFIG_USB_GADGET_MUSB_HDRC),y) musb_hdrc-objs += musb_gadget_ep0.o musb_gadget.o endif diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c new file mode 100644 index 000000000000..786134852092 --- /dev/null +++ b/drivers/usb/musb/blackfin.c @@ -0,0 +1,320 @@ +/* + * MUSB OTG controller driver for Blackfin Processors + * + * Copyright 2006-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#include <asm/cacheflush.h> + +#include "musb_core.h" +#include "blackfin.h" + +/* + * Load an endpoint's FIFO + */ +void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) +{ + void __iomem *fifo = hw_ep->fifo; + void __iomem *epio = hw_ep->regs; + + prefetch((u8 *)src); + + musb_writew(epio, MUSB_TXCOUNT, len); + + DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n", + hw_ep->epnum, fifo, len, src, epio); + + dump_fifo_data(src, len); + + if (unlikely((unsigned long)src & 0x01)) + outsw_8((unsigned long)fifo, src, + len & 0x01 ? (len >> 1) + 1 : len >> 1); + else + outsw((unsigned long)fifo, src, + len & 0x01 ? (len >> 1) + 1 : len >> 1); +} + +/* + * Unload an endpoint's FIFO + */ +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + void __iomem *fifo = hw_ep->fifo; + u8 epnum = hw_ep->epnum; + u16 dma_reg = 0; + + DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + 'R', hw_ep->epnum, fifo, len, dst); + +#ifdef CONFIG_BF52x + invalidate_dcache_range((unsigned int)dst, + (unsigned int)(dst + len)); + + /* Setup DMA address register */ + dma_reg = (u16) ((u32) dst & 0xFFFF); + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_LOW), dma_reg); + SSYNC(); + + dma_reg = (u16) (((u32) dst >> 16) & 0xFFFF); + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_ADDR_HIGH), dma_reg); + SSYNC(); + + /* Setup DMA count register */ + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_LOW), len); + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_COUNT_HIGH), 0); + SSYNC(); + + /* Enable the DMA */ + dma_reg = (epnum << 4) | DMA_ENA | INT_ENA; + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), dma_reg); + SSYNC(); + + /* Wait for compelete */ + while (!(bfin_read_USB_DMA_INTERRUPT() & (1 << epnum))) + cpu_relax(); + + /* acknowledge dma interrupt */ + bfin_write_USB_DMA_INTERRUPT(1 << epnum); + SSYNC(); + + /* Reset DMA */ + bfin_write16(USB_DMA_REG(epnum, USB_DMAx_CTRL), 0); + SSYNC(); +#else + if (unlikely((unsigned long)dst & 0x01)) + insw_8((unsigned long)fifo, dst, + len & 0x01 ? (len >> 1) + 1 : len >> 1); + else + insw((unsigned long)fifo, dst, + len & 0x01 ? (len >> 1) + 1 : len >> 1); +#endif + + dump_fifo_data(dst, len); +} + +static irqreturn_t blackfin_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t retval = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb || musb->int_tx || musb->int_rx) { + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + retval = musb_interrupt(musb); + } + + spin_unlock_irqrestore(&musb->lock, flags); + + /* REVISIT we sometimes get spurious IRQs on g_ep0 + * not clear why... fall in BF54x too. + */ + if (retval != IRQ_HANDLED) + DBG(5, "spurious?\n"); + + return IRQ_HANDLED; +} + +static void musb_conn_timer_handler(unsigned long _musb) +{ + struct musb *musb = (void *)_musb; + unsigned long flags; + u16 val; + + spin_lock_irqsave(&musb->lock, flags); + switch (musb->xceiv.state) { + case OTG_STATE_A_IDLE: + case OTG_STATE_A_WAIT_BCON: + /* Start a new session */ + val = musb_readw(musb->mregs, MUSB_DEVCTL); + val |= MUSB_DEVCTL_SESSION; + musb_writew(musb->mregs, MUSB_DEVCTL, val); + + val = musb_readw(musb->mregs, MUSB_DEVCTL); + if (!(val & MUSB_DEVCTL_BDEVICE)) { + gpio_set_value(musb->config->gpio_vrsel, 1); + musb->xceiv.state = OTG_STATE_A_WAIT_BCON; + } else { + gpio_set_value(musb->config->gpio_vrsel, 0); + + /* Ignore VBUSERROR and SUSPEND IRQ */ + val = musb_readb(musb->mregs, MUSB_INTRUSBE); + val &= ~MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSBE, val); + + val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR; + musb_writeb(musb->mregs, MUSB_INTRUSB, val); + + val = MUSB_POWER_HSENAB; + musb_writeb(musb->mregs, MUSB_POWER, val); + } + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + break; + + default: + DBG(1, "%s state not handled\n", otg_state_string(musb)); + break; + } + spin_unlock_irqrestore(&musb->lock, flags); + + DBG(4, "state is %s\n", otg_state_string(musb)); +} + +void musb_platform_enable(struct musb *musb) +{ + if (is_host_enabled(musb)) { + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); + musb->a_wait_bcon = TIMER_DELAY; + } +} + +void musb_platform_disable(struct musb *musb) +{ +} + +static void bfin_vbus_power(struct musb *musb, int is_on, int sleeping) +{ +} + +static void bfin_set_vbus(struct musb *musb, int is_on) +{ + if (is_on) + gpio_set_value(musb->config->gpio_vrsel, 1); + else + gpio_set_value(musb->config->gpio_vrsel, 0); + + DBG(1, "VBUS %s, devctl %02x " + /* otg %3x conf %08x prcm %08x */ "\n", + otg_state_string(musb), + musb_readb(musb->mregs, MUSB_DEVCTL)); +} + +static int bfin_set_power(struct otg_transceiver *x, unsigned mA) +{ + return 0; +} + +void musb_platform_try_idle(struct musb *musb, unsigned long timeout) +{ + if (is_host_enabled(musb)) + mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); +} + +int musb_platform_get_vbus_status(struct musb *musb) +{ + return 0; +} + +void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +{ +} + +int __init musb_platform_init(struct musb *musb) +{ + + /* + * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE + * and OTG HOST modes, while rev 1.1 and greater require PE7 to + * be low for DEVICE mode and high for HOST mode. We set it high + * here because we are in host mode + */ + + if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) { + printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d \n", + musb->config->gpio_vrsel); + return -ENODEV; + } + gpio_direction_output(musb->config->gpio_vrsel, 0); + + if (ANOMALY_05000346) { + bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); + SSYNC(); + } + + if (ANOMALY_05000347) { + bfin_write_USB_APHY_CNTRL(0x0); + SSYNC(); + } + + /* TODO + * Set SIC-IVG register + */ + + /* Configure PLL oscillator register */ + bfin_write_USB_PLLOSC_CTRL(0x30a8); + SSYNC(); + + bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1); + SSYNC(); + + bfin_write_USB_EP_NI0_RXMAXP(64); + SSYNC(); + + bfin_write_USB_EP_NI0_TXMAXP(64); + SSYNC(); + + /* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/ + bfin_write_USB_GLOBINTR(0x7); + SSYNC(); + + bfin_write_USB_GLOBAL_CTL(GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA | + EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA | + EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA | + EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA | + EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA); + SSYNC(); + + if (is_host_enabled(musb)) { + musb->board_set_vbus = bfin_set_vbus; + setup_timer(&musb_conn_timer, + musb_conn_timer_handler, (unsigned long) musb); + } + if (is_peripheral_enabled(musb)) + musb->xceiv.set_power = bfin_set_power; + + musb->isr = blackfin_interrupt; + + return 0; +} + +int musb_platform_suspend(struct musb *musb) +{ + return 0; +} + +int musb_platform_resume(struct musb *musb) +{ + return 0; +} + + +int musb_platform_exit(struct musb *musb) +{ + + bfin_vbus_power(musb, 0 /*off*/, 1); + gpio_free(musb->config->gpio_vrsel); + musb_platform_suspend(musb); + + return 0; +} diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h new file mode 100644 index 000000000000..a240c1e53d16 --- /dev/null +++ b/drivers/usb/musb/blackfin.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 by Analog Devices, Inc. + * + * The Inventra Controller Driver for Linux 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 __MUSB_BLACKFIN_H__ +#define __MUSB_BLACKFIN_H__ + +/* + * Blackfin specific definitions + */ + +#undef DUMP_FIFO_DATA +#ifdef DUMP_FIFO_DATA +static void dump_fifo_data(u8 *buf, u16 len) +{ + u8 *tmp = buf; + int i; + + for (i = 0; i < len; i++) { + if (!(i % 16) && i) + pr_debug("\n"); + pr_debug("%02x ", *tmp++); + } + pr_debug("\n"); +} +#else +#define dump_fifo_data(buf, len) do {} while (0) +#endif + +#ifdef CONFIG_BF52x + +#define USB_DMA_BASE USB_DMA_INTERRUPT +#define USB_DMAx_CTRL 0x04 +#define USB_DMAx_ADDR_LOW 0x08 +#define USB_DMAx_ADDR_HIGH 0x0C +#define USB_DMAx_COUNT_LOW 0x10 +#define USB_DMAx_COUNT_HIGH 0x14 + +#define USB_DMA_REG(ep, reg) (USB_DMA_BASE + 0x20 * ep + reg) +#endif + +/* Almost 1 second */ +#define TIMER_DELAY (1 * HZ) + +static struct timer_list musb_conn_timer; + +#endif /* __MUSB_BLACKFIN_H__ */ diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index dfb3bcbe00fc..0d566dc5ce06 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -32,9 +32,9 @@ #include <linux/io.h> #include <linux/gpio.h> -#include <asm/arch/hardware.h> -#include <asm/arch/memory.h> -#include <asm/arch/gpio.h> +#include <mach/arch/hardware.h> +#include <mach/arch/memory.h> +#include <mach/arch/gpio.h> #include <asm/mach-types.h> #include "musb_core.h" @@ -364,6 +364,18 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) return IRQ_HANDLED; } +int musb_platform_set_mode(struct musb *musb, u8 mode) +{ + /* EVM can't do this (right?) */ + return -EIO; +} + +int musb_platform_set_mode(struct musb *musb, u8 mode) +{ + /* EVM can't do this (right?) */ + return -EIO; +} + int __init musb_platform_init(struct musb *musb) { void __iomem *tibase = musb->ctrl_base; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 5280dba9b1fb..6c7faacfb535 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -148,7 +148,8 @@ static inline struct musb *dev_to_musb(struct device *dev) /*-------------------------------------------------------------------------*/ -#ifndef CONFIG_USB_TUSB6010 +#if !defined(CONFIG_USB_TUSB6010) && !defined(CONFIG_BLACKFIN) + /* * Load an endpoint's FIFO */ @@ -1124,25 +1125,25 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, #endif switch (cfg->style) { case FIFO_TX: - musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); - musb_writew(mbase, MUSB_TXFIFOADD, c_off); + musb_write_txfifosz(mbase, c_size); + musb_write_txfifoadd(mbase, c_off); hw_ep->tx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); hw_ep->max_packet_sz_tx = maxpacket; break; case FIFO_RX: - musb_writeb(mbase, MUSB_RXFIFOSZ, c_size); - musb_writew(mbase, MUSB_RXFIFOADD, c_off); + musb_write_rxfifosz(mbase, c_size); + musb_write_rxfifoadd(mbase, c_off); hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); hw_ep->max_packet_sz_rx = maxpacket; break; case FIFO_RXTX: - musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); - musb_writew(mbase, MUSB_TXFIFOADD, c_off); + musb_write_txfifosz(mbase, c_size); + musb_write_txfifoadd(mbase, c_off); hw_ep->rx_double_buffered = !!(c_size & MUSB_FIFOSZ_DPB); hw_ep->max_packet_sz_rx = maxpacket; - musb_writeb(mbase, MUSB_RXFIFOSZ, c_size); - musb_writew(mbase, MUSB_RXFIFOADD, c_off); + musb_write_rxfifosz(mbase, c_size); + musb_write_rxfifoadd(mbase, c_off); hw_ep->tx_double_buffered = hw_ep->rx_double_buffered; hw_ep->max_packet_sz_tx = maxpacket; @@ -1212,7 +1213,7 @@ static int __init ep_config_from_table(struct musb *musb) if (epn >= musb->config->num_eps) { pr_debug("%s: invalid ep %d\n", musb_driver_name, epn); - continue; + return -EINVAL; } offset = fifo_setup(musb, hw_ep + epn, cfg++, offset); if (offset < 0) { @@ -1246,9 +1247,10 @@ static int __init ep_config_from_table(struct musb *musb) */ static int __init ep_config_from_hw(struct musb *musb) { - u8 epnum = 0, reg; + u8 epnum = 0; struct musb_hw_ep *hw_ep; void *mbase = musb->mregs; + int ret = 0; DBG(2, "<== static silicon ep config\n"); @@ -1258,26 +1260,9 @@ static int __init ep_config_from_hw(struct musb *musb) musb_ep_select(mbase, epnum); hw_ep = musb->endpoints + epnum; - /* read from core using indexed model */ - reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE); - if (!reg) { - /* 0's returned when no more endpoints */ + ret = musb_read_fifosize(musb, hw_ep, epnum); + if (ret < 0) break; - } - musb->nr_endpoints++; - musb->epmask |= (1 << epnum); - - hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f); - - /* shared TX/RX FIFO? */ - if ((reg & 0xf0) == 0xf0) { - hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx; - hw_ep->is_shared_fifo = true; - continue; - } else { - hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4); - hw_ep->is_shared_fifo = false; - } /* FIXME set up hw_ep->{rx,tx}_double_buffered */ @@ -1326,7 +1311,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) /* log core options (read using indexed model) */ musb_ep_select(mbase, 0); - reg = musb_readb(mbase, 0x10 + MUSB_CONFIGDATA); + reg = musb_read_configdata(mbase); strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); if (reg & MUSB_CONFIGDATA_DYNFIFO) @@ -1391,7 +1376,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) } /* log release info */ - hwvers = musb_readw(mbase, MUSB_HWVERS); + hwvers = musb_read_hwvers(mbase); rev_major = (hwvers >> 10) & 0x1f; rev_minor = hwvers & 0x3ff; snprintf(aRevision, 32, "%d.%d%s", rev_major, @@ -1400,8 +1385,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) musb_driver_name, type, aRevision, aDate); /* configure ep0 */ - musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; - musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; + musb_configure_ep0(musb); /* discover endpoint configuration */ musb->nr_endpoints = 1; @@ -1445,7 +1429,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase; #ifdef CONFIG_USB_MUSB_HDRC_HCD - hw_ep->target_regs = MUSB_BUSCTL_OFFSET(i, 0) + mbase; + hw_ep->target_regs = musb_read_target_reg_base(i, mbase); hw_ep->rx_reinit = 1; hw_ep->tx_reinit = 1; #endif @@ -1671,17 +1655,20 @@ musb_mode_store(struct device *dev, struct device_attribute *attr, { struct musb *musb = dev_to_musb(dev); unsigned long flags; + int status; spin_lock_irqsave(&musb->lock, flags); - if (!strncmp(buf, "host", 4)) - musb_platform_set_mode(musb, MUSB_HOST); - if (!strncmp(buf, "peripheral", 10)) - musb_platform_set_mode(musb, MUSB_PERIPHERAL); - if (!strncmp(buf, "otg", 3)) - musb_platform_set_mode(musb, MUSB_OTG); + if (sysfs_streq(buf, "host")) + status = musb_platform_set_mode(musb, MUSB_HOST); + else if (sysfs_streq(buf, "peripheral")) + status = musb_platform_set_mode(musb, MUSB_PERIPHERAL); + else if (sysfs_streq(buf, "otg")) + status = musb_platform_set_mode(musb, MUSB_OTG); + else + status = -EINVAL; spin_unlock_irqrestore(&musb->lock, flags); - return n; + return (status == 0) ? n : status; } static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store); @@ -1781,7 +1768,7 @@ allocate_instance(struct device *dev, #ifdef CONFIG_USB_MUSB_HDRC_HCD struct usb_hcd *hcd; - hcd = usb_create_hcd(&musb_hc_driver, dev, dev->bus_id); + hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev)); if (!hcd) return NULL; /* usbcore sets dev->driver_data to hcd, and sometimes uses that... */ @@ -1810,7 +1797,6 @@ allocate_instance(struct device *dev, for (epnum = 0, ep = musb->endpoints; epnum < musb->config->num_eps; epnum++, ep++) { - ep->musb = musb; ep->epnum = epnum; } @@ -1838,7 +1824,7 @@ static void musb_free(struct musb *musb) musb_gadget_cleanup(musb); #endif - if (musb->nIrq >= 0) { + if (musb->nIrq >= 0 && musb->irq_wake) { disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } @@ -1984,15 +1970,19 @@ bad_config: INIT_WORK(&musb->irq_work, musb_irq_work); /* attach to the IRQ */ - if (request_irq(nIrq, musb->isr, 0, dev->bus_id, musb)) { + if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) { dev_err(dev, "request_irq %d failed!\n", nIrq); status = -ENODEV; goto fail2; } musb->nIrq = nIrq; /* FIXME this handles wakeup irqs wrong */ - if (enable_irq_wake(nIrq) == 0) + if (enable_irq_wake(nIrq) == 0) { + musb->irq_wake = 1; device_init_wakeup(dev, 1); + } else { + musb->irq_wake = 0; + } pr_info("%s: USB %s mode controller at %p using %s, IRQ %d\n", musb_driver_name, diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 82227251931b..630946a2d9fc 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -191,7 +191,7 @@ enum musb_g_ep0_state { */ #if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_ARCH_OMAP2430) \ - || defined(CONFIG_ARCH_OMAP3430) + || defined(CONFIG_ARCH_OMAP3430) || defined(CONFIG_BLACKFIN) /* REVISIT indexed access seemed to * misbehave (on DaVinci) for at least peripheral IN ... */ @@ -359,6 +359,7 @@ struct musb { struct otg_transceiver xceiv; int nIrq; + unsigned irq_wake:1; struct musb_hw_ep endpoints[MUSB_C_NUM_EPS]; #define control_ep endpoints @@ -447,6 +448,70 @@ static inline struct musb *gadget_to_musb(struct usb_gadget *g) } #endif +#ifdef CONFIG_BLACKFIN +static inline int musb_read_fifosize(struct musb *musb, + struct musb_hw_ep *hw_ep, u8 epnum) +{ + musb->nr_endpoints++; + musb->epmask |= (1 << epnum); + + if (epnum < 5) { + hw_ep->max_packet_sz_tx = 128; + hw_ep->max_packet_sz_rx = 128; + } else { + hw_ep->max_packet_sz_tx = 1024; + hw_ep->max_packet_sz_rx = 1024; + } + hw_ep->is_shared_fifo = false; + + return 0; +} + +static inline void musb_configure_ep0(struct musb *musb) +{ + musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].is_shared_fifo = true; +} + +#else + +static inline int musb_read_fifosize(struct musb *musb, + struct musb_hw_ep *hw_ep, u8 epnum) +{ + u8 reg = 0; + + /* read from core using indexed model */ + reg = musb_readb(hw_ep->regs, 0x10 + MUSB_FIFOSIZE); + /* 0's returned when no more endpoints */ + if (!reg) + return -ENODEV; + + musb->nr_endpoints++; + musb->epmask |= (1 << epnum); + + hw_ep->max_packet_sz_tx = 1 << (reg & 0x0f); + + /* shared TX/RX FIFO? */ + if ((reg & 0xf0) == 0xf0) { + hw_ep->max_packet_sz_rx = hw_ep->max_packet_sz_tx; + hw_ep->is_shared_fifo = true; + return 0; + } else { + hw_ep->max_packet_sz_rx = 1 << ((reg & 0xf0) >> 4); + hw_ep->is_shared_fifo = false; + } + + return 0; +} + +static inline void musb_configure_ep0(struct musb *musb) +{ + musb->endpoints[0].max_packet_sz_tx = MUSB_EP0_FIFOSIZE; + musb->endpoints[0].max_packet_sz_rx = MUSB_EP0_FIFOSIZE; +} +#endif /* CONFIG_BLACKFIN */ + /***************************** Glue it together *****************************/ @@ -467,16 +532,16 @@ extern void musb_platform_disable(struct musb *musb); extern void musb_hnp_stop(struct musb *musb); -extern void musb_platform_set_mode(struct musb *musb, u8 musb_mode); +extern int musb_platform_set_mode(struct musb *musb, u8 musb_mode); -#if defined(CONFIG_USB_TUSB6010) || \ +#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) || \ defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) extern void musb_platform_try_idle(struct musb *musb, unsigned long timeout); #else #define musb_platform_try_idle(x, y) do {} while (0) #endif -#ifdef CONFIG_USB_TUSB6010 +#if defined(CONFIG_USB_TUSB6010) || defined(CONFIG_BLACKFIN) extern int musb_platform_get_vbus_status(struct musb *musb); #else #define musb_platform_get_vbus_status(x) 0 diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index d6a802c224fa..6197daeab8f9 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1633,7 +1633,7 @@ int __init musb_gadget_setup(struct musb *musb) musb->g.speed = USB_SPEED_UNKNOWN; /* this "gadget" abstracts/virtualizes the controller */ - strcpy(musb->g.dev.bus_id, "gadget"); + dev_set_name(&musb->g.dev, "gadget"); musb->g.dev.parent = musb->controller; musb->g.dev.dma_mask = musb->controller->dma_mask; musb->g.dev.release = musb_gadget_release; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index cc64462d4c4e..99fa61234876 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -112,18 +112,21 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) { void __iomem *epio = ep->regs; u16 csr; + u16 lastcsr = 0; int retries = 1000; csr = musb_readw(epio, MUSB_TXCSR); while (csr & MUSB_TXCSR_FIFONOTEMPTY) { - DBG(5, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + if (csr != lastcsr) + DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + lastcsr = csr; csr |= MUSB_TXCSR_FLUSHFIFO; musb_writew(epio, MUSB_TXCSR, csr); csr = musb_readw(epio, MUSB_TXCSR); - if (retries-- < 1) { - ERR("Could not flush host TX fifo: csr: %04x\n", csr); + if (WARN(retries-- < 1, + "Could not flush host TX%d fifo: csr: %04x\n", + ep->epnum, csr)) return; - } mdelay(1); } } @@ -268,7 +271,7 @@ __musb_giveback(struct musb *musb, struct urb *urb, int status) __releases(musb->lock) __acquires(musb->lock) { - DBG(({ int level; switch (urb->status) { + DBG(({ int level; switch (status) { case 0: level = 4; break; @@ -283,8 +286,8 @@ __acquires(musb->lock) level = 2; break; }; level; }), - "complete %p (%d), dev%d ep%d%s, %d/%d\n", - urb, urb->status, + "complete %p %pF (%d), dev%d ep%d%s, %d/%d\n", + urb, urb->complete, status, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), usb_pipein(urb->pipe) ? "in" : "out", @@ -593,12 +596,10 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_writeb(ep->target_regs, MUSB_RXFUNCADDR, - qh->addr_reg); - musb_writeb(ep->target_regs, MUSB_RXHUBADDR, - qh->h_addr_reg); - musb_writeb(ep->target_regs, MUSB_RXHUBPORT, - qh->h_port_reg); + musb_write_rxfunaddr(ep->target_regs, qh->addr_reg); + musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg); + musb_write_rxhubport(ep->target_regs, qh->h_port_reg); + } else musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); @@ -712,15 +713,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_writeb(mbase, - MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), - qh->addr_reg); - musb_writeb(mbase, - MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), - qh->h_addr_reg); - musb_writeb(mbase, - MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), - qh->h_port_reg); + musb_write_txfunaddr(mbase, epnum, qh->addr_reg); + musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg); + musb_write_txhubport(mbase, epnum, qh->h_port_reg); /* FIXME if !epnum, do the same for RX ... */ } else musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); @@ -988,8 +983,10 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) if (fifo_count) { fifo_dest = (u8 *) (urb->transfer_buffer + urb->actual_length); - DBG(3, "Sending %d bytes to %p\n", - fifo_count, fifo_dest); + DBG(3, "Sending %d byte%s to ep0 fifo %p\n", + fifo_count, + (fifo_count == 1) ? "" : "s", + fifo_dest); musb_write_fifo(hw_ep, fifo_count, fifo_dest); urb->actual_length += fifo_count; diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 223f0a514094..b06e9ef00cfc 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -39,7 +39,7 @@ #if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \ && !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \ - && !defined(CONFIG_PPC64) + && !defined(CONFIG_PPC64) && !defined(CONFIG_BLACKFIN) static inline void readsl(const void __iomem *addr, void *buf, int len) { insl((unsigned long)addr, buf, len); } static inline void readsw(const void __iomem *addr, void *buf, int len) @@ -56,6 +56,8 @@ static inline void writesb(const void __iomem *addr, const void *buf, int len) #endif +#ifndef CONFIG_BLACKFIN + /* NOTE: these offsets are all in bytes */ static inline u16 musb_readw(const void __iomem *addr, unsigned offset) @@ -114,4 +116,26 @@ static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) #endif /* CONFIG_USB_TUSB6010 */ +#else + +static inline u8 musb_readb(const void __iomem *addr, unsigned offset) + { return (u8) (bfin_read16(addr + offset)); } + +static inline u16 musb_readw(const void __iomem *addr, unsigned offset) + { return bfin_read16(addr + offset); } + +static inline u32 musb_readl(const void __iomem *addr, unsigned offset) + { return (u32) (bfin_read16(addr + offset)); } + +static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data) + { bfin_write16(addr + offset, (u16) data); } + +static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data) + { bfin_write16(addr + offset, data); } + +static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data) + { bfin_write16(addr + offset, (u16) data); } + +#endif /* CONFIG_BLACKFIN */ + #endif diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 9c228661aa5a..de3b2f18db44 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -38,97 +38,6 @@ #define MUSB_EP0_FIFOSIZE 64 /* This is non-configurable */ /* - * Common USB registers - */ - -#define MUSB_FADDR 0x00 /* 8-bit */ -#define MUSB_POWER 0x01 /* 8-bit */ - -#define MUSB_INTRTX 0x02 /* 16-bit */ -#define MUSB_INTRRX 0x04 -#define MUSB_INTRTXE 0x06 -#define MUSB_INTRRXE 0x08 -#define MUSB_INTRUSB 0x0A /* 8 bit */ -#define MUSB_INTRUSBE 0x0B /* 8 bit */ -#define MUSB_FRAME 0x0C -#define MUSB_INDEX 0x0E /* 8 bit */ -#define MUSB_TESTMODE 0x0F /* 8 bit */ - -/* Get offset for a given FIFO from musb->mregs */ -#ifdef CONFIG_USB_TUSB6010 -#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20)) -#else -#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4)) -#endif - -/* - * Additional Control Registers - */ - -#define MUSB_DEVCTL 0x60 /* 8 bit */ - -/* These are always controlled through the INDEX register */ -#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ -#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */ -#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */ -#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */ - -/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */ -#define MUSB_HWVERS 0x6C /* 8 bit */ - -#define MUSB_EPINFO 0x78 /* 8 bit */ -#define MUSB_RAMINFO 0x79 /* 8 bit */ -#define MUSB_LINKINFO 0x7a /* 8 bit */ -#define MUSB_VPLEN 0x7b /* 8 bit */ -#define MUSB_HS_EOF1 0x7c /* 8 bit */ -#define MUSB_FS_EOF1 0x7d /* 8 bit */ -#define MUSB_LS_EOF1 0x7e /* 8 bit */ - -/* Offsets to endpoint registers */ -#define MUSB_TXMAXP 0x00 -#define MUSB_TXCSR 0x02 -#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ -#define MUSB_RXMAXP 0x04 -#define MUSB_RXCSR 0x06 -#define MUSB_RXCOUNT 0x08 -#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ -#define MUSB_TXTYPE 0x0A -#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ -#define MUSB_TXINTERVAL 0x0B -#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ -#define MUSB_RXTYPE 0x0C -#define MUSB_RXINTERVAL 0x0D -#define MUSB_FIFOSIZE 0x0F -#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */ - -/* Offsets to endpoint registers in indexed model (using INDEX register) */ -#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ - (0x10 + (_offset)) - -/* Offsets to endpoint registers in flat models */ -#define MUSB_FLAT_OFFSET(_epnum, _offset) \ - (0x100 + (0x10*(_epnum)) + (_offset)) - -#ifdef CONFIG_USB_TUSB6010 -/* TUSB6010 EP0 configuration register is special */ -#define MUSB_TUSB_OFFSET(_epnum, _offset) \ - (0x10 + _offset) -#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */ -#endif - -/* "bus control"/target registers, for host side multipoint (external hubs) */ -#define MUSB_TXFUNCADDR 0x00 -#define MUSB_TXHUBADDR 0x02 -#define MUSB_TXHUBPORT 0x03 - -#define MUSB_RXFUNCADDR 0x04 -#define MUSB_RXHUBADDR 0x06 -#define MUSB_RXHUBPORT 0x07 - -#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ - (0x80 + (8*(_epnum)) + (_offset)) - -/* * MUSB Register bits */ @@ -228,7 +137,6 @@ /* TXCSR in Peripheral and Host mode */ #define MUSB_TXCSR_AUTOSET 0x8000 -#define MUSB_TXCSR_MODE 0x2000 #define MUSB_TXCSR_DMAENAB 0x1000 #define MUSB_TXCSR_FRCDATATOG 0x0800 #define MUSB_TXCSR_DMAMODE 0x0400 @@ -297,4 +205,309 @@ /* HUBADDR */ #define MUSB_HUBADDR_MULTI_TT 0x80 + +#ifndef CONFIG_BLACKFIN + +/* + * Common USB registers + */ + +#define MUSB_FADDR 0x00 /* 8-bit */ +#define MUSB_POWER 0x01 /* 8-bit */ + +#define MUSB_INTRTX 0x02 /* 16-bit */ +#define MUSB_INTRRX 0x04 +#define MUSB_INTRTXE 0x06 +#define MUSB_INTRRXE 0x08 +#define MUSB_INTRUSB 0x0A /* 8 bit */ +#define MUSB_INTRUSBE 0x0B /* 8 bit */ +#define MUSB_FRAME 0x0C +#define MUSB_INDEX 0x0E /* 8 bit */ +#define MUSB_TESTMODE 0x0F /* 8 bit */ + +/* Get offset for a given FIFO from musb->mregs */ +#ifdef CONFIG_USB_TUSB6010 +#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20)) +#else +#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4)) +#endif + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL 0x60 /* 8 bit */ + +/* These are always controlled through the INDEX register */ +#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */ +#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */ +#define MUSB_TXFIFOADD 0x64 /* 16-bit offset shifted right 3 */ +#define MUSB_RXFIFOADD 0x66 /* 16-bit offset shifted right 3 */ + +/* REVISIT: vctrl/vstatus: optional vendor utmi+phy register at 0x68 */ +#define MUSB_HWVERS 0x6C /* 8 bit */ + +#define MUSB_EPINFO 0x78 /* 8 bit */ +#define MUSB_RAMINFO 0x79 /* 8 bit */ +#define MUSB_LINKINFO 0x7a /* 8 bit */ +#define MUSB_VPLEN 0x7b /* 8 bit */ +#define MUSB_HS_EOF1 0x7c /* 8 bit */ +#define MUSB_FS_EOF1 0x7d /* 8 bit */ +#define MUSB_LS_EOF1 0x7e /* 8 bit */ + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x00 +#define MUSB_TXCSR 0x02 +#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ +#define MUSB_RXMAXP 0x04 +#define MUSB_RXCSR 0x06 +#define MUSB_RXCOUNT 0x08 +#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ +#define MUSB_TXTYPE 0x0A +#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ +#define MUSB_TXINTERVAL 0x0B +#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ +#define MUSB_RXTYPE 0x0C +#define MUSB_RXINTERVAL 0x0D +#define MUSB_FIFOSIZE 0x0F +#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */ + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ + (0x10 + (_offset)) + +/* Offsets to endpoint registers in flat models */ +#define MUSB_FLAT_OFFSET(_epnum, _offset) \ + (0x100 + (0x10*(_epnum)) + (_offset)) + +#ifdef CONFIG_USB_TUSB6010 +/* TUSB6010 EP0 configuration register is special */ +#define MUSB_TUSB_OFFSET(_epnum, _offset) \ + (0x10 + _offset) +#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */ +#endif + +#define MUSB_TXCSR_MODE 0x2000 + +/* "bus control"/target registers, for host side multipoint (external hubs) */ +#define MUSB_TXFUNCADDR 0x00 +#define MUSB_TXHUBADDR 0x02 +#define MUSB_TXHUBPORT 0x03 + +#define MUSB_RXFUNCADDR 0x04 +#define MUSB_RXHUBADDR 0x06 +#define MUSB_RXHUBPORT 0x07 + +#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ + (0x80 + (8*(_epnum)) + (_offset)) + +static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) +{ + musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); +} + +static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off) +{ + musb_writew(mbase, MUSB_TXFIFOADD, c_off); +} + +static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size) +{ + musb_writeb(mbase, MUSB_RXFIFOSZ, c_size); +} + +static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) +{ + musb_writew(mbase, MUSB_RXFIFOADD, c_off); +} + +static inline u8 musb_read_configdata(void __iomem *mbase) +{ + return musb_readb(mbase, 0x10 + MUSB_CONFIGDATA); +} + +static inline u16 musb_read_hwvers(void __iomem *mbase) +{ + return musb_readw(mbase, MUSB_HWVERS); +} + +static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) +{ + return (MUSB_BUSCTL_OFFSET(i, 0) + mbase); +} + +static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, + u8 qh_addr_reg) +{ + musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg); +} + +static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, + u8 qh_h_addr_reg) +{ + musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg); +} + +static inline void musb_write_rxhubport(void __iomem *ep_target_regs, + u8 qh_h_port_reg) +{ + musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg); +} + +static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), + qh_addr_reg); +} + +static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + qh_addr_reg); +} + +static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, + u8 qh_h_port_reg) +{ + musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + qh_h_port_reg); +} + +#else /* CONFIG_BLACKFIN */ + +#define USB_BASE USB_FADDR +#define USB_OFFSET(reg) (reg - USB_BASE) + +/* + * Common USB registers + */ +#define MUSB_FADDR USB_OFFSET(USB_FADDR) /* 8-bit */ +#define MUSB_POWER USB_OFFSET(USB_POWER) /* 8-bit */ +#define MUSB_INTRTX USB_OFFSET(USB_INTRTX) /* 16-bit */ +#define MUSB_INTRRX USB_OFFSET(USB_INTRRX) +#define MUSB_INTRTXE USB_OFFSET(USB_INTRTXE) +#define MUSB_INTRRXE USB_OFFSET(USB_INTRRXE) +#define MUSB_INTRUSB USB_OFFSET(USB_INTRUSB) /* 8 bit */ +#define MUSB_INTRUSBE USB_OFFSET(USB_INTRUSBE)/* 8 bit */ +#define MUSB_FRAME USB_OFFSET(USB_FRAME) +#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */ +#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */ + +/* Get offset for a given FIFO from musb->mregs */ +#define MUSB_FIFO_OFFSET(epnum) \ + (USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8)) + +/* + * Additional Control Registers + */ + +#define MUSB_DEVCTL USB_OFFSET(USB_OTG_DEV_CTL) /* 8 bit */ + +#define MUSB_LINKINFO USB_OFFSET(USB_LINKINFO)/* 8 bit */ +#define MUSB_VPLEN USB_OFFSET(USB_VPLEN) /* 8 bit */ +#define MUSB_HS_EOF1 USB_OFFSET(USB_HS_EOF1) /* 8 bit */ +#define MUSB_FS_EOF1 USB_OFFSET(USB_FS_EOF1) /* 8 bit */ +#define MUSB_LS_EOF1 USB_OFFSET(USB_LS_EOF1) /* 8 bit */ + +/* Offsets to endpoint registers */ +#define MUSB_TXMAXP 0x00 +#define MUSB_TXCSR 0x04 +#define MUSB_CSR0 MUSB_TXCSR /* Re-used for EP0 */ +#define MUSB_RXMAXP 0x08 +#define MUSB_RXCSR 0x0C +#define MUSB_RXCOUNT 0x10 +#define MUSB_COUNT0 MUSB_RXCOUNT /* Re-used for EP0 */ +#define MUSB_TXTYPE 0x14 +#define MUSB_TYPE0 MUSB_TXTYPE /* Re-used for EP0 */ +#define MUSB_TXINTERVAL 0x18 +#define MUSB_NAKLIMIT0 MUSB_TXINTERVAL /* Re-used for EP0 */ +#define MUSB_RXTYPE 0x1C +#define MUSB_RXINTERVAL 0x20 +#define MUSB_TXCOUNT 0x28 + +/* Offsets to endpoint registers in indexed model (using INDEX register) */ +#define MUSB_INDEXED_OFFSET(_epnum, _offset) \ + (0x40 + (_offset)) + +/* Offsets to endpoint registers in flat models */ +#define MUSB_FLAT_OFFSET(_epnum, _offset) \ + (USB_OFFSET(USB_EP_NI0_TXMAXP) + (0x40 * (_epnum)) + (_offset)) + +/* Not implemented - HW has seperate Tx/Rx FIFO */ +#define MUSB_TXCSR_MODE 0x0000 + +/* + * Dummy stub for clk framework, it will be removed + * until Blackfin supports clk framework + */ +#define clk_get(dev, id) NULL +#define clk_put(clock) do {} while (0) +#define clk_enable(clock) do {} while (0) +#define clk_disable(clock) do {} while (0) + +static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) +{ +} + +static inline void musb_write_txfifoadd(void __iomem *mbase, u16 c_off) +{ +} + +static inline void musb_write_rxfifosz(void __iomem *mbase, u8 c_size) +{ +} + +static inline void musb_write_rxfifoadd(void __iomem *mbase, u16 c_off) +{ +} + +static inline u8 musb_read_configdata(void __iomem *mbase) +{ + return 0; +} + +static inline u16 musb_read_hwvers(void __iomem *mbase) +{ + return 0; +} + +static inline u16 musb_read_target_reg_base(u8 i, void __iomem *mbase) +{ + return 0; +} + +static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, + u8 qh_addr_req) +{ +} + +static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, + u8 qh_h_addr_reg) +{ +} + +static inline void musb_write_rxhubport(void __iomem *ep_target_regs, + u8 qh_h_port_reg) +{ +} + +static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ +} + +static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, + u8 qh_addr_reg) +{ +} + +static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, + u8 qh_h_port_reg) +{ +} + +#endif /* CONFIG_BLACKFIN */ + #endif /* __MUSB_REGS_H__ */ diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 8c734ef2c1ed..8662e9e159c3 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -34,58 +34,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include "musb_core.h" - -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) -#include "omap2430.h" -#endif - -#define MUSB_HSDMA_BASE 0x200 -#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) -#define MUSB_HSDMA_CONTROL 0x4 -#define MUSB_HSDMA_ADDRESS 0x8 -#define MUSB_HSDMA_COUNT 0xc - -#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ - (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset) - -/* control register (16-bit): */ -#define MUSB_HSDMA_ENABLE_SHIFT 0 -#define MUSB_HSDMA_TRANSMIT_SHIFT 1 -#define MUSB_HSDMA_MODE1_SHIFT 2 -#define MUSB_HSDMA_IRQENABLE_SHIFT 3 -#define MUSB_HSDMA_ENDPOINT_SHIFT 4 -#define MUSB_HSDMA_BUSERROR_SHIFT 8 -#define MUSB_HSDMA_BURSTMODE_SHIFT 9 -#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT) -#define MUSB_HSDMA_BURSTMODE_UNSPEC 0 -#define MUSB_HSDMA_BURSTMODE_INCR4 1 -#define MUSB_HSDMA_BURSTMODE_INCR8 2 -#define MUSB_HSDMA_BURSTMODE_INCR16 3 - -#define MUSB_HSDMA_CHANNELS 8 - -struct musb_dma_controller; - -struct musb_dma_channel { - struct dma_channel channel; - struct musb_dma_controller *controller; - u32 start_addr; - u32 len; - u16 max_packet_sz; - u8 idx; - u8 epnum; - u8 transmit; -}; - -struct musb_dma_controller { - struct dma_controller controller; - struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS]; - void *private_data; - void __iomem *base; - u8 channel_count; - u8 used_channels; - u8 irq; -}; +#include "musbhsdma.h" static int dma_controller_start(struct dma_controller *c) { @@ -203,12 +152,8 @@ static void configure_channel(struct dma_channel *channel, : 0); /* address/count */ - musb_writel(mbase, - MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), - dma_addr); - musb_writel(mbase, - MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), - len); + musb_write_hsdma_addr(mbase, bchannel, dma_addr); + musb_write_hsdma_count(mbase, bchannel, len); /* control (this should start things) */ musb_writew(mbase, @@ -279,13 +224,8 @@ static int dma_channel_abort(struct dma_channel *channel) musb_writew(mbase, MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_CONTROL), 0); - musb_writel(mbase, - MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), - 0); - musb_writel(mbase, - MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), - 0); - + musb_write_hsdma_addr(mbase, bchannel, 0); + musb_write_hsdma_count(mbase, bchannel, 0); channel->status = MUSB_DMA_STATUS_FREE; } @@ -333,10 +273,8 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) } else { u8 devctl; - addr = musb_readl(mbase, - MUSB_HSDMA_CHANNEL_OFFSET( - bchannel, - MUSB_HSDMA_ADDRESS)); + addr = musb_read_hsdma_addr(mbase, + bchannel); channel->actual_len = addr - musb_channel->start_addr; @@ -375,6 +313,12 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) } } } + +#ifdef CONFIG_BLACKFIN + /* Clear DMA interrup flags */ + musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma); +#endif + retval = IRQ_HANDLED; done: spin_unlock_irqrestore(&musb->lock, flags); @@ -424,7 +368,7 @@ dma_controller_create(struct musb *musb, void __iomem *base) controller->controller.channel_abort = dma_channel_abort; if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, - musb->controller->bus_id, &controller->controller)) { + dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); dma_controller_destroy(&controller->controller); diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h new file mode 100644 index 000000000000..1299d92dc83f --- /dev/null +++ b/drivers/usb/musb/musbhsdma.h @@ -0,0 +1,149 @@ +/* + * MUSB OTG driver - support for Mentor's DMA controller + * + * Copyright 2005 Mentor Graphics Corporation + * Copyright (C) 2005-2007 by Texas Instruments + * + * 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 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * 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 AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) +#include "omap2430.h" +#endif + +#ifndef CONFIG_BLACKFIN + +#define MUSB_HSDMA_BASE 0x200 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x4 +#define MUSB_HSDMA_ADDRESS 0x8 +#define MUSB_HSDMA_COUNT 0xc + +#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ + (MUSB_HSDMA_BASE + (_bchannel << 4) + _offset) + +#define musb_read_hsdma_addr(mbase, bchannel) \ + musb_readl(mbase, \ + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS)) + +#define musb_write_hsdma_addr(mbase, bchannel, addr) \ + musb_writel(mbase, \ + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS), \ + addr) + +#define musb_write_hsdma_count(mbase, bchannel, len) \ + musb_writel(mbase, \ + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT), \ + len) +#else + +#define MUSB_HSDMA_BASE 0x400 +#define MUSB_HSDMA_INTR (MUSB_HSDMA_BASE + 0) +#define MUSB_HSDMA_CONTROL 0x04 +#define MUSB_HSDMA_ADDR_LOW 0x08 +#define MUSB_HSDMA_ADDR_HIGH 0x0C +#define MUSB_HSDMA_COUNT_LOW 0x10 +#define MUSB_HSDMA_COUNT_HIGH 0x14 + +#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset) \ + (MUSB_HSDMA_BASE + (_bchannel * 0x20) + _offset) + +static inline u32 musb_read_hsdma_addr(void __iomem *mbase, u8 bchannel) +{ + u32 addr = musb_readw(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_HIGH)); + + addr = addr << 16; + + addr |= musb_readw(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_LOW)); + + return addr; +} + +static inline void musb_write_hsdma_addr(void __iomem *mbase, + u8 bchannel, dma_addr_t dma_addr) +{ + musb_writew(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_LOW), + ((u16)((u32) dma_addr & 0xFFFF))); + musb_writew(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDR_HIGH), + ((u16)(((u32) dma_addr >> 16) & 0xFFFF))); +} + +static inline void musb_write_hsdma_count(void __iomem *mbase, + u8 bchannel, u32 len) +{ + musb_writew(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_LOW), + ((u16)((u32) len & 0xFFFF))); + musb_writew(mbase, + MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT_HIGH), + ((u16)(((u32) len >> 16) & 0xFFFF))); +} + +#endif /* CONFIG_BLACKFIN */ + +/* control register (16-bit): */ +#define MUSB_HSDMA_ENABLE_SHIFT 0 +#define MUSB_HSDMA_TRANSMIT_SHIFT 1 +#define MUSB_HSDMA_MODE1_SHIFT 2 +#define MUSB_HSDMA_IRQENABLE_SHIFT 3 +#define MUSB_HSDMA_ENDPOINT_SHIFT 4 +#define MUSB_HSDMA_BUSERROR_SHIFT 8 +#define MUSB_HSDMA_BURSTMODE_SHIFT 9 +#define MUSB_HSDMA_BURSTMODE (3 << MUSB_HSDMA_BURSTMODE_SHIFT) +#define MUSB_HSDMA_BURSTMODE_UNSPEC 0 +#define MUSB_HSDMA_BURSTMODE_INCR4 1 +#define MUSB_HSDMA_BURSTMODE_INCR8 2 +#define MUSB_HSDMA_BURSTMODE_INCR16 3 + +#define MUSB_HSDMA_CHANNELS 8 + +struct musb_dma_controller; + +struct musb_dma_channel { + struct dma_channel channel; + struct musb_dma_controller *controller; + u32 start_addr; + u32 len; + u16 max_packet_sz; + u8 idx; + u8 epnum; + u8 transmit; +}; + +struct musb_dma_controller { + struct dma_controller controller; + struct musb_dma_channel channel[MUSB_HSDMA_CHANNELS]; + void *private_data; + void __iomem *base; + u8 channel_count; + u8 used_channels; + u8 irq; +}; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index ce6c162920f7..901dffdf23b1 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -58,10 +58,10 @@ static void musb_do_idle(unsigned long _musb) #endif u8 devctl; - devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - spin_lock_irqsave(&musb->lock, flags); + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + switch (musb->xceiv.state) { case OTG_STATE_A_WAIT_BCON: devctl &= ~MUSB_DEVCTL_SESSION; @@ -196,7 +196,7 @@ static int omap_set_power(struct otg_transceiver *x, unsigned mA) static int musb_platform_resume(struct musb *musb); -void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +int musb_platform_set_mode(struct musb *musb, u8 musb_mode) { u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -204,15 +204,24 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); switch (musb_mode) { +#ifdef CONFIG_USB_MUSB_HDRC_HCD case MUSB_HOST: otg_set_host(&musb->xceiv, musb->xceiv.host); break; +#endif +#ifdef CONFIG_USB_GADGET_MUSB_HDRC case MUSB_PERIPHERAL: otg_set_peripheral(&musb->xceiv, musb->xceiv.gadget); break; +#endif +#ifdef CONFIG_USB_MUSB_OTG case MUSB_OTG: break; +#endif + default: + return -EINVAL; } + return 0; } int __init musb_platform_init(struct musb *musb) diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index ee8fca92a4ac..9e20fd070d71 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -598,7 +598,7 @@ static void tusb_source_power(struct musb *musb, int is_on) * and peripheral modes in non-OTG configurations by reconfiguring hardware * and then setting musb->board_mode. For now, only support OTG mode. */ -void musb_platform_set_mode(struct musb *musb, u8 musb_mode) +int musb_platform_set_mode(struct musb *musb, u8 musb_mode) { void __iomem *tbase = musb->ctrl_base; u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf; @@ -641,7 +641,8 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) #endif default: - DBG(2, "Trying to set unknown mode %i\n", musb_mode); + DBG(2, "Trying to set mode %i\n", musb_mode); + return -EINVAL; } musb_writel(tbase, TUSB_PHY_OTG_CTRL, @@ -655,6 +656,8 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) INFO("Cannot be peripheral with mini-A cable " "otg_stat: %08x\n", otg_stat); + + return 0; } static inline unsigned long diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig new file mode 100644 index 000000000000..8e8dbdb9b39b --- /dev/null +++ b/drivers/usb/otg/Kconfig @@ -0,0 +1,54 @@ +# +# USB OTG infrastructure may be needed for peripheral-only, host-only, +# or OTG-capable configurations when OTG transceivers or controllers +# are used. +# + +comment "OTG and related infrastructure" + +if USB || USB_GADGET + +config USB_OTG_UTILS + bool + help + Select this to make sure the build includes objects from + the OTG infrastructure directory. + +# +# USB Transceiver Drivers +# +config USB_GPIO_VBUS + tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" + depends on GENERIC_GPIO + select USB_OTG_UTILS + help + Provides simple GPIO VBUS sensing for controllers with an + internal transceiver via the otg_transceiver interface, and + optionally control of a D+ pullup GPIO as well as a VBUS + current limit regulator. + +config ISP1301_OMAP + tristate "Philips ISP1301 with OMAP OTG" + depends on I2C && ARCH_OMAP_OTG + select USB_OTG_UTILS + help + If you say yes here you get support for the Philips ISP1301 + USB-On-The-Go transceiver working with the OMAP OTG controller. + The ISP1301 is a full speed USB transceiver which is used in + products including H2, H3, and H4 development boards for Texas + Instruments OMAP processors. + + This driver can also be built as a module. If so, the module + will be called isp1301_omap. + +config TWL4030_USB + tristate "TWL4030 USB Transceiver Driver" + depends on TWL4030_CORE + select USB_OTG_UTILS + help + Enable this to support the USB OTG transceiver on TWL4030 + family chips (including the TWL5030 and TPS659x0 devices). + This transceiver supports high and full speed devices plus, + in host mode, low speed. + +endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile new file mode 100644 index 000000000000..d73c7cf5e2f7 --- /dev/null +++ b/drivers/usb/otg/Makefile @@ -0,0 +1,15 @@ +# +# OTG infrastructure and transceiver drivers +# + +# infrastructure +obj-$(CONFIG_USB_OTG_UTILS) += otg.o + +# transceiver drivers +obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o +obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o +obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o + +ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG +ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG + diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c new file mode 100644 index 000000000000..63a6036f04be --- /dev/null +++ b/drivers/usb/otg/gpio_vbus.c @@ -0,0 +1,335 @@ +/* + * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices + * + * Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com> + * + * 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 <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/usb.h> + +#include <linux/regulator/consumer.h> + +#include <linux/usb/gadget.h> +#include <linux/usb/gpio_vbus.h> +#include <linux/usb/otg.h> + + +/* + * A simple GPIO VBUS sensing driver for B peripheral only devices + * with internal transceivers. It can control a D+ pullup GPIO and + * a regulator to limit the current drawn from VBUS. + * + * Needs to be loaded before the UDC driver that will use it. + */ +struct gpio_vbus_data { + struct otg_transceiver otg; + struct device *dev; + struct regulator *vbus_draw; + int vbus_draw_enabled; + unsigned mA; +}; + + +/* + * This driver relies on "both edges" triggering. VBUS has 100 msec to + * stabilize, so the peripheral controller driver may need to cope with + * some bouncing due to current surges (e.g. charging local capacitance) + * and contact chatter. + * + * REVISIT in desperate straits, toggling between rising and falling + * edges might be workable. + */ +#define VBUS_IRQ_FLAGS \ + ( IRQF_SAMPLE_RANDOM | IRQF_SHARED \ + | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ) + + +/* interface to regulator framework */ +static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA) +{ + struct regulator *vbus_draw = gpio_vbus->vbus_draw; + int enabled; + + if (!vbus_draw) + return; + + enabled = gpio_vbus->vbus_draw_enabled; + if (mA) { + regulator_set_current_limit(vbus_draw, 0, 1000 * mA); + if (!enabled) { + regulator_enable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 1; + } + } else { + if (enabled) { + regulator_disable(vbus_draw); + gpio_vbus->vbus_draw_enabled = 0; + } + } + gpio_vbus->mA = mA; +} + +/* VBUS change IRQ handler */ +static irqreturn_t gpio_vbus_irq(int irq, void *data) +{ + struct platform_device *pdev = data; + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + int gpio, vbus; + + vbus = gpio_get_value(pdata->gpio_vbus); + if (pdata->gpio_vbus_inverted) + vbus = !vbus; + + dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n", + vbus ? "supplied" : "inactive", + gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none"); + + if (!gpio_vbus->otg.gadget) + return IRQ_HANDLED; + + /* Peripheral controllers which manage the pullup themselves won't have + * gpio_pullup configured here. If it's configured here, we'll do what + * isp1301_omap::b_peripheral() does and enable the pullup here... although + * that may complicate usb_gadget_{,dis}connect() support. + */ + gpio = pdata->gpio_pullup; + if (vbus) { + gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL; + usb_gadget_vbus_connect(gpio_vbus->otg.gadget); + + /* drawing a "unit load" is *always* OK, except for OTG */ + set_vbus_draw(gpio_vbus, 100); + + /* optionally enable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, !pdata->gpio_pullup_inverted); + } else { + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget); + gpio_vbus->otg.state = OTG_STATE_B_IDLE; + } + + return IRQ_HANDLED; +} + +/* OTG transceiver interface */ + +/* bind/unbind the peripheral controller */ +static int gpio_vbus_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + struct gpio_vbus_data *gpio_vbus; + struct gpio_vbus_mach_info *pdata; + struct platform_device *pdev; + int gpio, irq; + + gpio_vbus = container_of(otg, struct gpio_vbus_data, otg); + pdev = to_platform_device(gpio_vbus->dev); + pdata = gpio_vbus->dev->platform_data; + irq = gpio_to_irq(pdata->gpio_vbus); + gpio = pdata->gpio_pullup; + + if (!gadget) { + dev_dbg(&pdev->dev, "unregistering gadget '%s'\n", + otg->gadget->name); + + /* optionally disable D+ pullup */ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, pdata->gpio_pullup_inverted); + + set_vbus_draw(gpio_vbus, 0); + + usb_gadget_vbus_disconnect(otg->gadget); + otg->state = OTG_STATE_UNDEFINED; + + otg->gadget = NULL; + return 0; + } + + otg->gadget = gadget; + dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name); + + /* initialize connection state */ + gpio_vbus_irq(irq, pdev); + return 0; +} + +/* effective for B devices, ignored for A-peripheral */ +static int gpio_vbus_set_power(struct otg_transceiver *otg, unsigned mA) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(otg, struct gpio_vbus_data, otg); + + if (otg->state == OTG_STATE_B_PERIPHERAL) + set_vbus_draw(gpio_vbus, mA); + return 0; +} + +/* for non-OTG B devices: set/clear transceiver suspend mode */ +static int gpio_vbus_set_suspend(struct otg_transceiver *otg, int suspend) +{ + struct gpio_vbus_data *gpio_vbus; + + gpio_vbus = container_of(otg, struct gpio_vbus_data, otg); + + /* draw max 0 mA from vbus in suspend mode; or the previously + * recorded amount of current if not suspended + * + * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA + * if they're wake-enabled ... we don't handle that yet. + */ + return gpio_vbus_set_power(otg, suspend ? 0 : gpio_vbus->mA); +} + +/* platform driver interface */ + +static int __init gpio_vbus_probe(struct platform_device *pdev) +{ + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + struct gpio_vbus_data *gpio_vbus; + struct resource *res; + int err, gpio, irq; + + if (!pdata || !gpio_is_valid(pdata->gpio_vbus)) + return -EINVAL; + gpio = pdata->gpio_vbus; + + gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL); + if (!gpio_vbus) + return -ENOMEM; + + platform_set_drvdata(pdev, gpio_vbus); + gpio_vbus->dev = &pdev->dev; + gpio_vbus->otg.label = "gpio-vbus"; + gpio_vbus->otg.state = OTG_STATE_UNDEFINED; + gpio_vbus->otg.set_peripheral = gpio_vbus_set_peripheral; + gpio_vbus->otg.set_power = gpio_vbus_set_power; + gpio_vbus->otg.set_suspend = gpio_vbus_set_suspend; + + err = gpio_request(gpio, "vbus_detect"); + if (err) { + dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n", + gpio, err); + goto err_gpio; + } + gpio_direction_input(gpio); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) { + irq = res->start; + res->flags &= IRQF_TRIGGER_MASK; + res->flags |= IRQF_SAMPLE_RANDOM | IRQF_SHARED; + } else + irq = gpio_to_irq(gpio); + + /* if data line pullup is in use, initialize it to "not pulling up" */ + gpio = pdata->gpio_pullup; + if (gpio_is_valid(gpio)) { + err = gpio_request(gpio, "udc_pullup"); + if (err) { + dev_err(&pdev->dev, + "can't request pullup gpio %d, err: %d\n", + gpio, err); + gpio_free(pdata->gpio_vbus); + goto err_gpio; + } + gpio_direction_output(gpio, pdata->gpio_pullup_inverted); + } + + err = request_irq(irq, gpio_vbus_irq, VBUS_IRQ_FLAGS, + "vbus_detect", pdev); + if (err) { + dev_err(&pdev->dev, "can't request irq %i, err: %d\n", + irq, err); + goto err_irq; + } + + /* only active when a gadget is registered */ + err = otg_set_transceiver(&gpio_vbus->otg); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_otg; + } + + gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + if (IS_ERR(gpio_vbus->vbus_draw)) { + dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", + PTR_ERR(gpio_vbus->vbus_draw)); + gpio_vbus->vbus_draw = NULL; + } + + return 0; +err_otg: + free_irq(irq, &pdev->dev); +err_irq: + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(pdata->gpio_vbus); +err_gpio: + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus); + return err; +} + +static int __exit gpio_vbus_remove(struct platform_device *pdev) +{ + struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev); + struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data; + int gpio = pdata->gpio_vbus; + + regulator_put(gpio_vbus->vbus_draw); + + otg_set_transceiver(NULL); + + free_irq(gpio_to_irq(gpio), &pdev->dev); + if (gpio_is_valid(pdata->gpio_pullup)) + gpio_free(pdata->gpio_pullup); + gpio_free(gpio); + platform_set_drvdata(pdev, NULL); + kfree(gpio_vbus); + + return 0; +} + +/* NOTE: the gpio-vbus device may *NOT* be hotplugged */ + +MODULE_ALIAS("platform:gpio-vbus"); + +static struct platform_driver gpio_vbus_driver = { + .driver = { + .name = "gpio-vbus", + .owner = THIS_MODULE, + }, + .remove = __exit_p(gpio_vbus_remove), +}; + +static int __init gpio_vbus_init(void) +{ + return platform_driver_probe(&gpio_vbus_driver, gpio_vbus_probe); +} +module_init(gpio_vbus_init); + +static void __exit gpio_vbus_exit(void) +{ + platform_driver_unregister(&gpio_vbus_driver); +} +module_exit(gpio_vbus_exit); + +MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver"); +MODULE_AUTHOR("Philipp Zabel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index e0d56ef2bcb0..e0d56ef2bcb0 100644 --- a/drivers/i2c/chips/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c new file mode 100644 index 000000000000..ff318fae7d4d --- /dev/null +++ b/drivers/usb/otg/otg.c @@ -0,0 +1,65 @@ +/* + * otg.c -- USB OTG utility code + * + * Copyright (C) 2004 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/device.h> + +#include <linux/usb/otg.h> + +static struct otg_transceiver *xceiv; + +/** + * otg_get_transceiver - find the (single) OTG transceiver + * + * Returns the transceiver driver, after getting a refcount to it; or + * null if there is no such transceiver. The caller is responsible for + * calling otg_put_transceiver() to release that count. + * + * For use by USB host and peripheral drivers. + */ +struct otg_transceiver *otg_get_transceiver(void) +{ + if (xceiv) + get_device(xceiv->dev); + return xceiv; +} +EXPORT_SYMBOL(otg_get_transceiver); + +/** + * otg_put_transceiver - release the (single) OTG transceiver + * @x: the transceiver returned by otg_get_transceiver() + * + * Releases a refcount the caller received from otg_get_transceiver(). + * + * For use by USB host and peripheral drivers. + */ +void otg_put_transceiver(struct otg_transceiver *x) +{ + put_device(x->dev); +} +EXPORT_SYMBOL(otg_put_transceiver); + +/** + * otg_set_transceiver - declare the (single) OTG transceiver + * @x: the USB OTG transceiver to be used; or NULL + * + * This call is exclusively for use by transceiver drivers, which + * coordinate the activities of drivers for host and peripheral + * controllers, and in some cases for VBUS current regulation. + */ +int otg_set_transceiver(struct otg_transceiver *x) +{ + if (xceiv && x) + return -EBUSY; + xceiv = x; + return 0; +} +EXPORT_SYMBOL(otg_set_transceiver); diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c new file mode 100644 index 000000000000..416e4410be02 --- /dev/null +++ b/drivers/usb/otg/twl4030-usb.c @@ -0,0 +1,721 @@ +/* + * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004-2007 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi <felipe.balbi@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * - HS USB ULPI mode works. + * - 3-pin mode support may be added in future. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/usb/otg.h> +#include <linux/i2c/twl4030.h> + + +/* Register defines */ + +#define VENDOR_ID_LO 0x00 +#define VENDOR_ID_HI 0x01 +#define PRODUCT_ID_LO 0x02 +#define PRODUCT_ID_HI 0x03 + +#define FUNC_CTRL 0x04 +#define FUNC_CTRL_SET 0x05 +#define FUNC_CTRL_CLR 0x06 +#define FUNC_CTRL_SUSPENDM (1 << 6) +#define FUNC_CTRL_RESET (1 << 5) +#define FUNC_CTRL_OPMODE_MASK (3 << 3) /* bits 3 and 4 */ +#define FUNC_CTRL_OPMODE_NORMAL (0 << 3) +#define FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) +#define FUNC_CTRL_OPMODE_DISABLE_BIT_NRZI (2 << 3) +#define FUNC_CTRL_TERMSELECT (1 << 2) +#define FUNC_CTRL_XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */ +#define FUNC_CTRL_XCVRSELECT_HS (0 << 0) +#define FUNC_CTRL_XCVRSELECT_FS (1 << 0) +#define FUNC_CTRL_XCVRSELECT_LS (2 << 0) +#define FUNC_CTRL_XCVRSELECT_FS4LS (3 << 0) + +#define IFC_CTRL 0x07 +#define IFC_CTRL_SET 0x08 +#define IFC_CTRL_CLR 0x09 +#define IFC_CTRL_INTERFACE_PROTECT_DISABLE (1 << 7) +#define IFC_CTRL_AUTORESUME (1 << 4) +#define IFC_CTRL_CLOCKSUSPENDM (1 << 3) +#define IFC_CTRL_CARKITMODE (1 << 2) +#define IFC_CTRL_FSLSSERIALMODE_3PIN (1 << 1) + +#define TWL4030_OTG_CTRL 0x0A +#define TWL4030_OTG_CTRL_SET 0x0B +#define TWL4030_OTG_CTRL_CLR 0x0C +#define TWL4030_OTG_CTRL_DRVVBUS (1 << 5) +#define TWL4030_OTG_CTRL_CHRGVBUS (1 << 4) +#define TWL4030_OTG_CTRL_DISCHRGVBUS (1 << 3) +#define TWL4030_OTG_CTRL_DMPULLDOWN (1 << 2) +#define TWL4030_OTG_CTRL_DPPULLDOWN (1 << 1) +#define TWL4030_OTG_CTRL_IDPULLUP (1 << 0) + +#define USB_INT_EN_RISE 0x0D +#define USB_INT_EN_RISE_SET 0x0E +#define USB_INT_EN_RISE_CLR 0x0F +#define USB_INT_EN_FALL 0x10 +#define USB_INT_EN_FALL_SET 0x11 +#define USB_INT_EN_FALL_CLR 0x12 +#define USB_INT_STS 0x13 +#define USB_INT_LATCH 0x14 +#define USB_INT_IDGND (1 << 4) +#define USB_INT_SESSEND (1 << 3) +#define USB_INT_SESSVALID (1 << 2) +#define USB_INT_VBUSVALID (1 << 1) +#define USB_INT_HOSTDISCONNECT (1 << 0) + +#define CARKIT_CTRL 0x19 +#define CARKIT_CTRL_SET 0x1A +#define CARKIT_CTRL_CLR 0x1B +#define CARKIT_CTRL_MICEN (1 << 6) +#define CARKIT_CTRL_SPKRIGHTEN (1 << 5) +#define CARKIT_CTRL_SPKLEFTEN (1 << 4) +#define CARKIT_CTRL_RXDEN (1 << 3) +#define CARKIT_CTRL_TXDEN (1 << 2) +#define CARKIT_CTRL_IDGNDDRV (1 << 1) +#define CARKIT_CTRL_CARKITPWR (1 << 0) +#define CARKIT_PLS_CTRL 0x22 +#define CARKIT_PLS_CTRL_SET 0x23 +#define CARKIT_PLS_CTRL_CLR 0x24 +#define CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) +#define CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) +#define CARKIT_PLS_CTRL_RXPLSEN (1 << 1) +#define CARKIT_PLS_CTRL_TXPLSEN (1 << 0) + +#define MCPC_CTRL 0x30 +#define MCPC_CTRL_SET 0x31 +#define MCPC_CTRL_CLR 0x32 +#define MCPC_CTRL_RTSOL (1 << 7) +#define MCPC_CTRL_EXTSWR (1 << 6) +#define MCPC_CTRL_EXTSWC (1 << 5) +#define MCPC_CTRL_VOICESW (1 << 4) +#define MCPC_CTRL_OUT64K (1 << 3) +#define MCPC_CTRL_RTSCTSSW (1 << 2) +#define MCPC_CTRL_HS_UART (1 << 0) + +#define MCPC_IO_CTRL 0x33 +#define MCPC_IO_CTRL_SET 0x34 +#define MCPC_IO_CTRL_CLR 0x35 +#define MCPC_IO_CTRL_MICBIASEN (1 << 5) +#define MCPC_IO_CTRL_CTS_NPU (1 << 4) +#define MCPC_IO_CTRL_RXD_PU (1 << 3) +#define MCPC_IO_CTRL_TXDTYP (1 << 2) +#define MCPC_IO_CTRL_CTSTYP (1 << 1) +#define MCPC_IO_CTRL_RTSTYP (1 << 0) + +#define MCPC_CTRL2 0x36 +#define MCPC_CTRL2_SET 0x37 +#define MCPC_CTRL2_CLR 0x38 +#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) + +#define OTHER_FUNC_CTRL 0x80 +#define OTHER_FUNC_CTRL_SET 0x81 +#define OTHER_FUNC_CTRL_CLR 0x82 +#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) +#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) + +#define OTHER_IFC_CTRL 0x83 +#define OTHER_IFC_CTRL_SET 0x84 +#define OTHER_IFC_CTRL_CLR 0x85 +#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) +#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) +#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) +#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) +#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) +#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) + +#define OTHER_INT_EN_RISE 0x86 +#define OTHER_INT_EN_RISE_SET 0x87 +#define OTHER_INT_EN_RISE_CLR 0x88 +#define OTHER_INT_EN_FALL 0x89 +#define OTHER_INT_EN_FALL_SET 0x8A +#define OTHER_INT_EN_FALL_CLR 0x8B +#define OTHER_INT_STS 0x8C +#define OTHER_INT_LATCH 0x8D +#define OTHER_INT_VB_SESS_VLD (1 << 7) +#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ +#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ +#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ +#define OTHER_INT_MANU (1 << 1) +#define OTHER_INT_ABNORMAL_STRESS (1 << 0) + +#define ID_STATUS 0x96 +#define ID_RES_FLOAT (1 << 4) +#define ID_RES_440K (1 << 3) +#define ID_RES_200K (1 << 2) +#define ID_RES_102K (1 << 1) +#define ID_RES_GND (1 << 0) + +#define POWER_CTRL 0xAC +#define POWER_CTRL_SET 0xAD +#define POWER_CTRL_CLR 0xAE +#define POWER_CTRL_OTG_ENAB (1 << 5) + +#define OTHER_IFC_CTRL2 0xAF +#define OTHER_IFC_CTRL2_SET 0xB0 +#define OTHER_IFC_CTRL2_CLR 0xB1 +#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) +#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) +#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) + +#define REG_CTRL_EN 0xB2 +#define REG_CTRL_EN_SET 0xB3 +#define REG_CTRL_EN_CLR 0xB4 +#define REG_CTRL_ERROR 0xB5 +#define ULPI_I2C_CONFLICT_INTEN (1 << 0) + +#define OTHER_FUNC_CTRL2 0xB8 +#define OTHER_FUNC_CTRL2_SET 0xB9 +#define OTHER_FUNC_CTRL2_CLR 0xBA +#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) + +/* following registers do not have separate _clr and _set registers */ +#define VBUS_DEBOUNCE 0xC0 +#define ID_DEBOUNCE 0xC1 +#define VBAT_TIMER 0xD3 +#define PHY_PWR_CTRL 0xFD +#define PHY_PWR_PHYPWD (1 << 0) +#define PHY_CLK_CTRL 0xFE +#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) +#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) +#define REQ_PHY_DPLL_CLK (1 << 0) +#define PHY_CLK_CTRL_STS 0xFF +#define PHY_DPLL_CLK (1 << 0) + +/* In module TWL4030_MODULE_PM_MASTER */ +#define PROTECT_KEY 0x0E + +/* In module TWL4030_MODULE_PM_RECEIVER */ +#define VUSB_DEDICATED1 0x7D +#define VUSB_DEDICATED2 0x7E +#define VUSB1V5_DEV_GRP 0x71 +#define VUSB1V5_TYPE 0x72 +#define VUSB1V5_REMAP 0x73 +#define VUSB1V8_DEV_GRP 0x74 +#define VUSB1V8_TYPE 0x75 +#define VUSB1V8_REMAP 0x76 +#define VUSB3V1_DEV_GRP 0x77 +#define VUSB3V1_TYPE 0x78 +#define VUSB3V1_REMAP 0x79 + +/* In module TWL4030_MODULE_INTBR */ +#define PMBR1 0x0D +#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) + + + +enum linkstat { + USB_LINK_UNKNOWN = 0, + USB_LINK_NONE, + USB_LINK_VBUS, + USB_LINK_ID, +}; + +struct twl4030_usb { + struct otg_transceiver otg; + struct device *dev; + + /* for vbus reporting with irqs disabled */ + spinlock_t lock; + + /* pin configuration */ + enum twl4030_usb_mode usb_mode; + + int irq; + u8 linkstat; + u8 asleep; + bool irq_enabled; +}; + +/* internal define on top of container_of */ +#define xceiv_to_twl(x) container_of((x), struct twl4030_usb, otg); + +/*-------------------------------------------------------------------------*/ + +static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, + u8 module, u8 data, u8 address) +{ + u8 check; + + if ((twl4030_i2c_write_u8(module, data, address) >= 0) && + (twl4030_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 1, module, address, check, data); + + /* Failed once: Try again */ + if ((twl4030_i2c_write_u8(module, data, address) >= 0) && + (twl4030_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 2, module, address, check, data); + + /* Failed again: Return error */ + return -EBUSY; +} + +#define twl4030_usb_write_verify(twl, address, data) \ + twl4030_i2c_write_u8_verify(twl, TWL4030_MODULE_USB, (data), (address)) + +static inline int twl4030_usb_write(struct twl4030_usb *twl, + u8 address, u8 data) +{ + int ret = 0; + + ret = twl4030_i2c_write_u8(TWL4030_MODULE_USB, data, address); + if (ret < 0) + dev_dbg(twl->dev, + "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) +{ + u8 data; + int ret = 0; + + ret = twl4030_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_dbg(twl->dev, + "TWL4030:readb[0x%x,0x%x] Error %d\n", + module, address, ret); + + return ret; +} + +static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) +{ + return twl4030_readb(twl, TWL4030_MODULE_USB, address); +} + +/*-------------------------------------------------------------------------*/ + +static inline int +twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, reg + 1, bits); +} + +static inline int +twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, reg + 2, bits); +} + +/*-------------------------------------------------------------------------*/ + +static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl) +{ + int status; + int linkstat = USB_LINK_UNKNOWN; + + /* STS_HW_CONDITIONS */ + status = twl4030_readb(twl, TWL4030_MODULE_PM_MASTER, 0x0f); + if (status < 0) + dev_err(twl->dev, "USB link status err %d\n", status); + else if (status & BIT(7)) + linkstat = USB_LINK_VBUS; + else if (status & BIT(2)) + linkstat = USB_LINK_ID; + else + linkstat = USB_LINK_NONE; + + dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", + status, status, linkstat); + + /* REVISIT this assumes host and peripheral controllers + * are registered, and that both are active... + */ + + spin_lock_irq(&twl->lock); + twl->linkstat = linkstat; + if (linkstat == USB_LINK_ID) { + twl->otg.default_a = true; + twl->otg.state = OTG_STATE_A_IDLE; + } else { + twl->otg.default_a = false; + twl->otg.state = OTG_STATE_B_IDLE; + } + spin_unlock_irq(&twl->lock); + + return linkstat; +} + +static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) +{ + twl->usb_mode = mode; + + switch (mode) { + case T2_USB_MODE_ULPI: + twl4030_usb_clear_bits(twl, IFC_CTRL, IFC_CTRL_CARKITMODE); + twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + twl4030_usb_clear_bits(twl, FUNC_CTRL, + FUNC_CTRL_XCVRSELECT_MASK | + FUNC_CTRL_OPMODE_MASK); + break; + case -1: + /* FIXME: power on defaults */ + break; + default: + dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", + mode); + break; + }; +} + +static void twl4030_i2c_access(struct twl4030_usb *twl, int on) +{ + unsigned long timeout; + int val = twl4030_usb_read(twl, PHY_CLK_CTRL); + + if (val >= 0) { + if (on) { + /* enable DPLL to access PHY registers over I2C */ + val |= REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + + timeout = jiffies + HZ; + while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK) + && time_before(jiffies, timeout)) + udelay(10); + if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK)) + dev_err(twl->dev, "Timeout setting T2 HSUSB " + "PHY DPLL clock\n"); + } else { + /* let ULPI control the DPLL clock */ + val &= ~REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + } + } +} + +static void twl4030_phy_power(struct twl4030_usb *twl, int on) +{ + u8 pwr; + + pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); + if (on) { + pwr &= ~PHY_PWR_PHYPWD; + WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); + twl4030_usb_write(twl, PHY_CLK_CTRL, + twl4030_usb_read(twl, PHY_CLK_CTRL) | + (PHY_CLK_CTRL_CLOCKGATING_EN | + PHY_CLK_CTRL_CLK32K_EN)); + } else { + pwr |= PHY_PWR_PHYPWD; + WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); + } +} + +static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off) +{ + if (twl->asleep) + return; + + twl4030_phy_power(twl, 0); + twl->asleep = 1; +} + +static void twl4030_phy_resume(struct twl4030_usb *twl) +{ + if (!twl->asleep) + return; + + twl4030_phy_power(twl, 1); + twl4030_i2c_access(twl, 1); + twl4030_usb_set_mode(twl, twl->usb_mode); + if (twl->usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); + twl->asleep = 0; +} + +static void twl4030_usb_ldo_init(struct twl4030_usb *twl) +{ + /* Enable writing to power configuration registers */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0xC0, PROTECT_KEY); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x0C, PROTECT_KEY); + + /* put VUSB3V1 LDO in active state */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); + + /* input to VUSB3V1 LDO is from VBAT, not VBUS */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); + + /* turn on 3.1V regulator */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB3V1_DEV_GRP); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); + + /* turn on 1.5V regulator */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V5_DEV_GRP); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); + + /* turn on 1.8V regulator */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0x20, VUSB1V8_DEV_GRP); + twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); + + /* disable access to power configuration registers */ + twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, PROTECT_KEY); +} + +static ssize_t twl4030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&twl->lock, flags); + ret = sprintf(buf, "%s\n", + (twl->linkstat == USB_LINK_VBUS) ? "on" : "off"); + spin_unlock_irqrestore(&twl->lock, flags); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); + +static irqreturn_t twl4030_usb_irq(int irq, void *_twl) +{ + struct twl4030_usb *twl = _twl; + int status; + +#ifdef CONFIG_LOCKDEP + /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which + * we don't want and can't tolerate. Although it might be + * friendlier not to borrow this thread context... + */ + local_irq_enable(); +#endif + + status = twl4030_usb_linkstat(twl); + if (status != USB_LINK_UNKNOWN) { + + /* FIXME add a set_power() method so that B-devices can + * configure the charger appropriately. It's not always + * correct to consume VBUS power, and how much current to + * consume is a function of the USB configuration chosen + * by the host. + * + * REVISIT usb_gadget_vbus_connect(...) as needed, ditto + * its disconnect() sibling, when changing to/from the + * USB_LINK_VBUS state. musb_hdrc won't care until it + * starts to handle softconnect right. + */ + twl4030charger_usb_en(status == USB_LINK_VBUS); + + if (status == USB_LINK_NONE) + twl4030_phy_suspend(twl, 0); + else + twl4030_phy_resume(twl); + } + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static int twl4030_set_suspend(struct otg_transceiver *x, int suspend) +{ + struct twl4030_usb *twl = xceiv_to_twl(x); + + if (suspend) + twl4030_phy_suspend(twl, 1); + else + twl4030_phy_resume(twl); + + return 0; +} + +static int twl4030_set_peripheral(struct otg_transceiver *x, + struct usb_gadget *gadget) +{ + struct twl4030_usb *twl; + + if (!x) + return -ENODEV; + + twl = xceiv_to_twl(x); + twl->otg.gadget = gadget; + if (!gadget) + twl->otg.state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl4030_set_host(struct otg_transceiver *x, struct usb_bus *host) +{ + struct twl4030_usb *twl; + + if (!x) + return -ENODEV; + + twl = xceiv_to_twl(x); + twl->otg.host = host; + if (!host) + twl->otg.state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int __init twl4030_usb_probe(struct platform_device *pdev) +{ + struct twl4030_usb_data *pdata = pdev->dev.platform_data; + struct twl4030_usb *twl; + int status; + + if (!pdata) { + dev_dbg(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + + twl = kzalloc(sizeof *twl, GFP_KERNEL); + if (!twl) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq = platform_get_irq(pdev, 0); + twl->otg.dev = twl->dev; + twl->otg.label = "twl4030"; + twl->otg.set_host = twl4030_set_host; + twl->otg.set_peripheral = twl4030_set_peripheral; + twl->otg.set_suspend = twl4030_set_suspend; + twl->usb_mode = pdata->usb_mode; + twl->asleep = 1; + + /* init spinlock for workqueue */ + spin_lock_init(&twl->lock); + + twl4030_usb_ldo_init(twl); + otg_set_transceiver(&twl->otg); + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + /* Our job is to use irqs and status from the power module + * to keep the transceiver disabled when nothing's connected. + * + * FIXME we actually shouldn't start enabling it until the + * USB controller drivers have said they're ready, by calling + * set_host() and/or set_peripheral() ... OTG_capable boards + * need both handles, otherwise just one suffices. + */ + twl->irq_enabled = true; + status = request_irq(twl->irq, twl4030_usb_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "twl4030_usb", twl); + if (status < 0) { + dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq, status); + kfree(twl); + return status; + } + + /* The IRQ handler just handles changes from the previous states + * of the ID and VBUS pins ... in probe() we must initialize that + * previous state. The easy way: fake an IRQ. + * + * REVISIT: a real IRQ might have happened already, if PREEMPT is + * enabled. Else the IRQ may not yet be configured or enabled, + * because of scheduling delays. + */ + twl4030_usb_irq(twl->irq, twl); + + dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); + return 0; +} + +static int __exit twl4030_usb_remove(struct platform_device *pdev) +{ + struct twl4030_usb *twl = platform_get_drvdata(pdev); + int val; + + free_irq(twl->irq, twl); + device_remove_file(twl->dev, &dev_attr_vbus); + + /* set transceiver mode to power on defaults */ + twl4030_usb_set_mode(twl, -1); + + /* autogate 60MHz ULPI clock, + * clear dpll clock request for i2c access, + * disable 32KHz + */ + val = twl4030_usb_read(twl, PHY_CLK_CTRL); + if (val >= 0) { + val |= PHY_CLK_CTRL_CLOCKGATING_EN; + val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); + twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); + } + + /* disable complete OTG block */ + twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + twl4030_phy_power(twl, 0); + + kfree(twl); + + return 0; +} + +static struct platform_driver twl4030_usb_driver = { + .probe = twl4030_usb_probe, + .remove = __exit_p(twl4030_usb_remove), + .driver = { + .name = "twl4030_usb", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_usb_init(void) +{ + return platform_driver_register(&twl4030_usb_driver); +} +subsys_initcall(twl4030_usb_init); + +static void __exit twl4030_usb_exit(void) +{ + platform_driver_unregister(&twl4030_usb_driver); +} +module_exit(twl4030_usb_exit); + +MODULE_ALIAS("platform:twl4030_usb"); +MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); +MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 70338f4ec918..b361f05cafac 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -496,6 +496,14 @@ config USB_SERIAL_SAFE_PADDED bool "USB Secure Encapsulated Driver - Padded" depends on USB_SERIAL_SAFE +config USB_SERIAL_SIEMENS_MPI + tristate "USB Siemens MPI driver" + help + Say M here if you want to use a Siemens USB/MPI adapter. + + To compile this driver as a module, choose M here: the + module will be called siemens_mpi. + config USB_SERIAL_SIERRAWIRELESS tristate "USB Sierra Wireless Driver" help @@ -565,6 +573,15 @@ config USB_SERIAL_OMNINET To compile this driver as a module, choose M here: the module will be called omninet. +config USB_SERIAL_OPTICON + tristate "USB Opticon Barcode driver (serial mode)" + help + Say Y here if you want to use a Opticon USB Barcode device + in serial emulation mode. + + To compile this driver as a module, choose M here: the + module will be called opticon. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 6047f818adfe..b75be91eb8f1 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -41,10 +41,12 @@ obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o obj-$(CONFIG_USB_SERIAL_MOTOROLA) += moto_modem.o obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o +obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o obj-$(CONFIG_USB_SERIAL_OPTION) += option.o obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o +obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 69f84f0ea6fe..38ba4ea8b6bf 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -635,8 +635,7 @@ static int digi_write_oob_command(struct usb_serial_port *port, spin_lock_irqsave(&oob_priv->dp_port_lock, flags); while (count > 0) { - while (oob_port->write_urb->status == -EINPROGRESS - || oob_priv->dp_write_urb_in_use) { + while (oob_priv->dp_write_urb_in_use) { cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, &oob_priv->dp_port_lock, flags); @@ -699,9 +698,8 @@ static int digi_write_inb_command(struct usb_serial_port *port, spin_lock_irqsave(&priv->dp_port_lock, flags); while (count > 0 && ret == 0) { - while ((port->write_urb->status == -EINPROGRESS - || priv->dp_write_urb_in_use) - && time_before(jiffies, timeout)) { + while (priv->dp_write_urb_in_use && + time_before(jiffies, timeout)) { cond_wait_interruptible_timeout_irqrestore( &port->write_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags); @@ -779,8 +777,7 @@ static int digi_set_modem_signals(struct usb_serial_port *port, spin_lock_irqsave(&oob_priv->dp_port_lock, flags); spin_lock(&port_priv->dp_port_lock); - while (oob_port->write_urb->status == -EINPROGRESS || - oob_priv->dp_write_urb_in_use) { + while (oob_priv->dp_write_urb_in_use) { spin_unlock(&port_priv->dp_port_lock); cond_wait_interruptible_timeout_irqrestore( &oob_port->write_wait, DIGI_RETRY_TIMEOUT, @@ -1168,12 +1165,10 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, /* be sure only one write proceeds at a time */ /* there are races on the port private buffer */ - /* and races to check write_urb->status */ spin_lock_irqsave(&priv->dp_port_lock, flags); /* wait for urb status clear to submit another urb */ - if (port->write_urb->status == -EINPROGRESS || - priv->dp_write_urb_in_use) { + if (priv->dp_write_urb_in_use) { /* buffer data if count is 1 (probably put_char) if possible */ if (count == 1 && priv->dp_out_buf_len < DIGI_OUT_BUF_SIZE) { priv->dp_out_buf[priv->dp_out_buf_len++] = *buf; @@ -1236,7 +1231,7 @@ static void digi_write_bulk_callback(struct urb *urb) int ret = 0; int status = urb->status; - dbg("digi_write_bulk_callback: TOP, urb->status=%d", status); + dbg("digi_write_bulk_callback: TOP, status=%d", status); /* port and serial sanity check */ if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { @@ -1266,8 +1261,7 @@ static void digi_write_bulk_callback(struct urb *urb) /* try to send any buffered data on this port, if it is open */ spin_lock(&priv->dp_port_lock); priv->dp_write_urb_in_use = 0; - if (port->port.count && port->write_urb->status != -EINPROGRESS - && priv->dp_out_buf_len > 0) { + if (port->port.count && priv->dp_out_buf_len > 0) { *((unsigned char *)(port->write_urb->transfer_buffer)) = (unsigned char)DIGI_CMD_SEND_DATA; *((unsigned char *)(port->write_urb->transfer_buffer) + 1) @@ -1305,8 +1299,7 @@ static int digi_write_room(struct tty_struct *tty) spin_lock_irqsave(&priv->dp_port_lock, flags); - if (port->write_urb->status == -EINPROGRESS || - priv->dp_write_urb_in_use) + if (priv->dp_write_urb_in_use) room = 0; else room = port->bulk_out_size - 2 - priv->dp_out_buf_len; @@ -1322,8 +1315,7 @@ static int digi_chars_in_buffer(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); - if (port->write_urb->status == -EINPROGRESS - || priv->dp_write_urb_in_use) { + if (priv->dp_write_urb_in_use) { dbg("digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2); /* return(port->bulk_out_size - 2); */ @@ -1702,7 +1694,7 @@ static int digi_read_inb_callback(struct urb *urb) /* short/multiple packet check */ if (urb->actual_length != len + 2) { dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, " - "urb->status=%d, port=%d, opcode=%d, len=%d, " + "status=%d, port=%d, opcode=%d, len=%d, " "actual_length=%d, status=%d\n", __func__, status, priv->dp_port_num, opcode, len, urb->actual_length, port_status); diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 8e6a66e38db2..a26a0e2cdb4a 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1056,7 +1056,7 @@ static void garmin_write_bulk_callback(struct urb *urb) if (status) { dbg("%s - nonzero write bulk status received: %d", - __func__, urb->status); + __func__, status); spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= CLEAR_HALT_REQUIRED; spin_unlock_irqrestore(&garmin_data_p->lock, flags); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 3ac59a8a980f..f530032ed93d 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -473,7 +473,7 @@ static struct usb_serial_driver ipw_device = { -static int usb_ipw_init(void) +static int __init usb_ipw_init(void) { int retval; @@ -490,7 +490,7 @@ static int usb_ipw_init(void) return 0; } -static void usb_ipw_exit(void) +static void __exit usb_ipw_exit(void) { usb_deregister(&usb_ipw_driver); usb_serial_deregister(&ipw_device); diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index e320972cb227..2314c6ae4fc2 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -190,10 +190,12 @@ static void iuu_rxcmd(struct urb *urb) { struct usb_serial_port *port = urb->context; int result; + int status = urb->status; + dbg("%s - enter", __func__); - if (urb->status) { - dbg("%s - urb->status = %d", __func__, urb->status); + if (status) { + dbg("%s - status = %d", __func__, status); /* error stop all */ return; } @@ -245,10 +247,12 @@ static void iuu_update_status_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct iuu_private *priv = usb_get_serial_port_data(port); u8 *st; + int status = urb->status; + dbg("%s - enter", __func__); - if (urb->status) { - dbg("%s - urb->status = %d", __func__, urb->status); + if (status) { + dbg("%s - status = %d", __func__, status); /* error stop all */ return; } @@ -274,9 +278,9 @@ static void iuu_status_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int result; - dbg("%s - enter", __func__); + int status = urb->status; - dbg("%s - urb->status = %d", __func__, urb->status); + dbg("%s - status = %d", __func__, status); usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -618,11 +622,12 @@ static void read_buf_callback(struct urb *urb) struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; struct tty_struct *tty; - dbg("%s - urb->status = %d", __func__, urb->status); + int status = urb->status; - if (urb->status) { - dbg("%s - urb->status = %d", __func__, urb->status); - if (urb->status == -EPROTO) { + dbg("%s - status = %d", __func__, status); + + if (status) { + if (status == -EPROTO) { /* reschedule needed */ } return; @@ -695,7 +700,7 @@ static void iuu_uart_read_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct iuu_private *priv = usb_get_serial_port_data(port); unsigned long flags; - int status; + int status = urb->status; int error = 0; int len = 0; unsigned char *data = urb->transfer_buffer; @@ -703,8 +708,8 @@ static void iuu_uart_read_callback(struct urb *urb) dbg("%s - enter", __func__); - if (urb->status) { - dbg("%s - urb->status = %d", __func__, urb->status); + if (status) { + dbg("%s - status = %d", __func__, status); /* error stop all */ return; } @@ -782,12 +787,11 @@ static void read_rxcmd_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int result; - dbg("%s - enter", __func__); + int status = urb->status; - dbg("%s - urb->status = %d", __func__, urb->status); + dbg("%s - status = %d", __func__, status); - if (urb->status) { - dbg("%s - urb->status = %d", __func__, urb->status); + if (status) { /* error stop all */ return; } diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 96a8c7713212..2c20e88a91b3 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -214,6 +214,7 @@ struct moschip_port { spinlock_t pool_lock; struct urb *write_urb_pool[NUM_URBS]; char busy[NUM_URBS]; + bool read_urb_busy; }; @@ -679,26 +680,30 @@ static void mos7840_bulk_in_callback(struct urb *urb) struct tty_struct *tty; int status = urb->status; - if (status) { - dbg("nonzero read bulk status received: %d", status); - return; - } - mos7840_port = urb->context; if (!mos7840_port) { dbg("%s", "NULL mos7840_port pointer \n"); + mos7840_port->read_urb_busy = false; + return; + } + + if (status) { + dbg("nonzero read bulk status received: %d", status); + mos7840_port->read_urb_busy = false; return; } port = (struct usb_serial_port *)mos7840_port->port; if (mos7840_port_paranoia_check(port, __func__)) { dbg("%s", "Port Paranoia failed \n"); + mos7840_port->read_urb_busy = false; return; } serial = mos7840_get_usb_serial(port, __func__); if (!serial) { dbg("%s\n", "Bad serial pointer "); + mos7840_port->read_urb_busy = false; return; } @@ -725,17 +730,19 @@ static void mos7840_bulk_in_callback(struct urb *urb) if (!mos7840_port->read_urb) { dbg("%s", "URB KILLED !!!\n"); + mos7840_port->read_urb_busy = false; return; } mos7840_port->read_urb->dev = serial->dev; + mos7840_port->read_urb_busy = true; retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); if (retval) { - dbg(" usb_submit_urb(read bulk) failed, retval = %d", - retval); + dbg("usb_submit_urb(read bulk) failed, retval = %d", retval); + mos7840_port->read_urb_busy = false; } } @@ -1055,10 +1062,12 @@ static int mos7840_open(struct tty_struct *tty, dbg("mos7840_open: bulkin endpoint is %d\n", port->bulk_in_endpointAddress); + mos7840_port->read_urb_busy = true; response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL); if (response) { dev_err(&port->dev, "%s - Error %d submitting control urb\n", __func__, response); + mos7840_port->read_urb_busy = false; } /* initialize our wait queues */ @@ -1227,6 +1236,7 @@ static void mos7840_close(struct tty_struct *tty, if (mos7840_port->read_urb) { dbg("%s", "Shutdown bulk read\n"); usb_kill_urb(mos7840_port->read_urb); + mos7840_port->read_urb_busy = false; } if ((&mos7840_port->control_urb)) { dbg("%s", "Shutdown control read\n"); @@ -2043,14 +2053,14 @@ static void mos7840_change_port_settings(struct tty_struct *tty, Data = 0x0c; mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); - if (mos7840_port->read_urb->status != -EINPROGRESS) { + if (mos7840_port->read_urb_busy == false) { mos7840_port->read_urb->dev = serial->dev; - + mos7840_port->read_urb_busy = true; status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); - if (status) { - dbg(" usb_submit_urb(read bulk) failed, status = %d", + dbg("usb_submit_urb(read bulk) failed, status = %d", status); + mos7840_port->read_urb_busy = false; } } wake_up(&mos7840_port->delta_msr_wait); @@ -2117,12 +2127,14 @@ static void mos7840_set_termios(struct tty_struct *tty, return; } - if (mos7840_port->read_urb->status != -EINPROGRESS) { + if (mos7840_port->read_urb_busy == false) { mos7840_port->read_urb->dev = serial->dev; + mos7840_port->read_urb_busy = true; status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); if (status) { - dbg(" usb_submit_urb(read bulk) failed, status = %d", + dbg("usb_submit_urb(read bulk) failed, status = %d", status); + mos7840_port->read_urb_busy = false; } } return; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c new file mode 100644 index 000000000000..cea326f1f105 --- /dev/null +++ b/drivers/usb/serial/opticon.c @@ -0,0 +1,358 @@ +/* + * Opticon USB barcode to serial driver + * + * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> + * Copyright (C) 2008 Novell Inc. + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <linux/uaccess.h> + +static int debug; + +static struct usb_device_id id_table[] = { + { USB_DEVICE(0x065a, 0x0009) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* This structure holds all of the individual device information */ +struct opticon_private { + struct usb_device *udev; + struct usb_serial *serial; + struct usb_serial_port *port; + unsigned char *bulk_in_buffer; + struct urb *bulk_read_urb; + int buffer_size; + u8 bulk_address; + spinlock_t lock; /* protects the following flags */ + bool throttled; + bool actually_throttled; + bool rts; +}; + +static void opticon_bulk_callback(struct urb *urb) +{ + struct opticon_private *priv = urb->context; + unsigned char *data = urb->transfer_buffer; + struct usb_serial_port *port = priv->port; + int status = urb->status; + struct tty_struct *tty; + int result; + int available_room = 0; + int data_length; + + dbg("%s - port %d", __func__, port->number); + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, status); + goto exit; + } + + usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, + data); + + if (urb->actual_length > 2) { + data_length = urb->actual_length - 2; + + /* + * Data from the device comes with a 2 byte header: + * + * <0x00><0x00>data... + * This is real data to be sent to the tty layer + * <0x00><0x01)level + * This is a RTS level change, the third byte is the RTS + * value (0 for low, 1 for high). + */ + if ((data[0] == 0x00) && (data[1] == 0x00)) { + /* real data, send it to the tty layer */ + tty = tty_port_tty_get(&port->port); + if (tty) { + available_room = tty_buffer_request_room(tty, + data_length); + if (available_room) { + tty_insert_flip_string(tty, data, + available_room); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); + } + } else { + if ((data[0] == 0x00) && (data[1] == 0x01)) { + if (data[2] == 0x00) + priv->rts = false; + else + priv->rts = true; + /* FIXME change the RTS level */ + } else { + dev_dbg(&priv->udev->dev, + "Unknown data packet received from the device:" + " %2x %2x\n", + data[0], data[1]); + } + } + } else { + dev_dbg(&priv->udev->dev, + "Improper ammount of data received from the device, " + "%d bytes", urb->actual_length); + } + +exit: + spin_lock(&priv->lock); + + /* Continue trying to always read if we should */ + if (!priv->throttled) { + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + priv->bulk_address), + priv->bulk_in_buffer, priv->buffer_size, + opticon_bulk_callback, priv); + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + } else + priv->actually_throttled = true; + spin_unlock(&priv->lock); +} + +static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + int result = 0; + + dbg("%s - port %d", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = false; + priv->actually_throttled = false; + priv->port = port; + spin_unlock_irqrestore(&priv->lock, flags); + + /* + * Force low_latency on so that our tty_push actually forces the data + * through, otherwise it is scheduled, and with high data rates (like + * with OHCI) data can get lost. + */ + if (tty) + tty->low_latency = 1; + + /* Start reading from the device */ + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + priv->bulk_address), + priv->bulk_in_buffer, priv->buffer_size, + opticon_bulk_callback, priv); + result = usb_submit_urb(priv->bulk_read_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + return result; +} + +static void opticon_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp) +{ + struct opticon_private *priv = usb_get_serial_data(port->serial); + + dbg("%s - port %d", __func__, port->number); + + /* shutdown our urbs */ + usb_kill_urb(priv->bulk_read_urb); +} + +static void opticon_throttle(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + + dbg("%s - port %d", __func__, port->number); + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = true; + spin_unlock_irqrestore(&priv->lock, flags); +} + + +static void opticon_unthrottle(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + struct opticon_private *priv = usb_get_serial_data(port->serial); + unsigned long flags; + int result; + + dbg("%s - port %d", __func__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + priv->throttled = false; + priv->actually_throttled = false; + spin_unlock_irqrestore(&priv->lock, flags); + + priv->bulk_read_urb->dev = port->serial->dev; + result = usb_submit_urb(priv->bulk_read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); +} + +static int opticon_startup(struct usb_serial *serial) +{ + struct opticon_private *priv; + struct usb_host_interface *intf; + int i; + int retval = -ENOMEM; + bool bulk_in_found = false; + + /* create our private serial structure */ + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__); + return -ENOMEM; + } + spin_lock_init(&priv->lock); + priv->serial = serial; + priv->port = serial->port[0]; + priv->udev = serial->dev; + + /* find our bulk endpoint */ + intf = serial->interface->altsetting; + for (i = 0; i < intf->desc.bNumEndpoints; ++i) { + struct usb_endpoint_descriptor *endpoint; + + endpoint = &intf->endpoint[i].desc; + if (!usb_endpoint_is_bulk_in(endpoint)) + continue; + + priv->bulk_read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->bulk_read_urb) { + dev_err(&priv->udev->dev, "out of memory\n"); + goto error; + } + + priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); + if (!priv->bulk_in_buffer) { + dev_err(&priv->udev->dev, "out of memory\n"); + goto error; + } + + priv->bulk_address = endpoint->bEndpointAddress; + + /* set up our bulk urb */ + usb_fill_bulk_urb(priv->bulk_read_urb, priv->udev, + usb_rcvbulkpipe(priv->udev, + endpoint->bEndpointAddress), + priv->bulk_in_buffer, priv->buffer_size, + opticon_bulk_callback, priv); + + bulk_in_found = true; + break; + } + + if (!bulk_in_found) { + dev_err(&priv->udev->dev, + "Error - the proper endpoints were not found!\n"); + goto error; + } + + usb_set_serial_data(serial, priv); + return 0; + +error: + usb_free_urb(priv->bulk_read_urb); + kfree(priv->bulk_in_buffer); + kfree(priv); + return retval; +} + +static void opticon_shutdown(struct usb_serial *serial) +{ + struct opticon_private *priv = usb_get_serial_data(serial); + + dbg("%s", __func__); + + usb_kill_urb(priv->bulk_read_urb); + usb_free_urb(priv->bulk_read_urb); + kfree(priv->bulk_in_buffer); + kfree(priv); + usb_set_serial_data(serial, NULL); +} + + +static struct usb_driver opticon_driver = { + .name = "opticon", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static struct usb_serial_driver opticon_device = { + .driver = { + .owner = THIS_MODULE, + .name = "opticon", + }, + .id_table = id_table, + .usb_driver = &opticon_driver, + .num_ports = 1, + .attach = opticon_startup, + .open = opticon_open, + .close = opticon_close, + .shutdown = opticon_shutdown, + .throttle = opticon_throttle, + .unthrottle = opticon_unthrottle, +}; + +static int __init opticon_init(void) +{ + int retval; + + retval = usb_serial_register(&opticon_device); + if (retval) + return retval; + retval = usb_register(&opticon_driver); + if (retval) + usb_serial_deregister(&opticon_device); + return retval; +} + +static void __exit opticon_exit(void) +{ + usb_deregister(&opticon_driver); + usb_serial_deregister(&opticon_device); +} + +module_init(opticon_init); +module_exit(opticon_exit); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 809697b3c7fc..5ed183477aaf 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -522,9 +522,9 @@ static int debug; /* per port private data */ #define N_IN_URB 4 -#define N_OUT_URB 1 +#define N_OUT_URB 4 #define IN_BUFLEN 4096 -#define OUT_BUFLEN 128 +#define OUT_BUFLEN 4096 struct option_port_private { /* Input endpoints and buffer for this port */ @@ -654,10 +654,6 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, usb_unlink_urb(this_urb); continue; } - if (this_urb->status != 0) - dbg("usb_write %p failed (err=%d)", - this_urb, this_urb->status); - dbg("%s: endpoint %d buf %d", __func__, usb_pipeendpoint(this_urb->pipe), i); @@ -669,8 +665,7 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { dbg("usb_submit_urb %p (write bulk) failed " - "(%d, has %d)", this_urb, - err, this_urb->status); + "(%d)", this_urb, err); clear_bit(i, &portdata->out_busy); continue; } diff --git a/drivers/usb/serial/siemens_mpi.c b/drivers/usb/serial/siemens_mpi.c new file mode 100644 index 000000000000..951ea0c6ba77 --- /dev/null +++ b/drivers/usb/serial/siemens_mpi.c @@ -0,0 +1,77 @@ +/* + * Siemens USB-MPI Serial USB driver + * + * Copyright (C) 2005 Thomas Hergenhahn <thomas.hergenhahn@suse.de> + * Copyright (C) 2005,2008 Greg Kroah-Hartman <gregkh@suse.de> + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +/* Version Information */ +#define DRIVER_VERSION "Version 0.1 09/26/2005" +#define DRIVER_AUTHOR "Thomas Hergenhahn@web.de http://libnodave.sf.net" +#define DRIVER_DESC "Driver for Siemens USB/MPI adapter" + + +static struct usb_device_id id_table[] = { + /* Vendor and product id for 6ES7-972-0CB20-0XA0 */ + { USB_DEVICE(0x908, 0x0004) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver siemens_usb_mpi_driver = { + .name = "siemens_mpi", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, +}; + +static struct usb_serial_driver siemens_usb_mpi_device = { + .driver = { + .owner = THIS_MODULE, + .name = "siemens_mpi", + }, + .id_table = id_table, + .num_ports = 1, +}; + +static int __init siemens_usb_mpi_init(void) +{ + int retval; + + retval = usb_serial_register(&siemens_usb_mpi_device); + if (retval) + goto failed_usb_serial_register; + retval = usb_register(&siemens_usb_mpi_driver); + if (retval) + goto failed_usb_register; + printk(KERN_INFO DRIVER_DESC "\n"); + printk(KERN_INFO DRIVER_VERSION " " DRIVER_AUTHOR "\n"); + return retval; +failed_usb_register: + usb_serial_deregister(&siemens_usb_mpi_device); +failed_usb_serial_register: + return retval; +} + +static void __exit siemens_usb_mpi_exit(void) +{ + usb_deregister(&siemens_usb_mpi_driver); + usb_serial_deregister(&siemens_usb_mpi_device); +} + +module_init(siemens_usb_mpi_init); +module_exit(siemens_usb_mpi_exit); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index a65bc2bd8e71..5e7528cc81a8 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -709,21 +709,20 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; unsigned long flags; int i; - int result; - u8 status = 0; + int result = urb->status; + u8 status; char tty_flag; - dev_dbg(&port->dev, "start, urb->status = %d, " - "urb->actual_length = %d\n,", urb->status, urb->actual_length); + dev_dbg(&port->dev, "start, result = %d, urb->actual_length = %d\n,", + result, urb->actual_length); /* check the urb status */ - if (urb->status) { + if (result) { if (!port->port.count) return; - if (urb->status == -EPROTO) { + if (result == -EPROTO) { /* spcp8x5 mysteriously fails with -EPROTO */ /* reschedule the read */ - urb->status = 0; urb->dev = port->serial->dev; result = usb_submit_urb(urb , GFP_ATOMIC); if (result) @@ -833,8 +832,9 @@ static void spcp8x5_write_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct spcp8x5_private *priv = usb_get_serial_port_data(port); int result; + int status = urb->status; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -843,14 +843,14 @@ static void spcp8x5_write_bulk_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(&port->dev, "urb shutting down with status: %d\n", - urb->status); + status); priv->write_urb_in_use = 0; return; default: /* error in the urb, so we have to resubmit it */ dbg("%s - Overflow in write", __func__); dbg("%s - nonzero write bulk status received: %d", - __func__, urb->status); + __func__, status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index fc5d9952b03b..6c9cbb59552a 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -31,7 +31,7 @@ static struct usb_driver debug_driver = { .no_dynamic_id = 1, }; -int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, +static int usb_debug_open(struct tty_struct *tty, struct usb_serial_port *port, struct file *filp) { port->bulk_out_size = USB_DEBUG_MAX_PACKET_SIZE; diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index c68b738900bd..9df6887b91f6 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -61,13 +61,6 @@ config USB_STORAGE_ISD200 - CyQ've CQ8060A CDRW drive - Planex eXtreme Drive RX-25HU USB-IDE cable (not model RX-25U) -config USB_STORAGE_DPCM - bool "Microtech/ZiO! CompactFlash/SmartMedia support" - depends on USB_STORAGE - help - Say Y here to support the Microtech/ZiO! CompactFlash reader. - There is a web page at <http://www.ziocorp.com/products/>. - config USB_STORAGE_USBAT bool "USBAT/USBAT02-based storage support" depends on USB_STORAGE @@ -90,12 +83,12 @@ config USB_STORAGE_USBAT - Sandisk ImageMate SDDR-05b config USB_STORAGE_SDDR09 - bool "SanDisk SDDR-09 (and other SmartMedia) support" + bool "SanDisk SDDR-09 (and other SmartMedia, including DPCM) support" depends on USB_STORAGE help Say Y here to include additional code to support the Sandisk SDDR-09 SmartMedia reader in the USB Mass Storage driver. - Also works for the Microtech Zio! SmartMedia reader. + Also works for the Microtech Zio! CompactFlash/SmartMedia reader. config USB_STORAGE_SDDR55 bool "SanDisk SDDR-55 SmartMedia support" diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 7f8beb5366ae..b32069313390 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -14,7 +14,6 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_USBAT) += shuttle_usbat.o usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o -usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o @@ -24,7 +23,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o usb-storage-obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += cypress_atacb.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ - initializers.o sierra_ms.o $(usb-storage-obj-y) + initializers.o sierra_ms.o option_ms.o $(usb-storage-obj-y) ifneq ($(CONFIG_USB_LIBUSUAL),) obj-$(CONFIG_USB) += libusual.o diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c deleted file mode 100644 index 939923471af4..000000000000 --- a/drivers/usb/storage/dpcm.c +++ /dev/null @@ -1,86 +0,0 @@ -/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * DPCM driver v0.1: - * - * First release - * - * Current development and maintenance by: - * (c) 2000 Brian Webb (webbb@earthlink.net) - * - * This device contains both a CompactFlash card reader, which - * uses the Control/Bulk w/o Interrupt protocol and - * a SmartMedia card reader that uses the same protocol - * as the SDDR09. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <scsi/scsi.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> - -#include "usb.h" -#include "transport.h" -#include "protocol.h" -#include "debug.h" -#include "dpcm.h" -#include "sddr09.h" - -/* - * Transport for the Microtech DPCM-USB - * - */ -int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) -{ - int ret; - - if (srb == NULL) - return USB_STOR_TRANSPORT_ERROR; - - US_DEBUGP("dpcm_transport: LUN=%d\n", srb->device->lun); - - switch (srb->device->lun) { - case 0: - - /* - * LUN 0 corresponds to the CompactFlash card reader. - */ - ret = usb_stor_CB_transport(srb, us); - break; - -#ifdef CONFIG_USB_STORAGE_SDDR09 - case 1: - - /* - * LUN 1 corresponds to the SmartMedia card reader. - */ - - /* - * Set the LUN to 0 (just in case). - */ - srb->device->lun = 0; us->srb->device->lun = 0; - ret = sddr09_transport(srb, us); - srb->device->lun = 1; us->srb->device->lun = 1; - break; - -#endif - - default: - US_DEBUGP("dpcm_transport: Invalid LUN %d\n", srb->device->lun); - ret = USB_STOR_TRANSPORT_ERROR; - break; - } - return ret; -} diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h deleted file mode 100644 index e7b7b0f120d7..000000000000 --- a/drivers/usb/storage/dpcm.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * DPCM driver v0.1: - * - * First release - * - * Current development and maintenance by: - * (c) 2000 Brian Webb (webbb@earthlink.net) - * - * See dpcm.c for more explanation - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _MICROTECH_DPCM_USB_H -#define _MICROTECH_DPCM_USB_H - -extern int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us); - -#endif diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c index d617e8ae6b00..f970b27ba308 100644 --- a/drivers/usb/storage/libusual.c +++ b/drivers/usb/storage/libusual.c @@ -46,6 +46,12 @@ static int usu_probe_thread(void *arg); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \ .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } +#define COMPLIANT_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ + .driver_info = (flags) } + #define USUAL_DEV(useProto, useTrans, useType) \ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ .driver_info = ((useType)<<24) } @@ -57,6 +63,7 @@ struct usb_device_id storage_usb_ids [] = { #undef USUAL_DEV #undef UNUSUAL_DEV +#undef COMPLIANT_DEV MODULE_DEVICE_TABLE(usb, storage_usb_ids); EXPORT_SYMBOL_GPL(storage_usb_ids); diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c new file mode 100644 index 000000000000..353f922939a4 --- /dev/null +++ b/drivers/usb/storage/option_ms.c @@ -0,0 +1,147 @@ +/* + * Driver for Option High Speed Mobile Devices. + * + * (c) 2008 Dan Williams <dcbw@redhat.com> + * + * Inspiration taken from sierra_ms.c by Kevin Lloyd <klloyd@sierrawireless.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/usb.h> + +#include "usb.h" +#include "transport.h" +#include "option_ms.h" +#include "debug.h" + +#define ZCD_FORCE_MODEM 0x01 +#define ZCD_ALLOW_MS 0x02 + +static unsigned int option_zero_cd = ZCD_FORCE_MODEM; +module_param(option_zero_cd, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default)," + " 2=Allow CD-Rom"); + +#define RESPONSE_LEN 1024 + +static int option_rezero(struct us_data *us, int ep_in, int ep_out) +{ + const unsigned char rezero_msg[] = { + 0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12, + 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + char *buffer; + int result; + + US_DEBUGP("Option MS: %s", "DEVICE MODE SWITCH\n"); + + buffer = kzalloc(RESPONSE_LEN, GFP_KERNEL); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + memcpy(buffer, rezero_msg, sizeof (rezero_msg)); + result = usb_stor_bulk_transfer_buf(us, + usb_sndbulkpipe(us->pusb_dev, ep_out), + buffer, sizeof (rezero_msg), NULL); + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_XFER_ERROR; + goto out; + } + + /* Some of the devices need to be asked for a response, but we don't + * care what that response is. + */ + result = usb_stor_bulk_transfer_buf(us, + usb_sndbulkpipe(us->pusb_dev, ep_out), + buffer, RESPONSE_LEN, NULL); + result = USB_STOR_XFER_GOOD; + +out: + kfree(buffer); + return result; +} + +int option_ms_init(struct us_data *us) +{ + struct usb_device *udev; + struct usb_interface *intf; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint = NULL; + u8 ep_in = 0, ep_out = 0; + int ep_in_size = 0, ep_out_size = 0; + int i, result; + + udev = us->pusb_dev; + intf = us->pusb_intf; + + /* Ensure it's really a ZeroCD device; devices that are already + * in modem mode return 0xFF for class, subclass, and protocol. + */ + if (udev->descriptor.bDeviceClass != 0 || + udev->descriptor.bDeviceSubClass != 0 || + udev->descriptor.bDeviceProtocol != 0) + return USB_STOR_TRANSPORT_GOOD; + + US_DEBUGP("Option MS: option_ms_init called\n"); + + /* Find the right mass storage interface */ + iface_desc = intf->cur_altsetting; + if (iface_desc->desc.bInterfaceClass != 0x8 || + iface_desc->desc.bInterfaceSubClass != 0x6 || + iface_desc->desc.bInterfaceProtocol != 0x50) { + US_DEBUGP("Option MS: mass storage interface not found, no action " + "required\n"); + return USB_STOR_TRANSPORT_GOOD; + } + + /* Find the mass storage bulk endpoints */ + for (i = 0; i < iface_desc->desc.bNumEndpoints && (!ep_in_size || !ep_out_size); ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(endpoint)) { + ep_in = usb_endpoint_num(endpoint); + ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); + } else if (usb_endpoint_is_bulk_out(endpoint)) { + ep_out = usb_endpoint_num(endpoint); + ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); + } + } + + /* Can't find the mass storage endpoints */ + if (!ep_in_size || !ep_out_size) { + US_DEBUGP("Option MS: mass storage endpoints not found, no action " + "required\n"); + return USB_STOR_TRANSPORT_GOOD; + } + + /* Force Modem mode */ + if (option_zero_cd == ZCD_FORCE_MODEM) { + US_DEBUGP("Option MS: %s", "Forcing Modem Mode\n"); + result = option_rezero(us, ep_in, ep_out); + if (result != USB_STOR_XFER_GOOD) + US_DEBUGP("Option MS: Failed to switch to modem mode.\n"); + return -EIO; + } else if (option_zero_cd == ZCD_ALLOW_MS) { + /* Allow Mass Storage mode (keep CD-Rom) */ + US_DEBUGP("Option MS: %s", "Allowing Mass Storage Mode if device" + " requests it\n"); + } + + return USB_STOR_TRANSPORT_GOOD; +} + diff --git a/drivers/usb/storage/option_ms.h b/drivers/usb/storage/option_ms.h new file mode 100644 index 000000000000..b6e448cab039 --- /dev/null +++ b/drivers/usb/storage/option_ms.h @@ -0,0 +1,4 @@ +#ifndef _OPTION_MS_H_ +#define _OPTION_MS_H_ +extern int option_ms_init(struct us_data *us); +#endif diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 3b3357e20ea7..be441d84bc64 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -56,9 +56,9 @@ * Protocol routines ***********************************************************************/ -void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) +void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us) { - /* Pad the ATAPI command with zeros + /* Pad the SCSI command with zeros out to 12 bytes * * NOTE: This only works because a scsi_cmnd struct field contains * a unsigned char cmnd[16], so we know we have storage available @@ -73,26 +73,6 @@ void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us) usb_stor_invoke_transport(srb, us); } -void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us) -{ - /* Pad the ATAPI command with zeros - * - * NOTE: This only works because a scsi_cmnd struct field contains - * a unsigned char cmnd[16], so we know we have storage available - */ - - /* Pad the ATAPI command with zeros */ - for (; srb->cmd_len<12; srb->cmd_len++) - srb->cmnd[srb->cmd_len] = 0; - - /* set command length to 12 bytes */ - srb->cmd_len = 12; - - /* send the command to the transport layer */ - usb_stor_invoke_transport(srb, us); -} - - void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us) { /* fix some commands -- this is a form of mode translation diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 487056ffb516..ffc3e2af0156 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -40,8 +40,7 @@ #define _PROTOCOL_H_ /* Protocol handling routines */ -extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*); -extern void usb_stor_qic157_command(struct scsi_cmnd*, struct us_data*); +extern void usb_stor_pad12_command(struct scsi_cmnd*, struct us_data*); extern void usb_stor_ufi_command(struct scsi_cmnd*, struct us_data*); extern void usb_stor_transparent_scsi_command(struct scsi_cmnd*, struct us_data*); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 09779f6a8179..2a42b862aa9f 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -59,6 +59,13 @@ #include "transport.h" #include "protocol.h" +/* Vendor IDs for companies that seem to include the READ CAPACITY bug + * in all their devices + */ +#define VENDOR_ID_NOKIA 0x0421 +#define VENDOR_ID_NIKON 0x04b0 +#define VENDOR_ID_MOTOROLA 0x22b8 + /*********************************************************************** * Host functions ***********************************************************************/ @@ -129,11 +136,35 @@ static int slave_configure(struct scsi_device *sdev) max_sectors); } + /* Some USB host controllers can't do DMA; they have to use PIO. + * They indicate this by setting their dma_mask to NULL. For + * such controllers we need to make sure the block layer sets + * up bounce buffers in addressable memory. + */ + if (!us->pusb_dev->bus->controller->dma_mask) + blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_HIGH); + /* We can't put these settings in slave_alloc() because that gets * called before the device type is known. Consequently these * settings can't be overridden via the scsi devinfo mechanism. */ if (sdev->type == TYPE_DISK) { + /* Some vendors seem to put the READ CAPACITY bug into + * all their devices -- primarily makers of cell phones + * and digital cameras. Since these devices always use + * flash media and can be expected to have an even number + * of sectors, we will always enable the CAPACITY_HEURISTICS + * flag unless told otherwise. */ + switch (le16_to_cpu(us->pusb_dev->descriptor.idVendor)) { + case VENDOR_ID_NOKIA: + case VENDOR_ID_NIKON: + case VENDOR_ID_MOTOROLA: + if (!(us->fflags & (US_FL_FIX_CAPACITY | + US_FL_CAPACITY_OK))) + us->fflags |= US_FL_CAPACITY_HEURISTICS; + break; + } + /* Disk-type devices use MODE SENSE(6) if the protocol * (SubClass) is Transparent SCSI, otherwise they use * MODE SENSE(10). */ @@ -170,6 +201,10 @@ static int slave_configure(struct scsi_device *sdev) if (us->fflags & US_FL_CAPACITY_HEURISTICS) sdev->guess_capacity = 1; + /* assume SPC3 or latter devices support sense size > 18 */ + if (sdev->scsi_level > SCSI_SPC_2) + us->fflags |= US_FL_SANE_SENSE; + /* Some devices report a SCSI revision level above 2 but are * unable to handle the REPORT LUNS command (for which * support is mandatory at level 3). Since we already have @@ -196,6 +231,14 @@ static int slave_configure(struct scsi_device *sdev) * sector in a larger then 1 sector read, since the performance * impact is negible we set this flag for all USB disks */ sdev->last_sector_bug = 1; + + /* Enable last-sector hacks for single-target devices using + * the Bulk-only transport, unless we already know the + * capacity will be decremented or is correct. */ + if (!(us->fflags & (US_FL_FIX_CAPACITY | US_FL_CAPACITY_OK | + US_FL_SCM_MULT_TARG)) && + us->protocol == US_PR_BULK) + us->use_last_sector_hacks = 1; } else { /* Non-disk-type devices don't need to blacklist any pages diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index c5a54b872c24..531ae5c5abf3 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -45,6 +45,7 @@ #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> #include "usb.h" #include "transport.h" @@ -1446,6 +1447,48 @@ usb_stor_sddr09_dpcm_init(struct us_data *us) { } /* + * Transport for the Microtech DPCM-USB + */ +int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int ret; + + US_DEBUGP("dpcm_transport: LUN=%d\n", srb->device->lun); + + switch (srb->device->lun) { + case 0: + + /* + * LUN 0 corresponds to the CompactFlash card reader. + */ + ret = usb_stor_CB_transport(srb, us); + break; + + case 1: + + /* + * LUN 1 corresponds to the SmartMedia card reader. + */ + + /* + * Set the LUN to 0 (just in case). + */ + srb->device->lun = 0; + ret = sddr09_transport(srb, us); + srb->device->lun = 1; + break; + + default: + US_DEBUGP("dpcm_transport: Invalid LUN %d\n", + srb->device->lun); + ret = USB_STOR_TRANSPORT_ERROR; + break; + } + return ret; +} + + +/* * Transport for the Sandisk SDDR-09 */ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index e50033ad7b19..b701172e12e3 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -28,8 +28,11 @@ /* Sandisk SDDR-09 stuff */ extern int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us); +extern int usb_stor_sddr09_init(struct us_data *us); + +/* Microtech DPCM-USB stuff */ +extern int dpcm_transport(struct scsi_cmnd *srb, struct us_data *us); extern int usb_stor_sddr09_dpcm_init(struct us_data *us); -extern int usb_stor_sddr09_init(struct us_data *us); #endif diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 79108d5d3171..1d5438e6363b 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -57,6 +57,9 @@ #include "scsiglue.h" #include "debug.h" +#include <linux/blkdev.h> +#include "../../scsi/sd.h" + /*********************************************************************** * Data transfer routines @@ -511,6 +514,110 @@ int usb_stor_bulk_transfer_sg(struct us_data* us, unsigned int pipe, * Transport routines ***********************************************************************/ +/* There are so many devices that report the capacity incorrectly, + * this routine was written to counteract some of the resulting + * problems. + */ +static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) +{ + struct gendisk *disk; + struct scsi_disk *sdkp; + u32 sector; + + /* To Report "Medium Error: Record Not Found */ + static unsigned char record_not_found[18] = { + [0] = 0x70, /* current error */ + [2] = MEDIUM_ERROR, /* = 0x03 */ + [7] = 0x0a, /* additional length */ + [12] = 0x14 /* Record Not Found */ + }; + + /* If last-sector problems can't occur, whether because the + * capacity was already decremented or because the device is + * known to report the correct capacity, then we don't need + * to do anything. + */ + if (!us->use_last_sector_hacks) + return; + + /* Was this command a READ(10) or a WRITE(10)? */ + if (srb->cmnd[0] != READ_10 && srb->cmnd[0] != WRITE_10) + goto done; + + /* Did this command access the last sector? */ + sector = (srb->cmnd[2] << 24) | (srb->cmnd[3] << 16) | + (srb->cmnd[4] << 8) | (srb->cmnd[5]); + disk = srb->request->rq_disk; + if (!disk) + goto done; + sdkp = scsi_disk(disk); + if (!sdkp) + goto done; + if (sector + 1 != sdkp->capacity) + goto done; + + if (srb->result == SAM_STAT_GOOD && scsi_get_resid(srb) == 0) { + + /* The command succeeded. If the capacity is odd + * (i.e., if the sector number is even) then the + * "always-even" heuristic would be wrong for this + * device. Issue a WARN() so that the kerneloops.org + * project will be notified and we will then know to + * mark the device with a CAPACITY_OK flag. Hopefully + * this will occur for only a few devices. + * + * Use the sign of us->last_sector_hacks to tell whether + * the warning has already been issued; we don't need + * more than one warning per device. + */ + if (!(sector & 1) && us->use_last_sector_hacks > 0) { + unsigned vid = le16_to_cpu( + us->pusb_dev->descriptor.idVendor); + unsigned pid = le16_to_cpu( + us->pusb_dev->descriptor.idProduct); + unsigned rev = le16_to_cpu( + us->pusb_dev->descriptor.bcdDevice); + + WARN(1, "%s: Successful last sector success at %u, " + "device %04x:%04x:%04x\n", + sdkp->disk->disk_name, sector, + vid, pid, rev); + us->use_last_sector_hacks = -1; + } + + } else { + /* The command failed. Allow up to 3 retries in case this + * is some normal sort of failure. After that, assume the + * capacity is wrong and we're trying to access the sector + * beyond the end. Replace the result code and sense data + * with values that will cause the SCSI core to fail the + * command immediately, instead of going into an infinite + * (or even just a very long) retry loop. + */ + if (++us->last_sector_retries < 3) + return; + srb->result = SAM_STAT_CHECK_CONDITION; + memcpy(srb->sense_buffer, record_not_found, + sizeof(record_not_found)); + + /* In theory we might want to issue a WARN() here if the + * capacity is even, since it could indicate the device + * has the READ CAPACITY bug _and_ the real capacity is + * odd. But it could also indicate that the device + * simply can't access its last sector, a failure mode + * which is surprisingly common. So no warning. + */ + } + + done: + /* Don't reset the retry counter for TEST UNIT READY commands, + * because they get issued after device resets which might be + * caused by a failed last-sector access. + */ + if (srb->cmnd[0] != TEST_UNIT_READY) + us->last_sector_retries = 0; +} + /* Invoke the transport and basic error-handling/recovery methods * * This is used by the protocol layers to actually send the message to @@ -544,6 +651,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) /* if the transport provided its own sense data, don't auto-sense */ if (result == USB_STOR_TRANSPORT_NO_SENSE) { srb->result = SAM_STAT_CHECK_CONDITION; + last_sector_hacks(us, srb); return; } @@ -579,6 +687,20 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) } /* + * Determine if this device is SAT by seeing if the + * command executed successfully. Otherwise we'll have + * to wait for at least one CHECK_CONDITION to determine + * SANE_SENSE support + */ + if ((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) && + result == USB_STOR_TRANSPORT_GOOD && + !(us->fflags & US_FL_SANE_SENSE) && + !(srb->cmnd[2] & 0x20)) { + US_DEBUGP("-- SAT supported, increasing auto-sense\n"); + us->fflags |= US_FL_SANE_SENSE; + } + + /* * A short transfer on a command where we don't expect it * is unusual, but it doesn't mean we need to auto-sense. */ @@ -595,10 +717,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) if (need_auto_sense) { int temp_result; struct scsi_eh_save ses; + int sense_size = US_SENSE_SIZE; + + /* device supports and needs bigger sense buffer */ + if (us->fflags & US_FL_SANE_SENSE) + sense_size = ~0; US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); - scsi_eh_prep_cmnd(srb, &ses, NULL, 0, US_SENSE_SIZE); + scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sense_size); /* FIXME: we must do the protocol translation here */ if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI || @@ -632,6 +759,25 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) return; } + /* If the sense data returned is larger than 18-bytes then we + * assume this device supports requesting more in the future. + * The response code must be 70h through 73h inclusive. + */ + if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) && + !(us->fflags & US_FL_SANE_SENSE) && + (srb->sense_buffer[0] & 0x7C) == 0x70) { + US_DEBUGP("-- SANE_SENSE support enabled\n"); + us->fflags |= US_FL_SANE_SENSE; + + /* Indicate to the user that we truncated their sense + * because we didn't know it supported larger sense. + */ + US_DEBUGP("-- Sense data truncated to %i from %i\n", + US_SENSE_SIZE, + srb->sense_buffer[7] + 8); + srb->sense_buffer[7] = (US_SENSE_SIZE - 8); + } + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", srb->sense_buffer[0], @@ -667,6 +813,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) scsi_bufflen(srb) - scsi_get_resid(srb) < srb->underflow) srb->result = (DID_ERROR << 16) | (SUGGEST_RETRY << 24); + last_sector_hacks(us, srb); return; /* Error and abort processing: try to resynchronize with the device @@ -694,6 +841,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) us->transport_reset(us); } clear_bit(US_FLIDX_RESETTING, &us->dflags); + last_sector_hacks(us, srb); } /* Stop the current URB transfer */ @@ -718,10 +866,10 @@ void usb_stor_stop_transport(struct us_data *us) } /* - * Control/Bulk/Interrupt transport + * Control/Bulk and Control/Bulk/Interrupt transport */ -int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us) +int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) { unsigned int transfer_length = scsi_bufflen(srb); unsigned int pipe = 0; @@ -763,6 +911,13 @@ int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us) } /* STATUS STAGE */ + + /* NOTE: CB does not have a status stage. Silly, I know. So + * we have to catch this at a higher level. + */ + if (us->protocol != US_PR_CBI) + return USB_STOR_TRANSPORT_GOOD; + result = usb_stor_intr_transfer(us, us->iobuf, 2); US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", us->iobuf[0], us->iobuf[1]); @@ -817,56 +972,6 @@ int usb_stor_CBI_transport(struct scsi_cmnd *srb, struct us_data *us) } /* - * Control/Bulk transport - */ -int usb_stor_CB_transport(struct scsi_cmnd *srb, struct us_data *us) -{ - unsigned int transfer_length = scsi_bufflen(srb); - int result; - - /* COMMAND STAGE */ - /* let's send the command via the control pipe */ - result = usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, - US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len); - - /* check the return code for the command */ - US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result); - - /* if we stalled the command, it means command failed */ - if (result == USB_STOR_XFER_STALLED) { - return USB_STOR_TRANSPORT_FAILED; - } - - /* Uh oh... serious problem here */ - if (result != USB_STOR_XFER_GOOD) { - return USB_STOR_TRANSPORT_ERROR; - } - - /* DATA STAGE */ - /* transfer the data payload for this command, if one exists*/ - if (transfer_length) { - unsigned int pipe = srb->sc_data_direction == DMA_FROM_DEVICE ? - us->recv_bulk_pipe : us->send_bulk_pipe; - result = usb_stor_bulk_srb(us, pipe, srb); - US_DEBUGP("CB data stage result is 0x%x\n", result); - - /* if we stalled the data transfer it means command failed */ - if (result == USB_STOR_XFER_STALLED) - return USB_STOR_TRANSPORT_FAILED; - if (result > USB_STOR_XFER_STALLED) - return USB_STOR_TRANSPORT_ERROR; - } - - /* STATUS STAGE */ - /* NOTE: CB does not have a status stage. Silly, I know. So - * we have to catch this at a higher level. - */ - return USB_STOR_TRANSPORT_GOOD; -} - -/* * Bulk only transport */ @@ -1173,10 +1278,9 @@ int usb_stor_Bulk_reset(struct us_data *us) */ int usb_stor_port_reset(struct us_data *us) { - int result, rc_lock; + int result; - result = rc_lock = - usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); + result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf); if (result < 0) US_DEBUGP("unable to lock device for reset: %d\n", result); else { @@ -1189,8 +1293,7 @@ int usb_stor_port_reset(struct us_data *us) US_DEBUGP("usb_reset_device returns %d\n", result); } - if (rc_lock) - usb_unlock_device(us->pusb_dev); + usb_unlock_device(us->pusb_dev); } return result; } diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index e70b88182f0e..242ff5e791a5 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -113,8 +113,6 @@ struct bulk_cs_wrap { #define US_CBI_ADSC 0 -extern int usb_stor_CBI_transport(struct scsi_cmnd *, struct us_data*); - extern int usb_stor_CB_transport(struct scsi_cmnd *, struct us_data*); extern int usb_stor_CB_reset(struct us_data*); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index bfcc1fe82518..a7f9513fa19d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -27,7 +27,8 @@ /* IMPORTANT NOTE: This file must be included in another file which does * the following thing for it to work: - * The macro UNUSUAL_DEV() must be defined before this file is included + * The UNUSUAL_DEV, COMPLIANT_DEV, and USUAL_DEV macros must be defined + * before this file is included. */ /* If you edit this file, please try to keep it sorted first by VendorID, @@ -46,6 +47,12 @@ * <usb-storage@lists.one-eyed-alien.net> */ +/* Note: If you add an entry only in order to set the CAPACITY_OK flag, + * use the COMPLIANT_DEV macro instead of UNUSUAL_DEV. This is + * because such entries mark devices which actually work correctly, + * as opposed to devices that do something strangely or wrongly. + */ + /* patch submitted by Vivian Bregier <Vivian.Bregier@imag.fr> */ UNUSUAL_DEV( 0x03eb, 0x2002, 0x0100, 0x0100, @@ -85,6 +92,13 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, US_SC_8070, US_PR_USBAT, init_usbat_cd, 0), #endif +/* Reported by Ben Efros <ben@pc-doctor.com> */ +UNUSUAL_DEV( 0x03f0, 0x070c, 0x0000, 0x0000, + "HP", + "Personal Media Drive", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + /* Reported by Grant Grundler <grundler@parisc-linux.org> * HP r707 camera in "Disk" mode with 2.00.23 or 2.00.24 firmware. */ @@ -160,34 +174,6 @@ UNUSUAL_DEV( 0x0421, 0x0019, 0x0592, 0x0592, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MAX_SECTORS_64 ), -/* Reported by Filip Joelsson <filip@blueturtle.nu> */ -UNUSUAL_DEV( 0x0421, 0x005d, 0x0001, 0x0600, - "Nokia", - "Nokia 3110c", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - -/* Reported by Ozan Sener <themgzzy@gmail.com> */ -UNUSUAL_DEV( 0x0421, 0x0060, 0x0551, 0x0551, - "Nokia", - "3500c", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - -/* Reported by CSECSY Laszlo <boobaa@frugalware.org> */ -UNUSUAL_DEV( 0x0421, 0x0063, 0x0001, 0x0601, - "Nokia", - "Nokia 3109c", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - -/* Patch for Nokia 5310 capacity */ -UNUSUAL_DEV( 0x0421, 0x006a, 0x0000, 0x0701, - "Nokia", - "5310", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* Reported by Mario Rettig <mariorettig@web.de> */ UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100, "Nokia", @@ -253,35 +239,6 @@ UNUSUAL_DEV( 0x0421, 0x0495, 0x0370, 0x0370, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_MAX_SECTORS_64 ), -/* Reported by Cedric Godin <cedric@belbone.be> */ -UNUSUAL_DEV( 0x0421, 0x04b9, 0x0500, 0x0551, - "Nokia", - "5300", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - -/* Reported by Richard Nauber <RichardNauber@web.de> */ -UNUSUAL_DEV( 0x0421, 0x04fa, 0x0550, 0x0660, - "Nokia", - "6300", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - -/* Patch for Nokia 5310 capacity */ -UNUSUAL_DEV( 0x0421, 0x006a, 0x0000, 0x0591, - "Nokia", - "5310", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - -/* Submitted by Ricky Wong Yung Fei <evilbladewarrior@gmail.com> */ -/* Nokia 7610 Supernova - Too many sectors reported in usb storage mode */ -UNUSUAL_DEV( 0x0421, 0x00f5, 0x0000, 0x0470, - "Nokia", - "7610 Supernova", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */ UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, "SMSC", @@ -289,11 +246,17 @@ UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), -#ifdef CONFIG_USB_STORAGE_DPCM +#ifdef CONFIG_USB_STORAGE_SDDR09 UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", "CameraMate (DPCM_USB)", US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), +#else +UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, + "Microtech", + "CameraMate", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN ), #endif /* Patch submitted by Daniel Drake <dsd@gentoo.org> @@ -388,6 +351,15 @@ UNUSUAL_DEV( 0x04a4, 0x0004, 0x0001, 0x0001, "DVD-CAM DZ-MV100A Camcorder", US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN), +/* BENQ DC5330 + * Reported by Manuel Fombuena <mfombuena@ya.com> and + * Frank Copeland <fjc@thingy.apana.org.au> */ +UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, + "Tekom Technologies, Inc", + "300_CAMERA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Patch for Nikon coolpix 2000 * Submitted by Fabien Cosse <fabien.cosse@wanadoo.fr>*/ UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010, @@ -396,83 +368,6 @@ UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010, US_SC_DEVICE, US_PR_DEVICE,NULL, US_FL_NOT_LOCKABLE ), -/* Reported by Stefan de Konink <skinkie@xs4all.nl> */ -UNUSUAL_DEV( 0x04b0, 0x0401, 0x0200, 0x0200, - "NIKON", - "NIKON DSC D100", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Tobias Kunze Briseno <t-linux@fictive.com> */ -UNUSUAL_DEV( 0x04b0, 0x0403, 0x0200, 0x0200, - "NIKON", - "NIKON DSC D2H", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Milinevsky Dmitry <niam.niam@gmail.com> */ -UNUSUAL_DEV( 0x04b0, 0x0409, 0x0100, 0x0100, - "NIKON", - "NIKON DSC D50", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Andreas Bockhold <andreas@bockionline.de> */ -UNUSUAL_DEV( 0x04b0, 0x0405, 0x0100, 0x0100, - "NIKON", - "NIKON DSC D70", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Jamie Kitson <jamie@staberinde.fsnet.co.uk> */ -UNUSUAL_DEV( 0x04b0, 0x040d, 0x0100, 0x0100, - "NIKON", - "NIKON DSC D70s", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Graber and Mike Pagano <mpagano-kernel@mpagano.com> */ -UNUSUAL_DEV( 0x04b0, 0x040f, 0x0100, 0x0200, - "NIKON", - "NIKON DSC D200", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Emil Larsson <emil@swip.net> */ -UNUSUAL_DEV( 0x04b0, 0x0411, 0x0100, 0x0111, - "NIKON", - "NIKON DSC D80", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Ortwin Glueck <odi@odi.ch> */ -UNUSUAL_DEV( 0x04b0, 0x0413, 0x0110, 0x0111, - "NIKON", - "NIKON DSC D40", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Paul Check <paul@openstreet.com> */ -UNUSUAL_DEV( 0x04b0, 0x0415, 0x0100, 0x0100, - "NIKON", - "NIKON DSC D2Xs", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by Shan Destromp (shansan@gmail.com) */ -UNUSUAL_DEV( 0x04b0, 0x0417, 0x0100, 0x0100, - "NIKON", - "NIKON DSC D40X", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* Reported by paul ready <lxtwin@homecall.co.uk> */ -UNUSUAL_DEV( 0x04b0, 0x0419, 0x0100, 0x0200, - "NIKON", - "NIKON DSC D300", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - /* Reported by Doug Maxey (dwm@austin.ibm.com) */ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, "IBM", @@ -480,15 +375,6 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, US_SC_DEVICE, US_PR_CB, NULL, US_FL_MAX_SECTORS_MIN), -/* BENQ DC5330 - * Reported by Manuel Fombuena <mfombuena@ya.com> and - * Frank Copeland <fjc@thingy.apana.org.au> */ -UNUSUAL_DEV( 0x04a5, 0x3010, 0x0100, 0x0100, - "Tekom Technologies, Inc", - "300_CAMERA", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - #ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB /* CY7C68300 : support atacb */ UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999, @@ -594,6 +480,12 @@ UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208, "eUSB SmartMedia / CompactFlash Adapter", US_SC_SCSI, US_PR_DPCM_USB, usb_stor_sddr09_dpcm_init, 0), +#else +UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208, + "SCM Microsystems", + "eUSB CompactFlash Adapter", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN), #endif /* Reported by Markus Demleitner <msdemlei@cl.uni-heidelberg.de> */ @@ -685,6 +577,13 @@ UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100, US_SC_8070, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Added by Alan Stern <stern@rowland.harvard.edu> */ +COMPLIANT_DEV(0x0525, 0xa4a5, 0x0000, 0x9999, + "Linux", + "File-backed Storage Gadget", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_CAPACITY_OK ), + /* Yakumo Mega Image 37 * Submitted by Stephan Fuhrmann <atomenergie@t-online.de> */ UNUSUAL_DEV( 0x052b, 0x1801, 0x0100, 0x0100, @@ -807,15 +706,15 @@ UNUSUAL_DEV( 0x054c, 0x006d, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), -/* Submitted by Mike Alborn <malborn@deandra.homeip.net> */ -UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, +/* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */ +UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999, "Sony", "PEG Mass Storage", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), - -/* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */ -UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999, + +/* Submitted by Mike Alborn <malborn@deandra.homeip.net> */ +UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, "Sony", "PEG Mass Storage", US_SC_DEVICE, US_PR_DEVICE, NULL, @@ -966,6 +865,18 @@ UNUSUAL_DEV( 0x05ac, 0x120a, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Reported by Dan Williams <dcbw@redhat.com> + * Option N.V. mobile broadband modems + * Ignore driver CD mode and force into modem mode by default. + */ + +/* Globetrotter HSDPA; mass storage shows up as Qualcomm for vendor */ +UNUSUAL_DEV( 0x05c6, 0x1000, 0x0000, 0x9999, + "Option N.V.", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, option_ms_init, + 0), + #ifdef CONFIG_USB_STORAGE_JUMPSHOT UNUSUAL_DEV( 0x05dc, 0x0001, 0x0000, 0x0001, "Lexar", @@ -1004,6 +915,13 @@ UNUSUAL_DEV( 0x05e3, 0x0702, 0x0000, 0xffff, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 ), +/* Reported by Ben Efros <ben@pc-doctor.com> */ +UNUSUAL_DEV( 0x05e3, 0x0723, 0x9451, 0x9451, + "Genesys Logic", + "USB to SATA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + /* Reported by Hanno Boeck <hanno@gmx.de> * Taken from the Lycoris Kernel */ UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, @@ -1040,7 +958,7 @@ UNUSUAL_DEV( 0x067b, 0x2507, 0x0100, 0x0100, US_FL_FIX_CAPACITY | US_FL_GO_SLOW ), /* Reported by Alex Butcher <alex.butcher@assursys.co.uk> */ -UNUSUAL_DEV( 0x067b, 0x3507, 0x0001, 0x0001, +UNUSUAL_DEV( 0x067b, 0x3507, 0x0001, 0x0101, "Prolific Technology Inc.", "ATAPI-6 Bridge Controller", US_SC_DEVICE, US_PR_DEVICE, NULL, @@ -1161,11 +1079,17 @@ UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), -#ifdef CONFIG_USB_STORAGE_DPCM +#ifdef CONFIG_USB_STORAGE_SDDR09 UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100, "Microtech", "CameraMate (DPCM_USB)", US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), +#else +UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100, + "Microtech", + "CameraMate", + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN ), #endif #ifdef CONFIG_USB_STORAGE_ALAUDA @@ -1320,6 +1244,13 @@ UNUSUAL_DEV( 0x0840, 0x0082, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported and patched by Nguyen Anh Quynh <aquynh@gmail.com> */ +UNUSUAL_DEV( 0x0840, 0x0084, 0x0001, 0x0001, + "Argosy", + "Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>. * Flag will support Bulk devices which use a standards-violating 32-byte * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with @@ -1343,17 +1274,6 @@ UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE), -/* Andrew Lunn <andrew@lunn.ch> - * PanDigital Digital Picture Frame. Does not like ALLOW_MEDIUM_REMOVAL - * on LUN 4. - * Note: Vend:Prod clash with "Ltd Maxell WS30 Slim Digital Camera" -*/ -UNUSUAL_DEV( 0x0851, 0x1543, 0x0200, 0x0200, - "PanDigital", - "Photo Frame", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_NOT_LOCKABLE), - /* Submitted by Jan De Luyck <lkml@kcore.org> */ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, "CITIZEN", @@ -1425,6 +1345,13 @@ UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), +/* Reported by Jaak Ristioja <Ristioja@gmail.com> */ +UNUSUAL_DEV( 0x0a17, 0x006e, 0x0100, 0x0100, + "Pentax", + "K10D", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* These are virtual windows driver CDs, which the zd1211rw driver * automatically converts into WLAN devices. */ UNUSUAL_DEV( 0x0ace, 0x2011, 0x0101, 0x0101, @@ -1439,6 +1366,18 @@ UNUSUAL_DEV( 0x0ace, 0x20ff, 0x0101, 0x0101, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_DEVICE ), +/* Reported by Dan Williams <dcbw@redhat.com> + * Option N.V. mobile broadband modems + * Ignore driver CD mode and force into modem mode by default. + */ + +/* iCON 225 */ +UNUSUAL_DEV( 0x0af0, 0x6971, 0x0000, 0x9999, + "Option N.V.", + "Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, option_ms_init, + 0), + /* Reported by F. Aben <f.aben@option.com> * This device (wrongly) has a vendor-specific device descriptor. * The entry is needed so usb-storage can bind to it's mass-storage @@ -1449,6 +1388,13 @@ UNUSUAL_DEV( 0x0af0, 0x7401, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, 0 ), +/* Reported by Ben Efros <ben@pc-doctor.com> */ +UNUSUAL_DEV( 0x0bc2, 0x3010, 0x0000, 0x0000, + "Seagate", + "FreeAgent Pro", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE ), + #ifdef CONFIG_USB_STORAGE_ISD200 UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110, "ATI", @@ -1472,6 +1418,22 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, US_FL_SINGLE_LUN ), #endif +UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999, + "Maxtor", + "USB to SATA", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE), + +/* + * Pete Zaitcev <zaitcev@yahoo.com>, bz#164688. + * The device blatantly ignores LUN and returns 1 in GetMaxLUN. + */ +UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100, + "Unknown", + "Unknown", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), + /* Submitted by: Nick Sillik <n.sillik@temple.edu> * Needed for OneTouch extension to usb-storage * @@ -1489,16 +1451,6 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, 0), #endif -/* - * Pete Zaitcev <zaitcev@yahoo.com>, bz#164688. - * The device blatantly ignores LUN and returns 1 in GetMaxLUN. - */ -UNUSUAL_DEV( 0x0c45, 0x1060, 0x0100, 0x0100, - "Unknown", - "Unknown", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN ), - /* Submitted by Joris Struyve <joris@struyve.be> */ UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff, "Medion", @@ -1516,6 +1468,13 @@ UNUSUAL_DEV( 0x0d96, 0x5200, 0x0001, 0x0200, "JD 5200 z3", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), +/* Reported by Jason Johnston <killean@shaw.ca> */ +UNUSUAL_DEV( 0x0dc4, 0x0073, 0x0000, 0x0000, + "Macpower Technology Co.LTD.", + "USB 2.0 3.5\" DEVICE", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Reported by Lubomir Blaha <tritol@trilogic.cz> * I _REALLY_ don't know what 3rd, 4th number and all defines mean, but this * works for me. Can anybody correct these values? (I able to test corrected @@ -1638,13 +1597,6 @@ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), -/* Reported by Ricardo Barberis <ricardo@dattatec.com> */ -UNUSUAL_DEV( 0x0fce, 0xe092, 0x0000, 0x0000, - "Sony Ericsson", - "P1i", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - /* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, "Sony Ericsson", @@ -1652,6 +1604,13 @@ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ), +/* Reported by Ricardo Barberis <ricardo@dattatec.com> */ +UNUSUAL_DEV( 0x0fce, 0xe092, 0x0000, 0x0000, + "Sony Ericsson", + "P1i", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu> * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. @@ -1664,6 +1623,12 @@ UNUSUAL_DEV( 0x1019, 0x0c55, 0x0000, 0x0110, US_SC_DEVICE, US_PR_DEVICE, usb_stor_ucr61s2b_init, 0 ), +UNUSUAL_DEV( 0x1058, 0x0704, 0x0000, 0x9999, + "Western Digital", + "External HDD", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SANE_SENSE), + /* Reported by Fabio Venturi <f.venturi@tdnet.it> * The device reports a vendor-specific bDeviceClass. */ @@ -2053,10 +2018,10 @@ UNUSUAL_DEV( 0x14cd, 0x6600, 0x0201, 0x0201, * JMicron responds to USN and several other SCSI ioctls with a * residue that causes subsequent I/O requests to fail. */ UNUSUAL_DEV( 0x152d, 0x2329, 0x0100, 0x0100, - "JMicron", - "USB to ATA/ATAPI Bridge", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), + "JMicron", + "USB to ATA/ATAPI Bridge", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE | US_FL_SANE_SENSE ), /* Reported by Robert Schedel <r.schedel@yahoo.de> * Note: this is a 'super top' device like the above 14cd/6600 device */ @@ -2086,27 +2051,6 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), /* - * Patch by Pete Zaitcev <zaitcev@redhat.com> - * Report by Mark Patton. Red Hat bz#208928. - * Added support for rev 0x0002 (Motorola ROKR W5) - * by Javier Smaldone <javier@smaldone.com.ar> - */ -UNUSUAL_DEV( 0x22b8, 0x4810, 0x0001, 0x0002, - "Motorola", - "RAZR V3i/ROKR W5", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* - * Patch by Jost Diederichs <jost@qdusa.com> - */ -UNUSUAL_DEV(0x22b8, 0x6410, 0x0001, 0x9999, - "Motorola Inc.", - "Motorola Phone (RAZRV3xx)", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), - -/* * Patch by Constantin Baranov <const@tltsu.ru> * Report by Andreas Koenecke. * Motorola ROKR Z6. diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 27016fd2cad1..4becf495ca2d 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -75,9 +75,6 @@ #ifdef CONFIG_USB_STORAGE_SDDR55 #include "sddr55.h" #endif -#ifdef CONFIG_USB_STORAGE_DPCM -#include "dpcm.h" -#endif #ifdef CONFIG_USB_STORAGE_FREECOM #include "freecom.h" #endif @@ -103,6 +100,7 @@ #include "cypress_atacb.h" #endif #include "sierra_ms.h" +#include "option_ms.h" /* Some informational data */ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); @@ -113,6 +111,10 @@ static unsigned int delay_use = 5; module_param(delay_use, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); +static char quirks[128]; +module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); + /* * The entries in this table correspond, line for line, @@ -126,6 +128,8 @@ MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device"); { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \ .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } +#define COMPLIANT_DEV UNUSUAL_DEV + #define USUAL_DEV(useProto, useTrans, useType) \ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ .driver_info = (USB_US_TYPE_STOR<<24) } @@ -134,6 +138,7 @@ static struct usb_device_id storage_usb_ids [] = { # include "unusual_devs.h" #undef UNUSUAL_DEV +#undef COMPLIANT_DEV #undef USUAL_DEV /* Terminating entry */ { } @@ -164,6 +169,8 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); .initFunction = init_function, \ } +#define COMPLIANT_DEV UNUSUAL_DEV + #define USUAL_DEV(use_protocol, use_transport, use_type) \ { \ .useProtocol = use_protocol, \ @@ -173,6 +180,7 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); static struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" # undef UNUSUAL_DEV +# undef COMPLIANT_DEV # undef USUAL_DEV /* Terminating entry */ @@ -464,13 +472,83 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) US_DEBUGP("I/O buffer allocation failed\n"); return -ENOMEM; } + return 0; +} - us->sensebuf = kmalloc(US_SENSE_SIZE, GFP_KERNEL); - if (!us->sensebuf) { - US_DEBUGP("Sense buffer allocation failed\n"); - return -ENOMEM; +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) + +/* Adjust device flags based on the "quirks=" module parameter */ +static void adjust_quirks(struct us_data *us) +{ + char *p; + u16 vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor); + u16 pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct); + unsigned f = 0; + unsigned int mask = (US_FL_SANE_SENSE | US_FL_FIX_CAPACITY | + US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | + US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | + US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | + US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT); + + p = quirks; + while (*p) { + /* Each entry consists of VID:PID:flags */ + if (vid == simple_strtoul(p, &p, 16) && + *p == ':' && + pid == simple_strtoul(p+1, &p, 16) && + *p == ':') + break; + + /* Move forward to the next entry */ + while (*p) { + if (*p++ == ',') + break; + } } - return 0; + if (!*p) /* No match */ + return; + + /* Collect the flags */ + while (*++p && *p != ',') { + switch (TOLOWER(*p)) { + case 'a': + f |= US_FL_SANE_SENSE; + break; + case 'c': + f |= US_FL_FIX_CAPACITY; + break; + case 'h': + f |= US_FL_CAPACITY_HEURISTICS; + break; + case 'i': + f |= US_FL_IGNORE_DEVICE; + break; + case 'l': + f |= US_FL_NOT_LOCKABLE; + break; + case 'm': + f |= US_FL_MAX_SECTORS_64; + break; + case 'o': + f |= US_FL_CAPACITY_OK; + break; + case 'r': + f |= US_FL_IGNORE_RESIDUE; + break; + case 's': + f |= US_FL_SINGLE_LUN; + break; + case 'w': + f |= US_FL_NO_WP_DETECT; + break; + /* Ignore unrecognized flag characters */ + } + } + us->fflags = (us->fflags & ~mask) | f; + dev_info(&us->pusb_intf->dev, "Quirks match for " + "vid %04x pid %04x: %x\n", + vid, pid, f); } /* Find an unusual_dev descriptor (always succeeds in the current code) */ @@ -497,6 +575,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) idesc->bInterfaceProtocol : unusual_dev->useTransport; us->fflags = USB_US_ORIG_FLAGS(id->driver_info); + adjust_quirks(us); if (us->fflags & US_FL_IGNORE_DEVICE) { printk(KERN_INFO USB_STORAGE "device ignored\n"); @@ -562,7 +641,7 @@ static int get_transport(struct us_data *us) case US_PR_CBI: us->transport_name = "Control/Bulk/Interrupt"; - us->transport = usb_stor_CBI_transport; + us->transport = usb_stor_CB_transport; us->transport_reset = usb_stor_CB_reset; us->max_lun = 7; break; @@ -675,19 +754,19 @@ static int get_protocol(struct us_data *us) case US_SC_8020: us->protocol_name = "8020i"; - us->proto_handler = usb_stor_ATAPI_command; + us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case US_SC_QIC: us->protocol_name = "QIC-157"; - us->proto_handler = usb_stor_qic157_command; + us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case US_SC_8070: us->protocol_name = "8070i"; - us->proto_handler = usb_stor_ATAPI_command; + us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; @@ -840,8 +919,6 @@ static void dissociate_dev(struct us_data *us) { US_DEBUGP("-- %s\n", __func__); - kfree(us->sensebuf); - /* Free the device-related DMA-mapped buffers */ if (us->cr) usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr, @@ -1064,6 +1141,7 @@ static struct usb_driver usb_storage_driver = { static int __init usb_stor_init(void) { int retval; + printk(KERN_INFO "Initializing USB Mass Storage driver...\n"); /* register the driver, return usb_register return code if error */ diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index a4ad73bd832d..65e674e4be99 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -138,7 +138,6 @@ struct us_data { struct usb_ctrlrequest *cr; /* control requests */ struct usb_sg_request current_sg; /* scatter-gather req. */ unsigned char *iobuf; /* I/O buffer */ - unsigned char *sensebuf; /* sense data buffer */ dma_addr_t cr_dma; /* buffer DMA addresses */ dma_addr_t iobuf_dma; struct task_struct *ctl_thread; /* the control thread */ @@ -155,6 +154,10 @@ struct us_data { #ifdef CONFIG_PM pm_hook suspend_resume_hook; #endif + + /* hacks for READ CAPACITY bug handling */ + int use_last_sector_hacks; + int last_sector_retries; }; /* Convert between us_data and the corresponding Scsi_Host */ diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index 95c6fa3bf6b2..3937bf6f8cef 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -326,7 +326,7 @@ static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, u32 *_buf, u16 wLength) { - u16 *buf = (u16 *) _buf; + __le16 *buf = (__le16 *)_buf; if (port_idx > wusbhc->ports_max) return -EINVAL; diff --git a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h index 5f1b2951bb83..3421d3339d7d 100644 --- a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h +++ b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h @@ -221,7 +221,6 @@ struct i1480u { struct net_device *net_dev; spinlock_t lock; - struct net_device_stats stats; /* RX context handling */ struct sk_buff *rx_skb; @@ -271,7 +270,6 @@ extern int i1480u_stop(struct net_device *); extern int i1480u_hard_start_xmit(struct sk_buff *, struct net_device *); extern void i1480u_tx_timeout(struct net_device *); extern int i1480u_set_config(struct net_device *, struct ifmap *); -extern struct net_device_stats *i1480u_get_stats(struct net_device *); extern int i1480u_change_mtu(struct net_device *, int); extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs); diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c index 049c05d4cc6a..f272dfe54d49 100644 --- a/drivers/uwb/i1480/i1480u-wlp/lc.c +++ b/drivers/uwb/i1480/i1480u-wlp/lc.c @@ -181,6 +181,15 @@ error: } #endif +static const struct net_device_ops i1480u_netdev_ops = { + .ndo_open = i1480u_open, + .ndo_stop = i1480u_stop, + .ndo_start_xmit = i1480u_hard_start_xmit, + .ndo_tx_timeout = i1480u_tx_timeout, + .ndo_set_config = i1480u_set_config, + .ndo_change_mtu = i1480u_change_mtu, +}; + static int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) { @@ -235,13 +244,7 @@ int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface) net_dev->features |= NETIF_F_HIGHDMA; net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */ - net_dev->open = i1480u_open; - net_dev->stop = i1480u_stop; - net_dev->hard_start_xmit = i1480u_hard_start_xmit; - net_dev->tx_timeout = i1480u_tx_timeout; - net_dev->get_stats = i1480u_get_stats; - net_dev->set_config = i1480u_set_config; - net_dev->change_mtu = i1480u_change_mtu; + net_dev->netdev_ops = &i1480u_netdev_ops; #ifdef i1480u_FLOW_CONTROL /* Notification endpoint setup (submitted when we open the device) */ diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c index e3873ffb942c..73055530e60f 100644 --- a/drivers/uwb/i1480/i1480u-wlp/netdev.c +++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c @@ -262,15 +262,6 @@ int i1480u_stop(struct net_device *net_dev) return 0; } - -/** Report statistics */ -struct net_device_stats *i1480u_get_stats(struct net_device *net_dev) -{ - struct i1480u *i1480u = netdev_priv(net_dev); - return &i1480u->stats; -} - - /** * * Change the interface config--we probably don't have to do anything. diff --git a/drivers/uwb/i1480/i1480u-wlp/rx.c b/drivers/uwb/i1480/i1480u-wlp/rx.c index 34f4cf9a7d34..25a2758beb61 100644 --- a/drivers/uwb/i1480/i1480u-wlp/rx.c +++ b/drivers/uwb/i1480/i1480u-wlp/rx.c @@ -167,7 +167,7 @@ do { \ do { \ if (printk_ratelimit()) \ dev_err(&i1480u->usb_iface->dev, msg); \ - i1480u->stats.rx_dropped++; \ + i1480u->net_dev->stats.rx_dropped++; \ } while (0) @@ -193,10 +193,8 @@ void i1480u_skb_deliver(struct i1480u *i1480u) if (!should_parse) goto out; i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev); - i1480u->stats.rx_packets++; - i1480u->stats.rx_bytes += i1480u->rx_untd_pkt_size; - net_dev->last_rx = jiffies; - /* FIXME: flow control: check netif_rx() retval */ + net_dev->stats.rx_packets++; + net_dev->stats.rx_bytes += i1480u->rx_untd_pkt_size; netif_rx(i1480u->rx_skb); /* deliver */ out: diff --git a/drivers/uwb/i1480/i1480u-wlp/tx.c b/drivers/uwb/i1480/i1480u-wlp/tx.c index 39032cc3503e..26bacc009c65 100644 --- a/drivers/uwb/i1480/i1480u-wlp/tx.c +++ b/drivers/uwb/i1480/i1480u-wlp/tx.c @@ -117,8 +117,8 @@ void i1480u_tx_cb(struct urb *urb) switch (urb->status) { case 0: spin_lock_irqsave(&i1480u->lock, flags); - i1480u->stats.tx_packets++; - i1480u->stats.tx_bytes += urb->actual_length; + net_dev->stats.tx_packets++; + net_dev->stats.tx_bytes += urb->actual_length; spin_unlock_irqrestore(&i1480u->lock, flags); break; case -ECONNRESET: /* Not an error, but a controlled situation; */ @@ -530,7 +530,7 @@ int i1480u_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev) return NETDEV_TX_OK; error: dev_kfree_skb_any(skb); - i1480u->stats.tx_dropped++; + net_dev->stats.tx_dropped++; out: return NETDEV_TX_OK; } diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c index 2ac52fd8cc11..4e046fed1380 100644 --- a/drivers/video/amba-clcd.c +++ b/drivers/video/amba-clcd.c @@ -24,6 +24,7 @@ #include <linux/amba/bus.h> #include <linux/amba/clcd.h> #include <linux/clk.h> +#include <linux/hardirq.h> #include <asm/sizes.h> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 4a4dd9adc328..72facb9eb7db 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -52,11 +52,11 @@ config LCD_ILI9320 then say y to include a power driver for it. config LCD_TDO24M - tristate "Toppoly TDO24M LCD Panels support" + tristate "Toppoly TDO24M and TDO35S LCD Panels support" depends on LCD_CLASS_DEVICE && SPI_MASTER default n help - If you have a Toppoly TDO24M series LCD panel, say y here to + If you have a Toppoly TDO24M/TDO35S series LCD panel, say y here to include the support for it. config LCD_VGG2432A4 @@ -123,17 +123,14 @@ config BACKLIGHT_ATMEL_PWM To compile this driver as a module, choose M here: the module will be called atmel-pwm-bl. -config BACKLIGHT_CORGI - tristate "Generic (aka Sharp Corgi) Backlight Driver (DEPRECATED)" +config BACKLIGHT_GENERIC + tristate "Generic (aka Sharp Corgi) Backlight Driver" depends on BACKLIGHT_CLASS_DEVICE - default n + default y help Say y to enable the generic platform backlight driver previously known as the Corgi backlight driver. If you have a Sharp Zaurus - SL-C7xx, SL-Cxx00 or SL-6000x say y. Most users can say n. - - Note: this driver is marked as deprecated, try enable SPI and - use the new corgi_lcd driver with integrated backlight control + SL-C7xx, SL-Cxx00 or SL-6000x say y. config BACKLIGHT_LOCOMO tristate "Sharp LOCOMO LCD/Backlight Driver" diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 103427de6703..363b3cb2f01b 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o -obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o +obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 0664fc032235..157057c79ca3 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -40,6 +40,10 @@ static int fb_notifier_callback(struct notifier_block *self, if (!bd->ops->check_fb || bd->ops->check_fb(evdata->info)) { bd->props.fb_blank = *(int *)evdata->data; + if (bd->props.fb_blank == FB_BLANK_UNBLANK) + bd->props.state &= ~BL_CORE_FBBLANK; + else + bd->props.state |= BL_CORE_FBBLANK; backlight_update_status(bd); } mutex_unlock(&bd->ops_lock); @@ -80,20 +84,18 @@ static ssize_t backlight_show_power(struct device *dev, static ssize_t backlight_store_power(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int rc = -ENXIO; - char *endp; + int rc; struct backlight_device *bd = to_backlight_device(dev); - int power = simple_strtoul(buf, &endp, 0); - size_t size = endp - buf; + unsigned long power; - if (*endp && isspace(*endp)) - size++; - if (size != count) - return -EINVAL; + rc = strict_strtoul(buf, 0, &power); + if (rc) + return rc; + rc = -ENXIO; mutex_lock(&bd->ops_lock); if (bd->ops) { - pr_debug("backlight: set power to %d\n", power); + pr_debug("backlight: set power to %lu\n", power); if (bd->props.power != power) { bd->props.power = power; backlight_update_status(bd); @@ -116,28 +118,25 @@ static ssize_t backlight_show_brightness(struct device *dev, static ssize_t backlight_store_brightness(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int rc = -ENXIO; - char *endp; + int rc; struct backlight_device *bd = to_backlight_device(dev); - int brightness = simple_strtoul(buf, &endp, 0); - size_t size = endp - buf; + unsigned long brightness; + + rc = strict_strtoul(buf, 0, &brightness); + if (rc) + return rc; - if (*endp && isspace(*endp)) - size++; - if (size != count) - return -EINVAL; + rc = -ENXIO; mutex_lock(&bd->ops_lock); if (bd->ops) { if (brightness > bd->props.max_brightness) rc = -EINVAL; else { - pr_debug("backlight: set brightness to %d\n", + pr_debug("backlight: set brightness to %lu\n", brightness); - if (bd->props.brightness != brightness) { - bd->props.brightness = brightness; - backlight_update_status(bd); - } + bd->props.brightness = brightness; + backlight_update_status(bd); rc = count; } } @@ -170,6 +169,34 @@ static ssize_t backlight_show_actual_brightness(struct device *dev, static struct class *backlight_class; +static int backlight_suspend(struct device *dev, pm_message_t state) +{ + struct backlight_device *bd = to_backlight_device(dev); + + if (bd->ops->options & BL_CORE_SUSPENDRESUME) { + mutex_lock(&bd->ops_lock); + bd->props.state |= BL_CORE_SUSPENDED; + backlight_update_status(bd); + mutex_unlock(&bd->ops_lock); + } + + return 0; +} + +static int backlight_resume(struct device *dev) +{ + struct backlight_device *bd = to_backlight_device(dev); + + if (bd->ops->options & BL_CORE_SUSPENDRESUME) { + mutex_lock(&bd->ops_lock); + bd->props.state &= ~BL_CORE_SUSPENDED; + backlight_update_status(bd); + mutex_unlock(&bd->ops_lock); + } + + return 0; +} + static void bl_device_release(struct device *dev) { struct backlight_device *bd = to_backlight_device(dev); @@ -286,6 +313,8 @@ static int __init backlight_class_init(void) } backlight_class->dev_attrs = bl_device_attributes; + backlight_class->suspend = backlight_suspend; + backlight_class->resume = backlight_resume; return 0; } diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c deleted file mode 100644 index 4d4d037e3ec9..000000000000 --- a/drivers/video/backlight/corgi_bl.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Backlight Driver for Sharp Zaurus Handhelds (various models) - * - * Copyright (c) 2004-2006 Richard Purdie - * - * Based on Sharp's 2.4 Backlight Driver - * - * 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 <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/mutex.h> -#include <linux/fb.h> -#include <linux/backlight.h> - -static int corgibl_intensity; -static struct backlight_properties corgibl_data; -static struct backlight_device *corgi_backlight_device; -static struct generic_bl_info *bl_machinfo; - -static unsigned long corgibl_flags; -#define CORGIBL_SUSPENDED 0x01 -#define CORGIBL_BATTLOW 0x02 - -static int corgibl_send_intensity(struct backlight_device *bd) -{ - int intensity = bd->props.brightness; - - if (bd->props.power != FB_BLANK_UNBLANK) - intensity = 0; - if (bd->props.fb_blank != FB_BLANK_UNBLANK) - intensity = 0; - if (corgibl_flags & CORGIBL_SUSPENDED) - intensity = 0; - if (corgibl_flags & CORGIBL_BATTLOW) - intensity &= bl_machinfo->limit_mask; - - bl_machinfo->set_bl_intensity(intensity); - - corgibl_intensity = intensity; - - if (bl_machinfo->kick_battery) - bl_machinfo->kick_battery(); - - return 0; -} - -#ifdef CONFIG_PM -static int corgibl_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct backlight_device *bd = platform_get_drvdata(pdev); - - corgibl_flags |= CORGIBL_SUSPENDED; - backlight_update_status(bd); - return 0; -} - -static int corgibl_resume(struct platform_device *pdev) -{ - struct backlight_device *bd = platform_get_drvdata(pdev); - - corgibl_flags &= ~CORGIBL_SUSPENDED; - backlight_update_status(bd); - return 0; -} -#else -#define corgibl_suspend NULL -#define corgibl_resume NULL -#endif - -static int corgibl_get_intensity(struct backlight_device *bd) -{ - return corgibl_intensity; -} - -/* - * Called when the battery is low to limit the backlight intensity. - * If limit==0 clear any limit, otherwise limit the intensity - */ -void corgibl_limit_intensity(int limit) -{ - if (limit) - corgibl_flags |= CORGIBL_BATTLOW; - else - corgibl_flags &= ~CORGIBL_BATTLOW; - backlight_update_status(corgi_backlight_device); -} -EXPORT_SYMBOL(corgibl_limit_intensity); - - -static struct backlight_ops corgibl_ops = { - .get_brightness = corgibl_get_intensity, - .update_status = corgibl_send_intensity, -}; - -static int corgibl_probe(struct platform_device *pdev) -{ - struct generic_bl_info *machinfo = pdev->dev.platform_data; - const char *name = "generic-bl"; - - bl_machinfo = machinfo; - if (!machinfo->limit_mask) - machinfo->limit_mask = -1; - - if (machinfo->name) - name = machinfo->name; - - corgi_backlight_device = backlight_device_register (name, - &pdev->dev, NULL, &corgibl_ops); - if (IS_ERR (corgi_backlight_device)) - return PTR_ERR (corgi_backlight_device); - - platform_set_drvdata(pdev, corgi_backlight_device); - - corgi_backlight_device->props.max_brightness = machinfo->max_intensity; - corgi_backlight_device->props.power = FB_BLANK_UNBLANK; - corgi_backlight_device->props.brightness = machinfo->default_intensity; - backlight_update_status(corgi_backlight_device); - - printk("Corgi Backlight Driver Initialized.\n"); - return 0; -} - -static int corgibl_remove(struct platform_device *pdev) -{ - struct backlight_device *bd = platform_get_drvdata(pdev); - - corgibl_data.power = 0; - corgibl_data.brightness = 0; - backlight_update_status(bd); - - backlight_device_unregister(bd); - - printk("Corgi Backlight Driver Unloaded\n"); - return 0; -} - -static struct platform_driver corgibl_driver = { - .probe = corgibl_probe, - .remove = corgibl_remove, - .suspend = corgibl_suspend, - .resume = corgibl_resume, - .driver = { - .name = "generic-bl", - }, -}; - -static int __init corgibl_init(void) -{ - return platform_driver_register(&corgibl_driver); -} - -static void __exit corgibl_exit(void) -{ - platform_driver_unregister(&corgibl_driver); -} - -module_init(corgibl_init); -module_exit(corgibl_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); -MODULE_DESCRIPTION("Corgi Backlight Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index 26add8898605..b9fe62b475c6 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -259,22 +259,18 @@ static int __init cr_backlight_init(void) { int ret = platform_driver_register(&cr_backlight_driver); - if (!ret) { - crp = platform_device_alloc("cr_backlight", -1); - if (!crp) - return -ENOMEM; + if (ret) + return ret; - ret = platform_device_add(crp); - - if (ret) { - platform_device_put(crp); - platform_driver_unregister(&cr_backlight_driver); - } + crp = platform_device_register_simple("cr_backlight", -1, NULL, 0); + if (IS_ERR(crp)) { + platform_driver_unregister(&cr_backlight_driver); + return PTR_ERR(crp); } printk("Carillo Ranch Backlight Driver Initialized.\n"); - return ret; + return 0; } static void __exit cr_backlight_exit(void) diff --git a/drivers/video/backlight/generic_bl.c b/drivers/video/backlight/generic_bl.c new file mode 100644 index 000000000000..6d27f62fdcd0 --- /dev/null +++ b/drivers/video/backlight/generic_bl.c @@ -0,0 +1,147 @@ +/* + * Generic Backlight Driver + * + * Copyright (c) 2004-2008 Richard Purdie + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/fb.h> +#include <linux/backlight.h> + +static int genericbl_intensity; +static struct backlight_device *generic_backlight_device; +static struct generic_bl_info *bl_machinfo; + +/* Flag to signal when the battery is low */ +#define GENERICBL_BATTLOW BL_CORE_DRIVER1 + +static int genericbl_send_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.state & BL_CORE_FBBLANK) + intensity = 0; + if (bd->props.state & BL_CORE_SUSPENDED) + intensity = 0; + if (bd->props.state & GENERICBL_BATTLOW) + intensity &= bl_machinfo->limit_mask; + + bl_machinfo->set_bl_intensity(intensity); + + genericbl_intensity = intensity; + + if (bl_machinfo->kick_battery) + bl_machinfo->kick_battery(); + + return 0; +} + +static int genericbl_get_intensity(struct backlight_device *bd) +{ + return genericbl_intensity; +} + +/* + * Called when the battery is low to limit the backlight intensity. + * If limit==0 clear any limit, otherwise limit the intensity + */ +void corgibl_limit_intensity(int limit) +{ + struct backlight_device *bd = generic_backlight_device; + + mutex_lock(&bd->ops_lock); + if (limit) + bd->props.state |= GENERICBL_BATTLOW; + else + bd->props.state &= ~GENERICBL_BATTLOW; + backlight_update_status(generic_backlight_device); + mutex_unlock(&bd->ops_lock); +} +EXPORT_SYMBOL(corgibl_limit_intensity); + +static struct backlight_ops genericbl_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = genericbl_get_intensity, + .update_status = genericbl_send_intensity, +}; + +static int genericbl_probe(struct platform_device *pdev) +{ + struct generic_bl_info *machinfo = pdev->dev.platform_data; + const char *name = "generic-bl"; + struct backlight_device *bd; + + bl_machinfo = machinfo; + if (!machinfo->limit_mask) + machinfo->limit_mask = -1; + + if (machinfo->name) + name = machinfo->name; + + bd = backlight_device_register (name, + &pdev->dev, NULL, &genericbl_ops); + if (IS_ERR (bd)) + return PTR_ERR (bd); + + platform_set_drvdata(pdev, bd); + + bd->props.max_brightness = machinfo->max_intensity; + bd->props.power = FB_BLANK_UNBLANK; + bd->props.brightness = machinfo->default_intensity; + backlight_update_status(bd); + + generic_backlight_device = bd; + + printk("Generic Backlight Driver Initialized.\n"); + return 0; +} + +static int genericbl_remove(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + + bd->props.power = 0; + bd->props.brightness = 0; + backlight_update_status(bd); + + backlight_device_unregister(bd); + + printk("Generic Backlight Driver Unloaded\n"); + return 0; +} + +static struct platform_driver genericbl_driver = { + .probe = genericbl_probe, + .remove = genericbl_remove, + .driver = { + .name = "generic-bl", + }, +}; + +static int __init genericbl_init(void) +{ + return platform_driver_register(&genericbl_driver); +} + +static void __exit genericbl_exit(void) +{ + platform_driver_unregister(&genericbl_driver); +} + +module_init(genericbl_init); +module_exit(genericbl_exit); + +MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); +MODULE_DESCRIPTION("Generic Backlight Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c index d4cfed0b26d5..5be55a20d8c7 100644 --- a/drivers/video/backlight/hp680_bl.c +++ b/drivers/video/backlight/hp680_bl.c @@ -151,19 +151,15 @@ static int __init hp680bl_init(void) int ret; ret = platform_driver_register(&hp680bl_driver); - if (!ret) { - hp680bl_device = platform_device_alloc("hp680-bl", -1); - if (!hp680bl_device) - return -ENOMEM; - - ret = platform_device_add(hp680bl_device); - - if (ret) { - platform_device_put(hp680bl_device); - platform_driver_unregister(&hp680bl_driver); - } + if (ret) + return ret; + hp680bl_device = platform_device_register_simple("hp680-bl", -1, + NULL, 0); + if (IS_ERR(hp680bl_device)) { + platform_driver_unregister(&hp680bl_driver); + return PTR_ERR(hp680bl_device); } - return ret; + return 0; } static void __exit hp680bl_exit(void) diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 06964af761c6..65864c500455 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -70,6 +70,7 @@ static int mbp_get_intensity(struct backlight_device *bd) } static struct backlight_ops mbp_ops = { + .options = BL_CORE_SUSPENDRESUME, .get_brightness = mbp_get_intensity, .update_status = mbp_send_intensity, }; diff --git a/drivers/video/backlight/progear_bl.c b/drivers/video/backlight/progear_bl.c index 15fb4d58b5bc..9edaf24fd82d 100644 --- a/drivers/video/backlight/progear_bl.c +++ b/drivers/video/backlight/progear_bl.c @@ -119,20 +119,16 @@ static int __init progearbl_init(void) { int ret = platform_driver_register(&progearbl_driver); - if (!ret) { - progearbl_device = platform_device_alloc("progear-bl", -1); - if (!progearbl_device) - return -ENOMEM; - - ret = platform_device_add(progearbl_device); - - if (ret) { - platform_device_put(progearbl_device); - platform_driver_unregister(&progearbl_driver); - } + if (ret) + return ret; + progearbl_device = platform_device_register_simple("progear-bl", -1, + NULL, 0); + if (IS_ERR(progearbl_device)) { + platform_driver_unregister(&progearbl_driver); + return PTR_ERR(progearbl_device); } - return ret; + return 0; } static void __exit progearbl_exit(void) diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c index 8427669162ea..1dae7f8f3c6b 100644 --- a/drivers/video/backlight/tdo24m.c +++ b/drivers/video/backlight/tdo24m.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/spi/spi.h> +#include <linux/spi/tdo24m.h> #include <linux/fb.h> #include <linux/lcd.h> @@ -31,6 +32,9 @@ struct tdo24m { struct spi_transfer xfer; uint8_t *buf; + int (*adj_mode)(struct tdo24m *lcd, int mode); + int color_invert; + int power; int mode; }; @@ -66,7 +70,7 @@ static uint32_t lcd_panel_off[] = { CMD_NULL, }; -static uint32_t lcd_vga_pass_through[] = { +static uint32_t lcd_vga_pass_through_tdo24m[] = { CMD1(0xB0, 0x16), CMD1(0xBC, 0x80), CMD1(0xE1, 0x00), @@ -75,7 +79,7 @@ static uint32_t lcd_vga_pass_through[] = { CMD_NULL, }; -static uint32_t lcd_qvga_pass_through[] = { +static uint32_t lcd_qvga_pass_through_tdo24m[] = { CMD1(0xB0, 0x16), CMD1(0xBC, 0x81), CMD1(0xE1, 0x00), @@ -84,7 +88,7 @@ static uint32_t lcd_qvga_pass_through[] = { CMD_NULL, }; -static uint32_t lcd_vga_transfer[] = { +static uint32_t lcd_vga_transfer_tdo24m[] = { CMD1(0xcf, 0x02), /* Blanking period control (1) */ CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */ CMD1(0xd1, 0x01), /* CKV timing control on/off */ @@ -110,6 +114,35 @@ static uint32_t lcd_qvga_transfer[] = { CMD_NULL, }; +static uint32_t lcd_vga_pass_through_tdo35s[] = { + CMD1(0xB0, 0x16), + CMD1(0xBC, 0x80), + CMD1(0xE1, 0x00), + CMD1(0x3B, 0x00), + CMD_NULL, +}; + +static uint32_t lcd_qvga_pass_through_tdo35s[] = { + CMD1(0xB0, 0x16), + CMD1(0xBC, 0x81), + CMD1(0xE1, 0x00), + CMD1(0x3B, 0x22), + CMD_NULL, +}; + +static uint32_t lcd_vga_transfer_tdo35s[] = { + CMD1(0xcf, 0x02), /* Blanking period control (1) */ + CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */ + CMD1(0xd1, 0x01), /* CKV timing control on/off */ + CMD2(0xd2, 0x00, 0x1e), /* CKV 1,2 timing control */ + CMD2(0xd3, 0x14, 0x28), /* OEV timing control */ + CMD2(0xd4, 0x28, 0x64), /* ASW timing control (1) */ + CMD1(0xd5, 0x28), /* ASW timing control (2) */ + CMD0(0x21), /* Invert for normally black display */ + CMD0(0x29), /* Display on */ + CMD_NULL, +}; + static uint32_t lcd_panel_config[] = { CMD2(0xb8, 0xff, 0xf9), /* Output control */ CMD0(0x11), /* sleep out */ @@ -148,6 +181,8 @@ static int tdo24m_writes(struct tdo24m *lcd, uint32_t *array) int nparams, err = 0; for (; *p != CMD_NULL; p++) { + if (!lcd->color_invert && *p == CMD0(0x21)) + continue; nparams = (*p >> 30) & 0x3; @@ -184,12 +219,33 @@ static int tdo24m_adj_mode(struct tdo24m *lcd, int mode) { switch (mode) { case MODE_VGA: - tdo24m_writes(lcd, lcd_vga_pass_through); + tdo24m_writes(lcd, lcd_vga_pass_through_tdo24m); tdo24m_writes(lcd, lcd_panel_config); - tdo24m_writes(lcd, lcd_vga_transfer); + tdo24m_writes(lcd, lcd_vga_transfer_tdo24m); break; case MODE_QVGA: - tdo24m_writes(lcd, lcd_qvga_pass_through); + tdo24m_writes(lcd, lcd_qvga_pass_through_tdo24m); + tdo24m_writes(lcd, lcd_panel_config); + tdo24m_writes(lcd, lcd_qvga_transfer); + break; + default: + return -EINVAL; + } + + lcd->mode = mode; + return 0; +} + +static int tdo35s_adj_mode(struct tdo24m *lcd, int mode) +{ + switch (mode) { + case MODE_VGA: + tdo24m_writes(lcd, lcd_vga_pass_through_tdo35s); + tdo24m_writes(lcd, lcd_panel_config); + tdo24m_writes(lcd, lcd_vga_transfer_tdo35s); + break; + case MODE_QVGA: + tdo24m_writes(lcd, lcd_qvga_pass_through_tdo35s); tdo24m_writes(lcd, lcd_panel_config); tdo24m_writes(lcd, lcd_qvga_transfer); break; @@ -213,7 +269,7 @@ static int tdo24m_power_on(struct tdo24m *lcd) if (err) goto out; - err = tdo24m_adj_mode(lcd, lcd->mode); + err = lcd->adj_mode(lcd, lcd->mode); out: return err; } @@ -262,7 +318,7 @@ static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m) if (lcd->mode == mode) return 0; - return tdo24m_adj_mode(lcd, mode); + return lcd->adj_mode(lcd, mode); } static struct lcd_ops tdo24m_ops = { @@ -276,8 +332,16 @@ static int __devinit tdo24m_probe(struct spi_device *spi) struct tdo24m *lcd; struct spi_message *m; struct spi_transfer *x; + struct tdo24m_platform_data *pdata; + enum tdo24m_model model; int err; + pdata = spi->dev.platform_data; + if (pdata) + model = pdata->model; + else + model = TDO24M; + spi->bits_per_word = 8; spi->mode = SPI_MODE_3; err = spi_setup(spi); @@ -306,6 +370,20 @@ static int __devinit tdo24m_probe(struct spi_device *spi) x->tx_buf = &lcd->buf[0]; spi_message_add_tail(x, m); + switch (model) { + case TDO24M: + lcd->color_invert = 1; + lcd->adj_mode = tdo24m_adj_mode; + break; + case TDO35S: + lcd->adj_mode = tdo35s_adj_mode; + lcd->color_invert = 0; + break; + default: + dev_err(&spi->dev, "Unsupported model"); + goto out_free; + } + lcd->lcd_dev = lcd_device_register("tdo24m", &spi->dev, lcd, &tdo24m_ops); if (IS_ERR(lcd->lcd_dev)) { diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 57a26649f1a5..b7fbc75a62fc 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -39,6 +39,7 @@ struct tosa_lcd_data { struct i2c_client *i2c; int lcd_power; + bool is_vga; }; static int tosa_tg_send(struct spi_device *spi, int adrs, uint8_t data) @@ -81,8 +82,12 @@ static void tosa_lcd_tg_init(struct tosa_lcd_data *data) static void tosa_lcd_tg_on(struct tosa_lcd_data *data) { struct spi_device *spi = data->spi; - const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; - tosa_tg_send(spi, TG_PNLCTL, value | TG_REG0_VQV); /* this depends on mode */ + int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; + + if (data->is_vga) + value |= TG_REG0_VQV; + + tosa_tg_send(spi, TG_PNLCTL, value); /* TG LCD pannel power up */ tosa_tg_send(spi, TG_PINICTL,0x4); @@ -142,9 +147,25 @@ static int tosa_lcd_get_power(struct lcd_device *lcd) return data->lcd_power; } +static int tosa_lcd_set_mode(struct lcd_device *lcd, struct fb_videomode *mode) +{ + struct tosa_lcd_data *data = lcd_get_data(lcd); + + if (mode->xres == 320 || mode->yres == 320) + data->is_vga = false; + else + data->is_vga = true; + + if (POWER_IS_ON(data->lcd_power)) + tosa_lcd_tg_on(data); + + return 0; +} + static struct lcd_ops tosa_lcd_ops = { .set_power = tosa_lcd_set_power, .get_power = tosa_lcd_get_power, + .set_mode = tosa_lcd_set_mode, }; static int __devinit tosa_lcd_probe(struct spi_device *spi) @@ -156,6 +177,8 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi) if (!data) return -ENOMEM; + data->is_vga = true; /* defaut to VGA mode */ + /* * bits_per_word cannot be configured in platform data */ diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c index 593c7687d54a..8e653b8a6f17 100644 --- a/drivers/video/backlight/vgg2432a4.c +++ b/drivers/video/backlight/vgg2432a4.c @@ -137,7 +137,7 @@ static int vgg2432a4_lcd_init(struct ili9320 *lcd, ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1); ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0); - ili9320_write(lcd, ILI9320_RGB_IF2, ILI9320_RGBIF2_DPL); + ili9320_write(lcd, ILI9320_RGB_IF2, cfg->rgb_if2); ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1)); if (ret != 0) diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 90616822cd20..96d2f8e4c275 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -34,6 +34,12 @@ config W1_MASTER_DS2482 This driver can also be built as a module. If so, the module will be called ds2482. +config W1_MASTER_MXC + tristate "Freescale MXC 1-wire busmaster" + depends on W1 && ARCH_MXC + help + Say Y here to enable MXC 1-wire host + config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" depends on W1 && ARM && HAVE_CLK diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index bc4714a75f3a..c5a3e96fcbab 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o +obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o + obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c new file mode 100644 index 000000000000..b9d74d0b353e --- /dev/null +++ b/drivers/w1/masters/mxc_w1.c @@ -0,0 +1,211 @@ +/* + * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Luotao Fu, kernel@pengutronix.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_log.h" + +/* According to the mx27 Datasheet the reset procedure should take up to about + * 1350us. We set the timeout to 500*100us = 50ms for sure */ +#define MXC_W1_RESET_TIMEOUT 500 + +/* + * MXC W1 Register offsets + */ +#define MXC_W1_CONTROL 0x00 +#define MXC_W1_TIME_DIVIDER 0x02 +#define MXC_W1_RESET 0x04 +#define MXC_W1_COMMAND 0x06 +#define MXC_W1_TXRX 0x08 +#define MXC_W1_INTERRUPT 0x0A +#define MXC_W1_INTERRUPT_EN 0x0C + +struct mxc_w1_device { + void __iomem *regs; + unsigned int clkdiv; + struct clk *clk; + struct w1_bus_master bus_master; +}; + +/* + * this is the low level routine to + * reset the device on the One Wire interface + * on the hardware + */ +static u8 mxc_w1_ds2_reset_bus(void *data) +{ + u8 reg_val; + unsigned int timeout_cnt = 0; + struct mxc_w1_device *dev = data; + + __raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); + + while (1) { + reg_val = __raw_readb(dev->regs + MXC_W1_CONTROL); + + if (((reg_val >> 7) & 0x1) == 0 || + timeout_cnt > MXC_W1_RESET_TIMEOUT) + break; + else + timeout_cnt++; + + udelay(100); + } + return (reg_val >> 7) & 0x1; +} + +/* + * this is the low level routine to read/write a bit on the One Wire + * interface on the hardware. It does write 0 if parameter bit is set + * to 0, otherwise a write 1/read. + */ +static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) +{ + struct mxc_w1_device *mdev = data; + void __iomem *ctrl_addr = mdev->regs + MXC_W1_CONTROL; + unsigned int timeout_cnt = 400; /* Takes max. 120us according to + * datasheet. + */ + + __raw_writeb((1 << (5 - bit)), ctrl_addr); + + while (timeout_cnt--) { + if (!((__raw_readb(ctrl_addr) >> (5 - bit)) & 0x1)) + break; + + udelay(1); + } + + return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; +} + +static int __init mxc_w1_probe(struct platform_device *pdev) +{ + struct mxc_w1_device *mdev; + struct resource *res; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + mdev = kzalloc(sizeof(struct mxc_w1_device), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->clk = clk_get(&pdev->dev, "owire_clk"); + if (!mdev->clk) { + err = -ENODEV; + goto failed_clk; + } + + mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1; + + res = request_mem_region(res->start, resource_size(res), + "mxc_w1"); + if (!res) { + err = -EBUSY; + goto failed_req; + } + + mdev->regs = ioremap(res->start, resource_size(res)); + if (!mdev->regs) { + printk(KERN_ERR "Cannot map frame buffer registers\n"); + goto failed_ioremap; + } + + clk_enable(mdev->clk); + __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER); + + mdev->bus_master.data = mdev; + mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus; + mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit; + + err = w1_add_master_device(&mdev->bus_master); + + if (err) + goto failed_add; + + platform_set_drvdata(pdev, mdev); + return 0; + +failed_add: + iounmap(mdev->regs); +failed_ioremap: + release_mem_region(res->start, resource_size(res)); +failed_req: + clk_put(mdev->clk); +failed_clk: + kfree(mdev); + return err; +} + +/* + * disassociate the w1 device from the driver + */ +static int mxc_w1_remove(struct platform_device *pdev) +{ + struct mxc_w1_device *mdev = platform_get_drvdata(pdev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + w1_remove_master_device(&mdev->bus_master); + + iounmap(mdev->regs); + release_mem_region(res->start, resource_size(res)); + clk_disable(mdev->clk); + clk_put(mdev->clk); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mxc_w1_driver = { + .driver = { + .name = "mxc_w1", + }, + .probe = mxc_w1_probe, + .remove = mxc_w1_remove, +}; + +static int __init mxc_w1_init(void) +{ + return platform_driver_register(&mxc_w1_driver); +} + +static void mxc_w1_exit(void) +{ + platform_driver_unregister(&mxc_w1_driver); +} + +module_init(mxc_w1_init); +module_exit(mxc_w1_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductors Inc"); +MODULE_DESCRIPTION("Driver for One-Wire on MXC"); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 97304bd83ec9..d8a9709f3449 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -210,6 +210,7 @@ u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); u8 w1_calc_crc8(u8 *, int); void w1_write_block(struct w1_master *, const u8 *, int); +void w1_touch_block(struct w1_master *, u8 *, int); u8 w1_read_block(struct w1_master *, u8 *, int); int w1_reset_select_slave(struct w1_slave *sl); void w1_next_pullup(struct w1_master *, int); diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index 5139c25ca962..442bd8bbd4a5 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -238,7 +238,6 @@ EXPORT_SYMBOL_GPL(w1_read_8); * @param dev the master device * @param buf pointer to the data to write * @param len the number of bytes to write - * @return the byte read */ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) { @@ -256,6 +255,31 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_write_block); /** + * Touches a series of bytes. + * + * @param dev the master device + * @param buf pointer to the data to write + * @param len the number of bytes to write + */ +void w1_touch_block(struct w1_master *dev, u8 *buf, int len) +{ + int i, j; + u8 tmp; + + for (i = 0; i < len; ++i) { + tmp = 0; + for (j = 0; j < 8; ++j) { + if (j == 7) + w1_pre_write(dev); + tmp |= w1_touch_bit(dev, (buf[i] >> j) & 0x1) << j; + } + + buf[i] = tmp; + } +} +EXPORT_SYMBOL_GPL(w1_touch_block); + +/** * Reads a series of bytes. * * @param dev the master device diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 65c5ebd0787e..fdf72851c574 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -47,21 +47,56 @@ void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) cn_netlink_send(m, 0, GFP_KERNEL); } -static int w1_process_command_master(struct w1_master *dev, struct cn_msg *msg, - struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +static void w1_send_slave(struct w1_master *dev, u64 rn) +{ + struct cn_msg *msg = dev->priv; + struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); + struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); + int avail; + + avail = dev->priv_size - cmd->len; + + if (avail > 8) { + u64 *data = (void *)(cmd + 1) + cmd->len; + + *data = rn; + cmd->len += 8; + hdr->len += 8; + msg->len += 8; + return; + } + + msg->ack++; + cn_netlink_send(msg, 0, GFP_KERNEL); + + msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); + hdr->len = sizeof(struct w1_netlink_cmd); + cmd->len = 0; +} + +static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg, + unsigned int avail) { - dev_dbg(&dev->dev, "%s: %s: cmd=%02x, len=%u.\n", - __func__, dev->name, cmd->cmd, cmd->len); + struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); + struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); + int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH; - if (cmd->cmd != W1_CMD_SEARCH && cmd->cmd != W1_CMD_ALARM_SEARCH) - return -EINVAL; + dev->priv = msg; + dev->priv_size = avail; + + w1_search_devices(dev, search_type, w1_send_slave); + + msg->ack = 0; + cn_netlink_send(msg, 0, GFP_KERNEL); + + dev->priv = NULL; + dev->priv_size = 0; - w1_search_process(dev, (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH); return 0; } -static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, - struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, + struct w1_netlink_cmd *cmd) { void *data; struct w1_netlink_msg *h; @@ -85,7 +120,8 @@ static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); cm->ack = msg->seq+1; - cm->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd) + cmd->len; + cm->len = sizeof(struct w1_netlink_msg) + + sizeof(struct w1_netlink_cmd) + cmd->len; h->len = sizeof(struct w1_netlink_cmd) + cmd->len; @@ -98,36 +134,178 @@ static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, return err; } -static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, +static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) { int err = 0; - dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", - __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc, - cmd->cmd, cmd->len); + switch (cmd->cmd) { + case W1_CMD_TOUCH: + w1_touch_block(dev, cmd->data, cmd->len); + w1_send_read_reply(msg, hdr, cmd); + break; + case W1_CMD_READ: + w1_read_block(dev, cmd->data, cmd->len); + w1_send_read_reply(msg, hdr, cmd); + break; + case W1_CMD_WRITE: + w1_write_block(dev, cmd->data, cmd->len); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg, + struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd) +{ + int err = -EINVAL; + struct cn_msg *msg; + struct w1_netlink_msg *hdr; + struct w1_netlink_cmd *cmd; + + msg = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = req_msg->id; + msg->seq = req_msg->seq; + msg->ack = 0; + msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); + + hdr = (struct w1_netlink_msg *)(msg + 1); + cmd = (struct w1_netlink_cmd *)(hdr + 1); + + hdr->type = W1_MASTER_CMD; + hdr->id = req_hdr->id; + hdr->len = sizeof(struct w1_netlink_cmd); + + cmd->cmd = req_cmd->cmd; + cmd->len = 0; switch (cmd->cmd) { - case W1_CMD_READ: - w1_read_block(sl->master, cmd->data, cmd->len); - w1_send_read_reply(sl, msg, hdr, cmd); - break; - case W1_CMD_WRITE: - w1_write_block(sl->master, cmd->data, cmd->len); - break; - case W1_CMD_SEARCH: - case W1_CMD_ALARM_SEARCH: - w1_search_process(sl->master, - (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH); - break; - default: - err = -1; - break; + case W1_CMD_SEARCH: + case W1_CMD_ALARM_SEARCH: + err = w1_process_search_command(dev, msg, + PAGE_SIZE - msg->len - sizeof(struct cn_msg)); + break; + case W1_CMD_READ: + case W1_CMD_WRITE: + case W1_CMD_TOUCH: + err = w1_process_command_io(dev, req_msg, req_hdr, req_cmd); + break; + case W1_CMD_RESET: + err = w1_reset_bus(dev); + break; + default: + err = -EINVAL; + break; } + kfree(msg); return err; } +static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, + struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +{ + dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", + __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, + sl->reg_num.crc, cmd->cmd, cmd->len); + + return w1_process_command_io(sl->master, msg, hdr, cmd); +} + +static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd) +{ + struct w1_master *m; + struct cn_msg *cn; + struct w1_netlink_msg *w; + u32 *id; + + if (mcmd->type != W1_LIST_MASTERS) { + printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n", + __func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len); + return -EPROTO; + } + + cn = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!cn) + return -ENOMEM; + + cn->id.idx = CN_W1_IDX; + cn->id.val = CN_W1_VAL; + + cn->seq = msg->seq; + cn->ack = 1; + cn->len = sizeof(struct w1_netlink_msg); + w = (struct w1_netlink_msg *)(cn + 1); + + w->type = W1_LIST_MASTERS; + w->status = 0; + w->len = 0; + id = (u32 *)(w + 1); + + mutex_lock(&w1_mlock); + list_for_each_entry(m, &w1_masters, w1_master_entry) { + if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) { + cn_netlink_send(cn, 0, GFP_KERNEL); + cn->ack++; + cn->len = sizeof(struct w1_netlink_msg); + w->len = 0; + id = (u32 *)(w + 1); + } + + *id = m->id; + w->len += sizeof(*id); + cn->len += sizeof(*id); + id++; + } + cn->ack = 0; + cn_netlink_send(cn, 0, GFP_KERNEL); + mutex_unlock(&w1_mlock); + + kfree(cn); + return 0; +} + +static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg, + struct w1_netlink_cmd *rcmd, int error) +{ + struct cn_msg *cmsg; + struct w1_netlink_msg *msg; + struct w1_netlink_cmd *cmd; + + cmsg = kzalloc(sizeof(*msg) + sizeof(*cmd) + sizeof(*cmsg), GFP_KERNEL); + if (!cmsg) + return -ENOMEM; + + msg = (struct w1_netlink_msg *)(cmsg + 1); + cmd = (struct w1_netlink_cmd *)(msg + 1); + + memcpy(cmsg, rcmsg, sizeof(*cmsg)); + cmsg->len = sizeof(*msg); + + memcpy(msg, rmsg, sizeof(*msg)); + msg->len = 0; + msg->status = (short)-error; + + if (rcmd) { + memcpy(cmd, rcmd, sizeof(*cmd)); + cmd->len = 0; + msg->len += sizeof(*cmd); + cmsg->len += sizeof(*cmd); + } + + error = cn_netlink_send(cmsg, 0, GFP_KERNEL); + kfree(cmsg); + + return error; +} + static void w1_cn_callback(void *data) { struct cn_msg *msg = data; @@ -144,6 +322,7 @@ static void w1_cn_callback(void *data) dev = NULL; sl = NULL; + cmd = NULL; memcpy(&id, m->id.id, sizeof(id)); #if 0 @@ -155,15 +334,15 @@ static void w1_cn_callback(void *data) break; } - if (!mlen) - goto out_cont; - if (m->type == W1_MASTER_CMD) { dev = w1_search_master_id(m->id.mst.id); } else if (m->type == W1_SLAVE_CMD) { sl = w1_search_slave(&id); if (sl) dev = sl->master; + } else { + err = w1_process_command_root(msg, m); + goto out_cont; } if (!dev) { @@ -171,6 +350,10 @@ static void w1_cn_callback(void *data) goto out_cont; } + err = 0; + if (!mlen) + goto out_cont; + mutex_lock(&dev->mutex); if (sl && w1_reset_select_slave(sl)) { @@ -187,9 +370,12 @@ static void w1_cn_callback(void *data) } if (sl) - w1_process_command_slave(sl, msg, m, cmd); + err = w1_process_command_slave(sl, msg, m, cmd); else - w1_process_command_master(dev, msg, m, cmd); + err = w1_process_command_master(dev, msg, m, cmd); + + w1_netlink_send_error(msg, m, cmd, err); + err = 0; cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); mlen -= cmd->len + sizeof(struct w1_netlink_cmd); @@ -200,6 +386,8 @@ out_up: atomic_dec(&sl->refcnt); mutex_unlock(&dev->mutex); out_cont: + if (!cmd || err) + w1_netlink_send_error(msg, m, cmd, err); msg->len -= sizeof(struct w1_netlink_msg) + m->len; m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); @@ -209,11 +397,6 @@ out_cont: if (err == -ENODEV) err = 0; } -#if 0 - if (err) { - printk("%s: malformed message. Dropping.\n", __func__); - } -#endif } int w1_init_netlink(void) diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index 56122b9e9294..27e950f935b1 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -34,12 +34,13 @@ enum w1_netlink_message_types { W1_MASTER_REMOVE, W1_MASTER_CMD, W1_SLAVE_CMD, + W1_LIST_MASTERS, }; struct w1_netlink_msg { __u8 type; - __u8 reserved; + __u8 status; __u16 len; union { __u8 id[8]; @@ -51,10 +52,15 @@ struct w1_netlink_msg __u8 data[0]; }; -#define W1_CMD_READ 0x0 -#define W1_CMD_WRITE 0x1 -#define W1_CMD_SEARCH 0x2 -#define W1_CMD_ALARM_SEARCH 0x3 +enum w1_commands { + W1_CMD_READ = 0, + W1_CMD_WRITE, + W1_CMD_SEARCH, + W1_CMD_ALARM_SEARCH, + W1_CMD_TOUCH, + W1_CMD_RESET, + W1_CMD_MAX, +}; struct w1_netlink_cmd { diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 4b75a16de009..526187c8a12d 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -17,3 +17,27 @@ config XEN_SCRUB_PAGES is not accidentally visible to other domains. Is it more secure, but slightly less efficient. If in doubt, say yes. + +config XENFS + tristate "Xen filesystem" + depends on XEN + default y + help + The xen filesystem provides a way for domains to share + information with each other and with the hypervisor. + For example, by reading and writing the "xenbus" file, guests + may pass arbitrary information to the initial domain. + If in doubt, say yes. + +config XEN_COMPAT_XENFS + bool "Create compatibility mount point /proc/xen" + depends on XENFS + default y + help + The old xenstore userspace tools expect to find "xenbus" + under /proc/xen, but "xenbus" is now found at the root of the + xenfs filesystem. Selecting this causes the kernel to create + the compatibilty mount point /proc/xen if it is running on + a xen platform. + If in doubt, say yes. + diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index d2a8fdf0e191..ff8accc9e103 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,5 +1,7 @@ obj-y += grant-table.o features.o events.o manage.o obj-y += xenbus/ + obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o obj-$(CONFIG_XEN_XENCOMM) += xencomm.o obj-$(CONFIG_XEN_BALLOON) += balloon.o +obj-$(CONFIG_XENFS) += xenfs/
\ No newline at end of file diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index 9678b3e98c63..92a1ef80a288 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -136,7 +136,6 @@ EXPORT_SYMBOL_GPL(xenbus_watch_pathfmt); /** * xenbus_switch_state * @dev: xenbus device - * @xbt: transaction handle * @state: new state * * Advertise in the store a change of the given driver to the given new_state. @@ -267,7 +266,7 @@ EXPORT_SYMBOL_GPL(xenbus_dev_error); * @fmt: error message format * * Equivalent to xenbus_dev_error(dev, err, fmt, args), followed by - * xenbus_switch_state(dev, NULL, XenbusStateClosing) to schedule an orderly + * xenbus_switch_state(dev, XenbusStateClosing) to schedule an orderly * closedown of this driver and its peer. */ diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index b2a03184a246..773d1cf23283 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -40,6 +40,7 @@ #include <linux/ctype.h> #include <linux/fcntl.h> #include <linux/mm.h> +#include <linux/proc_fs.h> #include <linux/notifier.h> #include <linux/kthread.h> #include <linux/mutex.h> @@ -55,7 +56,10 @@ #include "xenbus_comms.h" #include "xenbus_probe.h" + int xen_store_evtchn; +EXPORT_SYMBOL(xen_store_evtchn); + struct xenstore_domain_interface *xen_store_interface; static unsigned long xen_store_mfn; @@ -166,6 +170,9 @@ static int read_backend_details(struct xenbus_device *xendev) return read_otherend_details(xendev, "backend-id", "backend"); } +static struct device_attribute xenbus_dev_attrs[] = { + __ATTR_NULL +}; /* Bus type for frontend drivers. */ static struct xen_bus_type xenbus_frontend = { @@ -174,12 +181,13 @@ static struct xen_bus_type xenbus_frontend = { .get_bus_id = frontend_bus_id, .probe = xenbus_probe_frontend, .bus = { - .name = "xen", - .match = xenbus_match, - .uevent = xenbus_uevent, - .probe = xenbus_dev_probe, - .remove = xenbus_dev_remove, - .shutdown = xenbus_dev_shutdown, + .name = "xen", + .match = xenbus_match, + .uevent = xenbus_uevent, + .probe = xenbus_dev_probe, + .remove = xenbus_dev_remove, + .shutdown = xenbus_dev_shutdown, + .dev_attrs = xenbus_dev_attrs, }, }; @@ -852,6 +860,14 @@ static int __init xenbus_probe_init(void) if (!xen_initial_domain()) xenbus_probe(NULL); +#ifdef CONFIG_XEN_COMPAT_XENFS + /* + * Create xenfs mountpoint in /proc for compatibility with + * utilities that expect to find "xenbus" under "/proc/xen". + */ + proc_mkdir("xen", NULL); +#endif + return 0; out_unreg_back: diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 7f2f91c0e11d..e325eab4724d 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -184,6 +184,7 @@ void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg) return ret; } +EXPORT_SYMBOL(xenbus_dev_request_and_reply); /* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */ static void *xs_talkv(struct xenbus_transaction t, diff --git a/drivers/xen/xenfs/Makefile b/drivers/xen/xenfs/Makefile new file mode 100644 index 000000000000..25275c3bbdff --- /dev/null +++ b/drivers/xen/xenfs/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_XENFS) += xenfs.o + +xenfs-objs = super.o xenbus.o
\ No newline at end of file diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c new file mode 100644 index 000000000000..515741a8e6b8 --- /dev/null +++ b/drivers/xen/xenfs/super.c @@ -0,0 +1,64 @@ +/* + * xenfs.c - a filesystem for passing info between the a domain and + * the hypervisor. + * + * 2008-10-07 Alex Zeffertt Replaced /proc/xen/xenbus with xenfs filesystem + * and /proc/xen compatibility mount point. + * Turned xenfs into a loadable module. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/magic.h> + +#include "xenfs.h" + +#include <asm/xen/hypervisor.h> + +MODULE_DESCRIPTION("Xen filesystem"); +MODULE_LICENSE("GPL"); + +static int xenfs_fill_super(struct super_block *sb, void *data, int silent) +{ + static struct tree_descr xenfs_files[] = { + [2] = {"xenbus", &xenbus_file_ops, S_IRUSR|S_IWUSR}, + {""}, + }; + + return simple_fill_super(sb, XENFS_SUPER_MAGIC, xenfs_files); +} + +static int xenfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, xenfs_fill_super, mnt); +} + +static struct file_system_type xenfs_type = { + .owner = THIS_MODULE, + .name = "xenfs", + .get_sb = xenfs_get_sb, + .kill_sb = kill_litter_super, +}; + +static int __init xenfs_init(void) +{ + if (xen_pv_domain()) + return register_filesystem(&xenfs_type); + + printk(KERN_INFO "XENFS: not registering filesystem on non-xen platform\n"); + return 0; +} + +static void __exit xenfs_exit(void) +{ + if (xen_pv_domain()) + unregister_filesystem(&xenfs_type); +} + +module_init(xenfs_init); +module_exit(xenfs_exit); + diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c new file mode 100644 index 000000000000..875a4c59c594 --- /dev/null +++ b/drivers/xen/xenfs/xenbus.c @@ -0,0 +1,593 @@ +/* + * Driver giving user-space access to the kernel's xenbus connection + * to xenstore. + * + * Copyright (c) 2005, Christian Limpach + * Copyright (c) 2005, Rusty Russell, IBM Corporation + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Changes: + * 2008-10-07 Alex Zeffertt Replaced /proc/xen/xenbus with xenfs filesystem + * and /proc/xen compatibility mount point. + * Turned xenfs into a loadable module. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/uio.h> +#include <linux/notifier.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/mount.h> +#include <linux/pagemap.h> +#include <linux/uaccess.h> +#include <linux/init.h> +#include <linux/namei.h> +#include <linux/string.h> + +#include "xenfs.h" +#include "../xenbus/xenbus_comms.h" + +#include <xen/xenbus.h> +#include <asm/xen/hypervisor.h> + +/* + * An element of a list of outstanding transactions, for which we're + * still waiting a reply. + */ +struct xenbus_transaction_holder { + struct list_head list; + struct xenbus_transaction handle; +}; + +/* + * A buffer of data on the queue. + */ +struct read_buffer { + struct list_head list; + unsigned int cons; + unsigned int len; + char msg[]; +}; + +struct xenbus_file_priv { + /* + * msgbuffer_mutex is held while partial requests are built up + * and complete requests are acted on. It therefore protects + * the "transactions" and "watches" lists, and the partial + * request length and buffer. + * + * reply_mutex protects the reply being built up to return to + * usermode. It nests inside msgbuffer_mutex but may be held + * alone during a watch callback. + */ + struct mutex msgbuffer_mutex; + + /* In-progress transactions */ + struct list_head transactions; + + /* Active watches. */ + struct list_head watches; + + /* Partial request. */ + unsigned int len; + union { + struct xsd_sockmsg msg; + char buffer[PAGE_SIZE]; + } u; + + /* Response queue. */ + struct mutex reply_mutex; + struct list_head read_buffers; + wait_queue_head_t read_waitq; + +}; + +/* Read out any raw xenbus messages queued up. */ +static ssize_t xenbus_file_read(struct file *filp, + char __user *ubuf, + size_t len, loff_t *ppos) +{ + struct xenbus_file_priv *u = filp->private_data; + struct read_buffer *rb; + unsigned i; + int ret; + + mutex_lock(&u->reply_mutex); + while (list_empty(&u->read_buffers)) { + mutex_unlock(&u->reply_mutex); + ret = wait_event_interruptible(u->read_waitq, + !list_empty(&u->read_buffers)); + if (ret) + return ret; + mutex_lock(&u->reply_mutex); + } + + rb = list_entry(u->read_buffers.next, struct read_buffer, list); + i = 0; + while (i < len) { + unsigned sz = min((unsigned)len - i, rb->len - rb->cons); + + ret = copy_to_user(ubuf + i, &rb->msg[rb->cons], sz); + + i += sz - ret; + rb->cons += sz - ret; + + if (ret != sz) { + if (i == 0) + i = -EFAULT; + goto out; + } + + /* Clear out buffer if it has been consumed */ + if (rb->cons == rb->len) { + list_del(&rb->list); + kfree(rb); + if (list_empty(&u->read_buffers)) + break; + rb = list_entry(u->read_buffers.next, + struct read_buffer, list); + } + } + +out: + mutex_unlock(&u->reply_mutex); + return i; +} + +/* + * Add a buffer to the queue. Caller must hold the appropriate lock + * if the queue is not local. (Commonly the caller will build up + * multiple queued buffers on a temporary local list, and then add it + * to the appropriate list under lock once all the buffers have een + * successfully allocated.) + */ +static int queue_reply(struct list_head *queue, const void *data, size_t len) +{ + struct read_buffer *rb; + + if (len == 0) + return 0; + + rb = kmalloc(sizeof(*rb) + len, GFP_KERNEL); + if (rb == NULL) + return -ENOMEM; + + rb->cons = 0; + rb->len = len; + + memcpy(rb->msg, data, len); + + list_add_tail(&rb->list, queue); + return 0; +} + +/* + * Free all the read_buffer s on a list. + * Caller must have sole reference to list. + */ +static void queue_cleanup(struct list_head *list) +{ + struct read_buffer *rb; + + while (!list_empty(list)) { + rb = list_entry(list->next, struct read_buffer, list); + list_del(list->next); + kfree(rb); + } +} + +struct watch_adapter { + struct list_head list; + struct xenbus_watch watch; + struct xenbus_file_priv *dev_data; + char *token; +}; + +static void free_watch_adapter(struct watch_adapter *watch) +{ + kfree(watch->watch.node); + kfree(watch->token); + kfree(watch); +} + +static struct watch_adapter *alloc_watch_adapter(const char *path, + const char *token) +{ + struct watch_adapter *watch; + + watch = kzalloc(sizeof(*watch), GFP_KERNEL); + if (watch == NULL) + goto out_fail; + + watch->watch.node = kstrdup(path, GFP_KERNEL); + if (watch->watch.node == NULL) + goto out_free; + + watch->token = kstrdup(token, GFP_KERNEL); + if (watch->token == NULL) + goto out_free; + + return watch; + +out_free: + free_watch_adapter(watch); + +out_fail: + return NULL; +} + +static void watch_fired(struct xenbus_watch *watch, + const char **vec, + unsigned int len) +{ + struct watch_adapter *adap; + struct xsd_sockmsg hdr; + const char *path, *token; + int path_len, tok_len, body_len, data_len = 0; + int ret; + LIST_HEAD(staging_q); + + adap = container_of(watch, struct watch_adapter, watch); + + path = vec[XS_WATCH_PATH]; + token = adap->token; + + path_len = strlen(path) + 1; + tok_len = strlen(token) + 1; + if (len > 2) + data_len = vec[len] - vec[2] + 1; + body_len = path_len + tok_len + data_len; + + hdr.type = XS_WATCH_EVENT; + hdr.len = body_len; + + mutex_lock(&adap->dev_data->reply_mutex); + + ret = queue_reply(&staging_q, &hdr, sizeof(hdr)); + if (!ret) + ret = queue_reply(&staging_q, path, path_len); + if (!ret) + ret = queue_reply(&staging_q, token, tok_len); + if (!ret && len > 2) + ret = queue_reply(&staging_q, vec[2], data_len); + + if (!ret) { + /* success: pass reply list onto watcher */ + list_splice_tail(&staging_q, &adap->dev_data->read_buffers); + wake_up(&adap->dev_data->read_waitq); + } else + queue_cleanup(&staging_q); + + mutex_unlock(&adap->dev_data->reply_mutex); +} + +static int xenbus_write_transaction(unsigned msg_type, + struct xenbus_file_priv *u) +{ + int rc, ret; + void *reply; + struct xenbus_transaction_holder *trans = NULL; + LIST_HEAD(staging_q); + + if (msg_type == XS_TRANSACTION_START) { + trans = kmalloc(sizeof(*trans), GFP_KERNEL); + if (!trans) { + rc = -ENOMEM; + goto out; + } + } + + reply = xenbus_dev_request_and_reply(&u->u.msg); + if (IS_ERR(reply)) { + kfree(trans); + rc = PTR_ERR(reply); + goto out; + } + + if (msg_type == XS_TRANSACTION_START) { + trans->handle.id = simple_strtoul(reply, NULL, 0); + + list_add(&trans->list, &u->transactions); + } else if (msg_type == XS_TRANSACTION_END) { + list_for_each_entry(trans, &u->transactions, list) + if (trans->handle.id == u->u.msg.tx_id) + break; + BUG_ON(&trans->list == &u->transactions); + list_del(&trans->list); + + kfree(trans); + } + + mutex_lock(&u->reply_mutex); + ret = queue_reply(&staging_q, &u->u.msg, sizeof(u->u.msg)); + if (!ret) + ret = queue_reply(&staging_q, reply, u->u.msg.len); + if (!ret) { + list_splice_tail(&staging_q, &u->read_buffers); + wake_up(&u->read_waitq); + } else { + queue_cleanup(&staging_q); + rc = ret; + } + mutex_unlock(&u->reply_mutex); + + kfree(reply); + +out: + return rc; +} + +static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u) +{ + struct watch_adapter *watch, *tmp_watch; + char *path, *token; + int err, rc; + LIST_HEAD(staging_q); + + path = u->u.buffer + sizeof(u->u.msg); + token = memchr(path, 0, u->u.msg.len); + if (token == NULL) { + rc = -EILSEQ; + goto out; + } + token++; + + if (msg_type == XS_WATCH) { + watch = alloc_watch_adapter(path, token); + if (watch == NULL) { + rc = -ENOMEM; + goto out; + } + + watch->watch.callback = watch_fired; + watch->dev_data = u; + + err = register_xenbus_watch(&watch->watch); + if (err) { + free_watch_adapter(watch); + rc = err; + goto out; + } + list_add(&watch->list, &u->watches); + } else { + list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) { + if (!strcmp(watch->token, token) && + !strcmp(watch->watch.node, path)) { + unregister_xenbus_watch(&watch->watch); + list_del(&watch->list); + free_watch_adapter(watch); + break; + } + } + } + + /* Success. Synthesize a reply to say all is OK. */ + { + struct { + struct xsd_sockmsg hdr; + char body[3]; + } __packed reply = { + { + .type = msg_type, + .len = sizeof(reply.body) + }, + "OK" + }; + + mutex_lock(&u->reply_mutex); + rc = queue_reply(&u->read_buffers, &reply, sizeof(reply)); + mutex_unlock(&u->reply_mutex); + } + +out: + return rc; +} + +static ssize_t xenbus_file_write(struct file *filp, + const char __user *ubuf, + size_t len, loff_t *ppos) +{ + struct xenbus_file_priv *u = filp->private_data; + uint32_t msg_type; + int rc = len; + int ret; + LIST_HEAD(staging_q); + + /* + * We're expecting usermode to be writing properly formed + * xenbus messages. If they write an incomplete message we + * buffer it up. Once it is complete, we act on it. + */ + + /* + * Make sure concurrent writers can't stomp all over each + * other's messages and make a mess of our partial message + * buffer. We don't make any attemppt to stop multiple + * writers from making a mess of each other's incomplete + * messages; we're just trying to guarantee our own internal + * consistency and make sure that single writes are handled + * atomically. + */ + mutex_lock(&u->msgbuffer_mutex); + + /* Get this out of the way early to avoid confusion */ + if (len == 0) + goto out; + + /* Can't write a xenbus message larger we can buffer */ + if ((len + u->len) > sizeof(u->u.buffer)) { + /* On error, dump existing buffer */ + u->len = 0; + rc = -EINVAL; + goto out; + } + + ret = copy_from_user(u->u.buffer + u->len, ubuf, len); + + if (ret == len) { + rc = -EFAULT; + goto out; + } + + /* Deal with a partial copy. */ + len -= ret; + rc = len; + + u->len += len; + + /* Return if we haven't got a full message yet */ + if (u->len < sizeof(u->u.msg)) + goto out; /* not even the header yet */ + + /* If we're expecting a message that's larger than we can + possibly send, dump what we have and return an error. */ + if ((sizeof(u->u.msg) + u->u.msg.len) > sizeof(u->u.buffer)) { + rc = -E2BIG; + u->len = 0; + goto out; + } + + if (u->len < (sizeof(u->u.msg) + u->u.msg.len)) + goto out; /* incomplete data portion */ + + /* + * OK, now we have a complete message. Do something with it. + */ + + msg_type = u->u.msg.type; + + switch (msg_type) { + case XS_TRANSACTION_START: + case XS_TRANSACTION_END: + case XS_DIRECTORY: + case XS_READ: + case XS_GET_PERMS: + case XS_RELEASE: + case XS_GET_DOMAIN_PATH: + case XS_WRITE: + case XS_MKDIR: + case XS_RM: + case XS_SET_PERMS: + /* Send out a transaction */ + ret = xenbus_write_transaction(msg_type, u); + break; + + case XS_WATCH: + case XS_UNWATCH: + /* (Un)Ask for some path to be watched for changes */ + ret = xenbus_write_watch(msg_type, u); + break; + + default: + ret = -EINVAL; + break; + } + if (ret != 0) + rc = ret; + + /* Buffered message consumed */ + u->len = 0; + + out: + mutex_unlock(&u->msgbuffer_mutex); + return rc; +} + +static int xenbus_file_open(struct inode *inode, struct file *filp) +{ + struct xenbus_file_priv *u; + + if (xen_store_evtchn == 0) + return -ENOENT; + + nonseekable_open(inode, filp); + + u = kzalloc(sizeof(*u), GFP_KERNEL); + if (u == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&u->transactions); + INIT_LIST_HEAD(&u->watches); + INIT_LIST_HEAD(&u->read_buffers); + init_waitqueue_head(&u->read_waitq); + + mutex_init(&u->reply_mutex); + mutex_init(&u->msgbuffer_mutex); + + filp->private_data = u; + + return 0; +} + +static int xenbus_file_release(struct inode *inode, struct file *filp) +{ + struct xenbus_file_priv *u = filp->private_data; + struct xenbus_transaction_holder *trans, *tmp; + struct watch_adapter *watch, *tmp_watch; + + /* + * No need for locking here because there are no other users, + * by definition. + */ + + list_for_each_entry_safe(trans, tmp, &u->transactions, list) { + xenbus_transaction_end(trans->handle, 1); + list_del(&trans->list); + kfree(trans); + } + + list_for_each_entry_safe(watch, tmp_watch, &u->watches, list) { + unregister_xenbus_watch(&watch->watch); + list_del(&watch->list); + free_watch_adapter(watch); + } + + kfree(u); + + return 0; +} + +static unsigned int xenbus_file_poll(struct file *file, poll_table *wait) +{ + struct xenbus_file_priv *u = file->private_data; + + poll_wait(file, &u->read_waitq, wait); + if (!list_empty(&u->read_buffers)) + return POLLIN | POLLRDNORM; + return 0; +} + +const struct file_operations xenbus_file_ops = { + .read = xenbus_file_read, + .write = xenbus_file_write, + .open = xenbus_file_open, + .release = xenbus_file_release, + .poll = xenbus_file_poll, +}; diff --git a/drivers/xen/xenfs/xenfs.h b/drivers/xen/xenfs/xenfs.h new file mode 100644 index 000000000000..51f08b2d0bf1 --- /dev/null +++ b/drivers/xen/xenfs/xenfs.h @@ -0,0 +1,6 @@ +#ifndef _XENFS_XENBUS_H +#define _XENFS_XENBUS_H + +extern const struct file_operations xenbus_file_ops; + +#endif /* _XENFS_XENBUS_H */ |